diff options
Diffstat (limited to 'target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch')
-rw-r--r-- | target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch | 186 |
1 files changed, 94 insertions, 92 deletions
diff --git a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch index 4bba0ac..d554919 100644 --- a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch +++ b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch @@ -1,9 +1,9 @@ --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig -@@ -390,4 +390,15 @@ config PHY_CYGNUS_PCIE - Enable this to support the Broadcom Cygnus PCIe PHY. - If unsure, say N. - +@@ -390,4 +390,15 @@ + Enable this to support the Broadcom Cygnus PCIe PHY. + If unsure, say N. + +config PHY_QCOM_DWC3 + tristate "QCOM DWC3 USB PHY support" + depends on ARCH_QCOM @@ -18,25 +18,25 @@ endmenu --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile -@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1 +@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210) += 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 --- /dev/null +++ b/drivers/phy/phy-qcom-dwc3.c -@@ -0,0 +1,482 @@ -+/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved. +@@ -0,0 +1,484 @@ ++/* 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. -+ */ ++* 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> @@ -57,7 +57,7 @@ +#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_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) @@ -113,18 +113,24 @@ +#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 phy *phy; -+ -+ int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3); -+ int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3); -+ + 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 + * @@ -177,29 +183,32 @@ + * @addr - SSPHY address to write. + * @val - value to write. + */ -+static int qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val) ++static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3, ++ u32 addr, u32 val) +{ + int ret; + -+ writel(addr, base + CR_PROTOCOL_DATA_IN_REG); -+ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG); ++ 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(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, base + CR_PROTOCOL_DATA_IN_REG); -+ writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG); ++ 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(base + CR_PROTOCOL_CAP_DATA_REG); ++ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); + if (ret) + goto err_wait; + -+ writel(0x1, base + CR_PROTOCOL_WRITE_REG); ++ writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); + -+ ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG); ++ ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); + +err_wait: ++ if (ret) ++ dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); + return ret; +} + @@ -212,10 +221,9 @@ +static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val) +{ + int ret; -+ bool first_read = true; + + writel(addr, base + CR_PROTOCOL_DATA_IN_REG); -+ writel(0x1, base + CR_PROTOCOL_CAP_ADDR_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) @@ -226,18 +234,20 @@ + * incorrect. Hence as workaround, SW should perform SSPHY register + * read twice, but use only second read and ignore first read. + */ -+retry: -+ writel(0x1, base + CR_PROTOCOL_READ_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; + -+ if (first_read) { -+ readl(base + CR_PROTOCOL_DATA_OUT_REG); -+ first_read = false; -+ goto retry; -+ } ++ /* 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); + @@ -271,8 +281,9 @@ + return 0; +} + -+static int qcom_dwc3_hs_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3) ++static int qcom_dwc3_hs_phy_init(struct phy *phy) +{ ++ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); + u32 val; + + /* @@ -298,17 +309,18 @@ + return 0; +} + -+static int qcom_dwc3_ss_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3) ++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_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG); -+ writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET, ++ 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_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* clear REF_PAD if we don't have XO clk */ + if (!phy_dwc3->xo_clk) @@ -316,11 +328,13 @@ + else + data |= SSUSB_CTRL_REF_USE_PAD; + -+ writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ 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_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); ++ writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* + * Fix RX Equalization setting as follows @@ -339,7 +353,7 @@ + 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->base, ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, + SSPHY_CTRL_RX_OVRD_IN_HI(0), data); + if (ret) + goto err_phy_trans; @@ -360,7 +374,7 @@ + data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; + data |= 0x7f; + data |= TX_OVRD_DRV_LO_EN; -+ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base, ++ ret = qcom_dwc3_ss_write_phycreg(phy_dwc3, + SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); + if (ret) + goto err_phy_trans; @@ -378,13 +392,14 @@ + return ret; +} + -+static int qcom_dwc3_ss_phy_exit(struct qcom_dwc3_usb_phy *phy_dwc3) ++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 -+ * 4. Disable SSPHY ref clk + */ + qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_SS_PHY_EN, 0x0); @@ -396,37 +411,30 @@ + return 0; +} + -+static int qcom_dwc3_phy_init(struct phy *phy) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ -+ if (phy_dwc3->phy_init) -+ return phy_dwc3->phy_init(phy_dwc3); -+ -+ return 0; -+} -+ -+static int qcom_dwc3_phy_exit(struct phy *phy) -+{ -+ struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy); -+ -+ if (phy_dwc3->phy_exit) -+ return qcom_dwc3_ss_phy_exit(phy_dwc3); -+ -+ 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 struct phy_ops qcom_dwc3_phy_ops = { -+ .init = qcom_dwc3_phy_init, -+ .exit = qcom_dwc3_phy_exit, -+ .power_on = qcom_dwc3_phy_power_on, -+ .power_off = qcom_dwc3_phy_power_off, -+ .owner = THIS_MODULE, ++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", }, -+ { .compatible = "qcom,dwc3-ss-usb-phy", }, ++ { .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); @@ -435,13 +443,17 @@ +{ + 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; + -+ platform_set_drvdata(pdev, phy_dwc3); ++ match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node); ++ data = match->data; + + phy_dwc3->dev = &pdev->dev; + @@ -456,19 +468,7 @@ + return PTR_ERR(phy_dwc3->ref_clk); + } + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "qcom,dwc3-hs-usb-phy")) { -+ clk_set_rate(phy_dwc3->ref_clk, 60000000); -+ phy_dwc3->phy_init = qcom_dwc3_hs_phy_init; -+ } else if (of_device_is_compatible(pdev->dev.of_node, -+ "qcom,dwc3-ss-usb-phy")) { -+ phy_dwc3->phy_init = qcom_dwc3_ss_phy_init; -+ phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit; -+ clk_set_rate(phy_dwc3->ref_clk, 125000000); -+ } else { -+ dev_err(phy_dwc3->dev, "Unknown phy\n"); -+ return -EINVAL; -+ } ++ 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)) { @@ -476,12 +476,14 @@ + phy_dwc3->xo_clk = NULL; + } + -+ phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops); ++ generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, ++ &data->ops); + -+ if (IS_ERR(phy_dwc3->phy)) -+ return PTR_ERR(phy_dwc3->phy); ++ if (IS_ERR(generic_phy)) ++ return PTR_ERR(generic_phy); + -+ phy_set_drvdata(phy_dwc3->phy, phy_dwc3); ++ 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); |