1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Phy provider for USB 3.1 controller on HiSilicon Kirin970 platform |
4 | * |
5 | * Copyright (C) 2017-2020 Hilisicon Electronics Co., Ltd. |
6 | * http://www.huawei.com |
7 | * |
8 | * Authors: Yu Chen <chenyu56@huawei.com> |
9 | */ |
10 | |
11 | #include <linux/bitfield.h> |
12 | #include <linux/clk.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/phy/phy.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/regmap.h> |
20 | |
21 | #define SCTRL_SCDEEPSLEEPED (0x0) |
22 | #define USB_CLK_SELECTED BIT(20) |
23 | |
24 | #define PERI_CRG_PEREN0 (0x00) |
25 | #define PERI_CRG_PERDIS0 (0x04) |
26 | #define PERI_CRG_PEREN4 (0x40) |
27 | #define PERI_CRG_PERDIS4 (0x44) |
28 | #define PERI_CRG_PERRSTEN4 (0x90) |
29 | #define PERI_CRG_PERRSTDIS4 (0x94) |
30 | #define PERI_CRG_ISODIS (0x148) |
31 | #define PERI_CRG_PEREN6 (0x410) |
32 | #define PERI_CRG_PERDIS6 (0x414) |
33 | |
34 | #define USB_REFCLK_ISO_EN BIT(25) |
35 | |
36 | #define GT_CLK_USB2PHY_REF BIT(19) |
37 | |
38 | #define PCTRL_PERI_CTRL3 (0x10) |
39 | #define PCTRL_PERI_CTRL3_MSK_START (16) |
40 | #define USB_TCXO_EN BIT(1) |
41 | |
42 | #define PCTRL_PERI_CTRL24 (0x64) |
43 | #define SC_CLK_USB3PHY_3MUX1_SEL BIT(25) |
44 | |
45 | #define USB3OTG_CTRL0 (0x00) |
46 | #define USB3OTG_CTRL3 (0x0c) |
47 | #define USB3OTG_CTRL4 (0x10) |
48 | #define USB3OTG_CTRL5 (0x14) |
49 | #define USB3OTG_CTRL7 (0x1c) |
50 | #define USB_MISC_CFG50 (0x50) |
51 | #define USB_MISC_CFG54 (0x54) |
52 | #define USB_MISC_CFG58 (0x58) |
53 | #define USB_MISC_CFG5C (0x5c) |
54 | #define USB_MISC_CFGA0 (0xa0) |
55 | #define TCA_CLK_RST (0x200) |
56 | #define TCA_INTR_EN (0x204) |
57 | #define TCA_INTR_STS (0x208) |
58 | #define TCA_GCFG (0x210) |
59 | #define TCA_TCPC (0x214) |
60 | #define TCA_SYSMODE_CFG (0x218) |
61 | #define TCA_VBUS_CTRL (0x240) |
62 | |
63 | #define CTRL0_USB3_VBUSVLD BIT(7) |
64 | #define CTRL0_USB3_VBUSVLD_SEL BIT(6) |
65 | |
66 | #define CTRL3_USB2_VBUSVLDEXT0 BIT(6) |
67 | #define CTRL3_USB2_VBUSVLDEXTSEL0 BIT(5) |
68 | |
69 | #define CTRL5_USB2_SIDDQ BIT(0) |
70 | |
71 | #define CTRL7_USB2_REFCLKSEL_MASK GENMASK(4, 3) |
72 | #define CTRL7_USB2_REFCLKSEL_ABB (BIT(4) | BIT(3)) |
73 | #define CTRL7_USB2_REFCLKSEL_PAD BIT(4) |
74 | |
75 | #define CFG50_USB3_PHY_TEST_POWERDOWN BIT(23) |
76 | |
77 | #define CFG54_USB31PHY_CR_ADDR_MASK GENMASK(31, 16) |
78 | |
79 | #define CFG54_USB3PHY_REF_USE_PAD BIT(12) |
80 | #define CFG54_PHY0_PMA_PWR_STABLE BIT(11) |
81 | #define CFG54_PHY0_PCS_PWR_STABLE BIT(9) |
82 | #define CFG54_USB31PHY_CR_ACK BIT(7) |
83 | #define CFG54_USB31PHY_CR_WR_EN BIT(5) |
84 | #define CFG54_USB31PHY_CR_SEL BIT(4) |
85 | #define CFG54_USB31PHY_CR_RD_EN BIT(3) |
86 | #define CFG54_USB31PHY_CR_CLK BIT(2) |
87 | #define CFG54_USB3_PHY0_ANA_PWR_EN BIT(1) |
88 | |
89 | #define CFG58_USB31PHY_CR_DATA_MASK GENMASK(31, 16) |
90 | |
91 | #define CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN BIT(1) |
92 | |
93 | #define CFGA0_VAUX_RESET BIT(9) |
94 | #define CFGA0_USB31C_RESET BIT(8) |
95 | #define CFGA0_USB2PHY_REFCLK_SELECT BIT(4) |
96 | #define CFGA0_USB3PHY_RESET BIT(1) |
97 | #define CFGA0_USB2PHY_POR BIT(0) |
98 | |
99 | #define INTR_EN_XA_TIMEOUT_EVT_EN BIT(1) |
100 | #define INTR_EN_XA_ACK_EVT_EN BIT(0) |
101 | |
102 | #define CLK_RST_TCA_REF_CLK_EN BIT(1) |
103 | #define CLK_RST_SUSPEND_CLK_EN BIT(0) |
104 | |
105 | #define GCFG_ROLE_HSTDEV BIT(4) |
106 | #define GCFG_OP_MODE GENMASK(1, 0) |
107 | #define GCFG_OP_MODE_CTRL_SYNC_MODE BIT(0) |
108 | |
109 | #define TCPC_VALID BIT(4) |
110 | #define TCPC_LOW_POWER_EN BIT(3) |
111 | #define TCPC_MUX_CONTROL_MASK GENMASK(1, 0) |
112 | #define TCPC_MUX_CONTROL_USB31 BIT(0) |
113 | |
114 | #define SYSMODE_CFG_TYPEC_DISABLE BIT(3) |
115 | |
116 | #define VBUS_CTRL_POWERPRESENT_OVERRD GENMASK(3, 2) |
117 | #define VBUS_CTRL_VBUSVALID_OVERRD GENMASK(1, 0) |
118 | |
119 | #define KIRIN970_USB_DEFAULT_PHY_PARAM (0xfdfee4) |
120 | #define KIRIN970_USB_DEFAULT_PHY_VBOOST (0x5) |
121 | |
122 | #define TX_VBOOST_LVL_REG (0xf) |
123 | #define TX_VBOOST_LVL_START (6) |
124 | #define TX_VBOOST_LVL_ENABLE BIT(9) |
125 | |
126 | struct hi3670_priv { |
127 | struct device *dev; |
128 | struct regmap *peri_crg; |
129 | struct regmap *pctrl; |
130 | struct regmap *sctrl; |
131 | struct regmap *usb31misc; |
132 | |
133 | u32 eye_diagram_param; |
134 | u32 tx_vboost_lvl; |
135 | |
136 | u32 peri_crg_offset; |
137 | u32 pctrl_offset; |
138 | u32 usb31misc_offset; |
139 | }; |
140 | |
141 | static int hi3670_phy_cr_clk(struct regmap *usb31misc) |
142 | { |
143 | int ret; |
144 | |
145 | /* Clock up */ |
146 | ret = regmap_update_bits(map: usb31misc, USB_MISC_CFG54, |
147 | CFG54_USB31PHY_CR_CLK, CFG54_USB31PHY_CR_CLK); |
148 | if (ret) |
149 | return ret; |
150 | |
151 | /* Clock down */ |
152 | return regmap_update_bits(map: usb31misc, USB_MISC_CFG54, |
153 | CFG54_USB31PHY_CR_CLK, val: 0); |
154 | } |
155 | |
156 | static int hi3670_phy_cr_set_sel(struct regmap *usb31misc) |
157 | { |
158 | return regmap_update_bits(map: usb31misc, USB_MISC_CFG54, |
159 | CFG54_USB31PHY_CR_SEL, CFG54_USB31PHY_CR_SEL); |
160 | } |
161 | |
162 | static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction) |
163 | { |
164 | int ret, reg; |
165 | |
166 | if (direction) |
167 | reg = CFG54_USB31PHY_CR_WR_EN; |
168 | else |
169 | reg = CFG54_USB31PHY_CR_RD_EN; |
170 | |
171 | ret = regmap_update_bits(map: usb31misc, USB_MISC_CFG54, mask: reg, val: reg); |
172 | |
173 | if (ret) |
174 | return ret; |
175 | |
176 | ret = hi3670_phy_cr_clk(usb31misc); |
177 | if (ret) |
178 | return ret; |
179 | |
180 | return regmap_update_bits(map: usb31misc, USB_MISC_CFG54, |
181 | CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, val: 0); |
182 | } |
183 | |
184 | static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc) |
185 | { |
186 | u32 reg; |
187 | int retry = 10; |
188 | int ret; |
189 | |
190 | while (retry-- > 0) { |
191 | ret = regmap_read(map: usb31misc, USB_MISC_CFG54, val: ®); |
192 | if (ret) |
193 | return ret; |
194 | if ((reg & CFG54_USB31PHY_CR_ACK) == CFG54_USB31PHY_CR_ACK) |
195 | return 0; |
196 | |
197 | ret = hi3670_phy_cr_clk(usb31misc); |
198 | if (ret) |
199 | return ret; |
200 | |
201 | usleep_range(min: 10, max: 20); |
202 | } |
203 | |
204 | return -ETIMEDOUT; |
205 | } |
206 | |
207 | static int hi3670_phy_cr_set_addr(struct regmap *usb31misc, u32 addr) |
208 | { |
209 | u32 reg; |
210 | int ret; |
211 | |
212 | ret = regmap_read(map: usb31misc, USB_MISC_CFG54, val: ®); |
213 | if (ret) |
214 | return ret; |
215 | |
216 | reg = FIELD_PREP(CFG54_USB31PHY_CR_ADDR_MASK, addr); |
217 | |
218 | return regmap_update_bits(map: usb31misc, USB_MISC_CFG54, |
219 | CFG54_USB31PHY_CR_ADDR_MASK, val: reg); |
220 | } |
221 | |
222 | static int hi3670_phy_cr_read(struct regmap *usb31misc, u32 addr, u32 *val) |
223 | { |
224 | int reg, i, ret; |
225 | |
226 | for (i = 0; i < 100; i++) { |
227 | ret = hi3670_phy_cr_clk(usb31misc); |
228 | if (ret) |
229 | return ret; |
230 | } |
231 | |
232 | ret = hi3670_phy_cr_set_sel(usb31misc); |
233 | if (ret) |
234 | return ret; |
235 | |
236 | ret = hi3670_phy_cr_set_addr(usb31misc, addr); |
237 | if (ret) |
238 | return ret; |
239 | |
240 | ret = hi3670_phy_cr_start(usb31misc, direction: 0); |
241 | if (ret) |
242 | return ret; |
243 | |
244 | ret = hi3670_phy_cr_wait_ack(usb31misc); |
245 | if (ret) |
246 | return ret; |
247 | |
248 | ret = regmap_read(map: usb31misc, USB_MISC_CFG58, val: ®); |
249 | if (ret) |
250 | return ret; |
251 | |
252 | *val = FIELD_GET(CFG58_USB31PHY_CR_DATA_MASK, reg); |
253 | |
254 | return 0; |
255 | } |
256 | |
257 | static int hi3670_phy_cr_write(struct regmap *usb31misc, u32 addr, u32 val) |
258 | { |
259 | int i; |
260 | int ret; |
261 | |
262 | for (i = 0; i < 100; i++) { |
263 | ret = hi3670_phy_cr_clk(usb31misc); |
264 | if (ret) |
265 | return ret; |
266 | } |
267 | |
268 | ret = hi3670_phy_cr_set_sel(usb31misc); |
269 | if (ret) |
270 | return ret; |
271 | |
272 | ret = hi3670_phy_cr_set_addr(usb31misc, addr); |
273 | if (ret) |
274 | return ret; |
275 | |
276 | ret = regmap_write(map: usb31misc, USB_MISC_CFG58, |
277 | FIELD_PREP(CFG58_USB31PHY_CR_DATA_MASK, val)); |
278 | if (ret) |
279 | return ret; |
280 | |
281 | ret = hi3670_phy_cr_start(usb31misc, direction: 1); |
282 | if (ret) |
283 | return ret; |
284 | |
285 | return hi3670_phy_cr_wait_ack(usb31misc); |
286 | } |
287 | |
288 | static int hi3670_phy_set_params(struct hi3670_priv *priv) |
289 | { |
290 | u32 reg; |
291 | int ret; |
292 | int retry = 3; |
293 | |
294 | ret = regmap_write(map: priv->usb31misc, USB3OTG_CTRL4, |
295 | val: priv->eye_diagram_param); |
296 | if (ret) { |
297 | dev_err(priv->dev, "set USB3OTG_CTRL4 failed\n" ); |
298 | return ret; |
299 | } |
300 | |
301 | while (retry-- > 0) { |
302 | ret = hi3670_phy_cr_read(usb31misc: priv->usb31misc, |
303 | TX_VBOOST_LVL_REG, val: ®); |
304 | if (!ret) |
305 | break; |
306 | |
307 | if (ret != -ETIMEDOUT) { |
308 | dev_err(priv->dev, "read TX_VBOOST_LVL_REG failed\n" ); |
309 | return ret; |
310 | } |
311 | } |
312 | if (ret) |
313 | return ret; |
314 | |
315 | reg |= (TX_VBOOST_LVL_ENABLE | (priv->tx_vboost_lvl << TX_VBOOST_LVL_START)); |
316 | ret = hi3670_phy_cr_write(usb31misc: priv->usb31misc, TX_VBOOST_LVL_REG, val: reg); |
317 | if (ret) |
318 | dev_err(priv->dev, "write TX_VBOOST_LVL_REG failed\n" ); |
319 | |
320 | return ret; |
321 | } |
322 | |
323 | static bool hi3670_is_abbclk_selected(struct hi3670_priv *priv) |
324 | { |
325 | u32 reg; |
326 | |
327 | if (!priv->sctrl) { |
328 | dev_err(priv->dev, "priv->sctrl is null!\n" ); |
329 | return false; |
330 | } |
331 | |
332 | if (regmap_read(map: priv->sctrl, SCTRL_SCDEEPSLEEPED, val: ®)) { |
333 | dev_err(priv->dev, "SCTRL_SCDEEPSLEEPED read failed!\n" ); |
334 | return false; |
335 | } |
336 | |
337 | if ((reg & USB_CLK_SELECTED) == 0) |
338 | return false; |
339 | |
340 | return true; |
341 | } |
342 | |
343 | static int hi3670_config_phy_clock(struct hi3670_priv *priv) |
344 | { |
345 | u32 val, mask; |
346 | int ret; |
347 | |
348 | if (!hi3670_is_abbclk_selected(priv)) { |
349 | /* usb refclk iso disable */ |
350 | ret = regmap_write(map: priv->peri_crg, PERI_CRG_ISODIS, |
351 | USB_REFCLK_ISO_EN); |
352 | if (ret) |
353 | goto out; |
354 | |
355 | /* enable usb_tcxo_en */ |
356 | ret = regmap_write(map: priv->pctrl, PCTRL_PERI_CTRL3, |
357 | USB_TCXO_EN | |
358 | (USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START)); |
359 | |
360 | /* select usbphy clk from abb */ |
361 | mask = SC_CLK_USB3PHY_3MUX1_SEL; |
362 | ret = regmap_update_bits(map: priv->pctrl, |
363 | PCTRL_PERI_CTRL24, mask, val: 0); |
364 | if (ret) |
365 | goto out; |
366 | |
367 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFGA0, |
368 | CFGA0_USB2PHY_REFCLK_SELECT, val: 0); |
369 | if (ret) |
370 | goto out; |
371 | |
372 | ret = regmap_read(map: priv->usb31misc, USB3OTG_CTRL7, val: &val); |
373 | if (ret) |
374 | goto out; |
375 | val &= ~CTRL7_USB2_REFCLKSEL_MASK; |
376 | val |= CTRL7_USB2_REFCLKSEL_ABB; |
377 | ret = regmap_write(map: priv->usb31misc, USB3OTG_CTRL7, val); |
378 | if (ret) |
379 | goto out; |
380 | |
381 | return 0; |
382 | } |
383 | |
384 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFG54, |
385 | CFG54_USB3PHY_REF_USE_PAD, |
386 | CFG54_USB3PHY_REF_USE_PAD); |
387 | if (ret) |
388 | goto out; |
389 | |
390 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFGA0, |
391 | CFGA0_USB2PHY_REFCLK_SELECT, |
392 | CFGA0_USB2PHY_REFCLK_SELECT); |
393 | if (ret) |
394 | goto out; |
395 | |
396 | ret = regmap_read(map: priv->usb31misc, USB3OTG_CTRL7, val: &val); |
397 | if (ret) |
398 | goto out; |
399 | val &= ~CTRL7_USB2_REFCLKSEL_MASK; |
400 | val |= CTRL7_USB2_REFCLKSEL_PAD; |
401 | ret = regmap_write(map: priv->usb31misc, USB3OTG_CTRL7, val); |
402 | if (ret) |
403 | goto out; |
404 | |
405 | ret = regmap_write(map: priv->peri_crg, |
406 | PERI_CRG_PEREN6, GT_CLK_USB2PHY_REF); |
407 | if (ret) |
408 | goto out; |
409 | |
410 | return 0; |
411 | out: |
412 | dev_err(priv->dev, "failed to config phy clock ret: %d\n" , ret); |
413 | return ret; |
414 | } |
415 | |
416 | static int hi3670_config_tca(struct hi3670_priv *priv) |
417 | { |
418 | u32 val, mask; |
419 | int ret; |
420 | |
421 | ret = regmap_write(map: priv->usb31misc, TCA_INTR_STS, val: 0xffff); |
422 | if (ret) |
423 | goto out; |
424 | |
425 | ret = regmap_write(map: priv->usb31misc, TCA_INTR_EN, |
426 | INTR_EN_XA_TIMEOUT_EVT_EN | INTR_EN_XA_ACK_EVT_EN); |
427 | if (ret) |
428 | goto out; |
429 | |
430 | mask = CLK_RST_TCA_REF_CLK_EN | CLK_RST_SUSPEND_CLK_EN; |
431 | ret = regmap_update_bits(map: priv->usb31misc, TCA_CLK_RST, mask, val: 0); |
432 | if (ret) |
433 | goto out; |
434 | |
435 | ret = regmap_update_bits(map: priv->usb31misc, TCA_GCFG, |
436 | GCFG_ROLE_HSTDEV | GCFG_OP_MODE, |
437 | GCFG_ROLE_HSTDEV | GCFG_OP_MODE_CTRL_SYNC_MODE); |
438 | if (ret) |
439 | goto out; |
440 | |
441 | ret = regmap_update_bits(map: priv->usb31misc, TCA_SYSMODE_CFG, |
442 | SYSMODE_CFG_TYPEC_DISABLE, val: 0); |
443 | if (ret) |
444 | goto out; |
445 | |
446 | ret = regmap_read(map: priv->usb31misc, TCA_TCPC, val: &val); |
447 | if (ret) |
448 | goto out; |
449 | val &= ~(TCPC_VALID | TCPC_LOW_POWER_EN | TCPC_MUX_CONTROL_MASK); |
450 | val |= (TCPC_VALID | TCPC_MUX_CONTROL_USB31); |
451 | ret = regmap_write(map: priv->usb31misc, TCA_TCPC, val); |
452 | if (ret) |
453 | goto out; |
454 | |
455 | ret = regmap_write(map: priv->usb31misc, TCA_VBUS_CTRL, |
456 | VBUS_CTRL_POWERPRESENT_OVERRD | VBUS_CTRL_VBUSVALID_OVERRD); |
457 | if (ret) |
458 | goto out; |
459 | |
460 | return 0; |
461 | out: |
462 | dev_err(priv->dev, "failed to config phy clock ret: %d\n" , ret); |
463 | return ret; |
464 | } |
465 | |
466 | static int hi3670_phy_init(struct phy *phy) |
467 | { |
468 | struct hi3670_priv *priv = phy_get_drvdata(phy); |
469 | u32 val; |
470 | int ret; |
471 | |
472 | /* assert controller */ |
473 | val = CFGA0_VAUX_RESET | CFGA0_USB31C_RESET | |
474 | CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR; |
475 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFGA0, mask: val, val: 0); |
476 | if (ret) |
477 | goto out; |
478 | |
479 | ret = hi3670_config_phy_clock(priv); |
480 | if (ret) |
481 | goto out; |
482 | |
483 | /* Exit from IDDQ mode */ |
484 | ret = regmap_update_bits(map: priv->usb31misc, USB3OTG_CTRL5, |
485 | CTRL5_USB2_SIDDQ, val: 0); |
486 | if (ret) |
487 | goto out; |
488 | |
489 | /* Release USB31 PHY out of TestPowerDown mode */ |
490 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFG50, |
491 | CFG50_USB3_PHY_TEST_POWERDOWN, val: 0); |
492 | if (ret) |
493 | goto out; |
494 | |
495 | /* Deassert phy */ |
496 | val = CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR; |
497 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFGA0, mask: val, val); |
498 | if (ret) |
499 | goto out; |
500 | |
501 | usleep_range(min: 100, max: 120); |
502 | |
503 | /* Tell the PHY power is stable */ |
504 | val = CFG54_USB3_PHY0_ANA_PWR_EN | CFG54_PHY0_PCS_PWR_STABLE | |
505 | CFG54_PHY0_PMA_PWR_STABLE; |
506 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFG54, |
507 | mask: val, val); |
508 | if (ret) |
509 | goto out; |
510 | |
511 | ret = hi3670_config_tca(priv); |
512 | if (ret) |
513 | goto out; |
514 | |
515 | /* Enable SSC */ |
516 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFG5C, |
517 | CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN, |
518 | CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN); |
519 | if (ret) |
520 | goto out; |
521 | |
522 | /* Deassert controller */ |
523 | val = CFGA0_VAUX_RESET | CFGA0_USB31C_RESET; |
524 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFGA0, mask: val, val); |
525 | if (ret) |
526 | goto out; |
527 | |
528 | usleep_range(min: 100, max: 120); |
529 | |
530 | /* Set fake vbus valid signal */ |
531 | val = CTRL0_USB3_VBUSVLD | CTRL0_USB3_VBUSVLD_SEL; |
532 | ret = regmap_update_bits(map: priv->usb31misc, USB3OTG_CTRL0, mask: val, val); |
533 | if (ret) |
534 | goto out; |
535 | |
536 | val = CTRL3_USB2_VBUSVLDEXT0 | CTRL3_USB2_VBUSVLDEXTSEL0; |
537 | ret = regmap_update_bits(map: priv->usb31misc, USB3OTG_CTRL3, mask: val, val); |
538 | if (ret) |
539 | goto out; |
540 | |
541 | usleep_range(min: 100, max: 120); |
542 | |
543 | ret = hi3670_phy_set_params(priv); |
544 | if (ret) |
545 | goto out; |
546 | |
547 | return 0; |
548 | out: |
549 | dev_err(priv->dev, "failed to init phy ret: %d\n" , ret); |
550 | return ret; |
551 | } |
552 | |
553 | static int hi3670_phy_exit(struct phy *phy) |
554 | { |
555 | struct hi3670_priv *priv = phy_get_drvdata(phy); |
556 | u32 mask; |
557 | int ret; |
558 | |
559 | /* Assert phy */ |
560 | mask = CFGA0_USB3PHY_RESET | CFGA0_USB2PHY_POR; |
561 | ret = regmap_update_bits(map: priv->usb31misc, USB_MISC_CFGA0, mask, val: 0); |
562 | if (ret) |
563 | goto out; |
564 | |
565 | if (!hi3670_is_abbclk_selected(priv)) { |
566 | /* disable usb_tcxo_en */ |
567 | ret = regmap_write(map: priv->pctrl, PCTRL_PERI_CTRL3, |
568 | USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START); |
569 | } else { |
570 | ret = regmap_write(map: priv->peri_crg, PERI_CRG_PERDIS6, |
571 | GT_CLK_USB2PHY_REF); |
572 | if (ret) |
573 | goto out; |
574 | } |
575 | |
576 | return 0; |
577 | out: |
578 | dev_err(priv->dev, "failed to exit phy ret: %d\n" , ret); |
579 | return ret; |
580 | } |
581 | |
582 | static const struct phy_ops hi3670_phy_ops = { |
583 | .init = hi3670_phy_init, |
584 | .exit = hi3670_phy_exit, |
585 | .owner = THIS_MODULE, |
586 | }; |
587 | |
588 | static int hi3670_phy_probe(struct platform_device *pdev) |
589 | { |
590 | struct phy_provider *phy_provider; |
591 | struct device *dev = &pdev->dev; |
592 | struct phy *phy; |
593 | struct hi3670_priv *priv; |
594 | |
595 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
596 | if (!priv) |
597 | return -ENOMEM; |
598 | |
599 | priv->dev = dev; |
600 | priv->peri_crg = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
601 | property: "hisilicon,pericrg-syscon" ); |
602 | if (IS_ERR(ptr: priv->peri_crg)) { |
603 | dev_err(dev, "no hisilicon,pericrg-syscon\n" ); |
604 | return PTR_ERR(ptr: priv->peri_crg); |
605 | } |
606 | |
607 | priv->pctrl = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
608 | property: "hisilicon,pctrl-syscon" ); |
609 | if (IS_ERR(ptr: priv->pctrl)) { |
610 | dev_err(dev, "no hisilicon,pctrl-syscon\n" ); |
611 | return PTR_ERR(ptr: priv->pctrl); |
612 | } |
613 | |
614 | priv->sctrl = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
615 | property: "hisilicon,sctrl-syscon" ); |
616 | if (IS_ERR(ptr: priv->sctrl)) { |
617 | dev_err(dev, "no hisilicon,sctrl-syscon\n" ); |
618 | return PTR_ERR(ptr: priv->sctrl); |
619 | } |
620 | |
621 | /* node of hi3670 phy is a sub-node of usb3_otg_bc */ |
622 | priv->usb31misc = syscon_node_to_regmap(np: dev->parent->of_node); |
623 | if (IS_ERR(ptr: priv->usb31misc)) { |
624 | dev_err(dev, "no hisilicon,usb3-otg-bc-syscon\n" ); |
625 | return PTR_ERR(ptr: priv->usb31misc); |
626 | } |
627 | |
628 | if (of_property_read_u32(np: dev->of_node, propname: "hisilicon,eye-diagram-param" , |
629 | out_value: &priv->eye_diagram_param)) |
630 | priv->eye_diagram_param = KIRIN970_USB_DEFAULT_PHY_PARAM; |
631 | |
632 | if (of_property_read_u32(np: dev->of_node, propname: "hisilicon,tx-vboost-lvl" , |
633 | out_value: &priv->tx_vboost_lvl)) |
634 | priv->tx_vboost_lvl = KIRIN970_USB_DEFAULT_PHY_VBOOST; |
635 | |
636 | phy = devm_phy_create(dev, NULL, ops: &hi3670_phy_ops); |
637 | if (IS_ERR(ptr: phy)) |
638 | return PTR_ERR(ptr: phy); |
639 | |
640 | phy_set_drvdata(phy, data: priv); |
641 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
642 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
643 | } |
644 | |
645 | static const struct of_device_id hi3670_phy_of_match[] = { |
646 | { .compatible = "hisilicon,hi3670-usb-phy" }, |
647 | { }, |
648 | }; |
649 | MODULE_DEVICE_TABLE(of, hi3670_phy_of_match); |
650 | |
651 | static struct platform_driver hi3670_phy_driver = { |
652 | .probe = hi3670_phy_probe, |
653 | .driver = { |
654 | .name = "hi3670-usb-phy" , |
655 | .of_match_table = hi3670_phy_of_match, |
656 | } |
657 | }; |
658 | module_platform_driver(hi3670_phy_driver); |
659 | |
660 | MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>" ); |
661 | MODULE_LICENSE("GPL v2" ); |
662 | MODULE_DESCRIPTION("Hilisicon Kirin970 USB31 PHY Driver" ); |
663 | |