@@ -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
@@ -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
new file mode 100644
@@ -0,0 +1,174 @@
+/* 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.
+ *
+ */
+
+#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, "gdsc");
+
+ 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,dwc3",
+ },
+ { },
+};
+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");