summaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy/phy-generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/phy/phy-generic.c')
-rw-r--r--drivers/usb/phy/phy-generic.c144
1 files changed, 77 insertions, 67 deletions
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index efe59f3f7fd..aa6d37b3378 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -35,6 +35,9 @@
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
#include "phy-generic.h"
@@ -45,8 +48,9 @@ void usb_nop_xceiv_register(void)
if (pd)
return;
pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0);
- if (!pd) {
+ if (IS_ERR(pd)) {
pr_err("Unable to register generic usb transceiver\n");
+ pd = NULL;
return;
}
}
@@ -64,6 +68,23 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
return 0;
}
+static void nop_reset_set(struct usb_phy_gen_xceiv *nop, int asserted)
+{
+ int value;
+
+ if (!gpio_is_valid(nop->gpio_reset))
+ return;
+
+ value = asserted;
+ if (nop->reset_active_low)
+ value = !value;
+
+ gpio_set_value_cansleep(nop->gpio_reset, value);
+
+ if (!asserted)
+ usleep_range(10000, 20000);
+}
+
int usb_gen_phy_init(struct usb_phy *phy)
{
struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev);
@@ -74,13 +95,10 @@ int usb_gen_phy_init(struct usb_phy *phy)
}
if (!IS_ERR(nop->clk))
- clk_enable(nop->clk);
+ clk_prepare_enable(nop->clk);
- if (!IS_ERR(nop->reset)) {
- /* De-assert RESET */
- if (regulator_enable(nop->reset))
- dev_err(phy->dev, "Failed to de-assert reset\n");
- }
+ /* De-assert RESET */
+ nop_reset_set(nop, 0);
return 0;
}
@@ -90,14 +108,11 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
{
struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev);
- if (!IS_ERR(nop->reset)) {
- /* Assert RESET */
- if (regulator_disable(nop->reset))
- dev_err(phy->dev, "Failed to assert reset\n");
- }
+ /* Assert RESET */
+ nop_reset_set(nop, 1);
if (!IS_ERR(nop->clk))
- clk_disable(nop->clk);
+ clk_disable_unprepare(nop->clk);
if (!IS_ERR(nop->vcc)) {
if (regulator_disable(nop->vcc))
@@ -136,11 +151,40 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
}
int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
- enum usb_phy_type type, u32 clk_rate, bool needs_vcc,
- bool needs_reset)
+ struct usb_phy_gen_xceiv_platform_data *pdata)
{
+ enum usb_phy_type type = USB_PHY_TYPE_USB2;
int err;
+ u32 clk_rate = 0;
+ bool needs_vcc = false;
+
+ nop->reset_active_low = true; /* default behaviour */
+
+ if (dev->of_node) {
+ struct device_node *node = dev->of_node;
+ enum of_gpio_flags flags = 0;
+
+ if (of_property_read_u32(node, "clock-frequency", &clk_rate))
+ clk_rate = 0;
+
+ needs_vcc = of_property_read_bool(node, "vcc-supply");
+ nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios",
+ 0, &flags);
+ if (nop->gpio_reset == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+ } else if (pdata) {
+ type = pdata->type;
+ clk_rate = pdata->clk_rate;
+ needs_vcc = pdata->needs_vcc;
+ nop->gpio_reset = pdata->gpio_reset;
+ } else {
+ nop->gpio_reset = -1;
+ }
+
nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
GFP_KERNEL);
if (!nop->phy.otg)
@@ -160,14 +204,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
}
}
- if (!IS_ERR(nop->clk)) {
- err = clk_prepare(nop->clk);
- if (err) {
- dev_err(dev, "Error preparing clock\n");
- return err;
- }
- }
-
nop->vcc = devm_regulator_get(dev, "vcc");
if (IS_ERR(nop->vcc)) {
dev_dbg(dev, "Error getting vcc regulator: %ld\n",
@@ -176,12 +212,22 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
return -EPROBE_DEFER;
}
- nop->reset = devm_regulator_get(dev, "reset");
- if (IS_ERR(nop->reset)) {
- dev_dbg(dev, "Error getting reset regulator: %ld\n",
- PTR_ERR(nop->reset));
- if (needs_reset)
- return -EPROBE_DEFER;
+ if (gpio_is_valid(nop->gpio_reset)) {
+ unsigned long gpio_flags;
+
+ /* Assert RESET */
+ if (nop->reset_active_low)
+ gpio_flags = GPIOF_OUT_INIT_LOW;
+ else
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+
+ err = devm_gpio_request_one(dev, nop->gpio_reset,
+ gpio_flags, dev_name(dev));
+ if (err) {
+ dev_err(dev, "Error requesting RESET GPIO %d\n",
+ nop->gpio_reset);
+ return err;
+ }
}
nop->dev = dev;
@@ -200,48 +246,17 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
}
EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
-void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop)
-{
- if (!IS_ERR(nop->clk))
- clk_unprepare(nop->clk);
-}
-EXPORT_SYMBOL_GPL(usb_phy_gen_cleanup_phy);
-
static int usb_phy_gen_xceiv_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct usb_phy_gen_xceiv_platform_data *pdata =
- dev_get_platdata(&pdev->dev);
struct usb_phy_gen_xceiv *nop;
- enum usb_phy_type type = USB_PHY_TYPE_USB2;
int err;
- u32 clk_rate = 0;
- bool needs_vcc = false;
- bool needs_reset = false;
-
- if (dev->of_node) {
- struct device_node *node = dev->of_node;
-
- if (of_property_read_u32(node, "clock-frequency", &clk_rate))
- clk_rate = 0;
-
- needs_vcc = of_property_read_bool(node, "vcc-supply");
- needs_reset = of_property_read_bool(node, "reset-supply");
-
- } else if (pdata) {
- type = pdata->type;
- clk_rate = pdata->clk_rate;
- needs_vcc = pdata->needs_vcc;
- needs_reset = pdata->needs_reset;
- }
nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
if (!nop)
return -ENOMEM;
-
- err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc,
- needs_reset);
+ err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
if (err)
return err;
@@ -252,23 +267,18 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
err);
- goto err_add;
+ return err;
}
platform_set_drvdata(pdev, nop);
return 0;
-
-err_add:
- usb_phy_gen_cleanup_phy(nop);
- return err;
}
static int usb_phy_gen_xceiv_remove(struct platform_device *pdev)
{
struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev);
- usb_phy_gen_cleanup_phy(nop);
usb_remove_phy(&nop->phy);
return 0;