diff options
author | Oleksandr Suvorov <oleksandr.suvorov@toradex.com> | 2019-11-11 14:47:08 +0100 |
---|---|---|
committer | Philippe Schenker <philippe.schenker@toradex.com> | 2020-08-13 16:04:48 +0200 |
commit | 0d2c39207e403a8ce9141f2abd70bb432810d02d (patch) | |
tree | 59f9e79cf846d51f426a9c38cf3c099c9b484526 /drivers/usb/phy/phy-generic.c | |
parent | 149b42a99f4f79f10c8930da4d9d1f71a86e395f (diff) |
usb: phy: phy-generic: add an ability to reset on resume
Regular USB device gets power down in system suspend.
New hardware designs require to include USB devices as part of main
scheme. If this case the power could be provide to a device
unmanagable. The best option to reset this kind of devices is to
reset it on USB physical interface level.
Add this ability. Using "reset-on-resume" DT-option for usb-generic driver
enables reset nested device on resume event by gpio interface.
Signed-off-by: Oleksandr Suvorov <oleksandr.suvorov@toradex.com>
(cherry picked from commit 4c74d1a7b1e1ece7f6b7a4dfe5da9170a6dd9644)
Conflicts:
drivers/usb/phy/phy-generic.c
Signed-off-by: Max Krummenacher <max.krummenacher@toradex.com>
Diffstat (limited to 'drivers/usb/phy/phy-generic.c')
-rw-r--r-- | drivers/usb/phy/phy-generic.c | 40 |
1 files changed, 26 insertions, 14 deletions
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index a53b89be5324..5c39a2644b5c 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -44,30 +44,37 @@ void usb_phy_generic_unregister(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(usb_phy_generic_unregister); -static int nop_set_suspend(struct usb_phy *x, int suspend) -{ - struct usb_phy_generic *nop = dev_get_drvdata(x->dev); - - if (!IS_ERR(nop->clk)) { - if (suspend) - clk_disable_unprepare(nop->clk); - else - clk_prepare_enable(nop->clk); - } - - return 0; -} - static void nop_reset(struct usb_phy_generic *nop) { if (!nop->gpiod_reset) return; + dev_dbg(nop->dev, "reset\n"); + gpiod_set_value_cansleep(nop->gpiod_reset, 1); usleep_range(10000, 20000); gpiod_set_value_cansleep(nop->gpiod_reset, 0); } +static int nop_set_suspend(struct usb_phy *x, int suspend) +{ + struct usb_phy_generic *nop = dev_get_drvdata(x->dev); + + dev_dbg(x->dev, "%s\n", suspend ? "suspend" : "resume"); + + if (suspend) { + if (!IS_ERR(nop->clk)) + clk_disable_unprepare(nop->clk); + } else { + if (!IS_ERR(nop->clk)) + clk_prepare_enable(nop->clk); + + if (nop->reset_on_resume) + nop_reset(nop); + } + return 0; +} + /* interface to regulator framework */ static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA) { @@ -219,6 +226,9 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, if (of_property_read_u32(node, "clock-frequency", &clk_rate)) clk_rate = 0; + nop->reset_on_resume = + of_property_read_bool(node, "reset-on-resume"); + needs_vcc = of_property_read_bool(node, "vcc-supply"); needs_clk = of_property_read_bool(node, "clocks"); nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset", @@ -229,6 +239,8 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, "vbus-detect", GPIOD_ASIS); err = PTR_ERR_OR_ZERO(nop->gpiod_vbus); + } else { + nop->gpiod_reset = NULL; } } else if (pdata) { type = pdata->type; |