1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Rockchip USB2.0 PHY with Innosilicon IP block driver
4 *
5 * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
6 * Copyright (C) 2020 Amarula Solutions(India)
7 */
8
9 #include <clk-uclass.h>
10 #include <dm.h>
11 #include <dm/device_compat.h>
12 #include <dm/device-internal.h>
13 #include <dm/lists.h>
14 #include <generic-phy.h>
15 #include <regmap.h>
16 #include <syscon.h>
17 #include <asm/arch-rockchip/clock.h>
18
19 #define usleep_range(a, b) udelay((b))
20 #define BIT_WRITEABLE_SHIFT 16
21
22 enum rockchip_usb2phy_port_id {
23 USB2PHY_PORT_OTG,
24 USB2PHY_PORT_HOST,
25 USB2PHY_NUM_PORTS,
26 };
27
28 struct usb2phy_reg {
29 unsigned int offset;
30 unsigned int bitend;
31 unsigned int bitstart;
32 unsigned int disable;
33 unsigned int enable;
34 };
35
36 struct rockchip_usb2phy_port_cfg {
37 struct usb2phy_reg phy_sus;
38 };
39
40 struct rockchip_usb2phy_cfg {
41 unsigned int reg;
42 struct usb2phy_reg clkout_ctl;
43 struct usb2phy_reg clkout_ctl_phy;
44 const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS];
45 };
46
47 struct rockchip_usb2phy {
48 struct regmap *reg_base;
49 struct regmap *phy_base;
50 struct clk phyclk;
51 const struct rockchip_usb2phy_cfg *phy_cfg;
52 };
53
property_enable(struct regmap * base,const struct usb2phy_reg * reg,bool en)54 static inline int property_enable(struct regmap *base,
55 const struct usb2phy_reg *reg, bool en)
56 {
57 unsigned int val, mask, tmp;
58
59 if (!reg->offset && !reg->enable && !reg->disable)
60 return 0;
61
62 tmp = en ? reg->enable : reg->disable;
63 mask = GENMASK(reg->bitend, reg->bitstart);
64 val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
65
66 return regmap_write(base, reg->offset, val);
67 }
68
property_enabled(struct regmap * base,const struct usb2phy_reg * reg)69 static inline bool property_enabled(struct regmap *base,
70 const struct usb2phy_reg *reg)
71 {
72 int ret;
73 unsigned int tmp, orig;
74 unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
75
76 if (!reg->offset && !reg->enable && !reg->disable)
77 return false;
78
79 ret = regmap_read(base, reg->offset, &orig);
80 if (ret)
81 return false;
82
83 tmp = (orig & mask) >> reg->bitstart;
84 return tmp != reg->disable;
85 }
86
87 static const
us2phy_get_port(struct phy * phy)88 struct rockchip_usb2phy_port_cfg *us2phy_get_port(struct phy *phy)
89 {
90 struct udevice *parent = dev_get_parent(phy->dev);
91 struct rockchip_usb2phy *priv = dev_get_priv(parent);
92 const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg;
93
94 return &phy_cfg->port_cfgs[phy->id];
95 }
96
rockchip_usb2phy_power_on(struct phy * phy)97 static int rockchip_usb2phy_power_on(struct phy *phy)
98 {
99 struct udevice *parent = dev_get_parent(phy->dev);
100 struct rockchip_usb2phy *priv = dev_get_priv(parent);
101 const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy);
102
103 property_enable(priv->reg_base, &port_cfg->phy_sus, false);
104
105 /* waiting for the utmi_clk to become stable */
106 usleep_range(1500, 2000);
107
108 return 0;
109 }
110
rockchip_usb2phy_power_off(struct phy * phy)111 static int rockchip_usb2phy_power_off(struct phy *phy)
112 {
113 struct udevice *parent = dev_get_parent(phy->dev);
114 struct rockchip_usb2phy *priv = dev_get_priv(parent);
115 const struct rockchip_usb2phy_port_cfg *port_cfg = us2phy_get_port(phy);
116
117 property_enable(priv->reg_base, &port_cfg->phy_sus, true);
118
119 return 0;
120 }
121
rockchip_usb2phy_init(struct phy * phy)122 static int rockchip_usb2phy_init(struct phy *phy)
123 {
124 struct udevice *parent = dev_get_parent(phy->dev);
125 struct rockchip_usb2phy *priv = dev_get_priv(parent);
126 int ret;
127
128 ret = clk_enable(&priv->phyclk);
129 if (ret && ret != -ENOSYS) {
130 dev_err(phy->dev, "failed to enable phyclk (ret=%d)\n", ret);
131 return ret;
132 }
133
134 return 0;
135 }
136
rockchip_usb2phy_exit(struct phy * phy)137 static int rockchip_usb2phy_exit(struct phy *phy)
138 {
139 struct udevice *parent = dev_get_parent(phy->dev);
140 struct rockchip_usb2phy *priv = dev_get_priv(parent);
141
142 clk_disable(&priv->phyclk);
143
144 return 0;
145 }
146
rockchip_usb2phy_of_xlate(struct phy * phy,struct ofnode_phandle_args * args)147 static int rockchip_usb2phy_of_xlate(struct phy *phy,
148 struct ofnode_phandle_args *args)
149 {
150 const char *name = phy->dev->name;
151
152 if (!strcasecmp(name, "host-port"))
153 phy->id = USB2PHY_PORT_HOST;
154 else if (!strcasecmp(name, "otg-port"))
155 phy->id = USB2PHY_PORT_OTG;
156 else
157 dev_err(phy->dev, "improper %s device\n", name);
158
159 return 0;
160 }
161
162 static struct phy_ops rockchip_usb2phy_ops = {
163 .init = rockchip_usb2phy_init,
164 .exit = rockchip_usb2phy_exit,
165 .power_on = rockchip_usb2phy_power_on,
166 .power_off = rockchip_usb2phy_power_off,
167 .of_xlate = rockchip_usb2phy_of_xlate,
168 };
169
rockchip_usb2phy_clkout_ctl(struct clk * clk,struct regmap ** base,const struct usb2phy_reg ** clkout_ctl)170 static void rockchip_usb2phy_clkout_ctl(struct clk *clk, struct regmap **base,
171 const struct usb2phy_reg **clkout_ctl)
172 {
173 struct udevice *parent = dev_get_parent(clk->dev);
174 struct rockchip_usb2phy *priv = dev_get_priv(parent);
175 const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg;
176
177 if (priv->phy_cfg->clkout_ctl_phy.enable) {
178 *base = priv->phy_base;
179 *clkout_ctl = &phy_cfg->clkout_ctl_phy;
180 } else {
181 *base = priv->reg_base;
182 *clkout_ctl = &phy_cfg->clkout_ctl;
183 }
184 }
185
186 /**
187 * round_rate() - Adjust a rate to the exact rate a clock can provide.
188 * @clk: The clock to manipulate.
189 * @rate: Desidered clock rate in Hz.
190 *
191 * Return: rounded rate in Hz, or -ve error code.
192 */
rockchip_usb2phy_clk_round_rate(struct clk * clk,ulong rate)193 ulong rockchip_usb2phy_clk_round_rate(struct clk *clk, ulong rate)
194 {
195 return 480000000;
196 }
197
198 /**
199 * enable() - Enable a clock.
200 * @clk: The clock to manipulate.
201 *
202 * Return: zero on success, or -ve error code.
203 */
rockchip_usb2phy_clk_enable(struct clk * clk)204 int rockchip_usb2phy_clk_enable(struct clk *clk)
205 {
206 const struct usb2phy_reg *clkout_ctl;
207 struct regmap *base;
208
209 rockchip_usb2phy_clkout_ctl(clk, &base, &clkout_ctl);
210
211 /* turn on 480m clk output if it is off */
212 if (!property_enabled(base, clkout_ctl)) {
213 property_enable(base, clkout_ctl, true);
214
215 /* waiting for the clk become stable */
216 usleep_range(1200, 1300);
217 }
218
219 return 0;
220 }
221
222 /**
223 * disable() - Disable a clock.
224 * @clk: The clock to manipulate.
225 *
226 * Return: zero on success, or -ve error code.
227 */
rockchip_usb2phy_clk_disable(struct clk * clk)228 int rockchip_usb2phy_clk_disable(struct clk *clk)
229 {
230 const struct usb2phy_reg *clkout_ctl;
231 struct regmap *base;
232
233 rockchip_usb2phy_clkout_ctl(clk, &base, &clkout_ctl);
234
235 /* turn off 480m clk output */
236 property_enable(base, clkout_ctl, false);
237
238 return 0;
239 }
240
241 static struct clk_ops rockchip_usb2phy_clk_ops = {
242 .enable = rockchip_usb2phy_clk_enable,
243 .disable = rockchip_usb2phy_clk_disable,
244 .round_rate = rockchip_usb2phy_clk_round_rate
245 };
246
rockchip_usb2phy_probe(struct udevice * dev)247 static int rockchip_usb2phy_probe(struct udevice *dev)
248 {
249 struct rockchip_usb2phy *priv = dev_get_priv(dev);
250 const struct rockchip_usb2phy_cfg *phy_cfgs;
251 unsigned int reg;
252 int index, ret;
253
254 if (dev_read_bool(dev, "rockchip,usbgrf"))
255 priv->reg_base =
256 syscon_regmap_lookup_by_phandle(dev, "rockchip,usbgrf");
257 else
258 priv->reg_base = syscon_get_regmap(dev_get_parent(dev));
259 if (IS_ERR(priv->reg_base))
260 return PTR_ERR(priv->reg_base);
261
262 ret = ofnode_read_u32_index(dev_ofnode(dev), "reg", 0, ®);
263 if (ret) {
264 dev_err(dev, "failed to read reg property (ret = %d)\n", ret);
265 return ret;
266 }
267
268 /* support address_cells=2 */
269 if (dev_read_addr_cells(dev) == 2 && reg == 0) {
270 if (ofnode_read_u32_index(dev_ofnode(dev), "reg", 1, ®)) {
271 dev_err(dev, "%s must have reg[1]\n",
272 ofnode_get_name(dev_ofnode(dev)));
273 return -EINVAL;
274 }
275 }
276
277 phy_cfgs = (const struct rockchip_usb2phy_cfg *)
278 dev_get_driver_data(dev);
279 if (!phy_cfgs)
280 return -EINVAL;
281
282 /* find out a proper config which can be matched with dt. */
283 index = 0;
284 do {
285 if (phy_cfgs[index].reg == reg) {
286 priv->phy_cfg = &phy_cfgs[index];
287 break;
288 }
289
290 ++index;
291 } while (phy_cfgs[index].reg);
292
293 if (!priv->phy_cfg) {
294 dev_err(dev, "failed find proper phy-cfg\n");
295 return -EINVAL;
296 }
297
298 ret = clk_get_by_name(dev, "phyclk", &priv->phyclk);
299 if (ret) {
300 dev_err(dev, "failed to get the phyclk (ret=%d)\n", ret);
301 return ret;
302 }
303
304 if (priv->phy_cfg->clkout_ctl_phy.enable)
305 ret = regmap_init_mem_index(dev_ofnode(dev), &priv->phy_base, 0);
306
307 return ret;
308 }
309
rockchip_usb2phy_bind(struct udevice * dev)310 static int rockchip_usb2phy_bind(struct udevice *dev)
311 {
312 struct udevice *usb2phy_dev;
313 ofnode node;
314 const char *name;
315 int ret = 0;
316
317 dev_for_each_subnode(node, dev) {
318 if (!ofnode_is_enabled(node))
319 continue;
320
321 name = ofnode_get_name(node);
322 dev_dbg(dev, "subnode %s\n", name);
323
324 ret = device_bind_driver_to_node(dev, "rockchip_usb2phy_port",
325 name, node, &usb2phy_dev);
326 if (ret) {
327 dev_err(dev,
328 "'%s' cannot bind 'rockchip_usb2phy_port'\n", name);
329 goto bind_fail;
330 }
331 }
332
333 node = dev_ofnode(dev);
334 name = "clk_usbphy_480m";
335 dev_read_string_index(dev, "clock-output-names", 0, &name);
336
337 dev_dbg(dev, "clk %s for node %s\n", name, ofnode_get_name(node));
338
339 ret = device_bind_driver_to_node(dev, "rockchip_usb2phy_clock",
340 name, node, &usb2phy_dev);
341 if (ret) {
342 dev_err(dev,
343 "'%s' cannot bind 'rockchip_usb2phy_clock'\n", name);
344 goto bind_fail;
345 }
346
347 return 0;
348
349 bind_fail:
350 device_chld_unbind(dev, NULL);
351
352 return ret;
353 }
354
355 static const struct rockchip_usb2phy_cfg rk3308_phy_cfgs[] = {
356 {
357 .reg = 0x100,
358 .clkout_ctl = { 0x0108, 4, 4, 1, 0 },
359 .port_cfgs = {
360 [USB2PHY_PORT_OTG] = {
361 .phy_sus = { 0x0100, 1, 0, 2, 1 },
362 },
363 [USB2PHY_PORT_HOST] = {
364 .phy_sus = { 0x0104, 1, 0, 2, 1 },
365 }
366 },
367 },
368 { /* sentinel */ }
369 };
370
371 static const struct rockchip_usb2phy_cfg rk3328_usb2phy_cfgs[] = {
372 {
373 .reg = 0x100,
374 .clkout_ctl = { 0x0108, 4, 4, 1, 0 },
375 .port_cfgs = {
376 [USB2PHY_PORT_OTG] = {
377 .phy_sus = { 0x0100, 1, 0, 2, 1 },
378 },
379 [USB2PHY_PORT_HOST] = {
380 .phy_sus = { 0x0104, 1, 0, 2, 1 },
381 }
382 },
383 },
384 { /* sentinel */ }
385 };
386
387 static const struct rockchip_usb2phy_cfg rk3399_usb2phy_cfgs[] = {
388 {
389 .reg = 0xe450,
390 .clkout_ctl = { 0xe450, 4, 4, 1, 0 },
391 .port_cfgs = {
392 [USB2PHY_PORT_OTG] = {
393 .phy_sus = { 0xe454, 1, 0, 2, 1 },
394 },
395 [USB2PHY_PORT_HOST] = {
396 .phy_sus = { 0xe458, 1, 0, 2, 1 },
397 }
398 },
399 },
400 {
401 .reg = 0xe460,
402 .clkout_ctl = { 0xe460, 4, 4, 1, 0 },
403 .port_cfgs = {
404 [USB2PHY_PORT_OTG] = {
405 .phy_sus = { 0xe464, 1, 0, 2, 1 },
406 },
407 [USB2PHY_PORT_HOST] = {
408 .phy_sus = { 0xe468, 1, 0, 2, 1 },
409 }
410 },
411 },
412 { /* sentinel */ }
413 };
414
415 static const struct rockchip_usb2phy_cfg rk3528_phy_cfgs[] = {
416 {
417 .reg = 0xffdf0000,
418 .clkout_ctl_phy = { 0x041c, 7, 2, 0, 0x27 },
419 .port_cfgs = {
420 [USB2PHY_PORT_OTG] = {
421 .phy_sus = { 0x004c, 1, 0, 2, 1 },
422 },
423 [USB2PHY_PORT_HOST] = {
424 .phy_sus = { 0x005c, 1, 0, 2, 1 },
425 }
426 },
427 },
428 { /* sentinel */ }
429 };
430
431 static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = {
432 {
433 .reg = 0xfe8a0000,
434 .clkout_ctl = { 0x0008, 4, 4, 1, 0 },
435 .port_cfgs = {
436 [USB2PHY_PORT_OTG] = {
437 .phy_sus = { 0x0000, 1, 0, 2, 1 },
438 },
439 [USB2PHY_PORT_HOST] = {
440 .phy_sus = { 0x0004, 1, 0, 2, 1 },
441 }
442 },
443 },
444 {
445 .reg = 0xfe8b0000,
446 .clkout_ctl = { 0x0008, 4, 4, 1, 0 },
447 .port_cfgs = {
448 [USB2PHY_PORT_OTG] = {
449 .phy_sus = { 0x0000, 1, 0, 2, 1 },
450 },
451 [USB2PHY_PORT_HOST] = {
452 .phy_sus = { 0x0004, 1, 0, 2, 1 },
453 }
454 },
455 },
456 { /* sentinel */ }
457 };
458
459 static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = {
460 {
461 .reg = 0x0000,
462 .clkout_ctl = { 0x0000, 0, 0, 1, 0 },
463 .port_cfgs = {
464 [USB2PHY_PORT_OTG] = {
465 .phy_sus = { 0x000c, 11, 11, 0, 1 },
466 }
467 },
468 },
469 {
470 .reg = 0x4000,
471 .clkout_ctl = { 0x0000, 0, 0, 1, 0 },
472 .port_cfgs = {
473 [USB2PHY_PORT_OTG] = {
474 .phy_sus = { 0x000c, 11, 11, 0, 1 },
475 }
476 },
477 },
478 {
479 .reg = 0x8000,
480 .clkout_ctl = { 0x0000, 0, 0, 1, 0 },
481 .port_cfgs = {
482 [USB2PHY_PORT_HOST] = {
483 .phy_sus = { 0x0008, 2, 2, 0, 1 },
484 }
485 },
486 },
487 {
488 .reg = 0xc000,
489 .clkout_ctl = { 0x0000, 0, 0, 1, 0 },
490 .port_cfgs = {
491 [USB2PHY_PORT_HOST] = {
492 .phy_sus = { 0x0008, 2, 2, 0, 1 },
493 }
494 },
495 },
496 { /* sentinel */ }
497 };
498
499 static const struct udevice_id rockchip_usb2phy_ids[] = {
500 {
501 .compatible = "rockchip,rk3308-usb2phy",
502 .data = (ulong)&rk3308_phy_cfgs,
503 },
504 {
505 .compatible = "rockchip,rk3328-usb2phy",
506 .data = (ulong)&rk3328_usb2phy_cfgs,
507 },
508 {
509 .compatible = "rockchip,rk3399-usb2phy",
510 .data = (ulong)&rk3399_usb2phy_cfgs,
511 },
512 {
513 .compatible = "rockchip,rk3528-usb2phy",
514 .data = (ulong)&rk3528_phy_cfgs,
515 },
516 {
517 .compatible = "rockchip,rk3568-usb2phy",
518 .data = (ulong)&rk3568_phy_cfgs,
519 },
520 {
521 .compatible = "rockchip,rk3588-usb2phy",
522 .data = (ulong)&rk3588_phy_cfgs,
523 },
524 { /* sentinel */ }
525 };
526
527 U_BOOT_DRIVER(rockchip_usb2phy_port) = {
528 .name = "rockchip_usb2phy_port",
529 .id = UCLASS_PHY,
530 .ops = &rockchip_usb2phy_ops,
531 };
532
533 U_BOOT_DRIVER(rockchip_usb2phy_clock) = {
534 .name = "rockchip_usb2phy_clock",
535 .id = UCLASS_CLK,
536 .ops = &rockchip_usb2phy_clk_ops,
537 };
538
539 U_BOOT_DRIVER(rockchip_usb2phy) = {
540 .name = "rockchip_usb2phy",
541 .id = UCLASS_PHY,
542 .of_match = rockchip_usb2phy_ids,
543 .probe = rockchip_usb2phy_probe,
544 .bind = rockchip_usb2phy_bind,
545 .priv_auto = sizeof(struct rockchip_usb2phy),
546 };
547