1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * phy-rtk-usb2.c RTK usb2.0 PHY driver |
4 | * |
5 | * Copyright (C) 2023 Realtek Semiconductor Corporation |
6 | * |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/uaccess.h> |
14 | #include <linux/debugfs.h> |
15 | #include <linux/nvmem-consumer.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/sys_soc.h> |
18 | #include <linux/mfd/syscon.h> |
19 | #include <linux/phy/phy.h> |
20 | #include <linux/usb.h> |
21 | |
22 | /* GUSB2PHYACCn register */ |
23 | #define PHY_NEW_REG_REQ BIT(25) |
24 | #define PHY_VSTS_BUSY BIT(23) |
25 | #define PHY_VCTRL_SHIFT 8 |
26 | #define PHY_REG_DATA_MASK 0xff |
27 | |
28 | #define GET_LOW_NIBBLE(addr) ((addr) & 0x0f) |
29 | #define GET_HIGH_NIBBLE(addr) (((addr) & 0xf0) >> 4) |
30 | |
31 | #define EFUS_USB_DC_CAL_RATE 2 |
32 | #define EFUS_USB_DC_CAL_MAX 7 |
33 | |
34 | #define EFUS_USB_DC_DIS_RATE 1 |
35 | #define EFUS_USB_DC_DIS_MAX 7 |
36 | |
37 | #define MAX_PHY_DATA_SIZE 20 |
38 | #define OFFEST_PHY_READ 0x20 |
39 | |
40 | #define MAX_USB_PHY_NUM 4 |
41 | #define MAX_USB_PHY_PAGE0_DATA_SIZE 16 |
42 | #define MAX_USB_PHY_PAGE1_DATA_SIZE 16 |
43 | #define MAX_USB_PHY_PAGE2_DATA_SIZE 8 |
44 | |
45 | #define SET_PAGE_OFFSET 0xf4 |
46 | #define SET_PAGE_0 0x9b |
47 | #define SET_PAGE_1 0xbb |
48 | #define SET_PAGE_2 0xdb |
49 | |
50 | #define PAGE_START 0xe0 |
51 | #define PAGE0_0XE4 0xe4 |
52 | #define PAGE0_0XE6 0xe6 |
53 | #define PAGE0_0XE7 0xe7 |
54 | #define PAGE1_0XE0 0xe0 |
55 | #define PAGE1_0XE2 0xe2 |
56 | |
57 | #define SENSITIVITY_CTRL (BIT(4) | BIT(5) | BIT(6)) |
58 | #define ENABLE_AUTO_SENSITIVITY_CALIBRATION BIT(2) |
59 | #define DEFAULT_DC_DRIVING_VALUE (0x8) |
60 | #define DEFAULT_DC_DISCONNECTION_VALUE (0x6) |
61 | #define HS_CLK_SELECT BIT(6) |
62 | |
63 | struct phy_reg { |
64 | void __iomem *reg_wrap_vstatus; |
65 | void __iomem *reg_gusb2phyacc0; |
66 | int vstatus_index; |
67 | }; |
68 | |
69 | struct phy_data { |
70 | u8 addr; |
71 | u8 data; |
72 | }; |
73 | |
74 | struct phy_cfg { |
75 | int page0_size; |
76 | struct phy_data page0[MAX_USB_PHY_PAGE0_DATA_SIZE]; |
77 | int page1_size; |
78 | struct phy_data page1[MAX_USB_PHY_PAGE1_DATA_SIZE]; |
79 | int page2_size; |
80 | struct phy_data page2[MAX_USB_PHY_PAGE2_DATA_SIZE]; |
81 | |
82 | int num_phy; |
83 | |
84 | bool check_efuse; |
85 | int check_efuse_version; |
86 | #define CHECK_EFUSE_V1 1 |
87 | #define CHECK_EFUSE_V2 2 |
88 | int efuse_dc_driving_rate; |
89 | int efuse_dc_disconnect_rate; |
90 | int dc_driving_mask; |
91 | int dc_disconnect_mask; |
92 | bool usb_dc_disconnect_at_page0; |
93 | int driving_updated_for_dev_dis; |
94 | |
95 | bool do_toggle; |
96 | bool do_toggle_driving; |
97 | bool use_default_parameter; |
98 | bool is_double_sensitivity_mode; |
99 | }; |
100 | |
101 | struct phy_parameter { |
102 | struct phy_reg phy_reg; |
103 | |
104 | /* Get from efuse */ |
105 | s8 efuse_usb_dc_cal; |
106 | s8 efuse_usb_dc_dis; |
107 | |
108 | /* Get from dts */ |
109 | bool inverse_hstx_sync_clock; |
110 | u32 driving_level; |
111 | s32 driving_level_compensate; |
112 | s32 disconnection_compensate; |
113 | }; |
114 | |
115 | struct rtk_phy { |
116 | struct device *dev; |
117 | |
118 | struct phy_cfg *phy_cfg; |
119 | int num_phy; |
120 | struct phy_parameter *phy_parameter; |
121 | |
122 | struct dentry *debug_dir; |
123 | }; |
124 | |
125 | /* mapping 0xE0 to 0 ... 0xE7 to 7, 0xF0 to 8 ,,, 0xF7 to 15 */ |
126 | static inline int page_addr_to_array_index(u8 addr) |
127 | { |
128 | return (int)((((addr) - PAGE_START) & 0x7) + |
129 | ((((addr) - PAGE_START) & 0x10) >> 1)); |
130 | } |
131 | |
132 | static inline u8 array_index_to_page_addr(int index) |
133 | { |
134 | return ((((index) + PAGE_START) & 0x7) + |
135 | ((((index) & 0x8) << 1) + PAGE_START)); |
136 | } |
137 | |
138 | #define PHY_IO_TIMEOUT_USEC (50000) |
139 | #define PHY_IO_DELAY_US (100) |
140 | |
141 | static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) |
142 | { |
143 | int ret; |
144 | unsigned int val; |
145 | |
146 | ret = read_poll_timeout(readl, val, ((val & mask) == result), |
147 | PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg); |
148 | if (ret) { |
149 | pr_err("%s can't program USB phy\n" , __func__); |
150 | return -ETIMEDOUT; |
151 | } |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | static char rtk_phy_read(struct phy_reg *phy_reg, char addr) |
157 | { |
158 | void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0; |
159 | unsigned int val; |
160 | int ret = 0; |
161 | |
162 | addr -= OFFEST_PHY_READ; |
163 | |
164 | /* polling until VBusy == 0 */ |
165 | ret = utmi_wait_register(reg: reg_gusb2phyacc0, PHY_VSTS_BUSY, result: 0); |
166 | if (ret) |
167 | return (char)ret; |
168 | |
169 | /* VCtrl = low nibble of addr, and set PHY_NEW_REG_REQ */ |
170 | val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT); |
171 | writel(val, addr: reg_gusb2phyacc0); |
172 | ret = utmi_wait_register(reg: reg_gusb2phyacc0, PHY_VSTS_BUSY, result: 0); |
173 | if (ret) |
174 | return (char)ret; |
175 | |
176 | /* VCtrl = high nibble of addr, and set PHY_NEW_REG_REQ */ |
177 | val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT); |
178 | writel(val, addr: reg_gusb2phyacc0); |
179 | ret = utmi_wait_register(reg: reg_gusb2phyacc0, PHY_VSTS_BUSY, result: 0); |
180 | if (ret) |
181 | return (char)ret; |
182 | |
183 | val = readl(addr: reg_gusb2phyacc0); |
184 | |
185 | return (char)(val & PHY_REG_DATA_MASK); |
186 | } |
187 | |
188 | static int rtk_phy_write(struct phy_reg *phy_reg, char addr, char data) |
189 | { |
190 | unsigned int val; |
191 | void __iomem *reg_wrap_vstatus = phy_reg->reg_wrap_vstatus; |
192 | void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0; |
193 | int shift_bits = phy_reg->vstatus_index * 8; |
194 | int ret = 0; |
195 | |
196 | /* write data to VStatusOut2 (data output to phy) */ |
197 | writel(val: (u32)data << shift_bits, addr: reg_wrap_vstatus); |
198 | |
199 | ret = utmi_wait_register(reg: reg_gusb2phyacc0, PHY_VSTS_BUSY, result: 0); |
200 | if (ret) |
201 | return ret; |
202 | |
203 | /* VCtrl = low nibble of addr, set PHY_NEW_REG_REQ */ |
204 | val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT); |
205 | |
206 | writel(val, addr: reg_gusb2phyacc0); |
207 | ret = utmi_wait_register(reg: reg_gusb2phyacc0, PHY_VSTS_BUSY, result: 0); |
208 | if (ret) |
209 | return ret; |
210 | |
211 | /* VCtrl = high nibble of addr, set PHY_NEW_REG_REQ */ |
212 | val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT); |
213 | |
214 | writel(val, addr: reg_gusb2phyacc0); |
215 | ret = utmi_wait_register(reg: reg_gusb2phyacc0, PHY_VSTS_BUSY, result: 0); |
216 | if (ret) |
217 | return ret; |
218 | |
219 | return 0; |
220 | } |
221 | |
222 | static int rtk_phy_set_page(struct phy_reg *phy_reg, int page) |
223 | { |
224 | switch (page) { |
225 | case 0: |
226 | return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_0); |
227 | case 1: |
228 | return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_1); |
229 | case 2: |
230 | return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_2); |
231 | default: |
232 | pr_err("%s error page=%d\n" , __func__, page); |
233 | } |
234 | |
235 | return -EINVAL; |
236 | } |
237 | |
238 | static u8 __updated_dc_disconnect_level_page0_0xe4(struct phy_cfg *phy_cfg, |
239 | struct phy_parameter *phy_parameter, u8 data) |
240 | { |
241 | u8 ret; |
242 | s32 val; |
243 | s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask; |
244 | int offset = 4; |
245 | |
246 | val = (s32)((data >> offset) & dc_disconnect_mask) |
247 | + phy_parameter->efuse_usb_dc_dis |
248 | + phy_parameter->disconnection_compensate; |
249 | |
250 | if (val > dc_disconnect_mask) |
251 | val = dc_disconnect_mask; |
252 | else if (val < 0) |
253 | val = 0; |
254 | |
255 | ret = (data & (~(dc_disconnect_mask << offset))) | |
256 | (val & dc_disconnect_mask) << offset; |
257 | |
258 | return ret; |
259 | } |
260 | |
261 | /* updated disconnect level at page0 */ |
262 | static void update_dc_disconnect_level_at_page0(struct rtk_phy *rtk_phy, |
263 | struct phy_parameter *phy_parameter, bool update) |
264 | { |
265 | struct phy_cfg *phy_cfg; |
266 | struct phy_reg *phy_reg; |
267 | struct phy_data *phy_data_page; |
268 | struct phy_data *phy_data; |
269 | u8 addr, data; |
270 | int offset = 4; |
271 | s32 dc_disconnect_mask; |
272 | int i; |
273 | |
274 | phy_cfg = rtk_phy->phy_cfg; |
275 | phy_reg = &phy_parameter->phy_reg; |
276 | |
277 | /* Set page 0 */ |
278 | phy_data_page = phy_cfg->page0; |
279 | rtk_phy_set_page(phy_reg, page: 0); |
280 | |
281 | i = page_addr_to_array_index(PAGE0_0XE4); |
282 | phy_data = phy_data_page + i; |
283 | if (!phy_data->addr) { |
284 | phy_data->addr = PAGE0_0XE4; |
285 | phy_data->data = rtk_phy_read(phy_reg, PAGE0_0XE4); |
286 | } |
287 | |
288 | addr = phy_data->addr; |
289 | data = phy_data->data; |
290 | dc_disconnect_mask = phy_cfg->dc_disconnect_mask; |
291 | |
292 | if (update) |
293 | data = __updated_dc_disconnect_level_page0_0xe4(phy_cfg, phy_parameter, data); |
294 | else |
295 | data = (data & ~(dc_disconnect_mask << offset)) | |
296 | (DEFAULT_DC_DISCONNECTION_VALUE << offset); |
297 | |
298 | if (rtk_phy_write(phy_reg, addr, data)) |
299 | dev_err(rtk_phy->dev, |
300 | "%s: Error to set page1 parameter addr=0x%x value=0x%x\n" , |
301 | __func__, addr, data); |
302 | } |
303 | |
304 | static u8 __updated_dc_disconnect_level_page1_0xe2(struct phy_cfg *phy_cfg, |
305 | struct phy_parameter *phy_parameter, u8 data) |
306 | { |
307 | u8 ret; |
308 | s32 val; |
309 | s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask; |
310 | |
311 | if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { |
312 | val = (s32)(data & dc_disconnect_mask) |
313 | + phy_parameter->efuse_usb_dc_dis |
314 | + phy_parameter->disconnection_compensate; |
315 | } else { /* for CHECK_EFUSE_V2 or no efuse */ |
316 | if (phy_parameter->efuse_usb_dc_dis) |
317 | val = (s32)(phy_parameter->efuse_usb_dc_dis + |
318 | phy_parameter->disconnection_compensate); |
319 | else |
320 | val = (s32)((data & dc_disconnect_mask) + |
321 | phy_parameter->disconnection_compensate); |
322 | } |
323 | |
324 | if (val > dc_disconnect_mask) |
325 | val = dc_disconnect_mask; |
326 | else if (val < 0) |
327 | val = 0; |
328 | |
329 | ret = (data & (~dc_disconnect_mask)) | (val & dc_disconnect_mask); |
330 | |
331 | return ret; |
332 | } |
333 | |
334 | /* updated disconnect level at page1 */ |
335 | static void update_dc_disconnect_level_at_page1(struct rtk_phy *rtk_phy, |
336 | struct phy_parameter *phy_parameter, bool update) |
337 | { |
338 | struct phy_cfg *phy_cfg; |
339 | struct phy_data *phy_data_page; |
340 | struct phy_data *phy_data; |
341 | struct phy_reg *phy_reg; |
342 | u8 addr, data; |
343 | s32 dc_disconnect_mask; |
344 | int i; |
345 | |
346 | phy_cfg = rtk_phy->phy_cfg; |
347 | phy_reg = &phy_parameter->phy_reg; |
348 | |
349 | /* Set page 1 */ |
350 | phy_data_page = phy_cfg->page1; |
351 | rtk_phy_set_page(phy_reg, page: 1); |
352 | |
353 | i = page_addr_to_array_index(PAGE1_0XE2); |
354 | phy_data = phy_data_page + i; |
355 | if (!phy_data->addr) { |
356 | phy_data->addr = PAGE1_0XE2; |
357 | phy_data->data = rtk_phy_read(phy_reg, PAGE1_0XE2); |
358 | } |
359 | |
360 | addr = phy_data->addr; |
361 | data = phy_data->data; |
362 | dc_disconnect_mask = phy_cfg->dc_disconnect_mask; |
363 | |
364 | if (update) |
365 | data = __updated_dc_disconnect_level_page1_0xe2(phy_cfg, phy_parameter, data); |
366 | else |
367 | data = (data & ~dc_disconnect_mask) | DEFAULT_DC_DISCONNECTION_VALUE; |
368 | |
369 | if (rtk_phy_write(phy_reg, addr, data)) |
370 | dev_err(rtk_phy->dev, |
371 | "%s: Error to set page1 parameter addr=0x%x value=0x%x\n" , |
372 | __func__, addr, data); |
373 | } |
374 | |
375 | static void update_dc_disconnect_level(struct rtk_phy *rtk_phy, |
376 | struct phy_parameter *phy_parameter, bool update) |
377 | { |
378 | struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; |
379 | |
380 | if (phy_cfg->usb_dc_disconnect_at_page0) |
381 | update_dc_disconnect_level_at_page0(rtk_phy, phy_parameter, update); |
382 | else |
383 | update_dc_disconnect_level_at_page1(rtk_phy, phy_parameter, update); |
384 | } |
385 | |
386 | static u8 __update_dc_driving_page0_0xe4(struct phy_cfg *phy_cfg, |
387 | struct phy_parameter *phy_parameter, u8 data) |
388 | { |
389 | s32 driving_level_compensate = phy_parameter->driving_level_compensate; |
390 | s32 dc_driving_mask = phy_cfg->dc_driving_mask; |
391 | s32 val; |
392 | u8 ret; |
393 | |
394 | if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { |
395 | val = (s32)(data & dc_driving_mask) + driving_level_compensate |
396 | + phy_parameter->efuse_usb_dc_cal; |
397 | } else { /* for CHECK_EFUSE_V2 or no efuse */ |
398 | if (phy_parameter->efuse_usb_dc_cal) |
399 | val = (s32)((phy_parameter->efuse_usb_dc_cal & dc_driving_mask) |
400 | + driving_level_compensate); |
401 | else |
402 | val = (s32)(data & dc_driving_mask); |
403 | } |
404 | |
405 | if (val > dc_driving_mask) |
406 | val = dc_driving_mask; |
407 | else if (val < 0) |
408 | val = 0; |
409 | |
410 | ret = (data & (~dc_driving_mask)) | (val & dc_driving_mask); |
411 | |
412 | return ret; |
413 | } |
414 | |
415 | static void update_dc_driving_level(struct rtk_phy *rtk_phy, |
416 | struct phy_parameter *phy_parameter) |
417 | { |
418 | struct phy_cfg *phy_cfg; |
419 | struct phy_reg *phy_reg; |
420 | |
421 | phy_reg = &phy_parameter->phy_reg; |
422 | phy_cfg = rtk_phy->phy_cfg; |
423 | if (!phy_cfg->page0[4].addr) { |
424 | rtk_phy_set_page(phy_reg, page: 0); |
425 | phy_cfg->page0[4].addr = PAGE0_0XE4; |
426 | phy_cfg->page0[4].data = rtk_phy_read(phy_reg, PAGE0_0XE4); |
427 | } |
428 | |
429 | if (phy_parameter->driving_level != DEFAULT_DC_DRIVING_VALUE) { |
430 | u32 dc_driving_mask; |
431 | u8 driving_level; |
432 | u8 data; |
433 | |
434 | data = phy_cfg->page0[4].data; |
435 | dc_driving_mask = phy_cfg->dc_driving_mask; |
436 | driving_level = data & dc_driving_mask; |
437 | |
438 | dev_dbg(rtk_phy->dev, "%s driving_level=%d => dts driving_level=%d\n" , |
439 | __func__, driving_level, phy_parameter->driving_level); |
440 | |
441 | phy_cfg->page0[4].data = (data & (~dc_driving_mask)) | |
442 | (phy_parameter->driving_level & dc_driving_mask); |
443 | } |
444 | |
445 | phy_cfg->page0[4].data = __update_dc_driving_page0_0xe4(phy_cfg, |
446 | phy_parameter, |
447 | data: phy_cfg->page0[4].data); |
448 | } |
449 | |
450 | static void update_hs_clk_select(struct rtk_phy *rtk_phy, |
451 | struct phy_parameter *phy_parameter) |
452 | { |
453 | struct phy_cfg *phy_cfg; |
454 | struct phy_reg *phy_reg; |
455 | |
456 | phy_cfg = rtk_phy->phy_cfg; |
457 | phy_reg = &phy_parameter->phy_reg; |
458 | |
459 | if (phy_parameter->inverse_hstx_sync_clock) { |
460 | if (!phy_cfg->page0[6].addr) { |
461 | rtk_phy_set_page(phy_reg, page: 0); |
462 | phy_cfg->page0[6].addr = PAGE0_0XE6; |
463 | phy_cfg->page0[6].data = rtk_phy_read(phy_reg, PAGE0_0XE6); |
464 | } |
465 | |
466 | phy_cfg->page0[6].data = phy_cfg->page0[6].data | HS_CLK_SELECT; |
467 | } |
468 | } |
469 | |
470 | static void do_rtk_phy_toggle(struct rtk_phy *rtk_phy, |
471 | int index, bool connect) |
472 | { |
473 | struct phy_parameter *phy_parameter; |
474 | struct phy_cfg *phy_cfg; |
475 | struct phy_reg *phy_reg; |
476 | struct phy_data *phy_data_page; |
477 | u8 addr, data; |
478 | int i; |
479 | |
480 | phy_cfg = rtk_phy->phy_cfg; |
481 | phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; |
482 | phy_reg = &phy_parameter->phy_reg; |
483 | |
484 | if (!phy_cfg->do_toggle) |
485 | goto out; |
486 | |
487 | if (phy_cfg->is_double_sensitivity_mode) |
488 | goto do_toggle_driving; |
489 | |
490 | /* Set page 0 */ |
491 | rtk_phy_set_page(phy_reg, page: 0); |
492 | |
493 | addr = PAGE0_0XE7; |
494 | data = rtk_phy_read(phy_reg, addr); |
495 | |
496 | if (connect) |
497 | rtk_phy_write(phy_reg, addr, data: data & (~SENSITIVITY_CTRL)); |
498 | else |
499 | rtk_phy_write(phy_reg, addr, data: data | (SENSITIVITY_CTRL)); |
500 | |
501 | do_toggle_driving: |
502 | |
503 | if (!phy_cfg->do_toggle_driving) |
504 | goto do_toggle; |
505 | |
506 | /* Page 0 addr 0xE4 driving capability */ |
507 | |
508 | /* Set page 0 */ |
509 | phy_data_page = phy_cfg->page0; |
510 | rtk_phy_set_page(phy_reg, page: 0); |
511 | |
512 | i = page_addr_to_array_index(PAGE0_0XE4); |
513 | addr = phy_data_page[i].addr; |
514 | data = phy_data_page[i].data; |
515 | |
516 | if (connect) { |
517 | rtk_phy_write(phy_reg, addr, data); |
518 | } else { |
519 | u8 value; |
520 | s32 tmp; |
521 | s32 driving_updated = |
522 | phy_cfg->driving_updated_for_dev_dis; |
523 | s32 dc_driving_mask = phy_cfg->dc_driving_mask; |
524 | |
525 | tmp = (s32)(data & dc_driving_mask) + driving_updated; |
526 | |
527 | if (tmp > dc_driving_mask) |
528 | tmp = dc_driving_mask; |
529 | else if (tmp < 0) |
530 | tmp = 0; |
531 | |
532 | value = (data & (~dc_driving_mask)) | (tmp & dc_driving_mask); |
533 | |
534 | rtk_phy_write(phy_reg, addr, data: value); |
535 | } |
536 | |
537 | do_toggle: |
538 | /* restore dc disconnect level before toggle */ |
539 | update_dc_disconnect_level(rtk_phy, phy_parameter, update: false); |
540 | |
541 | /* Set page 1 */ |
542 | rtk_phy_set_page(phy_reg, page: 1); |
543 | |
544 | addr = PAGE1_0XE0; |
545 | data = rtk_phy_read(phy_reg, addr); |
546 | |
547 | rtk_phy_write(phy_reg, addr, data: data & |
548 | (~ENABLE_AUTO_SENSITIVITY_CALIBRATION)); |
549 | mdelay(1); |
550 | rtk_phy_write(phy_reg, addr, data: data | |
551 | (ENABLE_AUTO_SENSITIVITY_CALIBRATION)); |
552 | |
553 | /* update dc disconnect level after toggle */ |
554 | update_dc_disconnect_level(rtk_phy, phy_parameter, update: true); |
555 | |
556 | out: |
557 | return; |
558 | } |
559 | |
560 | static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index) |
561 | { |
562 | struct phy_parameter *phy_parameter; |
563 | struct phy_cfg *phy_cfg; |
564 | struct phy_data *phy_data_page; |
565 | struct phy_reg *phy_reg; |
566 | int i; |
567 | |
568 | phy_cfg = rtk_phy->phy_cfg; |
569 | phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; |
570 | phy_reg = &phy_parameter->phy_reg; |
571 | |
572 | if (phy_cfg->use_default_parameter) { |
573 | dev_dbg(rtk_phy->dev, "%s phy#%d use default parameter\n" , |
574 | __func__, index); |
575 | goto do_toggle; |
576 | } |
577 | |
578 | /* Set page 0 */ |
579 | phy_data_page = phy_cfg->page0; |
580 | rtk_phy_set_page(phy_reg, page: 0); |
581 | |
582 | for (i = 0; i < phy_cfg->page0_size; i++) { |
583 | struct phy_data *phy_data = phy_data_page + i; |
584 | u8 addr = phy_data->addr; |
585 | u8 data = phy_data->data; |
586 | |
587 | if (!addr) |
588 | continue; |
589 | |
590 | if (rtk_phy_write(phy_reg, addr, data)) { |
591 | dev_err(rtk_phy->dev, |
592 | "%s: Error to set page0 parameter addr=0x%x value=0x%x\n" , |
593 | __func__, addr, data); |
594 | return -EINVAL; |
595 | } |
596 | } |
597 | |
598 | /* Set page 1 */ |
599 | phy_data_page = phy_cfg->page1; |
600 | rtk_phy_set_page(phy_reg, page: 1); |
601 | |
602 | for (i = 0; i < phy_cfg->page1_size; i++) { |
603 | struct phy_data *phy_data = phy_data_page + i; |
604 | u8 addr = phy_data->addr; |
605 | u8 data = phy_data->data; |
606 | |
607 | if (!addr) |
608 | continue; |
609 | |
610 | if (rtk_phy_write(phy_reg, addr, data)) { |
611 | dev_err(rtk_phy->dev, |
612 | "%s: Error to set page1 parameter addr=0x%x value=0x%x\n" , |
613 | __func__, addr, data); |
614 | return -EINVAL; |
615 | } |
616 | } |
617 | |
618 | if (phy_cfg->page2_size == 0) |
619 | goto do_toggle; |
620 | |
621 | /* Set page 2 */ |
622 | phy_data_page = phy_cfg->page2; |
623 | rtk_phy_set_page(phy_reg, page: 2); |
624 | |
625 | for (i = 0; i < phy_cfg->page2_size; i++) { |
626 | struct phy_data *phy_data = phy_data_page + i; |
627 | u8 addr = phy_data->addr; |
628 | u8 data = phy_data->data; |
629 | |
630 | if (!addr) |
631 | continue; |
632 | |
633 | if (rtk_phy_write(phy_reg, addr, data)) { |
634 | dev_err(rtk_phy->dev, |
635 | "%s: Error to set page2 parameter addr=0x%x value=0x%x\n" , |
636 | __func__, addr, data); |
637 | return -EINVAL; |
638 | } |
639 | } |
640 | |
641 | do_toggle: |
642 | do_rtk_phy_toggle(rtk_phy, index, connect: false); |
643 | |
644 | return 0; |
645 | } |
646 | |
647 | static int rtk_phy_init(struct phy *phy) |
648 | { |
649 | struct rtk_phy *rtk_phy = phy_get_drvdata(phy); |
650 | unsigned long phy_init_time = jiffies; |
651 | int i, ret = 0; |
652 | |
653 | if (!rtk_phy) |
654 | return -EINVAL; |
655 | |
656 | for (i = 0; i < rtk_phy->num_phy; i++) |
657 | ret = do_rtk_phy_init(rtk_phy, index: i); |
658 | |
659 | dev_dbg(rtk_phy->dev, "Initialized RTK USB 2.0 PHY (take %dms)\n" , |
660 | jiffies_to_msecs(jiffies - phy_init_time)); |
661 | return ret; |
662 | } |
663 | |
664 | static int rtk_phy_exit(struct phy *phy) |
665 | { |
666 | return 0; |
667 | } |
668 | |
669 | static void rtk_phy_toggle(struct rtk_phy *rtk_phy, bool connect, int port) |
670 | { |
671 | int index = port; |
672 | |
673 | if (index > rtk_phy->num_phy) { |
674 | dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n" , |
675 | __func__, index, rtk_phy->num_phy); |
676 | return; |
677 | } |
678 | |
679 | do_rtk_phy_toggle(rtk_phy, index, connect); |
680 | } |
681 | |
682 | static int rtk_phy_connect(struct phy *phy, int port) |
683 | { |
684 | struct rtk_phy *rtk_phy = phy_get_drvdata(phy); |
685 | |
686 | dev_dbg(rtk_phy->dev, "%s port=%d\n" , __func__, port); |
687 | rtk_phy_toggle(rtk_phy, connect: true, port); |
688 | |
689 | return 0; |
690 | } |
691 | |
692 | static int rtk_phy_disconnect(struct phy *phy, int port) |
693 | { |
694 | struct rtk_phy *rtk_phy = phy_get_drvdata(phy); |
695 | |
696 | dev_dbg(rtk_phy->dev, "%s port=%d\n" , __func__, port); |
697 | rtk_phy_toggle(rtk_phy, connect: false, port); |
698 | |
699 | return 0; |
700 | } |
701 | |
702 | static const struct phy_ops ops = { |
703 | .init = rtk_phy_init, |
704 | .exit = rtk_phy_exit, |
705 | .connect = rtk_phy_connect, |
706 | .disconnect = rtk_phy_disconnect, |
707 | .owner = THIS_MODULE, |
708 | }; |
709 | |
710 | #ifdef CONFIG_DEBUG_FS |
711 | static struct dentry *create_phy_debug_root(void) |
712 | { |
713 | struct dentry *phy_debug_root; |
714 | |
715 | phy_debug_root = debugfs_lookup(name: "phy" , parent: usb_debug_root); |
716 | if (!phy_debug_root) |
717 | phy_debug_root = debugfs_create_dir(name: "phy" , parent: usb_debug_root); |
718 | |
719 | return phy_debug_root; |
720 | } |
721 | |
722 | static int rtk_usb2_parameter_show(struct seq_file *s, void *unused) |
723 | { |
724 | struct rtk_phy *rtk_phy = s->private; |
725 | struct phy_cfg *phy_cfg; |
726 | int i, index; |
727 | |
728 | phy_cfg = rtk_phy->phy_cfg; |
729 | |
730 | seq_puts(m: s, s: "Property:\n" ); |
731 | seq_printf(m: s, fmt: " check_efuse: %s\n" , |
732 | phy_cfg->check_efuse ? "Enable" : "Disable" ); |
733 | seq_printf(m: s, fmt: " check_efuse_version: %d\n" , |
734 | phy_cfg->check_efuse_version); |
735 | seq_printf(m: s, fmt: " efuse_dc_driving_rate: %d\n" , |
736 | phy_cfg->efuse_dc_driving_rate); |
737 | seq_printf(m: s, fmt: " dc_driving_mask: 0x%x\n" , |
738 | phy_cfg->dc_driving_mask); |
739 | seq_printf(m: s, fmt: " efuse_dc_disconnect_rate: %d\n" , |
740 | phy_cfg->efuse_dc_disconnect_rate); |
741 | seq_printf(m: s, fmt: " dc_disconnect_mask: 0x%x\n" , |
742 | phy_cfg->dc_disconnect_mask); |
743 | seq_printf(m: s, fmt: " usb_dc_disconnect_at_page0: %s\n" , |
744 | phy_cfg->usb_dc_disconnect_at_page0 ? "true" : "false" ); |
745 | seq_printf(m: s, fmt: " do_toggle: %s\n" , |
746 | phy_cfg->do_toggle ? "Enable" : "Disable" ); |
747 | seq_printf(m: s, fmt: " do_toggle_driving: %s\n" , |
748 | phy_cfg->do_toggle_driving ? "Enable" : "Disable" ); |
749 | seq_printf(m: s, fmt: " driving_updated_for_dev_dis: 0x%x\n" , |
750 | phy_cfg->driving_updated_for_dev_dis); |
751 | seq_printf(m: s, fmt: " use_default_parameter: %s\n" , |
752 | phy_cfg->use_default_parameter ? "Enable" : "Disable" ); |
753 | seq_printf(m: s, fmt: " is_double_sensitivity_mode: %s\n" , |
754 | phy_cfg->is_double_sensitivity_mode ? "Enable" : "Disable" ); |
755 | |
756 | for (index = 0; index < rtk_phy->num_phy; index++) { |
757 | struct phy_parameter *phy_parameter; |
758 | struct phy_reg *phy_reg; |
759 | struct phy_data *phy_data_page; |
760 | |
761 | phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; |
762 | phy_reg = &phy_parameter->phy_reg; |
763 | |
764 | seq_printf(m: s, fmt: "PHY %d:\n" , index); |
765 | |
766 | seq_puts(m: s, s: "Page 0:\n" ); |
767 | /* Set page 0 */ |
768 | phy_data_page = phy_cfg->page0; |
769 | rtk_phy_set_page(phy_reg, page: 0); |
770 | |
771 | for (i = 0; i < phy_cfg->page0_size; i++) { |
772 | struct phy_data *phy_data = phy_data_page + i; |
773 | u8 addr = array_index_to_page_addr(index: i); |
774 | u8 data = phy_data->data; |
775 | u8 value = rtk_phy_read(phy_reg, addr); |
776 | |
777 | if (phy_data->addr) |
778 | seq_printf(m: s, fmt: " Page 0: addr=0x%x data=0x%02x ==> read value=0x%02x\n" , |
779 | addr, data, value); |
780 | else |
781 | seq_printf(m: s, fmt: " Page 0: addr=0x%x data=none ==> read value=0x%02x\n" , |
782 | addr, value); |
783 | } |
784 | |
785 | seq_puts(m: s, s: "Page 1:\n" ); |
786 | /* Set page 1 */ |
787 | phy_data_page = phy_cfg->page1; |
788 | rtk_phy_set_page(phy_reg, page: 1); |
789 | |
790 | for (i = 0; i < phy_cfg->page1_size; i++) { |
791 | struct phy_data *phy_data = phy_data_page + i; |
792 | u8 addr = array_index_to_page_addr(index: i); |
793 | u8 data = phy_data->data; |
794 | u8 value = rtk_phy_read(phy_reg, addr); |
795 | |
796 | if (phy_data->addr) |
797 | seq_printf(m: s, fmt: " Page 1: addr=0x%x data=0x%02x ==> read value=0x%02x\n" , |
798 | addr, data, value); |
799 | else |
800 | seq_printf(m: s, fmt: " Page 1: addr=0x%x data=none ==> read value=0x%02x\n" , |
801 | addr, value); |
802 | } |
803 | |
804 | if (phy_cfg->page2_size == 0) |
805 | goto out; |
806 | |
807 | seq_puts(m: s, s: "Page 2:\n" ); |
808 | /* Set page 2 */ |
809 | phy_data_page = phy_cfg->page2; |
810 | rtk_phy_set_page(phy_reg, page: 2); |
811 | |
812 | for (i = 0; i < phy_cfg->page2_size; i++) { |
813 | struct phy_data *phy_data = phy_data_page + i; |
814 | u8 addr = array_index_to_page_addr(index: i); |
815 | u8 data = phy_data->data; |
816 | u8 value = rtk_phy_read(phy_reg, addr); |
817 | |
818 | if (phy_data->addr) |
819 | seq_printf(m: s, fmt: " Page 2: addr=0x%x data=0x%02x ==> read value=0x%02x\n" , |
820 | addr, data, value); |
821 | else |
822 | seq_printf(m: s, fmt: " Page 2: addr=0x%x data=none ==> read value=0x%02x\n" , |
823 | addr, value); |
824 | } |
825 | |
826 | out: |
827 | seq_puts(m: s, s: "PHY Property:\n" ); |
828 | seq_printf(m: s, fmt: " efuse_usb_dc_cal: %d\n" , |
829 | (int)phy_parameter->efuse_usb_dc_cal); |
830 | seq_printf(m: s, fmt: " efuse_usb_dc_dis: %d\n" , |
831 | (int)phy_parameter->efuse_usb_dc_dis); |
832 | seq_printf(m: s, fmt: " inverse_hstx_sync_clock: %s\n" , |
833 | phy_parameter->inverse_hstx_sync_clock ? "Enable" : "Disable" ); |
834 | seq_printf(m: s, fmt: " driving_level: %d\n" , |
835 | phy_parameter->driving_level); |
836 | seq_printf(m: s, fmt: " driving_level_compensate: %d\n" , |
837 | phy_parameter->driving_level_compensate); |
838 | seq_printf(m: s, fmt: " disconnection_compensate: %d\n" , |
839 | phy_parameter->disconnection_compensate); |
840 | } |
841 | |
842 | return 0; |
843 | } |
844 | DEFINE_SHOW_ATTRIBUTE(rtk_usb2_parameter); |
845 | |
846 | static inline void create_debug_files(struct rtk_phy *rtk_phy) |
847 | { |
848 | struct dentry *phy_debug_root = NULL; |
849 | |
850 | phy_debug_root = create_phy_debug_root(); |
851 | if (!phy_debug_root) |
852 | return; |
853 | |
854 | rtk_phy->debug_dir = debugfs_create_dir(name: dev_name(dev: rtk_phy->dev), |
855 | parent: phy_debug_root); |
856 | |
857 | debugfs_create_file(name: "parameter" , mode: 0444, parent: rtk_phy->debug_dir, data: rtk_phy, |
858 | fops: &rtk_usb2_parameter_fops); |
859 | } |
860 | |
861 | static inline void remove_debug_files(struct rtk_phy *rtk_phy) |
862 | { |
863 | debugfs_remove_recursive(dentry: rtk_phy->debug_dir); |
864 | } |
865 | #else |
866 | static inline void create_debug_files(struct rtk_phy *rtk_phy) { } |
867 | static inline void remove_debug_files(struct rtk_phy *rtk_phy) { } |
868 | #endif /* CONFIG_DEBUG_FS */ |
869 | |
870 | static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy, |
871 | struct phy_parameter *phy_parameter, int index) |
872 | { |
873 | struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; |
874 | u8 value = 0; |
875 | struct nvmem_cell *cell; |
876 | struct soc_device_attribute rtk_soc_groot[] = { |
877 | { .family = "Realtek Groot" ,}, |
878 | { /* empty */ } }; |
879 | |
880 | if (!phy_cfg->check_efuse) |
881 | goto out; |
882 | |
883 | /* Read efuse for usb dc cal */ |
884 | cell = nvmem_cell_get(dev: rtk_phy->dev, id: "usb-dc-cal" ); |
885 | if (IS_ERR(ptr: cell)) { |
886 | dev_dbg(rtk_phy->dev, "%s no usb-dc-cal: %ld\n" , |
887 | __func__, PTR_ERR(cell)); |
888 | } else { |
889 | unsigned char *buf; |
890 | size_t buf_size; |
891 | |
892 | buf = nvmem_cell_read(cell, len: &buf_size); |
893 | if (!IS_ERR(ptr: buf)) { |
894 | value = buf[0] & phy_cfg->dc_driving_mask; |
895 | kfree(objp: buf); |
896 | } |
897 | nvmem_cell_put(cell); |
898 | } |
899 | |
900 | if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { |
901 | int rate = phy_cfg->efuse_dc_driving_rate; |
902 | |
903 | if (value <= EFUS_USB_DC_CAL_MAX) |
904 | phy_parameter->efuse_usb_dc_cal = (int8_t)(value * rate); |
905 | else |
906 | phy_parameter->efuse_usb_dc_cal = -(int8_t) |
907 | ((EFUS_USB_DC_CAL_MAX & value) * rate); |
908 | |
909 | if (soc_device_match(matches: rtk_soc_groot)) { |
910 | dev_dbg(rtk_phy->dev, "For groot IC we need a workaround to adjust efuse_usb_dc_cal\n" ); |
911 | |
912 | /* We don't multiple dc_cal_rate=2 for positive dc cal compensate */ |
913 | if (value <= EFUS_USB_DC_CAL_MAX) |
914 | phy_parameter->efuse_usb_dc_cal = (int8_t)(value); |
915 | |
916 | /* We set max dc cal compensate is 0x8 if otp is 0x7 */ |
917 | if (value == 0x7) |
918 | phy_parameter->efuse_usb_dc_cal = (int8_t)(value + 1); |
919 | } |
920 | } else { /* for CHECK_EFUSE_V2 */ |
921 | phy_parameter->efuse_usb_dc_cal = value & phy_cfg->dc_driving_mask; |
922 | } |
923 | |
924 | /* Read efuse for usb dc disconnect level */ |
925 | value = 0; |
926 | cell = nvmem_cell_get(dev: rtk_phy->dev, id: "usb-dc-dis" ); |
927 | if (IS_ERR(ptr: cell)) { |
928 | dev_dbg(rtk_phy->dev, "%s no usb-dc-dis: %ld\n" , |
929 | __func__, PTR_ERR(cell)); |
930 | } else { |
931 | unsigned char *buf; |
932 | size_t buf_size; |
933 | |
934 | buf = nvmem_cell_read(cell, len: &buf_size); |
935 | if (!IS_ERR(ptr: buf)) { |
936 | value = buf[0] & phy_cfg->dc_disconnect_mask; |
937 | kfree(objp: buf); |
938 | } |
939 | nvmem_cell_put(cell); |
940 | } |
941 | |
942 | if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { |
943 | int rate = phy_cfg->efuse_dc_disconnect_rate; |
944 | |
945 | if (value <= EFUS_USB_DC_DIS_MAX) |
946 | phy_parameter->efuse_usb_dc_dis = (int8_t)(value * rate); |
947 | else |
948 | phy_parameter->efuse_usb_dc_dis = -(int8_t) |
949 | ((EFUS_USB_DC_DIS_MAX & value) * rate); |
950 | } else { /* for CHECK_EFUSE_V2 */ |
951 | phy_parameter->efuse_usb_dc_dis = value & phy_cfg->dc_disconnect_mask; |
952 | } |
953 | |
954 | out: |
955 | return 0; |
956 | } |
957 | |
958 | static int parse_phy_data(struct rtk_phy *rtk_phy) |
959 | { |
960 | struct device *dev = rtk_phy->dev; |
961 | struct device_node *np = dev->of_node; |
962 | struct phy_parameter *phy_parameter; |
963 | int ret = 0; |
964 | int index; |
965 | |
966 | rtk_phy->phy_parameter = devm_kzalloc(dev, size: sizeof(struct phy_parameter) * |
967 | rtk_phy->num_phy, GFP_KERNEL); |
968 | if (!rtk_phy->phy_parameter) |
969 | return -ENOMEM; |
970 | |
971 | for (index = 0; index < rtk_phy->num_phy; index++) { |
972 | phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; |
973 | |
974 | phy_parameter->phy_reg.reg_wrap_vstatus = of_iomap(node: np, index: 0); |
975 | phy_parameter->phy_reg.reg_gusb2phyacc0 = of_iomap(node: np, index: 1) + index; |
976 | phy_parameter->phy_reg.vstatus_index = index; |
977 | |
978 | if (of_property_read_bool(np, propname: "realtek,inverse-hstx-sync-clock" )) |
979 | phy_parameter->inverse_hstx_sync_clock = true; |
980 | else |
981 | phy_parameter->inverse_hstx_sync_clock = false; |
982 | |
983 | if (of_property_read_u32_index(np, propname: "realtek,driving-level" , |
984 | index, out_value: &phy_parameter->driving_level)) |
985 | phy_parameter->driving_level = DEFAULT_DC_DRIVING_VALUE; |
986 | |
987 | if (of_property_read_u32_index(np, propname: "realtek,driving-level-compensate" , |
988 | index, out_value: &phy_parameter->driving_level_compensate)) |
989 | phy_parameter->driving_level_compensate = 0; |
990 | |
991 | if (of_property_read_u32_index(np, propname: "realtek,disconnection-compensate" , |
992 | index, out_value: &phy_parameter->disconnection_compensate)) |
993 | phy_parameter->disconnection_compensate = 0; |
994 | |
995 | get_phy_data_by_efuse(rtk_phy, phy_parameter, index); |
996 | |
997 | update_dc_driving_level(rtk_phy, phy_parameter); |
998 | |
999 | update_hs_clk_select(rtk_phy, phy_parameter); |
1000 | } |
1001 | |
1002 | return ret; |
1003 | } |
1004 | |
1005 | static int rtk_usb2phy_probe(struct platform_device *pdev) |
1006 | { |
1007 | struct rtk_phy *rtk_phy; |
1008 | struct device *dev = &pdev->dev; |
1009 | struct phy *generic_phy; |
1010 | struct phy_provider *phy_provider; |
1011 | const struct phy_cfg *phy_cfg; |
1012 | int ret = 0; |
1013 | |
1014 | phy_cfg = of_device_get_match_data(dev); |
1015 | if (!phy_cfg) { |
1016 | dev_err(dev, "phy config are not assigned!\n" ); |
1017 | return -EINVAL; |
1018 | } |
1019 | |
1020 | rtk_phy = devm_kzalloc(dev, size: sizeof(*rtk_phy), GFP_KERNEL); |
1021 | if (!rtk_phy) |
1022 | return -ENOMEM; |
1023 | |
1024 | rtk_phy->dev = &pdev->dev; |
1025 | rtk_phy->phy_cfg = devm_kzalloc(dev, size: sizeof(*phy_cfg), GFP_KERNEL); |
1026 | |
1027 | memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); |
1028 | |
1029 | rtk_phy->num_phy = phy_cfg->num_phy; |
1030 | |
1031 | ret = parse_phy_data(rtk_phy); |
1032 | if (ret) |
1033 | goto err; |
1034 | |
1035 | platform_set_drvdata(pdev, data: rtk_phy); |
1036 | |
1037 | generic_phy = devm_phy_create(dev: rtk_phy->dev, NULL, ops: &ops); |
1038 | if (IS_ERR(ptr: generic_phy)) |
1039 | return PTR_ERR(ptr: generic_phy); |
1040 | |
1041 | phy_set_drvdata(phy: generic_phy, data: rtk_phy); |
1042 | |
1043 | phy_provider = devm_of_phy_provider_register(rtk_phy->dev, |
1044 | of_phy_simple_xlate); |
1045 | if (IS_ERR(ptr: phy_provider)) |
1046 | return PTR_ERR(ptr: phy_provider); |
1047 | |
1048 | create_debug_files(rtk_phy); |
1049 | |
1050 | err: |
1051 | return ret; |
1052 | } |
1053 | |
1054 | static void rtk_usb2phy_remove(struct platform_device *pdev) |
1055 | { |
1056 | struct rtk_phy *rtk_phy = platform_get_drvdata(pdev); |
1057 | |
1058 | remove_debug_files(rtk_phy); |
1059 | } |
1060 | |
1061 | static const struct phy_cfg rtd1295_phy_cfg = { |
1062 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1063 | .page0 = { [0] = {0xe0, 0x90}, |
1064 | [3] = {0xe3, 0x3a}, |
1065 | [4] = {0xe4, 0x68}, |
1066 | [6] = {0xe6, 0x91}, |
1067 | [13] = {0xf5, 0x81}, |
1068 | [15] = {0xf7, 0x02}, }, |
1069 | .page1_size = 8, |
1070 | .page1 = { /* default parameter */ }, |
1071 | .page2_size = 0, |
1072 | .page2 = { /* no parameter */ }, |
1073 | .num_phy = 1, |
1074 | .check_efuse = false, |
1075 | .check_efuse_version = CHECK_EFUSE_V1, |
1076 | .efuse_dc_driving_rate = 1, |
1077 | .dc_driving_mask = 0xf, |
1078 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1079 | .dc_disconnect_mask = 0xf, |
1080 | .usb_dc_disconnect_at_page0 = true, |
1081 | .do_toggle = true, |
1082 | .do_toggle_driving = false, |
1083 | .driving_updated_for_dev_dis = 0xf, |
1084 | .use_default_parameter = false, |
1085 | .is_double_sensitivity_mode = false, |
1086 | }; |
1087 | |
1088 | static const struct phy_cfg rtd1395_phy_cfg = { |
1089 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1090 | .page0 = { [4] = {0xe4, 0xac}, |
1091 | [13] = {0xf5, 0x00}, |
1092 | [15] = {0xf7, 0x02}, }, |
1093 | .page1_size = 8, |
1094 | .page1 = { /* default parameter */ }, |
1095 | .page2_size = 0, |
1096 | .page2 = { /* no parameter */ }, |
1097 | .num_phy = 1, |
1098 | .check_efuse = false, |
1099 | .check_efuse_version = CHECK_EFUSE_V1, |
1100 | .efuse_dc_driving_rate = 1, |
1101 | .dc_driving_mask = 0xf, |
1102 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1103 | .dc_disconnect_mask = 0xf, |
1104 | .usb_dc_disconnect_at_page0 = true, |
1105 | .do_toggle = true, |
1106 | .do_toggle_driving = false, |
1107 | .driving_updated_for_dev_dis = 0xf, |
1108 | .use_default_parameter = false, |
1109 | .is_double_sensitivity_mode = false, |
1110 | }; |
1111 | |
1112 | static const struct phy_cfg rtd1395_phy_cfg_2port = { |
1113 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1114 | .page0 = { [4] = {0xe4, 0xac}, |
1115 | [13] = {0xf5, 0x00}, |
1116 | [15] = {0xf7, 0x02}, }, |
1117 | .page1_size = 8, |
1118 | .page1 = { /* default parameter */ }, |
1119 | .page2_size = 0, |
1120 | .page2 = { /* no parameter */ }, |
1121 | .num_phy = 2, |
1122 | .check_efuse = false, |
1123 | .check_efuse_version = CHECK_EFUSE_V1, |
1124 | .efuse_dc_driving_rate = 1, |
1125 | .dc_driving_mask = 0xf, |
1126 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1127 | .dc_disconnect_mask = 0xf, |
1128 | .usb_dc_disconnect_at_page0 = true, |
1129 | .do_toggle = true, |
1130 | .do_toggle_driving = false, |
1131 | .driving_updated_for_dev_dis = 0xf, |
1132 | .use_default_parameter = false, |
1133 | .is_double_sensitivity_mode = false, |
1134 | }; |
1135 | |
1136 | static const struct phy_cfg rtd1619_phy_cfg = { |
1137 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1138 | .page0 = { [4] = {0xe4, 0x68}, }, |
1139 | .page1_size = 8, |
1140 | .page1 = { /* default parameter */ }, |
1141 | .page2_size = 0, |
1142 | .page2 = { /* no parameter */ }, |
1143 | .num_phy = 1, |
1144 | .check_efuse = true, |
1145 | .check_efuse_version = CHECK_EFUSE_V1, |
1146 | .efuse_dc_driving_rate = 1, |
1147 | .dc_driving_mask = 0xf, |
1148 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1149 | .dc_disconnect_mask = 0xf, |
1150 | .usb_dc_disconnect_at_page0 = true, |
1151 | .do_toggle = true, |
1152 | .do_toggle_driving = false, |
1153 | .driving_updated_for_dev_dis = 0xf, |
1154 | .use_default_parameter = false, |
1155 | .is_double_sensitivity_mode = false, |
1156 | }; |
1157 | |
1158 | static const struct phy_cfg rtd1319_phy_cfg = { |
1159 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1160 | .page0 = { [0] = {0xe0, 0x18}, |
1161 | [4] = {0xe4, 0x6a}, |
1162 | [7] = {0xe7, 0x71}, |
1163 | [13] = {0xf5, 0x15}, |
1164 | [15] = {0xf7, 0x32}, }, |
1165 | .page1_size = 8, |
1166 | .page1 = { [3] = {0xe3, 0x44}, }, |
1167 | .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, |
1168 | .page2 = { [0] = {0xe0, 0x01}, }, |
1169 | .num_phy = 1, |
1170 | .check_efuse = true, |
1171 | .check_efuse_version = CHECK_EFUSE_V1, |
1172 | .efuse_dc_driving_rate = 1, |
1173 | .dc_driving_mask = 0xf, |
1174 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1175 | .dc_disconnect_mask = 0xf, |
1176 | .usb_dc_disconnect_at_page0 = true, |
1177 | .do_toggle = true, |
1178 | .do_toggle_driving = true, |
1179 | .driving_updated_for_dev_dis = 0xf, |
1180 | .use_default_parameter = false, |
1181 | .is_double_sensitivity_mode = true, |
1182 | }; |
1183 | |
1184 | static const struct phy_cfg rtd1312c_phy_cfg = { |
1185 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1186 | .page0 = { [0] = {0xe0, 0x14}, |
1187 | [4] = {0xe4, 0x67}, |
1188 | [5] = {0xe5, 0x55}, }, |
1189 | .page1_size = 8, |
1190 | .page1 = { [3] = {0xe3, 0x23}, |
1191 | [6] = {0xe6, 0x58}, }, |
1192 | .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, |
1193 | .page2 = { /* default parameter */ }, |
1194 | .num_phy = 1, |
1195 | .check_efuse = true, |
1196 | .check_efuse_version = CHECK_EFUSE_V1, |
1197 | .efuse_dc_driving_rate = 1, |
1198 | .dc_driving_mask = 0xf, |
1199 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1200 | .dc_disconnect_mask = 0xf, |
1201 | .usb_dc_disconnect_at_page0 = true, |
1202 | .do_toggle = true, |
1203 | .do_toggle_driving = true, |
1204 | .driving_updated_for_dev_dis = 0xf, |
1205 | .use_default_parameter = false, |
1206 | .is_double_sensitivity_mode = true, |
1207 | }; |
1208 | |
1209 | static const struct phy_cfg rtd1619b_phy_cfg = { |
1210 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1211 | .page0 = { [0] = {0xe0, 0xa3}, |
1212 | [4] = {0xe4, 0xa8}, |
1213 | [5] = {0xe5, 0x4f}, |
1214 | [6] = {0xe6, 0x02}, }, |
1215 | .page1_size = 8, |
1216 | .page1 = { [3] = {0xe3, 0x64}, }, |
1217 | .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, |
1218 | .page2 = { [7] = {0xe7, 0x45}, }, |
1219 | .num_phy = 1, |
1220 | .check_efuse = true, |
1221 | .check_efuse_version = CHECK_EFUSE_V1, |
1222 | .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, |
1223 | .dc_driving_mask = 0x1f, |
1224 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1225 | .dc_disconnect_mask = 0xf, |
1226 | .usb_dc_disconnect_at_page0 = false, |
1227 | .do_toggle = true, |
1228 | .do_toggle_driving = true, |
1229 | .driving_updated_for_dev_dis = 0x8, |
1230 | .use_default_parameter = false, |
1231 | .is_double_sensitivity_mode = true, |
1232 | }; |
1233 | |
1234 | static const struct phy_cfg rtd1319d_phy_cfg = { |
1235 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1236 | .page0 = { [0] = {0xe0, 0xa3}, |
1237 | [4] = {0xe4, 0x8e}, |
1238 | [5] = {0xe5, 0x4f}, |
1239 | [6] = {0xe6, 0x02}, }, |
1240 | .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE, |
1241 | .page1 = { [14] = {0xf5, 0x1}, }, |
1242 | .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, |
1243 | .page2 = { [7] = {0xe7, 0x44}, }, |
1244 | .check_efuse = true, |
1245 | .num_phy = 1, |
1246 | .check_efuse_version = CHECK_EFUSE_V1, |
1247 | .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, |
1248 | .dc_driving_mask = 0x1f, |
1249 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1250 | .dc_disconnect_mask = 0xf, |
1251 | .usb_dc_disconnect_at_page0 = false, |
1252 | .do_toggle = true, |
1253 | .do_toggle_driving = false, |
1254 | .driving_updated_for_dev_dis = 0x8, |
1255 | .use_default_parameter = false, |
1256 | .is_double_sensitivity_mode = true, |
1257 | }; |
1258 | |
1259 | static const struct phy_cfg rtd1315e_phy_cfg = { |
1260 | .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, |
1261 | .page0 = { [0] = {0xe0, 0xa3}, |
1262 | [4] = {0xe4, 0x8c}, |
1263 | [5] = {0xe5, 0x4f}, |
1264 | [6] = {0xe6, 0x02}, }, |
1265 | .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE, |
1266 | .page1 = { [3] = {0xe3, 0x7f}, |
1267 | [14] = {0xf5, 0x01}, }, |
1268 | .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, |
1269 | .page2 = { [7] = {0xe7, 0x44}, }, |
1270 | .num_phy = 1, |
1271 | .check_efuse = true, |
1272 | .check_efuse_version = CHECK_EFUSE_V2, |
1273 | .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, |
1274 | .dc_driving_mask = 0x1f, |
1275 | .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, |
1276 | .dc_disconnect_mask = 0xf, |
1277 | .usb_dc_disconnect_at_page0 = false, |
1278 | .do_toggle = true, |
1279 | .do_toggle_driving = false, |
1280 | .driving_updated_for_dev_dis = 0x8, |
1281 | .use_default_parameter = false, |
1282 | .is_double_sensitivity_mode = true, |
1283 | }; |
1284 | |
1285 | static const struct of_device_id usbphy_rtk_dt_match[] = { |
1286 | { .compatible = "realtek,rtd1295-usb2phy" , .data = &rtd1295_phy_cfg }, |
1287 | { .compatible = "realtek,rtd1312c-usb2phy" , .data = &rtd1312c_phy_cfg }, |
1288 | { .compatible = "realtek,rtd1315e-usb2phy" , .data = &rtd1315e_phy_cfg }, |
1289 | { .compatible = "realtek,rtd1319-usb2phy" , .data = &rtd1319_phy_cfg }, |
1290 | { .compatible = "realtek,rtd1319d-usb2phy" , .data = &rtd1319d_phy_cfg }, |
1291 | { .compatible = "realtek,rtd1395-usb2phy" , .data = &rtd1395_phy_cfg }, |
1292 | { .compatible = "realtek,rtd1395-usb2phy-2port" , .data = &rtd1395_phy_cfg_2port }, |
1293 | { .compatible = "realtek,rtd1619-usb2phy" , .data = &rtd1619_phy_cfg }, |
1294 | { .compatible = "realtek,rtd1619b-usb2phy" , .data = &rtd1619b_phy_cfg }, |
1295 | {}, |
1296 | }; |
1297 | MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match); |
1298 | |
1299 | static struct platform_driver rtk_usb2phy_driver = { |
1300 | .probe = rtk_usb2phy_probe, |
1301 | .remove_new = rtk_usb2phy_remove, |
1302 | .driver = { |
1303 | .name = "rtk-usb2phy" , |
1304 | .of_match_table = usbphy_rtk_dt_match, |
1305 | }, |
1306 | }; |
1307 | |
1308 | module_platform_driver(rtk_usb2phy_driver); |
1309 | |
1310 | MODULE_LICENSE("GPL" ); |
1311 | MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>" ); |
1312 | MODULE_DESCRIPTION("Realtek usb 2.0 phy driver" ); |
1313 | |