From: "Ying-Chun Liu (PaulLiu)" <paul....@linaro.org>

implement the generic NOP USB transceiver for all USB transceiver
which are either built-in into USB IP or which are mostly autonomous.

Signed-off-by: Ying-Chun Liu (PaulLiu) <paul....@linaro.org>
---
 drivers/usb/phy/Kconfig       |   8 ++
 drivers/usb/phy/Makefile      |   1 +
 drivers/usb/phy/usb_nop_phy.c | 137 ++++++++++++++++++++++++++++++++++
 3 files changed, 146 insertions(+)
 create mode 100644 drivers/usb/phy/usb_nop_phy.c

diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 8741553d09..663b50a937 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -13,3 +13,11 @@ config OMAP_USB_PHY
 
 config ROCKCHIP_USB2_PHY
        bool "Rockchip USB2 PHY"
+
+config NOP_USB_XCEIV
+       bool "NOP USB Transceiver Driver"
+       depends on PHY
+       help
+         This driver is to be used by all the usb transceiver which are either
+         built-in with usb ip or which are autonomous and doesn't require any
+         phy programming such as ISP1x04 etc.
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 20f7edf48d..079e24770f 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -6,3 +6,4 @@
 obj-$(CONFIG_TWL4030_USB) += twl4030.o
 obj-$(CONFIG_OMAP_USB_PHY) += omap_usb_phy.o
 obj-$(CONFIG_ROCKCHIP_USB2_PHY) += rockchip_usb2_phy.o
+obj-$(CONFIG_NOP_USB_XCEIV) += usb_nop_phy.o
diff --git a/drivers/usb/phy/usb_nop_phy.c b/drivers/usb/phy/usb_nop_phy.c
new file mode 100644
index 0000000000..7f4133af39
--- /dev/null
+++ b/drivers/usb/phy/usb_nop_phy.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NOP USB transceiver for all USB transceiver which are either built-in
+ * into USB IP or which are mostly autonomous.
+ *
+ * Copyright (C) 2009 Texas Instruments Inc
+ * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2021 Linaro
+ * Author: Ajay Kumar Gupta <ajay.gu...@ti.com>
+ *         Jean-Jacques Hiblot  <jjhib...@ti.com>
+ *         Ying-Chun Liu (PaulLiu) <paul....@linaro.org>
+ * Current status:
+ *      This provides a "nop" transceiver for PHYs which are
+ *      autonomous such as isp1504, isp1707, etc.
+ */
+
+#include <clk.h>
+#include <common.h>
+#include <generic-phy.h>
+#include <asm/gpio.h>
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+
+struct usb_nop_phy_priv {
+       struct clk_bulk bulk;
+       struct udevice *vcc;
+       struct gpio_desc *gpiod_reset;
+       struct gpio_desc *gpiod_vbus;
+};
+
+static int usb_nop_phy_reset(struct phy *phy)
+{
+       int ret = 0;
+       struct usb_nop_phy_priv *priv = dev_get_priv(phy->dev);
+
+       if (!priv->gpiod_reset)
+               return ret;
+
+       ret = dm_gpio_set_value(priv->gpiod_reset, 1);
+       mdelay(20);
+       ret = dm_gpio_set_value(priv->gpiod_reset, 0);
+
+       return ret;
+}
+
+static int usb_nop_phy_init(struct phy *phy)
+{
+       struct usb_nop_phy_priv *priv = dev_get_priv(phy->dev);
+       int ret = 0;
+
+       if (CONFIG_IS_ENABLED(DM_REGULATOR) && priv->vcc) {
+               ret = regulator_set_enable(priv->vcc, true);
+               if (ret < 0) {
+                       dev_err(phy->dev, "Failed to enable power: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       if (CONFIG_IS_ENABLED(CLK)) {
+               ret = clk_enable_bulk(&priv->bulk);
+               if (ret < 0) {
+                       dev_err(phy->dev, "Failed to enable clk: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+static int usb_nop_phy_probe(struct udevice *dev)
+{
+       struct usb_nop_phy_priv *priv = dev_get_priv(dev);
+       u32 clk_rate = 0;
+       int i, ret = 0;
+
+       /* Get all clocks. Actually only main_clk should be assigned */
+       if (CONFIG_IS_ENABLED(CLK)) {
+               ret = clk_get_bulk(dev, &priv->bulk);
+               if (ret < 0)
+                       dev_info(dev, "failed to get clock: %d\n", ret);
+       }
+
+       /* Get vcc-supply */
+       if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
+               ret = device_get_supply_regulator(dev, "vcc-supply", 
&priv->vcc);
+               if (ret < 0) {
+                       dev_info(dev, "failed to get vcc-supply: %d\n", ret);
+                       priv->vcc = NULL;
+               }
+       }
+
+       ret = ofnode_read_u32(dev_ofnode(dev), "clock-frequency", &clk_rate);
+       if (ret < 0)
+               clk_rate = 0;
+
+       priv->gpiod_reset = devm_gpiod_get_optional(dev, "reset", 0);
+       if (priv->gpiod_reset)
+               priv->gpiod_vbus = devm_gpiod_get_optional(dev,
+                                                          "vbus-detect",
+                                                          0);
+
+       if (clk_rate)
+               for (i = 0; i < priv->bulk.count; i++) {
+                       ret = clk_set_rate(&priv->bulk.clks[i], clk_rate);
+                       if (ret < 0) {
+                               dev_err(dev,
+                                       "Failed to set clk rate %d: %d\n",
+                                       clk_rate,
+                                       ret);
+                               return ret;
+                       }
+               }
+
+       return 0;
+}
+
+static const struct udevice_id usb_nop_phy_ids[] = {
+       { .compatible = "usb-nop-xceiv" },
+       { }
+};
+
+static struct phy_ops usb_nop_phy_ops = {
+       .init = usb_nop_phy_init,
+       .reset = usb_nop_phy_reset,
+};
+
+U_BOOT_DRIVER(usb_nop_phy) = {
+       .name   = "usb_nop_phy",
+       .id     = UCLASS_PHY,
+       .of_match = usb_nop_phy_ids,
+       .ops = &usb_nop_phy_ops,
+       .probe = usb_nop_phy_probe,
+       .priv_auto      = sizeof(struct usb_nop_phy_priv),
+};
-- 
2.30.1

Reply via email to