summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorOleksandr Suvorov <oleksandr.suvorov@toradex.com>2019-11-11 14:47:08 +0100
committerMarcel Ziswiler <marcel.ziswiler@toradex.com>2020-02-12 11:06:07 +0100
commit4009291ebb6f79e0ee451539e28d87f5d041ae4b (patch)
tree07ec8b180f79cb40b85650bdcd59de9d2be8fd72 /drivers/usb
parent9f8104b2b55285be20391db8ab61e8ed966062f7 (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>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/phy/phy-generic.c44
-rw-r--r--drivers/usb/phy/phy-generic.h1
2 files changed, 29 insertions, 16 deletions
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 89d6e7a5fdb7..08b8875ac520 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -57,28 +57,35 @@ 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)
+static void nop_reset(struct usb_phy_generic *nop)
{
- struct usb_phy_generic *nop = dev_get_drvdata(x->dev);
+ if (!nop->gpiod_reset)
+ return;
- if (!IS_ERR(nop->clk)) {
- if (suspend)
- clk_disable_unprepare(nop->clk);
- else
- clk_prepare_enable(nop->clk);
- }
+ dev_dbg(nop->dev, "reset\n");
- return 0;
+ gpiod_set_value_cansleep(nop->gpiod_reset, 1);
+ usleep_range(10000, 20000);
+ gpiod_set_value_cansleep(nop->gpiod_reset, 0);
}
-static void nop_reset(struct usb_phy_generic *nop)
+static int nop_set_suspend(struct usb_phy *x, int suspend)
{
- if (!nop->gpiod_reset)
- return;
+ struct usb_phy_generic *nop = dev_get_drvdata(x->dev);
- gpiod_set_value(nop->gpiod_reset, 1);
- usleep_range(10000, 20000);
- gpiod_set_value(nop->gpiod_reset, 0);
+ 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 */
@@ -172,7 +179,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
{
struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
- gpiod_set_value(nop->gpiod_reset, 1);
+ gpiod_set_value_cansleep(nop->gpiod_reset, 1);
if (!IS_ERR(nop->clk))
clk_disable_unprepare(nop->clk);
@@ -232,6 +239,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");
nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
GPIOD_ASIS);
@@ -241,6 +251,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;
diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h
index 97289627561d..9ddca95fad33 100644
--- a/drivers/usb/phy/phy-generic.h
+++ b/drivers/usb/phy/phy-generic.h
@@ -15,6 +15,7 @@ struct usb_phy_generic {
struct gpio_desc *gpiod_vbus;
struct regulator *vbus_draw;
bool vbus_draw_enabled;
+ bool reset_on_resume;
unsigned long mA;
unsigned int vbus;
};