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