2015-11-20 08:35:41

by Andy Gross

[permalink] [raw]
Subject: [PATCH 0/4] Add QCOM DWC3 Phy support

This set of patches adds support for the QCOM DWC3 phys found on various
Qualcomm platforms. The PHY portion of this set was originally part of:

https://lkml.org/lkml/2014/9/12/597

The applicable review comments were fixed and additional changes were made to
accomodate the TCSR phy mux selection required to get working ports.

Andy Gross (4):
phy: Add Qualcomm DWC3 HS/SS PHY driver
usb: dwc3: qcom: Configure TCSR phy mux register
ARM: dts: qcom: Add DWC3 USB support on IPQ8064
Documentation: usb: dwc3: qcom: Add TCSR mux usage

.../devicetree/bindings/usb/qcom,dwc3.txt | 11 +
arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 24 +
arch/arm/boot/dts/qcom-ipq8064.dtsi | 89 ++++
drivers/phy/Kconfig | 11 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-qcom-dwc3.c | 483 +++++++++++++++++++++
drivers/usb/dwc3/dwc3-qcom.c | 25 ++
7 files changed, 644 insertions(+)
create mode 100644 drivers/phy/phy-qcom-dwc3.c

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation


2015-11-20 08:37:28

by Andy Gross

[permalink] [raw]
Subject: [PATCH 1/4] phy: Add Qualcomm DWC3 HS/SS PHY driver

This patch adds a new driver for the Qualcomm USB 3.0 PHY that exists on some
Qualcomm platforms. This driver uses the generic PHY framework and will
interact with the DWC3 controller.

Signed-off-by: Andy Gross <[email protected]>
---
drivers/phy/Kconfig | 11 +
drivers/phy/Makefile | 1 +
drivers/phy/phy-qcom-dwc3.c | 483 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 495 insertions(+)
create mode 100644 drivers/phy/phy-qcom-dwc3.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 7eb5859d..f26bfc9 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -305,6 +305,17 @@ config PHY_QCOM_APQ8064_SATA
depends on OF
select GENERIC_PHY

