Re: [PATCH 03/13] twl4030_charger: correctly handle -EPROBE_DEFER from devm_usb_get_phy_by_node
On Tue, 18 Aug 2015 01:07:58 -0700 Tony Lindgren t...@atomide.com wrote: * NeilBrown n...@brown.name [150729 17:29]: 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 n...@brown.name --- 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; + } } Neil, the return with -EPROBE_DEFER here causes flakeyness booting for me somehow at least on my logicpd-torpedo-37xx-devkit using omap2plus_defconfig. It seems that the twl4030_bci_probe keeps looping or something about 1/3 of the boots and that probably prevents the other twl modules from loading? Reverting this patch alone seems to fix the issue. I don't think I have a battery wired on this board the USB is wired the same way as on beagle-xm. So I'd assume also flakeyness on beagle xm with this patch. Regards, Tony What dts file are you using? I'm guessing that it doesn't have something like: usb_otg_hs { interface-type = 0; usb-phy = usb2_phy; phys = usb2_phy; phy-names = usb2-phy; mode = 3; power = 50; }; ? i.e. with a usb-phy=usb2_phy ? What if you add usb2_phy { status = disabled; } to your dts file? Should the 'status' be disabled in twl4030.dtsi, and then marked OK in any dts that uses it? I'm not at all clear on how 'status' is meant to be used. Thanks, NeilBrown -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 07/13] twl4030_charger: distinguish between USB current and 'AC' current
On Fri, 7 Aug 2015 07:13:09 +0200 Sebastian Reichel s...@kernel.org wrote: Hi, This actually slipped through my review. IMHO madc should be accessed through IIO, as already done for twl4030-madc-battery and rx51-battery. That way the custom API can be removed at some point. Anyway, I queued the below patch with Tony's ACK to fix the build issue in next. OK, thanks. I'll try to figure out are more proper approach ... might be a week or so though. Thanks, NeilBrown pgpEQ176T2qUB.pgp Description: OpenPGP digital signature
Re: [PATCH 07/13] twl4030_charger: distinguish between USB current and 'AC' current
On Thu, 6 Aug 2015 20:11:16 -0700 Tony Lindgren t...@atomide.com wrote: * NeilBrown n...@brown.name [150729 17:28]: --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c 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 = 20) cgain = true; Neil, you need a stub or something for twl4030_get_madc_conversion if madc is not selected. Now at least omap2plus_defconfig and ARM allmodconfig fails in Linux next. Regards, Tony Thanks, I did get notified about that by Fengguang's test robot, but it's still on my list I guess making CHARGER_TWL4030 auto-select TWL4030_MADC would not be acceptable? That would pull in IIO (it didn't use to...). If this OK? Thanks, NeilBrown From: NeilBrown n...@brown.name Date: Fri, 7 Aug 2015 13:44:37 +1000 Subject: [PATCH] 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 t...@atomide.com Signed-off-by: NeilBrown n...@brown.name diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index c7432f532a83..265fd236f4c0 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 { -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [Gta04-owner] [PATCH 08/14] twl4030_charger: allow max_current to be managed via sysfs.
On Mon, 23 Mar 2015 13:14:50 +0100 jake42 jak...@rommel.stw.uni-erlangen.de wrote: Hello Neil, some suggestions: On 23.03.2015 00:20, NeilBrown wrote: From: NeilBrown ne...@suse.de diff --git a/Documentation/ABI/testing/sysfs-class-power-twl4030 b/Documentation/ABI/testing/sysfs-class-power-twl4030 new file mode 100644 index ..06092209d851 --- /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 which may one less which^^ + 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 ^^ + + Value can the set by writing to the attribute. ^^ be set? + 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 -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 03/13] 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 n...@brown.name --- 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. */ -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 02/13] twl4030_charger: convert to module_platform_driver instead of ..._probe.
From: Pavel Machek pa...@ucw.cz 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 pa...@ucw.cz Signed-off-by: NeilBrown n...@brown.name --- 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); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 06/13] 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 pa...@ucw.cz Signed-off-by: NeilBrown n...@brown.name --- 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_BCICTL10x23 #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 intichg_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 = 20) + cgain = true; + if (bci-ichg_lo = 40) + cgain = true; + if (bci-ichg_hi = 82) + 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 +* 100000, and requires the 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 10. +* 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 101100 (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
[PATCH 00/13] Enhance twl4030_charger functionality. - V3
Following is most of my twl4030_charger patches, rebased against git://git.infradead.org/battery-2.6 Since the previous set I have added the conversion to module_platform_driver so EPROBE_DEFER can be used, and fixed a few minor typos. This does not include the changes to add extcon support, in part because extcon has seen some changes lately which leave me even more confused about how best to use it than before. I need to sort that out before I can resolve the rest of my usb phy patches and then add a few more charger patches. Thanks, NeilBrown --- NeilBrown (12): twl4030_charger: use runtime_pm to keep usb phy active while charging. twl4030_charger: correctly handle -EPROBE_DEFER from devm_usb_get_phy_by_node 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: 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. Pavel Machek (1): twl4030_charger: convert to module_platform_driver instead of ..._probe. .../ABI/testing/sysfs-class-power-twl4030 | 45 ++ drivers/mfd/twl-core.c |9 drivers/power/twl4030_charger.c| 541 ++-- 3 files changed, 531 insertions(+), 64 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-power-twl4030 -- Signature -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 08/13] 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 pa...@ucw.cz Signed-off-by: NeilBrown n...@brown.name --- .../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 ..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 = 50; 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 = 50; + else + bci-usb_cur = 10; + 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
[PATCH 04/13] 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 n...@brown.name --- 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 @@ -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. @@ -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); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 09/13] 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 pa...@ucw.cz Signed-off-by: NeilBrown n...@brown.name --- .../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 intichg_eoc, ichg_lo, ichg_hi; unsigned intusb_cur, ac_cur; boolac_is_active; + int usb_mode; /* charging mode requested */ +#defineCHARGE_OFF 0 +#defineCHARGE_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 = 50; /* 500mA */ else bci-usb_cur = 10; /* 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
[PATCH 07/13] 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 pa...@ucw.cz Signed-off-by: NeilBrown n...@brown.name --- 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 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_BCIICHG0x08 @@ -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 intichg_eoc, ichg_lo, ichg_hi, cur; + unsigned intichg_eoc, ichg_lo, ichg_hi; + unsigned intusb_cur, ac_cur; + boolac_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 = 20) cgain = true; @@ -237,7 +254,7 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) cgain = true; if (bci-ichg_hi = 82) 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 = 50; /* High threshold */ + bci-ac_cur = 50; /* 500mA */ if (allow_usb) - bci-cur = 50; /* 500mA */ + bci-usb_cur = 50; /* 500mA */ else - bci-cur = 10; /* 100mA */ + bci-usb_cur = 10; /* 100mA */ bci-dev = pdev-dev; bci-irq_chg = platform_get_irq(pdev, 0); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org
[PATCH 05/13] 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 pa...@ucw.cz Signed-off-by: NeilBrown n...@brown.name --- 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 @@ -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 * 1) / 10; - if (bcictl1 TWL4030_CGAIN) - ret *= 2; - - return ret; + return regval2ua(curr, bcictl1 TWL4030_CGAIN); } /* -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 01/13] 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 lee.jo...@linaro.org Signed-off-by: NeilBrown n...@brown.name --- 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 linux/power_supply.h #include linux/notifier.h #include linux/usb/otg.h -#include linux/regulator/machine.h #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG0x08 @@ -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
[PATCH 11/13] 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 pa...@ucw.cz Signed-off-by: NeilBrown n...@brown.name --- .../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 intichg_eoc, ichg_lo, ichg_hi; unsigned intusb_cur, ac_cur; boolac_is_active; - int usb_mode; /* charging mode requested */ + int usb_mode, ac_mode; /* charging mode requested */ #defineCHARGE_OFF 0 #defineCHARGE_AUTO 1 #defineCHARGE_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 = 10; /* 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
[PATCH 13/13] twl4030_charger: assume a 'charger' can supply maximum current.
If it cannot, we will stop pulling more current when voltage drops. Signed-off-by: NeilBrown n...@brown.name --- drivers/power/twl4030_charger.c |4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 2c537ee11bbe..c7432f532a83 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -697,8 +697,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: twl4030_charger_enable_usb(bci, true); break; case USB_EVENT_NONE: -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 12/13] 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 n...@brown.name --- 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 { #defineCHARGE_AUTO 1 #defineCHARGE_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 intusb_cur_target; + struct delayed_work current_worker; +#defineUSB_CUR_STEP2 /* 20mA at a time */ +#defineUSB_MIN_VOLT475 /* 4.75V */ +#defineUSB_CUR_DELAY msecs_to_jiffies(100) +#defineUSB_MAX_CURRENT 170 /* 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
[PATCH 10/13] 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 andr...@kemnade.info Signed-off-by: NeilBrown n...@brown.name --- .../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 linux/usb/otg.h #include linux/i2c/twl4030-madc.h +#define TWL4030_BCIMDEN0x00 +#define TWL4030_BCIMDKEY 0x01 #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG0x08 #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_DONEBIT(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 */ #defineCHARGE_OFF 0 #defineCHARGE_AUTO 1 +#defineCHARGE_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
Re: [PATCH 5/6] phy: twl4030-usb: add support for reading resistor on ID pin.
On Mon, 1 Jun 2015 19:06:52 +0530 Kishon Vijay Abraham I kis...@ti.com wrote: Hi, On Thursday 16 April 2015 01:33 PM, NeilBrown wrote: From: NeilBrown ne...@suse.de The twl4030 phy can measure, with low precision, the resistance-to-ground of the ID pin. Add a function to read the value, and export the result via sysfs. Little sceptical about adding new sysfs entries. Do you have a good reason to add this? The hardware can report the value, so why not present it to user-space? I originally used this with a udev rule which would configure the maximum current based on the resistance measure - to work with the particular charger hardware I have. More recent patches try to do all of the max-current configuration in the kernel, so I could live without exporting the value via sysfs if that is a show-stopper. I can't see where the scepticism comes from though. It is a well defined and cleary documented feature of the hardware. Why not expose it? Thanks, NeilBrown Thanks Kishon If the read fails, which it does sometimes, try again in 50msec. Acked-by: Pavel Machek pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- .../ABI/testing/sysfs-platform-twl4030-usb | 22 +++ drivers/phy/phy-twl4030-usb.c | 63 2 files changed, 85 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-twl4030-usb b/Documentation/ABI/testing/sysfs-platform-twl4030-usb index 512c51be64ae..425d23676f8a 100644 --- a/Documentation/ABI/testing/sysfs-platform-twl4030-usb +++ b/Documentation/ABI/testing/sysfs-platform-twl4030-usb @@ -6,3 +6,25 @@ Description: Possible values: on, off. Changes are notified via select/poll. + +What: /sys/bus/platform/devices/*twl4030-usb/id +Description: + Read-only report on measurement of USB-OTG ID pin. + + The ID pin may be floating, grounded, or pulled to + ground by a resistor. + + A very course grained reading of the resistance is + available. The numbers given in kilo-ohms are roughly + the center-point of the detected range. + + Possible values are: + ground + 102k + 200k + 440k + floating + unknown + + unknown indicates a problem with trying to detect + the resistance. diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 3a707dd14238..1d6f3e70193e 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -379,6 +379,56 @@ static void twl4030_i2c_access(struct twl4030_usb *twl, int on) } } +enum twl4030_id_status { + TWL4030_GROUND, + TWL4030_102K, + TWL4030_200K, + TWL4030_440K, + TWL4030_FLOATING, + TWL4030_ID_UNKNOWN, +}; +static char *twl4030_id_names[] = { + ground, + 102k, + 200k, + 440k, + floating, + unknown +}; + +enum twl4030_id_status twl4030_get_id(struct twl4030_usb *twl) +{ + int ret; + + pm_runtime_get_sync(twl-dev); + if (twl-usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 1); + ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); + if (ret 0 || !(ret ULPI_OTG_ID_PULLUP)) { + /* Need pull-up to read ID */ + twl4030_usb_set_bits(twl, ULPI_OTG_CTRL, +ULPI_OTG_ID_PULLUP); + mdelay(50); + } + ret = twl4030_usb_read(twl, ID_STATUS); + if (ret 0 || (ret 0x1f) == 0) { + mdelay(50); + ret = twl4030_usb_read(twl, ID_STATUS); + } + + if (twl-usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 0); + pm_runtime_put_autosuspend(twl-dev); + + if (ret 0) + return TWL4030_ID_UNKNOWN; + ret = ffs(ret) - 1; + if (ret TWL4030_GROUND || ret TWL4030_FLOATING) + return TWL4030_ID_UNKNOWN; + + return ret; +} + static void __twl4030_phy_power(struct twl4030_usb *twl, int on) { u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); @@ -532,6 +582,16 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev, } static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); +static ssize_t twl4030_usb_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, %s\n, +twl4030_id_names[twl4030_get_id(twl)]); +} +static DEVICE_ATTR(id, 0444, twl4030_usb_id_show, NULL); + static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; @@ -709,6 +769,8 @@ static int twl4030_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, twl); if (device_create_file(pdev-dev, dev_attr_vbus
Re: [PATCH 4/6] phy: twl4030-usb: add ABI documentation
On Sat, 18 Apr 2015 00:14:36 +0200 Pavel Machek pa...@ucw.cz wrote: On Thu 2015-04-16 18:03:04, NeilBrown wrote: From: NeilBrown ne...@suse.de This driver device one local attribute: vbus. Describe that in Documentation/ABI/testing/sysfs-platform/twl4030-usb. Signed-off-by: NeilBrown n...@brown.name --- .../ABI/testing/sysfs-platform-twl4030-usb |8 1 file changed, 8 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-platform-twl4030-usb diff --git a/Documentation/ABI/testing/sysfs-platform-twl4030-usb b/Documentation/ABI/testing/sysfs-platform-twl4030-usb new file mode 100644 index ..512c51be64ae --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-twl4030-usb @@ -0,0 +1,8 @@ +What: /sys/bus/platform/devices/*twl4030-usb/vbus +Description: + Read-only status reporting if VBUS (approx 5V) + is being supplied by the USB bus. + + Possible values: on, off. Would bit be better to have values 0 and 1? Kernel usually does that for booleans... 1/ The code already uses on and off, so changing would be an ABI breakage. 2/ No it doesn't. For modules params, the kernel uses Y and N git grep '? on : off' | wc find 172 matches. NeilBrown pgpMBx3gIbOQL.pgp Description: OpenPGP digital signature
[PATCH 2/6] phy: twl4030-usb: remove pointless 'suspended' test in 'suspend' callback.
When the runtime_suspend callback is running, 'runtime_status' is always RPM_SUSPENDING, so pm_runtime_suspended() will always fail. Similarly while the runtime_resume callback is running 'runtime_status' is RPM_RESUMING, so pm_runtime_active() will always fail. So remove these two pointless tests. Signed-off-by: NeilBrown n...@brown.name --- drivers/phy/phy-twl4030-usb.c |4 1 file changed, 4 deletions(-) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 3078f80bf520..590c2b1c1a94 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -396,8 +396,6 @@ static int twl4030_usb_runtime_suspend(struct device *dev) struct twl4030_usb *twl = dev_get_drvdata(dev); dev_dbg(twl-dev, %s\n, __func__); - if (pm_runtime_suspended(dev)) - return 0; __twl4030_phy_power(twl, 0); regulator_disable(twl-usb1v5); @@ -413,8 +411,6 @@ static int twl4030_usb_runtime_resume(struct device *dev) int res; dev_dbg(twl-dev, %s\n, __func__); - if (pm_runtime_active(dev)) - return 0; res = regulator_enable(twl-usb3v1); if (res) -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/6] phy: twl4030-usb: remove incorrect pm_runtime_get_sync() in probe function.
The USB phy should initialize with power-off, and will be powered on by the USB system when a cable connection is detected. Having this pm_runtime_get_sync() during probe causes the phy to *always* be powered on. Removing it returns to sensible power management. Fixes: 96be39ab34b77c6f6f5cd6ae03aac6c6449ee5c4 Signed-off-by: NeilBrown n...@brown.name --- drivers/phy/phy-twl4030-usb.c |1 - 1 file changed, 1 deletion(-) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 590c2b1c1a94..3a707dd14238 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -715,7 +715,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(pdev-dev); pm_runtime_set_autosuspend_delay(pdev-dev, 2000); pm_runtime_enable(pdev-dev); - pm_runtime_get_sync(pdev-dev); /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/6] phy: twl4030-usb: make runtime pm more reliable.
From: NeilBrown ne...@suse.de A construct like: if (pm_runtime_suspended(twl-dev)) pm_runtime_get_sync(twl-dev); is against the spirit of the runtime_pm interface as it makes the internal refcounting useless. In this case it is also racy, particularly as 'put_autosuspend' is used to drop a reference. When that happens a timer is started and the device is runtime-suspended after the timeout. If the above code runs in this window, the device will not be found to be suspended so no pm_runtime reference is taken. When the timer expires the device will be suspended, which is against the intention of the code. So be more direct is taking and dropping references. If twl-linkstat is VBUS_VALID or ID_GROUND, then hold a pm_runtime reference, otherwise don't. Define cable_present() to test for this condition. Tested-by: Tony Lindgren t...@atomide.com Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 29 - 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index bc42d6a8939f..3078f80bf520 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -144,6 +144,16 @@ #define PMBR1 0x0D #define GPIO_USB_4PIN_ULPI_2430C (3 0) +/* + * If VBUS is valid or ID is ground, then we know a + * cable is present and we need to be runtime-enabled + */ +static inline bool cable_present(enum omap_musb_vbus_id_status stat) +{ + return stat == OMAP_MUSB_VBUS_VALID || + stat == OMAP_MUSB_ID_GROUND; +} + struct twl4030_usb { struct usb_phy phy; struct device *dev; @@ -536,8 +546,10 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) mutex_lock(twl-lock); if (status = 0 status != twl-linkstat) { + status_changed = + cable_present(twl-linkstat) != + cable_present(status); twl-linkstat = status; - status_changed = true; } mutex_unlock(twl-lock); @@ -553,15 +565,11 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - if ((status == OMAP_MUSB_VBUS_VALID) || - (status == OMAP_MUSB_ID_GROUND)) { - if (pm_runtime_suspended(twl-dev)) - pm_runtime_get_sync(twl-dev); + if (cable_present(status)) { + pm_runtime_get_sync(twl-dev); } else { - if (pm_runtime_active(twl-dev)) { - pm_runtime_mark_last_busy(twl-dev); - pm_runtime_put_autosuspend(twl-dev); - } + pm_runtime_mark_last_busy(twl-dev); + pm_runtime_put_autosuspend(twl-dev); } omap_musb_mailbox(status); } @@ -767,6 +775,9 @@ static int twl4030_usb_remove(struct platform_device *pdev) /* disable complete OTG block */ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + if (cable_present(twl-linkstat)) + pm_runtime_put_noidle(twl-dev); pm_runtime_mark_last_busy(twl-dev); pm_runtime_put(twl-dev); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 5/6] phy: twl4030-usb: add support for reading resistor on ID pin.
From: NeilBrown ne...@suse.de The twl4030 phy can measure, with low precision, the resistance-to-ground of the ID pin. Add a function to read the value, and export the result via sysfs. If the read fails, which it does sometimes, try again in 50msec. Acked-by: Pavel Machek pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- .../ABI/testing/sysfs-platform-twl4030-usb | 22 +++ drivers/phy/phy-twl4030-usb.c | 63 2 files changed, 85 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-twl4030-usb b/Documentation/ABI/testing/sysfs-platform-twl4030-usb index 512c51be64ae..425d23676f8a 100644 --- a/Documentation/ABI/testing/sysfs-platform-twl4030-usb +++ b/Documentation/ABI/testing/sysfs-platform-twl4030-usb @@ -6,3 +6,25 @@ Description: Possible values: on, off. Changes are notified via select/poll. + +What: /sys/bus/platform/devices/*twl4030-usb/id +Description: + Read-only report on measurement of USB-OTG ID pin. + + The ID pin may be floating, grounded, or pulled to + ground by a resistor. + + A very course grained reading of the resistance is + available. The numbers given in kilo-ohms are roughly + the center-point of the detected range. + + Possible values are: + ground + 102k + 200k + 440k + floating + unknown + + unknown indicates a problem with trying to detect + the resistance. diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 3a707dd14238..1d6f3e70193e 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -379,6 +379,56 @@ static void twl4030_i2c_access(struct twl4030_usb *twl, int on) } } +enum twl4030_id_status { + TWL4030_GROUND, + TWL4030_102K, + TWL4030_200K, + TWL4030_440K, + TWL4030_FLOATING, + TWL4030_ID_UNKNOWN, +}; +static char *twl4030_id_names[] = { + ground, + 102k, + 200k, + 440k, + floating, + unknown +}; + +enum twl4030_id_status twl4030_get_id(struct twl4030_usb *twl) +{ + int ret; + + pm_runtime_get_sync(twl-dev); + if (twl-usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 1); + ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); + if (ret 0 || !(ret ULPI_OTG_ID_PULLUP)) { + /* Need pull-up to read ID */ + twl4030_usb_set_bits(twl, ULPI_OTG_CTRL, +ULPI_OTG_ID_PULLUP); + mdelay(50); + } + ret = twl4030_usb_read(twl, ID_STATUS); + if (ret 0 || (ret 0x1f) == 0) { + mdelay(50); + ret = twl4030_usb_read(twl, ID_STATUS); + } + + if (twl-usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 0); + pm_runtime_put_autosuspend(twl-dev); + + if (ret 0) + return TWL4030_ID_UNKNOWN; + ret = ffs(ret) - 1; + if (ret TWL4030_GROUND || ret TWL4030_FLOATING) + return TWL4030_ID_UNKNOWN; + + return ret; +} + static void __twl4030_phy_power(struct twl4030_usb *twl, int on) { u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); @@ -532,6 +582,16 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev, } static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); +static ssize_t twl4030_usb_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, %s\n, +twl4030_id_names[twl4030_get_id(twl)]); +} +static DEVICE_ATTR(id, 0444, twl4030_usb_id_show, NULL); + static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; @@ -709,6 +769,8 @@ static int twl4030_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, twl); if (device_create_file(pdev-dev, dev_attr_vbus)) dev_warn(pdev-dev, could not create sysfs file\n); + if (device_create_file(pdev-dev, dev_attr_id)) + dev_warn(pdev-dev, could not create sysfs file\n); ATOMIC_INIT_NOTIFIER_HEAD(twl-phy.notifier); @@ -753,6 +815,7 @@ static int twl4030_usb_remove(struct platform_device *pdev) pm_runtime_get_sync(twl-dev); cancel_delayed_work(twl-id_workaround_work); device_remove_file(twl-dev, dev_attr_vbus); + device_remove_file(twl-dev, dev_attr_id); /* set transceiver mode to power on defaults */ twl4030_usb_set_mode(twl, -1); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 6/6] phy: twl4030-usb: add extcon to report cable connections.
From: NeilBrown ne...@suse.de Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 67 + 1 file changed, 67 insertions(+) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 1d6f3e70193e..c42153d43ec2 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -40,6 +40,7 @@ #include linux/regulator/consumer.h #include linux/err.h #include linux/slab.h +#include linux/extcon.h /* Register defines */ @@ -173,6 +174,9 @@ struct twl4030_usb { enum omap_musb_vbus_id_status linkstat; boolvbus_supplied; + /* cable connection */ + struct extcon_dev edev; + struct delayed_work id_workaround_work; }; @@ -592,6 +596,54 @@ static ssize_t twl4030_usb_id_show(struct device *dev, } static DEVICE_ATTR(id, 0444, twl4030_usb_id_show, NULL); +static const char *usb_cables[] = { + USB, /* id is floating */ + Charger-downstream, /* id is floating and D+ shorted to D- */ + USB-Host, /* id is ground */ + USB-ACA, /* Accessory Charger Adapter id is something else */ + NULL +}; +enum { + TWL_CABLE_USB, + TWL_CABLE_CHARGER, /* Not used - twl4030 can detect charger, +* but driver cannot yet */ + TWL_CABLE_OTG, + TWL_CABLE_ACA, +}; +static u32 all_exclusive[] = {0x, 0}; + +static void twl4030_usb_report_cable(struct twl4030_usb *twl) +{ + enum twl4030_id_status sts; + + if (!cable_present(twl-linkstat)) { + extcon_set_state(twl-edev, 0); + return; + } + + sts = twl4030_get_id(twl); + + switch (sts) { + case TWL4030_FLOATING: /* USB downstream */ + extcon_update_state(twl-edev, + 1TWL_CABLE_USB, + 1TWL_CABLE_USB); + break; + case TWL4030_GROUND: /* USB host */ + extcon_update_state(twl-edev, + 1TWL_CABLE_OTG, + 1TWL_CABLE_OTG); + break; + default: /* Some resistor */ + extcon_update_state(twl-edev, + 1TWL_CABLE_ACA, + 1TWL_CABLE_ACA); + /* An ACA should set port to 'host' mode */ + twl-linkstat = OMAP_MUSB_ID_GROUND; + break; + } +} + static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; @@ -628,6 +680,7 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) pm_runtime_put_autosuspend(twl-dev); } omap_musb_mailbox(status); + twl4030_usb_report_cable(twl); } /* don't schedule during sleep - irq works right then */ @@ -766,6 +819,20 @@ static int twl4030_usb_probe(struct platform_device *pdev) } usb_add_phy_dev(twl-phy); + twl-edev.name = devm_kasprintf(twl-dev, GFP_KERNEL, %s-usb, + dev_name(twl-dev-parent)); + twl-edev.supported_cable = usb_cables; + twl-edev.mutually_exclusive = all_exclusive; + twl-edev.print_name = NULL; /* why would you change this? */ + twl-edev.print_state = NULL; /* probably want to change this */ + + twl-edev.dev.parent = pdev-dev; + err = extcon_dev_register(twl-edev); + if (err) { + dev_err(pdev-dev, register extcon failed\n); + return err; + } + platform_set_drvdata(pdev, twl); if (device_create_file(pdev-dev, dev_attr_vbus)) dev_warn(pdev-dev, could not create sysfs file\n); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/6] Enhancements to twl4030 phy to support better charging.
Hi Kishon, this is a slightly modified version of the series I sent earlier. I've removed that patches which use the old 'notifier chain' and will solve the issue of communicating current available separately. I've added a couple of patches which fix some pm_runtime issues. In particular: phy: twl4030-usb: remove incorrect pm_runtime_get_sync() in probe function. Fixes a bug which causes the usb phy to remain permanently powered on, hence the Cc to Tony. If these could be queued for some future merge window, I would really appreciate it. Thanks, NeilBrown --- NeilBrown (6): phy: twl4030-usb: make runtime pm more reliable. phy: twl4030-usb: remove pointless 'suspended' test in 'suspend' callback. phy: twl4030-usb: remove incorrect pm_runtime_get_sync() in probe function. phy: twl4030-usb: add ABI documentation phy: twl4030-usb: add support for reading resistor on ID pin. phy: twl4030-usb: add extcon to report cable connections. .../ABI/testing/sysfs-platform-twl4030-usb | 30 drivers/phy/phy-twl4030-usb.c | 164 ++-- 2 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-platform-twl4030-usb -- Signature -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 4/6] phy: twl4030-usb: add ABI documentation
From: NeilBrown ne...@suse.de This driver device one local attribute: vbus. Describe that in Documentation/ABI/testing/sysfs-platform/twl4030-usb. Signed-off-by: NeilBrown n...@brown.name --- .../ABI/testing/sysfs-platform-twl4030-usb |8 1 file changed, 8 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-platform-twl4030-usb diff --git a/Documentation/ABI/testing/sysfs-platform-twl4030-usb b/Documentation/ABI/testing/sysfs-platform-twl4030-usb new file mode 100644 index ..512c51be64ae --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-twl4030-usb @@ -0,0 +1,8 @@ +What: /sys/bus/platform/devices/*twl4030-usb/vbus +Description: + Read-only status reporting if VBUS (approx 5V) + is being supplied by the USB bus. + + Possible values: on, off. + + Changes are notified via select/poll. -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 13/14] twl4030_charger: Increase current carefully while watching voltage.
On Mon, 23 Mar 2015 22:25:41 +0100 Pavel Machek pa...@ucw.cz wrote: Hi! 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. 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? 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. + 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. + 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...? 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 Best regards, Pavel pgpc908HCllRF.pgp Description: OpenPGP digital signature
Re: [PATCH 3/4] mmc: sdio: support switching to 1-bit before turning off clocks
On Mon, 23 Mar 2015 10:10:18 +0100 Ulf Hansson ulf.hans...@linaro.org wrote: On 24 February 2015 at 03:42, NeilBrown ne...@suse.de wrote: @@ -941,8 +947,12 @@ void mmc_release_host(struct mmc_host *host) WARN_ON(!host-claimed); - if (host-ops-disable host-claim_cnt == 1) - host-ops-disable(host); + if (host-claim_cnt == 1) { + if (atomic_read(host-sdio_narrowed) == 1) + atomic_set(host-sdio_narrowed, 0); + if (host-ops-disable) + host-ops-disable(host); + } As omap_hsmmc in currently the only user of the host_ops-enable|disable() callbacks, I wonder if this approach will work race-free for those hosts that don't implement these callbacks? Hi Ulf, you might have guessed, but to be explicit: I withdraw this patchset. I much prefer the other approach that I suggested using runtime PM on the mmc_host device and switching to 1-bit more in the pm_runtime_suspend function. As you say, omap_hsmmc is the only user of enable/disable and they were an important part of my strategy in this patchset. Which raises the question: what is omap_hsmmc using enable/disable? My guess is that it is largely historical - added because omap_hsmmc wanted runtime pm before sufficient generic infrastructure was available. I've tested a patch to remove it, and it seem fine. I'll post it. Thanks, NeilBrown pgp_36U6si_xZ.pgp Description: OpenPGP digital signature
[PATCH 2/2] mmc: remove enable/disable methods.
The 'enable' and 'disable' methods are deprecated according to host.h, and are no longer used. So discard them. Signed-off-by: NeilBrown n...@brown.name --- drivers/mmc/core/core.c |5 - include/linux/mmc/host.h |6 -- 2 files changed, 11 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 23f10f72e5f3..709ada9f26b5 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -920,8 +920,6 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) wake_up(host-wq); spin_unlock_irqrestore(host-lock, flags); remove_wait_queue(host-wq, wait); - if (host-ops-enable !stop host-claim_cnt == 1) - host-ops-enable(host); return stop; } @@ -940,9 +938,6 @@ void mmc_release_host(struct mmc_host *host) WARN_ON(!host-claimed); - if (host-ops-disable host-claim_cnt == 1) - host-ops-disable(host); - spin_lock_irqsave(host-lock, flags); if (--host-claim_cnt) { /* Release for nested claim */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0c8cbe5d1550..b5bedaec6223 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -80,12 +80,6 @@ struct mmc_ios { struct mmc_host_ops { /* -* 'enable' is called when the host is claimed and 'disable' is called -* when the host is released. 'enable' and 'disable' are deprecated. -*/ - int (*enable)(struct mmc_host *host); - int (*disable)(struct mmc_host *host); - /* * It is optional for the host to implement pre_req and post_req in * order to support double buffering of requests (prepare one * request while another request is active). -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/2] mmc: omap_hsmmc: stop using .enable and .disable method.
enable and disable are only used to get and put runtime pm references. .set_ios already does this itself, and other drivers just do it in set_ios and .request without using enable/disable. So add pm_runtime get/put to omap_hsmmc_request(), and discard the enable/disable methods. Signed-off-by: NeilBrown n...@brown.name --- drivers/mmc/host/omap_hsmmc.c | 24 +++- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index f84cfb01716d..092bcecd73e6 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -882,6 +882,8 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req return; host-mrq = NULL; mmc_request_done(host-mmc, mrq); + pm_runtime_mark_last_busy(host-dev); + pm_runtime_put_autosuspend(host-dev); } /* @@ -1537,6 +1539,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) BUG_ON(host-req_in_progress); BUG_ON(host-dma_ch != -1); + pm_runtime_get_sync(host-dev); if (host-protect_card) { if (host-reqs_blocked 3) { /* @@ -1778,25 +1781,6 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) set_sd_bus_power(host); } -static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) -{ - struct omap_hsmmc_host *host = mmc_priv(mmc); - - pm_runtime_get_sync(host-dev); - - return 0; -} - -static int omap_hsmmc_disable_fclk(struct mmc_host *mmc) -{ - struct omap_hsmmc_host *host = mmc_priv(mmc); - - pm_runtime_mark_last_busy(host-dev); - pm_runtime_put_autosuspend(host-dev); - - return 0; -} - static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, unsigned int direction, int blk_size) { @@ -1808,8 +1792,6 @@ static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, } static struct mmc_host_ops omap_hsmmc_ops = { - .enable = omap_hsmmc_enable_fclk, - .disable = omap_hsmmc_disable_fclk, .post_req = omap_hsmmc_post_req, .pre_req = omap_hsmmc_pre_req, .request = omap_hsmmc_request, -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/2] Remove mmc_host enable/disable methods.
Only omap_hsmmc uses enable and disable, and this seems to be largely for historical reasons and is no longer necessary. I have tested these patches with an OMAP3 with an uSD card on mmc0 and a wifi SDIO device on mmc1. NeilBrown --- NeilBrown (2): mmc: omap_hsmmc: stop using .enable and .disable method. mmc: remove enable/disable methods. drivers/mmc/core/core.c |5 - drivers/mmc/host/omap_hsmmc.c | 24 +++- include/linux/mmc/host.h |6 -- 3 files changed, 3 insertions(+), 32 deletions(-) -- Signature -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/2] mmc: omap_hsmmc: stop using .enable and .disable method.
On Thu, 26 Mar 2015 08:43:37 +1100 NeilBrown n...@brown.name wrote: enable and disable are only used to get and put runtime pm references. .set_ios already does this itself, and other drivers just do it in set_ios and .request without using enable/disable. So add pm_runtime get/put to omap_hsmmc_request(), and discard the enable/disable methods. Signed-off-by: NeilBrown n...@brown.name Sorry, that patch wasn't much good. This one I have really tested properly and checked that the 'put' match the 'get's and that the device to regularly go to sleep as expected. NeilBrown From: NeilBrown n...@brown.name Date: Thu, 26 Mar 2015 08:28:43 +1100 Subject: [PATCH] mmc: omap_hsmmc: stop using .enable and .disable method. enable and disable are only used to get and put runtime pm references. .set_ios already does this itself, and other drivers just do it in set_ios and .request without using enable/disable. So add pm_runtime get/put to omap_hsmmc_request(), and discard the enable/disable methods. Signed-off-by: NeilBrown n...@brown.name diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index f84cfb01716d..2cb81538a612 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -882,6 +882,8 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req return; host-mrq = NULL; mmc_request_done(host-mmc, mrq); + pm_runtime_mark_last_busy(host-dev); + pm_runtime_put_autosuspend(host-dev); } /* @@ -1305,6 +1307,8 @@ static void omap_hsmmc_dma_callback(void *param) host-mrq = NULL; mmc_request_done(host-mmc, mrq); + pm_runtime_mark_last_busy(host-dev); + pm_runtime_put_autosuspend(host-dev); } } @@ -1537,6 +1541,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) BUG_ON(host-req_in_progress); BUG_ON(host-dma_ch != -1); + pm_runtime_get_sync(host-dev); if (host-protect_card) { if (host-reqs_blocked 3) { /* @@ -1553,6 +1558,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) req-data-error = -EBADF; req-cmd-retries = 0; mmc_request_done(mmc, req); + pm_runtime_mark_last_busy(host-dev); + pm_runtime_put_autosuspend(host-dev); return; } else if (host-reqs_blocked) host-reqs_blocked = 0; @@ -1566,6 +1573,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) req-data-error = err; host-mrq = NULL; mmc_request_done(mmc, req); + pm_runtime_mark_last_busy(host-dev); + pm_runtime_put_autosuspend(host-dev); return; } if (req-sbc !(host-flags AUTO_CMD23)) { @@ -1778,25 +1787,6 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) set_sd_bus_power(host); } -static int omap_hsmmc_enable_fclk(struct mmc_host *mmc) -{ - struct omap_hsmmc_host *host = mmc_priv(mmc); - - pm_runtime_get_sync(host-dev); - - return 0; -} - -static int omap_hsmmc_disable_fclk(struct mmc_host *mmc) -{ - struct omap_hsmmc_host *host = mmc_priv(mmc); - - pm_runtime_mark_last_busy(host-dev); - pm_runtime_put_autosuspend(host-dev); - - return 0; -} - static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, unsigned int direction, int blk_size) { @@ -1808,8 +1798,6 @@ static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, } static struct mmc_host_ops omap_hsmmc_ops = { - .enable = omap_hsmmc_enable_fclk, - .disable = omap_hsmmc_disable_fclk, .post_req = omap_hsmmc_post_req, .pre_req = omap_hsmmc_pre_req, .request = omap_hsmmc_request, pgpHruKccfCsN.pgp Description: OpenPGP digital signature
[PATCH 09/14] twl4030_charger: only draw USB current as negotiated with host.
From: NeilBrown ne...@suse.de 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. Acked-by: Pavel Machek pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c |5 + 1 file changed, 5 insertions(+) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index df031b0123d0..2042e7619954 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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 = 10; 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; -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [Gta04-owner] [PATCH 3/4] usb: phy: twl4030: add support for reading restore on ID pin.
On Wed, 4 Mar 2015 07:54:41 +0100 Dr. H. Nikolaus Schaller h...@goldelico.com wrote: Am 04.03.2015 um 07:35 schrieb NeilBrown ne...@suse.de: On Mon, 2 Mar 2015 22:04:31 +0100 Pavel Machek pa...@ucw.cz wrote: Hi! The twl4030 phy can measure, with low precision, the resistance-to-ground of the ID pin. Add a function to read the value, and export the result via sysfs. If the read fails, which it does sometimes, try again in 50msec. Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 63 + 1 file changed, 63 insertions(+) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 023fe150c7a1..759950898df9 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -374,6 +374,56 @@ static void twl4030_i2c_access(struct twl4030_usb *twl, int on) } } +enum twl4030_id_status { + TWL4030_GROUND, + TWL4030_102K, + TWL4030_200K, + TWL4030_440K, + TWL4030_FLOATING, + TWL4030_ID_UNKNOWN, +}; +static char *twl4030_id_names[] = { + ground, + 102k, + 200k, + 440k, New /sys files should be documented somewhere...? Preferably with the code... Does it make sense to change 440k - 440KOhm? Interesting question. I prefer to avoid including units in files - bare numbers is better. But there is no number to match floating unless I spell it out as infinity, and wouldn't be helpful. Certainly K would be preferred over kâ, The international standard for kilo = 1000 is a lower case k. While it is (non-standard) to use K for 1024: http://en.wikipedia.org/wiki/Kilobyte So I would keep the string constants lower case to avoid this potential confusion. Yes, I got that backwards, didn't I. I think I'll leave it as lower-case. I won't include the word ohm - it is very uncommon to have units explicit in sysfs attribute values. Thanks, NeilBrown pgpTfJsV9eFf0.pgp Description: OpenPGP digital signature
[PATCH 03/14] twl4030_charger: use runtime_pm to keep usb phy active while charging.
From: NeilBrown ne...@suse.de 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 lee.jo...@linaro.org Signed-off-by: NeilBrown ne...@suse.de --- 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 7b6c4000cd30..94c7a4d9400a 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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_BCIICHG0x08 @@ -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
[PATCH 02/14] twl4030_charger: use devres for power_supply_register and kzalloc.
From: NeilBrown ne...@suse.de Final allocations/registrations are now managed by devres. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 32 +--- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 58996d252ecf..7b6c4000cd30 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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; } -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 06/14] twl4030_charger: allow fine control of charger current.
From: NeilBrown ne...@suse.de 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 pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- 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 c9a7efbb9141..02d677ef63e2 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -31,6 +31,11 @@ #define TWL4030_BCIMFSTS4 0x10 #define TWL4030_BCICTL10x23 #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 intichg_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 = 20) + cgain = true; + if (bci-ichg_lo = 40) + cgain = true; + if (bci-ichg_hi = 82) + 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 +* 100000, and requires the 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 10. +* 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 101100 (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
[PATCH 05/14] twl4030_charger: split uA calculation into a function.
From: NeilBrown ne...@suse.de We will need this calculation in other places, so create functions to map between register value and uA value. Acked-by: Pavel Machek pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- 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 0e62a5ae56a3..c9a7efbb9141 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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 * 1) / 10; - if (bcictl1 TWL4030_CGAIN) - ret *= 2; - - return ret; + return regval2ua(curr, bcictl1 TWL4030_CGAIN); } /* -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 04/14] twl4030_charger: trust phy to determine when USB power is available.
From: NeilBrown ne...@suse.de 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 ne...@suse.de --- 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 94c7a4d9400a..0e62a5ae56a3 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 11/14] twl4030_charger: add software controlled linear charging mode.
From: NeilBrown ne...@suse.de 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. Orignal-by: Andreas Kemnade andr...@kemnade.info Signed-off-by: NeilBrown ne...@suse.de --- .../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 390db7a55231..c27e8ca8c9db 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 802cdd6d3e00..2c8d85102def 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -24,6 +24,8 @@ #include linux/usb/otg.h #include linux/i2c/twl4030-madc.h +#define TWL4030_BCIMDEN0x00 +#define TWL4030_BCIMDKEY 0x01 #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG0x08 #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_DONEBIT(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 */ #defineCHARGE_OFF 0 #defineCHARGE_AUTO 1 +#defineCHARGE_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
[PATCH 14/14] twl4030_charger: assume a 'charger' can supply maximum current.
From: NeilBrown ne...@suse.de If it cannot, we will stop pulling more current when voltage drops. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c |4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index d0199495dddc..6f0dc971d84d 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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; -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 13/15] twl4030_charger: add ac/mode to match usb/mode
On Fri, 6 Mar 2015 23:59:04 +0200 Grazvydas Ignotas nota...@gmail.com wrote: On Tue, Feb 24, 2015 at 6:33 AM, NeilBrown ne...@suse.de wrote: This allows AC charging to be turned off, much like usb charging. continuous (aka linear) mode maps to the CVENAC (constant voltage) feature of the twl4030. Are you sure? Before your patches CVENAC was set at all times and and charger still worked in automatic mode. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 40 +-- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 6c53f0b601a4..e5a0225ea87e 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -112,7 +112,7 @@ struct twl4030_bci { int ichg_eoc, ichg_lo, ichg_hi; int usb_cur, ac_cur; boolac_is_active; - int usb_mode; /* charging mode requested */ + int usb_mode, ac_mode; /* charging mode requested */ #defineCHARGE_OFF 0 #defineCHARGE_AUTO 1 #defineCHARGE_LINEAR 2 @@ -449,12 +449,18 @@ 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 (enable) - ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC); + if (bci-ac_mode == CHARGE_OFF) + enable = false; + + if (enable bci-ac_mode == CHARGE_LINEAR) + ret = twl4030_clear_set_boot_bci(0, (TWL4030_CVENAC | +TWL4030_BCIAUTOAC)); + else if (enable) + ret = twl4030_clear_set_boot_bci(TWL4030_CVENAC, TWL4030_BCIAUTOAC); else ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0); CVENAC is required to be set for operation on AC without battery (which works fine on most pandora boards). After this patch, when booted without battery, the board will reset before there is a chance to set the linear mode by userspace, because this is called on probe... Yes, it looks like I misunderstood CVENAC a bit - thanks. I've removed 'continuous' mode for AC and no longer clear or set that bit. Thanks, NeilBrown pgplztCo9D_mx.pgp Description: OpenPGP digital signature
[PATCH 13/14] twl4030_charger: Increase current carefully while watching voltage.
From: NeilBrown ne...@suse.de 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 ne...@suse.de --- drivers/power/twl4030_charger.c | 68 ++- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 73c7cd96ebc1..d0199495dddc 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -119,6 +119,18 @@ struct twl4030_bci { #defineCHARGE_AUTO 1 #defineCHARGE_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 intusb_cur_target; + struct delayed_work current_worker; +#defineUSB_CUR_STEP2 /* 20mA at a time */ +#defineUSB_MIN_VOLT475 /* 4.75V */ +#defineUSB_CUR_DELAY msecs_to_jiffies(100) +#defineUSB_MAX_CURRENT 170 /* 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
[PATCH 12/14] twl4030_charger: add ac/mode to match usb/mode
From: NeilBrown ne...@suse.de This allows AC charging to be turned off, much like usb charging. continuous mode is not available though. Acked-by: Pavel Machek pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- .../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 c27e8ca8c9db..35a211b9a172 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 2c8d85102def..73c7cd96ebc1 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -114,7 +114,7 @@ struct twl4030_bci { unsigned intichg_eoc, ichg_lo, ichg_hi; unsigned intusb_cur, ac_cur; boolac_is_active; - int usb_mode; /* charging mode requested */ + int usb_mode, ac_mode; /* charging mode requested */ #defineCHARGE_OFF 0 #defineCHARGE_AUTO 1 #defineCHARGE_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 = 10; /* 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
[PATCH 08/14] twl4030_charger: allow max_current to be managed via sysfs.
From: NeilBrown ne...@suse.de '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 pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- .../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 ..06092209d851 --- /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 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. diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 37f2f40991ee..df031b0123d0 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 = 50; 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 = 50; + else + bci-usb_cur = 10; + bci-event = val; schedule_work(bci-work); @@ -818,6 +883,11 @@ static int __init 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, @@ -842,6 +912,8 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev
[PATCH 07/14] twl4030_charger: distinguish between USB current and 'AC' current
From: NeilBrown ne...@suse.de 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. Acked-by: Pavel Machek pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- 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 02d677ef63e2..37f2f40991ee 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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_BCIICHG0x08 @@ -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 intichg_eoc, ichg_lo, ichg_hi, cur; + unsigned intichg_eoc, ichg_lo, ichg_hi; + unsigned intusb_cur, ac_cur; + boolac_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 = 20) cgain = true; @@ -237,7 +254,7 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) cgain = true; if (bci-ichg_hi = 82) 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 = 50; /* High threshold */ + bci-ac_cur = 50; /* 500mA */ if (allow_usb) - bci-cur = 50; /* 500mA */ + bci-usb_cur = 50; /* 500mA */ else - bci-cur = 10; /* 100mA */ + bci-usb_cur = 10; /* 100mA */ bci-dev = pdev-dev; bci-irq_chg = platform_get_irq(pdev, 0); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org
[PATCH 10/14] twl4030_charger: enable manual enable/disable of usb charging.
From: NeilBrown ne...@suse.de '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 pa...@ucw.cz Signed-off-by: NeilBrown ne...@suse.de --- .../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 06092209d851..390db7a55231 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 2042e7619954..802cdd6d3e00 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -109,10 +109,16 @@ struct twl4030_bci { unsigned intichg_eoc, ichg_lo, ichg_hi; unsigned intusb_cur, ac_cur; boolac_is_active; + int usb_mode; /* charging mode requested */ +#defineCHARGE_OFF 0 +#defineCHARGE_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); @@ -634,6 +642,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; @@ -804,6 +859,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) bci-usb_cur = 50; /* 500mA */ else bci-usb_cur = 10; /* 100mA */ + bci-usb_mode = CHARGE_AUTO; bci-dev = pdev-dev; bci-irq_chg = platform_get_irq(pdev, 0); @@ -890,6 +946,8 @@ static int __init 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); @@ -918,6 +976,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl4030_charger_enable_backup(0, 0
[PATCH 00/14] 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 -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 01/14] twl4030_charger: use devm_request_threaded_irq
From: NeilBrown ne...@suse.de This simplifies the error paths. Signed-off-by: NeilBrown ne...@suse.de --- 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 b07f4e2f2dde..58996d252ecf 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: twl4030_charger: need changes to get probed?
On Sat, 7 Mar 2015 22:01:02 +0100 Sebastian Reichel s...@debian.org wrote: Hi, On Fri, Mar 06, 2015 at 10:24:17PM +0100, Pavel Machek wrote: According to n900 dts, twl4030-bci (aka charger) should be included. its part of twl, but not used on N900 afaik. (But it does not seem to do anything useful on n900. I was hoping for measurement of input voltage, but .. no.) check for rx51-battery. Any ideas why the patch below is needed? platform_driver_probe() does not support deferred probing. Neil, can you take this patch into your series for the next round? I could, but I do wonder if it is the right thing to do. Shouldn't we fix platform_driver_probe() to support deferred probing. As I understand it, it refused to retry a probe if there is an error, and the comments suggest that such retrying is avoided because it would be a waste of time: /* * Prevent driver from requesting probe deferral to avoid further * futile probe attempts. */ In this case, it isn't futile. Earlier there is a comment saying: * Use this instead of platform_driver_register() when you know the device * is not hotpluggable and has already been registered, and you want to * remove its run-once probe() infrastructure from memory after the driver * has bound to the device. I presume all this applies. I assume that the only problem is a probe-order thing. So maybe we should fix platform_driver_probe() to do the right thing with -EPROBEDEFER?? Trouble is, I really don't understand the point or mechanism for platform_driver_probe(), so I cannot suggest anything. But I have been annoyed before that platform_driver_probe doesn't cope with EPROBEDEFER, so I would like it fixed. NeilBrown pgp5MxAjPABro.pgp Description: OpenPGP digital signature
Re: [PATCH 15/15] twl4030_charger: assume a 'charger' can supply maximum current.
On Mon, 2 Mar 2015 22:29:39 +0100 Pavel Machek pa...@ucw.cz wrote: On Tue 2015-02-24 15:33:53, NeilBrown wrote: If it cannot, we will stop pulling more current when voltage drops. Can you justify it a bit more? I mean... maybe there's a fuse in the charger? Or maybe it will supply the current but overheat in the process? (USB_MAX_CURRENT is 500mA or 1.7A?) USB_MAX_CURRENT is 1.7A - the most the twl4030 will manage. The relevant specs say that a charger can deliver from 0.5A to 5.0A at between 4.75 and 5.25 volts. They don't, as far as I can tell, describe how a gadget can determine where in those ranges the charger is actually happy. My understanding of electronics suggests that if you start to pull too much current, the voltage will start to drop. It is that voltage drop across internal resistance which causes over-heating. So if voltage is above 4.75 volts, it seems reasonable to assume that the charger is happy. The code currently (see previous patch) ramps up the current until the voltage drops below 4.75, or until the maximum is reached. If the voltage drops, it backs off. The current should only be too high for 100ms. Hopefully not too long. I'm very open if anyone can suggest a more safe way to make full use of a charger without risking excess current draw, but I cannot find one. Maybe I should measure the unloaded voltage, and not let it drop more than 0.25V below that? I'm also very open to someone finding out how to get the twl4030 to detect a D+ and D- shorted charger. I think it is supposed to be able to do this, but I haven't managed to make it work yet. Thanks, NeilBrown pgpVJ8GmrAVeE.pgp Description: OpenPGP digital signature
Re: [PATCH 12/15] twl4030_charger: add software controlled linear charging mode.
On Mon, 2 Mar 2015 22:09:26 +0100 Pavel Machek pa...@ucw.cz wrote: On Tue 2015-02-24 15:33:52, NeilBrown wrote: Add a 'continuous' option for usb charging which enabled the linear charging mode of the twl4030. Documentation/ :-). ! Linear charging does a good job with not so reliable power sources, since several voltage controlling is then often too intelligent. Parse error. 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. = 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. Orignal-by: Andreas Kemnade andr...@kemnade.info Signed-off-by: NeilBrown ne...@suse.de + 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 */ USB + 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 Heat save? Is ignoring over-current good idea? The data sheet refers to a flag HSEN which is Heat Save function enable. your guess is as good as mine. We don't currently have any code for responding to warnings. Maybe we should. For now this feature is use at your own risk - and one person has found it very useful. @@ -650,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; Time for loop? When we get one more mode it will be :-) Thanks, NeilBrown pgpSRiRJsR3zi.pgp Description: OpenPGP digital signature
Re: [PATCH 04/15] twl4030_charger: use runtime_pm to keep usb phy active while charging.
On Wed, 25 Feb 2015 07:24:43 + Lee Jones lee.jo...@linaro.org wrote: On Tue, 24 Feb 2015, NeilBrown wrote: 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. Signed-off-by: NeilBrown ne...@suse.de --- drivers/mfd/twl-core.c |9 - Acked-by: Lee Jones lee.jo...@linaro.org Thanks. I guess that clear it to go in though the 'power supply' tree. NeilBrown pgpPxECaAk_kV.pgp Description: OpenPGP digital signature
Re: [PATCH 09/15] twl4030_charger: allow max_current to be managed via sysfs.
On Mon, 2 Mar 2015 22:05:26 +0100 Pavel Machek pa...@ucw.cz wrote: On Tue 2015-02-24 15:33:52, NeilBrown wrote: '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. Does this need Documentation update? Oh all right... I've created the relevant documentation in Documentation/ABI. It seems persistence pays off :-) Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 76 +++ 1 file changed, 76 insertions(+) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index bfc9b808301e..b0242786d047 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -527,6 +529,67 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) return IRQ_HANDLED; } +/* + * sysfs max_current store + */ That's not exactly useful comment. Now: * 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) { + if (bci-ac_cur == cur) + return n; + bci-ac_cur = cur; + } else { + if (bci-usb_cur == cur) + return n; + bci-usb_cur = cur; + } + twl4030_charger_update_current(bci); + return (status == 0) ? n : status; +} Uff. but we know that status == 0 at this point, no? Yes. Fixed. Also... is optimalization of not calling update_current() when nothing changed worth it? Probably not... and code looks a lot nicer if I remove that. So I have. +/* + * 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); +} Is this in uA or mA? uA. Ok. uA, now described in Documentation/ABI/testing/sysfs-class-power-twl4030 +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. Acked-by: Pavel Machek pa...@ucw.cz Pavel Thanks, NeilBrown pgpUEAx3Scuug.pgp Description: OpenPGP digital signature
Re: [PATCH 14/15] twl4030_charger: Increase current carefully while watching voltage.
On Mon, 2 Mar 2015 22:29:45 +0100 Pavel Machek pa...@ucw.cz wrote: On Tue 2015-02-24 15:33:53, NeilBrown wrote: 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 configure Standard Downstream Port, it may not be possible to reliably pull 500mA - depending on cable quality and source configured? quality I have reports of charging failure due to the voltage dropping too low. To address both these concern, this patch introduce incremental concerns. 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 long, the target current is reduced too low? 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.7mA imposed by the hardware. 1.7A? @@ -249,8 +261,14 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) cur = bci-ac_cur; bci-ac_is_active = 1; } else { - cur = bci-usb_cur; + cur = bci-usb_cur_actual; usb_cur_actual is not a really great variable name... I changed usb_cur to usb_cur_target and usb_cur_actual to usb_cur. bci-ac_is_active = 0; + if (cur bci-usb_cur) { + cur = bci-usb_cur; + bci-usb_cur_actual = cur; + } + if (cur bci-usb_cur) + schedule_delayed_work(bci-current_worker, USB_CUR_DELAY); } /* First, check thresholds and see if cgain is needed */ @@ -379,6 +397,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_actual, + bci-usb_cur); + + if (v USB_MIN_VOLT) { + /* Back up and stop adjusting. */ + bci-usb_cur_actual -= USB_CUR_STEP; + bci-usb_cur = bci-usb_cur_actual; + } else if (bci-usb_cur_actual = bci-usb_cur || + bci-usb_cur_actual + USB_CUR_STEP USB_MAX_CURRENT) { + /* Reach target and volts are OK - stop */ Reached ... and the voltage is OK - stop. Thanks for all your proof-reading :-) NeilBrown pgpJjaK_GqbTb.pgp Description: OpenPGP digital signature
Re: [PATCH 06/15] twl4030_charger: split uA calculation into a function.
On Mon, 2 Mar 2015 22:05:18 +0100 Pavel Machek pa...@ucw.cz wrote: On Tue 2015-02-24 15:33:51, NeilBrown wrote: We will need this calculation in other places, so create functions to map between register value and uA value. Signed-off-by: NeilBrown ne...@suse.de Acked-by: Pavel Machek pa...@ucw.cz Thanks. +static int regval2ua(int regval, bool cgain) +{ + if (cgain) + return (regval * 16618 - 8500 * 1000) / 5; + else + return (regval * 16618 - 8500 * 1000) / 10; +} int res = (regval * 16618 - 8500 * 1000); if (cgain) return res / 5; return res / 10; ? Pavel Maybe ... not sure it is really more readable. I think I'll leave it as is. Thanks, NeilBrown pgpGQcRlihT4k.pgp Description: OpenPGP digital signature
Re: [PATCH 1/4] usb: phy: twl4030: make runtime pm more reliable.
On Mon, 2 Mar 2015 22:03:59 +0100 Pavel Machek pa...@ucw.cz wrote: Hi! + status_changed = + (twl-linkstat == OMAP_MUSB_VBUS_VALID || +twl-linkstat == OMAP_MUSB_ID_GROUND) + != + (status == OMAP_MUSB_VBUS_VALID || +status == OMAP_MUSB_ID_GROUND); twl-linkstat = status; ... @@ -768,6 +770,10 @@ static int twl4030_usb_remove(struct platform_device *pdev) /* disable complete OTG block */ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + if (twl-linkstat == OMAP_MUSB_VBUS_VALID || + twl-linkstat == OMAP_MUSB_ID_GROUND) + pm_runtime_put_noidle(twl-dev); pm_runtime_mark_last_busy(twl-dev); inline function returning (x == OMAP_MUSB_VBUS_VALID || x == OMAP_MUSB_ID_GROUND) would really help readability here. Thanks, Pavel Good idea. I've done that. The function is called cable_present(). Thanks, NeilBrown pgpbO85e0cIau.pgp Description: OpenPGP digital signature
Re: [PATCH 08/15] twl4030_charger: distinguish between USB current and 'AC' current
On Mon, 2 Mar 2015 22:05:10 +0100 Pavel Machek pa...@ucw.cz wrote: Hi! The twl4030 charger has two current sources, 'USB' and 'AC' (which is really DC of course...). 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. Signed-off-by: NeilBrown ne...@suse.de @@ -103,7 +104,9 @@ struct twl4030_bci { /* ichg values in uA. If any are 'large', we set CGAIN to ichg/_cur values * '1' which doubles the range for half the precision. */ - int ichg_eoc, ichg_lo, ichg_hi, cur; + int ichg_eoc, ichg_lo, ichg_hi; + int usb_cur, ac_cur; + /* If VAC exceeds 4.5V (MADC 11) and ac is enabled, set current +* for 'ac' /* * If AC voltage exceeds 4.5V (MADC 11) and AC is enabled, set current * for 'ac'. Acked-by: Pavel Machek pa...@ucw.cz Thanks - I made those changes, or something much like them. NeilBrown pgpIpeDsG7pEg.pgp Description: OpenPGP digital signature
Re: [PATCH 2/4] usb: phy: twl4030: allow charger to see usb current draw limits.
On Mon, 2 Mar 2015 22:03:55 +0100 Pavel Machek pa...@ucw.cz wrote: On Tue 2015-02-24 14:40:37, NeilBrown wrote: The charger needs to know when a USB gadget has been enumerated and what the agreed maximum current was so that it can adjust charging accordingly. So define a set_power() function to record the permitted draw, and pass a pointer to that when sending USB_EVENT_ENUMERATED notification. Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 27 +-- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 97c59074233f..023fe150c7a1 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -163,6 +163,11 @@ struct twl4030_usb { enum omap_musb_vbus_id_status linkstat; boolvbus_supplied; + /* Permitted vbus draw - only meaningful after add in mA? +* USB_EVENT_ENUMERATED +*/ + unsignedvbus_draw; + struct delayed_work id_workaround_work; Yes. I make it 'unsigned int' too. Thanks, NeilBrown pgpnzygiI3oWb.pgp Description: OpenPGP digital signature
Re: [PATCH 07/15] twl4030_charger: allow fine control of charger current.
On Mon, 2 Mar 2015 22:05:01 +0100 Pavel Machek pa...@ucw.cz wrote: Hi! + /* ichg values in uA. If any are 'large', we set CGAIN to +* '1' which doubles the range for half the precision. +*/ + int ichg_eoc, ichg_lo, ichg_hi, cur; We should really get uA_t. Maybe. 32bits allows for 4000A, which should be plenty... I made it unsigned int. [Plus, this is not kernel comment style, but...] Fixed + /* For ichg_eoc, reg value must be 100000, we only +* set the in high nibble. +*/ Confused. High nibble is 0xf0, right? does this help? /* * For ichg_eoc, the hardware only supports reg values matching * 100000, and requires the be stored in the high nibble * of TWL4030_BCIMFTH8 */ + /* And finally, set the current. This is stored in +* two registers. */ + reg = ua2regval(bci-cur, cgain); + /* we have only 10 bit */ bits @@ -562,6 +698,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 */ Low + bci-ichg_hi = 50; /* High threshold */ Acked-by: Pavel Machek pa...@ucw.cz Thanks again! NeilBrown pgp0d5hnlrrkh.pgp Description: OpenPGP digital signature
Re: [PATCH 4/4] usb: phy: twl4030: test ID resistance to see if charger is present.
On Mon, 2 Mar 2015 22:04:44 +0100 Pavel Machek pa...@ucw.cz wrote: On Tue 2015-02-24 14:40:37, NeilBrown wrote: If an 'A' plug is inserted, ID should be pulled to ground. If a 'B' plug, then ID should be floating. If an Accessory Charger Adapter is inserted, then ID will be neither grounded nor floating. In this case tell the USB subsystem that it is an A plug, and the battery charging subsystem that it is a charger. Fortunately, this will treat the Openmoko charger (and other similar chargers) as a charger. Signed-off-by: NeilBrown ne...@suse.de I guess signed-off-by should be real name, so I'd add a space.. This is how I always sign-off my name (2858 times in git log) so I don't think I'll change it now. Acked-by: Pavel Machek pa...@ucw.cz Thanks. --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -596,9 +596,31 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) struct twl4030_usb *twl = _twl; enum omap_musb_vbus_id_status status; bool status_changed = false; + bool found_charger = false; status = twl4030_usb_linkstat(twl); + if (status == OMAP_MUSB_ID_GROUND || + status == OMAP_MUSB_VBUS_VALID) { + /* We should check the resistance on the ID pin. +* If not a Ground or Floating, then this is +* likely a charger charger., and I guess kernel comments should have /* on separate line. Yep. So it will draw .5A from the charger? 1A? 2A? Pavel That is up to the charger driver. The phy just detects what is there, it doesn't decide what to do with it. Thanks, NeilBrown pgpJgxw0afmzg.pgp Description: OpenPGP digital signature
Re: [PATCH 11/15] twl4030_charger: enable manual enable/disable of usb charging.
On Mon, 2 Mar 2015 22:03:42 +0100 Pavel Machek pa...@ucw.cz wrote: On Tue 2015-02-24 15:33:52, NeilBrown wrote: '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. This should go to Documentation/ You mean in Documentation/ABI/stable I guess?? That strikes me as a failed experiment - there is hardly anything there. I'd be very happy to put the documentation with the code if there was some mechanism to automatically extract it. But I really see little value in Documentation/ABI. Or did you mean somewhere else? Signed-off-by: NeilBrown ne...@suse.de Conflicts: drivers/power/twl4030_charger.c Is it supposed to be here? ah, no. Gone now. Thanks. diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 01090a440583..19e8dbb1303e 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -107,6 +107,9 @@ struct twl4030_bci { int ichg_eoc, ichg_lo, ichg_hi; int usb_cur, ac_cur; boolac_is_active; + int usb_mode; /* charging mode requested */ +#defineCHARGE_OFF 0 +#defineCHARGE_AUTO 1 unsigned long event; }; @@ -377,6 +380,8 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) { int ret; n + if (bci-usb_mode == CHARGE_OFF) + enable = false; Sometimes, you use = false and sometimes = 0 when assigning to bools... I found a fixed a few - thanks. @@ -629,6 +634,54 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, return NOTIFY_OK; } +/* + * sysfs charger enabled store + */ +static char *modes[] = { off, auto }; I'd move this before the comment. Or better near struct twl4030_bci. Makes sense. Done. Thanks. +static DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show, + twl4030_bci_mode_store); + static int twl4030_charger_get_current(void) { int curr; @@ -799,6 +852,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) bci-usb_cur = 50; /* 500mA */ else bci-usb_cur = 10; /* 100mA */ + bci-usb_mode = CHARGE_AUTO; bci-dev = pdev-dev; bci-irq_chg = platform_get_irq(pdev, 0); @@ -885,6 +939,8 @@ static int __init 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); @@ -917,6 +973,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) device_remove_file(bci-usb.dev, dev_attr_max_current); device_remove_file(bci-ac.dev, dev_attr_max_current); + device_remove_file(bci-usb.dev, dev_attr_mode); move the line above for consistency with create? Yep. Acked-by: Pavel Machek pa...@ucw.cz Thanks a lot! NeilBrown pgpcgY61j9ce8.pgp Description: OpenPGP digital signature
Re: [PATCH 3/4] usb: phy: twl4030: add support for reading restore on ID pin.
On Mon, 2 Mar 2015 22:04:31 +0100 Pavel Machek pa...@ucw.cz wrote: Hi! The twl4030 phy can measure, with low precision, the resistance-to-ground of the ID pin. Add a function to read the value, and export the result via sysfs. If the read fails, which it does sometimes, try again in 50msec. Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 63 + 1 file changed, 63 insertions(+) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 023fe150c7a1..759950898df9 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -374,6 +374,56 @@ static void twl4030_i2c_access(struct twl4030_usb *twl, int on) } } +enum twl4030_id_status { + TWL4030_GROUND, + TWL4030_102K, + TWL4030_200K, + TWL4030_440K, + TWL4030_FLOATING, + TWL4030_ID_UNKNOWN, +}; +static char *twl4030_id_names[] = { + ground, + 102k, + 200k, + 440k, New /sys files should be documented somewhere...? Preferably with the code... Does it make sense to change 440k - 440KOhm? Interesting question. I prefer to avoid including units in files - bare numbers is better. But there is no number to match floating unless I spell it out as infinity, and wouldn't be helpful. Certainly K would be preferred over k, and given that I have ground and floating, it is more consistent to include the Ohm These are really names, not measures of resistance. The data sheet calls them: ID_RES_FLOAT (or sometimes ID_FLOAT) ID_RES_440K ID_RES_200K ID_RES_102K ID_GND(or sometimes ID_RES_GND) So using those names is defensible. I think I'll change them all to upper case, but leave out the Ohm. My justification is consistency with the data sheet. Plus I guess you need to update Documentation/ I guess I'll need to give in to this eventually :-) Acked-by: Pavel Machek pa...@ucw.cz Thanks, NeilBrown pgpAdjFjGRGQh.pgp Description: OpenPGP digital signature
Re: [PATCH 3/4] mmc: sdio: support switching to 1-bit before turning off clocks
On Tue, 3 Mar 2015 14:53:55 -0800 Tony Lindgren t...@atomide.com wrote: * NeilBrown ne...@suse.de [150223 18:47]: According to section 7.1.2 of http://www.sandisk.com/media/File/OEM/Manuals/SD_SDIO_specsv1.pdf In the case where the interrupt mechanism is used to wake the host while the card is in a low power state (i.e. no clocks), Both the card and the host shall be placed into the 1-bit SD mode prior to stopping the clock. This is particularly important for the Marvell libertas wifi chip in the GTA04. While in 4-bit mode it will only signal an interrupt when the clock is running (which is why setting CLKEXTFREE is important). In 1-bit mode, the interrupt is asynchronous (explained in OMAP3 TRM description of the CIRQ flag to MMCHS_STAT: In 1-bit mode, interrupt source is asynchronous (can be a source of asynchronous wakeup). In 4-bit mode, interrupt source is sampled during the interrupt cycle. ) It is awkward to simply set 1-bit mode in -runtime_suspend as that will call mmc_set_ios which calls ops-set_ios(), which will likely call pm_runtime_get_sync(), on the device that is currently suspending. This deadlocks. So: - create a work_struct to schedule setting of 1-bit mode - introduce an 'sdio_narrowed' state flag which transitions: 0 (normal) - 1 (convert to 1-bit pending) - 2 (have switch to 1-bit mode) - 0 (normal) - create a function mmc_sdio_want_no_clocks() which can be called when the driver wants to turn off clocks (presumably after an idle timeout). This either succeeds (in 1-bit mode) or fails and schedules the work to switch to 1-bit mode. - when the host is claimed, if sdio_narrowed is 2, restore the 4-bit bus - When the host is released, if sdio_narrowed is 1, then some caller other than our worker claimed the host first, so clear sdio_narrowed. This all allows a graceful and race-free switch to 1-bit mode before switching off the clocks, if SDIO interrupts are enabled. A host should call mmc_sdio_want_no_clocks() when about to turn off clocks if sdio interrupts are enabled, and the -disable() function should not use a timeout (pm_runtime_put_autosuspend) if -sdio_narrowed is 2. Wow! A mystery finally solved for why libertas_sdio using devices like overo won't work for the wakeirqs.. The interface has to be in 1-bit mode for libertas to produce any SDIO interrupts.. Below is a patch enabling some more SDIO wakeirqs. Seems to work on overo now too :) So tor the whole series, please feel free to add: Tested-by: Tony Lindgren t...@atomide.com Thanks a lot! 8 --- From: Tony Lindgren t...@atomide.com Date: Thu, 26 Feb 2015 16:16:03 -0800 Subject: [PATCH] ARM: dts: Fix omap3 SDIO wakeirqs for devices using sdio_libertas Turns out the the MMC interface needs to be in 1-bit mode for the libertas card to send any SDIO interrupts as pointed out by NeilBrown ne...@suse.de. Now that the MMC framework is getting fixed for setting 1-bit mode for idle, we can enable SDIO wakeirqs for libertas using devices too. Cc: Andreas Fenkart afenk...@gmail.com Cc: Ash Charles a...@gumstix.com Cc: Florian Vaussard florian.vauss...@epfl.ch Cc: NeilBrown n...@brown.name Signed-off-by: Tony Lindgren t...@atomide.com --- a/arch/arm/boot/dts/omap3-gta04.dtsi +++ b/arch/arm/boot/dts/omap3-gta04.dtsi @@ -367,6 +367,7 @@ }; mmc2 { + interrupts-extended = intc 86 omap3_pmx_core 0x12e; vmmc-supply = vaux4; bus-width = 4; ti,non-removable; I had + interrupts-extended = intc 86 gpio5 5 0; /* GPIO_133 */ + pinctrl-names = default, idle; + pinctrl-0 = mmc2_pins; + pinctrl-1 = mmc2_cirq_pin; together with + mmc2_cirq_pin: pinmux_cirq_pin { + pinctrl-single,pins = + OMAP3_CORE1_IOPAD(0x215e, PIN_INPUT_PULLUP | MUX_MODE4) + ; + }; + and a longer definition for mmc2_pins. Is that one line reconfigure the pin on demand? How does that work? Thanks, NeilBrown pgpJ6LLS5NdnN.pgp Description: OpenPGP digital signature
Re: [PATCH 0/6] mmc: omap_hsmmc: simplify cover/card detect logic
On Tue, 3 Mar 2015 13:28:12 +0100 Andreas Fenkart afenk...@gmail.com wrote: These patches are trying to clean up the cover/card detect logic. Mobile phones (some) have no card detect pin, but can detect if the cover is removed. The purpose is the same; detect if card is being added/removed, but the details differ. When the cover is removed, it does not mean the card is gone. But it might, since it is accessible now. It's like a warning. All the driver does is to limit write access to the card, see protect_card flag. In contrast, card detect notifies us after the fact, e.g. card is gone, card is inserted. While cover detect is only used by one platform (rx51), it complicates the card detect logic. By separating the code paths they both become easier to understand and maintain Patches have been tested by reverting: 95bebb5696ab 'mmc: omap_hsmmc: use mmc_of_parse to parse common mmc configuration' otherwise gpio detection is handled by mmc_of_parse Wouldn't it make more sense to put this core in mmc/core rather than in just one host controller? That way it would be available to all hosts, and you wouldn't need to revert that patch. NeilBrown pgplTpnSDXJsy.pgp Description: OpenPGP digital signature
Re: [PATCH] OMAP: DSS: DPI: disable vt-switch on suspend/resume.
On Wed, 25 Feb 2015 12:03:36 +0200 Tomi Valkeinen tomi.valkei...@ti.com wrote: On 25/02/15 11:37, NeilBrown wrote: These devices do not need to return to non-graphic console for suspend, so disable that option. This means there is less work to do in the suspend/resume cycle, making it smoother and cheaper. Signed-off-by: NeilBrown n...@brown.name -- Hi Tomi, I wonder if you would consider this patch too. It works for me and removes pointless vt switching. I assume no-one would need or want that switching on suspend/resume but I guess I cannot be certain. Maybe a module-parameter could be used if there was any uncertainty. I was just yesterday picking patches from TI's kernel on top of mainline, and there's a similar one for omapfb and for omapdrm. Here's for omapfb: http://git.ti.com/android-sdk/kernel-video/commit/5915d8744c927307987b12b14bc6aa37b3bac05c?format=patch The patch in TI's kernel claims it's needed to make resume work. You're saying it just optimizes suspend/resume. I hadn't picked this up earlier, because I didn't understand why pm_set_vt_switch() would be needed to make resume work. And never had time to study it, so I still don't know what it's about... Looking at the drivers/tty/vt/vt_ioctl.c, it sounds to me that we should always do pm_set_vt_switch(0), as omapdss restores everything just fine on resume. Although it makes me wonder how it works if there are two display controllers, one needing the switch and the other not... I wondered that too. It would seem to make more sense for pm_set_vt_switch(0) to be the default, that drivers which need the textmode switch should call (1). But I suspect there are historical reasons for the current situation. My experience is that suspend works just find without this setting, and I don't even notice the vt switch, unless I have DEBUG_DRIVERS which slows suspend/resume down so much (writing lots of test to a serial console) that the transition is noticeable. I made the patch because I think it is the right thing to do, rather than because it fixed a particular symptom. I suspect that if suspend doesn't work without this patch, then something very different is being done in user-space. Maybe some other display manager, not X. Maybe the X server is running on vt1 rather than vt7. Your patch does the call in a bit odd place, so I'll be queuing the patches for omapfb and omapdrm. Or actually, maybe the call could be done in one place in omapdss driver, as you do, but in omap_dsshw_probe(). I just figured that it had to be in some 'probe' function somewhere - I have no opinion about which one (maybe all?-). I'm perfectly happy with it appearing anywhere that affects omap dss devices. One thing I was reminded while testing and may as well mention: I usually blank my display before suspending (using FBIOBLANK/FB_BLANK_POWERDOWN ioctls). However if I don't then I get a warning: [ 87.261077] WARNING: CPU: 0 PID: 1901 at ../drivers/video/fbdev/omap2/dss/dispc.c:558 dispc_mgr_go+0x20/0x8c() [ 87.261138] Modules linked in: ipv6 usb_f_ecm g_ether usb_f_rndis u_ether libcomposite configfs hso w1_bq27000 libertas_sdio libertas cfg80211 omap_hdq itg3200 ehci_omap ehci_hcd uio_pdrv_genirq uio [ 87.261169] CPU: 0 PID: 1901 Comm: Xorg Not tainted 4.0.0-rc1-gta04+ #538 [ 87.261169] Hardware name: Generic OMAP36xx (Flattened Device Tree) [ 87.261199] [c00137e0] (unwind_backtrace) from [c00113a4] (show_stack+0x10/0x14) [ 87.261230] [c00113a4] (show_stack) from [c0033938] (warn_slowpath_common+0x80/0xa8) [ 87.261260] [c0033938] (warn_slowpath_common) from [c0033978] (warn_slowpath_null+0x18/0x1c) [ 87.261291] [c0033978] (warn_slowpath_null) from [c0232538] (dispc_mgr_go+0x20/0x8c) [ 87.261291] [c0232538] (dispc_mgr_go) from [c023e30c] (dss_set_go_bits+0xd4/0x100) [ 87.261322] [c023e30c] (dss_set_go_bits) from [c023f308] (omap_dss_mgr_apply+0x16c/0x1b8) [ 87.261352] [c023f308] (omap_dss_mgr_apply) from [c0250790] (omapfb_apply_changes+0x428/0x488) [ 87.261352] [c0250790] (omapfb_apply_changes) from [c0251024] (omapfb_set_par+0x74/0xb0) [ 87.261383] [c0251024] (omapfb_set_par) from [c02281a4] (fb_set_var+0x250/0x330) [ 87.261413] [c02281a4] (fb_set_var) from [c021fa24] (fbcon_blank+0x6c/0x260) [ 87.261444] [c021fa24] (fbcon_blank) from [c0275464] (do_unblank_screen+0xf8/0x19c) [ 87.261474] [c0275464] (do_unblank_screen) from [c026c268] (complete_change_console+0x50/0xcc) [ 87.261474] [c026c268] (complete_change_console) from [c026ccd8] (vt_ioctl+0x9f4/0x1238) [ 87.261505] [c026ccd8] (vt_ioctl) from [c02621e8] (tty_ioctl+0xc48/0xcac) [ 87.261535] [c02621e8] (tty_ioctl) from [c00dc344] (do_vfs_ioctl+0x5b0/0x61c) [ 87.261566] [c00dc344] (do_vfs_ioctl) from [c00dc3e4] (SyS_ioctl+0x34/0x58) [ 87.261566] [c00dc3e4] (SyS_ioctl) from [c000ea40] (ret_fast_syscall+0x0/0x34) I think the Xserver is responding to a 'suspend' notification
Re: advanced LED controllers
On Wed, 25 Feb 2015 22:49:41 +0100 Pavel Machek pa...@ucw.cz wrote: On Mon 2015-02-23 16:58:36, Felipe Balbi wrote: On Mon, Feb 23, 2015 at 11:34:57PM +0100, Pavel Machek wrote: On Thu 2015-02-19 15:14:24, Felipe Balbi wrote: Hi, Do we have support for LED controllers which can handle patterns of different kinds ? I mean, currently, if we have an LED controller such as TPIC2810 [1] which can control 8 different leds and each LED corresponds to one bit on register 0x44, we could control leds by just playing a wave file on the controller and create easy patterns with that. [1] http://www.ti.com/product/tpic2810 ps: tpic2810 is probably the simplest example, lp551, lp5523 and others have even more advanced pattern engines which can even handle RGB leds. Well... some more advanced pattern engines can actually run code, up to and including prime number computation. So yes, this is complex, and how to handle it nicely is a question... I have notcc to compile for that. right, the point is that this is a solution which only works with lp5523 and IMO linux led subsystem should do a little more for such devices. Well, question is what we want. Possibilities I see: 1) We won't support all the features, just some common subset. Kernel will get commands for LED controller and translate them. Question is what reasonable subset is, then. I guess delay, set led brightness to X, jump would be minimal shared command set. lp5523 can do also slowly increase/decrease brightness to X (I believe we should support that one), arithmetics, conditional jumps, and communications between 3 threads. 2) We want to support all the features. I guess that would mean doing compilation in userspace, and having compiler for each led controller. Having common source code would still be nice. Pavel All (most) current options for controlling LEDs are based on what a user might want, rather than what the hardware can provide. I think it would be good to keep that approach, but add more interesting functions which each hardware can support in whichever way suits it best. So ramp_blink which allow a ramp on/off time to be specified would be useful. audio_meter which allows a particular sound card (or output or something) to be specified would also be useful. You could also specify a what volume the LED saturates. Then if you set each led on a given controller to saturate at different level and to use the same sound source, then you could get the graphic equaliser effect. Maybe 'blinking' should have a 'synchronise' setting to that a bunch of LEDs can be synchonised so you can create a cylon eye effect. i.e. don't focus on the low-level 'what can we provide' but on the high level what might users want. NeilBrown pgpmXIEFj4kez.pgp Description: OpenPGP digital signature
Re: [PATCH] OMAPDSS: restore name sysfs entry.
On Wed, 25 Feb 2015 11:32:18 +0200 Tomi Valkeinen tomi.valkei...@ti.com wrote: Yep, I don't think so. In any case, all this is to be deprecated, and as soon as omapdrm driver works reliably that should be the driver to use. How close is that? Is it worth experimenting yet? Is there an xorg driver available? Maybe a wiki page telling me how to set it up? http://processors.wiki.ti.com/index.php/Linux_Core_DSS_User%27s_Guide seems to have some useful suggestions, but no mention of Xorg... Thanks, NeilBrown So of course we need to keep omapfb working for the years to come, but I'd rather not add any new sysfs files for a soon deprecated driver. Tomi pgpYXnUpYaOd7.pgp Description: OpenPGP digital signature
[PATCH] OMAP: DSS: DPI: disable vt-switch on suspend/resume.
These devices do not need to return to non-graphic console for suspend, so disable that option. This means there is less work to do in the suspend/resume cycle, making it smoother and cheaper. Signed-off-by: NeilBrown n...@brown.name -- Hi Tomi, I wonder if you would consider this patch too. It works for me and removes pointless vt switching. I assume no-one would need or want that switching on suspend/resume but I guess I cannot be certain. Maybe a module-parameter could be used if there was any uncertainty. thanks, NeilBrown diff --git a/drivers/video/fbdev/omap2/dss/dpi.c b/drivers/video/fbdev/omap2/dss/dpi.c index f83e7b030249..4a29bab4ade3 100644 --- a/drivers/video/fbdev/omap2/dss/dpi.c +++ b/drivers/video/fbdev/omap2/dss/dpi.c @@ -32,6 +32,7 @@ #include linux/string.h #include linux/of.h #include linux/clk.h +#include linux/suspend.h #include video/omapdss.h @@ -798,6 +799,8 @@ static int omap_dpi_probe(struct platform_device *pdev) mutex_init(dpi-lock); dpi_init_output(pdev); + /* No need to vt_switch in suspend/resume */ + pm_set_vt_switch(0); return 0; } pgpuk4wfmxlqZ.pgp Description: OpenPGP digital signature
Re: [PATCH] OMAPDSS: restore name sysfs entry.
On Wed, 25 Feb 2015 10:49:58 +0200 Tomi Valkeinen tomi.valkei...@ti.com wrote: Hi, On 24/02/15 22:31, NeilBrown wrote: On Tue, 24 Feb 2015 12:40:32 +0200 Tomi Valkeinen tomi.valkei...@ti.com wrote: Hi, On 24/02/15 11:37, NeilBrown wrote: commit 303e4697e762dc92a40405f4e4b8aac02cd0d70b OMAPDSS: rename display-sysfs 'name' entry broke the xorg X server on my device as it couldn't find the display any more. It needs the 'name' file and now there isn't one. That commit claims that 'name' is not compatible with i2c or spi. i2c does register it own 'name' file, but spi does not, hence my problem - I have an spi display. So create a special case for i2c: add the name attribute for non-i2c devices. How about this patch instead. It separates the underlying device's sysfs directory from the displayX directory, and allows us to have name display_name. So it should work for any device type. From 8e411fa684d42fca35628b41837c6d72e87aaff0 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen tomi.valkei...@ti.com Date: Wed, 25 Feb 2015 10:23:58 +0200 Subject: [PATCH] OMAPDSS: fix regression with display sysfs files omapdss's sysfs directories for displays used to have 'name' file, giving the name for the display. This file was later renamed to 'display_name' to avoid conflicts with i2c sysfs 'name' file. Looks like at least xserver-xorg-video-omap3 requires the 'name' file to be present. To fix the regression, this patch creates new kobjects for each display, allowing us to create sysfs directories for the displays. This way we have the whole directory for omapdss, and there will be no sysfs file clashes with the underlying display device's sysfs files. We can thus add the 'name' sysfs file back. Signed-off-by: Tomi Valkeinen tomi.valkei...@ti.com --- drivers/video/fbdev/omap2/dss/display-sysfs.c | 179 ++ include/video/omapdss.h | 1 + 2 files changed, 96 insertions(+), 84 deletions(-) diff --git a/drivers/video/fbdev/omap2/dss/display-sysfs.c b/drivers/video/fbdev/omap2/dss/display-sysfs.c index 5a2095a98ed8..12186557a9d4 100644 --- a/drivers/video/fbdev/omap2/dss/display-sysfs.c +++ b/drivers/video/fbdev/omap2/dss/display-sysfs.c @@ -28,44 +28,22 @@ #include video/omapdss.h #include dss.h -static struct omap_dss_device *to_dss_device_sysfs(struct device *dev) +static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = NULL; - - for_each_dss_dev(dssdev) { - if (dssdev-dev == dev) { - omap_dss_put_device(dssdev); - return dssdev; - } - } - - return NULL; -} - -static ssize_t display_name_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); - return snprintf(buf, PAGE_SIZE, %s\n, dssdev-name ? dssdev-name : ); } -static ssize_t display_enabled_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); - return snprintf(buf, PAGE_SIZE, %d\n, omapdss_device_is_enabled(dssdev)); } -static ssize_t display_enabled_store(struct device *dev, - struct device_attribute *attr, +static ssize_t display_enabled_store(struct omap_dss_device *dssdev, const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int r; bool enable; @@ -90,19 +68,16 @@ static ssize_t display_enabled_store(struct device *dev, return size; } -static ssize_t display_tear_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); return snprintf(buf, PAGE_SIZE, %d\n, dssdev-driver-get_te ? dssdev-driver-get_te(dssdev) : 0); } -static ssize_t display_tear_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t display_tear_store(struct omap_dss_device *dssdev, + const char *buf, size_t size) { - struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); int r; bool te; @@ -120,10 +95,8 @@ static ssize_t display_tear_store(struct device *dev, return size; } -static ssize_t display_timings_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf) { - struct
Re: [PATCH] OMAPDSS: restore name sysfs entry.
On Tue, 24 Feb 2015 12:40:32 +0200 Tomi Valkeinen tomi.valkei...@ti.com wrote: Hi, On 24/02/15 11:37, NeilBrown wrote: commit 303e4697e762dc92a40405f4e4b8aac02cd0d70b OMAPDSS: rename display-sysfs 'name' entry broke the xorg X server on my device as it couldn't find the display any more. It needs the 'name' file and now there isn't one. That commit claims that 'name' is not compatible with i2c or spi. i2c does register it own 'name' file, but spi does not, hence my problem - I have an spi display. So create a special case for i2c: add the name attribute for non-i2c devices. What X driver is that? What's it doing with the display name? Is it just using the display name to show something for the user, and the returned value can be essentially any string? Tomi /usr/lib/xorg/modules/drivers/omapfb_drv.so from package xserver-xorg-video-omap3 in Debian. I don't know where the main upstream source is, but here: https://gitorious.org/gnutoo-s-programs-for-shr/xf86-video-omapfb/source/28c006c94e57ea71df11ec4fff79d7ffcfc4860f:src/omapfb-output-dss.c#L258 is the code which reads /sys/devices/platform/omapdss/display0/name and fails if that file cannot be opened. Thanks, NeilBrown pgpU5K5ew1vFC.pgp Description: OpenPGP digital signature
[PATCH 09/15] 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. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 76 +++ 1 file changed, 76 insertions(+) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index bfc9b808301e..b0242786d047 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -473,6 +473,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 = 50; twl4030_charger_update_current(bci); power_supply_changed(bci-ac); power_supply_changed(bci-usb); @@ -527,6 +529,67 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) return IRQ_HANDLED; } +/* + * sysfs max_current store + */ +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) { + if (bci-ac_cur == cur) + return n; + bci-ac_cur = cur; + } else { + if (bci-usb_cur == cur) + return n; + bci-usb_cur = cur; + } + twl4030_charger_update_current(bci); + return (status == 0) ? n : status; +} + +/* + * 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); @@ -549,6 +612,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 = 50; + else + bci-usb_cur = 10; + bci-event = val; schedule_work(bci-work); @@ -809,6 +878,11 @@ static int __init 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, @@ -836,6 +910,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); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 15/15] twl4030_charger: assume a 'charger' can supply maximum current.
If it cannot, we will stop pulling more current when voltage drops. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c |4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 7ad6b4b531d7..89e2c121dd22 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -691,8 +691,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; -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 14/15] 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 configure 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 concern, 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 long, 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.7mA imposed by the hardware. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 54 ++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index e5a0225ea87e..7ad6b4b531d7 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -117,6 +117,18 @@ struct twl4030_bci { #defineCHARGE_AUTO 1 #defineCHARGE_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 set back one +* step. +*/ + int usb_cur_actual; + struct delayed_work current_worker; +#defineUSB_CUR_STEP2 /* 20mA at a time */ +#defineUSB_MIN_VOLT475 /* 4.75V */ +#defineUSB_CUR_DELAY msecs_to_jiffies(100) +#defineUSB_MAX_CURRENT 170 /* TWL4030 caps at 1.7mA */ + unsigned long event; }; @@ -249,8 +261,14 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) cur = bci-ac_cur; bci-ac_is_active = 1; } else { - cur = bci-usb_cur; + cur = bci-usb_cur_actual; bci-ac_is_active = 0; + if (cur bci-usb_cur) { + cur = bci-usb_cur; + bci-usb_cur_actual = cur; + } + if (cur bci-usb_cur) + schedule_delayed_work(bci-current_worker, USB_CUR_DELAY); } /* First, check thresholds and see if cgain is needed */ @@ -379,6 +397,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_actual, + bci-usb_cur); + + if (v USB_MIN_VOLT) { + /* Back up and stop adjusting. */ + bci-usb_cur_actual -= USB_CUR_STEP; + bci-usb_cur = bci-usb_cur_actual; + } else if (bci-usb_cur_actual = bci-usb_cur || + bci-usb_cur_actual + USB_CUR_STEP USB_MAX_CURRENT) { + /* Reach target and volts are OK - stop */ + return; + } else { + bci-usb_cur_actual += USB_CUR_STEP; + schedule_delayed_work(bci-current_worker, USB_CUR_DELAY); + } + twl4030_charger_update_current(bci); +} + /* * Enable/Disable USB Charge functionality. */ @@ -441,6 +491,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_actual = 0; } return ret; @@ -972,6 +1023,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) { -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 10/15] twl4030_charger: only draw USB current as negotiated with host.
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. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c |5 + 1 file changed, 5 insertions(+) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index b0242786d047..01090a440583 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -597,6 +597,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: @@ -609,6 +610,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 *vbus_draw = priv; dev_dbg(bci-dev, OTG notify %lu\n, val); @@ -619,6 +621,9 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, bci-usb_cur = 10; 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; -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 11/15] 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. Signed-off-by: NeilBrown ne...@suse.de Conflicts: drivers/power/twl4030_charger.c --- drivers/power/twl4030_charger.c | 57 +++ 1 file changed, 57 insertions(+) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 01090a440583..19e8dbb1303e 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -107,6 +107,9 @@ struct twl4030_bci { int ichg_eoc, ichg_lo, ichg_hi; int usb_cur, ac_cur; boolac_is_active; + int usb_mode; /* charging mode requested */ +#defineCHARGE_OFF 0 +#defineCHARGE_AUTO 1 unsigned long event; }; @@ -377,6 +380,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 +634,54 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, return NOTIFY_OK; } +/* + * sysfs charger enabled store + */ +static char *modes[] = { off, auto }; +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; @@ -799,6 +852,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) bci-usb_cur = 50; /* 500mA */ else bci-usb_cur = 10; /* 100mA */ + bci-usb_mode = CHARGE_AUTO; bci-dev = pdev-dev; bci-irq_chg = platform_get_irq(pdev, 0); @@ -885,6 +939,8 @@ static int __init 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); @@ -917,6 +973,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) device_remove_file(bci-usb.dev, dev_attr_max_current); device_remove_file(bci-ac.dev, dev_attr_max_current); + device_remove_file(bci-usb.dev, dev_attr_mode); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR1A); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 12/15] twl4030_charger: add software controlled linear charging mode.
Add a 'continuous' option for usb charging which enabled the linear charging mode of the twl4030. Linear charging does a good job with not so reliable power sources, since several voltage controlling is then often too intelligent. 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. Orignal-by: Andreas Kemnade andr...@kemnade.info Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 57 --- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 19e8dbb1303e..6c53f0b601a4 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -24,6 +24,8 @@ #include linux/usb/otg.h #include linux/i2c/twl4030-madc.h +#define TWL4030_BCIMDEN0x00 +#define TWL4030_BCIMDKEY 0x01 #define TWL4030_BCIMSTATEC 0x02 #define TWL4030_BCIICHG0x08 #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_DONEBIT(4) +#define TWL4030_CVENAC BIT(2) #define TWL4030_BCIAUTOUSB BIT(1) #define TWL4030_BCIAUTOAC BIT(0) #define TWL4030_CGAIN BIT(5) @@ -110,6 +115,7 @@ struct twl4030_bci { int usb_mode; /* charging mode requested */ #defineCHARGE_OFF 0 #defineCHARGE_AUTO 1 +#defineCHARGE_LINEAR 2 unsigned long event; }; @@ -392,16 +398,44 @@ 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); + twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC|TWL4030_CVENAC); + } /* 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); @@ -637,7 +671,7 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, /* * sysfs charger enabled store */ -static char *modes[] = { off, auto }; +static char
[PATCH 13/15] twl4030_charger: add ac/mode to match usb/mode
This allows AC charging to be turned off, much like usb charging. continuous (aka linear) mode maps to the CVENAC (constant voltage) feature of the twl4030. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 40 +-- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 6c53f0b601a4..e5a0225ea87e 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -112,7 +112,7 @@ struct twl4030_bci { int ichg_eoc, ichg_lo, ichg_hi; int usb_cur, ac_cur; boolac_is_active; - int usb_mode; /* charging mode requested */ + int usb_mode, ac_mode; /* charging mode requested */ #defineCHARGE_OFF 0 #defineCHARGE_AUTO 1 #defineCHARGE_LINEAR 2 @@ -449,12 +449,18 @@ 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 (enable) - ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC); + if (bci-ac_mode == CHARGE_OFF) + enable = false; + + if (enable bci-ac_mode == CHARGE_LINEAR) + ret = twl4030_clear_set_boot_bci(0, (TWL4030_CVENAC | +TWL4030_BCIAUTOAC)); + else if (enable) + ret = twl4030_clear_set_boot_bci(TWL4030_CVENAC, TWL4030_BCIAUTOAC); else ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0); @@ -688,9 +694,15 @@ 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) { + 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 +716,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 @@ -900,6 +916,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) else bci-usb_cur = 10; /* 100mA */ bci-usb_mode = CHARGE_AUTO; + bci-ac_mode = CHARGE_AUTO; bci-dev = pdev-dev; bci-irq_chg = platform_get_irq(pdev, 0); @@ -988,10 +1005,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, @@ -1014,13 +1033,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-ac.dev, dev_attr_max_current); device_remove_file(bci-usb.dev, dev_attr_mode); + device_remove_file(bci-ac.dev, dev_attr_mode); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR1A); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message
[PATCH] OMAPDSS: restore name sysfs entry.
commit 303e4697e762dc92a40405f4e4b8aac02cd0d70b OMAPDSS: rename display-sysfs 'name' entry broke the xorg X server on my device as it couldn't find the display any more. It needs the 'name' file and now there isn't one. That commit claims that 'name' is not compatible with i2c or spi. i2c does register it own 'name' file, but spi does not, hence my problem - I have an spi display. So create a special case for i2c: add the name attribute for non-i2c devices. Fixes: 303e4697e762dc92a40405f4e4b8aac02cd0d70b Signed-off-by: NeilBrown ne...@suse.de -- Hi Tomi, I wonder if you would consider this for the next merge window (or maybe even sooner as a bug-fix). I haven't tested it with an i2c display, but it certainly allows xorg to work on my spi-attached display. Thanks, NeilBrown diff --git a/drivers/video/fbdev/omap2/dss/display-sysfs.c b/drivers/video/fbdev/omap2/dss/display-sysfs.c index 5a2095a98ed8..53897b743130 100644 --- a/drivers/video/fbdev/omap2/dss/display-sysfs.c +++ b/drivers/video/fbdev/omap2/dss/display-sysfs.c @@ -25,6 +25,15 @@ #include linux/platform_device.h #include linux/sysfs.h +#if IS_ENABLED(CONFIG_I2C) +#include linux/i2c.h +#else +static inline int i2c_verify_client(struct device *dev) +{ + return NULL; +} +#endif + #include video/omapdss.h #include dss.h @@ -278,6 +287,7 @@ static ssize_t display_wss_store(struct device *dev, } static DEVICE_ATTR(display_name, S_IRUGO, display_name_show, NULL); +static DEVICE_ATTR(name, S_IRUGO, display_name_show, NULL); static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, display_enabled_show, display_enabled_store); static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, @@ -315,6 +325,17 @@ int display_init_sysfs(struct platform_device *pdev) DSSERR(failed to create sysfs files\n); goto err; } + if (!i2c_verify_client(dssdev-dev)) { + /* +* Add 'name' file - not compatible with i2c, but +* need by Xorg for other devices. +*/ + r = sysfs_create_file(kobj, dev_attr_name.attr); + if (r) { + DSSERR(fail to create 'name' sysfs file\n); + goto err; + } + } r = sysfs_create_link(pdev-dev.kobj, kobj, dssdev-alias); if (r) { @@ -341,5 +362,7 @@ void display_uninit_sysfs(struct platform_device *pdev) sysfs_remove_link(pdev-dev.kobj, dssdev-alias); sysfs_remove_files(dssdev-dev-kobj, display_sysfs_attrs); + sysfs_remove_file(dssdev-dev-kobj, + dev_attr_name.attr); } } pgpkhJV_A17Yn.pgp Description: OpenPGP digital signature
[PATCH 1/4] mmc: core: fold mmc_set_bus_width calls into sdio_enable_4bit_bus.
Every call to sdio_enable_4bit_bus is followed (on success) but a call to mmc_set_bus_width(). To simplify the code, include those calls directly in sdio_enable_4bit_bus(). Signed-off-by: NeilBrown n...@brown.name --- drivers/mmc/core/sdio.c | 32 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index ce6cc47206b0..5bc6c7dbbd60 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -293,19 +293,22 @@ static int sdio_enable_4bit_bus(struct mmc_card *card) int err; if (card-type == MMC_TYPE_SDIO) - return sdio_enable_wide(card); - - if ((card-host-caps MMC_CAP_4_BIT_DATA) - (card-scr.bus_widths SD_SCR_BUS_WIDTH_4)) { + err = sdio_enable_wide(card); + else if ((card-host-caps MMC_CAP_4_BIT_DATA) +(card-scr.bus_widths SD_SCR_BUS_WIDTH_4)) { err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); if (err) return err; + err = sdio_enable_wide(card); + if (err = 0) + mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1); } else return 0; - err = sdio_enable_wide(card); - if (err = 0) - mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1); + if (err 0) { + mmc_set_bus_width(card-host, MMC_BUS_WIDTH_4); + err = 0; + } return err; } @@ -547,13 +550,8 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card) /* * Switch to wider bus (if supported). */ - if (card-host-caps MMC_CAP_4_BIT_DATA) { + if (card-host-caps MMC_CAP_4_BIT_DATA) err = sdio_enable_4bit_bus(card); - if (err 0) { - mmc_set_bus_width(card-host, MMC_BUS_WIDTH_4); - err = 0; - } - } /* Set the driver strength for the card */ sdio_select_driver_type(card); @@ -803,9 +801,7 @@ try_again: * Switch to wider bus (if supported). */ err = sdio_enable_4bit_bus(card); - if (err 0) - mmc_set_bus_width(card-host, MMC_BUS_WIDTH_4); - else if (err) + if (err) goto remove; } finish: @@ -983,10 +979,6 @@ static int mmc_sdio_resume(struct mmc_host *host) } else if (mmc_card_keep_power(host) mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host-card); - if (err 0) { - mmc_set_bus_width(host, MMC_BUS_WIDTH_4); - err = 0; - } } if (!err host-sdio_irqs) { -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 4/4] mmc: omap_hsmmc: switch to 1-bit before stopping clocks.
Make use of the new mmc_sdio_want_no_clocks() call to avoid stopping clocks while SD Card interrupts are enabled and we aren't in 1-bit mode. Also stop clocks immediately in omap_hsmmc_disable_fclk() if 1-bit mode has been entered for this purpose. With this, I can use my libertas wifi with a 4-bit bus, with interrupts and runtime power-management enabled, and get around 14Mb/sec throughput (which is the best I've seen). Signed-off-by: NeilBrown n...@brown.name --- drivers/mmc/host/omap_hsmmc.c | 13 ++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index f84cfb01716d..14fce3b92633 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1791,9 +1791,12 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc) { struct omap_hsmmc_host *host = mmc_priv(mmc); - pm_runtime_mark_last_busy(host-dev); - pm_runtime_put_autosuspend(host-dev); - + if (atomic_read(mmc-sdio_narrowed) == 2) + pm_runtime_put_sync(host-dev); + else { + pm_runtime_mark_last_busy(host-dev); + pm_runtime_put_autosuspend(host-dev); + } return 0; } @@ -2311,6 +2314,10 @@ static int omap_hsmmc_runtime_suspend(struct device *dev) spin_lock_irqsave(host-irq_lock, flags); if ((host-mmc-caps MMC_CAP_SDIO_IRQ) (host-flags HSMMC_SDIO_IRQ_ENABLED)) { + if (mmc_sdio_want_no_clocks(host-mmc) == 0) { + ret = -EBUSY; + goto abort; + } /* disable sdio irq handling to prevent race */ OMAP_HSMMC_WRITE(host-base, ISE, 0); OMAP_HSMMC_WRITE(host-base, IE, 0); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/4] mmc: core: allow non-blocking form of mmc_claim_host
Change the handling for the 'abort' flag so that if it is set, but we can claim the host, then do the claim, rather than aborting. When the abort is async this just means that a race between aborting an allowing a claim is resolved slightly differently. Any code must already be able to handle 'abort' being set just as the host is claimed. This allows extra functionality. If __mmc_claim_host() is called with an 'abort' pointer which is initialized to '1', it will effect a non-blocking 'claim'. Signed-off-by: NeilBrown n...@brown.name --- drivers/mmc/core/core.c |3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 23f10f72e5f3..541c8903dc6b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -912,10 +912,11 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) spin_lock_irqsave(host-lock, flags); } set_current_state(TASK_RUNNING); - if (!stop) { + if (!host-claimed || host-claimer == current) { host-claimed = 1; host-claimer = current; host-claim_cnt += 1; + stop = 0; } else wake_up(host-wq); spin_unlock_irqrestore(host-lock, flags); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/4] Switch to 1-bit mode SDIO before disabling clocks.
This is a reworking of this code that I promised at the end of January. SDIO interrupt in 4-bit mode require the clock to be running. So we need to switch to 1-bit mode before turning off the clock. This series provides infrastructure in mmc-core, and adds the functionality to omap_hsmmc. Thanks, NeilBrown --- NeilBrown (4): mmc: core: fold mmc_set_bus_width calls into sdio_enable_4bit_bus. mmc: core: allow non-blocking form of mmc_claim_host mmc: sdio: support switching to 1-bit before turning off clocks mmc: omap_hsmmc: switch to 1-bit before stopping clocks. drivers/mmc/core/core.c | 21 +--- drivers/mmc/core/sdio.c | 74 + drivers/mmc/host/omap_hsmmc.c | 13 ++- include/linux/mmc/core.h |2 + include/linux/mmc/host.h |2 + 5 files changed, 83 insertions(+), 29 deletions(-) -- Signature -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/4] mmc: sdio: support switching to 1-bit before turning off clocks
According to section 7.1.2 of http://www.sandisk.com/media/File/OEM/Manuals/SD_SDIO_specsv1.pdf In the case where the interrupt mechanism is used to wake the host while the card is in a low power state (i.e. no clocks), Both the card and the host shall be placed into the 1-bit SD mode prior to stopping the clock. This is particularly important for the Marvell libertas wifi chip in the GTA04. While in 4-bit mode it will only signal an interrupt when the clock is running (which is why setting CLKEXTFREE is important). In 1-bit mode, the interrupt is asynchronous (explained in OMAP3 TRM description of the CIRQ flag to MMCHS_STAT: In 1-bit mode, interrupt source is asynchronous (can be a source of asynchronous wakeup). In 4-bit mode, interrupt source is sampled during the interrupt cycle. ) It is awkward to simply set 1-bit mode in -runtime_suspend as that will call mmc_set_ios which calls ops-set_ios(), which will likely call pm_runtime_get_sync(), on the device that is currently suspending. This deadlocks. So: - create a work_struct to schedule setting of 1-bit mode - introduce an 'sdio_narrowed' state flag which transitions: 0 (normal) - 1 (convert to 1-bit pending) - 2 (have switch to 1-bit mode) - 0 (normal) - create a function mmc_sdio_want_no_clocks() which can be called when the driver wants to turn off clocks (presumably after an idle timeout). This either succeeds (in 1-bit mode) or fails and schedules the work to switch to 1-bit mode. - when the host is claimed, if sdio_narrowed is 2, restore the 4-bit bus - When the host is released, if sdio_narrowed is 1, then some caller other than our worker claimed the host first, so clear sdio_narrowed. This all allows a graceful and race-free switch to 1-bit mode before switching off the clocks, if SDIO interrupts are enabled. A host should call mmc_sdio_want_no_clocks() when about to turn off clocks if sdio interrupts are enabled, and the -disable() function should not use a timeout (pm_runtime_put_autosuspend) if -sdio_narrowed is 2. Signed-off-by: NeilBrown n...@brown.name --- drivers/mmc/core/core.c | 18 ++ drivers/mmc/core/sdio.c | 42 +- include/linux/mmc/core.h |2 ++ include/linux/mmc/host.h |2 ++ 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 541c8903dc6b..0258fdf1a03d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -921,8 +921,14 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) wake_up(host-wq); spin_unlock_irqrestore(host-lock, flags); remove_wait_queue(host-wq, wait); - if (host-ops-enable !stop host-claim_cnt == 1) - host-ops-enable(host); + if (!stop host-claim_cnt == 1) { + if (host-ops-enable) + host-ops-enable(host); + if (atomic_read(host-sdio_narrowed) == 2) { + sdio_enable_4bit_bus(host-card); + atomic_set(host-sdio_narrowed, 0); + } + } return stop; } @@ -941,8 +947,12 @@ void mmc_release_host(struct mmc_host *host) WARN_ON(!host-claimed); - if (host-ops-disable host-claim_cnt == 1) - host-ops-disable(host); + if (host-claim_cnt == 1) { + if (atomic_read(host-sdio_narrowed) == 1) + atomic_set(host-sdio_narrowed, 0); + if (host-ops-disable) + host-ops-disable(host); + } spin_lock_irqsave(host-lock, flags); if (--host-claim_cnt) { diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 5bc6c7dbbd60..9761e4d5f49b 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -288,7 +288,7 @@ static int sdio_disable_wide(struct mmc_card *card) } -static int sdio_enable_4bit_bus(struct mmc_card *card) +int sdio_enable_4bit_bus(struct mmc_card *card) { int err; @@ -313,6 +313,45 @@ static int sdio_enable_4bit_bus(struct mmc_card *card) return err; } +static void mmc_sdio_width_work(struct work_struct *work) +{ + struct mmc_host *host = container_of(work, struct mmc_host, +sdio_width_work); + atomic_t noblock; + + atomic_set(noblock, 1); + if (__mmc_claim_host(host, noblock)) + return; + if (atomic_read(host-sdio_narrowed) != 1) { + /* Nothing to do */ + mmc_release_host(host); + return; + } + if (sdio_disable_wide(host-card) == 0) + atomic_set(host-sdio_narrowed, 2); + else + atomic_set(host-sdio_narrowed, 0); + mmc_release_host(host); +} + +int mmc_sdio_want_no_clocks(struct mmc_host *host) +{ + if (!(host-caps MMC_CAP_SDIO_IRQ
[PATCH 2/4] usb: phy: twl4030: allow charger to see usb current draw limits.
The charger needs to know when a USB gadget has been enumerated and what the agreed maximum current was so that it can adjust charging accordingly. So define a set_power() function to record the permitted draw, and pass a pointer to that when sending USB_EVENT_ENUMERATED notification. Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 27 +-- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 97c59074233f..023fe150c7a1 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -163,6 +163,11 @@ struct twl4030_usb { enum omap_musb_vbus_id_status linkstat; boolvbus_supplied; + /* Permitted vbus draw - only meaningful after +* USB_EVENT_ENUMERATED +*/ + unsignedvbus_draw; + struct delayed_work id_workaround_work; }; @@ -547,12 +552,7 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) mutex_unlock(twl-lock); if (status_changed) { - /* FIXME add a set_power() method so that B-devices can -* configure the charger appropriately. It's not always -* correct to consume VBUS power, and how much current to -* consume is a function of the USB configuration chosen -* by the host. -* + /* * REVISIT usb_gadget_vbus_connect(...) as needed, ditto * its disconnect() sibling, when changing to/from the * USB_LINK_VBUS state. musb_hdrc won't care until it @@ -625,6 +625,20 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } +static int twl4030_set_power(struct usb_phy *phy, unsigned mA) +{ + struct twl4030_usb *twl = phy_to_twl(phy); + + if (twl-vbus_draw != mA) { + phy-last_event = USB_EVENT_ENUMERATED; + twl-vbus_draw = mA; + atomic_notifier_call_chain(phy-notifier, + USB_EVENT_ENUMERATED, + twl-vbus_draw); + } + return 0; +} + static const struct phy_ops ops = { .init = twl4030_phy_init, .power_on = twl4030_phy_power_on, @@ -675,6 +689,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) twl-phy.label = twl4030; twl-phy.otg= otg; twl-phy.type = USB_PHY_TYPE_USB2; + twl-phy.set_power = twl4030_set_power; otg-usb_phy= twl-phy; otg-set_host = twl4030_set_host; -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/4] usb: phy: twl4030: make runtime pm more reliable.
A construct like: if (pm_runtime_suspended(twl-dev)) pm_runtime_get_sync(twl-dev); is against the spirit of the runtime_pm interface as it makes the internal refcounting useless. In this case it is also racy, particularly as 'put_autosuspend' is use to drop a reference. When that happens a timer is started and the device is runtime-suspended after the timeout. If the above code runs in this window, the device will not be found to be suspended so no pm_runtime reference is taken. When the timer expires the device will be suspended, which is against the intention of the code. So be more direct is taking and dropping references. If twl-linkstat is VBUS_VALID or ID_GROUND, then hold a pm_runtime reference, otherwise don't. Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 20 +--- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 8e87f54671f3..97c59074233f 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -536,8 +536,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) mutex_lock(twl-lock); if (status = 0 status != twl-linkstat) { + status_changed = + (twl-linkstat == OMAP_MUSB_VBUS_VALID || +twl-linkstat == OMAP_MUSB_ID_GROUND) + != + (status == OMAP_MUSB_VBUS_VALID || +status == OMAP_MUSB_ID_GROUND); twl-linkstat = status; - status_changed = true; } mutex_unlock(twl-lock); @@ -555,13 +560,10 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) */ if ((status == OMAP_MUSB_VBUS_VALID) || (status == OMAP_MUSB_ID_GROUND)) { - if (pm_runtime_suspended(twl-dev)) - pm_runtime_get_sync(twl-dev); + pm_runtime_get_sync(twl-dev); } else { - if (pm_runtime_active(twl-dev)) { - pm_runtime_mark_last_busy(twl-dev); - pm_runtime_put_autosuspend(twl-dev); - } + pm_runtime_mark_last_busy(twl-dev); + pm_runtime_put_autosuspend(twl-dev); } omap_musb_mailbox(status); } @@ -768,6 +770,10 @@ static int twl4030_usb_remove(struct platform_device *pdev) /* disable complete OTG block */ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + if (twl-linkstat == OMAP_MUSB_VBUS_VALID || + twl-linkstat == OMAP_MUSB_ID_GROUND) + pm_runtime_put_noidle(twl-dev); pm_runtime_mark_last_busy(twl-dev); pm_runtime_put(twl-dev); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/4] usb: phy: twl4030: add support for reading restore on ID pin.
The twl4030 phy can measure, with low precision, the resistance-to-ground of the ID pin. Add a function to read the value, and export the result via sysfs. If the read fails, which it does sometimes, try again in 50msec. Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 63 + 1 file changed, 63 insertions(+) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 023fe150c7a1..759950898df9 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -374,6 +374,56 @@ static void twl4030_i2c_access(struct twl4030_usb *twl, int on) } } +enum twl4030_id_status { + TWL4030_GROUND, + TWL4030_102K, + TWL4030_200K, + TWL4030_440K, + TWL4030_FLOATING, + TWL4030_ID_UNKNOWN, +}; +static char *twl4030_id_names[] = { + ground, + 102k, + 200k, + 440k, + floating, + unknown +}; + +enum twl4030_id_status twl4030_get_id(struct twl4030_usb *twl) +{ + int ret; + + pm_runtime_get_sync(twl-dev); + if (twl-usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 1); + ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); + if (ret 0 || !(ret ULPI_OTG_ID_PULLUP)) { + /* Need pull-up to read ID */ + twl4030_usb_set_bits(twl, ULPI_OTG_CTRL, +ULPI_OTG_ID_PULLUP); + mdelay(50); + } + ret = twl4030_usb_read(twl, ID_STATUS); + if (ret 0 || (ret 0x1f) == 0) { + mdelay(50); + ret = twl4030_usb_read(twl, ID_STATUS); + } + + if (twl-usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 0); + pm_runtime_put_autosuspend(twl-dev); + + if (ret 0) + return TWL4030_ID_UNKNOWN; + ret = ffs(ret) - 1; + if (ret TWL4030_GROUND || ret TWL4030_FLOATING) + return TWL4030_ID_UNKNOWN; + + return ret; +} + static void __twl4030_phy_power(struct twl4030_usb *twl, int on) { u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); @@ -531,6 +581,16 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev, } static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); +static ssize_t twl4030_usb_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, %s\n, +twl4030_id_names[twl4030_get_id(twl)]); +} +static DEVICE_ATTR(id, 0444, twl4030_usb_id_show, NULL); + static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; @@ -723,6 +783,8 @@ static int twl4030_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, twl); if (device_create_file(pdev-dev, dev_attr_vbus)) dev_warn(pdev-dev, could not create sysfs file\n); + if (device_create_file(pdev-dev, dev_attr_id)) + dev_warn(pdev-dev, could not create sysfs file\n); ATOMIC_INIT_NOTIFIER_HEAD(twl-phy.notifier); @@ -768,6 +830,7 @@ static int twl4030_usb_remove(struct platform_device *pdev) pm_runtime_get_sync(twl-dev); cancel_delayed_work(twl-id_workaround_work); device_remove_file(twl-dev, dev_attr_vbus); + device_remove_file(twl-dev, dev_attr_id); /* set transceiver mode to power on defaults */ twl4030_usb_set_mode(twl, -1); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 4/4] usb: phy: twl4030: test ID resistance to see if charger is present.
If an 'A' plug is inserted, ID should be pulled to ground. If a 'B' plug, then ID should be floating. If an Accessory Charger Adapter is inserted, then ID will be neither grounded nor floating. In this case tell the USB subsystem that it is an A plug, and the battery charging subsystem that it is a charger. Fortunately, this will treat the Openmoko charger (and other similar chargers) as a charger. Signed-off-by: NeilBrown ne...@suse.de --- drivers/phy/phy-twl4030-usb.c | 28 1 file changed, 28 insertions(+) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 759950898df9..8a43080cdbd7 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -596,9 +596,31 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) struct twl4030_usb *twl = _twl; enum omap_musb_vbus_id_status status; bool status_changed = false; + bool found_charger = false; status = twl4030_usb_linkstat(twl); + if (status == OMAP_MUSB_ID_GROUND || + status == OMAP_MUSB_VBUS_VALID) { + /* We should check the resistance on the ID pin. +* If not a Ground or Floating, then this is +* likely a charger +*/ + enum twl4030_id_status sts = twl4030_get_id(twl); + if (sts TWL4030_GROUND + sts TWL4030_FLOATING) { + /* +* This might be a charger, or an +* Accessory Charger Adapter. +* In either case we can charge, and it +* makes sense to tell the USB system +* that we might be acting as a HOST. +*/ + status = OMAP_MUSB_ID_GROUND; + found_charger = true; + } + } + mutex_lock(twl-lock); if (status = 0 status != twl-linkstat) { status_changed = @@ -627,6 +649,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) } omap_musb_mailbox(status); } + if (found_charger twl-phy.last_event != USB_EVENT_CHARGER) { + twl-phy.last_event = USB_EVENT_CHARGER; + atomic_notifier_call_chain(twl-phy.notifier, + USB_EVENT_CHARGER, + NULL); + } /* don't schedule during sleep - irq works right then */ if (status == OMAP_MUSB_ID_GROUND pm_runtime_active(twl-dev)) { -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/4] Enhancements to twl4030 phy to support better charging.
Hi, the following 4 patches make some improvements to the twl4030 phy. Together with some other patches I have for twl4030_charger, they allow for better automatic control of charging. In particular, the status of the ID pin is assessed and the result in communicated to the charger drivers. There is also support for conveying the max current negotiated by a host to the charger. Comments most welcome. thanks, NeilBrown --- NeilBrown (4): usb: phy: twl4030: make runtime pm more reliable. usb: phy: twl4030: allow charger to see usb current draw limits. usb: phy: twl4030: add support for reading restore on ID pin. usb: phy: twl4030: test ID resistance to see if charger is present. drivers/phy/phy-twl4030-usb.c | 138 + 1 file changed, 125 insertions(+), 13 deletions(-) -- Signature -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 08/15] twl4030_charger: distinguish between USB current and 'AC' current
The twl4030 charger has two current sources, 'USB' and 'AC' (which is really DC of course...). 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. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 30 +- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 7c35cd9ba171..bfc9b808301e 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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_BCIICHG0x08 @@ -103,7 +104,9 @@ struct twl4030_bci { /* ichg values in uA. If any are 'large', we set CGAIN to * '1' which doubles the range for half the precision. */ - int ichg_eoc, ichg_lo, ichg_hi, cur; + int ichg_eoc, ichg_lo, ichg_hi; + int usb_cur, ac_cur; + boolac_is_active; unsigned long event; }; @@ -224,11 +227,23 @@ 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; int cgain = 0; u8 boot_bci; + /* If VAC 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 = 1; + } else { + cur = bci-usb_cur; + bci-ac_is_active = 0; + } + /* First, check thresholds and see if cgain is needed */ if (bci-ichg_eoc = 20) cgain = 1; @@ -236,7 +251,7 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) cgain = 1; if (bci-ichg_hi = 82) cgain = 1; - if (bci-cur 852000) + if (cur 852000) cgain = 1; status = twl4030_bci_read(TWL4030_BCICTL1, bcictl1); @@ -311,7 +326,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 bit */ if (reg 0x3ff) reg = 0x3ff; @@ -364,6 +379,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); @@ -456,6 +473,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); @@ -488,6 +506,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)) @@ -701,10 +720,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 = 50; /* High threshold */ + bci-ac_cur = 50; /* 500mA */ if (allow_usb) - bci-cur = 50; /* 500mA */ + bci-usb_cur = 50; /* 500mA */ else - bci-cur = 10; /* 100mA */ + bci-usb_cur = 10; /* 100mA */ bci-dev = pdev-dev; bci-irq_chg = platform_get_irq(pdev, 0); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 07/15] 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. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 161 +-- 1 file changed, 153 insertions(+), 8 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 0b6fb06a0c01..7c35cd9ba171 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -31,6 +31,11 @@ #define TWL4030_BCIMFSTS4 0x10 #define TWL4030_BCICTL10x23 #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,11 @@ 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. +*/ + int ichg_eoc, ichg_lo, ichg_hi, cur; + unsigned long event; }; @@ -211,6 +221,140 @@ 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; + int cgain = 0; + u8 boot_bci; + + /* First, check thresholds and see if cgain is needed */ + if (bci-ichg_eoc = 20) + cgain = 1; + if (bci-ichg_lo = 40) + cgain = 1; + if (bci-ichg_hi = 82) + cgain = 1; + if (bci-cur 852000) + cgain = 1; + + 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, reg value must be 100000, we only +* set the in high nibble. +*/ + 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 be 10. +* is stored in low nibble */ + 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 101100 (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 bit */ + if (reg 0x3ff) + reg = 0x3ff
[PATCH 04/15] 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. Signed-off-by: NeilBrown ne...@suse.de --- 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 51321f0c5548..11f352a5ef55 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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_BCIICHG0x08 @@ -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
[PATCH 06/15] 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. Signed-off-by: NeilBrown ne...@suse.de --- 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 db8931a17541..0b6fb06a0c01 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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 * 1) / 10; - if (bcictl1 TWL4030_CGAIN) - ret *= 2; - - return ret; + return regval2ua(curr, bcictl1 TWL4030_CGAIN); } /* -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 05/15] 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 ne...@suse.de --- 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 11f352a5ef55..db8931a17541 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 00/15] Enhance twl4030_charger functionality.
These patches make a number of improvements to twl4030_charger. So 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. Some of this functionality requires patch to phy-twl4030-usb.c which have been sent separately. Thanks, NeilBrown --- NeilBrown (15): power_supply core: support use of devres to register/unregister a power supply. 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. drivers/mfd/twl-core.c|9 - drivers/power/power_supply_core.c | 45 +++ drivers/power/twl4030_charger.c | 572 +++-- include/linux/power_supply.h |4 4 files changed, 539 insertions(+), 91 deletions(-) -- Signature -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 03/15] twl4030_charger: use devres for power_supply_register and kzalloc.
Final allocations/registrations are now managed by devres. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/twl4030_charger.c | 23 ++- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 300dd7a34e80..51321f0c5548 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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; + goto fail; } 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; + goto fail; } 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; + goto fail; } ret = devm_request_threaded_irq(pdev-dev, bci-irq_chg, NULL, @@ -667,13 +667,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) 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; } @@ -691,10 +684,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; } -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 02/15] twl4030_charger: use devm_request_threaded_irq
This simplifies the error paths. Signed-off-by: NeilBrown ne...@suse.de --- 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 4cf5ffbc904a..300dd7a34e80 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -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); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 01/15] power_supply core: support use of devres to register/unregister a power supply.
Using devm_power_supply_register allows the unregister to happen automatically on error or final put. Signed-off-by: NeilBrown ne...@suse.de --- drivers/power/power_supply_core.c | 45 + include/linux/power_supply.h |4 +++ 2 files changed, 49 insertions(+) diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 694e8cddd5c1..44c810456212 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -617,6 +617,51 @@ int power_supply_register_no_ws(struct device *parent, struct power_supply *psy) } EXPORT_SYMBOL_GPL(power_supply_register_no_ws); +static void devm_power_supply_release(struct device *dev, void *res) +{ + struct power_supply **psy = res; + + power_supply_unregister(*psy); +} + +int devm_power_supply_register(struct device *parent, struct power_supply *psy) +{ + struct power_supply **ptr = devres_alloc(devm_power_supply_release, +sizeof(*ptr), GFP_KERNEL); + int ret; + + if (!ptr) + return -ENOMEM; + ret = __power_supply_register(parent, psy, true); + if (ret 0) + devres_free(ptr); + else { + *ptr = psy; + devres_add(parent, ptr); + } + return ret; +} +EXPORT_SYMBOL_GPL(devm_power_supply_register); + +int devm_power_supply_register_no_ws(struct device *parent, struct power_supply *psy) +{ + struct power_supply **ptr = devres_alloc(devm_power_supply_release, +sizeof(*ptr), GFP_KERNEL); + int ret; + + if (!ptr) + return -ENOMEM; + ret = __power_supply_register(parent, psy, false); + if (ret 0) + devres_free(ptr); + else { + *ptr = psy; + devres_add(parent, ptr); + } + return ret; +} +EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws); + void power_supply_unregister(struct power_supply *psy) { cancel_work_sync(psy-changed_work); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 096dbced02ac..f606d6b4bd56 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -278,6 +278,10 @@ extern int power_supply_register(struct device *parent, struct power_supply *psy); extern int power_supply_register_no_ws(struct device *parent, struct power_supply *psy); +extern int devm_power_supply_register(struct device *parent, +struct power_supply *psy); +extern int devm_power_supply_register_no_ws(struct device *parent, +struct power_supply *psy); extern void power_supply_unregister(struct power_supply *psy); extern int power_supply_powers(struct power_supply *psy, struct device *dev); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/4] mmc: core: allow non-blocking form of mmc_claim_host
Change the handling for the 'abort' flag so that if it is set, but we can claim the host, then do the claim, rather than aborting. When the abort is async this just means that a race between aborting an allowing a claim is resolved slightly differently. Any code must already be able to handle 'abort' being set just as the host is claimed. This allows extra functionality. If __mmc_claim_host() is called with an 'abort' pointer which is initialized to '1', it will effect a non-blocking 'claim'. Signed-off-by: NeilBrown n...@brown.name --- drivers/mmc/core/core.c |3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e9eb721e3664..051198073d21 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -912,10 +912,11 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) spin_lock_irqsave(host-lock, flags); } set_current_state(TASK_RUNNING); - if (!stop) { + if (!host-claimed || host-claimer == current) { host-claimed = 1; host-claimer = current; host-claim_cnt += 1; + stop = 0; } else wake_up(host-wq); spin_unlock_irqrestore(host-lock, flags); -- To unsubscribe from this list: send the line unsubscribe linux-omap in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html