From f5bbc91c88c18a4c518f066cc4e784d7b5fb9735 Mon Sep 17 00:00:00 2001 From: Anda-Maria Nicolae Date: Tue, 7 Jul 2015 15:25:53 +0300 Subject: [PATCH 01/35] power_supply: rt9455_charger: Properly notify userspace about charging events Charging events this patch refers to are: - charger is connected to/disconnected from the power source - battery is reconnected to the charger, after it was absent. When the charger is connected to/disconnected from the power source, CHRVPI interrupt occurs and PWR_RDY bit is either set or cleared. PWR_RDY bit is updated after 1-2 seconds CHRVPI interrupt has occurred. power_supply_changed() should be called after PWR_RDY bit is updated. /sys/class/power_supply/rt9455-charger/online file displays the value of PWR_RDY bit. This way, if the userspace is notified that a charging event has occurred and the userspace reads /sys/class/power_supply/rt9455-charger/online file, this file is properly updated when the userspace reads it. This is the reason why power_supply_changed() is called in rt9455_pwr_rdy_work_callback(), instead of being called in interrupt handler. Since no interrupt is triggered when the battery is reconnected to the charger, the userspace is never notified that the battery is reconnected. This is why power_supply_changed() is called in rt9455_max_charging_time_work_callback(), so that the userspace is notified that the battery is reconnected. Signed-off-by: Anda-Maria Nicolae Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/rt9455_charger.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/power/rt9455_charger.c b/drivers/power/rt9455_charger.c index 08baac6e3ada..a49a9d44bdda 100644 --- a/drivers/power/rt9455_charger.c +++ b/drivers/power/rt9455_charger.c @@ -973,7 +973,6 @@ static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info, if (irq2 & GET_MASK(F_CHRVPI)) { dev_dbg(dev, "Charger fault occurred\n"); - alert_userspace = true; /* * CHRVPI bit is set in 2 cases: * 1. when the power source is connected to the charger. @@ -981,6 +980,9 @@ static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info, * To identify the case, PWR_RDY bit is checked. Because * PWR_RDY bit is set / cleared after CHRVPI interrupt is * triggered, it is used delayed_work to later read PWR_RDY bit. + * Also, do not set to true alert_userspace, because there is no + * need to notify userspace when CHRVPI interrupt has occurred. + * Userspace will be notified after PWR_RDY bit is read. */ queue_delayed_work(system_power_efficient_wq, &info->pwr_rdy_work, @@ -1178,7 +1180,7 @@ static irqreturn_t rt9455_irq_handler_thread(int irq, void *data) /* * Sometimes, an interrupt occurs while rt9455_probe() function * is executing and power_supply_register() is not yet called. - * Do not call power_supply_charged() in this case. + * Do not call power_supply_changed() in this case. */ if (info->charger) power_supply_changed(info->charger); @@ -1478,6 +1480,11 @@ static void rt9455_pwr_rdy_work_callback(struct work_struct *work) RT9455_MAX_CHARGING_TIME * HZ); break; } + /* + * Notify userspace that the charger has been either connected to or + * disconnected from the power source. + */ + power_supply_changed(info->charger); } static void rt9455_max_charging_time_work_callback(struct work_struct *work) @@ -1533,6 +1540,11 @@ static void rt9455_batt_presence_work_callback(struct work_struct *work) if (ret) dev_err(dev, "Failed to unmask BATAB interrupt\n"); } + /* + * Notify userspace that the battery is now connected to the + * charger. + */ + power_supply_changed(info->charger); } } From b1a55af2773d5e4d30b748517fedfac26fc5fd81 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 14:39:22 +0900 Subject: [PATCH 02/35] power_supply: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/bq24190_charger.c | 1 - drivers/power/bq24735-charger.c | 1 - drivers/power/pm2301_charger.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index 052db78c3736..703ebecaf38b 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -1534,7 +1534,6 @@ static struct i2c_driver bq24190_driver = { .id_table = bq24190_i2c_ids, .driver = { .name = "bq24190-charger", - .owner = THIS_MODULE, .pm = &bq24190_pm_ops, .of_match_table = of_match_ptr(bq24190_of_match), }, diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c index 961a18930027..b0174379713c 100644 --- a/drivers/power/bq24735-charger.c +++ b/drivers/power/bq24735-charger.c @@ -409,7 +409,6 @@ MODULE_DEVICE_TABLE(of, bq24735_match_ids); static struct i2c_driver bq24735_charger_driver = { .driver = { .name = "bq24735-charger", - .owner = THIS_MODULE, .of_match_table = bq24735_match_ids, }, .probe = bq24735_charger_probe, diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index cc0893ffbf7e..3a45cc0c4dce 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -1244,7 +1244,6 @@ static struct i2c_driver pm2xxx_charger_driver = { .remove = pm2xxx_wall_charger_remove, .driver = { .name = "pm2xxx-wall_charger", - .owner = THIS_MODULE, .pm = PM2XXX_PM_OPS, }, .id_table = pm2xxx_id, From 1ae25d626cfe7e11adc2c3e71d0de1f882954ef3 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 20 Jul 2015 17:32:05 +0800 Subject: [PATCH 03/35] power: reset: at91: add sama5d3 reset function This patch introduces a new compatible string: "atmel,sama5d3-rstc" and new reset function for sama5d3 and later chips. As in sama5d3 or later chips, we don't have to shutdown the DDR controller before reset. Shutdown the DDR controller before reset is a workaround to avoid DDR signal driving the bus, but since sama5d3 and later chips there is no such a conflict. So in this patch: 1. the sama5d3 reset function only need to write the rstc register and return. 2. we can remove the code related with sama5d3 DDR controller as we don't use it at all. Signed-off-by: Josh Wu Acked-by: Nicolas Ferre Acked-by: Alexandre Belloni Reviewed-by: Guenter Roeck Signed-off-by: Sebastian Reichel --- .../devicetree/bindings/arm/atmel-at91.txt | 2 +- drivers/power/reset/at91-reset.c | 26 ++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index 424ac8cbfa08..dd998b9c0433 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -87,7 +87,7 @@ One interrupt per TC channel in a TC block: RSTC Reset Controller required properties: - compatible: Should be "atmel,-rstc". - can be "at91sam9260" or "at91sam9g45" + can be "at91sam9260" or "at91sam9g45" or "sama5d3" - reg: Should contain registers location and length Example: diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 36dc52fb2ec8..c378d4ec826f 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -123,6 +123,15 @@ static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode, return NOTIFY_DONE; } +static int sama5d3_restart(struct notifier_block *this, unsigned long mode, + void *cmd) +{ + writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST), + at91_rstc_base); + + return NOTIFY_DONE; +} + static void __init at91_reset_status(struct platform_device *pdev) { u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); @@ -155,13 +164,13 @@ static void __init at91_reset_status(struct platform_device *pdev) static const struct of_device_id at91_ramc_of_match[] = { { .compatible = "atmel,at91sam9260-sdramc", }, { .compatible = "atmel,at91sam9g45-ddramc", }, - { .compatible = "atmel,sama5d3-ddramc", }, { /* sentinel */ } }; static const struct of_device_id at91_reset_of_match[] = { { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart }, { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, + { .compatible = "atmel,sama5d3-rstc", .data = sama5d3_restart }, { /* sentinel */ } }; @@ -181,13 +190,16 @@ static int at91_reset_of_probe(struct platform_device *pdev) return -ENODEV; } - for_each_matching_node(np, at91_ramc_of_match) { - at91_ramc_base[idx] = of_iomap(np, 0); - if (!at91_ramc_base[idx]) { - dev_err(&pdev->dev, "Could not map ram controller address\n"); - return -ENODEV; + if (!of_device_is_compatible(pdev->dev.of_node, "atmel,sama5d3-rstc")) { + /* we need to shutdown the ddr controller, so get ramc base */ + for_each_matching_node(np, at91_ramc_of_match) { + at91_ramc_base[idx] = of_iomap(np, 0); + if (!at91_ramc_base[idx]) { + dev_err(&pdev->dev, "Could not map ram controller address\n"); + return -ENODEV; + } + idx++; } - idx++; } match = of_match_node(at91_reset_of_match, pdev->dev.of_node); From 1b7cac23419e1398eac4c28368da6671c9c4ce9d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 10:20:28 +1100 Subject: [PATCH 04/35] twl4030_charger: use devm_request_threaded_irq This simplifies the error paths. Signed-off-by: NeilBrown Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 022b8910e443..4c779aecd86b 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -619,21 +619,21 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) goto fail_register_usb; } - ret = request_threaded_irq(bci->irq_chg, NULL, + ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL, twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name, bci); if (ret < 0) { dev_err(&pdev->dev, "could not request irq %d, status %d\n", bci->irq_chg, ret); - goto fail_chg_irq; + goto fail; } - ret = request_threaded_irq(bci->irq_bci, NULL, + ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL, twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci); if (ret < 0) { dev_err(&pdev->dev, "could not request irq %d, status %d\n", bci->irq_bci, ret); - goto fail_bci_irq; + goto fail; } INIT_WORK(&bci->work, twl4030_bci_usb_work); @@ -656,7 +656,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) TWL4030_INTERRUPTS_BCIIMR1A); if (ret < 0) { dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret); - goto fail_unmask_interrupts; + goto fail; } reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); @@ -675,11 +675,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) return 0; -fail_unmask_interrupts: - free_irq(bci->irq_bci, bci); -fail_bci_irq: - free_irq(bci->irq_chg, bci); -fail_chg_irq: +fail: power_supply_unregister(bci->usb); fail_register_usb: power_supply_unregister(bci->ac); @@ -704,8 +700,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - free_irq(bci->irq_bci, bci); - free_irq(bci->irq_chg, bci); power_supply_unregister(bci->usb); power_supply_unregister(bci->ac); kfree(bci); From 325b50aa5d1c7dae57d1e44defdbacd1e2bcabde Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 10:20:28 +1100 Subject: [PATCH 05/35] twl4030_charger: use devres for power_supply_register and kzalloc. Final allocations/registrations are now managed by devres. Signed-off-by: NeilBrown Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 36 ++++++++++----------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 4c779aecd86b..709d90dc75f1 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -581,7 +581,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) int ret; u32 reg; - bci = kzalloc(sizeof(*bci), GFP_KERNEL); + bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL); if (bci == NULL) return -ENOMEM; @@ -596,27 +596,27 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) ret = twl4030_is_battery_present(bci); if (ret) { dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret); - goto fail_no_battery; + return ret; } platform_set_drvdata(pdev, bci); - bci->ac = power_supply_register(&pdev->dev, &twl4030_bci_ac_desc, - NULL); + bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc, + NULL); if (IS_ERR(bci->ac)) { ret = PTR_ERR(bci->ac); dev_err(&pdev->dev, "failed to register ac: %d\n", ret); - goto fail_register_ac; + return ret; } bci->usb_reg = regulator_get(bci->dev, "bci3v1"); - bci->usb = power_supply_register(&pdev->dev, &twl4030_bci_usb_desc, - NULL); + bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc, + NULL); if (IS_ERR(bci->usb)) { ret = PTR_ERR(bci->usb); dev_err(&pdev->dev, "failed to register usb: %d\n", ret); - goto fail_register_usb; + return ret; } ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL, @@ -625,7 +625,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "could not request irq %d, status %d\n", bci->irq_chg, ret); - goto fail; + return ret; } ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL, @@ -633,7 +633,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "could not request irq %d, status %d\n", bci->irq_bci, ret); - goto fail; + return ret; } INIT_WORK(&bci->work, twl4030_bci_usb_work); @@ -656,7 +656,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) TWL4030_INTERRUPTS_BCIIMR1A); if (ret < 0) { dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret); - goto fail; + return ret; } reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); @@ -674,16 +674,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) twl4030_charger_enable_backup(0, 0); return 0; - -fail: - power_supply_unregister(bci->usb); -fail_register_usb: - power_supply_unregister(bci->ac); -fail_register_ac: -fail_no_battery: - kfree(bci); - - return ret; } static int __exit twl4030_bci_remove(struct platform_device *pdev) @@ -700,10 +690,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - power_supply_unregister(bci->usb); - power_supply_unregister(bci->ac); - kfree(bci); - return 0; } From 377b641ac84c573bcea06aec47f7ad1ba4047e1c Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Wed, 1 Jul 2015 11:40:53 +0200 Subject: [PATCH 06/35] power/ltc2941-battery-gauge.c: Use the devicetree node name as supply name Make it possible to set the name of the supply from the devicetree. Like other power supply drivers just use the node name. This makes the code smaller as well, as it doesn't need to allocate memory to hold the name and allocate a unique ID. Signed-off-by: Mike Looijmans Signed-off-by: Sebastian Reichel --- drivers/power/ltc2941-battery-gauge.c | 54 ++++----------------------- 1 file changed, 8 insertions(+), 46 deletions(-) diff --git a/drivers/power/ltc2941-battery-gauge.c b/drivers/power/ltc2941-battery-gauge.c index daeb0860736c..4adf2ba021ce 100644 --- a/drivers/power/ltc2941-battery-gauge.c +++ b/drivers/power/ltc2941-battery-gauge.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -63,15 +62,11 @@ struct ltc294x_info { struct power_supply_desc supply_desc; /* Supply description */ struct delayed_work work; /* Work scheduler */ int num_regs; /* Number of registers (chip type) */ - int id; /* Identifier of ltc294x chip */ int charge; /* Last charge register content */ int r_sense; /* mOhm */ int Qlsb; /* nAh */ }; -static DEFINE_IDR(ltc294x_id); -static DEFINE_MUTEX(ltc294x_lock); - static inline int convert_bin_to_uAh( const struct ltc294x_info *info, int Q) { @@ -371,10 +366,6 @@ static int ltc294x_i2c_remove(struct i2c_client *client) cancel_delayed_work(&info->work); power_supply_unregister(info->supply); - kfree(info->supply_desc.name); - mutex_lock(<c294x_lock); - idr_remove(<c294x_id, info->id); - mutex_unlock(<c294x_lock); return 0; } @@ -384,44 +375,28 @@ static int ltc294x_i2c_probe(struct i2c_client *client, struct power_supply_config psy_cfg = {}; struct ltc294x_info *info; int ret; - int num; u32 prescaler_exp; s32 r_sense; struct device_node *np; - mutex_lock(<c294x_lock); - ret = idr_alloc(<c294x_id, client, 0, 0, GFP_KERNEL); - mutex_unlock(<c294x_lock); - if (ret < 0) - goto fail_id; - - num = ret; - info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); - if (info == NULL) { - ret = -ENOMEM; - goto fail_info; - } + if (info == NULL) + return -ENOMEM; i2c_set_clientdata(client, info); - info->num_regs = id->driver_data; - info->supply_desc.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, - num); - if (!info->supply_desc.name) { - ret = -ENOMEM; - goto fail_name; - } - np = of_node_get(client->dev.of_node); + info->num_regs = id->driver_data; + info->supply_desc.name = np->name; + /* r_sense can be negative, when sense+ is connected to the battery * instead of the sense-. This results in reversed measurements. */ ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense); if (ret < 0) { dev_err(&client->dev, "Could not find lltc,resistor-sense in devicetree\n"); - goto fail_name; + return ret; } info->r_sense = r_sense; @@ -446,7 +421,6 @@ static int ltc294x_i2c_probe(struct i2c_client *client, } info->client = client; - info->id = num; info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY; info->supply_desc.properties = ltc294x_properties; if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB) @@ -473,31 +447,19 @@ static int ltc294x_i2c_probe(struct i2c_client *client, ret = ltc294x_reset(info, prescaler_exp); if (ret < 0) { dev_err(&client->dev, "Communication with chip failed\n"); - goto fail_comm; + return ret; } info->supply = power_supply_register(&client->dev, &info->supply_desc, &psy_cfg); if (IS_ERR(info->supply)) { dev_err(&client->dev, "failed to register ltc2941\n"); - ret = PTR_ERR(info->supply); - goto fail_register; + return PTR_ERR(info->supply); } else { schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); } return 0; - -fail_register: - kfree(info->supply_desc.name); -fail_comm: -fail_name: -fail_info: - mutex_lock(<c294x_lock); - idr_remove(<c294x_id, num); - mutex_unlock(<c294x_lock); -fail_id: - return ret; } #ifdef CONFIG_PM_SLEEP From dd9f1486ae207cd947416f1bdc461edc4880f2df Mon Sep 17 00:00:00 2001 From: Jun Nie Date: Wed, 15 Jul 2015 11:25:58 +0800 Subject: [PATCH 07/35] power/reset: zx: Register restart handler Register with kernel restart handler instead of setting arm_pm_restart directly. Signed-off-by: Jun Nie Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 7 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/zx-reboot.c | 82 +++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 drivers/power/reset/zx-reboot.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 17d93a73c513..5a0189bf19bb 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -166,5 +166,12 @@ config POWER_RESET_RMOBILE help Reboot support for Renesas R-Mobile and SH-Mobile SoCs. +config POWER_RESET_ZX + tristate "ZTE SoCs reset driver" + depends on ARCH_ZX || COMPILE_TEST + depends on HAS_IOMEM + help + Reboot support for ZTE SoCs. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index dbe06c368743..096fa67047f6 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o +obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o diff --git a/drivers/power/reset/zx-reboot.c b/drivers/power/reset/zx-reboot.c new file mode 100644 index 000000000000..cf4b973dcedf --- /dev/null +++ b/drivers/power/reset/zx-reboot.c @@ -0,0 +1,82 @@ +/* + * ZTE zx296702 SoC reset code + * + * Copyright (c) 2015 Linaro Ltd. + * + * Author: Jun Nie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static void __iomem *base; +static void __iomem *pcu_base; + +static int zx_restart_handler(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + writel_relaxed(1, base + 0xb0); + writel_relaxed(1, pcu_base + 0x34); + + mdelay(50); + pr_emerg("Unable to restart system\n"); + + return NOTIFY_DONE; +} + +static struct notifier_block zx_restart_nb = { + .notifier_call = zx_restart_handler, + .priority = 128, +}; + +static int zx_reboot_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int err; + + base = of_iomap(np, 0); + if (!base) { + WARN(1, "failed to map base address"); + return -ENODEV; + } + + np = of_find_compatible_node(NULL, NULL, "zte,zx296702-pcu"); + pcu_base = of_iomap(np, 0); + if (!pcu_base) { + iounmap(base); + WARN(1, "failed to map pcu_base address"); + return -ENODEV; + } + + err = register_restart_handler(&zx_restart_nb); + if (err) + dev_err(&pdev->dev, "Register restart handler failed(err=%d)\n", + err); + + return err; +} + +static const struct of_device_id zx_reboot_of_match[] = { + { .compatible = "zte,sysctrl" }, + {} +}; + +static struct platform_driver zx_reboot_driver = { + .probe = zx_reboot_probe, + .driver = { + .name = "zx-reboot", + .of_match_table = zx_reboot_of_match, + }, +}; +module_platform_driver(zx_reboot_driver); From db04fc5caa79260170a68d58db8fc401f1ab05f5 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Wed, 22 Jul 2015 16:51:53 -0500 Subject: [PATCH 08/35] power: bq27x00_battery: Add manufacturer property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the manufacturer property to the bq27x00 driver. Signed-off-by: Andrew F. Davis Acked-by: Dan Murphy Acked-by: Pali Rohár Signed-off-by: Sebastian Reichel --- drivers/power/bq27x00_battery.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index b6b98378faa3..2257bd4e7378 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -41,6 +41,8 @@ #define DRIVER_VERSION "1.2.0" +#define BQ27XXX_MANUFACTURER "Texas Instruments" + #define BQ27x00_REG_TEMP 0x06 #define BQ27x00_REG_VOLT 0x08 #define BQ27x00_REG_AI 0x14 @@ -142,6 +144,7 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_MANUFACTURER, }; static enum power_supply_property bq27425_battery_props[] = { @@ -156,6 +159,7 @@ static enum power_supply_property bq27425_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_MANUFACTURER, }; static enum power_supply_property bq27742_battery_props[] = { @@ -174,6 +178,7 @@ static enum power_supply_property bq27742_battery_props[] = { POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_MANUFACTURER, }; static enum power_supply_property bq27510_battery_props[] = { @@ -192,6 +197,7 @@ static enum power_supply_property bq27510_battery_props[] = { POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_MANUFACTURER, }; static unsigned int poll_interval = 360; @@ -749,6 +755,9 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_HEALTH: ret = bq27x00_simple_value(di->cache.health, val); break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ27XXX_MANUFACTURER; + break; default: return -EINVAL; } From 6eb207f271478472195cfaa2e59646f7e5036ae8 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Wed, 22 Jul 2015 16:51:54 -0500 Subject: [PATCH 09/35] power: bq27x00_battery: Fix lines over 80 characters long MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shorted lines over 80 characters long by reducing tab count. Signed-off-by: Andrew F. Davis Acked-by: Pali Rohár Acked-by: Dan Murphy Signed-off-by: Sebastian Reichel --- drivers/power/bq27x00_battery.c | 66 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 2257bd4e7378..203cf613c559 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -39,49 +39,49 @@ #include -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.2.0" #define BQ27XXX_MANUFACTURER "Texas Instruments" -#define BQ27x00_REG_TEMP 0x06 -#define BQ27x00_REG_VOLT 0x08 -#define BQ27x00_REG_AI 0x14 -#define BQ27x00_REG_FLAGS 0x0A -#define BQ27x00_REG_TTE 0x16 -#define BQ27x00_REG_TTF 0x18 -#define BQ27x00_REG_TTECP 0x26 -#define BQ27x00_REG_NAC 0x0C /* Nominal available capacity */ -#define BQ27x00_REG_LMD 0x12 /* Last measured discharge */ -#define BQ27x00_REG_CYCT 0x2A /* Cycle count total */ -#define BQ27x00_REG_AE 0x22 /* Available energy */ -#define BQ27x00_POWER_AVG 0x24 +#define BQ27x00_REG_TEMP 0x06 +#define BQ27x00_REG_VOLT 0x08 +#define BQ27x00_REG_AI 0x14 +#define BQ27x00_REG_FLAGS 0x0A +#define BQ27x00_REG_TTE 0x16 +#define BQ27x00_REG_TTF 0x18 +#define BQ27x00_REG_TTECP 0x26 +#define BQ27x00_REG_NAC 0x0C /* Nominal available capacity */ +#define BQ27x00_REG_LMD 0x12 /* Last measured discharge */ +#define BQ27x00_REG_CYCT 0x2A /* Cycle count total */ +#define BQ27x00_REG_AE 0x22 /* Available energy */ +#define BQ27x00_POWER_AVG 0x24 -#define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ -#define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ -#define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */ -#define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */ -#define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */ -#define BQ27000_FLAG_FC BIT(5) -#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ +#define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ +#define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ +#define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */ +#define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */ +#define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */ +#define BQ27000_FLAG_FC BIT(5) +#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ -#define BQ27500_REG_SOC 0x2C -#define BQ27500_REG_DCAP 0x3C /* Design capacity */ -#define BQ27500_FLAG_DSC BIT(0) -#define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ -#define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ -#define BQ27500_FLAG_FC BIT(9) -#define BQ27500_FLAG_OTC BIT(15) +#define BQ27500_REG_SOC 0x2C +#define BQ27500_REG_DCAP 0x3C /* Design capacity */ +#define BQ27500_FLAG_DSC BIT(0) +#define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ +#define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ +#define BQ27500_FLAG_FC BIT(9) +#define BQ27500_FLAG_OTC BIT(15) -#define BQ27742_POWER_AVG 0x76 +#define BQ27742_POWER_AVG 0x76 -#define BQ27510_REG_SOC 0x20 -#define BQ27510_REG_DCAP 0x2E /* Design capacity */ -#define BQ27510_REG_CYCT 0x1E /* Cycle count total */ +#define BQ27510_REG_SOC 0x20 +#define BQ27510_REG_DCAP 0x2E /* Design capacity */ +#define BQ27510_REG_CYCT 0x1E /* Cycle count total */ /* bq27425 register addresses are same as bq27x00 addresses minus 4 */ -#define BQ27425_REG_OFFSET 0x04 +#define BQ27425_REG_OFFSET 0x04 #define BQ27425_REG_SOC (0x1C + BQ27425_REG_OFFSET) -#define BQ27425_REG_DCAP (0x3C + BQ27425_REG_OFFSET) +#define BQ27425_REG_DCAP (0x3C + BQ27425_REG_OFFSET) #define BQ27000_RS 20 /* Resistor sense */ #define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000) From ce33edfa4903d8b8ded2569e880489678f75a85b Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Wed, 22 Jul 2015 16:51:55 -0500 Subject: [PATCH 10/35] power: bq27x00_battery: Fix function parameter alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the alignment of function parameters on new lines. Signed-off-by: Andrew F. Davis Acked-by: Pali Rohár Acked-by: Dan Murphy Signed-off-by: Sebastian Reichel --- drivers/power/bq27x00_battery.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 203cf613c559..9cdad1992a2d 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -210,7 +210,7 @@ MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ */ static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, - bool single) + bool single) { if (di->chip == BQ27425) return di->bus.read(di, reg - BQ27425_REG_OFFSET, single); @@ -565,7 +565,7 @@ static void bq27x00_battery_poll(struct work_struct *work) * Or 0 if something fails. */ static int bq27x00_battery_current(struct bq27x00_device_info *di, - union power_supply_propval *val) + union power_supply_propval *val) { int curr; int flags; @@ -593,7 +593,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, } static int bq27x00_battery_status(struct bq27x00_device_info *di, - union power_supply_propval *val) + union power_supply_propval *val) { int status; @@ -621,7 +621,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, } static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di, - union power_supply_propval *val) + union power_supply_propval *val) { int level; @@ -655,7 +655,7 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di, * Or < 0 if something fails. */ static int bq27x00_battery_voltage(struct bq27x00_device_info *di, - union power_supply_propval *val) + union power_supply_propval *val) { int volt; @@ -671,7 +671,7 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di, } static int bq27x00_simple_value(int value, - union power_supply_propval *val) + union power_supply_propval *val) { if (value < 0) return value; @@ -987,7 +987,7 @@ static inline void bq27x00_battery_i2c_exit(void) {}; #ifdef CONFIG_BATTERY_BQ27X00_PLATFORM static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg, - bool single) + bool single) { struct device *dev = di->dev; struct bq27000_platform_data *pdata = dev->platform_data; From 6d0a1815e814ee5eaa0cec17913d5e4abae99ef0 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Wed, 22 Jul 2015 16:51:56 -0500 Subject: [PATCH 11/35] power: bq27x00_battery: Checkpatch fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove space before tab. Remove unnecessary line continuations. Add braces to else statement. Remove unnecessary parentheses. Remove unneeded blank lines. Remove unnecessary 'out of memory' message. Add missing line after declarations. Change use of printk to pr_err. Signed-off-by: Andrew F. Davis Acked-by: Pali Rohár Acked-by: Dan Murphy Signed-off-by: Sebastian Reichel --- drivers/power/bq27x00_battery.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 9cdad1992a2d..8287261fd978 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -108,7 +108,7 @@ struct bq27x00_reg_cache { }; struct bq27x00_device_info { - struct device *dev; + struct device *dev; int id; enum bq27x00_chip chip; @@ -202,8 +202,8 @@ static enum power_supply_property bq27510_battery_props[] = { static unsigned int poll_interval = 360; module_param(poll_interval, uint, 0644); -MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ - "0 disables polling"); +MODULE_PARM_DESC(poll_interval, + "battery poll interval in seconds - 0 disables polling"); /* * Common code for BQ27x00 devices @@ -319,8 +319,9 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) ilmd = bq27x00_read(di, BQ27510_REG_DCAP, false); else ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); - } else + } else { ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); + } if (ilmd < 0) { dev_dbg(di->dev, "error reading initial last measured discharge\n"); @@ -451,7 +452,7 @@ static int bq27x00_battery_read_health(struct bq27x00_device_info *di) return tval; } - if ((di->chip == BQ27500)) { + if (di->chip == BQ27500) { if (tval & BQ27500_FLAG_SOCF) tval = POWER_SUPPLY_HEALTH_DEAD; else if (tval & BQ27500_FLAG_OTC) @@ -836,7 +837,6 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) mutex_destroy(&di->lock); } - /* i2c specific code */ #ifdef CONFIG_BATTERY_BQ27X00_I2C @@ -897,14 +897,12 @@ static int bq27x00_battery_probe(struct i2c_client *client, name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num); if (!name) { - dev_err(&client->dev, "failed to allocate device name\n"); retval = -ENOMEM; goto batt_failed; } di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL); if (!di) { - dev_err(&client->dev, "failed to allocate device info data\n"); retval = -ENOMEM; goto batt_failed; } @@ -965,8 +963,9 @@ static struct i2c_driver bq27x00_battery_driver = { static inline int bq27x00_battery_i2c_init(void) { int ret = i2c_add_driver(&bq27x00_battery_driver); + if (ret) - printk(KERN_ERR "Unable to register BQ27x00 i2c driver\n"); + pr_err("Unable to register BQ27x00 i2c driver\n"); return ret; } @@ -1037,10 +1036,8 @@ static int bq27000_battery_probe(struct platform_device *pdev) } di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); - if (!di) { - dev_err(&pdev->dev, "failed to allocate device info data\n"); + if (!di) return -ENOMEM; - } platform_set_drvdata(pdev, di); @@ -1073,8 +1070,9 @@ static struct platform_driver bq27000_battery_driver = { static inline int bq27x00_battery_platform_init(void) { int ret = platform_driver_register(&bq27000_battery_driver); + if (ret) - printk(KERN_ERR "Unable to register BQ27000 platform driver\n"); + pr_err("Unable to register BQ27000 platform driver\n"); return ret; } From 3309aa494186b0c5364d812ae000955262590655 Mon Sep 17 00:00:00 2001 From: Jun Nie Date: Mon, 27 Jul 2015 14:59:06 +0800 Subject: [PATCH 12/35] power/reset: zx: Remove unnecessary include file Including ARM related header file cause build failure in i386 build because COMILE_TEST also involve building zx driver. Remove the unnecessary include file. Signed-off-by: Jun Nie Signed-off-by: Sebastian Reichel --- drivers/power/reset/zx-reboot.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/power/reset/zx-reboot.c b/drivers/power/reset/zx-reboot.c index cf4b973dcedf..a5b009673d0e 100644 --- a/drivers/power/reset/zx-reboot.c +++ b/drivers/power/reset/zx-reboot.c @@ -18,8 +18,6 @@ #include #include -#include - static void __iomem *base; static void __iomem *pcu_base; From 71a5a508436332ac40f688ef06e61628f25f3e5b Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Sat, 25 Jul 2015 09:23:16 +0530 Subject: [PATCH 13/35] power_supply: bq24735: Convert to using managed resources Use managed resource functions like devm_kasprintf and devm_power_supply_register in bq24735_charger_probe. To be compatible with the change, replace various gotos by direct returns and drop unneeded labels. Also, remove bq24735_charger_remove as it is now redundant. Signed-off-by: Vaishali Thakkar Signed-off-by: Sebastian Reichel --- drivers/power/bq24735-charger.c | 51 +++++++++------------------------ 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c index b0174379713c..eb2b3689de97 100644 --- a/drivers/power/bq24735-charger.c +++ b/drivers/power/bq24735-charger.c @@ -267,8 +267,9 @@ static int bq24735_charger_probe(struct i2c_client *client, name = (char *)charger->pdata->name; if (!name) { - name = kasprintf(GFP_KERNEL, "bq24735@%s", - dev_name(&client->dev)); + name = devm_kasprintf(&client->dev, GFP_KERNEL, + "bq24735@%s", + dev_name(&client->dev)); if (!name) { dev_err(&client->dev, "Failed to alloc device name\n"); return -ENOMEM; @@ -296,23 +297,21 @@ static int bq24735_charger_probe(struct i2c_client *client, if (ret < 0) { dev_err(&client->dev, "Failed to read manufacturer id : %d\n", ret); - goto err_free_name; + return ret; } else if (ret != 0x0040) { dev_err(&client->dev, "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret); - ret = -ENODEV; - goto err_free_name; + return -ENODEV; } ret = bq24735_read_word(client, BQ24735_DEVICE_ID); if (ret < 0) { dev_err(&client->dev, "Failed to read device id : %d\n", ret); - goto err_free_name; + return ret; } else if (ret != 0x000B) { dev_err(&client->dev, "device id mismatch. 0x000b != 0x%04x\n", ret); - ret = -ENODEV; - goto err_free_name; + return -ENODEV; } if (gpio_is_valid(charger->pdata->status_gpio)) { @@ -331,7 +330,7 @@ static int bq24735_charger_probe(struct i2c_client *client, ret = bq24735_config_charger(charger); if (ret < 0) { dev_err(&client->dev, "failed in configuring charger"); - goto err_free_name; + return ret; } /* check for AC adapter presence */ @@ -339,17 +338,17 @@ static int bq24735_charger_probe(struct i2c_client *client, ret = bq24735_enable_charging(charger); if (ret < 0) { dev_err(&client->dev, "Failed to enable charging\n"); - goto err_free_name; + return ret; } } - charger->charger = power_supply_register(&client->dev, supply_desc, - &psy_cfg); + charger->charger = devm_power_supply_register(&client->dev, supply_desc, + &psy_cfg); if (IS_ERR(charger->charger)) { ret = PTR_ERR(charger->charger); dev_err(&client->dev, "Failed to register power supply: %d\n", ret); - goto err_free_name; + return ret; } if (client->irq) { @@ -364,33 +363,10 @@ static int bq24735_charger_probe(struct i2c_client *client, dev_err(&client->dev, "Unable to register IRQ %d err %d\n", client->irq, ret); - goto err_unregister_supply; + return ret; } } - return 0; -err_unregister_supply: - power_supply_unregister(charger->charger); -err_free_name: - if (name != charger->pdata->name) - kfree(name); - - return ret; -} - -static int bq24735_charger_remove(struct i2c_client *client) -{ - struct bq24735 *charger = i2c_get_clientdata(client); - - if (charger->client->irq) - devm_free_irq(&charger->client->dev, charger->client->irq, - &charger->charger); - - power_supply_unregister(charger->charger); - - if (charger->charger_desc.name != charger->pdata->name) - kfree(charger->charger_desc.name); - return 0; } @@ -412,7 +388,6 @@ static struct i2c_driver bq24735_charger_driver = { .of_match_table = bq24735_match_ids, }, .probe = bq24735_charger_probe, - .remove = bq24735_charger_remove, .id_table = bq24735_charger_id, }; From 753f15833c4d782ee41e333f66bfe424a39680e6 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 27 Jul 2015 00:26:47 +0300 Subject: [PATCH 14/35] power: ds2780_battery: clean up eeprom read/write functions The change removes redundant sysfs binary file boundary checks while reading or writing "param_eeprom" or "user_eeprom", the checks are not needed, since this task is done on caller side in fs/sysfs/file.c Signed-off-by: Vladimir Zapolskiy Signed-off-by: Sebastian Reichel --- drivers/power/ds2780_battery.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c index a7a0427343f3..d3743d0ad55b 100644 --- a/drivers/power/ds2780_battery.c +++ b/drivers/power/ds2780_battery.c @@ -637,10 +637,6 @@ static ssize_t ds2780_read_param_eeprom_bin(struct file *filp, struct power_supply *psy = to_power_supply(dev); struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); - count = min_t(loff_t, count, - DS2780_EEPROM_BLOCK1_END - - DS2780_EEPROM_BLOCK1_START + 1 - off); - return ds2780_read_block(dev_info, buf, DS2780_EEPROM_BLOCK1_START + off, count); } @@ -655,10 +651,6 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); int ret; - count = min_t(loff_t, count, - DS2780_EEPROM_BLOCK1_END - - DS2780_EEPROM_BLOCK1_START + 1 - off); - ret = ds2780_write(dev_info, buf, DS2780_EEPROM_BLOCK1_START + off, count); if (ret < 0) @@ -676,7 +668,7 @@ static struct bin_attribute ds2780_param_eeprom_bin_attr = { .name = "param_eeprom", .mode = S_IRUGO | S_IWUSR, }, - .size = DS2780_EEPROM_BLOCK1_END - DS2780_EEPROM_BLOCK1_START + 1, + .size = DS2780_PARAM_EEPROM_SIZE, .read = ds2780_read_param_eeprom_bin, .write = ds2780_write_param_eeprom_bin, }; @@ -690,10 +682,6 @@ static ssize_t ds2780_read_user_eeprom_bin(struct file *filp, struct power_supply *psy = to_power_supply(dev); struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); - count = min_t(loff_t, count, - DS2780_EEPROM_BLOCK0_END - - DS2780_EEPROM_BLOCK0_START + 1 - off); - return ds2780_read_block(dev_info, buf, DS2780_EEPROM_BLOCK0_START + off, count); } @@ -708,10 +696,6 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); int ret; - count = min_t(loff_t, count, - DS2780_EEPROM_BLOCK0_END - - DS2780_EEPROM_BLOCK0_START + 1 - off); - ret = ds2780_write(dev_info, buf, DS2780_EEPROM_BLOCK0_START + off, count); if (ret < 0) @@ -729,7 +713,7 @@ static struct bin_attribute ds2780_user_eeprom_bin_attr = { .name = "user_eeprom", .mode = S_IRUGO | S_IWUSR, }, - .size = DS2780_EEPROM_BLOCK0_END - DS2780_EEPROM_BLOCK0_START + 1, + .size = DS2780_USER_EEPROM_SIZE, .read = ds2780_read_user_eeprom_bin, .write = ds2780_write_user_eeprom_bin, }; From 9f28b86552aef94733387814a57755cb0019cfb9 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 27 Jul 2015 00:26:48 +0300 Subject: [PATCH 15/35] power: ds2781_battery: clean up eeprom read/write functions The change removes redundant calculation of left space on eeprom while reading or writing "param_eeprom" or "user_eeprom", the checks are not needed, since this task is done on caller side in fs/sysfs/file.c Signed-off-by: Vladimir Zapolskiy Signed-off-by: Sebastian Reichel --- drivers/power/ds2781_battery.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c index 56d583dae908..c3680024f399 100644 --- a/drivers/power/ds2781_battery.c +++ b/drivers/power/ds2781_battery.c @@ -639,8 +639,6 @@ static ssize_t ds2781_read_param_eeprom_bin(struct file *filp, struct power_supply *psy = to_power_supply(dev); struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); - count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off); - return ds2781_read_block(dev_info, buf, DS2781_EEPROM_BLOCK1_START + off, count); } @@ -655,8 +653,6 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); int ret; - count = min_t(loff_t, count, DS2781_PARAM_EEPROM_SIZE - off); - ret = ds2781_write(dev_info, buf, DS2781_EEPROM_BLOCK1_START + off, count); if (ret < 0) @@ -688,8 +684,6 @@ static ssize_t ds2781_read_user_eeprom_bin(struct file *filp, struct power_supply *psy = to_power_supply(dev); struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); - count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off); - return ds2781_read_block(dev_info, buf, DS2781_EEPROM_BLOCK0_START + off, count); @@ -705,8 +699,6 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); int ret; - count = min_t(loff_t, count, DS2781_USER_EEPROM_SIZE - off); - ret = ds2781_write(dev_info, buf, DS2781_EEPROM_BLOCK0_START + off, count); if (ret < 0) From 3e1d9c6f241961e2e21b8df9de5ee12e3dc6f177 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 27 Jul 2015 00:26:49 +0300 Subject: [PATCH 16/35] power: olpc_battery: clean up eeprom read function The change removes redundant sysfs binary file boundary check while reading eeprom content from userspace, the check is done on caller side in fs/sysfs/file.c, if binary attribute size is not zero. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Sebastian Reichel --- drivers/power/olpc_battery.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index a944338a39de..9e29b1321648 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -521,11 +521,6 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj, int ret; int i; - if (off >= EEPROM_SIZE) - return 0; - if (off + count > EEPROM_SIZE) - count = EEPROM_SIZE - off; - for (i = 0; i < count; i++) { ec_byte = EEPROM_START + off + i; ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1); @@ -545,7 +540,7 @@ static struct bin_attribute olpc_bat_eeprom = { .name = "eeprom", .mode = S_IRUGO, }, - .size = 0, + .size = EEPROM_SIZE, .read = olpc_bat_eeprom_read, }; From 26305022e7ffdeff49b15c9ab18e2c0c880356da Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 24 Jul 2015 23:11:28 +0200 Subject: [PATCH 17/35] MAINTAINERS: AVS is not maintained via power supply tree The adaptive voltage scaling framework is not maintained via the power supply tree and has its own entry in the MAINTAINERS file, so add an exception in the power supply entry. Signed-off-by: Sebastian Reichel Acked-by: Kevin Hilman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8133cefb6b6e..243f76f4fc07 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8049,6 +8049,7 @@ T: git git://git.infradead.org/battery-2.6.git S: Maintained F: include/linux/power_supply.h F: drivers/power/ +X: drivers/power/avs/ PNP SUPPORT M: "Rafael J. Wysocki" From b5e578631e5c1d77f58727ea0769dd55c462b572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 28 Jul 2015 20:44:41 +0200 Subject: [PATCH 18/35] rx51-battery: Set name to rx51-battery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For compatibility between board code and DT, set battery name to same value. Signed-off-by: Pali Rohár Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/power/rx51_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c index ac6206951d58..af9383d23d12 100644 --- a/drivers/power/rx51_battery.c +++ b/drivers/power/rx51_battery.c @@ -215,7 +215,7 @@ static int rx51_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, di); di->dev = &pdev->dev; - di->bat_desc.name = dev_name(&pdev->dev); + di->bat_desc.name = "rx51-battery"; di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; di->bat_desc.properties = rx51_battery_props; di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props); From e57c4a67d71275e4a15b9f0f92a322ea27b26a6e Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 19/35] twl4030_charger: use runtime_pm to keep usb phy active while charging. The twl4030 usb phy needs to be active while we are using the USB VBUS as a current source for charging. In particular, the usb3v1 regulator must be enabled and the PHY_PWR_PHYPWD bit must be set to keep the phy powered. commit ab37813f4093a5f59cb8e083cde277289dc72ed3 twl4030_charger: Allow charger to control the regulator that feeds it gave the charger control over the regulator, but didn't resolve the PHY_PWR_PHYPWD issue. Now that both of these are controlled by runtime_pm in phy-twl4030-usb, we can simply take a runtime_pm reference to the USB phy whenever the charger wants to use it as a current source. So this patch reverts the above commit, and adds the necessary runtime_pm calls. Acked-by: Lee Jones Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- drivers/mfd/twl-core.c | 9 ++++----- drivers/power/twl4030_charger.c | 18 +++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 489674a2497e..831696ee2472 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -788,9 +788,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, static struct regulator_consumer_supply usb1v8 = { .supply = "usb1v8", }; - static struct regulator_consumer_supply usb3v1[] = { - { .supply = "usb3v1" }, - { .supply = "bci3v1" }, + static struct regulator_consumer_supply usb3v1 = { + .supply = "usb3v1", }; /* First add the regulators so that they can be used by transceiver */ @@ -818,7 +817,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, return PTR_ERR(child); child = add_regulator_linked(TWL4030_REG_VUSB3V1, - &usb_fixed, usb3v1, 2, + &usb_fixed, &usb3v1, 1, features); if (IS_ERR(child)) return PTR_ERR(child); @@ -838,7 +837,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) { usb1v5.dev_name = dev_name(child); usb1v8.dev_name = dev_name(child); - usb3v1[0].dev_name = dev_name(child); + usb3v1.dev_name = dev_name(child); } } diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 709d90dc75f1..fe71c61109f5 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -22,7 +22,6 @@ #include #include #include -#include #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG 0x08 @@ -94,7 +93,6 @@ struct twl4030_bci { struct work_struct work; int irq_chg; int irq_bci; - struct regulator *usb_reg; int usb_enabled; unsigned long event; @@ -208,7 +206,7 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) { int ret; - if (enable) { + if (enable && !IS_ERR_OR_NULL(bci->transceiver)) { /* Check for USB charger connected */ if (!twl4030_bci_have_vbus(bci)) return -ENODEV; @@ -222,14 +220,9 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) return -EACCES; } - /* Need to keep regulator on */ + /* Need to keep phy powered */ if (!bci->usb_enabled) { - ret = regulator_enable(bci->usb_reg); - if (ret) { - dev_err(bci->dev, - "Failed to enable regulator\n"); - return ret; - } + pm_runtime_get_sync(bci->transceiver->dev); bci->usb_enabled = 1; } @@ -244,7 +237,8 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) } else { ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); if (bci->usb_enabled) { - regulator_disable(bci->usb_reg); + pm_runtime_mark_last_busy(bci->transceiver->dev); + pm_runtime_put_autosuspend(bci->transceiver->dev); bci->usb_enabled = 0; } } @@ -609,8 +603,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) return ret; } - bci->usb_reg = regulator_get(bci->dev, "bci3v1"); - bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc, NULL); if (IS_ERR(bci->usb)) { From 7396f708b9f1d8874dda9c6386c37b065d99b68f Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 20/35] twl4030_charger: convert to module_platform_driver instead of ..._probe. Drivers using module_platform_driver_probe cannot return EPROBE_DEFER from the probe function, which makes them rather useless these days... Convert to module_platform_driver() so EPROBE_DEFER can be used. Signed-off-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fe71c61109f5..045238370d3f 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -568,7 +568,7 @@ static const struct power_supply_desc twl4030_bci_usb_desc = { .get_property = twl4030_bci_get_property, }; -static int __init twl4030_bci_probe(struct platform_device *pdev) +static int twl4030_bci_probe(struct platform_device *pdev) { struct twl4030_bci *bci; const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; @@ -692,14 +692,14 @@ static const struct of_device_id twl_bci_of_match[] = { MODULE_DEVICE_TABLE(of, twl_bci_of_match); static struct platform_driver twl4030_bci_driver = { + .probe = twl4030_bci_probe, .driver = { .name = "twl4030_bci", .of_match_table = of_match_ptr(twl_bci_of_match), }, .remove = __exit_p(twl4030_bci_remove), }; - -module_platform_driver_probe(twl4030_bci_driver, twl4030_bci_probe); +module_platform_driver(twl4030_bci_driver); MODULE_AUTHOR("Gražvydas Ignotas"); MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); From 3fc3895e4fe17ee92ae1d1bb9f04da6069e8c370 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 21/35] twl4030_charger: correctly handle -EPROBE_DEFER from devm_usb_get_phy_by_node Now that twl4030_bci_probe can safely return -EPROBE_DEFER, do so when devm_usb_get_phy_by_node returns that error. Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 045238370d3f..ffc123fb7158 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -636,9 +636,13 @@ static int twl4030_bci_probe(struct platform_device *pdev) phynode = of_find_compatible_node(bci->dev->of_node->parent, NULL, "ti,twl4030-usb"); - if (phynode) + if (phynode) { bci->transceiver = devm_usb_get_phy_by_node( bci->dev, phynode, &bci->usb_nb); + if (IS_ERR(bci->transceiver) && + PTR_ERR(bci->transceiver) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } } /* Enable interrupts now. */ From 6e37ec8c77e3e6580cbb844dfeea0fb3970c0a35 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 22/35] twl4030_charger: trust phy to determine when USB power is available. The usb phy driver already determines when VBUS is available, so repeating the test in the charger driver is pointless duplication. On probe, process the last event from the phy, and from then on, do whatever the phy tells us without double-checking. Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index ffc123fb7158..a075216d65ed 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -177,28 +177,6 @@ static int twl4030_is_battery_present(struct twl4030_bci *bci) return -ENODEV; } -/* - * Check if VBUS power is present - */ -static int twl4030_bci_have_vbus(struct twl4030_bci *bci) -{ - int ret; - u8 hwsts; - - ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &hwsts, - TWL4030_PM_MASTER_STS_HW_CONDITIONS); - if (ret < 0) - return 0; - - dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts); - - /* in case we also have STS_USB_ID, VBUS is driven by TWL itself */ - if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID)) - return 1; - - return 0; -} - /* * Enable/Disable USB Charge functionality. */ @@ -207,10 +185,6 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) int ret; if (enable && !IS_ERR_OR_NULL(bci->transceiver)) { - /* Check for USB charger connected */ - if (!twl4030_bci_have_vbus(bci)) - return -ENODEV; - /* * Until we can find out what current the device can provide, * require a module param to enable USB charging. @@ -662,7 +636,12 @@ static int twl4030_bci_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); twl4030_charger_enable_ac(true); - twl4030_charger_enable_usb(bci, true); + if (!IS_ERR_OR_NULL(bci->transceiver)) + twl4030_bci_usb_ncb(&bci->usb_nb, + bci->transceiver->last_event, + NULL); + else + twl4030_charger_enable_usb(bci, false); if (pdata) twl4030_charger_enable_backup(pdata->bb_uvolt, pdata->bb_uamp); From 3b542f089dcbdcf1c21a01927fbc6d5116af01f6 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 23/35] twl4030_charger: split uA calculation into a function. We will need this calculation in other places, so create functions to map between register value and uA value. Acked-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 48 ++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index a075216d65ed..29984b263a35 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -177,6 +177,40 @@ static int twl4030_is_battery_present(struct twl4030_bci *bci) return -ENODEV; } +/* + * TI provided formulas: + * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85 + * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7 + * Here we use integer approximation of: + * CGAIN == 0: val * 1.6618 - 0.85 * 1000 + * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2 + */ +/* + * convert twl register value for currents into uA + */ +static int regval2ua(int regval, bool cgain) +{ + if (cgain) + return (regval * 16618 - 8500 * 1000) / 5; + else + return (regval * 16618 - 8500 * 1000) / 10; +} + +/* + * convert uA currents into twl register value + */ +static int ua2regval(int ua, bool cgain) +{ + int ret; + if (cgain) + ua /= 2; + ret = (ua * 10 + 8500 * 1000) / 16618; + /* rounding problems */ + if (ret < 512) + ret = 512; + return ret; +} + /* * Enable/Disable USB Charge functionality. */ @@ -366,14 +400,6 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, return NOTIFY_OK; } -/* - * TI provided formulas: - * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85 - * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7 - * Here we use integer approximation of: - * CGAIN == 0: val * 1.6618 - 0.85 - * CGAIN == 1: (val * 1.6618 - 0.85) * 2 - */ static int twl4030_charger_get_current(void) { int curr; @@ -388,11 +414,7 @@ static int twl4030_charger_get_current(void) if (ret) return ret; - ret = (curr * 16618 - 850 * 10000) / 10; - if (bcictl1 & TWL4030_CGAIN) - ret *= 2; - - return ret; + return regval2ua(curr, bcictl1 & TWL4030_CGAIN); } /* From 1098cb58aed8eb4e06302d947a38bbfb69c8b4ba Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 24/35] twl4030_charger: allow fine control of charger current. The twl4030 allows control of the incoming current. Part of this control is a 'CGAIN' setting which doubles the range for half the precision. This control affects several different current setting, so all need to be updated at once when CGAIN is changed. With this patch, all of these current setting are managed by the driver, but most are left at their default settings. The current drawn is set to 500mA if the allow_usb module parameter is set, and to 100mA otherwise. More fine control will appear in later patches. Acked-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 168 ++++++++++++++++++++++++++++++-- 1 file changed, 160 insertions(+), 8 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 29984b263a35..3b7cc631bb8a 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -31,6 +31,11 @@ #define TWL4030_BCIMFSTS4 0x10 #define TWL4030_BCICTL1 0x23 #define TWL4030_BB_CFG 0x12 +#define TWL4030_BCIIREF1 0x27 +#define TWL4030_BCIIREF2 0x28 +#define TWL4030_BCIMFKEY 0x11 +#define TWL4030_BCIMFTH8 0x1d +#define TWL4030_BCIMFTH9 0x1e #define TWL4030_BCIMFSTS1 0x01 @@ -95,6 +100,12 @@ struct twl4030_bci { int irq_bci; int usb_enabled; + /* + * ichg values in uA. If any are 'large', we set CGAIN to + * '1' which doubles the range for half the precision. + */ + unsigned int ichg_eoc, ichg_lo, ichg_hi, cur; + unsigned long event; }; @@ -211,6 +222,146 @@ static int ua2regval(int ua, bool cgain) return ret; } +static int twl4030_charger_update_current(struct twl4030_bci *bci) +{ + int status; + unsigned reg, cur_reg; + u8 bcictl1, oldreg, fullreg; + bool cgain = false; + u8 boot_bci; + + /* First, check thresholds and see if cgain is needed */ + if (bci->ichg_eoc >= 200000) + cgain = true; + if (bci->ichg_lo >= 400000) + cgain = true; + if (bci->ichg_hi >= 820000) + cgain = true; + if (bci->cur > 852000) + cgain = true; + + status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); + if (status < 0) + return status; + if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci, + TWL4030_PM_MASTER_BOOT_BCI) < 0) + boot_bci = 0; + boot_bci &= 7; + + if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) + /* Need to turn for charging while we change the + * CGAIN bit. Leave it off while everything is + * updated. + */ + twl4030_clear_set_boot_bci(boot_bci, 0); + + /* + * For ichg_eoc, the hardware only supports reg values matching + * 100XXXX000, and requires the XXXX be stored in the high nibble + * of TWL4030_BCIMFTH8. + */ + reg = ua2regval(bci->ichg_eoc, cgain); + if (reg > 0x278) + reg = 0x278; + if (reg < 0x200) + reg = 0x200; + reg = (reg >> 3) & 0xf; + fullreg = reg << 4; + + /* + * For ichg_lo, reg value must match 10XXXX0000. + * XXXX is stored in low nibble of TWL4030_BCIMFTH8. + */ + reg = ua2regval(bci->ichg_lo, cgain); + if (reg > 0x2F0) + reg = 0x2F0; + if (reg < 0x200) + reg = 0x200; + reg = (reg >> 4) & 0xf; + fullreg |= reg; + + /* ichg_eoc and ichg_lo live in same register */ + status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg); + if (status < 0) + return status; + if (oldreg != fullreg) { + status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4, + TWL4030_BCIMFKEY); + if (status < 0) + return status; + twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + fullreg, TWL4030_BCIMFTH8); + } + + /* ichg_hi threshold must be 1XXXX01100 (I think) */ + reg = ua2regval(bci->ichg_hi, cgain); + if (reg > 0x3E0) + reg = 0x3E0; + if (reg < 0x200) + reg = 0x200; + fullreg = (reg >> 5) & 0xF; + fullreg <<= 4; + status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg); + if (status < 0) + return status; + if ((oldreg & 0xF0) != fullreg) { + fullreg |= (oldreg & 0x0F); + status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7, + TWL4030_BCIMFKEY); + if (status < 0) + return status; + twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + fullreg, TWL4030_BCIMFTH9); + } + + /* + * And finally, set the current. This is stored in + * two registers. + */ + reg = ua2regval(bci->cur, cgain); + /* we have only 10 bits */ + if (reg > 0x3ff) + reg = 0x3ff; + status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg); + if (status < 0) + return status; + cur_reg = oldreg; + status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg); + if (status < 0) + return status; + cur_reg |= oldreg << 8; + if (reg != oldreg) { + /* disable write protection for one write access for + * BCIIREF */ + status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7, + TWL4030_BCIMFKEY); + if (status < 0) + return status; + status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + (reg & 0x100) ? 3 : 2, + TWL4030_BCIIREF2); + if (status < 0) + return status; + /* disable write protection for one write access for + * BCIIREF */ + status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7, + TWL4030_BCIMFKEY); + if (status < 0) + return status; + status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + reg & 0xff, + TWL4030_BCIIREF1); + } + if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) { + /* Flip CGAIN and re-enable charging */ + bcictl1 ^= TWL4030_CGAIN; + twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + bcictl1, TWL4030_BCICTL1); + twl4030_clear_set_boot_bci(0, boot_bci); + } + return 0; +} + /* * Enable/Disable USB Charge functionality. */ @@ -219,14 +370,6 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) int ret; if (enable && !IS_ERR_OR_NULL(bci->transceiver)) { - /* - * Until we can find out what current the device can provide, - * require a module param to enable USB charging. - */ - if (!allow_usb) { - dev_warn(bci->dev, "USB charging is disabled.\n"); - return -EACCES; - } /* Need to keep phy powered */ if (!bci->usb_enabled) { @@ -578,6 +721,14 @@ static int twl4030_bci_probe(struct platform_device *pdev) if (!pdata) pdata = twl4030_bci_parse_dt(&pdev->dev); + bci->ichg_eoc = 80100; /* Stop charging when current drops to here */ + bci->ichg_lo = 241000; /* Low threshold */ + bci->ichg_hi = 500000; /* High threshold */ + if (allow_usb) + bci->cur = 500000; /* 500mA */ + else + bci->cur = 100000; /* 100mA */ + bci->dev = &pdev->dev; bci->irq_chg = platform_get_irq(pdev, 0); bci->irq_bci = platform_get_irq(pdev, 1); @@ -657,6 +808,7 @@ static int twl4030_bci_probe(struct platform_device *pdev) if (ret < 0) dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); + twl4030_charger_update_current(bci); twl4030_charger_enable_ac(true); if (!IS_ERR_OR_NULL(bci->transceiver)) twl4030_bci_usb_ncb(&bci->usb_nb, From e4ae537e0482e99eeaa5373d39932fe65a477c21 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 25/35] twl4030_charger: distinguish between USB current and 'AC' current The twl4030 charger has two current sources, 'USB' and 'AC' (presumably "Accessory Charger" because it isn't Alternating Current). If 'AC' is providing current, we should set the current limit differently to when it isn't (and so USB is used). So split 'cur' into 'usb_cur' and 'ac_cur' and use accordingly. Now we must review the current setting on any interrupt or USB event which might indicate that the charger-source has changed. Acked-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 36 ++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 3b7cc631bb8a..982675df21b7 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -22,6 +22,7 @@ #include #include #include +#include #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG 0x08 @@ -101,10 +102,13 @@ struct twl4030_bci { int usb_enabled; /* - * ichg values in uA. If any are 'large', we set CGAIN to - * '1' which doubles the range for half the precision. + * ichg_* and *_cur values in uA. If any are 'large', we set + * CGAIN to '1' which doubles the range for half the + * precision. */ - unsigned int ichg_eoc, ichg_lo, ichg_hi, cur; + unsigned int ichg_eoc, ichg_lo, ichg_hi; + unsigned int usb_cur, ac_cur; + bool ac_is_active; unsigned long event; }; @@ -225,11 +229,24 @@ static int ua2regval(int ua, bool cgain) static int twl4030_charger_update_current(struct twl4030_bci *bci) { int status; + int cur; unsigned reg, cur_reg; u8 bcictl1, oldreg, fullreg; bool cgain = false; u8 boot_bci; + /* + * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11) + * and AC is enabled, set current for 'ac' + */ + if (twl4030_get_madc_conversion(11) > 4500) { + cur = bci->ac_cur; + bci->ac_is_active = true; + } else { + cur = bci->usb_cur; + bci->ac_is_active = false; + } + /* First, check thresholds and see if cgain is needed */ if (bci->ichg_eoc >= 200000) cgain = true; @@ -237,7 +254,7 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) cgain = true; if (bci->ichg_hi >= 820000) cgain = true; - if (bci->cur > 852000) + if (cur > 852000) cgain = true; status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); @@ -318,7 +335,7 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) * And finally, set the current. This is stored in * two registers. */ - reg = ua2regval(bci->cur, cgain); + reg = ua2regval(cur, cgain); /* we have only 10 bits */ if (reg > 0x3ff) reg = 0x3ff; @@ -371,6 +388,8 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) if (enable && !IS_ERR_OR_NULL(bci->transceiver)) { + twl4030_charger_update_current(bci); + /* Need to keep phy powered */ if (!bci->usb_enabled) { pm_runtime_get_sync(bci->transceiver->dev); @@ -463,6 +482,7 @@ static irqreturn_t twl4030_charger_interrupt(int irq, void *arg) struct twl4030_bci *bci = arg; dev_dbg(bci->dev, "CHG_PRES irq\n"); + twl4030_charger_update_current(bci); power_supply_changed(bci->ac); power_supply_changed(bci->usb); @@ -495,6 +515,7 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) power_supply_changed(bci->ac); power_supply_changed(bci->usb); } + twl4030_charger_update_current(bci); /* various monitoring events, for now we just log them here */ if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1)) @@ -724,10 +745,11 @@ static int twl4030_bci_probe(struct platform_device *pdev) bci->ichg_eoc = 80100; /* Stop charging when current drops to here */ bci->ichg_lo = 241000; /* Low threshold */ bci->ichg_hi = 500000; /* High threshold */ + bci->ac_cur = 500000; /* 500mA */ if (allow_usb) - bci->cur = 500000; /* 500mA */ + bci->usb_cur = 500000; /* 500mA */ else - bci->cur = 100000; /* 100mA */ + bci->usb_cur = 100000; /* 100mA */ bci->dev = &pdev->dev; bci->irq_chg = platform_get_irq(pdev, 0); From aca3c3546396b305fff79f09883cff48a908aac0 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 26/35] twl4030_charger: allow max_current to be managed via sysfs. 'max_current' sysfs attributes are created which allow the max to be set. Whenever a current source changes, the default is restored. This will be followed by a uevent, so user-space can decide to update again. Acked-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- .../ABI/testing/sysfs-class-power-twl4030 | 15 ++++ drivers/power/twl4030_charger.c | 72 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-power-twl4030 diff --git a/Documentation/ABI/testing/sysfs-class-power-twl4030 b/Documentation/ABI/testing/sysfs-class-power-twl4030 new file mode 100644 index 000000000000..0331bba4605d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-power-twl4030 @@ -0,0 +1,15 @@ +What: /sys/class/power_supply/twl4030_ac/max_current + /sys/class/power_supply/twl4030_usb/max_current +Description: + Read/Write limit on current which may + be drawn from the ac (Accessory Charger) or + USB port. + + Value is in micro-Amps. + + Value is set automatically to an appropriate + value when a cable is plugged or unplugged. + + Value can the set by writing to the attribute. + The change will only persist until the next + plug event. These event are reported via udev. diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 982675df21b7..b0a50adebfda 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -482,6 +482,8 @@ static irqreturn_t twl4030_charger_interrupt(int irq, void *arg) struct twl4030_bci *bci = arg; dev_dbg(bci->dev, "CHG_PRES irq\n"); + /* reset current on each 'plug' event */ + bci->ac_cur = 500000; twl4030_charger_update_current(bci); power_supply_changed(bci->ac); power_supply_changed(bci->usb); @@ -536,6 +538,63 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) return IRQ_HANDLED; } +/* + * Provide "max_current" attribute in sysfs. + */ +static ssize_t +twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct twl4030_bci *bci = dev_get_drvdata(dev->parent); + int cur = 0; + int status = 0; + status = kstrtoint(buf, 10, &cur); + if (status) + return status; + if (cur < 0) + return -EINVAL; + if (dev == &bci->ac->dev) + bci->ac_cur = cur; + else + bci->usb_cur = cur; + + twl4030_charger_update_current(bci); + return n; +} + +/* + * sysfs max_current show + */ +static ssize_t twl4030_bci_max_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int status = 0; + int cur = -1; + u8 bcictl1; + struct twl4030_bci *bci = dev_get_drvdata(dev->parent); + + if (dev == &bci->ac->dev) { + if (!bci->ac_is_active) + cur = bci->ac_cur; + } else { + if (bci->ac_is_active) + cur = bci->usb_cur; + } + if (cur < 0) { + cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1); + if (cur < 0) + return cur; + status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); + if (status < 0) + return status; + cur = regval2ua(cur, bcictl1 & TWL4030_CGAIN); + } + return scnprintf(buf, PAGE_SIZE, "%u\n", cur); +} + +static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show, + twl4030_bci_max_current_store); + static void twl4030_bci_usb_work(struct work_struct *data) { struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work); @@ -558,6 +617,12 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, dev_dbg(bci->dev, "OTG notify %lu\n", val); + /* reset current on each 'plug' event */ + if (allow_usb) + bci->usb_cur = 500000; + else + bci->usb_cur = 100000; + bci->event = val; schedule_work(&bci->work); @@ -831,6 +896,11 @@ static int twl4030_bci_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); twl4030_charger_update_current(bci); + if (device_create_file(&bci->usb->dev, &dev_attr_max_current)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + if (device_create_file(&bci->ac->dev, &dev_attr_max_current)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + twl4030_charger_enable_ac(true); if (!IS_ERR_OR_NULL(bci->transceiver)) twl4030_bci_usb_ncb(&bci->usb_nb, @@ -855,6 +925,8 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl4030_charger_enable_usb(bci, false); twl4030_charger_enable_backup(0, 0); + device_remove_file(&bci->usb->dev, &dev_attr_max_current); + device_remove_file(&bci->ac->dev, &dev_attr_max_current); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR1A); From 22d4c33f7335ddf6deda26630c78650e133bfe72 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 27/35] twl4030_charger: enable manual enable/disable of usb charging. 'off' or 'auto' to /sys/class/power/twl4030_usb/mode will now enable or disable charging from USB port. Normally this is enabled on 'plug' and disabled on 'unplug'. Unplug will still disable charging. 'plug' will only enable it if 'auto' if selected. Acked-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- .../ABI/testing/sysfs-class-power-twl4030 | 11 ++++ drivers/power/twl4030_charger.c | 59 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-power-twl4030 b/Documentation/ABI/testing/sysfs-class-power-twl4030 index 0331bba4605d..40e0f919cbde 100644 --- a/Documentation/ABI/testing/sysfs-class-power-twl4030 +++ b/Documentation/ABI/testing/sysfs-class-power-twl4030 @@ -13,3 +13,14 @@ Description: Value can the set by writing to the attribute. The change will only persist until the next plug event. These event are reported via udev. + + +What: /sys/class/power_supply/twl4030_usb/mode +Description: + Changing mode for USB port. + Writing to this can disable charging. + + Possible values are: + "auto" - draw power as appropriate for detected + power source and battery status. + "off" - do not draw any power. diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index b0a50adebfda..6fa928ed3128 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -109,10 +109,16 @@ struct twl4030_bci { unsigned int ichg_eoc, ichg_lo, ichg_hi; unsigned int usb_cur, ac_cur; bool ac_is_active; + int usb_mode; /* charging mode requested */ +#define CHARGE_OFF 0 +#define CHARGE_AUTO 1 unsigned long event; }; +/* strings for 'usb_mode' values */ +static char *modes[] = { "off", "auto" }; + /* * clear and set bits on an given register on a given module */ @@ -386,6 +392,8 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) { int ret; + if (bci->usb_mode == CHARGE_OFF) + enable = false; if (enable && !IS_ERR_OR_NULL(bci->transceiver)) { twl4030_charger_update_current(bci); @@ -629,6 +637,53 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, return NOTIFY_OK; } +/* + * sysfs charger enabled store + */ +static ssize_t +twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct twl4030_bci *bci = dev_get_drvdata(dev->parent); + int mode; + int status; + + if (sysfs_streq(buf, modes[0])) + mode = 0; + else if (sysfs_streq(buf, modes[1])) + mode = 1; + else + return -EINVAL; + twl4030_charger_enable_usb(bci, false); + bci->usb_mode = mode; + status = twl4030_charger_enable_usb(bci, true); + return (status == 0) ? n : status; +} + +/* + * sysfs charger enabled show + */ +static ssize_t +twl4030_bci_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl4030_bci *bci = dev_get_drvdata(dev->parent); + int len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(modes); i++) + if (bci->usb_mode == i) + len += snprintf(buf+len, PAGE_SIZE-len, + "[%s] ", modes[i]); + else + len += snprintf(buf+len, PAGE_SIZE-len, + "%s ", modes[i]); + buf[len-1] = '\n'; + return len; +} +static DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show, + twl4030_bci_mode_store); + static int twl4030_charger_get_current(void) { int curr; @@ -815,6 +870,7 @@ static int twl4030_bci_probe(struct platform_device *pdev) bci->usb_cur = 500000; /* 500mA */ else bci->usb_cur = 100000; /* 100mA */ + bci->usb_mode = CHARGE_AUTO; bci->dev = &pdev->dev; bci->irq_chg = platform_get_irq(pdev, 0); @@ -898,6 +954,8 @@ static int twl4030_bci_probe(struct platform_device *pdev) twl4030_charger_update_current(bci); if (device_create_file(&bci->usb->dev, &dev_attr_max_current)) dev_warn(&pdev->dev, "could not create sysfs file\n"); + if (device_create_file(&bci->usb->dev, &dev_attr_mode)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); if (device_create_file(&bci->ac->dev, &dev_attr_max_current)) dev_warn(&pdev->dev, "could not create sysfs file\n"); @@ -926,6 +984,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl4030_charger_enable_backup(0, 0); device_remove_file(&bci->usb->dev, &dev_attr_max_current); + device_remove_file(&bci->usb->dev, &dev_attr_mode); device_remove_file(&bci->ac->dev, &dev_attr_max_current); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, From 7f4a633d21331155ee06c5ee44749ed35a3a3cc5 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 28/35] twl4030_charger: add software controlled linear charging mode. Add a 'continuous' option for usb charging which enables the "linear" charging mode of the twl4030. Linear charging does a good job with not-so-reliable power sources. Auto mode does not work well as it switches off when voltage drops momentarily. Care must be taken not to over-charge. It was used with a bike hub dynamo since a year or so. In that case there are automatically charging stops when the cyclist needs a break. Original-by: Andreas Kemnade Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- .../ABI/testing/sysfs-class-power-twl4030 | 9 +++ drivers/power/twl4030_charger.c | 55 +++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-power-twl4030 b/Documentation/ABI/testing/sysfs-class-power-twl4030 index 40e0f919cbde..e8baa36aaa2b 100644 --- a/Documentation/ABI/testing/sysfs-class-power-twl4030 +++ b/Documentation/ABI/testing/sysfs-class-power-twl4030 @@ -24,3 +24,12 @@ Description: "auto" - draw power as appropriate for detected power source and battery status. "off" - do not draw any power. + "continuous" + - activate mode described as "linear" in + TWL data sheets. This uses whatever + current is available and doesn't switch off + when voltage drops. + + This is useful for unstable power sources + such as bicycle dynamo, but care should + be taken that battery is not over-charged. diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 6fa928ed3128..de5430deaf23 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -24,6 +24,8 @@ #include #include +#define TWL4030_BCIMDEN 0x00 +#define TWL4030_BCIMDKEY 0x01 #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG 0x08 #define TWL4030_BCIVAC 0x0a @@ -35,13 +37,16 @@ #define TWL4030_BCIIREF1 0x27 #define TWL4030_BCIIREF2 0x28 #define TWL4030_BCIMFKEY 0x11 +#define TWL4030_BCIMFEN3 0x14 #define TWL4030_BCIMFTH8 0x1d #define TWL4030_BCIMFTH9 0x1e +#define TWL4030_BCIWDKEY 0x21 #define TWL4030_BCIMFSTS1 0x01 #define TWL4030_BCIAUTOWEN BIT(5) #define TWL4030_CONFIG_DONE BIT(4) +#define TWL4030_CVENAC BIT(2) #define TWL4030_BCIAUTOUSB BIT(1) #define TWL4030_BCIAUTOAC BIT(0) #define TWL4030_CGAIN BIT(5) @@ -112,12 +117,13 @@ struct twl4030_bci { int usb_mode; /* charging mode requested */ #define CHARGE_OFF 0 #define CHARGE_AUTO 1 +#define CHARGE_LINEAR 2 unsigned long event; }; /* strings for 'usb_mode' values */ -static char *modes[] = { "off", "auto" }; +static char *modes[] = { "off", "auto", "continuous" }; /* * clear and set bits on an given register on a given module @@ -404,16 +410,42 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) bci->usb_enabled = 1; } - /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ - ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB); - if (ret < 0) - return ret; + if (bci->usb_mode == CHARGE_AUTO) + /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ + ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB); /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0, TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); + if (bci->usb_mode == CHARGE_LINEAR) { + twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC|TWL4030_CVENAC, 0); + /* Watch dog key: WOVF acknowledge */ + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x33, + TWL4030_BCIWDKEY); + /* 0x24 + EKEY6: off mode */ + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a, + TWL4030_BCIMDKEY); + /* EKEY2: Linear charge: USB path */ + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x26, + TWL4030_BCIMDKEY); + /* WDKEY5: stop watchdog count */ + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf3, + TWL4030_BCIWDKEY); + /* enable MFEN3 access */ + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x9c, + TWL4030_BCIMFKEY); + /* ICHGEOCEN - end-of-charge monitor (current < 80mA) + * (charging continues) + * ICHGLOWEN - current level monitor (charge continues) + * don't monitor over-current or heat save + */ + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf0, + TWL4030_BCIMFEN3); + } } else { ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); + ret |= twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a, + TWL4030_BCIMDKEY); if (bci->usb_enabled) { pm_runtime_mark_last_busy(bci->transceiver->dev); pm_runtime_put_autosuspend(bci->transceiver->dev); @@ -652,6 +684,8 @@ twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr, mode = 0; else if (sysfs_streq(buf, modes[1])) mode = 1; + else if (sysfs_streq(buf, modes[2])) + mode = 2; else return -EINVAL; twl4030_charger_enable_usb(bci, false); @@ -750,6 +784,17 @@ static int twl4030_bci_get_property(struct power_supply *psy, is_charging = state & TWL4030_MSTATEC_USB; else is_charging = state & TWL4030_MSTATEC_AC; + if (!is_charging) { + u8 s; + twl4030_bci_read(TWL4030_BCIMDEN, &s); + if (psy->desc->type == POWER_SUPPLY_TYPE_USB) + is_charging = s & 1; + else + is_charging = s & 2; + if (is_charging) + /* A little white lie */ + state = TWL4030_MSTATEC_QUICK1; + } switch (psp) { case POWER_SUPPLY_PROP_STATUS: From b04b908d8a2901c2cc59db87defd9c08bd4293cc Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 29/35] twl4030_charger: add ac/mode to match usb/mode This allows AC charging to be turned off, much like usb charging. "continuous" mode is not available though. Acked-by: Pavel Machek Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- .../ABI/testing/sysfs-class-power-twl4030 | 10 ++++++ drivers/power/twl4030_charger.c | 35 ++++++++++++++----- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-power-twl4030 b/Documentation/ABI/testing/sysfs-class-power-twl4030 index e8baa36aaa2b..be26af0f1895 100644 --- a/Documentation/ABI/testing/sysfs-class-power-twl4030 +++ b/Documentation/ABI/testing/sysfs-class-power-twl4030 @@ -33,3 +33,13 @@ Description: This is useful for unstable power sources such as bicycle dynamo, but care should be taken that battery is not over-charged. + +What: /sys/class/power_supply/twl4030_ac/mode +Description: + Changing mode for 'ac' port. + Writing to this can disable charging. + + Possible values are: + "auto" - draw power as appropriate for detected + power source and battery status. + "off" - do not draw any power. diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index de5430deaf23..68117ad23564 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -114,7 +114,7 @@ struct twl4030_bci { unsigned int ichg_eoc, ichg_lo, ichg_hi; unsigned int usb_cur, ac_cur; bool ac_is_active; - int usb_mode; /* charging mode requested */ + int usb_mode, ac_mode; /* charging mode requested */ #define CHARGE_OFF 0 #define CHARGE_AUTO 1 #define CHARGE_LINEAR 2 @@ -459,10 +459,13 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) /* * Enable/Disable AC Charge funtionality. */ -static int twl4030_charger_enable_ac(bool enable) +static int twl4030_charger_enable_ac(struct twl4030_bci *bci, bool enable) { int ret; + if (bci->ac_mode == CHARGE_OFF) + enable = false; + if (enable) ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC); else @@ -688,9 +691,17 @@ twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr, mode = 2; else return -EINVAL; - twl4030_charger_enable_usb(bci, false); - bci->usb_mode = mode; - status = twl4030_charger_enable_usb(bci, true); + if (dev == &bci->ac->dev) { + if (mode == 2) + return -EINVAL; + twl4030_charger_enable_ac(bci, false); + bci->ac_mode = mode; + status = twl4030_charger_enable_ac(bci, true); + } else { + twl4030_charger_enable_usb(bci, false); + bci->usb_mode = mode; + status = twl4030_charger_enable_usb(bci, true); + } return (status == 0) ? n : status; } @@ -704,9 +715,13 @@ twl4030_bci_mode_show(struct device *dev, struct twl4030_bci *bci = dev_get_drvdata(dev->parent); int len = 0; int i; + int mode = bci->usb_mode; + + if (dev == &bci->ac->dev) + mode = bci->ac_mode; for (i = 0; i < ARRAY_SIZE(modes); i++) - if (bci->usb_mode == i) + if (mode == i) len += snprintf(buf+len, PAGE_SIZE-len, "[%s] ", modes[i]); else @@ -916,6 +931,7 @@ static int twl4030_bci_probe(struct platform_device *pdev) else bci->usb_cur = 100000; /* 100mA */ bci->usb_mode = CHARGE_AUTO; + bci->ac_mode = CHARGE_AUTO; bci->dev = &pdev->dev; bci->irq_chg = platform_get_irq(pdev, 0); @@ -1001,10 +1017,12 @@ static int twl4030_bci_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "could not create sysfs file\n"); if (device_create_file(&bci->usb->dev, &dev_attr_mode)) dev_warn(&pdev->dev, "could not create sysfs file\n"); + if (device_create_file(&bci->ac->dev, &dev_attr_mode)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); if (device_create_file(&bci->ac->dev, &dev_attr_max_current)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - twl4030_charger_enable_ac(true); + twl4030_charger_enable_ac(bci, true); if (!IS_ERR_OR_NULL(bci->transceiver)) twl4030_bci_usb_ncb(&bci->usb_nb, bci->transceiver->last_event, @@ -1024,13 +1042,14 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) { struct twl4030_bci *bci = platform_get_drvdata(pdev); - twl4030_charger_enable_ac(false); + twl4030_charger_enable_ac(bci, false); twl4030_charger_enable_usb(bci, false); twl4030_charger_enable_backup(0, 0); device_remove_file(&bci->usb->dev, &dev_attr_max_current); device_remove_file(&bci->usb->dev, &dev_attr_mode); device_remove_file(&bci->ac->dev, &dev_attr_max_current); + device_remove_file(&bci->ac->dev, &dev_attr_mode); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR1A); From 21ae40404f3ca66fb37dd26e7b67eb109f2453ab Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 30 Jul 2015 10:11:24 +1000 Subject: [PATCH 30/35] twl4030_charger: Increase current carefully while watching voltage. The USB Battery Charging spec (BC1.2) suggests a dedicated charging port can deliver from 0.5 to 5.0A at between 4.75 and 5.25 volts. To choose the "correct" current voltage setting requires a trial and error approach: try to draw current and see if the voltage drops too low. Even with a configured Standard Downstream Port, it may not be possible to reliably pull 500mA - depending on cable quality and source quality I have reports of charging failure due to the voltage dropping too low. To address both these concerns, this patch introduce incremental current setting. The current pull from VBUS is increased in steps of 20mA every 100ms until the target is reached or until the measure voltage drops below 4.75V. If the voltage does go too low, the target current is reduced by 20mA and kept there. This applies to currents selected automatically, or to values set via sysfs. So setting a large value will cause the maximum available to be used - up to the limit of 1.7A imposed by the hardware. Signed-off-by: NeilBrown Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 67 ++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 68117ad23564..2c537ee11bbe 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -119,6 +119,18 @@ struct twl4030_bci { #define CHARGE_AUTO 1 #define CHARGE_LINEAR 2 + /* When setting the USB current we slowly increase the + * requested current until target is reached or the voltage + * drops below 4.75V. In the latter case we step back one + * step. + */ + unsigned int usb_cur_target; + struct delayed_work current_worker; +#define USB_CUR_STEP 20000 /* 20mA at a time */ +#define USB_MIN_VOLT 4750000 /* 4.75V */ +#define USB_CUR_DELAY msecs_to_jiffies(100) +#define USB_MAX_CURRENT 1700000 /* TWL4030 caps at 1.7A */ + unsigned long event; }; @@ -257,6 +269,12 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) } else { cur = bci->usb_cur; bci->ac_is_active = false; + if (cur > bci->usb_cur_target) { + cur = bci->usb_cur_target; + bci->usb_cur = cur; + } + if (cur < bci->usb_cur_target) + schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY); } /* First, check thresholds and see if cgain is needed */ @@ -391,6 +409,41 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) return 0; } +static int twl4030_charger_get_current(void); + +static void twl4030_current_worker(struct work_struct *data) +{ + int v, curr; + int res; + struct twl4030_bci *bci = container_of(data, struct twl4030_bci, + current_worker.work); + + res = twl4030bci_read_adc_val(TWL4030_BCIVBUS); + if (res < 0) + v = 0; + else + /* BCIVBUS uses ADCIN8, 7/1023 V/step */ + v = res * 6843; + curr = twl4030_charger_get_current(); + + dev_dbg(bci->dev, "v=%d cur=%d limit=%d target=%d\n", v, curr, + bci->usb_cur, bci->usb_cur_target); + + if (v < USB_MIN_VOLT) { + /* Back up and stop adjusting. */ + bci->usb_cur -= USB_CUR_STEP; + bci->usb_cur_target = bci->usb_cur; + } else if (bci->usb_cur >= bci->usb_cur_target || + bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) { + /* Reached target and voltage is OK - stop */ + return; + } else { + bci->usb_cur += USB_CUR_STEP; + schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY); + } + twl4030_charger_update_current(bci); +} + /* * Enable/Disable USB Charge functionality. */ @@ -451,6 +504,7 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) pm_runtime_put_autosuspend(bci->transceiver->dev); bci->usb_enabled = 0; } + bci->usb_cur = 0; } return ret; @@ -599,7 +653,7 @@ twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr, if (dev == &bci->ac->dev) bci->ac_cur = cur; else - bci->usb_cur = cur; + bci->usb_cur_target = cur; twl4030_charger_update_current(bci); return n; @@ -621,7 +675,7 @@ static ssize_t twl4030_bci_max_current_show(struct device *dev, cur = bci->ac_cur; } else { if (bci->ac_is_active) - cur = bci->usb_cur; + cur = bci->usb_cur_target; } if (cur < 0) { cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1); @@ -662,9 +716,9 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, /* reset current on each 'plug' event */ if (allow_usb) - bci->usb_cur = 500000; + bci->usb_cur_target = 500000; else - bci->usb_cur = 100000; + bci->usb_cur_target = 100000; bci->event = val; schedule_work(&bci->work); @@ -927,9 +981,9 @@ static int twl4030_bci_probe(struct platform_device *pdev) bci->ichg_hi = 500000; /* High threshold */ bci->ac_cur = 500000; /* 500mA */ if (allow_usb) - bci->usb_cur = 500000; /* 500mA */ + bci->usb_cur_target = 500000; /* 500mA */ else - bci->usb_cur = 100000; /* 100mA */ + bci->usb_cur_target = 100000; /* 100mA */ bci->usb_mode = CHARGE_AUTO; bci->ac_mode = CHARGE_AUTO; @@ -980,6 +1034,7 @@ static int twl4030_bci_probe(struct platform_device *pdev) } INIT_WORK(&bci->work, twl4030_bci_usb_work); + INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker); bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; if (bci->dev->of_node) { From 63369b2b99d718ca2777bf8d71360de75888f05b Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 30 Jul 2015 18:18:33 +0200 Subject: [PATCH 31/35] power: Export I2C module alias information in missing drivers The I2C core always reports the MODALIAS uevent as "i2c: Signed-off-by: Sebastian Reichel --- drivers/power/bq24190_charger.c | 1 + drivers/power/rt5033_battery.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index 703ebecaf38b..bb6d85bd6e56 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -1515,6 +1515,7 @@ static const struct i2c_device_id bq24190_i2c_ids[] = { { "bq24190", BQ24190_REG_VPRS_PN_24190 }, { }, }; +MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); #ifdef CONFIG_OF static const struct of_device_id bq24190_of_match[] = { diff --git a/drivers/power/rt5033_battery.c b/drivers/power/rt5033_battery.c index a7a6877b4e16..bcdd83048492 100644 --- a/drivers/power/rt5033_battery.c +++ b/drivers/power/rt5033_battery.c @@ -165,7 +165,7 @@ static const struct i2c_device_id rt5033_battery_id[] = { { "rt5033-battery", }, { } }; -MODULE_DEVICE_TABLE(platform, rt5033_battery_id); +MODULE_DEVICE_TABLE(i2c, rt5033_battery_id); static struct i2c_driver rt5033_battery_driver = { .driver = { From 1e8b82c1ff3bc52c5e4265d66fe5c6da9a66e2e6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 2 Aug 2015 11:09:50 +0200 Subject: [PATCH 32/35] power: Allow compile test of GPIO consumers if !GPIOLIB The GPIO subsystem provides dummy GPIO consumer functions if GPIOLIB is not enabled. Hence drivers that depend on GPIOLIB, but use GPIO consumer functionality only, can still be compiled if GPIOLIB is not enabled. Relax the dependency on GPIOLIB if COMPILE_TEST is enabled, where appropriate. Signed-off-by: Geert Uytterhoeven Acked-by: Linus Walleij Signed-off-by: Sebastian Reichel --- drivers/power/Kconfig | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 08beeed5485d..f8758d6febf8 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -333,7 +333,7 @@ config CHARGER_LP8788 config CHARGER_GPIO tristate "GPIO charger" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y to include support for chargers which report their online status through a GPIO pin. @@ -391,26 +391,30 @@ config CHARGER_BQ2415X config CHARGER_BQ24190 tristate "TI BQ24190 battery charger driver" - depends on I2C && GPIOLIB + depends on I2C + depends on GPIOLIB || COMPILE_TEST help Say Y to enable support for the TI BQ24190 battery charger. config CHARGER_BQ24257 tristate "TI BQ24257 battery charger driver" - depends on I2C && GPIOLIB + depends on I2C + depends on GPIOLIB || COMPILE_TEST depends on REGMAP_I2C help Say Y to enable support for the TI BQ24257 battery charger. config CHARGER_BQ24735 tristate "TI BQ24735 battery charger support" - depends on I2C && GPIOLIB + depends on I2C + depends on GPIOLIB || COMPILE_TEST help Say Y to enable support for the TI BQ24735 battery charger. config CHARGER_BQ25890 tristate "TI BQ25890 battery charger driver" - depends on I2C && GPIOLIB + depends on I2C + depends on GPIOLIB || COMPILE_TEST select REGMAP_I2C help Say Y to enable support for the TI BQ25890 battery charger. @@ -462,7 +466,8 @@ config BATTERY_RT5033 config CHARGER_RT9455 tristate "Richtek RT9455 battery charger driver" - depends on I2C && GPIOLIB + depends on I2C + depends on GPIOLIB || COMPILE_TEST select REGMAP_I2C help Say Y to enable support for Richtek RT9455 battery charger. From c9f85a90d41476aca2d3b60f98ab87d7ae1b8612 Mon Sep 17 00:00:00 2001 From: Andreas Dannenberg Date: Tue, 4 Aug 2015 11:36:17 -0500 Subject: [PATCH 33/35] power: bq24190_charger: Fix charge type sysfs property Access to the BQ24190's configurable charge type property (none, trickle, fast) is being masked by an incorrect power_supply_property entry. After applying this patch a new 'charge_type' property will appear in the bq24190-charger sysfs folder backed up by getters/setters already present in the driver. Signed-off-by: Andreas Dannenberg Acked-by: Dan Murphy Acked-by: Andrew F. Davis Signed-off-by: Sebastian Reichel --- drivers/power/bq24190_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index bb6d85bd6e56..469a452cbe10 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -902,7 +902,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, } static enum power_supply_property bq24190_charger_properties[] = { - POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, From b49d15d138aa4632f35a2db93dee775d920f3653 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 7 Aug 2015 13:45:25 +1000 Subject: [PATCH 34/35] twl4030_charger: fix compile error when TWL4030_MADC not available. We can only use the madc to check for 'ac' availability if the madc has been compiled in. If not: assume always using USB. Reported-by: Tony Lindgren Signed-off-by: NeilBrown Acked-by: Tony Lindgren Signed-off-by: Sebastian Reichel --- drivers/power/twl4030_charger.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 2c537ee11bbe..f4f2c1f76c32 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -91,6 +91,21 @@ #define TWL4030_MSTATEC_COMPLETE1 0x0b #define TWL4030_MSTATEC_COMPLETE4 0x0e +#if IS_ENABLED(CONFIG_TWL4030_MADC) +/* + * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11) + * then AC is available. + */ +static inline int ac_available(void) +{ + return twl4030_get_madc_conversion(11) > 4500; +} +#else +static inline int ac_available(void) +{ + return 0; +} +#endif static bool allow_usb; module_param(allow_usb, bool, 0644); MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current"); @@ -263,7 +278,7 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11) * and AC is enabled, set current for 'ac' */ - if (twl4030_get_madc_conversion(11) > 4500) { + if (ac_available()) { cur = bci->ac_cur; bci->ac_is_active = true; } else { From b68c3161430a4c7c0a001e658188bfea6a2fe5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 11 Aug 2015 13:22:19 +0200 Subject: [PATCH 35/35] bq2415x_charger: Allow to load and use driver even if notify device is not registered yet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver bq2415x_charger works also without notify power supply device for charger detection. But when charger detection is specified in DT, then bq2415x_charger refused to loaded with -EPROBE_DEFER. This patch rewrites code so that notify device for charger detection is checked when power supply event is received and not when registering power supply device. So this patch allows to use bq2415x_charger driver also when kernel is compiled without driver for notify power supply device. Now after this patch scheduled workqueue is called after INIT_DELAYED_WORK, so it also fix problem when scheduled workqueue was called before init. Signed-off-by: Pali Rohár Signed-off-by: Sebastian Reichel --- drivers/power/bq2415x_charger.c | 155 +++++++++++++++++--------------- 1 file changed, 85 insertions(+), 70 deletions(-) diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c index e98dcb661cc9..ec212b5be755 100644 --- a/drivers/power/bq2415x_charger.c +++ b/drivers/power/bq2415x_charger.c @@ -170,7 +170,7 @@ struct bq2415x_device { struct power_supply *charger; struct power_supply_desc charger_desc; struct delayed_work work; - struct power_supply *notify_psy; + struct device_node *notify_node; struct notifier_block nb; enum bq2415x_mode reported_mode;/* mode reported by hook function */ enum bq2415x_mode mode; /* currently configured mode */ @@ -792,31 +792,9 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode) } -static int bq2415x_notifier_call(struct notifier_block *nb, - unsigned long val, void *v) +static bool bq2415x_update_reported_mode(struct bq2415x_device *bq, int mA) { - struct bq2415x_device *bq = - container_of(nb, struct bq2415x_device, nb); - struct power_supply *psy = v; enum bq2415x_mode mode; - union power_supply_propval prop; - int ret; - int mA; - - if (val != PSY_EVENT_PROP_CHANGED) - return NOTIFY_OK; - - if (psy != bq->notify_psy) - return NOTIFY_OK; - - dev_dbg(bq->dev, "notifier call was called\n"); - - ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, - &prop); - if (ret != 0) - return NOTIFY_OK; - - mA = prop.intval; if (mA == 0) mode = BQ2415X_MODE_OFF; @@ -828,9 +806,43 @@ static int bq2415x_notifier_call(struct notifier_block *nb, mode = BQ2415X_MODE_DEDICATED_CHARGER; if (bq->reported_mode == mode) - return NOTIFY_OK; + return false; bq->reported_mode = mode; + return true; +} + +static int bq2415x_notifier_call(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct bq2415x_device *bq = + container_of(nb, struct bq2415x_device, nb); + struct power_supply *psy = v; + union power_supply_propval prop; + int ret; + + if (val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + /* Ignore event if it was not send by notify_node/notify_device */ + if (bq->notify_node) { + if (!psy->dev.parent || + psy->dev.parent->of_node != bq->notify_node) + return NOTIFY_OK; + } else if (bq->init_data.notify_device) { + if (strcmp(psy->desc->name, bq->init_data.notify_device) != 0) + return NOTIFY_OK; + } + + dev_dbg(bq->dev, "notifier call was called\n"); + + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, + &prop); + if (ret != 0) + return NOTIFY_OK; + + if (!bq2415x_update_reported_mode(bq, prop.intval)) + return NOTIFY_OK; /* if automode is not enabled do not tell about reported_mode */ if (bq->automode < 1) @@ -1536,6 +1548,8 @@ static int bq2415x_probe(struct i2c_client *client, struct device_node *np = client->dev.of_node; struct bq2415x_platform_data *pdata = client->dev.platform_data; const struct acpi_device_id *acpi_id = NULL; + struct power_supply *notify_psy = NULL; + union power_supply_propval prop; if (!np && !pdata && !ACPI_HANDLE(&client->dev)) { dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n"); @@ -1569,25 +1583,6 @@ static int bq2415x_probe(struct i2c_client *client, goto error_2; } - if (np) { - bq->notify_psy = power_supply_get_by_phandle(np, - "ti,usb-charger-detection"); - - if (IS_ERR(bq->notify_psy)) { - dev_info(&client->dev, - "no 'ti,usb-charger-detection' property (err=%ld)\n", - PTR_ERR(bq->notify_psy)); - bq->notify_psy = NULL; - } else if (!bq->notify_psy) { - ret = -EPROBE_DEFER; - goto error_2; - } - } else if (pdata && pdata->notify_device) { - bq->notify_psy = power_supply_get_by_name(pdata->notify_device); - } else { - bq->notify_psy = NULL; - } - i2c_set_clientdata(client, bq); bq->id = num; @@ -1607,32 +1602,35 @@ static int bq2415x_probe(struct i2c_client *client, "ti,current-limit", &bq->init_data.current_limit); if (ret) - goto error_3; + goto error_2; ret = device_property_read_u32(bq->dev, "ti,weak-battery-voltage", &bq->init_data.weak_battery_voltage); if (ret) - goto error_3; + goto error_2; ret = device_property_read_u32(bq->dev, "ti,battery-regulation-voltage", &bq->init_data.battery_regulation_voltage); if (ret) - goto error_3; + goto error_2; ret = device_property_read_u32(bq->dev, "ti,charge-current", &bq->init_data.charge_current); if (ret) - goto error_3; + goto error_2; ret = device_property_read_u32(bq->dev, "ti,termination-current", &bq->init_data.termination_current); if (ret) - goto error_3; + goto error_2; ret = device_property_read_u32(bq->dev, "ti,resistor-sense", &bq->init_data.resistor_sense); if (ret) - goto error_3; + goto error_2; + if (np) + bq->notify_node = of_parse_phandle(np, + "ti,usb-charger-detection", 0); } else { memcpy(&bq->init_data, pdata, sizeof(bq->init_data)); } @@ -1642,56 +1640,72 @@ static int bq2415x_probe(struct i2c_client *client, ret = bq2415x_power_supply_init(bq); if (ret) { dev_err(bq->dev, "failed to register power supply: %d\n", ret); - goto error_3; + goto error_2; } ret = bq2415x_sysfs_init(bq); if (ret) { dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret); - goto error_4; + goto error_3; } ret = bq2415x_set_defaults(bq); if (ret) { dev_err(bq->dev, "failed to set default values: %d\n", ret); - goto error_5; + goto error_4; } - if (bq->notify_psy) { + if (bq->notify_node || bq->init_data.notify_device) { bq->nb.notifier_call = bq2415x_notifier_call; ret = power_supply_reg_notifier(&bq->nb); if (ret) { dev_err(bq->dev, "failed to reg notifier: %d\n", ret); - goto error_6; + goto error_4; } - /* Query for initial reported_mode and set it */ - bq2415x_notifier_call(&bq->nb, PSY_EVENT_PROP_CHANGED, - bq->notify_psy); - bq2415x_set_mode(bq, bq->reported_mode); - bq->automode = 1; - dev_info(bq->dev, "automode enabled\n"); + dev_info(bq->dev, "automode supported, waiting for events\n"); } else { bq->automode = -1; dev_info(bq->dev, "automode not supported\n"); } + /* Query for initial reported_mode and set it */ + if (bq->nb.notifier_call) { + if (np) { + notify_psy = power_supply_get_by_phandle(np, + "ti,usb-charger-detection"); + if (IS_ERR(notify_psy)) + notify_psy = NULL; + } else if (bq->init_data.notify_device) { + notify_psy = power_supply_get_by_name( + bq->init_data.notify_device); + } + } + if (notify_psy) { + ret = power_supply_get_property(notify_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &prop); + power_supply_put(notify_psy); + + if (ret == 0) { + bq2415x_update_reported_mode(bq, prop.intval); + bq2415x_set_mode(bq, bq->reported_mode); + } + } + INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work); bq2415x_set_autotimer(bq, 1); dev_info(bq->dev, "driver registered\n"); return 0; -error_6: -error_5: - bq2415x_sysfs_exit(bq); error_4: - bq2415x_power_supply_exit(bq); + bq2415x_sysfs_exit(bq); error_3: - if (bq->notify_psy) - power_supply_put(bq->notify_psy); + bq2415x_power_supply_exit(bq); error_2: + if (bq->notify_node) + of_node_put(bq->notify_node); kfree(name); error_1: mutex_lock(&bq2415x_id_mutex); @@ -1707,10 +1721,11 @@ static int bq2415x_remove(struct i2c_client *client) { struct bq2415x_device *bq = i2c_get_clientdata(client); - if (bq->notify_psy) { + if (bq->nb.notifier_call) power_supply_unreg_notifier(&bq->nb); - power_supply_put(bq->notify_psy); - } + + if (bq->notify_node) + of_node_put(bq->notify_node); bq2415x_sysfs_exit(bq); bq2415x_power_supply_exit(bq);