+config PHY_QCOM_DWC3
+ tristate "Qualcomm DWC3 USB PHY support"
+ depends on ARCH_QCOM
+ depends on HAS_IOMEM
+ depends on OF
+ select GENERIC_PHY
+ help
+ This option enables support for the Synopsis PHYs present inside the
+ Qualcomm USB3.0 DWC3 controller. This driver supports both HS and SS
+ PHY controllers.
+
config PHY_QCOM_IPQ806X_SATA
tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
depends on ARCH_QCOM
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 075db1a..0610e0d 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
+obj-$(CONFIG_PHY_QCOM_DWC3) += phy-qcom-dwc3.o
diff --git a/drivers/phy/phy-qcom-dwc3.c b/drivers/phy/phy-qcom-dwc3.c
new file mode 100644
index 0000000..9398f6b
--- /dev/null
+++ b/drivers/phy/phy-qcom-dwc3.c
@@ -0,0 +1,483 @@
+/* Copyright (c) 2014-2015, Code Aurora Forum. 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/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+/**
+ * USB QSCRATCH Hardware registers
+ */
+#define QSCRATCH_GENERAL_CFG (0x08)
+#define HSUSB_PHY_CTRL_REG (0x10)
+
+/* PHY_CTRL_REG */
+#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24)
+#define HSUSB_CTRL_USB2_SUSPEND BIT(23)
+#define HSUSB_CTRL_UTMI_CLK_EN BIT(21)
+#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20)
+#define HSUSB_CTRL_USE_CLKCORE BIT(18)
+#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17)
+#define HSUSB_CTRL_COMMONONN BIT(11)
+#define HSUSB_CTRL_ID_HV_CLAMP BIT(9)
+#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8)
+#define HSUSB_CTRL_CLAMP_EN BIT(7)
+#define HSUSB_CTRL_RETENABLEN BIT(1)
+#define HSUSB_CTRL_POR BIT(0)
+
+/* QSCRATCH_GENERAL_CFG */
+#define HSUSB_GCFG_XHCI_REV BIT(2)
+
+/**
+ * USB QSCRATCH Hardware registers
+ */
+#define SSUSB_PHY_CTRL_REG (0x00)
+#define SSUSB_PHY_PARAM_CTRL_1 (0x04)
+#define SSUSB_PHY_PARAM_CTRL_2 (0x08)
+#define CR_PROTOCOL_DATA_IN_REG (0x0c)
+#define CR_PROTOCOL_DATA_OUT_REG (0x10)
+#define CR_PROTOCOL_CAP_ADDR_REG (0x14)
+#define CR_PROTOCOL_CAP_DATA_REG (0x18)
+#define CR_PROTOCOL_READ_REG (0x1c)
+#define CR_PROTOCOL_WRITE_REG (0x20)
+
+/* PHY_CTRL_REG */
+#define SSUSB_CTRL_REF_USE_PAD BIT(28)
+#define SSUSB_CTRL_TEST_POWERDOWN BIT(27)
+#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24)
+#define SSUSB_CTRL_SS_PHY_EN BIT(8)
+#define SSUSB_CTRL_SS_PHY_RESET BIT(7)
+
+/* SSPHY control registers */
+#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * lane)
+#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * lane)
+
+/* RX OVRD IN HI bits */
+#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13)
+#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12)
+#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11)
+#define RX_OVRD_IN_HI_RX_EQ_MASK 0x0700
+#define RX_OVRD_IN_HI_RX_EQ_SHIFT 8
+#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7)
+#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6)
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5)
+#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK 0x0018
+#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2)
+#define RX_OVRD_IN_HI_RX_RATE_MASK 0x0003
+
+/* TX OVRD DRV LO register bits */
+#define TX_OVRD_DRV_LO_AMPLITUDE_MASK 0x007F
+#define TX_OVRD_DRV_LO_PREEMPH_MASK 0x3F80
+#define TX_OVRD_DRV_LO_PREEMPH_SHIFT 7
+#define TX_OVRD_DRV_LO_EN BIT(14)
+
+/* SS CAP register bits */
+#define SS_CR_CAP_ADDR_REG BIT(0)
+#define SS_CR_CAP_DATA_REG BIT(0)
+#define SS_CR_READ_REG BIT(0)
+#define SS_CR_WRITE_REG BIT(0)
+
+struct qcom_dwc3_usb_phy {
+ void __iomem *base;
+ struct device *dev;
+ struct clk *xo_clk;
+ struct clk *ref_clk;
+};
+
+struct qcom_dwc3_phy_drvdata {
+ struct phy_ops ops;
+ u32 clk_rate;
+};
+
+/**
+ * Write register and read back masked value to confirm it is written
+ *
+ * @base - QCOM DWC3 PHY base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask specifying what should be updated
+ * @val - value to write.
+ */
+static inline void qcom_dwc3_phy_write_readback(
+ struct qcom_dwc3_usb_phy *phy_dwc3, u32 offset,
+ const u32 mask, u32 val)
+{
+ u32 write_val, tmp = readl(phy_dwc3->base + offset);
+
+ tmp &= ~mask; /* retain other bits */
+ write_val = tmp | val;
+
+ writel(write_val, phy_dwc3->base + offset);
+
+ /* Read back to see if val was written */
+ tmp = readl(phy_dwc3->base + offset);
+ tmp &= mask; /* clear other bits */
+
+ if (tmp != val)
+ dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n",
+ val, offset);
+}
+
+static int wait_for_latch(void __iomem *addr)
+{
+ u32 retry = 10;
+
+ while (true) {
+ if (!readl(addr))
+ break;
+
+ if (--retry == 0)
+ return -ETIMEDOUT;
+
+ usleep_range(10, 20);
+ }
+
+ return 0;
+}
+
+/**
+ * Write SSPHY register
+ *
+ * @base - QCOM DWC3 PHY base virtual address.
+ * @addr - SSPHY address to write.
+ * @val - value to write.
+ */
+static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3,
+ u32 addr, u32 val)
+{
+ int ret;
+
+ writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
+ writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
+ if (ret)
+ goto err_wait;
+
+ writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
+ writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
+ if (ret)
+ goto err_wait;
+
+ writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
+
+ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
+
+err_wait:
+ dev_err(phy_dwc3->dev, "timeout waiting for latch\n");
+ return ret;
+}
+
+/**
+ * Read SSPHY register.
+ *
+ * @base - QCOM DWC3 PHY base virtual address.
+ * @addr - SSPHY address to read.
+ */
+static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
+{
+ int ret;
+
+ writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
+ writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG);
+
+ ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
+ if (ret)
+ goto err_wait;
+
+ /*
+ * Due to hardware bug, first read of SSPHY register might be
+ * incorrect. Hence as workaround, SW should perform SSPHY register
+ * read twice, but use only second read and ignore first read.
+ */
+ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
+
+ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
+ if (ret)
+ goto err_wait;
+
+ /* throwaway read */
+ readl(base + CR_PROTOCOL_DATA_OUT_REG);
+
+ writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
+
+ ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
+ if (ret)
+ goto err_wait;
+
+ *val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
+
+err_wait:
+ return ret;
+}
+
+static int qcom_dwc3_phy_power_on(struct phy *phy)
+{
+ int ret;
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+
+ ret = clk_prepare_enable(phy_dwc3->xo_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(phy_dwc3->ref_clk);
+ if (ret)
+ clk_disable_unprepare(phy_dwc3->xo_clk);
+
+ return ret;
+}
+
+static int qcom_dwc3_phy_power_off(struct phy *phy)
+{
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+
+ clk_disable_unprepare(phy_dwc3->ref_clk);
+ clk_disable_unprepare(phy_dwc3->xo_clk);
+
+ return 0;
+}
+
+static int qcom_dwc3_hs_phy_init(struct phy *phy)
+{
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+ u32 val;
+
+ /*
+ * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel
+ * enable clamping, and disable RETENTION (power-on default is ENABLED)
+ */
+ val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP |
+ HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN |
+ HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP |
+ HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID |
+ HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70;
+
+ /* use core clock if external reference is not present */
+ if (!phy_dwc3->xo_clk)
+ val |= HSUSB_CTRL_USE_CLKCORE;
+
+ writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG);
+ usleep_range(2000, 2200);
+
+ /* Disable (bypass) VBUS and ID filters */
+ writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG);
+
+ return 0;
+}
+
+static int qcom_dwc3_ss_phy_init(struct phy *phy)
+{
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+ int ret;
+ u32 data = 0;
+
+ /* reset phy */
+ data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+ writel(data | SSUSB_CTRL_SS_PHY_RESET,
+ phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+ usleep_range(2000, 2200);
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+
+ /* clear REF_PAD if we don't have XO clk */
+ if (!phy_dwc3->xo_clk)
+ data &= ~SSUSB_CTRL_REF_USE_PAD;
+ else
+ data |= SSUSB_CTRL_REF_USE_PAD;
+
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+
+ /* wait for ref clk to become stable, this can take up to 30ms */
+ msleep(30);
+
+ data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
+ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
+
+ /*
+ * Fix RX Equalization setting as follows
+ * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
+ * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
+ * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
+ * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
+ */
+ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
+ SSPHY_CTRL_RX_OVRD_IN_HI(0), &data);
+ if (ret)
+ goto err_phy_trans;
+
+ data &= ~RX_OVRD_IN_HI_RX_EQ_EN;
+ data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD;
+ data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
+ data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
+ data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
+ SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
+ if (ret)
+ goto err_phy_trans;
+
+ /*
+ * Set EQ and TX launch amplitudes as follows
+ * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
+ * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
+ * LANE0.TX_OVRD_DRV_LO.EN set to 1.
+ */
+ ret = qcom_dwc3_ss_read_phycreg(phy_dwc3->base,
+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data);
+ if (ret)
+ goto err_phy_trans;
+
+ data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK;
+ data |= 0x16 << TX_OVRD_DRV_LO_PREEMPH_SHIFT;
+ data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
+ data |= 0x7f;
+ data |= TX_OVRD_DRV_LO_EN;
+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
+ SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
+ if (ret)
+ goto err_phy_trans;
+
+ /*
+ * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
+ * TX_FULL_SWING [26:20] amplitude to 127
+ * TX_DEEMPH_3_5DB [13:8] to 22
+ * LOS_BIAS [2:0] to 0x5
+ */
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1,
+ 0x07f03f07, 0x07f01605);
+
+err_phy_trans:
+ return ret;
+}
+
+static int qcom_dwc3_ss_phy_exit(struct phy *phy)
+{
+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
+
+ /* Sequence to put SSPHY in low power state:
+ * 1. Clear REF_PHY_EN in PHY_CTRL_REG
+ * 2. Clear REF_USE_PAD in PHY_CTRL_REG
+ * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
+ */
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
+ SSUSB_CTRL_SS_PHY_EN, 0x0);
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
+ SSUSB_CTRL_REF_USE_PAD, 0x0);
+ qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
+ 0x0, SSUSB_CTRL_TEST_POWERDOWN);
+
+ return 0;
+}
+
+static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = {
+ .ops = {
+ .init = qcom_dwc3_hs_phy_init,
+ .power_on = qcom_dwc3_phy_power_on,
+ .power_off = qcom_dwc3_phy_power_off,
+ .owner = THIS_MODULE,
+ },
+ .clk_rate = 60000000,
+};
+
+static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = {
+ .ops = {
+ .init = qcom_dwc3_ss_phy_init,
+ .exit = qcom_dwc3_ss_phy_exit,
+ .power_on = qcom_dwc3_phy_power_on,
+ .power_off = qcom_dwc3_phy_power_off,
+ .owner = THIS_MODULE,
+ },
+ .clk_rate = 125000000,
+};
+
+static const struct of_device_id qcom_dwc3_phy_table[] = {
+ { .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata },
+ { .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
+
+static int qcom_dwc3_phy_probe(struct platform_device *pdev)
+{
+ struct qcom_dwc3_usb_phy *phy_dwc3;
+ struct phy_provider *phy_provider;
+ struct phy *generic_phy;
+ struct resource *res;
+ const struct of_device_id *match;
+ const struct qcom_dwc3_phy_drvdata *data;
+
+ phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
+ if (!phy_dwc3)
+ return -ENOMEM;
+
+ match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node);
+ data = match->data;
+
+ phy_dwc3->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phy_dwc3->base = devm_ioremap_resource(phy_dwc3->dev, res);
+ if (IS_ERR(phy_dwc3->base))
+ return PTR_ERR(phy_dwc3->base);
+
+ phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref");
+ if (IS_ERR(phy_dwc3->ref_clk)) {
+ dev_dbg(phy_dwc3->dev, "cannot get reference clock\n");
+ return PTR_ERR(phy_dwc3->ref_clk);
+ }
+
+ clk_set_rate(phy_dwc3->ref_clk, data->clk_rate);
+
+ phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
+ if (IS_ERR(phy_dwc3->xo_clk)) {
+ dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n");
+ phy_dwc3->xo_clk = NULL;
+ }
+
+ generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node,
+ &data->ops);
+
+ if (IS_ERR(generic_phy))
+ return PTR_ERR(generic_phy);
+
+ phy_set_drvdata(generic_phy, phy_dwc3);
+ platform_set_drvdata(pdev, phy_dwc3);
+
+ phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
+ of_phy_simple_xlate);
+
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ return 0;
+}
+
+static struct platform_driver qcom_dwc3_phy_driver = {
+ .probe = qcom_dwc3_phy_probe,
+ .driver = {
+ .name = "qcom-dwc3-usb-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_dwc3_phy_table,
+ },
+};
+
+module_platform_driver(qcom_dwc3_phy_driver);
+
+MODULE_ALIAS("platform:phy-qcom-dwc3");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Andy Gross <[email protected]>");
+MODULE_AUTHOR("Ivan T. Ivanov <[email protected]>");
+MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver");
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

