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