[RFC 2/2] usb: dwc3: Add Qualcomm DWC3 glue layer driver

From: Ivan T. Ivanov
Date: Tue Aug 06 2013 - 07:54:13 EST


From: "Ivan T. Ivanov" <iivanov@xxxxxxxxxx>

Signed-off-by: Ivan T. Ivanov <iivanov@xxxxxxxxxx>
---
.../devicetree/bindings/usb/msm-ssusb.txt | 39 +++++
drivers/usb/dwc3/Kconfig | 8 +
drivers/usb/dwc3/Makefile | 1 +
drivers/usb/dwc3/dwc3-msm.c | 175 ++++++++++++++++++++
4 files changed, 223 insertions(+)
create mode 100644 drivers/usb/dwc3/dwc3-msm.c

diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 550b496..313ae0d 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -22,6 +22,23 @@ Required "supply-name" examples are:
"v1p8" : 1.8v supply for SS-PHY
"vddcx" : vdd supply for SS-PHY digital circuit operation

+Required properties :
+- compatible : should be "qcom,dwc-usb3-msm"
+- reg : offset and length of the register set in the memory map
+ offset and length of the TCSR register for routing USB
+ signals to either picoPHY0 or picoPHY1.
+- clocks = <&usb30_master_cxc>, <&sys_noc_usb3_axi_cxc>, <&usb30_sleep_cxc>, <&usb30_mock_utmi_cxc>;
+- clock-names = "core_clk", "iface_clk", "sleep_clk", "utmi_clk";
+
+Optional properties :
+- gdsc-supply : phandle to the globally distributed switch controller
+ regulator node to the USB controller.
+
+Sub nodes:
+- Sub node for "DWC3- USB3 controller".
+ This sub node is required property for device node. The properties of this subnode
+ are specified in dwc3.txt.
+
Example device nodes:

dwc3_usb2: phy@f92f8800 {
@@ -47,3 +64,25 @@ Example device nodes:
vddcx-supply = <&supply>;
v1p8-supply = <&supply>;
};
+
+ usb@fd4ab000 {
+ compatible = "qcom,dwc-usb3-msm";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0xfd4ab000 0x4>;
+
+ clocks = <&usb30_master_cxc>, <&sys_noc_usb3_axi_cxc>, <&usb30_sleep_cxc>, <&usb30_mock_utmi_cxc>;
+ clock-names = "core_clk", "iface_clk", "sleep_clk", "utmi_clk";
+
+ gdsc-supply = <&supply>;
+ ranges;
+
+ dwc3@f9200000 {
+ compatible = "snps,dwc3";
+ reg = <0xf9200000 0xcd00>;
+ interrupts = <0 131 0>;
+ interrupt-names = "irq";
+ usb-phy = <&dwc3_usb2>, <&dwc3_usb3>;
+ tx-fifo-resize;
+ };
+ };
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 3e225d5..e2032c4 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -70,6 +70,14 @@ config USB_DWC3_PCI
One such PCIe-based platform is Synopsys' PCIe HAPS model of
this IP.

+config USB_DWC3_MSM
+ tristate "Qualcomm MSM/APQ Platforms"
+ default USB_DWC3
+ select USB_MSM_DWC3_PHYS
+ help
+ Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside,
+ say 'Y' or 'M' if you have one such device.
+
comment "Debugging features"

