1 /* $OpenBSD: imxehci.c,v 1.4 2020/04/27 20:15:41 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, "fsl,usbphy", 134 phy, sizeof(phy)) != sizeof(phy)) 135 return; 136 137 if (OF_getpropintarray(faa->fa_node, "fsl,usbmisc", 138 misc, sizeof(misc)) != sizeof(misc)) 139 return; 140 141 misc_node = OF_getnodebyphandle(misc[0]); 142 if (misc_node == 0) 143 return; 144 145 if (OF_getpropintarray(misc_node, "reg", misc_reg, 146 sizeof(misc_reg)) != sizeof(misc_reg)) 147 return; 148 149 sc->sc.iot = faa->fa_iot; 150 sc->sc.sc_bus.dmatag = faa->fa_dmat; 151 sc->sc.sc_size = faa->fa_reg[0].size - USB_EHCI_OFFSET; 152 sc->sc.sc_flags = EHCIF_USBMODE; 153 sc->sc_unit = misc[1]; 154 155 /* Map I/O space */ 156 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr, 157 faa->fa_reg[0].size, 0, &sc->uh_ioh)) { 158 printf(": cannot map mem space\n"); 159 goto out; 160 } 161 if (bus_space_subregion(sc->sc.iot, sc->uh_ioh, USB_EHCI_OFFSET, 162 sc->sc.sc_size, &sc->sc.ioh)) { 163 printf(": cannot map mem space\n"); 164 goto mem0; 165 } 166 167 if (bus_space_map(sc->sc.iot, misc_reg[0], 168 misc_reg[1], 0, &sc->nc_ioh)) { 169 printf(": cannot map mem space\n"); 170 goto mem1; 171 } 172 173 printf("\n"); 174 175 pinctrl_byname(faa->fa_node, "default"); 176 power_domain_enable(faa->fa_node); 177 clock_set_assigned(faa->fa_node); 178 clock_enable(faa->fa_node, NULL); 179 delay(1000); 180 181 /* over current and polarity setting */ 182 switch (misc[1]) { 183 case 0: 184 off = USBNC_USB_OTG_CTRL; 185 break; 186 case 1: 187 off = USBNC_USB_UH1_CTRL; 188 break; 189 default: 190 printf("%s: invalid usbmisc property\n", devname); 191 return; 192 } 193 194 reg = bus_space_read_4(sc->sc.iot, sc->nc_ioh, off); 195 reg &= ~USBNC_USB_CTRL_OVER_CUR_DIS; 196 if (OF_getproplen(faa->fa_node, "disable-over-current") == 0) 197 reg |= USBNC_USB_CTRL_OVER_CUR_DIS; 198 if (OF_getproplen(faa->fa_node, "over-current-active-low") == 0) 199 reg |= USBNC_USB_CTRL_OVER_CUR_POL; 200 else if (OF_getproplen(faa->fa_node, "over-current-active-high") == 0) 201 reg &= ~USBNC_USB_CTRL_OVER_CUR_POL; 202 if (OF_getproplen(faa->fa_node, "power-active-high") == 0) 203 reg |= USBNC_USB_CTRL_PWR_POL; 204 reg |= USBNC_USB_CTRL_NON_BURST; 205 bus_space_write_4(sc->sc.iot, sc->nc_ioh, off, reg); 206 207 /* enable usb bus power */ 208 vbus = OF_getpropint(faa->fa_node, "vbus-supply", 0); 209 if (vbus) 210 regulator_enable(vbus); 211 212 /* Disable interrupts, so we don't get any spurious ones. */ 213 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH); 214 EOWRITE2(&sc->sc, EHCI_USBINTR, 0); 215 216 /* Stop then Reset */ 217 uint32_t val = EOREAD4(&sc->sc, EHCI_USBCMD); 218 val &= ~EHCI_CMD_RS; 219 EOWRITE4(&sc->sc, EHCI_USBCMD, val); 220 221 while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_RS) 222 ; 223 224 val = EOREAD4(&sc->sc, EHCI_USBCMD); 225 val |= EHCI_CMD_HCRESET; 226 EOWRITE4(&sc->sc, EHCI_USBCMD, val); 227 228 while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_HCRESET) 229 ; 230 231 /* power up PHY */ 232 imxehci_init_phy(sc, phy); 233 234 /* set host mode */ 235 EOWRITE4(&sc->sc, EHCI_USBMODE, 236 EOREAD4(&sc->sc, EHCI_USBMODE) | EHCI_USBMODE_CM_HOST); 237 238 /* set to UTMI mode */ 239 EOWRITE4(&sc->sc, EHCI_PORTSC(1), 240 EOREAD4(&sc->sc, EHCI_PORTSC(1)) & ~EHCI_PS_PTS_UTMI_MASK); 241 242 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB, 243 ehci_intr, &sc->sc, devname); 244 if (sc->sc_ih == NULL) { 245 printf(": unable to establish interrupt\n"); 246 goto mem2; 247 } 248 249 strlcpy(sc->sc.sc_vendor, "i.MX", sizeof(sc->sc.sc_vendor)); 250 r = ehci_init(&sc->sc); 251 if (r != USBD_NORMAL_COMPLETION) { 252 printf("%s: init failed, error=%d\n", devname, r); 253 goto intr; 254 } 255 256 config_found(self, &sc->sc.sc_bus, usbctlprint); 257 258 goto out; 259 260 intr: 261 fdt_intr_disestablish(sc->sc_ih); 262 sc->sc_ih = NULL; 263 mem2: 264 bus_space_unmap(sc->sc.iot, sc->nc_ioh, misc_reg[1]); 265 mem1: 266 mem0: 267 bus_space_unmap(sc->sc.iot, sc->sc.ioh, faa->fa_reg[0].size); 268 sc->sc.sc_size = 0; 269 out: 270 return; 271 } 272 273 int 274 imxehci_detach(struct device *self, int flags) 275 { 276 struct imxehci_softc *sc = (struct imxehci_softc *)self; 277 int rv; 278 279 rv = ehci_detach(self, flags); 280 if (rv) 281 return (rv); 282 283 if (sc->sc_ih != NULL) { 284 fdt_intr_disestablish(sc->sc_ih); 285 sc->sc_ih = NULL; 286 } 287 288 if (sc->sc.sc_size) { 289 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 290 sc->sc.sc_size = 0; 291 } 292 293 return (0); 294 } 295 296 /* 297 * PHY initialization. 298 */ 299 300 struct imxehci_phy { 301 const char *compat; 302 void (*init)(struct imxehci_softc *, uint32_t *); 303 }; 304 305 void imx23_usb_init(struct imxehci_softc *, uint32_t *); 306 static void nop_xceiv_init(struct imxehci_softc *, uint32_t *); 307 308 struct imxehci_phy imxehci_phys[] = { 309 { "fsl,imx23-usbphy", imx23_usb_init }, 310 { "usb-nop-xceiv", nop_xceiv_init }, 311 }; 312 313 void 314 imxehci_init_phy(struct imxehci_softc *sc, uint32_t *cells) 315 { 316 int node; 317 int i; 318 319 node = OF_getnodebyphandle(cells[0]); 320 if (node == 0) 321 return; 322 323 for (i = 0; i < nitems(imxehci_phys); i++) { 324 if (OF_is_compatible(node, imxehci_phys[i].compat)) { 325 imxehci_phys[i].init(sc, cells); 326 return; 327 } 328 } 329 } 330 331 /* 332 * i.MX5/6 PHYs. 333 */ 334 335 void 336 imx23_usb_init(struct imxehci_softc *sc, uint32_t *cells) 337 { 338 struct regmap *rm = NULL; 339 uint32_t phy_reg[2]; 340 uint32_t anatop[1]; 341 int node; 342 343 node = OF_getnodebyphandle(cells[0]); 344 KASSERT(node != 0); 345 346 if (OF_getpropintarray(node, "reg", phy_reg, 347 sizeof(phy_reg)) != sizeof(phy_reg)) 348 return; 349 350 if (bus_space_map(sc->sc.iot, phy_reg[0], 351 phy_reg[1], 0, &sc->ph_ioh)) { 352 printf("%s: can't map PHY registers\n", 353 sc->sc.sc_bus.bdev.dv_xname); 354 return; 355 } 356 357 if (OF_getpropintarray(node, "fsl,anatop", 358 anatop, sizeof(anatop)) == sizeof(anatop)) 359 rm = regmap_byphandle(anatop[0]); 360 361 /* Disable the carger detection, else signal on DP will be poor */ 362 switch (sc->sc_unit) { 363 case 0: 364 if (rm != NULL) 365 regmap_write_4(rm, ANALOG_USB1_CHRG_DETECT_SET, 366 ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B | 367 ANALOG_USB1_CHRG_DETECT_EN_B); 368 break; 369 case 1: 370 if (rm != NULL) 371 regmap_write_4(rm, ANALOG_USB2_CHRG_DETECT_SET, 372 ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B | 373 ANALOG_USB2_CHRG_DETECT_EN_B); 374 break; 375 } 376 377 clock_enable(node, NULL); 378 379 /* Reset USBPHY module */ 380 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET, 381 USBPHY_CTRL_SFTRST); 382 383 delay(10); 384 385 /* Remove CLKGATE and SFTRST */ 386 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_CLR, 387 USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST); 388 389 delay(10); 390 391 /* Power up the PHY */ 392 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_PWD, 0); 393 394 /* enable FS/LS device */ 395 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET, 396 USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3); 397 } 398 399 /* 400 * i.MX7 PHYs. 401 */ 402 403 static void 404 nop_xceiv_init(struct imxehci_softc *sc, uint32_t *cells) 405 { 406 int node; 407 408 node = OF_getnodebyphandle(cells[0]); 409 KASSERT(node != 0); 410 411 clock_set_assigned(node); 412 clock_enable(node, NULL); 413 } 414