1 /* $OpenBSD: ehci_fdt.c,v 1.12 2024/02/12 21:37:25 uaa Exp $ */
2
3 /*
4 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
5 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <sys/types.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 #include <sys/malloc.h>
24
25 #include <machine/intr.h>
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/ofw_clock.h>
31 #include <dev/ofw/ofw_pinctrl.h>
32 #include <dev/ofw/ofw_regulator.h>
33 #include <dev/ofw/ofw_misc.h>
34 #include <dev/ofw/fdt.h>
35
36 #include <dev/usb/usb.h>
37 #include <dev/usb/usbdi.h>
38 #include <dev/usb/usbdivar.h>
39 #include <dev/usb/usb_mem.h>
40
41 #include <dev/usb/ehcireg.h>
42 #include <dev/usb/ehcivar.h>
43
44 #define MARVELL_EHCI_HOST_OFFSET 0x0100
45
46 struct ehci_fdt_softc {
47 struct ehci_softc sc;
48
49 bus_space_handle_t sc_ioh;
50 bus_size_t sc_size;
51 void *sc_ih;
52
53 int sc_node;
54 };
55
56 int ehci_fdt_match(struct device *, void *, void *);
57 void ehci_fdt_attach(struct device *, struct device *, void *);
58 int ehci_fdt_detach(struct device *, int);
59
60 const struct cfattach ehci_fdt_ca = {
61 sizeof(struct ehci_fdt_softc), ehci_fdt_match, ehci_fdt_attach,
62 ehci_fdt_detach, ehci_activate
63 };
64
65 void ehci_init_phys(struct ehci_fdt_softc *);
66
67 int
ehci_fdt_match(struct device * parent,void * match,void * aux)68 ehci_fdt_match(struct device *parent, void *match, void *aux)
69 {
70 struct fdt_attach_args *faa = aux;
71
72 return OF_is_compatible(faa->fa_node, "generic-ehci") ||
73 OF_is_compatible(faa->fa_node, "marvell,armada-3700-ehci");
74 }
75
76 void
ehci_fdt_attach(struct device * parent,struct device * self,void * aux)77 ehci_fdt_attach(struct device *parent, struct device *self, void *aux)
78 {
79 struct ehci_fdt_softc *sc = (struct ehci_fdt_softc *)self;
80 struct fdt_attach_args *faa = aux;
81 char *devname = sc->sc.sc_bus.bdev.dv_xname;
82 bus_size_t offset = 0;
83 usbd_status r;
84
85 if (faa->fa_nreg < 1) {
86 printf(": no registers\n");
87 return;
88 }
89
90 sc->sc_node = faa->fa_node;
91 sc->sc.iot = faa->fa_iot;
92 sc->sc.sc_bus.dmatag = faa->fa_dmat;
93 sc->sc_size = faa->fa_reg[0].size;
94
95 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
96 sc->sc_size, 0, &sc->sc_ioh)) {
97 printf(": can't map registers\n");
98 goto out;
99 }
100
101 if (OF_is_compatible(faa->fa_node, "marvell,armada-3700-ehci"))
102 offset = MARVELL_EHCI_HOST_OFFSET;
103
104 sc->sc.sc_size = sc->sc_size - offset;
105 if (bus_space_subregion(sc->sc.iot, sc->sc_ioh, offset,
106 sc->sc.sc_size, &sc->sc.ioh)) {
107 printf(": can't map ehci registers\n");
108 goto unmap;
109 }
110
111 pinctrl_byname(sc->sc_node, "default");
112
113 clock_enable_all(sc->sc_node);
114 reset_deassert_all(sc->sc_node);
115
116 /* Disable interrupts, so we don't get any spurious ones. */
117 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
118 EOWRITE2(&sc->sc, EHCI_USBINTR, 0);
119
120 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB,
121 ehci_intr, &sc->sc, devname);
122 if (sc->sc_ih == NULL) {
123 printf(": can't establish interrupt\n");
124 clock_disable_all(sc->sc_node);
125 goto unmap;
126 }
127
128 printf("\n");
129
130 if (OF_is_compatible(faa->fa_node, "marvell,armada-3700-ehci")) {
131 uint32_t usbmode;
132
133 /* force HOST mode */
134 sc->sc.sc_flags = EHCIF_USBMODE;
135
136 usbmode = EOREAD4(&sc->sc, EHCI_USBMODE);
137 CLR(usbmode, EHCI_USBMODE_CM_M);
138 SET(usbmode, EHCI_USBMODE_CM_HOST);
139 EOWRITE4(&sc->sc, EHCI_USBMODE, usbmode);
140 }
141
142 ehci_init_phys(sc);
143
144 strlcpy(sc->sc.sc_vendor, "Generic", sizeof(sc->sc.sc_vendor));
145 r = ehci_init(&sc->sc);
146 if (r != USBD_NORMAL_COMPLETION) {
147 printf("%s: init failed, error=%d\n", devname, r);
148 clock_disable_all(sc->sc_node);
149 goto disestablish_intr;
150 }
151
152 /* Attach usb device. */
153 config_found(self, &sc->sc.sc_bus, usbctlprint);
154 return;
155
156 disestablish_intr:
157 fdt_intr_disestablish(sc->sc_ih);
158 sc->sc_ih = NULL;
159 unmap:
160 bus_space_unmap(sc->sc.iot, sc->sc_ioh, sc->sc_size);
161 sc->sc.sc_size = 0;
162 out:
163 return;
164 }
165
166 int
ehci_fdt_detach(struct device * self,int flags)167 ehci_fdt_detach(struct device *self, int flags)
168 {
169 struct ehci_fdt_softc *sc = (struct ehci_fdt_softc *)self;
170 int rv;
171
172 rv = ehci_detach(self, flags);
173 if (rv)
174 return rv;
175
176 if (sc->sc_ih != NULL) {
177 fdt_intr_disestablish(sc->sc_ih);
178 sc->sc_ih = NULL;
179 }
180
181 if (sc->sc.sc_size) {
182 bus_space_unmap(sc->sc.iot, sc->sc_ioh, sc->sc_size);
183 sc->sc.sc_size = 0;
184 }
185
186 clock_disable_all(sc->sc_node);
187 return 0;
188 }
189
190 struct ehci_phy {
191 const char *compat;
192 void (*init)(struct ehci_fdt_softc *, uint32_t *);
193 };
194
195 void sun4i_phy_init(struct ehci_fdt_softc *, uint32_t *);
196 void sun9i_phy_init(struct ehci_fdt_softc *, uint32_t *);
197
198 struct ehci_phy ehci_phys[] = {
199 { "allwinner,sun4i-a10-usb-phy", sun4i_phy_init },
200 { "allwinner,sun5i-a13-usb-phy", sun4i_phy_init },
201 { "allwinner,sun6i-a31-usb-phy", sun4i_phy_init },
202 { "allwinner,sun7i-a20-usb-phy", sun4i_phy_init },
203 { "allwinner,sun8i-a23-usb-phy", sun4i_phy_init },
204 { "allwinner,sun8i-a33-usb-phy", sun4i_phy_init },
205 { "allwinner,sun8i-h3-usb-phy", sun4i_phy_init },
206 { "allwinner,sun8i-r40-usb-phy", sun4i_phy_init },
207 { "allwinner,sun8i-v3s-usb-phy", sun4i_phy_init },
208 { "allwinner,sun20i-d1-usb-phy", sun4i_phy_init },
209 { "allwinner,sun50i-h6-usb-phy", sun4i_phy_init },
210 { "allwinner,sun50i-h616-usb-phy", sun4i_phy_init },
211 { "allwinner,sun50i-a64-usb-phy", sun4i_phy_init },
212 { "allwinner,sun9i-a80-usb-phy", sun9i_phy_init },
213 };
214
215 uint32_t *
ehci_next_phy(uint32_t * cells)216 ehci_next_phy(uint32_t *cells)
217 {
218 uint32_t phandle = cells[0];
219 int node, ncells;
220
221 node = OF_getnodebyphandle(phandle);
222 if (node == 0)
223 return NULL;
224
225 ncells = OF_getpropint(node, "#phy-cells", 0);
226 return cells + ncells + 1;
227 }
228
229 void
ehci_init_phy(struct ehci_fdt_softc * sc,uint32_t * cells)230 ehci_init_phy(struct ehci_fdt_softc *sc, uint32_t *cells)
231 {
232 uint32_t phy_supply;
233 int node;
234 int i;
235
236 node = OF_getnodebyphandle(cells[0]);
237 if (node == 0)
238 return;
239
240 for (i = 0; i < nitems(ehci_phys); i++) {
241 if (OF_is_compatible(node, ehci_phys[i].compat)) {
242 ehci_phys[i].init(sc, cells);
243 return;
244 }
245 }
246
247 phy_supply = OF_getpropint(node, "phy-supply", 0);
248 if (phy_supply)
249 regulator_enable(phy_supply);
250 }
251
252 void
ehci_init_phys(struct ehci_fdt_softc * sc)253 ehci_init_phys(struct ehci_fdt_softc *sc)
254 {
255 uint32_t *phys;
256 uint32_t *phy;
257 int len;
258
259 if (phy_enable(sc->sc_node, "usb") == 0)
260 return;
261
262 len = OF_getproplen(sc->sc_node, "phys");
263 if (len <= 0)
264 return;
265
266 phys = malloc(len, M_TEMP, M_WAITOK);
267 OF_getpropintarray(sc->sc_node, "phys", phys, len);
268
269 phy = phys;
270 while (phy && phy < phys + (len / sizeof(uint32_t))) {
271 ehci_init_phy(sc, phy);
272 phy = ehci_next_phy(phy);
273 }
274
275 free(phys, M_TEMP, len);
276 }
277
278 /*
279 * Allwinner PHYs.
280 */
281
282 /* Registers */
283 #define SUNXI_HCI_ICR 0x800
284 #define SUNXI_ULPI_BYPASS (1 << 0)
285 #define SUNXI_AHB_INCRX_ALIGN (1 << 8)
286 #define SUNXI_AHB_INCR4 (1 << 9)
287 #define SUNXI_AHB_INCR8 (1 << 10)
288 #define SUNXI_AHB_INCR16 (1 << 11)
289
290 void
sun50i_h616_phy2_init(struct ehci_fdt_softc * sc,int node)291 sun50i_h616_phy2_init(struct ehci_fdt_softc *sc, int node)
292 {
293 int len, idx;
294 uint32_t *reg, val;
295 bus_size_t size;
296 bus_space_handle_t ioh;
297
298 /*
299 * to access USB2-PHY register, get address from "reg" property of
300 * current "allwinner,...-usb-phy" node
301 */
302 len = OF_getproplen(node, "reg");
303 if (len <= 0)
304 goto out;
305
306 reg = malloc(len, M_TEMP, M_WAITOK);
307 OF_getpropintarray(node, "reg", reg, len);
308
309 idx = OF_getindex(node, "pmu2", "reg-names");
310 if (idx < 0 || (idx + 1) > (len / (sizeof(uint32_t) * 2))) {
311 printf(": no phy2 register\n");
312 goto free;
313 }
314
315 /* convert "reg-names" index to "reg" (address-size pair) index */
316 idx *= 2;
317
318 size = reg[idx + 1];
319 if (bus_space_map(sc->sc.iot, reg[idx], size, 0, &ioh)) {
320 printf(": can't map phy2 registers\n");
321 goto free;
322 }
323
324 clock_enable(node, "usb2_phy");
325 reset_deassert(node, "usb2_reset");
326 clock_enable(node, "pmu2_clk");
327
328 /*
329 * address is offset from "pmu2", not EHCI2 base address
330 * (normally it points EHCI2 base address + 0x810)
331 */
332 val = bus_space_read_4(sc->sc.iot, ioh, 0x10);
333 val &= ~(1 << 3); /* clear SIDDQ */
334 bus_space_write_4(sc->sc.iot, ioh, 0x10, val);
335
336 clock_disable(node, "pmu2_clk");
337 /* "usb2_reset" and "usb2_phy" unchanged */
338
339 bus_space_unmap(sc->sc.iot, ioh, size);
340 free:
341 free(reg, M_TEMP, len);
342 out:
343 return;
344 }
345
346 void
sun4i_phy_init(struct ehci_fdt_softc * sc,uint32_t * cells)347 sun4i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells)
348 {
349 uint32_t vbus_supply;
350 char name[32];
351 uint32_t val;
352 int node;
353
354 node = OF_getnodebyphandle(cells[0]);
355 if (node == -1)
356 return;
357
358 /* Allwinner H616 needs to clear PHY2's SIDDQ flag */
359 if (OF_is_compatible(node, "allwinner,sun50i-h616-usb-phy") &&
360 cells[1] != 2)
361 sun50i_h616_phy2_init(sc, node);
362
363 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR);
364 val |= SUNXI_AHB_INCR8 | SUNXI_AHB_INCR4;
365 val |= SUNXI_AHB_INCRX_ALIGN;
366 val |= SUNXI_ULPI_BYPASS;
367 bus_space_write_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR, val);
368
369 /*
370 * We need to poke an undocumented register to make the PHY
371 * work on Allwinner A64/D1/H3/H5/R40.
372 */
373 if (OF_is_compatible(node, "allwinner,sun8i-h3-usb-phy") ||
374 OF_is_compatible(node, "allwinner,sun8i-r40-usb-phy") ||
375 OF_is_compatible(node, "allwinner,sun50i-h6-usb-phy") ||
376 OF_is_compatible(node, "allwinner,sun50i-a64-usb-phy")) {
377 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810);
378 val &= ~(1 << 1);
379 bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val);
380 } else if (OF_is_compatible(node, "allwinner,sun8i-a83t-usb-phy") ||
381 OF_is_compatible(node, "allwinner,sun20i-d1-usb-phy") ||
382 OF_is_compatible(node, "allwinner,sun50i-h616-usb-phy")) {
383 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810);
384 val &= ~(1 << 3);
385 bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val);
386 }
387 if (OF_is_compatible(node, "allwinner,sun8i-a83t-usb-phy") ||
388 OF_is_compatible(node, "allwinner,sun50i-h616-usb-phy")) {
389 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810);
390 val |= 1 << 5;
391 bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val);
392 }
393
394 pinctrl_byname(node, "default");
395
396 /*
397 * On sun4i, sun5i and sun7i, there is a single clock. The
398 * more recent SoCs have a separate clock for each PHY.
399 */
400 if (OF_is_compatible(node, "allwinner,sun4i-a10-usb-phy") ||
401 OF_is_compatible(node, "allwinner,sun5i-a13-usb-phy") ||
402 OF_is_compatible(node, "allwinner,sun7i-a20-usb-phy")) {
403 clock_enable(node, "usb_phy");
404 } else {
405 snprintf(name, sizeof(name), "usb%d_phy", cells[1]);
406 clock_enable(node, name);
407 }
408
409 snprintf(name, sizeof(name), "usb%d_reset", cells[1]);
410 reset_deassert(node, name);
411
412 snprintf(name, sizeof(name), "usb%d_vbus-supply", cells[1]);
413 vbus_supply = OF_getpropint(node, name, 0);
414 if (vbus_supply)
415 regulator_enable(vbus_supply);
416 }
417
418 void
sun9i_phy_init(struct ehci_fdt_softc * sc,uint32_t * cells)419 sun9i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells)
420 {
421 uint32_t phy_supply;
422 uint32_t val;
423 int node;
424
425 node = OF_getnodebyphandle(cells[0]);
426 if (node == -1)
427 return;
428
429 pinctrl_byname(node, "default");
430 clock_enable(node, "phy");
431 reset_deassert(node, "phy");
432
433 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR);
434 val |= SUNXI_AHB_INCR16 | SUNXI_AHB_INCR8 | SUNXI_AHB_INCR4;
435 val |= SUNXI_AHB_INCRX_ALIGN;
436 val |= SUNXI_ULPI_BYPASS;
437 bus_space_write_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR, val);
438
439 phy_supply = OF_getpropint(node, "phy-supply", 0);
440 if (phy_supply)
441 regulator_enable(phy_supply);
442 }
443