xref: /openbsd-src/sys/dev/fdt/xhci_fdt.c (revision 0b6813b0c5547848ad4bfd1021622647a4059935)
1 /*	$OpenBSD: xhci_fdt.c,v 1.24 2023/07/23 11:49:17 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/task.h>
23 
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_clock.h>
29 #include <dev/ofw/ofw_misc.h>
30 #include <dev/ofw/ofw_power.h>
31 #include <dev/ofw/ofw_regulator.h>
32 #include <dev/ofw/fdt.h>
33 
34 #include <dev/usb/usb.h>
35 #include <dev/usb/usbdi.h>
36 #include <dev/usb/usbdivar.h>
37 #include <dev/usb/usb_mem.h>
38 
39 #include <dev/usb/xhcireg.h>
40 #include <dev/usb/xhcivar.h>
41 
42 struct xhci_fdt_softc {
43 	struct xhci_softc	sc;
44 	int			sc_node;
45 	bus_space_handle_t	ph_ioh;
46 	void			*sc_ih;
47 
48 	bus_addr_t		sc_otg_base;
49 	bus_size_t		sc_otg_size;
50 	bus_space_handle_t	sc_otg_ioh;
51 
52 	struct device_ports	sc_ports;
53 	struct usb_controller_port sc_usb_controller_port;
54 	struct task		sc_snps_connect_task;
55 };
56 
57 int	xhci_fdt_match(struct device *, void *, void *);
58 void	xhci_fdt_attach(struct device *, struct device *, void *);
59 int	xhci_fdt_activate(struct device *, int);
60 
61 const struct cfattach xhci_fdt_ca = {
62 	sizeof(struct xhci_fdt_softc), xhci_fdt_match, xhci_fdt_attach, NULL,
63 	xhci_fdt_activate
64 };
65 
66 int	xhci_cdns_attach(struct xhci_fdt_softc *);
67 int	xhci_snps_attach(struct xhci_fdt_softc *);
68 int	xhci_snps_init(struct xhci_fdt_softc *);
69 void	xhci_init_phys(struct xhci_fdt_softc *);
70 
71 int
xhci_fdt_match(struct device * parent,void * match,void * aux)72 xhci_fdt_match(struct device *parent, void *match, void *aux)
73 {
74 	struct fdt_attach_args *faa = aux;
75 
76 	return OF_is_compatible(faa->fa_node, "generic-xhci") ||
77 	    OF_is_compatible(faa->fa_node, "cavium,octeon-7130-xhci") ||
78 	    OF_is_compatible(faa->fa_node, "cdns,usb3") ||
79 	    OF_is_compatible(faa->fa_node, "snps,dwc3");
80 }
81 
82 void
xhci_fdt_attach(struct device * parent,struct device * self,void * aux)83 xhci_fdt_attach(struct device *parent, struct device *self, void *aux)
84 {
85 	struct xhci_fdt_softc *sc = (struct xhci_fdt_softc *)self;
86 	struct fdt_attach_args *faa = aux;
87 	int error = 0;
88 	int idx;
89 
90 	if (faa->fa_nreg < 1) {
91 		printf(": no registers\n");
92 		return;
93 	}
94 
95 	if (OF_is_compatible(faa->fa_node, "cdns,usb3")) {
96 		idx = OF_getindex(faa->fa_node, "otg", "reg-names");
97 		if (idx < 0 || idx > faa->fa_nreg) {
98 			printf(": no otg registers\n");
99 			return;
100 		}
101 
102 		sc->sc_otg_base = faa->fa_reg[idx].addr;
103 		sc->sc_otg_size = faa->fa_reg[idx].size;
104 	}
105 
106 	idx = OF_getindex(faa->fa_node, "xhci", "reg-names");
107 	if (idx == -1)
108 		idx = 0;
109 	if (idx >= faa->fa_nreg) {
110 		printf(": no xhci registers\n");
111 		return;
112 	}
113 
114 	sc->sc_node = faa->fa_node;
115 	sc->sc.iot = faa->fa_iot;
116 	sc->sc.sc_size = faa->fa_reg[idx].size;
117 	sc->sc.sc_bus.dmatag = faa->fa_dmat;
118 
119 	if (bus_space_map(sc->sc.iot, faa->fa_reg[idx].addr,
120 	    faa->fa_reg[idx].size, 0, &sc->sc.ioh)) {
121 		printf(": can't map registers\n");
122 		return;
123 	}
124 
125 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB,
126 	    xhci_intr, sc, sc->sc.sc_bus.bdev.dv_xname);
127 	if (sc->sc_ih == NULL) {
128 		printf(": can't establish interrupt\n");
129 		goto unmap;
130 	}
131 
132 	/* Set up power and clocks */
133 	power_domain_enable(sc->sc_node);
134 	reset_deassert_all(sc->sc_node);
135 	clock_set_assigned(sc->sc_node);
136 	clock_enable_all(sc->sc_node);
137 
138 	/*
139 	 * Cadence and Synopsys DesignWare USB3 controllers need some
140 	 * extra attention because of the additional OTG
141 	 * functionality.
142 	 */
143 	if (OF_is_compatible(sc->sc_node, "cdns,usb3"))
144 		error = xhci_cdns_attach(sc);
145 	if (OF_is_compatible(sc->sc_node, "snps,dwc3"))
146 		error = xhci_snps_attach(sc);
147 	if (error) {
148 		printf(": can't initialize hardware\n");
149 		goto disestablish_ret;
150 	}
151 
152 	xhci_init_phys(sc);
153 
154 	strlcpy(sc->sc.sc_vendor, "Generic", sizeof(sc->sc.sc_vendor));
155 	if ((error = xhci_init(&sc->sc)) != 0) {
156 		printf("%s: init failed, error=%d\n",
157 		    sc->sc.sc_bus.bdev.dv_xname, error);
158 		goto disestablish_ret;
159 	}
160 
161 	/* Attach usb device. */
162 	config_found(self, &sc->sc.sc_bus, usbctlprint);
163 
164 	/* Now that the stack is ready, config' the HC and enable interrupts. */
165 	xhci_config(&sc->sc);
166 
167 	return;
168 
169 disestablish_ret:
170 	fdt_intr_disestablish(sc->sc_ih);
171 unmap:
172 	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
173 }
174 
175 int
xhci_fdt_activate(struct device * self,int act)176 xhci_fdt_activate(struct device *self, int act)
177 {
178 	struct xhci_fdt_softc *sc = (struct xhci_fdt_softc *)self;
179 	int rv = 0;
180 
181 	switch (act) {
182 	case DVACT_POWERDOWN:
183 		rv = xhci_activate(self, act);
184 		power_domain_disable(sc->sc_node);
185 		break;
186 	case DVACT_RESUME:
187 		power_domain_enable(sc->sc_node);
188 		if (OF_is_compatible(sc->sc_node, "snps,dwc3"))
189 			xhci_snps_init(sc);
190 		rv = xhci_activate(self, act);
191 		break;
192 	default:
193 		rv = xhci_activate(self, act);
194 		break;
195 	}
196 
197 	return rv;
198 }
199 
200 /*
201  * Cadence USB3 controller.
202  */
203 
204 #define OTG_DID			0x00
205 #define  OTG_DID_V1		0x4024e
206 #define OTG_CMD			0x10
207 #define  OTG_CMD_HOST_BUS_REQ	(1 << 1)
208 #define  OTG_CMD_OTG_DIS	(1 << 3)
209 #define OTG_STS			0x14
210 #define  OTG_STS_XHCI_READY	(1 << 26)
211 
212 int
xhci_cdns_attach(struct xhci_fdt_softc * sc)213 xhci_cdns_attach(struct xhci_fdt_softc *sc)
214 {
215 	uint32_t did, sts;
216 	int timo;
217 
218 	if (bus_space_map(sc->sc.iot, sc->sc_otg_base,
219 	    sc->sc_otg_size, 0, &sc->sc_otg_ioh))
220 		return ENOMEM;
221 
222 	did = bus_space_read_4(sc->sc.iot, sc->sc_otg_ioh, OTG_DID);
223 	if (did != OTG_DID_V1)
224 		return ENOTSUP;
225 
226 	bus_space_write_4(sc->sc.iot, sc->sc_otg_ioh, OTG_CMD,
227 	    OTG_CMD_HOST_BUS_REQ | OTG_CMD_OTG_DIS);
228 	for (timo = 100; timo > 0; timo--) {
229 		sts = bus_space_read_4(sc->sc.iot, sc->sc_otg_ioh, OTG_STS);
230 		if (sts & OTG_STS_XHCI_READY)
231 			break;
232 		delay(1000);
233 	}
234 	if (timo == 0) {
235 		bus_space_unmap(sc->sc.iot, sc->sc_otg_ioh, sc->sc_otg_size);
236 		return ETIMEDOUT;
237 	}
238 
239 	return 0;
240 }
241 
242 /*
243  * Synopsys DesignWare USB3 controller.
244  */
245 
246 #define USB3_GCTL		0xc110
247 #define  USB3_GCTL_PRTCAPDIR_MASK	(0x3 << 12)
248 #define  USB3_GCTL_PRTCAPDIR_HOST	(0x1 << 12)
249 #define  USB3_GCTL_PRTCAPDIR_DEVICE	(0x2 << 12)
250 #define USB3_GUCTL1		0xc11c
251 #define  USB3_GUCTL1_TX_IPGAP_LINECHECK_DIS	(1 << 28)
252 #define USB3_GUSB2PHYCFG0	0xc200
253 #define  USB3_GUSB2PHYCFG0_U2_FREECLK_EXISTS	(1 << 30)
254 #define  USB3_GUSB2PHYCFG0_USBTRDTIM(n)	((n) << 10)
255 #define  USB3_GUSB2PHYCFG0_ENBLSLPM	(1 << 8)
256 #define  USB3_GUSB2PHYCFG0_SUSPENDUSB20	(1 << 6)
257 #define  USB3_GUSB2PHYCFG0_PHYIF	(1 << 3)
258 
259 void
xhci_snps_do_connect(void * arg)260 xhci_snps_do_connect(void *arg)
261 {
262 	struct xhci_fdt_softc *sc = arg;
263 
264 	xhci_reinit(&sc->sc);
265 }
266 
267 void
xhci_snps_connect(void * cookie)268 xhci_snps_connect(void *cookie)
269 {
270 	struct xhci_fdt_softc *sc = cookie;
271 
272 	task_add(systq, &sc->sc_snps_connect_task);
273 }
274 
275 void *
xhci_snps_ep_get_cookie(void * cookie,struct endpoint * ep)276 xhci_snps_ep_get_cookie(void *cookie, struct endpoint *ep)
277 {
278 	return cookie;
279 }
280 
281 int
xhci_snps_attach(struct xhci_fdt_softc * sc)282 xhci_snps_attach(struct xhci_fdt_softc *sc)
283 {
284 	/*
285 	 * On Apple hardware we need to reset the controller when we
286 	 * see a new connection.
287 	 */
288 	if (OF_is_compatible(sc->sc_node, "apple,dwc3")) {
289 		sc->sc_usb_controller_port.up_cookie = sc;
290 		sc->sc_usb_controller_port.up_connect = xhci_snps_connect;
291 		task_set(&sc->sc_snps_connect_task, xhci_snps_do_connect, sc);
292 
293 		sc->sc_ports.dp_node = sc->sc_node;
294 		sc->sc_ports.dp_cookie = &sc->sc_usb_controller_port;
295 		sc->sc_ports.dp_ep_get_cookie = xhci_snps_ep_get_cookie;
296 		device_ports_register(&sc->sc_ports, EP_USB_CONTROLLER_PORT);
297 	}
298 
299 	return xhci_snps_init(sc);
300 }
301 
302 int
xhci_snps_init(struct xhci_fdt_softc * sc)303 xhci_snps_init(struct xhci_fdt_softc *sc)
304 {
305 	char phy_type[16] = { 0 };
306 	int node = sc->sc_node;
307 	uint32_t reg;
308 
309 	/* We don't support device mode, so always force host mode. */
310 	reg = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USB3_GCTL);
311 	reg &= ~USB3_GCTL_PRTCAPDIR_MASK;
312 	reg |= USB3_GCTL_PRTCAPDIR_HOST;
313 	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USB3_GCTL, reg);
314 
315 	/* Configure USB2 PHY type and quirks. */
316 	OF_getprop(node, "phy_type", phy_type, sizeof(phy_type));
317 	reg = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USB3_GUSB2PHYCFG0);
318 	reg &= ~USB3_GUSB2PHYCFG0_USBTRDTIM(0xf);
319 	if (strcmp(phy_type, "utmi_wide") == 0) {
320 		reg |= USB3_GUSB2PHYCFG0_PHYIF;
321 		reg |= USB3_GUSB2PHYCFG0_USBTRDTIM(0x5);
322 	} else {
323 		reg &= ~USB3_GUSB2PHYCFG0_PHYIF;
324 		reg |= USB3_GUSB2PHYCFG0_USBTRDTIM(0x9);
325 	}
326 	if (OF_getproplen(node, "snps,dis-u2-freeclk-exists-quirk") == 0)
327 		reg &= ~USB3_GUSB2PHYCFG0_U2_FREECLK_EXISTS;
328 	if (OF_getproplen(node, "snps,dis_enblslpm_quirk") == 0)
329 		reg &= ~USB3_GUSB2PHYCFG0_ENBLSLPM;
330 	if (OF_getproplen(node, "snps,dis_u2_susphy_quirk") == 0)
331 		reg &= ~USB3_GUSB2PHYCFG0_SUSPENDUSB20;
332 	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USB3_GUSB2PHYCFG0, reg);
333 
334 	/* Configure USB3 quirks. */
335 	reg = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USB3_GUCTL1);
336 	if (OF_getproplen(node, "snps,dis-tx-ipgap-linecheck-quirk") == 0)
337 		reg |= USB3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
338 	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USB3_GUCTL1, reg);
339 
340 	return 0;
341 }
342 
343 /*
344  * PHY initialization.
345  */
346 
347 struct xhci_phy {
348 	const char *compat;
349 	void (*init)(struct xhci_fdt_softc *, uint32_t *);
350 };
351 
352 void exynos5_usbdrd_init(struct xhci_fdt_softc *, uint32_t *);
353 void imx8mp_usb_init(struct xhci_fdt_softc *, uint32_t *);
354 void imx8mq_usb_init(struct xhci_fdt_softc *, uint32_t *);
355 void nop_xceiv_init(struct xhci_fdt_softc *, uint32_t *);
356 
357 struct xhci_phy xhci_phys[] = {
358 	{ "fsl,imx8mp-usb-phy", imx8mp_usb_init },
359 	{ "fsl,imx8mq-usb-phy", imx8mq_usb_init },
360 	{ "samsung,exynos5250-usbdrd-phy", exynos5_usbdrd_init },
361 	{ "samsung,exynos5420-usbdrd-phy", exynos5_usbdrd_init },
362 	{ "usb-nop-xceiv", nop_xceiv_init },
363 };
364 
365 uint32_t *
xhci_next_phy(uint32_t * cells)366 xhci_next_phy(uint32_t *cells)
367 {
368 	uint32_t phandle = cells[0];
369 	int node, ncells;
370 
371 	node = OF_getnodebyphandle(phandle);
372 	if (node == 0)
373 		return NULL;
374 
375 	ncells = OF_getpropint(node, "#phy-cells", 0);
376 	return cells + ncells + 1;
377 }
378 
379 void
xhci_init_phy(struct xhci_fdt_softc * sc,uint32_t * cells)380 xhci_init_phy(struct xhci_fdt_softc *sc, uint32_t *cells)
381 {
382 	int node;
383 	int i;
384 
385 	node = OF_getnodebyphandle(cells[0]);
386 	if (node == 0)
387 		return;
388 
389 	for (i = 0; i < nitems(xhci_phys); i++) {
390 		if (OF_is_compatible(node, xhci_phys[i].compat)) {
391 			xhci_phys[i].init(sc, cells);
392 			return;
393 		}
394 	}
395 }
396 
397 void
xhci_phy_enable(struct xhci_fdt_softc * sc,char * name)398 xhci_phy_enable(struct xhci_fdt_softc *sc, char *name)
399 {
400 	uint32_t *phys;
401 	uint32_t *phy;
402 	int idx, len;
403 
404 	idx = OF_getindex(sc->sc_node, name, "phy-names");
405 	if (idx < 0)
406 		return;
407 
408 	len = OF_getproplen(sc->sc_node, "phys");
409 	if (len <= 0)
410 		return;
411 
412 	phys = malloc(len, M_TEMP, M_WAITOK);
413 	OF_getpropintarray(sc->sc_node, "phys", phys, len);
414 
415 	phy = phys;
416 	while (phy && phy < phys + (len / sizeof(uint32_t))) {
417 		if (idx == 0) {
418 			xhci_init_phy(sc, phy);
419 			free(phys, M_TEMP, len);
420 			return;
421 		}
422 
423 		phy = xhci_next_phy(phy);
424 		idx--;
425 	}
426 	free(phys, M_TEMP, len);
427 }
428 
429 void
xhci_init_phys(struct xhci_fdt_softc * sc)430 xhci_init_phys(struct xhci_fdt_softc *sc)
431 {
432 	int rv;
433 
434 	rv = phy_enable_prop_idx(sc->sc_node, "usb-phy", 0);
435 	if (rv != 0) {
436 		rv = phy_enable(sc->sc_node, "usb2-phy");
437 		if (rv != 0)
438 			xhci_phy_enable(sc, "usb2-phy");
439 	}
440 
441 	rv = phy_enable_prop_idx(sc->sc_node, "usb-phy", 1);
442 	if (rv != 0) {
443 		rv = phy_enable(sc->sc_node, "usb3-phy");
444 		if (rv != 0)
445 			xhci_phy_enable(sc, "usb3-phy");
446 	}
447 }
448 
449 /*
450  * Samsung Exynos 5 PHYs.
451  */
452 
453 /* Registers */
454 #define EXYNOS5_PHYUTMI			0x0008
455 #define  EXYNOS5_PHYUTMI_OTGDISABLE	(1 << 6)
456 #define EXYNOS5_PHYCLKRST		0x0010
457 #define  EXYNOS5_PHYCLKRST_SSC_EN	(1 << 20)
458 #define  EXYNOS5_PHYCLKRST_REF_SSP_EN	(1 << 19)
459 #define  EXYNOS5_PHYCLKRST_PORTRESET	(1 << 1)
460 #define  EXYNOS5_PHYCLKRST_COMMONONN	(1 << 0)
461 #define EXYNOS5_PHYTEST			0x0028
462 #define  EXYNOS5_PHYTEST_POWERDOWN_SSP	(1 << 3)
463 #define  EXYNOS5_PHYTEST_POWERDOWN_HSP	(1 << 2)
464 
465 /* PMU registers */
466 #define EXYNOS5_USBDRD0_POWER		0x0704
467 #define EXYNOS5420_USBDRD1_POWER	0x0708
468 #define  EXYNOS5_USBDRD_POWER_EN	(1 << 0)
469 
470 void
exynos5_usbdrd_init(struct xhci_fdt_softc * sc,uint32_t * cells)471 exynos5_usbdrd_init(struct xhci_fdt_softc *sc, uint32_t *cells)
472 {
473 	uint32_t phy_reg[2];
474 	struct regmap *pmurm;
475 	uint32_t pmureg;
476 	uint32_t val;
477 	bus_size_t offset;
478 	int node;
479 
480 	node = OF_getnodebyphandle(cells[0]);
481 	KASSERT(node != 0);
482 
483 	if (OF_getpropintarray(node, "reg", phy_reg,
484 	    sizeof(phy_reg)) != sizeof(phy_reg))
485 		return;
486 
487 	if (bus_space_map(sc->sc.iot, phy_reg[0],
488 	    phy_reg[1], 0, &sc->ph_ioh)) {
489 		printf("%s: can't map PHY registers\n",
490 		    sc->sc.sc_bus.bdev.dv_xname);
491 		return;
492 	}
493 
494 	/* Power up the PHY block. */
495 	pmureg = OF_getpropint(node, "samsung,pmu-syscon", 0);
496 	pmurm = regmap_byphandle(pmureg);
497 	if (pmurm) {
498 		node = OF_getnodebyphandle(pmureg);
499 		if (sc->sc.sc_bus.bdev.dv_unit == 0)
500 			offset = EXYNOS5_USBDRD0_POWER;
501 		else
502 			offset = EXYNOS5420_USBDRD1_POWER;
503 
504 		val = regmap_read_4(pmurm, offset);
505 		val |= EXYNOS5_USBDRD_POWER_EN;
506 		regmap_write_4(pmurm, offset, val);
507 	}
508 
509 	/* Initialize the PHY.  Assumes U-Boot has done initial setup. */
510 	val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYTEST);
511 	CLR(val, EXYNOS5_PHYTEST_POWERDOWN_SSP);
512 	CLR(val, EXYNOS5_PHYTEST_POWERDOWN_HSP);
513 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYTEST, val);
514 
515 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYUTMI,
516 	    EXYNOS5_PHYUTMI_OTGDISABLE);
517 
518 	val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYCLKRST);
519 	SET(val, EXYNOS5_PHYCLKRST_SSC_EN);
520 	SET(val, EXYNOS5_PHYCLKRST_REF_SSP_EN);
521 	SET(val, EXYNOS5_PHYCLKRST_COMMONONN);
522 	SET(val, EXYNOS5_PHYCLKRST_PORTRESET);
523 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYCLKRST, val);
524 	delay(10);
525 	CLR(val, EXYNOS5_PHYCLKRST_PORTRESET);
526 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYCLKRST, val);
527 }
528 
529 /*
530  * i.MX8MQ PHYs.
531  */
532 
533 /* Registers */
534 #define IMX8MQ_PHY_CTRL0			0x0000
535 #define  IMX8MQ_PHY_CTRL0_REF_SSP_EN			(1 << 2)
536 #define  IMX8MQ_PHY_CTRL0_FSEL_24M			(0x2a << 5)
537 #define  IMX8MQ_PHY_CTRL0_FSEL_MASK			(0x3f << 5)
538 #define IMX8MQ_PHY_CTRL1			0x0004
539 #define  IMX8MQ_PHY_CTRL1_RESET				(1 << 0)
540 #define  IMX8MQ_PHY_CTRL1_ATERESET			(1 << 3)
541 #define  IMX8MQ_PHY_CTRL1_VDATSRCENB0			(1 << 19)
542 #define  IMX8MQ_PHY_CTRL1_VDATDETENB0			(1 << 20)
543 #define IMX8MQ_PHY_CTRL2			0x0008
544 #define  IMX8MQ_PHY_CTRL2_TXENABLEN0			(1 << 8)
545 #define  IMX8MQ_PHY_CTRL2_OTG_DISABLE			(1 << 9)
546 #define IMX8MQ_PHY_CTRL6			0x0018
547 #define  IMX8MQ_PHY_CTRL6_ALT_CLK_SEL			(1 << 0)
548 #define  IMX8MQ_PHY_CTRL6_ALT_CLK_EN			(1 << 1)
549 
550 void
imx8mp_usb_init(struct xhci_fdt_softc * sc,uint32_t * cells)551 imx8mp_usb_init(struct xhci_fdt_softc *sc, uint32_t *cells)
552 {
553 	uint32_t phy_reg[2], reg;
554 	int node, vbus_supply;
555 
556 	node = OF_getnodebyphandle(cells[0]);
557 	KASSERT(node != 0);
558 
559 	if (OF_getpropintarray(node, "reg", phy_reg,
560 	    sizeof(phy_reg)) != sizeof(phy_reg))
561 		return;
562 
563 	if (bus_space_map(sc->sc.iot, phy_reg[0],
564 	    phy_reg[1], 0, &sc->ph_ioh)) {
565 		printf("%s: can't map PHY registers\n",
566 		    sc->sc.sc_bus.bdev.dv_xname);
567 		return;
568 	}
569 
570 	clock_set_assigned(node);
571 	clock_enable_all(node);
572 
573 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL0);
574 	reg &= ~IMX8MQ_PHY_CTRL0_FSEL_MASK;
575 	reg |= IMX8MQ_PHY_CTRL0_FSEL_24M;
576 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL0, reg);
577 
578 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL6);
579 	reg &= ~(IMX8MQ_PHY_CTRL6_ALT_CLK_SEL | IMX8MQ_PHY_CTRL6_ALT_CLK_EN);
580 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL6, reg);
581 
582 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL1);
583 	reg &= ~(IMX8MQ_PHY_CTRL1_VDATSRCENB0 | IMX8MQ_PHY_CTRL1_VDATDETENB0);
584 	reg |= IMX8MQ_PHY_CTRL1_RESET | IMX8MQ_PHY_CTRL1_ATERESET;
585 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL1, reg);
586 
587 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL0);
588 	reg |= IMX8MQ_PHY_CTRL0_REF_SSP_EN;
589 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL0, reg);
590 
591 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL2);
592 	reg |= IMX8MQ_PHY_CTRL2_TXENABLEN0 | IMX8MQ_PHY_CTRL2_OTG_DISABLE;
593 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL2, reg);
594 
595 	delay(10);
596 
597 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL1);
598 	reg &= ~(IMX8MQ_PHY_CTRL1_RESET | IMX8MQ_PHY_CTRL1_ATERESET);
599 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL1, reg);
600 
601 	vbus_supply = OF_getpropint(node, "vbus-supply", 0);
602 	if (vbus_supply)
603 		regulator_enable(vbus_supply);
604 }
605 
606 void
imx8mq_usb_init(struct xhci_fdt_softc * sc,uint32_t * cells)607 imx8mq_usb_init(struct xhci_fdt_softc *sc, uint32_t *cells)
608 {
609 	uint32_t phy_reg[2], reg;
610 	int node, vbus_supply;
611 
612 	node = OF_getnodebyphandle(cells[0]);
613 	KASSERT(node != 0);
614 
615 	if (OF_getpropintarray(node, "reg", phy_reg,
616 	    sizeof(phy_reg)) != sizeof(phy_reg))
617 		return;
618 
619 	if (bus_space_map(sc->sc.iot, phy_reg[0],
620 	    phy_reg[1], 0, &sc->ph_ioh)) {
621 		printf("%s: can't map PHY registers\n",
622 		    sc->sc.sc_bus.bdev.dv_xname);
623 		return;
624 	}
625 
626 	clock_set_assigned(node);
627 	clock_enable_all(node);
628 
629 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL1);
630 	reg &= ~(IMX8MQ_PHY_CTRL1_VDATSRCENB0 | IMX8MQ_PHY_CTRL1_VDATDETENB0);
631 	reg |= IMX8MQ_PHY_CTRL1_RESET | IMX8MQ_PHY_CTRL1_ATERESET;
632 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL1, reg);
633 
634 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL0);
635 	reg |= IMX8MQ_PHY_CTRL0_REF_SSP_EN;
636 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL0, reg);
637 
638 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL2);
639 	reg |= IMX8MQ_PHY_CTRL2_TXENABLEN0;
640 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL2, reg);
641 
642 	reg = bus_space_read_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL1);
643 	reg &= ~(IMX8MQ_PHY_CTRL1_RESET | IMX8MQ_PHY_CTRL1_ATERESET);
644 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, IMX8MQ_PHY_CTRL1, reg);
645 
646 	vbus_supply = OF_getpropint(node, "vbus-supply", 0);
647 	if (vbus_supply)
648 		regulator_enable(vbus_supply);
649 }
650 
651 void
nop_xceiv_init(struct xhci_fdt_softc * sc,uint32_t * cells)652 nop_xceiv_init(struct xhci_fdt_softc *sc, uint32_t *cells)
653 {
654 	uint32_t vcc_supply;
655 	int node;
656 
657 	node = OF_getnodebyphandle(cells[0]);
658 	KASSERT(node != 0);
659 
660 	vcc_supply = OF_getpropint(node, "vcc-supply", 0);
661 	if (vcc_supply)
662 		regulator_enable(vcc_supply);
663 }
664