| From c5713f7b3635b82b6ddef3298e8994802d943ee2 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sat, 22 Sep 2018 11:44:05 +0200 |
| Subject: phy: phy-twl4030-usb: fix denied runtime access |
| |
| From: Andreas Kemnade <andreas@kemnade.info> |
| |
| [ Upstream commit 6c7103aa026094a4ee2c2708ec6977a6dfc5331d ] |
| |
| When runtime is not enabled, pm_runtime_get_sync() returns -EACCESS, |
| the counter will be incremented but the resume callback not called, |
| so enumeration and charging will not start properly. |
| To avoid that happen, disable irq on suspend and recheck on resume. |
| |
| Practically this happens when the device is woken up from suspend by |
| plugging in usb. |
| |
| Signed-off-by: Andreas Kemnade <andreas@kemnade.info> |
| Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/phy/ti/phy-twl4030-usb.c | 29 +++++++++++++++++++++++++++++ |
| 1 file changed, 29 insertions(+) |
| |
| diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c |
| index a44680d64f9b4..c267afb68f077 100644 |
| --- a/drivers/phy/ti/phy-twl4030-usb.c |
| +++ b/drivers/phy/ti/phy-twl4030-usb.c |
| @@ -144,6 +144,7 @@ |
| #define PMBR1 0x0D |
| #define GPIO_USB_4PIN_ULPI_2430C (3 << 0) |
| |
| +static irqreturn_t twl4030_usb_irq(int irq, void *_twl); |
| /* |
| * If VBUS is valid or ID is ground, then we know a |
| * cable is present and we need to be runtime-enabled |
| @@ -395,6 +396,33 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on) |
| WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); |
| } |
| |
| +static int __maybe_unused twl4030_usb_suspend(struct device *dev) |
| +{ |
| + struct twl4030_usb *twl = dev_get_drvdata(dev); |
| + |
| + /* |
| + * we need enabled runtime on resume, |
| + * so turn irq off here, so we do not get it early |
| + * note: wakeup on usb plug works independently of this |
| + */ |
| + dev_dbg(twl->dev, "%s\n", __func__); |
| + disable_irq(twl->irq); |
| + |
| + return 0; |
| +} |
| + |
| +static int __maybe_unused twl4030_usb_resume(struct device *dev) |
| +{ |
| + struct twl4030_usb *twl = dev_get_drvdata(dev); |
| + |
| + dev_dbg(twl->dev, "%s\n", __func__); |
| + enable_irq(twl->irq); |
| + /* check whether cable status changed */ |
| + twl4030_usb_irq(0, twl); |
| + |
| + return 0; |
| +} |
| + |
| static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev) |
| { |
| struct twl4030_usb *twl = dev_get_drvdata(dev); |
| @@ -655,6 +683,7 @@ static const struct phy_ops ops = { |
| static const struct dev_pm_ops twl4030_usb_pm_ops = { |
| SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend, |
| twl4030_usb_runtime_resume, NULL) |
| + SET_SYSTEM_SLEEP_PM_OPS(twl4030_usb_suspend, twl4030_usb_resume) |
| }; |
| |
| static int twl4030_usb_probe(struct platform_device *pdev) |
| -- |
| 2.20.1 |
| |