aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChen-Yu Tsai <wens@csie.org>2019-04-16 02:40:20 -0400
committerSebastian Reichel <sebastian.reichel@collabora.com>2019-04-17 17:23:15 -0400
commit97ec136e7124fa12cf56ce706993e747c5f99a20 (patch)
treec096b1f1e1fd428022ebaa90c6e856ae33cb59f7
parentc11f0b8f226a411915f8d7467bd554a8c9ceec42 (diff)
power: supply: axp20x_usb_power: use polling to detect vbus status change
On AXP221 and later AXP PMICs that have the N_VBUSEN pin, when this pin is high, either due to the PMIC driving it high or as an input, the VBUS detection related interrupt mechanisms are disabled. Previously this was worked around in the phy-sun4i-usb driver, which needed to sense VBUS changes and report them to the musb driver in a timely matter. However this workaround was only for the A31 and A33 type USB PHYs. To support newer platforms we would have to enable it for almost all the post-A31 SoCs. However, since this is actually the result of the PMIC's behavior, the workaround would be better if done in the PMIC driver, in this case the VBUS power supply driver. Add the same workqueue-based polling to the VBUS power supply driver. The polling interval is chosen to be the debounce interval from the USB PHY driver, as this short interval is needed in some cases, but the power supply driver would not know when. Signed-off-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
-rw-r--r--drivers/power/supply/axp20x_usb_power.c53
1 files changed, 53 insertions, 0 deletions
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index cd9b90d79839..290028aed230 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -24,6 +24,7 @@
24#include <linux/regmap.h> 24#include <linux/regmap.h>
25#include <linux/slab.h> 25#include <linux/slab.h>
26#include <linux/iio/consumer.h> 26#include <linux/iio/consumer.h>
27#include <linux/workqueue.h>
27 28
28#define DRVNAME "axp20x-usb-power-supply" 29#define DRVNAME "axp20x-usb-power-supply"
29 30
@@ -46,6 +47,12 @@
46 47
47#define AXP20X_VBUS_MON_VBUS_VALID BIT(3) 48#define AXP20X_VBUS_MON_VBUS_VALID BIT(3)
48 49
50/*
51 * Note do not raise the debounce time, we must report Vusb high within
52 * 100ms otherwise we get Vbus errors in musb.
53 */
54#define DEBOUNCE_TIME msecs_to_jiffies(50)
55
49struct axp20x_usb_power { 56struct axp20x_usb_power {
50 struct device_node *np; 57 struct device_node *np;
51 struct regmap *regmap; 58 struct regmap *regmap;
@@ -53,6 +60,8 @@ struct axp20x_usb_power {
53 enum axp20x_variants axp20x_id; 60 enum axp20x_variants axp20x_id;
54 struct iio_channel *vbus_v; 61 struct iio_channel *vbus_v;
55 struct iio_channel *vbus_i; 62 struct iio_channel *vbus_i;
63 struct delayed_work vbus_detect;
64 unsigned int old_status;
56}; 65};
57 66
58static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) 67static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
@@ -64,6 +73,35 @@ static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
64 return IRQ_HANDLED; 73 return IRQ_HANDLED;
65} 74}
66 75
76static void axp20x_usb_power_poll_vbus(struct work_struct *work)
77{
78 struct axp20x_usb_power *power =
79 container_of(work, struct axp20x_usb_power, vbus_detect.work);
80 unsigned int val;
81 int ret;
82
83 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &val);
84 if (ret)
85 goto out;
86
87 val &= (AXP20X_PWR_STATUS_VBUS_PRESENT | AXP20X_PWR_STATUS_VBUS_USED);
88 if (val != power->old_status)
89 power_supply_changed(power->supply);
90
91 power->old_status = val;
92
93out:
94 mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME);
95}
96
97static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power)
98{
99 if (power->axp20x_id >= AXP221_ID)
100 return true;
101
102 return false;
103}
104
67static int axp20x_usb_power_get_property(struct power_supply *psy, 105static int axp20x_usb_power_get_property(struct power_supply *psy,
68 enum power_supply_property psp, union power_supply_propval *val) 106 enum power_supply_property psp, union power_supply_propval *val)
69{ 107{
@@ -357,6 +395,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
357 if (!power) 395 if (!power)
358 return -ENOMEM; 396 return -ENOMEM;
359 397
398 platform_set_drvdata(pdev, power);
360 power->axp20x_id = (enum axp20x_variants)of_device_get_match_data( 399 power->axp20x_id = (enum axp20x_variants)of_device_get_match_data(
361 &pdev->dev); 400 &pdev->dev);
362 401
@@ -415,6 +454,19 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
415 irq_names[i], ret); 454 irq_names[i], ret);
416 } 455 }
417 456
457 INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus);
458 if (axp20x_usb_vbus_needs_polling(power))
459 queue_delayed_work(system_wq, &power->vbus_detect, 0);
460
461 return 0;
462}
463
464static int axp20x_usb_power_remove(struct platform_device *pdev)
465{
466 struct axp20x_usb_power *power = platform_get_drvdata(pdev);
467
468 cancel_delayed_work_sync(&power->vbus_detect);
469
418 return 0; 470 return 0;
419} 471}
420 472
@@ -434,6 +486,7 @@ MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
434 486
435static struct platform_driver axp20x_usb_power_driver = { 487static struct platform_driver axp20x_usb_power_driver = {
436 .probe = axp20x_usb_power_probe, 488 .probe = axp20x_usb_power_probe,
489 .remove = axp20x_usb_power_remove,
437 .driver = { 490 .driver = {
438 .name = DRVNAME, 491 .name = DRVNAME,
439 .of_match_table = axp20x_usb_power_match, 492 .of_match_table = axp20x_usb_power_match,