aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neil@brown.name>2015-07-29 20:11:24 -0400
committerSebastian Reichel <sre@kernel.org>2015-08-04 23:19:44 -0400
commitaca3c3546396b305fff79f09883cff48a908aac0 (patch)
tree802519e100fd760e7e6462fc39611664f9cfc2b6
parente4ae537e0482e99eeaa5373d39932fe65a477c21 (diff)
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 <pavel@ucw.cz> Signed-off-by: NeilBrown <neil@brown.name> Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r--Documentation/ABI/testing/sysfs-class-power-twl403015
-rw-r--r--drivers/power/twl4030_charger.c72
2 files changed, 87 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-power-twl4030 b/Documentation/ABI/testing/sysfs-class-power-twl4030
new file mode 100644
index 000000000000..0331bba4605d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-power-twl4030
@@ -0,0 +1,15 @@
1What: /sys/class/power_supply/twl4030_ac/max_current
2 /sys/class/power_supply/twl4030_usb/max_current
3Description:
4 Read/Write limit on current which may
5 be drawn from the ac (Accessory Charger) or
6 USB port.
7
8 Value is in micro-Amps.
9
10 Value is set automatically to an appropriate
11 value when a cable is plugged or unplugged.
12
13 Value can the set by writing to the attribute.
14 The change will only persist until the next
15 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)
482 struct twl4030_bci *bci = arg; 482 struct twl4030_bci *bci = arg;
483 483
484 dev_dbg(bci->dev, "CHG_PRES irq\n"); 484 dev_dbg(bci->dev, "CHG_PRES irq\n");
485 /* reset current on each 'plug' event */
486 bci->ac_cur = 500000;
485 twl4030_charger_update_current(bci); 487 twl4030_charger_update_current(bci);
486 power_supply_changed(bci->ac); 488 power_supply_changed(bci->ac);
487 power_supply_changed(bci->usb); 489 power_supply_changed(bci->usb);
@@ -536,6 +538,63 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
536 return IRQ_HANDLED; 538 return IRQ_HANDLED;
537} 539}
538 540
541/*
542 * Provide "max_current" attribute in sysfs.
543 */
544static ssize_t
545twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr,
546 const char *buf, size_t n)
547{
548 struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
549 int cur = 0;
550 int status = 0;
551 status = kstrtoint(buf, 10, &cur);
552 if (status)
553 return status;
554 if (cur < 0)
555 return -EINVAL;
556 if (dev == &bci->ac->dev)
557 bci->ac_cur = cur;
558 else
559 bci->usb_cur = cur;
560
561 twl4030_charger_update_current(bci);
562 return n;
563}
564
565/*
566 * sysfs max_current show
567 */
568static ssize_t twl4030_bci_max_current_show(struct device *dev,
569 struct device_attribute *attr, char *buf)
570{
571 int status = 0;
572 int cur = -1;
573 u8 bcictl1;
574 struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
575
576 if (dev == &bci->ac->dev) {
577 if (!bci->ac_is_active)
578 cur = bci->ac_cur;
579 } else {
580 if (bci->ac_is_active)
581 cur = bci->usb_cur;
582 }
583 if (cur < 0) {
584 cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
585 if (cur < 0)
586 return cur;
587 status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
588 if (status < 0)
589 return status;
590 cur = regval2ua(cur, bcictl1 & TWL4030_CGAIN);
591 }
592 return scnprintf(buf, PAGE_SIZE, "%u\n", cur);
593}
594
595static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show,
596 twl4030_bci_max_current_store);
597
539static void twl4030_bci_usb_work(struct work_struct *data) 598static void twl4030_bci_usb_work(struct work_struct *data)
540{ 599{
541 struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work); 600 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,
558 617
559 dev_dbg(bci->dev, "OTG notify %lu\n", val); 618 dev_dbg(bci->dev, "OTG notify %lu\n", val);
560 619
620 /* reset current on each 'plug' event */
621 if (allow_usb)
622 bci->usb_cur = 500000;
623 else
624 bci->usb_cur = 100000;
625
561 bci->event = val; 626 bci->event = val;
562 schedule_work(&bci->work); 627 schedule_work(&bci->work);
563 628
@@ -831,6 +896,11 @@ static int twl4030_bci_probe(struct platform_device *pdev)
831 dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); 896 dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
832 897
833 twl4030_charger_update_current(bci); 898 twl4030_charger_update_current(bci);
899 if (device_create_file(&bci->usb->dev, &dev_attr_max_current))
900 dev_warn(&pdev->dev, "could not create sysfs file\n");
901 if (device_create_file(&bci->ac->dev, &dev_attr_max_current))
902 dev_warn(&pdev->dev, "could not create sysfs file\n");
903
834 twl4030_charger_enable_ac(true); 904 twl4030_charger_enable_ac(true);
835 if (!IS_ERR_OR_NULL(bci->transceiver)) 905 if (!IS_ERR_OR_NULL(bci->transceiver))
836 twl4030_bci_usb_ncb(&bci->usb_nb, 906 twl4030_bci_usb_ncb(&bci->usb_nb,
@@ -855,6 +925,8 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
855 twl4030_charger_enable_usb(bci, false); 925 twl4030_charger_enable_usb(bci, false);
856 twl4030_charger_enable_backup(0, 0); 926 twl4030_charger_enable_backup(0, 0);
857 927
928 device_remove_file(&bci->usb->dev, &dev_attr_max_current);
929 device_remove_file(&bci->ac->dev, &dev_attr_max_current);
858 /* mask interrupts */ 930 /* mask interrupts */
859 twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, 931 twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
860 TWL4030_INTERRUPTS_BCIIMR1A); 932 TWL4030_INTERRUPTS_BCIIMR1A);