summaryrefslogtreecommitdiff
path: root/drivers/usb/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r--drivers/usb/phy/Kconfig16
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c2
-rw-r--r--drivers/usb/phy/phy-msm-usb.c110
-rw-r--r--drivers/usb/phy/phy-rcar-gen2-usb.c246
-rw-r--r--drivers/usb/phy/phy.c97
6 files changed, 183 insertions, 289 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 2175678e674e..869c0cfcad98 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -91,7 +91,7 @@ config TWL6030_USB
config USB_GPIO_VBUS
tristate "GPIO based peripheral-only VBUS sensing 'transceiver'"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select USB_PHY
help
Provides simple GPIO VBUS sensing for controllers with an
@@ -141,6 +141,7 @@ config USB_MSM_OTG
tristate "Qualcomm on-chip USB OTG controller support"
depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST)
depends on RESET_CONTROLLER
+ depends on EXTCON
select USB_PHY
help
Enable this to support the USB OTG transceiver on Qualcomm chips. It
@@ -186,19 +187,6 @@ config USB_RCAR_PHY
To compile this driver as a module, choose M here: the
module will be called phy-rcar-usb.
-config USB_RCAR_GEN2_PHY
- tristate "Renesas R-Car Gen2 USB PHY support"
- depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
- select USB_PHY
- help
- Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver.
- It is typically used to control internal USB PHY for USBHS,
- and to configure shared USB channels 0 and 2.
- This driver supports R8A7790 and R8A7791.
-
- To compile this driver as a module, choose M here: the
- module will be called phy-rcar-gen2-usb.
-
config USB_ULPI
bool "Generic ULPI Transceiver Driver"
depends on ARM || ARM64
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 75f2bba58c84..e36ab1d46d8b 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -23,7 +23,6 @@ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
-obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 03ab0c699f74..0c912d3950a5 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -1504,7 +1504,7 @@ static int ab8500_usb_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id ab8500_usb_devtype[] = {
+static const struct platform_device_id ab8500_usb_devtype[] = {
{ .name = "ab8500-usb", },
{ .name = "ab8540-usb", },
{ .name = "ab9540-usb", },
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index c9156beeadef..00c49bb1bd29 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg)
static int msm_phy_notify_disconnect(struct usb_phy *phy,
enum usb_device_speed speed)
{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
int val;
+ if (motg->manual_pullup) {
+ val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL;
+ usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A));
+ }
+
/*
* Put the transceiver in non-driving mode. Otherwise host
* may not detect soft-disconnection.
@@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy)
ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
}
+ if (motg->manual_pullup) {
+ val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT;
+ ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A));
+
+ val = readl(USB_GENCONFIG_2);
+ val |= GENCONFIG_2_SESS_VLD_CTRL_EN;
+ writel(val, USB_GENCONFIG_2);
+
+ val = readl(USB_USBCMD);
+ val |= USBCMD_SESS_VLD_CTRL;
+ writel(val, USB_USBCMD);
+
+ val = ulpi_read(phy, ULPI_FUNC_CTRL);
+ val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ val |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+ ulpi_write(phy, val, ULPI_FUNC_CTRL);
+ }
+
if (motg->phy_number)
writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
@@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, msm_otg_dt_match);
+static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb);
+ struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus);
+
+ if (event)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+
+ schedule_work(&motg->sm_work);
+
+ return NOTIFY_DONE;
+}
+
+static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb);
+ struct msm_otg *motg = container_of(id, struct msm_otg, id);
+
+ if (event)
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+
+ schedule_work(&motg->sm_work);
+
+ return NOTIFY_DONE;
+}
+
static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
{
struct msm_otg_platform_data *pdata;
+ struct extcon_dev *ext_id, *ext_vbus;
const struct of_device_id *id;
struct device_node *node = pdev->dev.of_node;
struct property *prop;
@@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX];
}
+ motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup");
+
+ ext_id = ERR_PTR(-ENODEV);
+ ext_vbus = ERR_PTR(-ENODEV);
+ if (of_property_read_bool(node, "extcon")) {
+
+ /* Each one of them is not mandatory */
+ ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0);
+ if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
+ return PTR_ERR(ext_vbus);
+
+ ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1);
+ if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
+ return PTR_ERR(ext_id);
+ }
+
+ if (!IS_ERR(ext_vbus)) {
+ motg->vbus.nb.notifier_call = msm_otg_vbus_notifier;
+ ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name,
+ "USB", &motg->vbus.nb);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "register VBUS notifier failed\n");
+ return ret;
+ }
+
+ ret = extcon_get_cable_state(ext_vbus, "USB");
+ if (ret)
+ set_bit(B_SESS_VLD, &motg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &motg->inputs);
+ }
+
+ if (!IS_ERR(ext_id)) {
+ motg->id.nb.notifier_call = msm_otg_id_notifier;
+ ret = extcon_register_interest(&motg->id.conn, ext_id->name,
+ "USB-HOST", &motg->id.nb);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "register ID notifier failed\n");
+ return ret;
+ }
+
+ ret = extcon_get_cable_state(ext_id, "USB-HOST");
+ if (ret)
+ clear_bit(ID, &motg->inputs);
+ else
+ set_bit(ID, &motg->inputs);
+ }
+
prop = of_find_property(node, "qcom,phy-init-sequence", &len);
if (!prop || !len)
return 0;
@@ -1700,6 +1805,11 @@ static int msm_otg_remove(struct platform_device *pdev)
if (phy->otg->host || phy->otg->gadget)
return -EBUSY;
+ if (motg->id.conn.edev)
+ extcon_unregister_interest(&motg->id.conn);
+ if (motg->vbus.conn.edev)
+ extcon_unregister_interest(&motg->vbus.conn);
+
msm_otg_debugfs_cleanup();
cancel_delayed_work_sync(&motg->chg_work);
cancel_work_sync(&motg->sm_work);
diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c
deleted file mode 100644
index f81800b6562a..000000000000
--- a/drivers/usb/phy/phy-rcar-gen2-usb.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Renesas R-Car Gen2 USB phy driver
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Copyright (C) 2013 Cogent Embedded, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_data/usb-rcar-gen2-phy.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/usb/otg.h>
-
-struct rcar_gen2_usb_phy_priv {
- struct usb_phy phy;
- void __iomem *base;
- struct clk *clk;
- spinlock_t lock;
- int usecount;
- u32 ugctrl2;
-};
-
-#define usb_phy_to_priv(p) container_of(p, struct rcar_gen2_usb_phy_priv, phy)
-
-/* Low Power Status register */
-#define USBHS_LPSTS_REG 0x02
-#define USBHS_LPSTS_SUSPM (1 << 14)
-
-/* USB General control register */
-#define USBHS_UGCTRL_REG 0x80
-#define USBHS_UGCTRL_CONNECT (1 << 2)
-#define USBHS_UGCTRL_PLLRESET (1 << 0)
-
-/* USB General control register 2 */
-#define USBHS_UGCTRL2_REG 0x84
-#define USBHS_UGCTRL2_USB0_PCI (1 << 4)
-#define USBHS_UGCTRL2_USB0_HS (3 << 4)
-#define USBHS_UGCTRL2_USB2_PCI (0 << 31)
-#define USBHS_UGCTRL2_USB2_SS (1 << 31)
-
-/* USB General status register */
-#define USBHS_UGSTS_REG 0x88
-#define USBHS_UGSTS_LOCK (1 << 8)
-
-/* Enable USBHS internal phy */
-static int __rcar_gen2_usbhs_phy_enable(void __iomem *base)
-{
- u32 val;
- int i;
-
- /* USBHS PHY power on */
- val = ioread32(base + USBHS_UGCTRL_REG);
- val &= ~USBHS_UGCTRL_PLLRESET;
- iowrite32(val, base + USBHS_UGCTRL_REG);
-
- val = ioread16(base + USBHS_LPSTS_REG);
- val |= USBHS_LPSTS_SUSPM;
- iowrite16(val, base + USBHS_LPSTS_REG);
-
- for (i = 0; i < 20; i++) {
- val = ioread32(base + USBHS_UGSTS_REG);
- if ((val & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) {
- val = ioread32(base + USBHS_UGCTRL_REG);
- val |= USBHS_UGCTRL_CONNECT;
- iowrite32(val, base + USBHS_UGCTRL_REG);
- return 0;
- }
- udelay(1);
- }
-
- /* Timed out waiting for the PLL lock */
- return -ETIMEDOUT;
-}
-
-/* Disable USBHS internal phy */
-static int __rcar_gen2_usbhs_phy_disable(void __iomem *base)
-{
- u32 val;
-
- /* USBHS PHY power off */
- val = ioread32(base + USBHS_UGCTRL_REG);
- val &= ~USBHS_UGCTRL_CONNECT;
- iowrite32(val, base + USBHS_UGCTRL_REG);
-
- val = ioread16(base + USBHS_LPSTS_REG);
- val &= ~USBHS_LPSTS_SUSPM;
- iowrite16(val, base + USBHS_LPSTS_REG);
-
- val = ioread32(base + USBHS_UGCTRL_REG);
- val |= USBHS_UGCTRL_PLLRESET;
- iowrite32(val, base + USBHS_UGCTRL_REG);
- return 0;
-}
-
-/* Setup USB channels */
-static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv)
-{
- u32 val;
-
- clk_prepare_enable(priv->clk);
-
- /* Set USB channels in the USBHS UGCTRL2 register */
- val = ioread32(priv->base + USBHS_UGCTRL2_REG);
- val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS);
- val |= priv->ugctrl2;
- iowrite32(val, priv->base + USBHS_UGCTRL2_REG);
-}
-
-/* Shutdown USB channels */
-static void __rcar_gen2_usb_phy_shutdown(struct rcar_gen2_usb_phy_priv *priv)
-{
- __rcar_gen2_usbhs_phy_disable(priv->base);
- clk_disable_unprepare(priv->clk);
-}
-
-static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend)
-{
- struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy);
- unsigned long flags;
- int retval;
-
- spin_lock_irqsave(&priv->lock, flags);
- retval = suspend ? __rcar_gen2_usbhs_phy_disable(priv->base) :
- __rcar_gen2_usbhs_phy_enable(priv->base);
- spin_unlock_irqrestore(&priv->lock, flags);
- return retval;
-}
-
-static int rcar_gen2_usb_phy_init(struct usb_phy *phy)
-{
- struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- /*
- * Enable the clock and setup USB channels
- * if it's the first user
- */
- if (!priv->usecount++)
- __rcar_gen2_usb_phy_init(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
- return 0;
-}
-
-static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy)
-{
- struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- if (!priv->usecount) {
- dev_warn(phy->dev, "Trying to disable phy with 0 usecount\n");
- goto out;
- }
-
- /* Disable everything if it's the last user */
- if (!--priv->usecount)
- __rcar_gen2_usb_phy_shutdown(priv);
-out:
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static int rcar_gen2_usb_phy_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct rcar_gen2_phy_platform_data *pdata;
- struct rcar_gen2_usb_phy_priv *priv;
- struct resource *res;
- void __iomem *base;
- struct clk *clk;
- int retval;
-
- pdata = dev_get_platdata(dev);
- if (!pdata) {
- dev_err(dev, "No platform data\n");
- return -EINVAL;
- }
-
- clk = devm_clk_get(dev, "usbhs");
- if (IS_ERR(clk)) {
- dev_err(dev, "Can't get the clock\n");
- return PTR_ERR(clk);
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- spin_lock_init(&priv->lock);
- priv->clk = clk;
- priv->base = base;
- priv->ugctrl2 = pdata->chan0_pci ?
- USBHS_UGCTRL2_USB0_PCI : USBHS_UGCTRL2_USB0_HS;
- priv->ugctrl2 |= pdata->chan2_pci ?
- USBHS_UGCTRL2_USB2_PCI : USBHS_UGCTRL2_USB2_SS;
- priv->phy.dev = dev;
- priv->phy.label = dev_name(dev);
- priv->phy.init = rcar_gen2_usb_phy_init;
- priv->phy.shutdown = rcar_gen2_usb_phy_shutdown;
- priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend;
-
- retval = usb_add_phy_dev(&priv->phy);
- if (retval < 0) {
- dev_err(dev, "Failed to add USB phy\n");
- return retval;
- }
-
- platform_set_drvdata(pdev, priv);
-
- return retval;
-}
-
-static int rcar_gen2_usb_phy_remove(struct platform_device *pdev)
-{
- struct rcar_gen2_usb_phy_priv *priv = platform_get_drvdata(pdev);
-
- usb_remove_phy(&priv->phy);
-
- return 0;
-}
-
-static struct platform_driver rcar_gen2_usb_phy_driver = {
- .driver = {
- .name = "usb_phy_rcar_gen2",
- },
- .probe = rcar_gen2_usb_phy_probe,
- .remove = rcar_gen2_usb_phy_remove,
-};
-
-module_platform_driver(rcar_gen2_usb_phy_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Renesas R-Car Gen2 USB phy");
-MODULE_AUTHOR("Valentine Barshak <valentine.barshak@cogentembedded.com>");
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index d1cd6b50f520..98f75d2842b7 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -22,6 +22,11 @@ static LIST_HEAD(phy_list);
static LIST_HEAD(phy_bind_list);
static DEFINE_SPINLOCK(phy_lock);
+struct phy_devm {
+ struct usb_phy *phy;
+ struct notifier_block *nb;
+};
+
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@@ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res)
usb_put_phy(phy);
}
+static void devm_usb_phy_release2(struct device *dev, void *_res)
+{
+ struct phy_devm *res = _res;
+
+ if (res->nb)
+ usb_unregister_notifier(res->phy, res->nb);
+ usb_put_phy(res->phy);
+}
+
static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
{
struct usb_phy **phy = res;
@@ -153,40 +167,30 @@ err0:
EXPORT_SYMBOL_GPL(usb_get_phy);
/**
- * devm_usb_get_phy_by_phandle - find the USB PHY by phandle
+ * devm_usb_get_phy_by_node - find the USB PHY by device_node
* @dev - device that requests this phy
- * @phandle - name of the property holding the phy phandle value
- * @index - the index of the phy
+ * @node - the device_node for the phy device.
+ * @nb - a notifier_block to register with the phy.
*
- * Returns the phy driver associated with the given phandle value,
+ * Returns the phy driver associated with the given device_node,
* after getting a refcount to it, -ENODEV if there is no such phy or
- * -EPROBE_DEFER if there is a phandle to the phy, but the device is
- * not yet loaded. While at that, it also associates the device with
+ * -EPROBE_DEFER if the device is not yet loaded. While at that, it
+ * also associates the device with
* the phy using devres. On driver detach, release function is invoked
* on the devres data, then, devres data is freed.
*
- * For use by USB host and peripheral drivers.
+ * For use by peripheral drivers for devices related to a phy,
+ * such as a charger.
*/
-struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
- const char *phandle, u8 index)
+struct usb_phy *devm_usb_get_phy_by_node(struct device *dev,
+ struct device_node *node,
+ struct notifier_block *nb)
{
- struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr;
+ struct usb_phy *phy = ERR_PTR(-ENOMEM);
+ struct phy_devm *ptr;
unsigned long flags;
- struct device_node *node;
- if (!dev->of_node) {
- dev_dbg(dev, "device does not have a device node entry\n");
- return ERR_PTR(-EINVAL);
- }
-
- node = of_parse_phandle(dev->of_node, phandle, index);
- if (!node) {
- dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
- dev->of_node->full_name);
- return ERR_PTR(-ENODEV);
- }
-
- ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
+ ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL);
if (!ptr) {
dev_dbg(dev, "failed to allocate memory for devres\n");
goto err0;
@@ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
devres_free(ptr);
goto err1;
}
-
- *ptr = phy;
+ if (nb)
+ usb_register_notifier(phy, nb);
+ ptr->phy = phy;
+ ptr->nb = nb;
devres_add(dev, ptr);
get_device(phy->dev);
@@ -215,10 +221,47 @@ err1:
spin_unlock_irqrestore(&phy_lock, flags);
err0:
- of_node_put(node);
return phy;
}
+EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node);
+
+/**
+ * devm_usb_get_phy_by_phandle - find the USB PHY by phandle
+ * @dev - device that requests this phy
+ * @phandle - name of the property holding the phy phandle value
+ * @index - the index of the phy
+ *
+ * Returns the phy driver associated with the given phandle value,
+ * after getting a refcount to it, -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * not yet loaded. While at that, it also associates the device with
+ * the phy using devres. On driver detach, release function is invoked
+ * on the devres data, then, devres data is freed.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
+ const char *phandle, u8 index)
+{
+ struct device_node *node;
+ struct usb_phy *phy;
+
+ if (!dev->of_node) {
+ dev_dbg(dev, "device does not have a device node entry\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ node = of_parse_phandle(dev->of_node, phandle, index);
+ if (!node) {
+ dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
+ dev->of_node->full_name);
+ return ERR_PTR(-ENODEV);
+ }
+ phy = devm_usb_get_phy_by_node(dev, node, NULL);
+ of_node_put(node);
+ return phy;
+}
EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle);
/**