diff options
-rw-r--r-- | drivers/power/twl4030_charger.c | 67 |
1 files 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 { | |||
119 | #define CHARGE_AUTO 1 | 119 | #define CHARGE_AUTO 1 |
120 | #define CHARGE_LINEAR 2 | 120 | #define CHARGE_LINEAR 2 |
121 | 121 | ||
122 | /* When setting the USB current we slowly increase the | ||
123 | * requested current until target is reached or the voltage | ||
124 | * drops below 4.75V. In the latter case we step back one | ||
125 | * step. | ||
126 | */ | ||
127 | unsigned int usb_cur_target; | ||
128 | struct delayed_work current_worker; | ||
129 | #define USB_CUR_STEP 20000 /* 20mA at a time */ | ||
130 | #define USB_MIN_VOLT 4750000 /* 4.75V */ | ||
131 | #define USB_CUR_DELAY msecs_to_jiffies(100) | ||
132 | #define USB_MAX_CURRENT 1700000 /* TWL4030 caps at 1.7A */ | ||
133 | |||
122 | unsigned long event; | 134 | unsigned long event; |
123 | }; | 135 | }; |
124 | 136 | ||
@@ -257,6 +269,12 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) | |||
257 | } else { | 269 | } else { |
258 | cur = bci->usb_cur; | 270 | cur = bci->usb_cur; |
259 | bci->ac_is_active = false; | 271 | bci->ac_is_active = false; |
272 | if (cur > bci->usb_cur_target) { | ||
273 | cur = bci->usb_cur_target; | ||
274 | bci->usb_cur = cur; | ||
275 | } | ||
276 | if (cur < bci->usb_cur_target) | ||
277 | schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY); | ||
260 | } | 278 | } |
261 | 279 | ||
262 | /* First, check thresholds and see if cgain is needed */ | 280 | /* First, check thresholds and see if cgain is needed */ |
@@ -391,6 +409,41 @@ static int twl4030_charger_update_current(struct twl4030_bci *bci) | |||
391 | return 0; | 409 | return 0; |
392 | } | 410 | } |
393 | 411 | ||
412 | static int twl4030_charger_get_current(void); | ||
413 | |||
414 | static void twl4030_current_worker(struct work_struct *data) | ||
415 | { | ||
416 | int v, curr; | ||
417 | int res; | ||
418 | struct twl4030_bci *bci = container_of(data, struct twl4030_bci, | ||
419 | current_worker.work); | ||
420 | |||
421 | res = twl4030bci_read_adc_val(TWL4030_BCIVBUS); | ||
422 | if (res < 0) | ||
423 | v = 0; | ||
424 | else | ||
425 | /* BCIVBUS uses ADCIN8, 7/1023 V/step */ | ||
426 | v = res * 6843; | ||
427 | curr = twl4030_charger_get_current(); | ||
428 | |||
429 | dev_dbg(bci->dev, "v=%d cur=%d limit=%d target=%d\n", v, curr, | ||
430 | bci->usb_cur, bci->usb_cur_target); | ||
431 | |||
432 | if (v < USB_MIN_VOLT) { | ||
433 | /* Back up and stop adjusting. */ | ||
434 | bci->usb_cur -= USB_CUR_STEP; | ||
435 | bci->usb_cur_target = bci->usb_cur; | ||
436 | } else if (bci->usb_cur >= bci->usb_cur_target || | ||
437 | bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) { | ||
438 | /* Reached target and voltage is OK - stop */ | ||
439 | return; | ||
440 | } else { | ||
441 | bci->usb_cur += USB_CUR_STEP; | ||
442 | schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY); | ||
443 | } | ||
444 | twl4030_charger_update_current(bci); | ||
445 | } | ||
446 | |||
394 | /* | 447 | /* |
395 | * Enable/Disable USB Charge functionality. | 448 | * Enable/Disable USB Charge functionality. |
396 | */ | 449 | */ |
@@ -451,6 +504,7 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) | |||
451 | pm_runtime_put_autosuspend(bci->transceiver->dev); | 504 | pm_runtime_put_autosuspend(bci->transceiver->dev); |
452 | bci->usb_enabled = 0; | 505 | bci->usb_enabled = 0; |
453 | } | 506 | } |
507 | bci->usb_cur = 0; | ||
454 | } | 508 | } |
455 | 509 | ||
456 | return ret; | 510 | return ret; |
@@ -599,7 +653,7 @@ twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr, | |||
599 | if (dev == &bci->ac->dev) | 653 | if (dev == &bci->ac->dev) |
600 | bci->ac_cur = cur; | 654 | bci->ac_cur = cur; |
601 | else | 655 | else |
602 | bci->usb_cur = cur; | 656 | bci->usb_cur_target = cur; |
603 | 657 | ||
604 | twl4030_charger_update_current(bci); | 658 | twl4030_charger_update_current(bci); |
605 | return n; | 659 | return n; |
@@ -621,7 +675,7 @@ static ssize_t twl4030_bci_max_current_show(struct device *dev, | |||
621 | cur = bci->ac_cur; | 675 | cur = bci->ac_cur; |
622 | } else { | 676 | } else { |
623 | if (bci->ac_is_active) | 677 | if (bci->ac_is_active) |
624 | cur = bci->usb_cur; | 678 | cur = bci->usb_cur_target; |
625 | } | 679 | } |
626 | if (cur < 0) { | 680 | if (cur < 0) { |
627 | cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1); | 681 | cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1); |
@@ -662,9 +716,9 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, | |||
662 | 716 | ||
663 | /* reset current on each 'plug' event */ | 717 | /* reset current on each 'plug' event */ |
664 | if (allow_usb) | 718 | if (allow_usb) |
665 | bci->usb_cur = 500000; | 719 | bci->usb_cur_target = 500000; |
666 | else | 720 | else |
667 | bci->usb_cur = 100000; | 721 | bci->usb_cur_target = 100000; |
668 | 722 | ||
669 | bci->event = val; | 723 | bci->event = val; |
670 | schedule_work(&bci->work); | 724 | schedule_work(&bci->work); |
@@ -927,9 +981,9 @@ static int twl4030_bci_probe(struct platform_device *pdev) | |||
927 | bci->ichg_hi = 500000; /* High threshold */ | 981 | bci->ichg_hi = 500000; /* High threshold */ |
928 | bci->ac_cur = 500000; /* 500mA */ | 982 | bci->ac_cur = 500000; /* 500mA */ |
929 | if (allow_usb) | 983 | if (allow_usb) |
930 | bci->usb_cur = 500000; /* 500mA */ | 984 | bci->usb_cur_target = 500000; /* 500mA */ |
931 | else | 985 | else |
932 | bci->usb_cur = 100000; /* 100mA */ | 986 | bci->usb_cur_target = 100000; /* 100mA */ |
933 | bci->usb_mode = CHARGE_AUTO; | 987 | bci->usb_mode = CHARGE_AUTO; |
934 | bci->ac_mode = CHARGE_AUTO; | 988 | bci->ac_mode = CHARGE_AUTO; |
935 | 989 | ||
@@ -980,6 +1034,7 @@ static int twl4030_bci_probe(struct platform_device *pdev) | |||
980 | } | 1034 | } |
981 | 1035 | ||
982 | INIT_WORK(&bci->work, twl4030_bci_usb_work); | 1036 | INIT_WORK(&bci->work, twl4030_bci_usb_work); |
1037 | INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker); | ||
983 | 1038 | ||
984 | bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; | 1039 | bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; |
985 | if (bci->dev->of_node) { | 1040 | if (bci->dev->of_node) { |