summaryrefslogtreecommitdiffstats
path: root/drivers/phy/phy-qcom-qusb2.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-04 21:03:51 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-04 21:03:51 -0400
commit8f28472a739e8e39adc6e64ee5b460df039f0e4f (patch)
tree979e35f3d1d2be94c06c942bcdc9ee68cbebaacb /drivers/phy/phy-qcom-qusb2.c
parent4ac4d584886a4f47f8ff3bca0f32ff9a2987d3e5 (diff)
parentc034a43e72dda58e4a184d71f5502ef356e04453 (diff)
Merge tag 'usb-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH: "Here is the big USB patchset for 4.12-rc1. Lots of good stuff here, after many many many attempts, the kernel finally has a working typeC interface, many thanks to Heikki and Guenter and others who have taken the time to get this merged. It wasn't an easy path for them at all. There's also a staging driver that uses this new api, which is why it's coming in through this tree. Along with that, there's the usual huge number of changes for gadget drivers, xhci, and other stuff. Johan also finally refactored pretty much every driver that was looking at USB endpoints to do it in a common way, which will help prevent any "badly-formed" devices from causing problems in drivers. That too wasn't a simple task. All of these have been in linux-next for a while with no reported issues" * tag 'usb-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (263 commits) staging: typec: Fairchild FUSB302 Type-c chip driver staging: typec: Type-C Port Controller Interface driver (tcpci) staging: typec: USB Type-C Port Manager (tcpm) usb: host: xhci: remove #ifdef around PM functions usb: musb: don't mark of_dev_auxdata as initdata usb: misc: legousbtower: Fix buffers on stack USB: Revert "cdc-wdm: fix "out-of-sync" due to missing notifications" usb: Make sure usb/phy/of gets built-in USB: storage: e-mail update in drivers/usb/storage/unusual_devs.h usb: host: xhci: print correct command ring address usb: host: xhci: delete sp_dma_buffers for scratchpad usb: host: xhci: using correct specification chapter reference for DCBAAP xhci: switch to pci_alloc_irq_vectors usb: host: xhci-plat: set resume_quirk() for R-Car controllers usb: host: xhci-plat: add resume_quirk() usb: host: xhci-plat: enable clk in resume timing usb: host: plat: Enable xHCI plat runtime PM USB: serial: ftdi_sio: add device ID for Microsemi/Arrow SF2PLUS Dev Kit USB: serial: constify static arrays usb: fix some references for /proc/bus/usb ...
Diffstat (limited to 'drivers/phy/phy-qcom-qusb2.c')
-rw-r--r--drivers/phy/phy-qcom-qusb2.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/drivers/phy/phy-qcom-qusb2.c b/drivers/phy/phy-qcom-qusb2.c
new file mode 100644
index 000000000000..6c575244c0fb
--- /dev/null
+++ b/drivers/phy/phy-qcom-qusb2.c
@@ -0,0 +1,493 @@
1/*
2 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/clk.h>
15#include <linux/delay.h>
16#include <linux/err.h>
17#include <linux/io.h>
18#include <linux/kernel.h>
19#include <linux/mfd/syscon.h>
20#include <linux/module.h>
21#include <linux/nvmem-consumer.h>
22#include <linux/of.h>
23#include <linux/of_device.h>
24#include <linux/phy/phy.h>
25#include <linux/platform_device.h>
26#include <linux/regmap.h>
27#include <linux/regulator/consumer.h>
28#include <linux/reset.h>
29#include <linux/slab.h>
30
31#define QUSB2PHY_PLL_TEST 0x04
32#define CLK_REF_SEL BIT(7)
33
34#define QUSB2PHY_PLL_TUNE 0x08
35#define QUSB2PHY_PLL_USER_CTL1 0x0c
36#define QUSB2PHY_PLL_USER_CTL2 0x10
37#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c
38#define QUSB2PHY_PLL_PWR_CTRL 0x18
39
40#define QUSB2PHY_PLL_STATUS 0x38
41#define PLL_LOCKED BIT(5)
42
43#define QUSB2PHY_PORT_TUNE1 0x80
44#define QUSB2PHY_PORT_TUNE2 0x84
45#define QUSB2PHY_PORT_TUNE3 0x88
46#define QUSB2PHY_PORT_TUNE4 0x8c
47#define QUSB2PHY_PORT_TUNE5 0x90
48#define QUSB2PHY_PORT_TEST2 0x9c
49
50#define QUSB2PHY_PORT_POWERDOWN 0xb4
51#define CLAMP_N_EN BIT(5)
52#define FREEZIO_N BIT(1)
53#define POWER_DOWN BIT(0)
54
55#define QUSB2PHY_REFCLK_ENABLE BIT(0)
56
57#define PHY_CLK_SCHEME_SEL BIT(0)
58
59struct qusb2_phy_init_tbl {
60 unsigned int offset;
61 unsigned int val;
62};
63
64#define QUSB2_PHY_INIT_CFG(o, v) \
65 { \
66 .offset = o, \
67 .val = v, \
68 }
69
70static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
71 QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8),
72 QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3),
73 QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83),
74 QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0),
75 QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
76 QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
77 QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
78 QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
79 QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f),
80 QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
81};
82
83struct qusb2_phy_cfg {
84 const struct qusb2_phy_init_tbl *tbl;
85 /* number of entries in the table */
86 unsigned int tbl_num;
87 /* offset to PHY_CLK_SCHEME register in TCSR map */
88 unsigned int clk_scheme_offset;
89};
90
91static const struct qusb2_phy_cfg msm8996_phy_cfg = {
92 .tbl = msm8996_init_tbl,
93 .tbl_num = ARRAY_SIZE(msm8996_init_tbl),
94};
95
96static const char * const qusb2_phy_vreg_names[] = {
97 "vdda-pll", "vdda-phy-dpdm",
98};
99
100#define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names)
101
102/**
103 * struct qusb2_phy - structure holding qusb2 phy attributes
104 *
105 * @phy: generic phy
106 * @base: iomapped memory space for qubs2 phy
107 *
108 * @cfg_ahb_clk: AHB2PHY interface clock
109 * @ref_clk: phy reference clock
110 * @iface_clk: phy interface clock
111 * @phy_reset: phy reset control
112 * @vregs: regulator supplies bulk data
113 *
114 * @tcsr: TCSR syscon register map
115 * @cell: nvmem cell containing phy tuning value
116 *
117 * @cfg: phy config data
118 * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
119 */
120struct qusb2_phy {
121 struct phy *phy;
122 void __iomem *base;
123
124 struct clk *cfg_ahb_clk;
125 struct clk *ref_clk;
126 struct clk *iface_clk;
127 struct reset_control *phy_reset;
128 struct regulator_bulk_data vregs[QUSB2_NUM_VREGS];
129
130 struct regmap *tcsr;
131 struct nvmem_cell *cell;
132
133 const struct qusb2_phy_cfg *cfg;
134 bool has_se_clk_scheme;
135};
136
137static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val)
138{
139 u32 reg;
140
141 reg = readl(base + offset);
142 reg |= val;
143 writel(reg, base + offset);
144
145 /* Ensure above write is completed */
146 readl(base + offset);
147}
148
149static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val)
150{
151 u32 reg;
152
153 reg = readl(base + offset);
154 reg &= ~val;
155 writel(reg, base + offset);
156
157 /* Ensure above write is completed */
158 readl(base + offset);
159}
160
161static inline
162void qcom_qusb2_phy_configure(void __iomem *base,
163 const struct qusb2_phy_init_tbl tbl[], int num)
164{
165 int i;
166
167 for (i = 0; i < num; i++)
168 writel(tbl[i].val, base + tbl[i].offset);
169}
170
171/*
172 * Fetches HS Tx tuning value from nvmem and sets the
173 * QUSB2PHY_PORT_TUNE2 register.
174 * For error case, skip setting the value and use the default value.
175 */
176static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
177{
178 struct device *dev = &qphy->phy->dev;
179 u8 *val;
180
181 /*
182 * Read efuse register having TUNE2 parameter's high nibble.
183 * If efuse register shows value as 0x0, or if we fail to find
184 * a valid efuse register settings, then use default value
185 * as 0xB for high nibble that we have already set while
186 * configuring phy.
187 */
188 val = nvmem_cell_read(qphy->cell, NULL);
189 if (IS_ERR(val) || !val[0]) {
190 dev_dbg(dev, "failed to read a valid hs-tx trim value\n");
191 return;
192 }
193
194 /* Fused TUNE2 value is the higher nibble only */
195 qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4);
196}
197
198static int qusb2_phy_poweron(struct phy *phy)
199{
200 struct qusb2_phy *qphy = phy_get_drvdata(phy);
201 int num = ARRAY_SIZE(qphy->vregs);
202 int ret;
203
204 dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__);
205
206 /* turn on regulator supplies */
207 ret = regulator_bulk_enable(num, qphy->vregs);
208 if (ret)
209 return ret;
210
211 ret = clk_prepare_enable(qphy->iface_clk);
212 if (ret) {
213 dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
214 regulator_bulk_disable(num, qphy->vregs);
215 return ret;
216 }
217
218 return 0;
219}
220
221static int qusb2_phy_poweroff(struct phy *phy)
222{
223 struct qusb2_phy *qphy = phy_get_drvdata(phy);
224
225 clk_disable_unprepare(qphy->iface_clk);
226
227 regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
228
229 return 0;
230}
231
232static int qusb2_phy_init(struct phy *phy)
233{
234 struct qusb2_phy *qphy = phy_get_drvdata(phy);
235 unsigned int val;
236 unsigned int clk_scheme;
237 int ret;
238
239 dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__);
240
241 /* enable ahb interface clock to program phy */
242 ret = clk_prepare_enable(qphy->cfg_ahb_clk);
243 if (ret) {
244 dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
245 return ret;
246 }
247
248 /* Perform phy reset */
249 ret = reset_control_assert(qphy->phy_reset);
250 if (ret) {
251 dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
252 goto disable_ahb_clk;
253 }
254
255 /* 100 us delay to keep PHY in reset mode */
256 usleep_range(100, 150);
257
258 ret = reset_control_deassert(qphy->phy_reset);
259 if (ret) {
260 dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
261 goto disable_ahb_clk;
262 }
263
264 /* Disable the PHY */
265 qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN,
266 CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
267
268 /* save reset value to override reference clock scheme later */
269 val = readl(qphy->base + QUSB2PHY_PLL_TEST);
270
271 qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl,
272 qphy->cfg->tbl_num);
273
274 /* Set efuse value for tuning the PHY */
275 qusb2_phy_set_tune2_param(qphy);
276
277 /* Enable the PHY */
278 qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN);
279
280 /* Required to get phy pll lock successfully */
281 usleep_range(150, 160);
282
283 /* Default is single-ended clock on msm8996 */
284 qphy->has_se_clk_scheme = true;
285 /*
286 * read TCSR_PHY_CLK_SCHEME register to check if single-ended
287 * clock scheme is selected. If yes, then disable differential
288 * ref_clk and use single-ended clock, otherwise use differential
289 * ref_clk only.
290 */
291 if (qphy->tcsr) {
292 ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset,
293 &clk_scheme);
294 if (ret) {
295 dev_err(&phy->dev, "failed to read clk scheme reg\n");
296 goto assert_phy_reset;
297 }
298
299 /* is it a differential clock scheme ? */
300 if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) {
301 dev_vdbg(&phy->dev, "%s(): select differential clk\n",
302 __func__);
303 qphy->has_se_clk_scheme = false;
304 } else {
305 dev_vdbg(&phy->dev, "%s(): select single-ended clk\n",
306 __func__);
307 }
308 }
309
310 if (!qphy->has_se_clk_scheme) {
311 val &= ~CLK_REF_SEL;
312 ret = clk_prepare_enable(qphy->ref_clk);
313 if (ret) {
314 dev_err(&phy->dev, "failed to enable ref clk, %d\n",
315 ret);
316 goto assert_phy_reset;
317 }
318 } else {
319 val |= CLK_REF_SEL;
320 }
321
322 writel(val, qphy->base + QUSB2PHY_PLL_TEST);
323
324 /* ensure above write is through */
325 readl(qphy->base + QUSB2PHY_PLL_TEST);
326
327 /* Required to get phy pll lock successfully */
328 usleep_range(100, 110);
329
330 val = readb(qphy->base + QUSB2PHY_PLL_STATUS);
331 if (!(val & PLL_LOCKED)) {
332 dev_err(&phy->dev,
333 "QUSB2PHY pll lock failed: status reg = %x\n", val);
334 ret = -EBUSY;
335 goto disable_ref_clk;
336 }
337
338 return 0;
339
340disable_ref_clk:
341 if (!qphy->has_se_clk_scheme)
342 clk_disable_unprepare(qphy->ref_clk);
343assert_phy_reset:
344 reset_control_assert(qphy->phy_reset);
345disable_ahb_clk:
346 clk_disable_unprepare(qphy->cfg_ahb_clk);
347 return ret;
348}
349
350static int qusb2_phy_exit(struct phy *phy)
351{
352 struct qusb2_phy *qphy = phy_get_drvdata(phy);
353
354 /* Disable the PHY */
355 qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN,
356 CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
357
358 if (!qphy->has_se_clk_scheme)
359 clk_disable_unprepare(qphy->ref_clk);
360
361 reset_control_assert(qphy->phy_reset);
362
363 clk_disable_unprepare(qphy->cfg_ahb_clk);
364
365 return 0;
366}
367
368static const struct phy_ops qusb2_phy_gen_ops = {
369 .init = qusb2_phy_init,
370 .exit = qusb2_phy_exit,
371 .power_on = qusb2_phy_poweron,
372 .power_off = qusb2_phy_poweroff,
373 .owner = THIS_MODULE,
374};
375
376static const struct of_device_id qusb2_phy_of_match_table[] = {
377 {
378 .compatible = "qcom,msm8996-qusb2-phy",
379 .data = &msm8996_phy_cfg,
380 },
381 { },
382};
383MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
384
385static int qusb2_phy_probe(struct platform_device *pdev)
386{
387 struct device *dev = &pdev->dev;
388 struct qusb2_phy *qphy;
389 struct phy_provider *phy_provider;
390 struct phy *generic_phy;
391 struct resource *res;
392 int ret, i;
393 int num;
394
395 qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
396 if (!qphy)
397 return -ENOMEM;
398
399 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
400 qphy->base = devm_ioremap_resource(dev, res);
401 if (IS_ERR(qphy->base))
402 return PTR_ERR(qphy->base);
403
404 qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
405 if (IS_ERR(qphy->cfg_ahb_clk)) {
406 ret = PTR_ERR(qphy->cfg_ahb_clk);
407 if (ret != -EPROBE_DEFER)
408 dev_err(dev, "failed to get cfg ahb clk, %d\n", ret);
409 return ret;
410 }
411
412 qphy->ref_clk = devm_clk_get(dev, "ref");
413 if (IS_ERR(qphy->ref_clk)) {
414 ret = PTR_ERR(qphy->ref_clk);
415 if (ret != -EPROBE_DEFER)
416 dev_err(dev, "failed to get ref clk, %d\n", ret);
417 return ret;
418 }
419
420 qphy->iface_clk = devm_clk_get(dev, "iface");
421 if (IS_ERR(qphy->iface_clk)) {
422 ret = PTR_ERR(qphy->iface_clk);
423 if (ret == -EPROBE_DEFER)
424 return ret;
425 qphy->iface_clk = NULL;
426 dev_dbg(dev, "failed to get iface clk, %d\n", ret);
427 }
428
429 qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0);
430 if (IS_ERR(qphy->phy_reset)) {
431 dev_err(dev, "failed to get phy core reset\n");
432 return PTR_ERR(qphy->phy_reset);
433 }
434
435 num = ARRAY_SIZE(qphy->vregs);
436 for (i = 0; i < num; i++)
437 qphy->vregs[i].supply = qusb2_phy_vreg_names[i];
438
439 ret = devm_regulator_bulk_get(dev, num, qphy->vregs);
440 if (ret) {
441 dev_err(dev, "failed to get regulator supplies\n");
442 return ret;
443 }
444
445 /* Get the specific init parameters of QMP phy */
446 qphy->cfg = of_device_get_match_data(dev);
447
448 qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node,
449 "qcom,tcsr-syscon");
450 if (IS_ERR(qphy->tcsr)) {
451 dev_dbg(dev, "failed to lookup TCSR regmap\n");
452 qphy->tcsr = NULL;
453 }
454
455 qphy->cell = devm_nvmem_cell_get(dev, NULL);
456 if (IS_ERR(qphy->cell)) {
457 if (PTR_ERR(qphy->cell) == -EPROBE_DEFER)
458 return -EPROBE_DEFER;
459 qphy->cell = NULL;
460 dev_dbg(dev, "failed to lookup tune2 hstx trim value\n");
461 }
462
463 generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
464 if (IS_ERR(generic_phy)) {
465 ret = PTR_ERR(generic_phy);
466 dev_err(dev, "failed to create phy, %d\n", ret);
467 return ret;
468 }
469 qphy->phy = generic_phy;
470
471 dev_set_drvdata(dev, qphy);
472 phy_set_drvdata(generic_phy, qphy);
473
474 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
475 if (!IS_ERR(phy_provider))
476 dev_info(dev, "Registered Qcom-QUSB2 phy\n");
477
478 return PTR_ERR_OR_ZERO(phy_provider);
479}
480
481static struct platform_driver qusb2_phy_driver = {
482 .probe = qusb2_phy_probe,
483 .driver = {
484 .name = "qcom-qusb2-phy",
485 .of_match_table = qusb2_phy_of_match_table,
486 },
487};
488
489module_platform_driver(qusb2_phy_driver);
490
491MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
492MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver");
493MODULE_LICENSE("GPL v2");