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