1 From d968f8c82db73141c6bc145148642391cb698442 Mon Sep 17 00:00:00 2001
2 From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
3 Date: Mon, 7 Oct 2013 10:44:56 +0300
4 Subject: [PATCH 157/182] usb: phy: Add Qualcomm DWC3 HS/SS PHY drivers
6 These drivers handles control and configuration of the HS
7 and SS USB PHY transceivers. They are part of the driver
8 which manage Synopsys DesignWare USB3 controller stack
11 Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
13 drivers/usb/phy/Kconfig | 13 +-
14 drivers/usb/phy/Makefile | 2 +
15 drivers/usb/phy/phy-qcom-hsusb.c | 340 ++++++++++++++++++++++++++++
16 drivers/usb/phy/phy-qcom-ssusb.c | 455 ++++++++++++++++++++++++++++++++++++++
17 4 files changed, 809 insertions(+), 1 deletion(-)
18 create mode 100644 drivers/usb/phy/phy-qcom-hsusb.c
19 create mode 100644 drivers/usb/phy/phy-qcom-ssusb.c
21 --- a/drivers/usb/phy/Kconfig
22 +++ b/drivers/usb/phy/Kconfig
23 @@ -193,7 +193,7 @@ config USB_ISP1301
26 tristate "OTG support for Qualcomm on-chip USB controller"
27 - depends on (USB || USB_GADGET) && ARCH_MSM
28 + depends on (USB || USB_GADGET) && ARCH_QCOM
31 Enable this to support the USB OTG transceiver on MSM chips. It
32 @@ -251,6 +251,17 @@ config USB_RCAR_GEN2_PHY
33 To compile this driver as a module, choose M here: the
34 module will be called phy-rcar-gen2-usb.
36 +config USB_QCOM_DWC3_PHY
37 + tristate "Qualcomm USB controller DWC3 PHY wrappers support"
38 + depends on (USB || USB_GADGET) && ARCH_QCOM
41 + Enable this to support the DWC3 USB PHY transceivers on QCOM chips
42 + with DWC3 USB core. It handles PHY initialization, clock
43 + management required after resetting the hardware and power
44 + management. This driver is required even for peripheral only or
45 + host only mode configurations.
48 bool "Generic ULPI Transceiver Driver"
50 --- a/drivers/usb/phy/Makefile
51 +++ b/drivers/usb/phy/Makefile
52 @@ -26,6 +26,8 @@ obj-$(CONFIG_USB_EHCI_TEGRA) += phy-teg
53 obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o
54 obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o
55 obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
56 +obj-$(CONFIG_USB_QCOM_DWC3_PHY) += phy-qcom-hsusb.o
57 +obj-$(CONFIG_USB_QCOM_DWC3_PHY) += phy-qcom-ssusb.o
58 obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
59 obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
60 obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
62 +++ b/drivers/usb/phy/phy-qcom-hsusb.c
64 +/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
66 + * This program is free software; you can redistribute it and/or modify
67 + * it under the terms of the GNU General Public License version 2 and
68 + * only version 2 as published by the Free Software Foundation.
70 + * This program is distributed in the hope that it will be useful,
71 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
72 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
73 + * GNU General Public License for more details.
76 +#include <linux/clk.h>
77 +#include <linux/err.h>
78 +#include <linux/io.h>
79 +#include <linux/module.h>
80 +#include <linux/of.h>
81 +#include <linux/platform_device.h>
82 +#include <linux/regulator/consumer.h>
83 +#include <linux/usb/phy.h>
86 + * USB QSCRATCH Hardware registers
88 +#define QSCRATCH_CTRL_REG (0x04)
89 +#define QSCRATCH_GENERAL_CFG (0x08)
90 +#define PHY_CTRL_REG (0x10)
91 +#define PARAMETER_OVERRIDE_X_REG (0x14)
92 +#define CHARGING_DET_CTRL_REG (0x18)
93 +#define CHARGING_DET_OUTPUT_REG (0x1c)
94 +#define ALT_INTERRUPT_EN_REG (0x20)
95 +#define PHY_IRQ_STAT_REG (0x24)
96 +#define CGCTL_REG (0x28)
98 +#define PHY_3P3_VOL_MIN 3050000 /* uV */
99 +#define PHY_3P3_VOL_MAX 3300000 /* uV */
100 +#define PHY_3P3_HPM_LOAD 16000 /* uA */
102 +#define PHY_1P8_VOL_MIN 1800000 /* uV */
103 +#define PHY_1P8_VOL_MAX 1800000 /* uV */
104 +#define PHY_1P8_HPM_LOAD 19000 /* uA */
106 +/* TODO: these are suspicious */
107 +#define USB_VDDCX_NO 1 /* index */
108 +#define USB_VDDCX_MIN 5 /* index */
109 +#define USB_VDDCX_MAX 7 /* index */
111 +struct qcom_dwc3_hs_phy {
112 + struct usb_phy phy;
113 + void __iomem *base;
114 + struct device *dev;
116 + struct clk *xo_clk;
117 + struct clk *utmi_clk;
119 + struct regulator *v3p3;
120 + struct regulator *v1p8;
121 + struct regulator *vddcx;
122 + struct regulator *vbus;
125 +#define phy_to_dw_phy(x) container_of((x), struct qcom_dwc3_hs_phy, phy)
131 + * @base - QCOM DWC3 PHY base virtual address.
132 + * @offset - register offset.
133 + * @val - value to write.
135 +static inline void qcom_dwc3_hs_write(void __iomem *base, u32 offset, u32 val)
137 + writel(val, base + offset);
141 + * Write register and read back masked value to confirm it is written
143 + * @base - QCOM DWC3 PHY base virtual address.
144 + * @offset - register offset.
145 + * @mask - register bitmask specifying what should be updated
146 + * @val - value to write.
148 +static inline void qcom_dwc3_hs_write_readback(void __iomem *base, u32 offset,
149 + const u32 mask, u32 val)
151 + u32 write_val, tmp = readl(base + offset);
153 + tmp &= ~mask; /* retain other bits */
154 + write_val = tmp | val;
156 + writel(write_val, base + offset);
158 + /* Read back to see if val was written */
159 + tmp = readl(base + offset);
160 + tmp &= mask; /* clear other bits */
163 + pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset);
166 +static int qcom_dwc3_hs_notify_connect(struct usb_phy *x,
167 + enum usb_device_speed speed)
169 + struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
171 + dev_err(phy->dev, "notify connect\n");
175 +static int qcom_dwc3_hs_notify_disconnect(struct usb_phy *x,
176 + enum usb_device_speed speed)
178 + struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
180 + dev_err(phy->dev, "notify disconnect\n");
185 +static void qcom_dwc3_hs_phy_shutdown(struct usb_phy *x)
187 + struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
190 + ret = regulator_set_voltage(phy->v3p3, 0, PHY_3P3_VOL_MAX);
192 + dev_err(phy->dev, "cannot set voltage for v3p3\n");
194 + ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX);
196 + dev_err(phy->dev, "cannot set voltage for v1p8\n");
198 + ret = regulator_disable(phy->v1p8);
200 + dev_err(phy->dev, "cannot disable v1p8\n");
202 + ret = regulator_disable(phy->v3p3);
204 + dev_err(phy->dev, "cannot disable v3p3\n");
206 + ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX);
208 + dev_err(phy->dev, "cannot set voltage for vddcx\n");
210 + ret = regulator_disable(phy->vddcx);
212 + dev_err(phy->dev, "cannot enable vddcx\n");
214 + clk_disable_unprepare(phy->utmi_clk);
217 +static int qcom_dwc3_hs_phy_init(struct usb_phy *x)
219 + struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
223 + clk_prepare_enable(phy->utmi_clk);
225 + ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX);
227 + dev_err(phy->dev, "cannot set voltage for vddcx\n");
229 + ret = regulator_enable(phy->vddcx);
231 + dev_err(phy->dev, "cannot enable vddcx\n");
233 + ret = regulator_set_voltage(phy->v3p3, PHY_3P3_VOL_MIN,
236 + dev_err(phy->dev, "cannot set voltage for v3p3\n");
238 + ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN,
241 + dev_err(phy->dev, "cannot set voltage for v1p8\n");
243 + ret = regulator_set_optimum_mode(phy->v1p8, PHY_1P8_HPM_LOAD);
245 + dev_err(phy->dev, "cannot set HPM of regulator v1p8\n");
247 + ret = regulator_enable(phy->v1p8);
249 + dev_err(phy->dev, "cannot enable v1p8\n");
251 + ret = regulator_set_optimum_mode(phy->v3p3, PHY_3P3_HPM_LOAD);
253 + dev_err(phy->dev, "cannot set HPM of regulator v3p3\n");
255 + ret = regulator_enable(phy->v3p3);
257 + dev_err(phy->dev, "cannot enable v3p3\n");
260 + * HSPHY Initialization: Enable UTMI clock and clamp enable HVINTs,
261 + * and disable RETENTION (power-on default is ENABLED)
263 + val = readl(phy->base + PHY_CTRL_REG);
264 + val |= BIT(18) | BIT(20) | BIT(11) | 0x70;
265 + qcom_dwc3_hs_write(phy->base, PHY_CTRL_REG, val);
266 + usleep_range(2000, 2200);
269 + * write HSPHY init value to QSCRATCH reg to set HSPHY parameters like
270 + * VBUS valid threshold, disconnect valid threshold, DC voltage level,
271 + * preempasis and rise/fall time.
273 + qcom_dwc3_hs_write_readback(phy->base, PARAMETER_OVERRIDE_X_REG,
274 + 0x03ffffff, 0x00d191a4);
276 + /* Disable (bypass) VBUS and ID filters */
277 + qcom_dwc3_hs_write(phy->base, QSCRATCH_GENERAL_CFG, BIT(2));
282 +static int qcom_dwc3_hs_phy_set_vbus(struct usb_phy *x, int on)
284 + struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
287 + if (IS_ERR(phy->vbus))
288 + return on ? PTR_ERR(phy->vbus) : 0;
291 + ret = regulator_enable(phy->vbus);
293 + ret = regulator_disable(phy->vbus);
296 + dev_err(x->dev, "Cannot %s Vbus\n", on ? "set" : "off");
300 +static int qcom_dwc3_hs_probe(struct platform_device *pdev)
302 + struct qcom_dwc3_hs_phy *phy;
303 + struct resource *res;
304 + void __iomem *base;
306 + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
310 + platform_set_drvdata(pdev, phy);
312 + phy->dev = &pdev->dev;
314 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
315 + base = devm_ioremap_resource(phy->dev, res);
317 + return PTR_ERR(base);
319 + phy->vddcx = devm_regulator_get(phy->dev, "vddcx");
320 + if (IS_ERR(phy->vddcx)) {
321 + dev_dbg(phy->dev, "cannot get vddcx\n");
322 + return PTR_ERR(phy->vddcx);
325 + phy->v3p3 = devm_regulator_get(phy->dev, "v3p3");
326 + if (IS_ERR(phy->v3p3)) {
327 + dev_dbg(phy->dev, "cannot get v3p3\n");
328 + return PTR_ERR(phy->v3p3);
331 + phy->v1p8 = devm_regulator_get(phy->dev, "v1p8");
332 + if (IS_ERR(phy->v1p8)) {
333 + dev_dbg(phy->dev, "cannot get v1p8\n");
334 + return PTR_ERR(phy->v1p8);
337 + phy->vbus = devm_regulator_get(phy->dev, "vbus");
338 + if (IS_ERR(phy->vbus))
339 + dev_dbg(phy->dev, "Failed to get vbus\n");
341 + phy->utmi_clk = devm_clk_get(phy->dev, "utmi");
342 + if (IS_ERR(phy->utmi_clk)) {
343 + dev_dbg(phy->dev, "cannot get UTMI handle\n");
344 + return PTR_ERR(phy->utmi_clk);
347 + phy->xo_clk = devm_clk_get(phy->dev, "xo");
348 + if (IS_ERR(phy->xo_clk)) {
349 + dev_dbg(phy->dev, "cannot get TCXO buffer handle\n");
350 + phy->xo_clk = NULL;
353 + clk_set_rate(phy->utmi_clk, 60000000);
356 + clk_prepare_enable(phy->xo_clk);
359 + phy->phy.dev = phy->dev;
360 + phy->phy.label = "qcom-dwc3-hsphy";
361 + phy->phy.init = qcom_dwc3_hs_phy_init;
362 + phy->phy.notify_connect = qcom_dwc3_hs_notify_connect;
363 + phy->phy.notify_disconnect = qcom_dwc3_hs_notify_disconnect;
364 + phy->phy.shutdown = qcom_dwc3_hs_phy_shutdown;
365 + phy->phy.set_vbus = qcom_dwc3_hs_phy_set_vbus;
366 + phy->phy.type = USB_PHY_TYPE_USB2;
367 + phy->phy.state = OTG_STATE_UNDEFINED;
369 + usb_add_phy_dev(&phy->phy);
373 +static int qcom_dwc3_hs_remove(struct platform_device *pdev)
375 + struct qcom_dwc3_hs_phy *phy = platform_get_drvdata(pdev);
378 + clk_disable_unprepare(phy->xo_clk);
379 + usb_remove_phy(&phy->phy);
383 +static const struct of_device_id qcom_dwc3_hs_id_table[] = {
384 + { .compatible = "qcom,dwc3-hsphy" },
387 +MODULE_DEVICE_TABLE(of, qcom_dwc3_hs_id_table);
389 +static struct platform_driver qcom_dwc3_hs_driver = {
390 + .probe = qcom_dwc3_hs_probe,
391 + .remove = qcom_dwc3_hs_remove,
393 + .name = "qcom-dwc3-hsphy",
394 + .owner = THIS_MODULE,
395 + .of_match_table = qcom_dwc3_hs_id_table,
399 +module_platform_driver(qcom_dwc3_hs_driver);
401 +MODULE_ALIAS("platform:qcom-dwc3-hsphy");
402 +MODULE_LICENSE("GPL v2");
403 +MODULE_DESCRIPTION("DesignWare USB3 QCOM HSPHY driver");
405 +++ b/drivers/usb/phy/phy-qcom-ssusb.c
407 +/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
409 + * This program is free software; you can redistribute it and/or modify
410 + * it under the terms of the GNU General Public License version 2 and
411 + * only version 2 as published by the Free Software Foundation.
413 + * This program is distributed in the hope that it will be useful,
414 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
415 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
416 + * GNU General Public License for more details.
419 +#include <linux/clk.h>
420 +#include <linux/err.h>
421 +#include <linux/io.h>
422 +#include <linux/module.h>
423 +#include <linux/of.h>
424 +#include <linux/platform_device.h>
425 +#include <linux/regulator/consumer.h>
426 +#include <linux/usb/phy.h>
429 + * USB QSCRATCH Hardware registers
431 +#define PHY_CTRL_REG (0x00)
432 +#define PHY_PARAM_CTRL_1 (0x04)
433 +#define PHY_PARAM_CTRL_2 (0x08)
434 +#define CR_PROTOCOL_DATA_IN_REG (0x0c)
435 +#define CR_PROTOCOL_DATA_OUT_REG (0x10)
436 +#define CR_PROTOCOL_CAP_ADDR_REG (0x14)
437 +#define CR_PROTOCOL_CAP_DATA_REG (0x18)
438 +#define CR_PROTOCOL_READ_REG (0x1c)
439 +#define CR_PROTOCOL_WRITE_REG (0x20)
441 +#define PHY_1P8_VOL_MIN 1800000 /* uV */
442 +#define PHY_1P8_VOL_MAX 1800000 /* uV */
443 +#define PHY_1P8_HPM_LOAD 23000 /* uA */
445 +/* TODO: these are suspicious */
446 +#define USB_VDDCX_NO 1 /* index */
447 +#define USB_VDDCX_MIN 5 /* index */
448 +#define USB_VDDCX_MAX 7 /* index */
450 +struct qcom_dwc3_ss_phy {
451 + struct usb_phy phy;
452 + void __iomem *base;
453 + struct device *dev;
455 + struct regulator *v1p8;
456 + struct regulator *vddcx;
458 + struct clk *xo_clk;
459 + struct clk *ref_clk;
462 +#define phy_to_dw_phy(x) container_of((x), struct qcom_dwc3_ss_phy, phy)
468 + * @base - QCOM DWC3 PHY base virtual address.
469 + * @offset - register offset.
470 + * @val - value to write.
472 +static inline void qcom_dwc3_ss_write(void __iomem *base, u32 offset, u32 val)
474 + writel(val, base + offset);
478 + * Write register and read back masked value to confirm it is written
480 + * @base - QCOM DWC3 PHY base virtual address.
481 + * @offset - register offset.
482 + * @mask - register bitmask specifying what should be updated
483 + * @val - value to write.
485 +static inline void qcom_dwc3_ss_write_readback(void __iomem *base, u32 offset,
486 + const u32 mask, u32 val)
488 + u32 write_val, tmp = readl(base + offset);
490 + tmp &= ~mask; /* retain other bits */
491 + write_val = tmp | val;
493 + writel(write_val, base + offset);
495 + /* Read back to see if val was written */
496 + tmp = readl(base + offset);
497 + tmp &= mask; /* clear other bits */
500 + pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset);
504 + * Write SSPHY register
506 + * @base - QCOM DWC3 PHY base virtual address.
507 + * @addr - SSPHY address to write.
508 + * @val - value to write.
510 +static void qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val)
512 + writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
513 + writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
514 + while (readl(base + CR_PROTOCOL_CAP_ADDR_REG))
517 + writel(val, base + CR_PROTOCOL_DATA_IN_REG);
518 + writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
519 + while (readl(base + CR_PROTOCOL_CAP_DATA_REG))
522 + writel(0x1, base + CR_PROTOCOL_WRITE_REG);
523 + while (readl(base + CR_PROTOCOL_WRITE_REG))
528 + * Read SSPHY register.
530 + * @base - QCOM DWC3 PHY base virtual address.
531 + * @addr - SSPHY address to read.
533 +static u32 qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr)
535 + bool first_read = true;
537 + writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
538 + writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
539 + while (readl(base + CR_PROTOCOL_CAP_ADDR_REG))
543 + * Due to hardware bug, first read of SSPHY register might be
544 + * incorrect. Hence as workaround, SW should perform SSPHY register
545 + * read twice, but use only second read and ignore first read.
548 + writel(0x1, base + CR_PROTOCOL_READ_REG);
549 + while (readl(base + CR_PROTOCOL_READ_REG))
553 + readl(base + CR_PROTOCOL_DATA_OUT_REG);
554 + first_read = false;
558 + return readl(base + CR_PROTOCOL_DATA_OUT_REG);
561 +static void qcom_dwc3_ss_phy_shutdown(struct usb_phy *x)
563 + struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
566 + /* Sequence to put SSPHY in low power state:
567 + * 1. Clear REF_PHY_EN in PHY_CTRL_REG
568 + * 2. Clear REF_USE_PAD in PHY_CTRL_REG
569 + * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
570 + * 4. Disable SSPHY ref clk
572 + qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 8), 0x0);
573 + qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 28), 0x0);
574 + qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG,
575 + (1 << 26), (1 << 26));
577 + usleep_range(1000, 1200);
578 + clk_disable_unprepare(phy->ref_clk);
580 + ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX);
582 + dev_err(phy->dev, "cannot set voltage for vddcx\n");
584 + regulator_disable(phy->vddcx);
586 + ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX);
588 + dev_err(phy->dev, "cannot set v1p8\n");
590 + regulator_disable(phy->v1p8);
593 +static int qcom_dwc3_ss_phy_init(struct usb_phy *x)
595 + struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
599 + ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX);
601 + dev_err(phy->dev, "cannot set voltage for vddcx\n");
605 + ret = regulator_enable(phy->vddcx);
607 + dev_err(phy->dev, "cannot enable vddcx\n");
611 + ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN,
614 + regulator_disable(phy->vddcx);
615 + dev_err(phy->dev, "cannot set v1p8\n");
619 + ret = regulator_enable(phy->v1p8);
621 + regulator_disable(phy->vddcx);
622 + dev_err(phy->dev, "cannot enable v1p8\n");
626 + clk_prepare_enable(phy->ref_clk);
627 + usleep_range(1000, 1200);
630 + data = readl_relaxed(phy->base + PHY_CTRL_REG);
631 + writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG);
632 + usleep_range(2000, 2200);
633 + writel_relaxed(data, phy->base + PHY_CTRL_REG);
635 + /* clear REF_PAD, we don't have XO clk */
637 + writel_relaxed(data, phy->base + PHY_CTRL_REG);
640 + data |= BIT(8) | BIT(24);
641 + writel_relaxed(data, phy->base + PHY_CTRL_REG);
644 + * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates
645 + * in HS mode instead of SS mode. Workaround it by asserting
646 + * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode
648 + data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d);
650 + qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data);
652 + data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010);
655 + qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data);
658 + * Fix RX Equalization setting as follows
659 + * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
660 + * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
661 + * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
662 + * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
664 + data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006);
667 + data &= ~(0x7 << 8);
668 + data |= (0x3 << 8);
669 + data |= (0x1 << 11);
670 + qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data);
673 + * Set EQ and TX launch amplitudes as follows
674 + * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
675 + * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
676 + * LANE0.TX_OVRD_DRV_LO.EN set to 1.
678 + data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002);
680 + data |= (0x16 << 7);
682 + data |= (0x7f | (1 << 14));
683 + qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data);
686 + * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
687 + * TX_FULL_SWING [26:20] amplitude to 127
688 + * TX_DEEMPH_3_5DB [13:8] to 22
689 + * LOS_BIAS [2:0] to 0x5
691 + qcom_dwc3_ss_write_readback(phy->base, PHY_PARAM_CTRL_1,
692 + 0x07f03f07, 0x07f01605);
696 +static int qcom_dwc3_ss_set_suspend(struct usb_phy *x, int suspend)
698 + struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
703 + data = readl_relaxed(phy->base + PHY_CTRL_REG);
704 + writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG);
705 + usleep_range(2000, 2200);
706 + writel_relaxed(data, phy->base + PHY_CTRL_REG);
708 + /* clear REF_PAD, we don't have XO clk */
710 + writel_relaxed(data, phy->base + PHY_CTRL_REG);
713 + data |= BIT(8) | BIT(24);
714 + writel_relaxed(data, phy->base + PHY_CTRL_REG);
717 + * WORKAROUND: There is SSPHY suspend bug due to which USB
718 + * enumerates in HS mode instead of SS mode. Workaround it by
719 + * asserting LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use
722 + data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d);
724 + qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data);
726 + data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010);
729 + qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data);
732 + * Fix RX Equalization setting as follows
733 + * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
734 + * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
735 + * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
736 + * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
738 + data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006);
741 + data &= ~(0x7 << 8);
742 + data |= (0x3 << 8);
743 + data |= (0x1 << 11);
744 + qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data);
747 + * Set EQ and TX launch amplitudes as follows
748 + * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
749 + * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
750 + * LANE0.TX_OVRD_DRV_LO.EN set to 1.
752 + data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002);
754 + data |= (0x16 << 7);
756 + data |= (0x7f | (1 << 14));
757 + qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data);
760 + * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
761 + * TX_FULL_SWING [26:20] amplitude to 127
762 + * TX_DEEMPH_3_5DB [13:8] to 22
763 + * LOS_BIAS [2:0] to 0x5
765 + qcom_dwc3_ss_write_readback(phy->base, PHY_PARAM_CTRL_1,
766 + 0x07f03f07, 0x07f01605);
771 +static int qcom_dwc3_ss_probe(struct platform_device *pdev)
773 + struct qcom_dwc3_ss_phy *phy;
774 + struct resource *res;
775 + void __iomem *base;
778 + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
782 + platform_set_drvdata(pdev, phy);
784 + phy->dev = &pdev->dev;
786 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
787 + base = devm_ioremap_resource(phy->dev, res);
789 + return PTR_ERR(base);
791 + phy->vddcx = devm_regulator_get(phy->dev, "vddcx");
792 + if (IS_ERR(phy->vddcx)) {
793 + dev_dbg(phy->dev, "cannot get vddcx\n");
794 + return PTR_ERR(phy->vddcx);
797 + phy->v1p8 = devm_regulator_get(phy->dev, "v1p8");
798 + if (IS_ERR(phy->v1p8)) {
799 + dev_dbg(phy->dev, "cannot get v1p8\n");
800 + return PTR_ERR(phy->v1p8);
803 + phy->xo_clk = devm_clk_get(phy->dev, "xo");
804 + if (IS_ERR(phy->xo_clk)) {
805 + dev_dbg(phy->dev, "cannot get XO clk, assuming not present\n");
806 + phy->xo_clk = NULL;
809 + phy->ref_clk = devm_clk_get(phy->dev, "ref");
810 + if (IS_ERR(phy->ref_clk)) {
811 + dev_dbg(phy->dev, "cannot get ref clock handle\n");
812 + return PTR_ERR(phy->ref_clk);
815 + clk_set_rate(phy->ref_clk, 125000000);
817 + clk_prepare_enable(phy->xo_clk);
820 + phy->phy.dev = phy->dev;
821 + phy->phy.label = "qcom-dwc3-ssphy";
822 + phy->phy.init = qcom_dwc3_ss_phy_init;
823 + phy->phy.shutdown = qcom_dwc3_ss_phy_shutdown;
824 + phy->phy.set_suspend = qcom_dwc3_ss_set_suspend;
825 + phy->phy.type = USB_PHY_TYPE_USB3;
827 + ret = usb_add_phy_dev(&phy->phy);
831 +static int qcom_dwc3_ss_remove(struct platform_device *pdev)
833 + struct qcom_dwc3_ss_phy *phy = platform_get_drvdata(pdev);
836 + clk_disable_unprepare(phy->xo_clk);
837 + usb_remove_phy(&phy->phy);
841 +static const struct of_device_id qcom_dwc3_ss_id_table[] = {
842 + { .compatible = "qcom,dwc3-ssphy" },
845 +MODULE_DEVICE_TABLE(of, qcom_dwc3_ss_id_table);
847 +static struct platform_driver qcom_dwc3_ss_driver = {
848 + .probe = qcom_dwc3_ss_probe,
849 + .remove = qcom_dwc3_ss_remove,
851 + .name = "qcom-dwc3-ssphy",
852 + .owner = THIS_MODULE,
853 + .of_match_table = qcom_dwc3_ss_id_table,
857 +module_platform_driver(qcom_dwc3_ss_driver);
859 +MODULE_ALIAS("platform:qcom-dwc3-ssphy");
860 +MODULE_LICENSE("GPL v2");
861 +MODULE_DESCRIPTION("DesignWare USB3 QCOM SSPHY driver");