Enhance twl4030_charger functionality. - V2
Subsystems (4)
cover Enhance twl4030_charger functionality. - V2
Hi Sebastian,
here is V2 of my patches to improve the twl4030 charger. They include improved documentation and removal of changes to the CVENAC bit which I didn't properly understand.
One patch needs to make a change in drivers/mfd, and that has been acked by Lee Jones.
Summary from before:
These patches make a number of improvements to twl4030_charger.
Some are just internal cleanups (e.g. use of devres). Others allow better control of charging through both manual and automatic means.
- the maximum current can be configured via sysfs.
- the charger will only draw that current if it can do so without the voltage dropping too much
- a 'continuous' mode is available which ignores voltage and just takes what it can (to be used with caution, but very useful in some circumstances).
- 'ac' and 'usb' power sources can be configured separately.
Thanks,
NeilBrown
---
NeilBrown (14):
twl4030_charger: use devm_request_threaded_irq
twl4030_charger: use devres for power_supply_register and kzalloc.
twl4030_charger: use runtime_pm to keep usb phy active while charging.
twl4030_charger: trust phy to determine when USB power is available.
twl4030_charger: split uA calculation into a function.
twl4030_charger: allow fine control of charger current.
twl4030_charger: distinguish between USB current and 'AC' current
twl4030_charger: allow max_current to be managed via sysfs.
twl4030_charger: only draw USB current as negotiated with host.
twl4030_charger: enable manual enable/disable of usb charging.
twl4030_charger: add software controlled linear charging mode.
twl4030_charger: add ac/mode to match usb/mode
twl4030_charger: Increase current carefully while watching voltage.
twl4030_charger: assume a 'charger' can supply maximum current.
.../ABI/testing/sysfs-class-power-twl4030 | 45 ++ drivers/mfd/twl-core.c | 9 drivers/power/twl4030_charger.c | 573 +++++++++++++++++--- 3 files changed, 536 insertions(+), 91 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-power-twl4030
--
Signature
PATCH 1/14 twl4030_charger: use devm_request_threaded_irq
From: NeilBrown <[email protected]>
This simplifies the error paths.
--- drivers/power/twl4030_charger.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-)
drivers/power/twl4030_charger.c +6 -12
@@ -610,21 +610,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);
@@ -647,7 +647,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 );
@@ -666,11 +666,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);
@@ -695,8 +691,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);
Re: [PATCH 01/14] twl4030_charger: use devm_request_threaded_irq
Hi,
Quoted 5 lines · NeilBrown
From: NeilBrown <[email protected]>
This simplifies the error paths.
Signed-off-by NeilBrown <[email protected]>
I queued this with Pavel's ACK after a rebase to current master:
http://git.infradead.org/battery-2.6.git/commit/1b7cac23419e1398eac4c28368da6671c9c4ce9d
-- Sebastian
PATCH 2/14 twl4030_charger: use devres for power_supply_register and kzalloc.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
Final allocations/registrations are now managed by devres.
--- drivers/power/twl4030_charger.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-)
drivers/power/twl4030_charger.c +9 -23
@@ -565,7 +565,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 ;
@@ -580,7 +580,7 @@ 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);
@@ -590,10 +590,10 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) bci->ac.num_properties =ARRAY_SIZE (twl4030_charger_props); bci->ac.get_property = twl4030_bci_get_property; - ret =power_supply_register (&pdev->dev, &bci->ac); + ret =devm_power_supply_register (&pdev->dev, &bci->ac);if (ret) {dev_err (&pdev->dev,"failed to register ac: %d\n" , ret); - goto fail_register_ac; +return ret; } bci->usb.name ="twl4030_usb" ;
@@ -604,10 +604,10 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) bci->usb_reg =regulator_get (bci->dev,"bci3v1" ); - ret =power_supply_register (&pdev->dev, &bci->usb); + ret =devm_power_supply_register (&pdev->dev, &bci->usb);if (ret) {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,
@@ -616,7 +616,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,
@@ -624,7 +624,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);
@@ -647,7 +647,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 );
@@ -665,16 +665,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)
@@ -691,10 +681,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 ; }
Re: [PATCH 02/14] twl4030_charger: use devres for power_supply_register and kzalloc.
Quoted 5 lines · NeilBrown
From: NeilBrown <[email protected]>
Final allocations/registrations are now managed by devres.
Signed-off-by NeilBrown <[email protected]>
Patches 1,2: Acked-by: Pavel Machek <[email protected]>
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Sebastian Reichel
maintainerRe: [PATCH 02/14] twl4030_charger: use devres for power_supply_register and kzalloc.
In reply to NeilBrownHi,
Quoted 5 lines · NeilBrown
From: NeilBrown <[email protected]>
Final allocations/registrations are now managed by devres.
Signed-off-by NeilBrown <[email protected]>
Thanks for the patch, rebased to current master and queued with Pavel's ACK:
http://git.infradead.org/battery-2.6.git/commit/325b50aa5d1c7dae57d1e44defdbacd1e2bcabde
-- Sebastian
PATCH 3/14 twl4030_charger: use runtime_pm to keep usb phy active while charging.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
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.
--- drivers/mfd/twl-core.c | 9 ++++----- drivers/power/twl4030_charger.c | 18 +++++------------- 2 files changed, 9 insertions(+), 18 deletions(-)
drivers/mfd/twl-core.c +4 -5
@@ -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); } }
drivers/power/twl4030_charger.c +5 -13
@@ -22,7 +22,6 @@#include <linux/power_supply.h> #include <linux/notifier.h> #include <linux/usb/otg.h> -#include <linux/regulator/machine.h> #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 ; } }
@@ -602,8 +596,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) bci->usb.num_properties =ARRAY_SIZE (twl4030_charger_props); bci->usb.get_property = twl4030_bci_get_property; - bci->usb_reg =regulator_get (bci->dev,"bci3v1" ); - ret =devm_power_supply_register (&pdev->dev, &bci->usb);if (ret) {dev_err (&pdev->dev,"failed to register usb: %d\n" , ret);
PATCH 4/14 twl4030_charger: trust phy to determine when USB power is available.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
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.
--- drivers/power/twl4030_charger.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-)
drivers/power/twl4030_charger.c +6 -27
@@ -178,28 +178,6 @@ static int twl4030_is_battery_present(struct twl4030_bci *bci) }/* -* 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. */ static int twl4030_charger_enable_usb (struct twl4030_bci *bci,bool enable)
@@ -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.
@@ -649,7 +623,12 @@ static int __init 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);
PATCH 5/14 twl4030_charger: split uA calculation into a function.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
We will need this calculation in other places, so create functions to map between register value and uA value.
--- drivers/power/twl4030_charger.c | 48 ++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 13 deletions(-)
drivers/power/twl4030_charger.c +35 -13
@@ -178,6 +178,40 @@ static int twl4030_is_battery_present(struct twl4030_bci *bci) }/* +* 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. */ static int twl4030_charger_enable_usb (struct twl4030_bci *bci,bool enable)
@@ -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 ); }/*
PATCH 6/14 twl4030_charger: allow fine control of charger current.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
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.
--- drivers/power/twl4030_charger.c | 168 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 160 insertions(+), 8 deletions(-)
drivers/power/twl4030_charger.c +160 -8
@@ -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) {
@@ -562,6 +705,14 @@ static int __init 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 );
@@ -644,6 +795,7 @@ static int __init 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,
PATCH 7/14 twl4030_charger: distinguish between USB current and 'AC' current
In reply to NeilBrownFrom: NeilBrown <[email protected]>
The twl4030 charger has two current sources, 'USB' and 'AC' (presumably "Accessory Charger").
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.
--- drivers/power/twl4030_charger.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-)
drivers/power/twl4030_charger.c +29 -7
@@ -22,6 +22,7 @@#include <linux/power_supply.h> #include <linux/notifier.h> #include <linux/usb/otg.h> +#include <linux/i2c/twl4030-madc.h> #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 ))
@@ -708,10 +729,11 @@ static int __init 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 );
PATCH 8/14 twl4030_charger: allow max_current to be managed via sysfs.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
'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.
--- .../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
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +15 -0
@@ -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 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 on 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.
drivers/power/twl4030_charger.c +72 -0
102 lines not on this page · raw message
Re: [Gta04-owner] [PATCH 08/14] twl4030_charger: allow max_current to be managed via sysfs.
Hello Neil,
some suggestions:
Documentation/ABI/testing/sysfs-class-power-twl4030 line 4
Quoted 12 lines · NeilBrown
From: NeilBrown <[email protected]>
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +4
-0@@ -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 which may
one less which ^^
Documentation/ABI/testing/sysfs-class-power-twl4030 line 11
Quoted 7 lines
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +7
-0+ 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 on unplugged.
s/on/or ^^
Documentation/ABI/testing/sysfs-class-power-twl4030 line 13
Quoted 2 lines
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +2
-0+ + Value can the set by writing to the attribute.
^^ be set?
Documentation/ABI/testing/sysfs-class-power-twl4030 line 15
Quoted 2 lines
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +2
-0+ The change will only persist until the next + plug event. These event are reported via udev.
Regards
Jake
Re: [Gta04-owner] [PATCH 08/14] twl4030_charger: allow max_current to be managed via sysfs.
Quoted 26 lines · On Mon, 23 Mar 2015 13:14:50 +0100 jake42 <[email protected]>…
Hello Neil,
some suggestions:
Documentation/ABI/testing/sysfs-class-power-twl4030 line 4
Quoted 12 lines · NeilBrown
From: NeilBrown <[email protected]>
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +4
-0@@ -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 which mayone less which ^^
Documentation/ABI/testing/sysfs-class-power-twl4030 line 11
Quoted 7 lines
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +7
-0+ 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 on unplugged.s/on/or ^^
Quoted 9 lines
Documentation/ABI/testing/sysfs-class-power-twl4030 line 13
Quoted 2 lines
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +2
-0+ + Value can the set by writing to the attribute.^^ be set?Documentation/ABI/testing/sysfs-class-power-twl4030 line 15
Quoted 2 lines
Documentation/ABI/testing/sysfs-class-power-twl4030 new file +2
-0+ The change will only persist until the next + plug event. These event are reported via udev.Regards
Jake
Thanks.
I've made those two changes.
NeilBrown
PATCH 9/14 twl4030_charger: only draw USB current as negotiated with host.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
If the phy has been told what current it can draw, it tells us and now we use that number.
Note that 'vbus_draw' is in mA, while usb_cur is in uA.
--- drivers/power/twl4030_charger.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/power/twl4030_charger.c +5 -0
@@ -602,6 +602,7 @@ static void twl4030_bci_usb_work(struct work_struct *data)switch (bci->event) {case USB_EVENT_VBUS :case USB_EVENT_CHARGER : +case USB_EVENT_ENUMERATED :twl4030_charger_enable_usb (bci, true);break ;case USB_EVENT_NONE :
@@ -614,6 +615,7 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, void *priv) { struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb); +unsigned int *vbus_draw = priv;dev_dbg (bci->dev,"OTG notify %lu\n" , val);
@@ -624,6 +626,9 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, bci->usb_cur =100000 ; bci->event = val; +if (val ==USB_EVENT_ENUMERATED && vbus_draw && + *vbus_draw *1000 > bci->usb_cur) + bci->usb_cur = *vbus_draw *1000 ;schedule_work (&bci->work);return NOTIFY_OK ;
PATCH 10/14 twl4030_charger: enable manual enable/disable of usb charging.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
'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.
--- .../ABI/testing/sysfs-class-power-twl4030 | 11 ++++ drivers/power/twl4030_charger.c | 59 ++++++++++++++++++++ 2 files changed, 70 insertions(+)
Documentation/ABI/testing/sysfs-class-power-twl4030 +11 -0
@@ -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.
drivers/power/twl4030_charger.c +59 -0
99 lines not on this page · raw message
PATCH 11/14 twl4030_charger: add software controlled linear charging mode.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
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.
--- .../ABI/testing/sysfs-class-power-twl4030 | 9 +++ drivers/power/twl4030_charger.c | 55 ++++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-)
Documentation/ABI/testing/sysfs-class-power-twl4030 +9 -0
@@ -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.
drivers/power/twl4030_charger.c +50 -5
109 lines not on this page · raw message
PATCH 12/14 twl4030_charger: add ac/mode to match usb/mode
In reply to NeilBrownFrom: NeilBrown <[email protected]>
This allows AC charging to be turned off, much like usb charging. "continuous" mode is not available though.
--- .../ABI/testing/sysfs-class-power-twl4030 | 10 ++++++ drivers/power/twl4030_charger.c | 35 +++++++++++++++----- 2 files changed, 37 insertions(+), 8 deletions(-)
Documentation/ABI/testing/sysfs-class-power-twl4030 +10 -0
@@ -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.
drivers/power/twl4030_charger.c +27 -8
@@ -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
@@ -693,9 +696,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; }
@@ -709,9 +720,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
@@ -905,6 +920,7 @@ static int __init 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 );
@@ -993,10 +1009,12 @@ static int __init 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,
@@ -1016,13 +1034,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 );
PATCH 13/14 twl4030_charger: Increase current carefully while watching voltage.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
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.
--- drivers/power/twl4030_charger.c | 68 ++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 8 deletions(-)
drivers/power/twl4030_charger.c +60 -8
@@ -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,38 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci)return 0 ; } +static void twl4030_current_worker (struct work_struct *data) +{ +int v; +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 ; + +printk ("v=%d cur=%d target=%d\n" , v, 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 +501,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 +650,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 +672,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 );
@@ -664,14 +715,14 @@ 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;if (val ==USB_EVENT_ENUMERATED && vbus_draw && - *vbus_draw *1000 > bci->usb_cur) - bci->usb_cur = *vbus_draw *1000 ; + *vbus_draw *1000 > bci->usb_cur_target) + bci->usb_cur_target = *vbus_draw *1000 ;schedule_work (&bci->work);return NOTIFY_OK ;
@@ -916,9 +967,9 @@ static int __init 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 ;
@@ -976,6 +1027,7 @@ static int __init 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) {
Re: [PATCH 13/14] twl4030_charger: Increase current carefully while watching voltage.
Hi!
Quoted 13 lines
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.
Quoted 6 lines
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.
Still nervous. If it is possible to overheat the charger, without tripping internal fuse, then you'll do it.
drivers/power/twl4030_charger.c static int twl4030_charger_update_current(struct twl4030_bci *bci) · line 427
Quoted 8 lines
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.
drivers/power/twl4030_charger.c +2
-0+printk ("v=%d cur=%d target=%d\n" , v, bci->usb_cur, + bci->usb_cur_target);
dev_info() and a bit better message, or drop it for production?
drivers/power/twl4030_charger.c static int twl4030_charger_update_current(struct twl4030_bci *bci) · line 432
Quoted 4 lines
drivers/power/twl4030_charger.c +4
-0+if (v <USB_MIN_VOLT ) { +/* Back up and stop adjusting. */ + bci->usb_cur -=USB_CUR_STEP ; + bci->usb_cur_target = bci->usb_cur;
More importantly.... how does it work with device drawing power for operation, too?
Imagine device need 500mA with wifi hotspot, nearly nothing while idle.
Idle device. Code will find that it can charge using 1A, backs up to 0.9A. User starts hotspot. Now device will draw 1.4A, overloading the charger and not charging at all...?
Best regards,
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [PATCH 13/14] twl4030_charger: Increase current carefully while watching voltage.
Quoted 22 lines · On Mon, 23 Mar 2015 22:25:41 +0100 Pavel Machek…
Hi!
Quoted 13 lines
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.
Quoted 6 lines
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.
Quoted 2 lines
Still nervous. If it is possible to overheat the charger, without tripping internal fuse, then you'll do it.
If it is possible to overheat the charger without tripping an internal fuse, then sure the charger is mis-designed - is it not?
Can you suggest an algorithm for determining how much current can safely be pulled from a charger that would *not* make you nervous?
Quoted 11 lines
drivers/power/twl4030_charger.c static int twl4030_charger_update_current(struct twl4030_bci *bci) · line 427
Quoted 9 lines
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.
drivers/power/twl4030_charger.c +2
-0+printk ("v=%d cur=%d target=%d\n" , v, bci->usb_cur, + bci->usb_cur_target);
dev_info()and a bit better message, or drop it for production?
Changed to dev_dbg() - thanks.
Quoted 9 lines
drivers/power/twl4030_charger.c static int twl4030_charger_update_current(struct twl4030_bci *bci) · line 432
Quoted 5 lines
drivers/power/twl4030_charger.c +4
-0+if (v <USB_MIN_VOLT ) { +/* Back up and stop adjusting. */ + bci->usb_cur -=USB_CUR_STEP ; + bci->usb_cur_target = bci->usb_cur;More importantly.... how does it work with device drawing power for operation, too?
Quoted 5 lines
Imagine device need 500mA with wifi hotspot, nearly nothing while idle.
Idle device. Code will find that it can charge using 1A, backs up to 0.9A. User starts hotspot. Now device will draw 1.4A, overloading the charger and not charging at all...?
The current being measured and controlled is the current flowing in from the USB VBUS, not flowing out to the battery.
So I the code choose 0.9A, that is all that will be drawn.
This is a possible issue similar to this though.
If the device is idle and the battery is fully charged, then it won't draw much current from USB even if we allow it too.
So the algorithm might decide it is OK to draw 1.7A because at that time the device cannot use more than 200mA, and that doesn't cause the voltage to drop.
Then later when user enabled wifi-hotspot, the current needed might go up above what the charger can provide.
Maybe I should only increase the limit while the actual current is also increasing. Maybe also revisit the setting when the battery starts charging.
NeilBrown
Quoted 3 lines
Best regards,
Pavel
Re: [PATCH 13/14] twl4030_charger: Increase current carefully while watching voltage.
Hi!
Quoted 20 lines
Quoted 20 lines
Quoted 19 lines
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.
Quoted 8 lines
Quoted 2 lines
Still nervous. If it is possible to overheat the charger, without tripping internal fuse, then you'll do it.
If it is possible to overheat the charger without tripping an internal fuse, then sure the charger is mis-designed - is it not?
Can you suggest an algorithm for determining how much current can safely be pulled from a charger that would *not* make you nervous?
Not nervous? No.
Less nervous?
Run detection as you do, but then round down to "known reasonable" max charging currents?
If you detected 1.1A charger, you are probably overloading 1A charger. So idea would be to round down to 0.5A, 1A, 1.7A.
Quoted 14 lines
Quoted 3 lines
Idle device. Code will find that it can charge using 1A, backs up to 0.9A. User starts hotspot. Now device will draw 1.4A, overloading the charger and not charging at all...?
The current being measured and controlled is the current flowing in from the USB VBUS, not flowing out to the battery.
So I the code choose 0.9A, that is all that will be drawn.This is a possible issue similar to this though.
If the device is idle and the battery is fully charged, then it won't draw much current from USB even if we allow it too.
So the algorithm might decide it is OK to draw 1.7A because at that time the device cannot use more than 200mA, and that doesn't cause the voltage to drop.
Quoted 5 lines
Then later when user enabled wifi-hotspot, the current needed might go up above what the charger can provide.
Maybe I should only increase the limit while the actual current is also increasing. Maybe also revisit the setting when the battery starts charging.
Yes, that sounds better.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH 14/14 twl4030_charger: assume a 'charger' can supply maximum current.
In reply to NeilBrownFrom: NeilBrown <[email protected]>
If it cannot, we will stop pulling more current when voltage drops.
--- drivers/power/twl4030_charger.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/power/twl4030_charger.c +3 -1
@@ -694,8 +694,10 @@ static void twl4030_bci_usb_work(struct work_struct *data) struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work);switch (bci->event) { -case USB_EVENT_VBUS :case USB_EVENT_CHARGER : + bci->usb_cur =USB_MAX_CURRENT ; +/* FALL THROUGH */ +case USB_EVENT_VBUS :case USB_EVENT_ENUMERATED :twl4030_charger_enable_usb (bci, true);break ;
Sebastian Reichel
maintainerHi Neil,
Quoted 3 lines · NeilBrown
here is V2 of my patches to improve the twl4030 charger. They include improved documentation and removal of changes to the CVENAC bit which I didn't properly understand.
I rebased and queued patches 1 and 2. Can you rebase the other patches and take care of the remaining comments?
-- Sebastian