2015-11-20 08:37:26

by Andy Gross

[permalink] [raw]
Subject: [PATCH 2/4] usb: dwc3: qcom: Configure TCSR phy mux register

This patch adds automatic configuration of the TCSR phy mux register based on
the syscon-tcsr devicetree entry. This configuration is optional, as some
platforms may not require the mux selection.

Signed-off-by: Andy Gross <[email protected]>
---
drivers/usb/dwc3/dwc3-qcom.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index 0880260..fcf264c 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -17,6 +17,8 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>

struct dwc3_qcom {
struct device *dev;
@@ -30,6 +32,9 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct dwc3_qcom *qdwc;
+ struct regmap *regmap;
+ u32 mux_offset;
+ u32 mux_bit;
int ret;

qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL);
@@ -58,6 +63,26 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
qdwc->sleep_clk = NULL;
}

+ /* look for tcsr and if present, provision it */
+ regmap = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
+ if (!IS_ERR(regmap)) {
+ if (of_property_read_u32_index(node, "syscon-tcsr", 1,
+ &mux_offset)) {
+ dev_err(qdwc->dev, "missing USB TCSR mux offset\n");
+ return -EINVAL;
+ }
+ if (of_property_read_u32_index(node, "syscon-tcsr", 2,
+ &mux_bit)) {
+ dev_err(qdwc->dev, "missing USB TCSR mux bit\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, mux_offset, BIT(mux_bit),
+ BIT(mux_bit));
+ } else {
+ dev_info(qdwc->dev, "missing syscon tcsr entry\n");
+ }
+
ret = clk_prepare_enable(qdwc->core_clk);
if (ret) {
dev_err(qdwc->dev, "failed to enable core clock\n");
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

2015-11-20 08:36:38

by Andy Gross

[permalink] [raw]
Subject: [PATCH 3/4] ARM: dts: qcom: Add DWC3 USB support on IPQ8064

This patch adds Qualcomm DWC3 USB nodes to device tree to enable support for the
DWC3 controller found on IPQ8064/AP148 platforms.

Signed-off-by: Andy Gross <[email protected]>
---
arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 24 +++++++++
arch/arm/boot/dts/qcom-ipq8064.dtsi | 89 ++++++++++++++++++++++++++++++++
2 files changed, 113 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
index d501382..bf1638c 100644
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
@@ -97,5 +97,29 @@
sata@29000000 {
status = "ok";
};
+
+ phy@100f8800 {
+ status = "ok";
+ };
+
+ phy@100f8830 {
+ status = "ok";
+ };
+
+ usb30@0 {
+ status = "ok";
+ };
+
+ phy@110f8800 {
+ status = "ok";
+ };
+
+ phy@110f8830 {
+ status = "ok";
+ };
+
+ usb30@1 {
+ status = "ok";
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
index fa69863..b2dcd9d 100644
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
@@ -329,5 +329,94 @@
#reset-cells = <1>;
};

+ hs_phy_0: phy@100f8800 {
+ compatible = "qcom,dwc3-hs-usb-phy";
+ reg = <0x100f8800 0x30>;
+ clocks = <&gcc USB30_0_UTMI_CLK>;
+ clock-names = "ref";
+
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
+ ss_phy_0: phy@100f8830 {
+ compatible = "qcom,dwc3-ss-usb-phy";
+ reg = <0x100f8830 0x30>;
+
+ clocks = <&gcc USB30_0_MASTER_CLK>;
+ clock-names = "ref";
+
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
+ usb30@0 {
+ compatible = "qcom,dwc3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&gcc USB30_0_MASTER_CLK>;
+ clock-names = "core";
+
+ syscon-tcsr = <&tcsr 0xb0 1>;
+
+ ranges;
+
+ status = "disabled";
+
+ dwc3@10000000 {
+ compatible = "snps,dwc3";
+ reg = <0x10000000 0xcd00>;
+ interrupts = <0 205 0x4>;
+ phys = <&hs_phy_0>, <&ss_phy_0>;
+ phy-names = "usb2-phy", "usb3-phy";
+ tx-fifo-resize;
+ dr_mode = "host";
+ };
+ };
+
+ hs_phy_1: phy@110f8800 {
+ compatible = "qcom,dwc3-hs-usb-phy";
+ reg = <0x110f8800 0x30>;
+ clocks = <&gcc USB30_1_UTMI_CLK>;
+ clock-names = "ref";
+
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
+ ss_phy_1: phy@110f8830 {
+ compatible = "qcom,dwc3-ss-usb-phy";
+ reg = <0x110f8830 0x30>;
+
+ clocks = <&gcc USB30_1_MASTER_CLK>;
+ clock-names = "ref";
+
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
+ usb30@1 {
+ compatible = "qcom,dwc3";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&gcc USB30_1_MASTER_CLK>;
+ clock-names = "core";
+
+ syscon-tcsr = <&tcsr 0xb0 0>;
+
+ ranges;
+
+ status = "disabled";
+
+ dwc3@11000000 {
+ compatible = "snps,dwc3";
+ reg = <0x11000000 0xcd00>;
+ interrupts = <0 110 0x4>;
+ phys = <&hs_phy_1>, <&ss_phy_1>;
+ phy-names = "usb2-phy", "usb3-phy";
+ tx-fifo-resize;
+ dr_mode = "host";
+ };
+ };
};
};
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

2015-11-20 08:35:49

by Andy Gross

[permalink] [raw]
Subject: [PATCH 4/4] Documentation: usb: dwc3: qcom: Add TCSR mux usage

This patch adds documentation for the optional syscon-tcsr property in the
Qualcomm DWC3 node. The syscon-tcsr specifies the register and bit used to
configure the TCSR USB phy mux register.

Signed-off-by: Andy Gross <[email protected]>
---
Documentation/devicetree/bindings/usb/qcom,dwc3.txt | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
index ca164e7..dfa222d 100644
--- a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
@@ -8,6 +8,10 @@ Required properties:
"core" Master/Core clock, have to be >= 125 MHz for SS
operation and >= 60MHz for HS operation

+Optional properties:
+- syscon-tcsr Specifies TCSR handle, register offset, and bit position for
+ configuring the phy mux setting.
+
Optional clocks:
"iface" System bus AXI clock. Not present on all platforms
"sleep" Sleep clock, used when USB3 core goes into low
@@ -22,6 +26,11 @@ Documentation/devicetree/bindings/phy/qcom,dwc3-usb-phy.txt

Example device nodes:

+ tcsr: syscon@1a400000 {
+ compatible = "qcom,tcsr-ipq8064", "syscon";
+ reg = <0x1a400000 0x100>;
+ };
+
hs_phy: phy@100f8800 {
compatible = "qcom,dwc3-hs-usb-phy";
reg = <0x100f8800 0x30>;
@@ -51,6 +60,8 @@ Example device nodes:

ranges;

+ syscon-tcsr = <&tcsr 0xb0 0x1>;
+
status = "ok";

dwc3@10000000 {
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

2015-11-20 14:34:07

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH 4/4] Documentation: usb: dwc3: qcom: Add TCSR mux usage

On Fri, Nov 20, 2015 at 02:35:09AM -0600, Andy Gross wrote:
> This patch adds documentation for the optional syscon-tcsr property in the
> Qualcomm DWC3 node. The syscon-tcsr specifies the register and bit used to
> configure the TCSR USB phy mux register.
>
> Signed-off-by: Andy Gross <[email protected]>

Acked-by: Rob Herring <[email protected]>

> ---
> Documentation/devicetree/bindings/usb/qcom,dwc3.txt | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> index ca164e7..dfa222d 100644
> --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> @@ -8,6 +8,10 @@ Required properties:
> "core" Master/Core clock, have to be >= 125 MHz for SS
> operation and >= 60MHz for HS operation
>
> +Optional properties:
> +- syscon-tcsr Specifies TCSR handle, register offset, and bit position for
> + configuring the phy mux setting.
> +
> Optional clocks:
> "iface" System bus AXI clock. Not present on all platforms
> "sleep" Sleep clock, used when USB3 core goes into low
> @@ -22,6 +26,11 @@ Documentation/devicetree/bindings/phy/qcom,dwc3-usb-phy.txt
>
> Example device nodes:
>
> + tcsr: syscon@1a400000 {
> + compatible = "qcom,tcsr-ipq8064", "syscon";
> + reg = <0x1a400000 0x100>;
> + };
> +
> hs_phy: phy@100f8800 {
> compatible = "qcom,dwc3-hs-usb-phy";
> reg = <0x100f8800 0x30>;
> @@ -51,6 +60,8 @@ Example device nodes:
>
> ranges;
>
> + syscon-tcsr = <&tcsr 0xb0 0x1>;
> +
> status = "ok";
>
> dwc3@10000000 {
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> hosted by The Linux Foundation
>
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2015-11-20 15:07:31

by Felipe Balbi

[permalink] [raw]
Subject: Re: [PATCH 2/4] usb: dwc3: qcom: Configure TCSR phy mux register


Hi,

Andy Gross <[email protected]> writes:
> This patch adds automatic configuration of the TCSR phy mux register based on
> the syscon-tcsr devicetree entry. This configuration is optional, as some
> platforms may not require the mux selection.
>
> Signed-off-by: Andy Gross <[email protected]>

just when I find a way to make a generic dwc3-of-simple.c glue layer :-p

I can, certainly drop my patches but I need more details on the syscon
usage below.

> ---
> drivers/usb/dwc3/dwc3-qcom.c | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
> index 0880260..fcf264c 100644
> --- a/drivers/usb/dwc3/dwc3-qcom.c
> +++ b/drivers/usb/dwc3/dwc3-qcom.c
> @@ -17,6 +17,8 @@
> #include <linux/of.h>
> #include <linux/of_platform.h>
> #include <linux/platform_device.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
>
> struct dwc3_qcom {
> struct device *dev;
> @@ -30,6 +32,9 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
> {
> struct device_node *node = pdev->dev.of_node;
> struct dwc3_qcom *qdwc;
> + struct regmap *regmap;
> + u32 mux_offset;
> + u32 mux_bit;
> int ret;
>
> qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL);
> @@ -58,6 +63,26 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
> qdwc->sleep_clk = NULL;
> }
>
> + /* look for tcsr and if present, provision it */
> + regmap = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
> + if (!IS_ERR(regmap)) {
> + if (of_property_read_u32_index(node, "syscon-tcsr", 1,
> + &mux_offset)) {
> + dev_err(qdwc->dev, "missing USB TCSR mux offset\n");
> + return -EINVAL;
> + }
> + if (of_property_read_u32_index(node, "syscon-tcsr", 2,
> + &mux_bit)) {
> + dev_err(qdwc->dev, "missing USB TCSR mux bit\n");
> + return -EINVAL;
> + }
> +
> + regmap_update_bits(regmap, mux_offset, BIT(mux_bit),
> + BIT(mux_bit));

what is tcsr and what does it ? It also seems to be optional, why's that ?

--
balbi


Attachments:
signature.asc (818.00 B)

2015-11-20 15:09:19

by Felipe Balbi

[permalink] [raw]
Subject: Re: [PATCH 4/4] Documentation: usb: dwc3: qcom: Add TCSR mux usage


Hi,

Andy Gross <[email protected]> writes:
> This patch adds documentation for the optional syscon-tcsr property in the
> Qualcomm DWC3 node. The syscon-tcsr specifies the register and bit used to
> configure the TCSR USB phy mux register.
>
> Signed-off-by: Andy Gross <[email protected]>
> ---
> Documentation/devicetree/bindings/usb/qcom,dwc3.txt | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> index ca164e7..dfa222d 100644
> --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> @@ -8,6 +8,10 @@ Required properties:
> "core" Master/Core clock, have to be >= 125 MHz for SS
> operation and >= 60MHz for HS operation
>
> +Optional properties:
> +- syscon-tcsr Specifies TCSR handle, register offset, and bit position for
> + configuring the phy mux setting.

oh, it's a PHY mux ? I don't think it should be part of any dwc3-* glue
layer then. By the time we reach dwc3, the mux should be properly
configured.

Kishon, any ideas ?

--
balbi


Attachments:
signature.asc (818.00 B)

2015-11-20 15:54:17

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 2/4] usb: dwc3: qcom: Configure TCSR phy mux register

On Fri, Nov 20, 2015 at 09:06:33AM -0600, Felipe Balbi wrote:
>
> Hi,
>
> Andy Gross <[email protected]> writes:
> > This patch adds automatic configuration of the TCSR phy mux register based on
> > the syscon-tcsr devicetree entry. This configuration is optional, as some
> > platforms may not require the mux selection.
> >
> > Signed-off-by: Andy Gross <[email protected]>
>
> just when I find a way to make a generic dwc3-of-simple.c glue layer :-p
>
> I can, certainly drop my patches but I need more details on the syscon
> usage below.
>
> > ---
> > drivers/usb/dwc3/dwc3-qcom.c | 25 +++++++++++++++++++++++++
> > 1 file changed, 25 insertions(+)
> >
> > diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
> > index 0880260..fcf264c 100644
> > --- a/drivers/usb/dwc3/dwc3-qcom.c
> > +++ b/drivers/usb/dwc3/dwc3-qcom.c
> > @@ -17,6 +17,8 @@
> > #include <linux/of.h>
> > #include <linux/of_platform.h>
> > #include <linux/platform_device.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/regmap.h>
> >
> > struct dwc3_qcom {
> > struct device *dev;
> > @@ -30,6 +32,9 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
> > {
> > struct device_node *node = pdev->dev.of_node;
> > struct dwc3_qcom *qdwc;
> > + struct regmap *regmap;
> > + u32 mux_offset;
> > + u32 mux_bit;
> > int ret;
> >
> > qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL);
> > @@ -58,6 +63,26 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
> > qdwc->sleep_clk = NULL;
> > }
> >
> > + /* look for tcsr and if present, provision it */
> > + regmap = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
> > + if (!IS_ERR(regmap)) {
> > + if (of_property_read_u32_index(node, "syscon-tcsr", 1,
> > + &mux_offset)) {
> > + dev_err(qdwc->dev, "missing USB TCSR mux offset\n");
> > + return -EINVAL;
> > + }
> > + if (of_property_read_u32_index(node, "syscon-tcsr", 2,
> > + &mux_bit)) {
> > + dev_err(qdwc->dev, "missing USB TCSR mux bit\n");
> > + return -EINVAL;
> > + }
> > +
> > + regmap_update_bits(regmap, mux_offset, BIT(mux_bit),
> > + BIT(mux_bit));
>
> what is tcsr and what does it ? It also seems to be optional, why's that ?
>
> --
> balbi

The syscon is to set the mux selection for the phys. Our hardware has a
steering mux between hsic and dwc3 and setting this to 1 steers the phys to the
right controller.

It is optional because not all platforms appear to have this stupidity.


--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2015-11-20 15:56:20

by Andy Gross

[permalink] [raw]
Subject: Re: [PATCH 4/4] Documentation: usb: dwc3: qcom: Add TCSR mux usage

On Fri, Nov 20, 2015 at 09:08:46AM -0600, Felipe Balbi wrote:
>
> Hi,
>
> Andy Gross <[email protected]> writes:
> > This patch adds documentation for the optional syscon-tcsr property in the
> > Qualcomm DWC3 node. The syscon-tcsr specifies the register and bit used to
> > configure the TCSR USB phy mux register.
> >
> > Signed-off-by: Andy Gross <[email protected]>
> > ---
> > Documentation/devicetree/bindings/usb/qcom,dwc3.txt | 11 +++++++++++
> > 1 file changed, 11 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> > index ca164e7..dfa222d 100644
> > --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> > +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
> > @@ -8,6 +8,10 @@ Required properties:
> > "core" Master/Core clock, have to be >= 125 MHz for SS
> > operation and >= 60MHz for HS operation
> >
> > +Optional properties:
> > +- syscon-tcsr Specifies TCSR handle, register offset, and bit position for
> > + configuring the phy mux setting.
>
> oh, it's a PHY mux ? I don't think it should be part of any dwc3-* glue
> layer then. By the time we reach dwc3, the mux should be properly
> configured.
>
> Kishon, any ideas ?
>
> --
> balbi

The only issue with putting it at the phy layer is that i'd have redundant
syscon entries for each pair of phys, unless i group them somehow in dt. The
only other issue I can think of is that in the downstream kernels, they do this
before messing with the configuration of the dwc3. So long as the phys do their
thing before the dwc3 (phys latched before config), we're ok.



--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project