usb: dwc3: add THEAD/LicheePi4A vendor driver 64/316864/1
authorMarek Szyprowski <m.szyprowski@samsung.com>
Thu, 29 Aug 2024 14:24:57 +0000 (16:24 +0200)
committerMarek Szyprowski <m.szyprowski@samsung.com>
Thu, 29 Aug 2024 14:46:34 +0000 (16:46 +0200)
Taken from vendor kernel [1].

[1] https://gitee.com/thead-yocto/kernel.git

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Change-Id: I213452d619632addb75a23373542a962c21e6c9b

drivers/usb/dwc3/Kconfig
drivers/usb/dwc3/Makefile
drivers/usb/dwc3/dwc3-thead.c [new file with mode: 0644]

index 98efcbb76c882147f17981ed61dcb360f50c1739..b97b1fc2f2644a226bb83280fc399c606ffc44af 100644 (file)
@@ -178,4 +178,13 @@ config USB_DWC3_OCTEON
          Only the host mode is currently supported.
          Say 'Y' or 'M' here if you have one such device.
 
+config USB_DWC3_THEAD
+       tristate "Thead Platform"
+       depends on OF && COMMON_CLK
+       default USB_DWC3
+       help
+         Thead SoC use DesignWare Core IP for USB2/3
+         functionality.
+         Say 'Y' or 'M' if you have one such device.
+
 endif
index fe1493d4bbe58e5c4de0adf477e023ac386cd236..9523a51dd27979e08f1fed206324b8e18860e581 100644 (file)
@@ -55,3 +55,4 @@ obj-$(CONFIG_USB_DWC3_QCOM)           += dwc3-qcom.o
 obj-$(CONFIG_USB_DWC3_IMX8MP)          += dwc3-imx8mp.o
 obj-$(CONFIG_USB_DWC3_XILINX)          += dwc3-xilinx.o
 obj-$(CONFIG_USB_DWC3_OCTEON)          += dwc3-octeon.o
