1 /* $OpenBSD: rkusbphy.c,v 1.6 2024/11/24 22:46:54 kettenis Exp $ */
2
3 /*
4 * Copyright (c) 2023 David Gwynne <dlg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Rockchip USB2PHY with Innosilicon IP
21 */
22
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26
27 #include <machine/intr.h>
28 #include <machine/bus.h>
29 #include <machine/fdt.h>
30
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/ofw_clock.h>
33 #include <dev/ofw/ofw_regulator.h>
34 #include <dev/ofw/ofw_misc.h>
35 #include <dev/ofw/fdt.h>
36
37 /*
38 * chip stuff
39 */
40
41 struct rkusbphy_reg {
42 bus_size_t r_offs;
43 unsigned int r_shift;
44 uint32_t r_mask;
45 uint32_t r_set;
46 };
47
48 struct rkusbphy_port_regs {
49 struct rkusbphy_reg phy_enable;
50 };
51
52 struct rkusbphy_regs {
53 struct rkusbphy_reg clk_enable;
54
55 struct rkusbphy_port_regs otg;
56 struct rkusbphy_port_regs host;
57 };
58
59 struct rkusbphy_chip {
60 bus_addr_t c_base_addr;
61 const struct rkusbphy_regs *c_regs;
62 };
63
64 /*
65 * RK3399 has two USB2PHY nodes that share a GRF.
66 */
67
68 static const struct rkusbphy_regs rkusbphy_rk3399_usb0_regs = {
69 /* shift, mask, set */
70 .clk_enable = { 0xe450, 4, 0x1, 0x0 },
71
72 .otg = {
73 .phy_enable = { 0xe454, 0, 0x3, 0x2 },
74 },
75
76 .host = {
77 .phy_enable = { 0xe458, 0, 0x3, 0x2 },
78 },
79 };
80
81 static const struct rkusbphy_regs rkusbphy_rk3399_usb1_regs = {
82 /* shift, mask, set */
83 .clk_enable = { 0xe460, 4, 0x1, 0x0 },
84
85 .otg = {
86 .phy_enable = { 0xe464, 0, 0x3, 0x2 },
87 },
88
89 .host = {
90 .phy_enable = { 0xe468, 0, 0x3, 0x2 },
91 },
92 };
93
94 static const struct rkusbphy_chip rkusbphy_rk3399[] = {
95 {
96 .c_base_addr = 0xe450,
97 .c_regs = &rkusbphy_rk3399_usb0_regs,
98 },
99 {
100 .c_base_addr = 0xe460,
101 .c_regs = &rkusbphy_rk3399_usb1_regs,
102 },
103 };
104
105 /*
106 * RK3568 has two USB2PHY nodes that have a GRF each. Each GRF has
107 * the same register layout.
108 */
109
110 static const struct rkusbphy_regs rkusbphy_rk3568_regs = {
111 /* shift, mask, set */
112 .clk_enable = { 0x0008, 4, 0x1, 0x0 },
113
114 .otg = {
115 .phy_enable = { 0x0000, 0, 0x1ff, 0x1d2 },
116 },
117
118 .host = {
119 .phy_enable = { 0x0004, 0, 0x1ff, 0x1d2 },
120 },
121 };
122
123 static const struct rkusbphy_chip rkusbphy_rk3568[] = {
124 {
125 .c_base_addr = 0xfe8a0000,
126 .c_regs = &rkusbphy_rk3568_regs,
127 },
128 {
129 .c_base_addr = 0xfe8b0000,
130 .c_regs = &rkusbphy_rk3568_regs,
131 },
132 };
133
134 static const struct rkusbphy_regs rkusbphy_rk3588_regs = {
135 /* shift, mask, set */
136 .clk_enable = { 0x0000, 0, 0x1, 0x0 },
137
138 .otg = {
139 .phy_enable = { 0x000c, 11, 0x1, 0x0 },
140 },
141
142 .host = {
143 .phy_enable = { 0x0008, 2, 0x1, 0x0 },
144 },
145 };
146
147 static const struct rkusbphy_chip rkusbphy_rk3588[] = {
148 {
149 .c_base_addr = 0x0000,
150 .c_regs = &rkusbphy_rk3588_regs,
151 },
152 {
153 .c_base_addr = 0x4000,
154 .c_regs = &rkusbphy_rk3588_regs,
155 },
156 {
157 .c_base_addr = 0x8000,
158 .c_regs = &rkusbphy_rk3588_regs,
159 },
160 {
161 .c_base_addr = 0xc000,
162 .c_regs = &rkusbphy_rk3588_regs,
163 },
164 };
165
166 /*
167 * driver stuff
168 */
169
170 struct rkusbphy_softc {
171 struct device sc_dev;
172 const struct rkusbphy_regs *sc_regs;
173 struct regmap *sc_grf;
174 int sc_node;
175
176 int sc_running;
177
178 struct phy_device sc_otg_phy;
179 struct phy_device sc_host_phy;
180 };
181 #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
182
183 static int rkusbphy_match(struct device *, void *, void *);
184 static void rkusbphy_attach(struct device *, struct device *,
185 void *);
186
187 static uint32_t rkusbphy_rd(struct rkusbphy_softc *,
188 const struct rkusbphy_reg *);
189 static int rkusbphy_isset(struct rkusbphy_softc *,
190 const struct rkusbphy_reg *);
191 static void rkusbphy_wr(struct rkusbphy_softc *,
192 const struct rkusbphy_reg *, uint32_t);
193 static void rkusbphy_set(struct rkusbphy_softc *,
194 const struct rkusbphy_reg *);
195
196 static int rkusbphy_otg_phy_enable(void *, uint32_t *);
197 static int rkusbphy_host_phy_enable(void *, uint32_t *);
198
199 struct rkusbphy_port_config {
200 const char *pc_name;
201 int (*pc_enable)(void *, uint32_t *);
202 };
203
204 static void rkusbphy_register(struct rkusbphy_softc *,
205 struct phy_device *, const struct rkusbphy_port_config *);
206
207 static const struct rkusbphy_port_config rkusbphy_otg_config = {
208 .pc_name = "otg-port",
209 .pc_enable = rkusbphy_otg_phy_enable,
210 };
211
212 static const struct rkusbphy_port_config rkusbphy_host_config = {
213 .pc_name = "host-port",
214 .pc_enable = rkusbphy_host_phy_enable,
215 };
216
217 const struct cfattach rkusbphy_ca = {
218 sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach
219 };
220
221 struct cfdriver rkusbphy_cd = {
222 NULL, "rkusbphy", DV_DULL
223 };
224
225 struct rkusbphy_id {
226 const char *id_name;
227 const struct rkusbphy_chip *id_chips;
228 size_t id_nchips;
229 };
230
231 #define RKUSBPHY_ID(_n, _c) { _n, _c, nitems(_c) }
232
233 static const struct rkusbphy_id rkusbphy_ids[] = {
234 RKUSBPHY_ID("rockchip,rk3399-usb2phy", rkusbphy_rk3399),
235 RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568),
236 RKUSBPHY_ID("rockchip,rk3588-usb2phy", rkusbphy_rk3588),
237 };
238
239 static const struct rkusbphy_id *
rkusbphy_lookup(struct fdt_attach_args * faa)240 rkusbphy_lookup(struct fdt_attach_args *faa)
241 {
242 size_t i;
243
244 for (i = 0; i < nitems(rkusbphy_ids); i++) {
245 const struct rkusbphy_id *id = &rkusbphy_ids[i];
246 if (OF_is_compatible(faa->fa_node, id->id_name))
247 return (id);
248 }
249
250 return (NULL);
251 }
252
253 static int
rkusbphy_match(struct device * parent,void * match,void * aux)254 rkusbphy_match(struct device *parent, void *match, void *aux)
255 {
256 struct fdt_attach_args *faa = aux;
257
258 return (rkusbphy_lookup(faa) != NULL ? 1 : 0);
259 }
260
261 static void
rkusbphy_attach(struct device * parent,struct device * self,void * aux)262 rkusbphy_attach(struct device *parent, struct device *self, void *aux)
263 {
264 struct rkusbphy_softc *sc = (struct rkusbphy_softc *)self;
265 struct fdt_attach_args *faa = aux;
266 const struct rkusbphy_id *id = rkusbphy_lookup(faa);
267 size_t i;
268 uint32_t grfph;
269
270 if (faa->fa_nreg < 1) {
271 printf(": no registers\n");
272 return;
273 }
274
275 for (i = 0; i < id->id_nchips; i++) {
276 const struct rkusbphy_chip *c = &id->id_chips[i];
277 if (faa->fa_reg[0].addr == c->c_base_addr) {
278 printf(": phy %zu\n", i);
279 sc->sc_regs = c->c_regs;
280 break;
281 }
282 }
283 if (sc->sc_regs == NULL) {
284 printf(": unknown base address 0x%llu\n", faa->fa_reg[0].addr);
285 return;
286 }
287
288 sc->sc_node = faa->fa_node;
289
290 grfph = OF_getpropint(sc->sc_node, "rockchip,usbgrf", 0);
291 if (grfph)
292 sc->sc_grf = regmap_byphandle(grfph);
293 else
294 sc->sc_grf = regmap_bynode(OF_parent(faa->fa_node));
295 if (sc->sc_grf == NULL) {
296 printf("%s: rockchip,usbgrf 0x%x not found\n", DEVNAME(sc),
297 grfph);
298 return;
299 }
300
301 rkusbphy_register(sc, &sc->sc_otg_phy, &rkusbphy_otg_config);
302 rkusbphy_register(sc, &sc->sc_host_phy, &rkusbphy_host_config);
303 }
304
305 static uint32_t
rkusbphy_rd(struct rkusbphy_softc * sc,const struct rkusbphy_reg * r)306 rkusbphy_rd(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
307 {
308 uint32_t v;
309
310 if (r->r_mask == 0)
311 return (0);
312
313 v = regmap_read_4(sc->sc_grf, r->r_offs);
314
315 return ((v >> r->r_shift) & r->r_mask);
316 }
317
318 static int
rkusbphy_isset(struct rkusbphy_softc * sc,const struct rkusbphy_reg * r)319 rkusbphy_isset(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
320 {
321 return (rkusbphy_rd(sc, r) == r->r_set);
322 }
323
324 static void
rkusbphy_wr(struct rkusbphy_softc * sc,const struct rkusbphy_reg * r,uint32_t v)325 rkusbphy_wr(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r, uint32_t v)
326 {
327 if (r->r_mask == 0)
328 return;
329
330 regmap_write_4(sc->sc_grf, r->r_offs,
331 (r->r_mask << (r->r_shift + 16)) | (v << r->r_shift));
332 }
333
334 static void
rkusbphy_set(struct rkusbphy_softc * sc,const struct rkusbphy_reg * r)335 rkusbphy_set(struct rkusbphy_softc *sc, const struct rkusbphy_reg *r)
336 {
337 rkusbphy_wr(sc, r, r->r_set);
338 }
339
340 static void
rkusbphy_register(struct rkusbphy_softc * sc,struct phy_device * pd,const struct rkusbphy_port_config * pc)341 rkusbphy_register(struct rkusbphy_softc *sc, struct phy_device *pd,
342 const struct rkusbphy_port_config *pc)
343 {
344 char status[32];
345 int node;
346
347 node = OF_getnodebyname(sc->sc_node, pc->pc_name);
348 if (node == 0)
349 return;
350
351 if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
352 strcmp(status, "disabled") == 0)
353 return;
354
355 pd->pd_node = node;
356 pd->pd_cookie = sc;
357 pd->pd_enable = pc->pc_enable;
358 phy_register(pd);
359 }
360
361 static void
rkusbphy_phy_supply(struct rkusbphy_softc * sc,int node)362 rkusbphy_phy_supply(struct rkusbphy_softc *sc, int node)
363 {
364 int phandle;
365
366 if (!sc->sc_running) {
367 clock_enable(sc->sc_node, "phyclk");
368 if (!rkusbphy_isset(sc, &sc->sc_regs->clk_enable)) {
369 rkusbphy_set(sc, &sc->sc_regs->clk_enable);
370
371 delay(1200);
372 }
373
374 sc->sc_running = 1;
375 }
376
377 phandle = OF_getpropint(node, "phy-supply", 0);
378 if (phandle == 0)
379 return;
380
381 regulator_enable(phandle);
382 }
383
384 static int
rkusbphy_otg_phy_enable(void * cookie,uint32_t * cells)385 rkusbphy_otg_phy_enable(void *cookie, uint32_t *cells)
386 {
387 struct rkusbphy_softc *sc = cookie;
388
389 rkusbphy_phy_supply(sc, sc->sc_otg_phy.pd_node);
390
391 rkusbphy_set(sc, &sc->sc_regs->otg.phy_enable);
392 delay(1500);
393
394 return (EINVAL);
395 }
396
397 static int
rkusbphy_host_phy_enable(void * cookie,uint32_t * cells)398 rkusbphy_host_phy_enable(void *cookie, uint32_t *cells)
399 {
400 struct rkusbphy_softc *sc = cookie;
401
402 rkusbphy_phy_supply(sc, sc->sc_host_phy.pd_node);
403
404 rkusbphy_set(sc, &sc->sc_regs->host.phy_enable);
405 delay(1500);
406
407 return (EINVAL);
408 }
409