diff options
author | NeilBrown <neil@brown.name> | 2015-07-29 20:11:24 -0400 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2015-08-04 23:19:44 -0400 |
commit | aca3c3546396b305fff79f09883cff48a908aac0 (patch) | |
tree | 802519e100fd760e7e6462fc39611664f9cfc2b6 | |
parent | e4ae537e0482e99eeaa5373d39932fe65a477c21 (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-twl4030 | 15 | ||||
-rw-r--r-- | drivers/power/twl4030_charger.c | 72 |
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 @@ | |||
1 | What: /sys/class/power_supply/twl4030_ac/max_current | ||
2 | /sys/class/power_supply/twl4030_usb/max_current | ||
3 | Description: | ||
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 | */ | ||
544 | static ssize_t | ||
545 | twl4030_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 | */ | ||
568 | static 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 | |||
595 | static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show, | ||
596 | twl4030_bci_max_current_store); | ||
597 | |||
539 | static void twl4030_bci_usb_work(struct work_struct *data) | 598 | static 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); |