1 /* $OpenBSD: imxehci.c,v 1.5 2021/11/05 09:36:30 patrick Exp $ */ 2 /* 3 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> 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/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/kernel.h> 22 #include <sys/rwlock.h> 23 #include <sys/timeout.h> 24 25 #include <machine/intr.h> 26 #include <machine/bus.h> 27 #include <machine/fdt.h> 28 29 #include <dev/usb/usb.h> 30 #include <dev/usb/usbdi.h> 31 #include <dev/usb/usbdivar.h> 32 #include <dev/usb/usb_mem.h> 33 34 #include <dev/ofw/openfirm.h> 35 #include <dev/ofw/ofw_clock.h> 36 #include <dev/ofw/ofw_gpio.h> 37 #include <dev/ofw/ofw_misc.h> 38 #include <dev/ofw/ofw_pinctrl.h> 39 #include <dev/ofw/ofw_power.h> 40 #include <dev/ofw/ofw_regulator.h> 41 #include <dev/ofw/fdt.h> 42 43 #include <dev/usb/ehcireg.h> 44 #include <dev/usb/ehcivar.h> 45 46 /* usb phy */ 47 #define USBPHY_PWD 0x00 48 #define USBPHY_CTRL 0x30 49 #define USBPHY_CTRL_SET 0x34 50 #define USBPHY_CTRL_CLR 0x38 51 #define USBPHY_CTRL_TOG 0x3c 52 53 #define USBPHY_CTRL_ENUTMILEVEL2 (1 << 14) 54 #define USBPHY_CTRL_ENUTMILEVEL3 (1 << 15) 55 #define USBPHY_CTRL_CLKGATE (1 << 30) 56 #define USBPHY_CTRL_SFTRST (1U << 31) 57 58 /* ehci */ 59 #define USB_EHCI_OFFSET 0x100 60 61 #define EHCI_PS_PTS_UTMI_MASK ((1 << 25) | (3 << 30)) 62 63 /* usb non-core */ 64 #define USBNC_USB_OTG_CTRL 0x00 65 #define USBNC_USB_UH1_CTRL 0x04 66 67 #define USBNC_USB_CTRL_PWR_POL (1 << 9) 68 #define USBNC_USB_CTRL_OVER_CUR_POL (1 << 8) 69 #define USBNC_USB_CTRL_OVER_CUR_DIS (1 << 7) 70 #define USBNC_USB_CTRL_NON_BURST (1 << 1) 71 72 /* anatop */ 73 #define ANALOG_USB1_CHRG_DETECT 0x1b0 74 #define ANALOG_USB1_CHRG_DETECT_SET 0x1b4 75 #define ANALOG_USB1_CHRG_DETECT_CLR 0x1b8 76 #define ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B (1 << 19) 77 #define ANALOG_USB1_CHRG_DETECT_EN_B (1 << 20) 78 #define ANALOG_USB2_CHRG_DETECT 0x210 79 #define ANALOG_USB2_CHRG_DETECT_SET 0x214 80 #define ANALOG_USB2_CHRG_DETECT_CLR 0x218 81 #define ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B (1 << 19) 82 #define ANALOG_USB2_CHRG_DETECT_EN_B (1 << 20) 83 84 int imxehci_match(struct device *, void *, void *); 85 void imxehci_attach(struct device *, struct device *, void *); 86 int imxehci_detach(struct device *, int); 87 88 struct imxehci_softc { 89 struct ehci_softc sc; 90 void *sc_ih; 91 bus_space_handle_t uh_ioh; 92 bus_space_handle_t ph_ioh; 93 bus_space_handle_t nc_ioh; 94 uint32_t sc_unit; 95 }; 96 97 struct cfattach imxehci_ca = { 98 sizeof (struct imxehci_softc), imxehci_match, imxehci_attach, 99 imxehci_detach 100 }; 101 102 struct cfdriver imxehci_cd = { 103 NULL, "imxehci", DV_DULL 104 }; 105 106 void imxehci_init_phy(struct imxehci_softc *, uint32_t *); 107 108 int 109 imxehci_match(struct device *parent, void *match, void *aux) 110 { 111 struct fdt_attach_args *faa = aux; 112 113 return (OF_is_compatible(faa->fa_node, "fsl,imx27-usb") || 114 OF_is_compatible(faa->fa_node, "fsl,imx7d-usb")); 115 } 116 117 void 118 imxehci_attach(struct device *parent, struct device *self, void *aux) 119 { 120 struct imxehci_softc *sc = (struct imxehci_softc *)self; 121 struct fdt_attach_args *faa = aux; 122 usbd_status r; 123 char *devname = sc->sc.sc_bus.bdev.dv_xname; 124 uint32_t phy[1], misc[2]; 125 uint32_t misc_reg[2]; 126 uint32_t off, reg; 127 uint32_t vbus; 128 int misc_node; 129 130 if (faa->fa_nreg < 1) 131 return; 132 133 if (OF_getpropintarray(faa->fa_node, "phys", 134 phy, sizeof(phy)) != sizeof(phy)) { 135 if (OF_getpropintarray(faa->fa_node, "fsl,usbphy", 136 phy, sizeof(phy)) != sizeof(phy)) 137 return; 138 } 139 140 if (OF_getpropintarray(faa->fa_node, "fsl,usbmisc", 141 misc, sizeof(misc)) != sizeof(misc)) 142 return; 143 144 misc_node = OF_getnodebyphandle(misc[0]); 145 if (misc_node == 0) 146 return; 147 148 if (OF_getpropintarray(misc_node, "reg", misc_reg, 149 sizeof(misc_reg)) != sizeof(misc_reg)) 150 return; 151 152 sc->sc.iot = faa->fa_iot; 153 sc->sc.sc_bus.dmatag = faa->fa_dmat; 154 sc->sc.sc_size = faa->fa_reg[0].size - USB_EHCI_OFFSET; 155 sc->sc.sc_flags = EHCIF_USBMODE; 156 sc->sc_unit = misc[1]; 157 158 /* Map I/O space */ 159 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr, 160 faa->fa_reg[0].size, 0, &sc->uh_ioh)) { 161 printf(": cannot map mem space\n"); 162 goto out; 163 } 164 if (bus_space_subregion(sc->sc.iot, sc->uh_ioh, USB_EHCI_OFFSET, 165 sc->sc.sc_size, &sc->sc.ioh)) { 166 printf(": cannot map mem space\n"); 167 goto mem0; 168 } 169 170 if (bus_space_map(sc->sc.iot, misc_reg[0], 171 misc_reg[1], 0, &sc->nc_ioh)) { 172 printf(": cannot map mem space\n"); 173 goto mem1; 174 } 175 176 printf("\n"); 177 178 pinctrl_byname(faa->fa_node, "default"); 179 power_domain_enable(faa->fa_node); 180 clock_set_assigned(faa->fa_node); 181 clock_enable(faa->fa_node, NULL); 182 delay(1000); 183 184 /* over current and polarity setting */ 185 switch (misc[1]) { 186 case 0: 187 off = USBNC_USB_OTG_CTRL; 188 break; 189 case 1: 190 off = USBNC_USB_UH1_CTRL; 191 break; 192 default: 193 printf("%s: invalid usbmisc property\n", devname); 194 return; 195 } 196 197 reg = bus_space_read_4(sc->sc.iot, sc->nc_ioh, off); 198 reg &= ~USBNC_USB_CTRL_OVER_CUR_DIS; 199 if (OF_getproplen(faa->fa_node, "disable-over-current") == 0) 200 reg |= USBNC_USB_CTRL_OVER_CUR_DIS; 201 if (OF_getproplen(faa->fa_node, "over-current-active-low") == 0) 202 reg |= USBNC_USB_CTRL_OVER_CUR_POL; 203 else if (OF_getproplen(faa->fa_node, "over-current-active-high") == 0) 204 reg &= ~USBNC_USB_CTRL_OVER_CUR_POL; 205 if (OF_getproplen(faa->fa_node, "power-active-high") == 0) 206 reg |= USBNC_USB_CTRL_PWR_POL; 207 reg |= USBNC_USB_CTRL_NON_BURST; 208 bus_space_write_4(sc->sc.iot, sc->nc_ioh, off, reg); 209 210 /* enable usb bus power */ 211 vbus = OF_getpropint(faa->fa_node, "vbus-supply", 0); 212 if (vbus) 213 regulator_enable(vbus); 214 215 /* Disable interrupts, so we don't get any spurious ones. */ 216 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH); 217 EOWRITE2(&sc->sc, EHCI_USBINTR, 0); 218 219 /* Stop then Reset */ 220 uint32_t val = EOREAD4(&sc->sc, EHCI_USBCMD); 221 val &= ~EHCI_CMD_RS; 222 EOWRITE4(&sc->sc, EHCI_USBCMD, val); 223 224 while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_RS) 225 ; 226 227 val = EOREAD4(&sc->sc, EHCI_USBCMD); 228 val |= EHCI_CMD_HCRESET; 229 EOWRITE4(&sc->sc, EHCI_USBCMD, val); 230 231 while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_HCRESET) 232 ; 233 234 /* power up PHY */ 235 imxehci_init_phy(sc, phy); 236 237 /* set host mode */ 238 EOWRITE4(&sc->sc, EHCI_USBMODE, 239 EOREAD4(&sc->sc, EHCI_USBMODE) | EHCI_USBMODE_CM_HOST); 240 241 /* set to UTMI mode */ 242 EOWRITE4(&sc->sc, EHCI_PORTSC(1), 243 EOREAD4(&sc->sc, EHCI_PORTSC(1)) & ~EHCI_PS_PTS_UTMI_MASK); 244 245 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB, 246 ehci_intr, &sc->sc, devname); 247 if (sc->sc_ih == NULL) { 248 printf(": unable to establish interrupt\n"); 249 goto mem2; 250 } 251 252 strlcpy(sc->sc.sc_vendor, "i.MX", sizeof(sc->sc.sc_vendor)); 253 r = ehci_init(&sc->sc); 254 if (r != USBD_NORMAL_COMPLETION) { 255 printf("%s: init failed, error=%d\n", devname, r); 256 goto intr; 257 } 258 259 config_found(self, &sc->sc.sc_bus, usbctlprint); 260 261 goto out; 262 263 intr: 264 fdt_intr_disestablish(sc->sc_ih); 265 sc->sc_ih = NULL; 266 mem2: 267 bus_space_unmap(sc->sc.iot, sc->nc_ioh, misc_reg[1]); 268 mem1: 269 mem0: 270 bus_space_unmap(sc->sc.iot, sc->sc.ioh, faa->fa_reg[0].size); 271 sc->sc.sc_size = 0; 272 out: 273 return; 274 } 275 276 int 277 imxehci_detach(struct device *self, int flags) 278 { 279 struct imxehci_softc *sc = (struct imxehci_softc *)self; 280 int rv; 281 282 rv = ehci_detach(self, flags); 283 if (rv) 284 return (rv); 285 286 if (sc->sc_ih != NULL) { 287 fdt_intr_disestablish(sc->sc_ih); 288 sc->sc_ih = NULL; 289 } 290 291 if (sc->sc.sc_size) { 292 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 293 sc->sc.sc_size = 0; 294 } 295 296 return (0); 297 } 298 299 /* 300 * PHY initialization. 301 */ 302 303 struct imxehci_phy { 304 const char *compat; 305 void (*init)(struct imxehci_softc *, uint32_t *); 306 }; 307 308 void imx23_usb_init(struct imxehci_softc *, uint32_t *); 309 static void nop_xceiv_init(struct imxehci_softc *, uint32_t *); 310 311 struct imxehci_phy imxehci_phys[] = { 312 { "fsl,imx23-usbphy", imx23_usb_init }, 313 { "usb-nop-xceiv", nop_xceiv_init }, 314 }; 315 316 void 317 imxehci_init_phy(struct imxehci_softc *sc, uint32_t *cells) 318 { 319 int node; 320 int i; 321 322 node = OF_getnodebyphandle(cells[0]); 323 if (node == 0) 324 return; 325 326 for (i = 0; i < nitems(imxehci_phys); i++) { 327 if (OF_is_compatible(node, imxehci_phys[i].compat)) { 328 imxehci_phys[i].init(sc, cells); 329 return; 330 } 331 } 332 } 333 334 /* 335 * i.MX5/6 PHYs. 336 */ 337 338 void 339 imx23_usb_init(struct imxehci_softc *sc, uint32_t *cells) 340 { 341 struct regmap *rm = NULL; 342 uint32_t phy_reg[2]; 343 uint32_t anatop[1]; 344 int node; 345 346 node = OF_getnodebyphandle(cells[0]); 347 KASSERT(node != 0); 348 349 if (OF_getpropintarray(node, "reg", phy_reg, 350 sizeof(phy_reg)) != sizeof(phy_reg)) 351 return; 352 353 if (bus_space_map(sc->sc.iot, phy_reg[0], 354 phy_reg[1], 0, &sc->ph_ioh)) { 355 printf("%s: can't map PHY registers\n", 356 sc->sc.sc_bus.bdev.dv_xname); 357 return; 358 } 359 360 if (OF_getpropintarray(node, "fsl,anatop", 361 anatop, sizeof(anatop)) == sizeof(anatop)) 362 rm = regmap_byphandle(anatop[0]); 363 364 /* Disable the carger detection, else signal on DP will be poor */ 365 switch (sc->sc_unit) { 366 case 0: 367 if (rm != NULL) 368 regmap_write_4(rm, ANALOG_USB1_CHRG_DETECT_SET, 369 ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B | 370 ANALOG_USB1_CHRG_DETECT_EN_B); 371 break; 372 case 1: 373 if (rm != NULL) 374 regmap_write_4(rm, ANALOG_USB2_CHRG_DETECT_SET, 375 ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B | 376 ANALOG_USB2_CHRG_DETECT_EN_B); 377 break; 378 } 379 380 clock_enable(node, NULL); 381 382 /* Reset USBPHY module */ 383 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET, 384 USBPHY_CTRL_SFTRST); 385 386 delay(10); 387 388 /* Remove CLKGATE and SFTRST */ 389 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_CLR, 390 USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST); 391 392 delay(10); 393 394 /* Power up the PHY */ 395 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_PWD, 0); 396 397 /* enable FS/LS device */ 398 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET, 399 USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3); 400 } 401 402 /* 403 * i.MX7 PHYs. 404 */ 405 406 static void 407 nop_xceiv_init(struct imxehci_softc *sc, uint32_t *cells) 408 { 409 int node; 410 411 node = OF_getnodebyphandle(cells[0]); 412 KASSERT(node != 0); 413 414 clock_set_assigned(node); 415 clock_enable(node, NULL); 416 } 417