332a32c9109fdb36075a176af086d0220d57c51f
[15.05/openwrt.git] / target / linux / ipq806x / patches / 0157-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
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
5
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
9 inside Qualcomm SoC's.
10
11 Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
12 ---
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
20
21 diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
22 index 7d1451d..ddb65be 100644
23 --- a/drivers/usb/phy/Kconfig
24 +++ b/drivers/usb/phy/Kconfig
25 @@ -193,7 +193,7 @@ config USB_ISP1301
26  
27  config USB_MSM_OTG
28         tristate "OTG support for Qualcomm on-chip USB controller"
29 -       depends on (USB || USB_GADGET) && ARCH_MSM
30 +       depends on (USB || USB_GADGET) && ARCH_QCOM
31         select USB_PHY
32         help
33           Enable this to support the USB OTG transceiver on MSM chips. It
34 @@ -251,6 +251,17 @@ config USB_RCAR_GEN2_PHY
35           To compile this driver as a module, choose M here: the
36           module will be called phy-rcar-gen2-usb.
37  
38 +config USB_QCOM_DWC3_PHY
39 +       tristate "Qualcomm USB controller DWC3 PHY wrappers support"
40 +       depends on (USB || USB_GADGET) && ARCH_QCOM
41 +       select USB_PHY
42 +       help
43 +         Enable this to support the DWC3 USB PHY transceivers on QCOM chips
44 +         with DWC3 USB core. It handles PHY initialization, clock
45 +         management required after resetting the hardware and power
46 +         management. This driver is required even for peripheral only or
47 +         host only mode configurations.
48 +
49  config USB_ULPI
50         bool "Generic ULPI Transceiver Driver"
51         depends on ARM
52 diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
53 index be58ada..857f04e 100644
54 --- a/drivers/usb/phy/Makefile
55 +++ b/drivers/usb/phy/Makefile
56 @@ -26,6 +26,8 @@ obj-$(CONFIG_USB_EHCI_TEGRA)          += phy-tegra-usb.o
57  obj-$(CONFIG_USB_GPIO_VBUS)            += phy-gpio-vbus-usb.o
58  obj-$(CONFIG_USB_ISP1301)              += phy-isp1301.o
59  obj-$(CONFIG_USB_MSM_OTG)              += phy-msm-usb.o
60 +obj-$(CONFIG_USB_QCOM_DWC3_PHY)                += phy-qcom-hsusb.o
61 +obj-$(CONFIG_USB_QCOM_DWC3_PHY)                += phy-qcom-ssusb.o
62  obj-$(CONFIG_USB_MV_OTG)               += phy-mv-usb.o
63  obj-$(CONFIG_USB_MXS_PHY)              += phy-mxs-usb.o
64  obj-$(CONFIG_USB_RCAR_PHY)             += phy-rcar-usb.o
65 diff --git a/drivers/usb/phy/phy-qcom-hsusb.c b/drivers/usb/phy/phy-qcom-hsusb.c
66 new file mode 100644
67 index 0000000..f96b2b9
68 --- /dev/null
69 +++ b/drivers/usb/phy/phy-qcom-hsusb.c
70 @@ -0,0 +1,340 @@
71 +/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
72 + *
73 + * This program is free software; you can redistribute it and/or modify
74 + * it under the terms of the GNU General Public License version 2 and
75 + * only version 2 as published by the Free Software Foundation.
76 + *
77 + * This program is distributed in the hope that it will be useful,
78 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
79 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
80 + * GNU General Public License for more details.
81 + */
82 +
83 +#include <linux/clk.h>
84 +#include <linux/err.h>
85 +#include <linux/io.h>
86 +#include <linux/module.h>
87 +#include <linux/of.h>
88 +#include <linux/platform_device.h>
89 +#include <linux/regulator/consumer.h>
90 +#include <linux/usb/phy.h>
91 +
92 +/**
93 + *  USB QSCRATCH Hardware registers
94 + */
95 +#define QSCRATCH_CTRL_REG              (0x04)
96 +#define QSCRATCH_GENERAL_CFG           (0x08)
97 +#define PHY_CTRL_REG                   (0x10)
98 +#define PARAMETER_OVERRIDE_X_REG       (0x14)
99 +#define CHARGING_DET_CTRL_REG          (0x18)
100 +#define CHARGING_DET_OUTPUT_REG                (0x1c)
101 +#define ALT_INTERRUPT_EN_REG           (0x20)
102 +#define PHY_IRQ_STAT_REG               (0x24)
103 +#define CGCTL_REG                      (0x28)
104 +
105 +#define PHY_3P3_VOL_MIN                        3050000 /* uV */
106 +#define PHY_3P3_VOL_MAX                        3300000 /* uV */
107 +#define PHY_3P3_HPM_LOAD               16000   /* uA */
108 +
109 +#define PHY_1P8_VOL_MIN                        1800000 /* uV */
110 +#define PHY_1P8_VOL_MAX                        1800000 /* uV */
111 +#define PHY_1P8_HPM_LOAD               19000   /* uA */
112 +
113 +/* TODO: these are suspicious */
114 +#define USB_VDDCX_NO                   1       /* index */
115 +#define USB_VDDCX_MIN                  5       /* index */
116 +#define USB_VDDCX_MAX                  7       /* index */
117 +
118 +struct qcom_dwc3_hs_phy {
119 +       struct usb_phy          phy;
120 +       void __iomem            *base;
121 +       struct device           *dev;
122 +
123 +       struct clk              *xo_clk;
124 +       struct clk              *utmi_clk;
125 +
126 +       struct regulator        *v3p3;
127 +       struct regulator        *v1p8;
128 +       struct regulator        *vddcx;
129 +       struct regulator        *vbus;
130 +};
131 +
132 +#define        phy_to_dw_phy(x)        container_of((x), struct qcom_dwc3_hs_phy, phy)
133 +
134 +
135 +/**
136 + * Write register.
137 + *
138 + * @base - QCOM DWC3 PHY base virtual address.
139 + * @offset - register offset.
140 + * @val - value to write.
141 + */
142 +static inline void qcom_dwc3_hs_write(void __iomem *base, u32 offset, u32 val)
143 +{
144 +       writel(val, base + offset);
145 +}
146 +
147 +/**
148 + * Write register and read back masked value to confirm it is written
149 + *
150 + * @base - QCOM DWC3 PHY base virtual address.
151 + * @offset - register offset.
152 + * @mask - register bitmask specifying what should be updated
153 + * @val - value to write.
154 + */
155 +static inline void qcom_dwc3_hs_write_readback(void __iomem *base, u32 offset,
156 +                                           const u32 mask, u32 val)
157 +{
158 +       u32 write_val, tmp = readl(base + offset);
159 +
160 +       tmp &= ~mask;           /* retain other bits */
161 +       write_val = tmp | val;
162 +
163 +       writel(write_val, base + offset);
164 +
165 +       /* Read back to see if val was written */
166 +       tmp = readl(base + offset);
167 +       tmp &= mask;            /* clear other bits */
168 +
169 +       if (tmp != val)
170 +               pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset);
171 +}
172 +
173 +static int qcom_dwc3_hs_notify_connect(struct usb_phy *x,
174 +       enum usb_device_speed speed)
175 +{
176 +       struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
177 +
178 +       dev_err(phy->dev, "notify connect\n");
179 +       return 0;
180 +}
181 +
182 +static int qcom_dwc3_hs_notify_disconnect(struct usb_phy *x,
183 +       enum usb_device_speed speed)
184 +{
185 +       struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
186 +
187 +       dev_err(phy->dev, "notify disconnect\n");
188 +       return 0;
189 +}
190 +
191 +
192 +static void qcom_dwc3_hs_phy_shutdown(struct usb_phy *x)
193 +{
194 +       struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
195 +       int ret;
196 +
197 +       ret = regulator_set_voltage(phy->v3p3, 0, PHY_3P3_VOL_MAX);
198 +       if (ret)
199 +               dev_err(phy->dev, "cannot set voltage for v3p3\n");
200 +
201 +       ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX);
202 +       if (ret)
203 +               dev_err(phy->dev, "cannot set voltage for v1p8\n");
204 +
205 +       ret = regulator_disable(phy->v1p8);
206 +       if (ret)
207 +               dev_err(phy->dev, "cannot disable v1p8\n");
208 +
209 +       ret = regulator_disable(phy->v3p3);
210 +       if (ret)
211 +               dev_err(phy->dev, "cannot disable v3p3\n");
212 +
213 +       ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX);
214 +       if (ret)
215 +               dev_err(phy->dev, "cannot set voltage for vddcx\n");
216 +
217 +       ret = regulator_disable(phy->vddcx);
218 +       if (ret)
219 +               dev_err(phy->dev, "cannot enable vddcx\n");
220 +
221 +       clk_disable_unprepare(phy->utmi_clk);
222 +}
223 +
224 +static int qcom_dwc3_hs_phy_init(struct usb_phy *x)
225 +{
226 +       struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
227 +       int ret;
228 +       u32 val;
229 +
230 +       clk_prepare_enable(phy->utmi_clk);
231 +
232 +       ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX);
233 +       if (ret)
234 +               dev_err(phy->dev, "cannot set voltage for vddcx\n");
235 +
236 +       ret = regulator_enable(phy->vddcx);
237 +       if (ret)
238 +               dev_err(phy->dev, "cannot enable vddcx\n");
239 +
240 +       ret = regulator_set_voltage(phy->v3p3, PHY_3P3_VOL_MIN,
241 +                                   PHY_3P3_VOL_MAX);
242 +       if (ret)
243 +               dev_err(phy->dev, "cannot set voltage for v3p3\n");
244 +
245 +       ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN,
246 +                                   PHY_1P8_VOL_MAX);
247 +       if (ret)
248 +               dev_err(phy->dev, "cannot set voltage for v1p8\n");
249 +
250 +       ret = regulator_set_optimum_mode(phy->v1p8, PHY_1P8_HPM_LOAD);
251 +       if (ret < 0)
252 +               dev_err(phy->dev, "cannot set HPM of regulator v1p8\n");
253 +
254 +       ret = regulator_enable(phy->v1p8);
255 +       if (ret)
256 +               dev_err(phy->dev, "cannot enable v1p8\n");
257 +
258 +       ret = regulator_set_optimum_mode(phy->v3p3, PHY_3P3_HPM_LOAD);
259 +       if (ret < 0)
260 +               dev_err(phy->dev, "cannot set HPM of regulator v3p3\n");
261 +
262 +       ret = regulator_enable(phy->v3p3);
263 +       if (ret)
264 +               dev_err(phy->dev, "cannot enable v3p3\n");
265 +
266 +       /*
267 +        * HSPHY Initialization: Enable UTMI clock and clamp enable HVINTs,
268 +        * and disable RETENTION (power-on default is ENABLED)
269 +        */
270 +       val = readl(phy->base + PHY_CTRL_REG);
271 +       val |= BIT(18) | BIT(20) | BIT(11) | 0x70;
272 +       qcom_dwc3_hs_write(phy->base, PHY_CTRL_REG, val);
273 +       usleep_range(2000, 2200);
274 +
275 +       /*
276 +        * write HSPHY init value to QSCRATCH reg to set HSPHY parameters like
277 +        * VBUS valid threshold, disconnect valid threshold, DC voltage level,
278 +        * preempasis and rise/fall time.
279 +        */
280 +       qcom_dwc3_hs_write_readback(phy->base, PARAMETER_OVERRIDE_X_REG,
281 +                               0x03ffffff, 0x00d191a4);
282 +
283 +       /* Disable (bypass) VBUS and ID filters */
284 +       qcom_dwc3_hs_write(phy->base, QSCRATCH_GENERAL_CFG, BIT(2));
285 +
286 +       return 0;
287 +}
288 +
289 +static int qcom_dwc3_hs_phy_set_vbus(struct usb_phy *x, int on)
290 +{
291 +       struct qcom_dwc3_hs_phy *phy = phy_to_dw_phy(x);
292 +       int ret = 0;
293 +
294 +       if (IS_ERR(phy->vbus))
295 +               return on ? PTR_ERR(phy->vbus) : 0;
296 +
297 +       if (on)
298 +               ret = regulator_enable(phy->vbus);
299 +       else
300 +               ret = regulator_disable(phy->vbus);
301 +
302 +       if (ret)
303 +               dev_err(x->dev, "Cannot %s Vbus\n", on ? "set" : "off");
304 +       return ret;
305 +}
306 +
307 +static int qcom_dwc3_hs_probe(struct platform_device *pdev)
308 +{
309 +       struct qcom_dwc3_hs_phy *phy;
310 +       struct resource         *res;
311 +       void __iomem            *base;
312 +
313 +       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
314 +       if (!phy)
315 +               return -ENOMEM;
316 +
317 +       platform_set_drvdata(pdev, phy);
318 +
319 +       phy->dev = &pdev->dev;
320 +
321 +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
322 +       base = devm_ioremap_resource(phy->dev, res);
323 +       if (IS_ERR(base))
324 +               return PTR_ERR(base);
325 +
326 +       phy->vddcx = devm_regulator_get(phy->dev, "vddcx");
327 +       if (IS_ERR(phy->vddcx)) {
328 +               dev_dbg(phy->dev, "cannot get vddcx\n");
329 +               return  PTR_ERR(phy->vddcx);
330 +       }
331 +
332 +       phy->v3p3 = devm_regulator_get(phy->dev, "v3p3");
333 +       if (IS_ERR(phy->v3p3)) {
334 +               dev_dbg(phy->dev, "cannot get v3p3\n");
335 +               return PTR_ERR(phy->v3p3);
336 +       }
337 +
338 +       phy->v1p8 = devm_regulator_get(phy->dev, "v1p8");
339 +       if (IS_ERR(phy->v1p8)) {
340 +               dev_dbg(phy->dev, "cannot get v1p8\n");
341 +               return PTR_ERR(phy->v1p8);
342 +       }
343 +
344 +       phy->vbus = devm_regulator_get(phy->dev, "vbus");
345 +       if (IS_ERR(phy->vbus))
346 +               dev_dbg(phy->dev, "Failed to get vbus\n");
347 +
348 +       phy->utmi_clk = devm_clk_get(phy->dev, "utmi");
349 +       if (IS_ERR(phy->utmi_clk)) {
350 +               dev_dbg(phy->dev, "cannot get UTMI handle\n");
351 +               return PTR_ERR(phy->utmi_clk);
352 +       }
353 +
354 +       phy->xo_clk = devm_clk_get(phy->dev, "xo");
355 +       if (IS_ERR(phy->xo_clk)) {
356 +               dev_dbg(phy->dev, "cannot get TCXO buffer handle\n");
357 +               phy->xo_clk = NULL;
358 +       }
359 +
360 +       clk_set_rate(phy->utmi_clk, 60000000);
361 +
362 +       if (phy->xo_clk)
363 +               clk_prepare_enable(phy->xo_clk);
364 +
365 +       phy->base               = base;
366 +       phy->phy.dev            = phy->dev;
367 +       phy->phy.label          = "qcom-dwc3-hsphy";
368 +       phy->phy.init           = qcom_dwc3_hs_phy_init;
369 +       phy->phy.notify_connect = qcom_dwc3_hs_notify_connect;
370 +       phy->phy.notify_disconnect = qcom_dwc3_hs_notify_disconnect;
371 +       phy->phy.shutdown       = qcom_dwc3_hs_phy_shutdown;
372 +       phy->phy.set_vbus       = qcom_dwc3_hs_phy_set_vbus;
373 +       phy->phy.type           = USB_PHY_TYPE_USB2;
374 +       phy->phy.state          = OTG_STATE_UNDEFINED;
375 +
376 +       usb_add_phy_dev(&phy->phy);
377 +       return 0;
378 +}
379 +
380 +static int qcom_dwc3_hs_remove(struct platform_device *pdev)
381 +{
382 +       struct qcom_dwc3_hs_phy *phy = platform_get_drvdata(pdev);
383 +
384 +       if (phy->xo_clk)
385 +               clk_disable_unprepare(phy->xo_clk);
386 +       usb_remove_phy(&phy->phy);
387 +       return 0;
388 +}
389 +
390 +static const struct of_device_id qcom_dwc3_hs_id_table[] = {
391 +       { .compatible = "qcom,dwc3-hsphy" },
392 +       { /* Sentinel */ }
393 +};
394 +MODULE_DEVICE_TABLE(of, qcom_dwc3_hs_id_table);
395 +
396 +static struct platform_driver qcom_dwc3_hs_driver = {
397 +       .probe          = qcom_dwc3_hs_probe,
398 +       .remove         = qcom_dwc3_hs_remove,
399 +       .driver         = {
400 +               .name   = "qcom-dwc3-hsphy",
401 +               .owner  = THIS_MODULE,
402 +               .of_match_table = qcom_dwc3_hs_id_table,
403 +       },
404 +};
405 +
406 +module_platform_driver(qcom_dwc3_hs_driver);
407 +
408 +MODULE_ALIAS("platform:qcom-dwc3-hsphy");
409 +MODULE_LICENSE("GPL v2");
410 +MODULE_DESCRIPTION("DesignWare USB3 QCOM HSPHY driver");
411 diff --git a/drivers/usb/phy/phy-qcom-ssusb.c b/drivers/usb/phy/phy-qcom-ssusb.c
412 new file mode 100644
413 index 0000000..3da778f
414 --- /dev/null
415 +++ b/drivers/usb/phy/phy-qcom-ssusb.c
416 @@ -0,0 +1,455 @@
417 +/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
418 + *
419 + * This program is free software; you can redistribute it and/or modify
420 + * it under the terms of the GNU General Public License version 2 and
421 + * only version 2 as published by the Free Software Foundation.
422 + *
423 + * This program is distributed in the hope that it will be useful,
424 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
425 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
426 + * GNU General Public License for more details.
427 + */
428 +
429 +#include <linux/clk.h>
430 +#include <linux/err.h>
431 +#include <linux/io.h>
432 +#include <linux/module.h>
433 +#include <linux/of.h>
434 +#include <linux/platform_device.h>
435 +#include <linux/regulator/consumer.h>
436 +#include <linux/usb/phy.h>
437 +
438 +/**
439 + *  USB QSCRATCH Hardware registers
440 + */
441 +#define PHY_CTRL_REG                   (0x00)
442 +#define PHY_PARAM_CTRL_1               (0x04)
443 +#define PHY_PARAM_CTRL_2               (0x08)
444 +#define CR_PROTOCOL_DATA_IN_REG                (0x0c)
445 +#define CR_PROTOCOL_DATA_OUT_REG       (0x10)
446 +#define CR_PROTOCOL_CAP_ADDR_REG       (0x14)
447 +#define CR_PROTOCOL_CAP_DATA_REG       (0x18)
448 +#define CR_PROTOCOL_READ_REG           (0x1c)
449 +#define CR_PROTOCOL_WRITE_REG          (0x20)
450 +
451 +#define PHY_1P8_VOL_MIN                        1800000 /* uV */
452 +#define PHY_1P8_VOL_MAX                        1800000 /* uV */
453 +#define PHY_1P8_HPM_LOAD               23000   /* uA */
454 +
455 +/* TODO: these are suspicious */
456 +#define USB_VDDCX_NO                   1       /* index */
457 +#define USB_VDDCX_MIN                  5       /* index */
458 +#define USB_VDDCX_MAX                  7       /* index */
459 +
460 +struct qcom_dwc3_ss_phy {
461 +       struct usb_phy          phy;
462 +       void __iomem            *base;
463 +       struct device           *dev;
464 +
465 +       struct regulator        *v1p8;
466 +       struct regulator        *vddcx;
467 +
468 +       struct clk              *xo_clk;
469 +       struct clk              *ref_clk;
470 +};
471 +
472 +#define        phy_to_dw_phy(x)        container_of((x), struct qcom_dwc3_ss_phy, phy)
473 +
474 +
475 +/**
476 + * Write register
477 + *
478 + * @base - QCOM DWC3 PHY base virtual address.
479 + * @offset - register offset.
480 + * @val - value to write.
481 + */
482 +static inline void qcom_dwc3_ss_write(void __iomem *base, u32 offset, u32 val)
483 +{
484 +       writel(val, base + offset);
485 +}
486 +
487 +/**
488 + * Write register and read back masked value to confirm it is written
489 + *
490 + * @base - QCOM DWC3 PHY base virtual address.
491 + * @offset - register offset.
492 + * @mask - register bitmask specifying what should be updated
493 + * @val - value to write.
494 + */
495 +static inline void qcom_dwc3_ss_write_readback(void __iomem *base, u32 offset,
496 +                                           const u32 mask, u32 val)
497 +{
498 +       u32 write_val, tmp = readl(base + offset);
499 +
500 +       tmp &= ~mask;           /* retain other bits */
501 +       write_val = tmp | val;
502 +
503 +       writel(write_val, base + offset);
504 +
505 +       /* Read back to see if val was written */
506 +       tmp = readl(base + offset);
507 +       tmp &= mask;            /* clear other bits */
508 +
509 +       if (tmp != val)
510 +               pr_err("write: %x to QSCRATCH: %x FAILED\n", val, offset);
511 +}
512 +
513 +/**
514 + * Write SSPHY register
515 + *
516 + * @base - QCOM DWC3 PHY base virtual address.
517 + * @addr - SSPHY address to write.
518 + * @val - value to write.
519 + */
520 +static void qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val)
521 +{
522 +       writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
523 +       writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
524 +       while (readl(base + CR_PROTOCOL_CAP_ADDR_REG))
525 +               cpu_relax();
526 +
527 +       writel(val, base + CR_PROTOCOL_DATA_IN_REG);
528 +       writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
529 +       while (readl(base + CR_PROTOCOL_CAP_DATA_REG))
530 +               cpu_relax();
531 +
532 +       writel(0x1, base + CR_PROTOCOL_WRITE_REG);
533 +       while (readl(base + CR_PROTOCOL_WRITE_REG))
534 +               cpu_relax();
535 +}
536 +
537 +/**
538 + * Read SSPHY register.
539 + *
540 + * @base - QCOM DWC3 PHY base virtual address.
541 + * @addr - SSPHY address to read.
542 + */
543 +static u32 qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr)
544 +{
545 +       bool first_read = true;
546 +
547 +       writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
548 +       writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
549 +       while (readl(base + CR_PROTOCOL_CAP_ADDR_REG))
550 +               cpu_relax();
551 +
552 +       /*
553 +        * Due to hardware bug, first read of SSPHY register might be
554 +        * incorrect. Hence as workaround, SW should perform SSPHY register
555 +        * read twice, but use only second read and ignore first read.
556 +        */
557 +retry:
558 +       writel(0x1, base + CR_PROTOCOL_READ_REG);
559 +       while (readl(base + CR_PROTOCOL_READ_REG))
560 +               cpu_relax();
561 +
562 +       if (first_read) {
563 +               readl(base + CR_PROTOCOL_DATA_OUT_REG);
564 +               first_read = false;
565 +               goto retry;
566 +       }
567 +
568 +       return readl(base + CR_PROTOCOL_DATA_OUT_REG);
569 +}
570 +
571 +static void qcom_dwc3_ss_phy_shutdown(struct usb_phy *x)
572 +{
573 +       struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
574 +       int ret;
575 +
576 +       /* Sequence to put SSPHY in low power state:
577 +        * 1. Clear REF_PHY_EN in PHY_CTRL_REG
578 +        * 2. Clear REF_USE_PAD in PHY_CTRL_REG
579 +        * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
580 +        * 4. Disable SSPHY ref clk
581 +        */
582 +       qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 8), 0x0);
583 +       qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG, (1 << 28), 0x0);
584 +       qcom_dwc3_ss_write_readback(phy->base, PHY_CTRL_REG,
585 +                                   (1 << 26), (1 << 26));
586 +
587 +       usleep_range(1000, 1200);
588 +       clk_disable_unprepare(phy->ref_clk);
589 +
590 +       ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_NO, USB_VDDCX_MAX);
591 +       if (ret)
592 +               dev_err(phy->dev, "cannot set voltage for vddcx\n");
593 +
594 +       regulator_disable(phy->vddcx);
595 +
596 +       ret = regulator_set_voltage(phy->v1p8, 0, PHY_1P8_VOL_MAX);
597 +       if (ret)
598 +               dev_err(phy->dev, "cannot set v1p8\n");
599 +
600 +       regulator_disable(phy->v1p8);
601 +}
602 +
603 +static int qcom_dwc3_ss_phy_init(struct usb_phy *x)
604 +{
605 +       struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
606 +       u32 data = 0;
607 +       int ret;
608 +
609 +       ret = regulator_set_voltage(phy->vddcx, USB_VDDCX_MIN, USB_VDDCX_MAX);
610 +       if (ret) {
611 +               dev_err(phy->dev, "cannot set voltage for vddcx\n");
612 +               return ret;
613 +       }
614 +
615 +       ret = regulator_enable(phy->vddcx);
616 +       if (ret) {
617 +               dev_err(phy->dev, "cannot enable vddcx\n");
618 +               return ret;
619 +       }
620 +
621 +       ret = regulator_set_voltage(phy->v1p8, PHY_1P8_VOL_MIN,
622 +                                   PHY_1P8_VOL_MAX);
623 +       if (ret) {
624 +               regulator_disable(phy->vddcx);
625 +               dev_err(phy->dev, "cannot set v1p8\n");
626 +               return ret;
627 +       }
628 +
629 +       ret = regulator_enable(phy->v1p8);
630 +       if (ret) {
631 +               regulator_disable(phy->vddcx);
632 +               dev_err(phy->dev, "cannot enable v1p8\n");
633 +               return ret;
634 +       }
635 +
636 +       clk_prepare_enable(phy->ref_clk);
637 +       usleep_range(1000, 1200);
638 +
639 +       /* reset phy */
640 +       data = readl_relaxed(phy->base + PHY_CTRL_REG);
641 +       writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG);
642 +       usleep_range(2000, 2200);
643 +       writel_relaxed(data, phy->base + PHY_CTRL_REG);
644 +
645 +       /* clear REF_PAD, we don't have XO clk */
646 +       data &= ~BIT(28);
647 +       writel_relaxed(data, phy->base + PHY_CTRL_REG);
648 +       msleep(30);
649 +
650 +       data |= BIT(8) | BIT(24);
651 +       writel_relaxed(data, phy->base + PHY_CTRL_REG);
652 +
653 +       /*
654 +        * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates
655 +        * in HS mode instead of SS mode. Workaround it by asserting
656 +        * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode
657 +        */
658 +       data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d);
659 +       data |= (1 << 7);
660 +       qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data);
661 +
662 +       data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010);
663 +       data &= ~0xff0;
664 +       data |= 0x20;
665 +       qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data);
666 +
667 +       /*
668 +        * Fix RX Equalization setting as follows
669 +        * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
670 +        * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
671 +        * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
672 +        * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
673 +        */
674 +       data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006);
675 +       data &= ~(1 << 6);
676 +       data |= (1 << 7);
677 +       data &= ~(0x7 << 8);
678 +       data |= (0x3 << 8);
679 +       data |= (0x1 << 11);
680 +       qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data);
681 +
682 +       /*
683 +        * Set EQ and TX launch amplitudes as follows
684 +        * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
685 +        * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
686 +        * LANE0.TX_OVRD_DRV_LO.EN set to 1.
687 +        */
688 +       data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002);
689 +       data &= ~0x3f80;
690 +       data |= (0x16 << 7);
691 +       data &= ~0x7f;
692 +       data |= (0x7f | (1 << 14));
693 +       qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data);
694 +
695 +       /*
696 +        * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
697 +        * TX_FULL_SWING [26:20] amplitude to 127
698 +        * TX_DEEMPH_3_5DB [13:8] to 22
699 +        * LOS_BIAS [2:0] to 0x5
700 +        */
701 +       qcom_dwc3_ss_write_readback(phy->base, PHY_PARAM_CTRL_1,
702 +                                  0x07f03f07, 0x07f01605);
703 +       return 0;
704 +}
705 +
706 +static int qcom_dwc3_ss_set_suspend(struct usb_phy *x, int suspend)
707 +{
708 +       struct qcom_dwc3_ss_phy *phy = phy_to_dw_phy(x);
709 +       u32 data;
710 +
711 +       if (!suspend) {
712 +               /* reset phy */
713 +               data = readl_relaxed(phy->base + PHY_CTRL_REG);
714 +               writel_relaxed(data | BIT(7), phy->base + PHY_CTRL_REG);
715 +               usleep_range(2000, 2200);
716 +               writel_relaxed(data, phy->base + PHY_CTRL_REG);
717 +
718 +               /* clear REF_PAD, we don't have XO clk */
719 +               data &= ~BIT(28);
720 +               writel_relaxed(data, phy->base + PHY_CTRL_REG);
721 +               msleep(30);
722 +
723 +               data |= BIT(8) | BIT(24);
724 +               writel_relaxed(data, phy->base + PHY_CTRL_REG);
725 +
726 +               /*
727 +                * WORKAROUND: There is SSPHY suspend bug due to which USB
728 +                * enumerates in HS mode instead of SS mode. Workaround it by
729 +                * asserting LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use
730 +                * alt bus mode
731 +                */
732 +               data = qcom_dwc3_ss_read_phycreg(phy->base, 0x102d);
733 +               data |= (1 << 7);
734 +               qcom_dwc3_ss_write_phycreg(phy->base, 0x102D, data);
735 +
736 +               data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1010);
737 +               data &= ~0xff0;
738 +               data |= 0x20;
739 +               qcom_dwc3_ss_write_phycreg(phy->base, 0x1010, data);
740 +
741 +               /*
742 +                * Fix RX Equalization setting as follows
743 +                * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
744 +                * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
745 +                * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
746 +                * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
747 +                */
748 +               data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1006);
749 +               data &= ~(1 << 6);
750 +               data |= (1 << 7);
751 +               data &= ~(0x7 << 8);
752 +               data |= (0x3 << 8);
753 +               data |= (0x1 << 11);
754 +               qcom_dwc3_ss_write_phycreg(phy->base, 0x1006, data);
755 +
756 +               /*
757 +                * Set EQ and TX launch amplitudes as follows
758 +                * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
759 +                * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
760 +                * LANE0.TX_OVRD_DRV_LO.EN set to 1.
761 +                */
762 +               data = qcom_dwc3_ss_read_phycreg(phy->base, 0x1002);
763 +               data &= ~0x3f80;
764 +               data |= (0x16 << 7);
765 +               data &= ~0x7f;
766 +               data |= (0x7f | (1 << 14));
767 +               qcom_dwc3_ss_write_phycreg(phy->base, 0x1002, data);
768 +
769 +               /*
770 +                * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows
771 +                * TX_FULL_SWING [26:20] amplitude to 127
772 +                * TX_DEEMPH_3_5DB [13:8] to 22
773 +                * LOS_BIAS [2:0] to 0x5
774 +                */
775 +               qcom_dwc3_ss_write_readback(phy->base, PHY_PARAM_CTRL_1,
776 +                                          0x07f03f07, 0x07f01605);
777 +       }
778 +       return 0;
779 +}
780 +
781 +static int qcom_dwc3_ss_probe(struct platform_device *pdev)
782 +{
783 +       struct qcom_dwc3_ss_phy *phy;
784 +       struct resource         *res;
785 +       void __iomem            *base;
786 +       int ret;
787 +
788 +       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
789 +       if (!phy)
790 +               return -ENOMEM;
791 +
792 +       platform_set_drvdata(pdev, phy);
793 +
794 +       phy->dev = &pdev->dev;
795 +
796 +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
797 +       base = devm_ioremap_resource(phy->dev, res);
798 +       if (IS_ERR(base))
799 +               return PTR_ERR(base);
800 +
801 +       phy->vddcx = devm_regulator_get(phy->dev, "vddcx");
802 +       if (IS_ERR(phy->vddcx)) {
803 +               dev_dbg(phy->dev, "cannot get vddcx\n");
804 +               return  PTR_ERR(phy->vddcx);
805 +       }
806 +
807 +       phy->v1p8 = devm_regulator_get(phy->dev, "v1p8");
808 +       if (IS_ERR(phy->v1p8)) {
809 +               dev_dbg(phy->dev, "cannot get v1p8\n");
810 +               return  PTR_ERR(phy->v1p8);
811 +       }
812 +
813 +       phy->xo_clk = devm_clk_get(phy->dev, "xo");
814 +       if (IS_ERR(phy->xo_clk)) {
815 +               dev_dbg(phy->dev, "cannot get XO clk, assuming not present\n");
816 +               phy->xo_clk = NULL;
817 +       }
818 +
819 +       phy->ref_clk = devm_clk_get(phy->dev, "ref");
820 +       if (IS_ERR(phy->ref_clk)) {
821 +               dev_dbg(phy->dev, "cannot get ref clock handle\n");
822 +               return PTR_ERR(phy->ref_clk);
823 +       }
824 +
825 +       clk_set_rate(phy->ref_clk, 125000000);
826 +       if (phy->xo_clk)
827 +               clk_prepare_enable(phy->xo_clk);
828 +
829 +       phy->base               = base;
830 +       phy->phy.dev            = phy->dev;
831 +       phy->phy.label          = "qcom-dwc3-ssphy";
832 +       phy->phy.init           = qcom_dwc3_ss_phy_init;
833 +       phy->phy.shutdown       = qcom_dwc3_ss_phy_shutdown;
834 +       phy->phy.set_suspend    = qcom_dwc3_ss_set_suspend;
835 +       phy->phy.type           = USB_PHY_TYPE_USB3;
836 +
837 +       ret = usb_add_phy_dev(&phy->phy);
838 +       return ret;
839 +}
840 +
841 +static int qcom_dwc3_ss_remove(struct platform_device *pdev)
842 +{
843 +       struct qcom_dwc3_ss_phy *phy = platform_get_drvdata(pdev);
844 +
845 +       if (phy->xo_clk)
846 +               clk_disable_unprepare(phy->xo_clk);
847 +       usb_remove_phy(&phy->phy);
848 +       return 0;
849 +}
850 +
851 +static const struct of_device_id qcom_dwc3_ss_id_table[] = {
852 +       { .compatible = "qcom,dwc3-ssphy" },
853 +       { /* Sentinel */ }
854 +};
855 +MODULE_DEVICE_TABLE(of, qcom_dwc3_ss_id_table);
856 +
857 +static struct platform_driver qcom_dwc3_ss_driver = {
858 +       .probe          = qcom_dwc3_ss_probe,
859 +       .remove         = qcom_dwc3_ss_remove,
860 +       .driver         = {
861 +               .name   = "qcom-dwc3-ssphy",
862 +               .owner  = THIS_MODULE,
863 +               .of_match_table = qcom_dwc3_ss_id_table,
864 +       },
865 +};
866 +
867 +module_platform_driver(qcom_dwc3_ss_driver);
868 +
869 +MODULE_ALIAS("platform:qcom-dwc3-ssphy");
870 +MODULE_LICENSE("GPL v2");
871 +MODULE_DESCRIPTION("DesignWare USB3 QCOM SSPHY driver");
872 -- 
873 1.7.10.4
874