ipq806x: Add support for IPQ806x chip family
[openwrt/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