config USB_DWC3_DEBUG
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index dd17601..5226681 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -32,3 +32,4 @@ endif
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
+obj-$(CONFIG_USB_DWC3_MSM) += dwc3-msm.o
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
new file mode 100644
index 0000000..e509abc
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -0,0 +1,175 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#undef CONFIG_REGULATOR
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/phy.h>
+
+struct dwc3_msm {
+ struct device *dev;
+ void __iomem *base;
+
+ struct clk *core_clk;
+ struct clk *iface_clk;
+ struct clk *sleep_clk;
+ struct clk *utmi_clk;
+
+ struct regulator *gdsc;
+};
+
+static int dwc3_msm_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct dwc3_msm *mdwc;
+ struct resource *res;
+ void __iomem *tcsr;
+ int ret = 0;
+
+ dev_info(&pdev->dev, "MSM DWC3\n");
+
+ mdwc = devm_kzalloc(&pdev->dev, sizeof(*mdwc), GFP_KERNEL);
+ if (!mdwc) {
+ dev_err(&pdev->dev, "not enough memory\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, mdwc);
+ mdwc->dev = &pdev->dev;
+
+ mdwc->gdsc = devm_regulator_get(mdwc->dev, "gbsc");
+
+ mdwc->core_clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(mdwc->core_clk)) {
+ dev_err(&pdev->dev, "failed to get core_clk\n");
+ return PTR_ERR(mdwc->core_clk);
+ }
+
+ mdwc->iface_clk = devm_clk_get(&pdev->dev, "iface_clk");
+ if (IS_ERR(mdwc->iface_clk)) {
+ dev_err(&pdev->dev, "failed to get iface_clk\n");
+ return PTR_ERR(mdwc->iface_clk);
+ }
+
+ mdwc->sleep_clk = devm_clk_get(&pdev->dev, "sleep_clk");
+ if (IS_ERR(mdwc->sleep_clk)) {
+ dev_err(&pdev->dev, "failed to get sleep_clk\n");
+ return PTR_ERR(mdwc->sleep_clk);
+ }
+
+ mdwc->utmi_clk = devm_clk_get(&pdev->dev, "utmi_clk");
+ if (IS_ERR(mdwc->utmi_clk)) {
+ dev_err(&pdev->dev, "failed to get utmi_clk\n");
+ return PTR_ERR(mdwc->utmi_clk);
+ }
+
+ if (!IS_ERR(mdwc->gdsc)) {
+ ret = regulator_enable(mdwc->gdsc);
+ if (ret)
+ dev_err(mdwc->dev, "unable to enable usb3 gdsc\n");
+ }
+
+ /*
+ * DWC3 Core requires its CORE CLK (aka master / bus clk) to
+ * run at 125Mhz in SSUSB mode and >60MHZ for HSUSB mode.
+ */
+ clk_set_rate(mdwc->core_clk, 125000000);
+ clk_prepare_enable(mdwc->core_clk);
+ clk_prepare_enable(mdwc->iface_clk);
+ clk_prepare_enable(mdwc->sleep_clk);
+ clk_prepare_enable(mdwc->utmi_clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tcsr = devm_ioremap_resource(&pdev->dev, res);
+ if (!tcsr) {
+ dev_dbg(&pdev->dev, "tcsr ioremap failed\n");
+ } else {
+ /* TODO: This has to be revised */
+
+ /* Enable USB3 on the primary USB port. */
+ writel_relaxed(0x1, tcsr);
+ /*
+ * Ensure that TCSR write is completed before
+ * USB registers initialization.
+ */
+ mb();
+ }
+
+ ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add create dwc3 core\n");
+ goto dis_clks;
+ }
+
+ return 0;
+
+dis_clks:
+ clk_disable_unprepare(mdwc->utmi_clk);
+ clk_disable_unprepare(mdwc->sleep_clk);
+ clk_disable_unprepare(mdwc->iface_clk);
+ clk_disable_unprepare(mdwc->core_clk);
+ if (!IS_ERR(mdwc->gdsc)) {
+ ret = regulator_disable(mdwc->gdsc);
+ if (ret)
+ dev_err(mdwc->dev, "cannot disable usb3 gdsc\n");
+ }
+
+ return ret;
+}
+
+static int dwc3_msm_remove(struct platform_device *pdev)
+{
+ struct dwc3_msm *mdwc = platform_get_drvdata(pdev);
+ int ret;
+
+ clk_disable_unprepare(mdwc->utmi_clk);
+ clk_disable_unprepare(mdwc->sleep_clk);
+ clk_disable_unprepare(mdwc->iface_clk);
+ clk_disable_unprepare(mdwc->core_clk);
+
+ if (!IS_ERR(mdwc->gdsc)) {
+ ret = regulator_disable(mdwc->gdsc);
+ if (ret)
+ dev_err(mdwc->dev, "cannot disable usb3 gdsc\n");
+ }
+
+ return 0;
+}
+
+static const struct of_device_id of_dwc3_matach[] = {
+ {
+ .compatible = "qcom,dwc-usb3-msm",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_matach);
+
+static struct platform_driver dwc3_msm_driver = {
+ .probe = dwc3_msm_probe,
+ .remove = dwc3_msm_remove,
+ .driver = {
+ .name = "msm-dwc3",
+ .owner = THIS_MODULE,
+ .of_match_table = of_dwc3_matach,
+ },
+};
+
+module_platform_driver(dwc3_msm_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare USB3 MSM Glue Layer");
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/