+obj-$(CONFIG_USB_DWC3_THEAD)           += dwc3-thead.o
diff --git a/drivers/usb/dwc3/dwc3-thead.c b/drivers/usb/dwc3/dwc3-thead.c
new file mode 100644 (file)
index 0000000..5f71a06
--- /dev/null
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Inspired by dwc3-of-simple.c
+ */
+
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/of_clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/extcon.h>
+#include <linux/of_platform.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/usb/of.h>
+
+#include "core.h"
+
+/* USB3_DRD registers */
+#define USB_CLK_GATE_STS               0x0
+#define USB_LOGIC_ANALYZER_TRACE_STS0  0x4
+#define USB_LOGIC_ANALYZER_TRACE_STS1  0x8
+#define USB_GPIO                               0xc
+#define USB_DEBUG_STS0                 0x10
+#define USB_DEBUG_STS1                 0x14
+#define USB_DEBUG_STS2                 0x18
+#define USBCTL_CLK_CTRL0               0x1c
+#define USBPHY_CLK_CTRL1               0x20
+#define USBPHY_TEST_CTRL0              0x24
+#define USBPHY_TEST_CTRL1              0x28
+#define USBPHY_TEST_CTRL2              0x2c
+#define USBPHY_TEST_CTRL3              0x30
+#define USB_SSP_EN                             0x34
+#define USB_HADDR_SEL                  0x38
+#define USB_SYS                                        0x3c
+#define USB_HOST_STATUS                        0x40
+#define USB_HOST_CTRL                  0x44
+#define USBPHY_HOST_CTRL               0x48
+#define USBPHY_HOST_STATUS             0x4c
+#define USB_TEST_REG0                  0x50
+#define USB_TEST_REG1                  0x54
+#define USB_TEST_REG2                  0x58
+#define USB_TEST_REG3                  0x5c
+
+/* Bit fields */
+/* USB_SYS */
+#define TEST_POWERDOWN_SSP     BIT(2)
+#define TEST_POWERDOWN_HSP     BIT(1)
+#define COMMONONN                      BIT(0)
+
+/* USB_SSP_EN */
+#define REF_SSP_EN                     BIT(0)
+
+/* USBPHY_HOST_CTRL */
+#define HOST_U2_PORT_DISABLE   BIT(6)
+#define HOST_U3_PORT_DISABLE   BIT(5)
+
+/* MISC_SYSREG registers */
+#define USB3_DRD_SWRST                 0x14
+
+/* Bit fields */
+/* USB3_DRD_SWRST */
+#define USB3_DRD_VCCRST                BIT(2)
+#define USB3_DRD_PHYRST                BIT(1)
+#define USB3_DRD_PRST          BIT(0)
+#define USB3_DRD_MASK          GENMASK(2, 0)
+
+/* USB as host or device*/
+#define USB_AS_HOST         (true)
+#define USB_AS_DEVICE       (false)
+
+static bool usb_role = USB_AS_HOST;
+module_param(usb_role, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(usb_role, "USB role");
+
+struct dwc3_thead {
+       struct device           *dev;
+       struct clk_bulk_data    *clks;
+       int                     num_clocks;
+
+       struct regmap           *usb3_drd;
+       struct regmap           *misc_sysreg;
+
+       struct gpio_desc        *hubswitch;
+       struct regulator        *hub1v2;
+       struct regulator        *hub5v;
+       struct regulator        *vbus;
+};
+
+static void dwc3_thead_deassert(struct dwc3_thead *thead)
+{
+       /* 1. reset assert */
+       regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
+                               USB3_DRD_MASK, USB3_DRD_PRST);
+
+       /* 
+        *      2. Common Block Power-Down Control.
+        *      Controls the power-down signals in the PLL block
+        *      when the USB 3.0 femtoPHY is in Suspend or Sleep mode.
+        */
+       regmap_update_bits(thead->usb3_drd, USB_SYS,
+                               COMMONONN, COMMONONN);
+
+       /*
+        *      3. Reference Clock Enable for SS function.
+        *      Enables the reference clock to the prescaler. 
+        *      The ref_ssp_en signal must remain de-asserted until
+        *      the reference clock is running at the appropriate frequency,
+        *      at which point ref_ssp_en can be asserted.
+        *      For lower power states, ref_ssp_en can also be de-asserted.
+        */
+       regmap_update_bits(thead->usb3_drd, USB_SSP_EN,
+                               REF_SSP_EN, REF_SSP_EN);
+
+       /* 4. set host ctrl */
+       regmap_write(thead->usb3_drd, USB_HOST_CTRL, 0x1101);
+
+       /* 5. reset deassert */
+       regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
+                               USB3_DRD_MASK, USB3_DRD_MASK);
+
+       /* 6. wait deassert complete */
+       udelay(10);
+}
+
+static void dwc3_thead_assert(struct dwc3_thead *thead)
+{
+       /* close ssp */
+       regmap_update_bits(thead->usb3_drd, USB_SSP_EN,
+                               REF_SSP_EN, 0);
+
+       /* reset assert usb */
+       regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
+                               USB3_DRD_MASK, 0);
+
+}
+
+static int dwc3_thead_probe(struct platform_device *pdev)
+{
+       struct device           *dev = &pdev->dev;
+       struct device_node      *np = dev->of_node;
+       struct dwc3_thead       *thead;
+       int                     ret;
+
+       thead = devm_kzalloc(&pdev->dev, sizeof(*thead), GFP_KERNEL);
+       if (!thead)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, thead);
+       thead->dev = &pdev->dev;
+
+       thead->hubswitch = devm_gpiod_get(dev, "hubswitch", (usb_role == USB_AS_DEVICE) ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
+       if (IS_ERR(thead->hubswitch))
+               dev_dbg(dev, "no need to get hubswitch GPIO\n");
+       dev_info(dev, "hubswitch usb_role = %d\n", usb_role);
+
+       thead->vbus = devm_regulator_get(dev, "vbus");
+       if (IS_ERR(thead->vbus))
+               dev_dbg(dev, "no need to get vbus\n");
+       else {
+               ret = regulator_enable(thead->vbus);
+
+               if (ret) {
+                       dev_err(dev, "failed to enable regulator vbus %d\n", ret);
+               }
+       }
+
+       thead->hub1v2 = devm_regulator_get(dev, "hub1v2");
+       if (IS_ERR(thead->hub1v2))
+               dev_dbg(dev, "no need to set hub1v2\n");
+       else {
+               ret = regulator_enable(thead->hub1v2);
+
+               if (ret) {
+                       dev_err(dev, "failed to enable regulator hub1v2 %d\n", ret);
+               }
+       }
+
+       thead->hub5v = devm_regulator_get(dev, "hub5v");
+       if (IS_ERR(thead->hub5v))
+               dev_dbg(dev, "no need to set hub5v\n");
+       else {
+               ret = regulator_enable(thead->hub5v);
+
+               if (ret) {
+                       dev_err(dev, "failed to enable regulator hub1v2 %d\n", ret);
+               }
+       }
+
+       thead->misc_sysreg = syscon_regmap_lookup_by_phandle(np, "usb3-misc-regmap");
+       if (IS_ERR(thead->misc_sysreg))
+               return PTR_ERR(thead->misc_sysreg);
+
+       thead->usb3_drd = syscon_regmap_lookup_by_phandle(np, "usb3-drd-regmap");
+       if (IS_ERR(thead->usb3_drd))
+               return PTR_ERR(thead->usb3_drd);
+
+       ret = clk_bulk_get_all(thead->dev, &thead->clks);
+       if (ret < 0)
+               goto err;
+
+       thead->num_clocks = ret;
+
+       ret = clk_bulk_prepare_enable(thead->num_clocks, thead->clks);
+       if (ret)
+               goto err;
+
+       dwc3_thead_deassert(thead);
+
+       ret = of_platform_populate(np, NULL, NULL, dev);
+       if (ret) {
+               dev_err(dev, "failed to register dwc3 core - %d\n", ret);
+               goto err_clk_put;
+       }
+
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
+
+       dev_info(dev, "light dwc3 probe ok!\n");
+
+       return 0;
+
+err_clk_put:
+       clk_bulk_disable_unprepare(thead->num_clocks, thead->clks);
+       clk_bulk_put_all(thead->num_clocks, thead->clks);
+err:
+       return ret;
+}
+
+static int dwc3_thead_remove(struct platform_device *pdev)
+{
+       struct dwc3_thead       *thead = platform_get_drvdata(pdev);
+
+       dwc3_thead_assert(thead);
+
+       of_platform_depopulate(thead->dev);
+
+       clk_bulk_disable_unprepare(thead->num_clocks, thead->clks);
+       clk_bulk_put_all(thead->num_clocks, thead->clks);
+
+       pm_runtime_disable(thead->dev);
+       pm_runtime_set_suspended(thead->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_thead_pm_suspend(struct device *dev)
+{
+       struct dwc3_thead *thead = dev_get_drvdata(dev);
+    int ret;
+       dwc3_thead_assert(thead);
+
+       clk_bulk_disable(thead->num_clocks, thead->clks);
+    ret = regulator_disable(thead->vbus);
+    if (ret) {
+                       dev_err(dev, "failed to disable regulator vbus %d\n", ret);
+    }
+    ret = regulator_disable(thead->hub1v2);
+    if (ret) {
+        dev_err(dev, "failed to disable regulator hub1v2 %d\n", ret);
+    }
+    ret = regulator_disable(thead->hub5v);
+
+    if (ret) {
+        dev_err(dev, "failed to disable regulator hub1v2 %d\n", ret);
+    }
+
+       return 0;
+}
+
+
+static int dwc3_thead_pm_resume(struct device *dev)
+{
+       struct dwc3_thead *thead = dev_get_drvdata(dev);
+       int ret;
+    dev_info(dev,"%s\n",__func__);
+    ret = regulator_enable(thead->vbus);
+    if (ret) {
+                       dev_err(dev, "failed to enable regulator vbus %d\n", ret);
+    }
+    ret = regulator_enable(thead->hub1v2);
+    if (ret) {
+        dev_err(dev, "failed to enable regulator hub1v2 %d\n", ret);
+    }
+    ret = regulator_enable(thead->hub5v);
+
+    if (ret) {
+        dev_err(dev, "failed to enable regulator hub1v2 %d\n", ret);
+    }
+
+       ret = clk_bulk_prepare_enable(thead->num_clocks, thead->clks);
+       if (ret) {
+               dev_err(dev, "failed to enable clk ret=%d\n", ret);
+               return ret;
+       }
+
+       dwc3_thead_deassert(thead);
+
+       return ret;
+}
+
+static const struct dev_pm_ops dwc3_thead_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(dwc3_thead_pm_suspend, dwc3_thead_pm_resume)
+};
+#define DEV_PM_OPS     (&dwc3_thead_dev_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct of_device_id dwc3_thead_of_match[] = {
+       { .compatible = "thead,dwc3" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, dwc3_thead_of_match);
+
+static struct platform_driver dwc3_thead_driver = {
+       .probe          = dwc3_thead_probe,
+       .remove         = dwc3_thead_remove,
+       .driver         = {
+               .name   = "dwc3-thead",
+               .pm     = DEV_PM_OPS,
+               .of_match_table = dwc3_thead_of_match,
+       },
+};
+
+module_platform_driver(dwc3_thead_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare DWC3 Thead Glue Driver");