1 /* $OpenBSD: omehci.c,v 1.9 2021/10/24 17:52:27 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /*- 20 * Copyright (c) 2011 21 * Ben Gray <ben.r.gray@gmail.com>. 22 * All rights reserved. 23 * 24 * Redistribution and use in source and binary forms, with or without 25 * modification, are permitted provided that the following conditions 26 * are met: 27 * 1. Redistributions of source code must retain the above copyright 28 * notice, this list of conditions and the following disclaimer. 29 * 2. Redistributions in binary form must reproduce the above copyright 30 * notice, this list of conditions and the following disclaimer in the 31 * documentation and/or other materials provided with the distribution. 32 * 33 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 43 * SUCH DAMAGE. 44 */ 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/device.h> 49 #include <sys/kernel.h> 50 51 #include <machine/intr.h> 52 #include <machine/bus.h> 53 #include <machine/fdt.h> 54 55 #include <dev/usb/usb.h> 56 #include <dev/usb/usbdi.h> 57 #include <dev/usb/usbdivar.h> 58 59 #include <armv7/omap/prcmvar.h> 60 #include <armv7/omap/omehcivar.h> 61 62 #include <dev/ofw/openfirm.h> 63 #include <dev/ofw/ofw_misc.h> 64 #include <dev/ofw/fdt.h> 65 66 #include <dev/usb/ehcireg.h> 67 #include <dev/usb/ehcivar.h> 68 69 int omehci_match(struct device *, void *, void *); 70 void omehci_attach(struct device *, struct device *, void *); 71 int omehci_detach(struct device *, int); 72 int omehci_activate(struct device *, int); 73 74 struct omehci_softc { 75 struct ehci_softc sc; 76 void *sc_ih; 77 bus_space_handle_t uhh_ioh; 78 bus_space_handle_t tll_ioh; 79 80 uint32_t ehci_rev; 81 uint32_t tll_avail; 82 83 uint32_t port_mode[OMAP_HS_USB_PORTS]; 84 }; 85 86 int omehci_init(struct omehci_softc *); 87 void omehci_soft_phy_reset(struct omehci_softc *sc, unsigned int port); 88 void omehci_enable(struct omehci_softc *); 89 void omehci_disable(struct omehci_softc *); 90 void omehci_utmi_init(struct omehci_softc *sc, unsigned int en_mask); 91 void misc_setup(struct omehci_softc *sc); 92 void omehci_uhh_init(struct omehci_softc *sc); 93 94 const struct cfattach omehci_ca = { 95 sizeof (struct omehci_softc), omehci_match, omehci_attach, 96 omehci_detach, omehci_activate 97 }; 98 99 struct cfdriver omehci_cd = { 100 NULL, "omehci", DV_DULL 101 }; 102 103 int 104 omehci_match(struct device *parent, void *match, void *aux) 105 { 106 struct fdt_attach_args *faa = aux; 107 108 return OF_is_compatible(faa->fa_node, "ti,usbhs-host"); 109 } 110 111 void 112 omehci_attach(struct device *parent, struct device *self, void *aux) 113 { 114 struct omehci_softc *sc = (struct omehci_softc *)self; 115 struct fdt_attach_args *faa = aux; 116 usbd_status r; 117 char *devname = sc->sc.sc_bus.bdev.dv_xname; 118 uint32_t i; 119 char port_mode[16]; 120 char name[32]; 121 int node; 122 uint32_t reg[2]; 123 124 if (faa->fa_nreg < 1) 125 return; 126 127 sc->sc.iot = faa->fa_iot; 128 sc->sc.sc_bus.dmatag = faa->fa_dmat; 129 130 /* set defaults */ 131 for (i = 0; i < OMAP_HS_USB_PORTS; i++) 132 sc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; 133 134 strlcpy(name, "portX-mode", sizeof(name)); 135 136 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 137 name[4] = '1' + i; 138 memset(port_mode, 0, sizeof(port_mode)); 139 140 if (OF_getprop(faa->fa_node, name, port_mode, 141 sizeof(port_mode)) == -1) 142 continue; 143 144 if (strcmp(port_mode, "ehci-phy") == 0) 145 sc->port_mode[i] = EHCI_HCD_OMAP_MODE_PHY; 146 if (strcmp(port_mode, "ehci-hsic") == 0) 147 sc->port_mode[i] = EHCI_HCD_OMAP_MODE_HSIC; 148 if (strcmp(port_mode, "ehci-tll") == 0) 149 sc->port_mode[i] = EHCI_HCD_OMAP_MODE_TLL ; 150 } 151 152 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 153 if (OF_is_compatible(node, "ti,ehci-omap")) 154 break; 155 } 156 157 if (node == 0) 158 panic("could not find ehci child node"); 159 160 if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg)) 161 return; 162 163 /* Map I/O space */ 164 if (bus_space_map(sc->sc.iot, reg[0], reg[1], 0, &sc->sc.ioh)) { 165 printf(": cannot map mem space\n"); 166 goto out; 167 } 168 sc->sc.sc_size = reg[1]; 169 170 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 171 0, &sc->uhh_ioh)) { 172 printf(": cannot map mem space\n"); 173 goto mem0; 174 } 175 176 #if 0 177 if (sc->tll_avail && 178 bus_space_map(sc->sc.iot, aa->aa_dev->mem[2].addr, 179 aa->aa_dev->mem[2].size, 0, &sc->tll_ioh)) { 180 printf(": cannot map mem space\n"); 181 goto mem1; 182 } 183 #endif 184 185 printf("\n"); 186 187 phy_enable_idx(node, 0); 188 189 if (omehci_init(sc)) 190 return; 191 192 /* Disable interrupts, so we don't get any spurious ones. */ 193 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH); 194 EOWRITE2(&sc->sc, EHCI_USBINTR, 0); 195 196 sc->sc_ih = arm_intr_establish_fdt(node, IPL_USB, 197 ehci_intr, &sc->sc, devname); 198 if (sc->sc_ih == NULL) { 199 printf(": unable to establish interrupt\n"); 200 printf("XXX - disable ehci and prcm"); 201 goto mem2; 202 } 203 204 strlcpy(sc->sc.sc_vendor, "TI OMAP", sizeof(sc->sc.sc_vendor)); 205 r = ehci_init(&sc->sc); 206 if (r != USBD_NORMAL_COMPLETION) { 207 printf("%s: init failed, error=%d\n", devname, r); 208 printf("XXX - disable ehci and prcm"); 209 goto intr; 210 } 211 212 config_found(self, &sc->sc.sc_bus, usbctlprint); 213 214 goto out; 215 216 intr: 217 arm_intr_disestablish(sc->sc_ih); 218 sc->sc_ih = NULL; 219 mem2: 220 #if 0 221 bus_space_unmap(sc->sc.iot, sc->tll_ioh, aa->aa_dev->mem[2].size); 222 mem1: 223 #endif 224 bus_space_unmap(sc->sc.iot, sc->uhh_ioh, faa->fa_reg[0].size); 225 mem0: 226 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 227 sc->sc.sc_size = 0; 228 out: 229 return; 230 } 231 232 int 233 omehci_init(struct omehci_softc *sc) 234 { 235 uint32_t i = 0, reg; 236 uint32_t reset_performed = 0; 237 uint32_t timeout = 0; 238 uint32_t tll_ch_mask = 0; 239 240 /* enable high speed usb host clock */ 241 prcm_enablemodule(PRCM_USB); 242 243 /* Hold the PHY in RESET for enough time till DIR is high */ 244 if (reset_performed) 245 delay(10); 246 247 /* Read the UHH revision */ 248 sc->ehci_rev = bus_space_read_4(sc->sc.iot, sc->uhh_ioh, 249 OMAP_USBHOST_UHH_REVISION); 250 251 /* Initialise the low level interface module(s) */ 252 if (sc->ehci_rev == OMAP_EHCI_REV1) { 253 /* Enable the USB TLL */ 254 prcm_enablemodule(PRCM_USBTLL); 255 256 /* Perform TLL soft reset, and wait until reset is complete */ 257 bus_space_write_4(sc->sc.iot, sc->tll_ioh, 258 OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET); 259 260 /* Set the timeout to 100ms*/ 261 timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); 262 263 /* Wait for TLL reset to complete */ 264 while ((bus_space_read_4(sc->sc.iot, sc->tll_ioh, 265 OMAP_USBTLL_SYSSTATUS) & TLL_SYSSTATUS_RESETDONE) 266 == 0x00) { 267 268 /* Sleep for a tick */ 269 delay(10); 270 271 if (timeout-- == 0) { 272 return 1; 273 } 274 } 275 276 bus_space_write_4(sc->sc.iot, sc->tll_ioh, 277 OMAP_USBTLL_SYSCONFIG, 278 TLL_SYSCONFIG_ENAWAKEUP | TLL_SYSCONFIG_AUTOIDLE | 279 TLL_SYSCONFIG_SIDLE_SMART_IDLE | TLL_SYSCONFIG_CACTIVITY); 280 } else if (sc->ehci_rev == OMAP_EHCI_REV2) { 281 /* For OMAP44xx devices you have to enable the per-port clocks: 282 * PHY_MODE - External ULPI clock 283 * TTL_MODE - Internal UTMI clock 284 * HSIC_MODE - Internal 480Mhz and 60Mhz clocks 285 */ 286 if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) { 287 //ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK); 288 prcm_enablemodule(PRCM_USBP1_PHY); 289 } else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) 290 prcm_enablemodule(PRCM_USBP1_UTMI); 291 else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) 292 prcm_enablemodule(PRCM_USBP1_HSIC); 293 294 if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) { 295 //ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK); 296 prcm_enablemodule(PRCM_USBP2_PHY); 297 } else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) 298 prcm_enablemodule(PRCM_USBP2_UTMI); 299 else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) 300 prcm_enablemodule(PRCM_USBP2_HSIC); 301 } 302 303 /* Put UHH in SmartIdle/SmartStandby mode */ 304 reg = bus_space_read_4(sc->sc.iot, sc->uhh_ioh, 305 OMAP_USBHOST_UHH_SYSCONFIG); 306 if (sc->ehci_rev == OMAP_EHCI_REV1) { 307 reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK | 308 UHH_SYSCONFIG_MIDLEMODE_MASK); 309 reg |= (UHH_SYSCONFIG_ENAWAKEUP | 310 UHH_SYSCONFIG_AUTOIDLE | 311 UHH_SYSCONFIG_CLOCKACTIVITY | 312 UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE | 313 UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY); 314 } else if (sc->ehci_rev == OMAP_EHCI_REV2) { 315 reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; 316 reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; 317 reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; 318 reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; 319 } 320 bus_space_write_4(sc->sc.iot, sc->uhh_ioh, OMAP_USBHOST_UHH_SYSCONFIG, 321 reg); 322 323 reg = bus_space_read_4(sc->sc.iot, sc->uhh_ioh, 324 OMAP_USBHOST_UHH_HOSTCONFIG); 325 326 /* Setup ULPI bypass and burst configurations */ 327 reg |= (UHH_HOSTCONFIG_ENA_INCR4 | 328 UHH_HOSTCONFIG_ENA_INCR8 | 329 UHH_HOSTCONFIG_ENA_INCR16); 330 reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; 331 332 if (sc->ehci_rev == OMAP_EHCI_REV1) { 333 if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) 334 reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; 335 if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) 336 reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; 337 if (sc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) 338 reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; 339 340 /* Bypass the TLL module for PHY mode operation */ 341 if ((sc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || 342 (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || 343 (sc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) 344 reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; 345 else 346 reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; 347 } else if (sc->ehci_rev == OMAP_EHCI_REV2) { 348 reg |= UHH_HOSTCONFIG_APP_START_CLK; 349 350 /* Clear port mode fields for PHY mode*/ 351 reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK; 352 reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK; 353 354 if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) 355 reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; 356 else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) 357 reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; 358 359 if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) 360 reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; 361 else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) 362 reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; 363 } 364 365 bus_space_write_4(sc->sc.iot, sc->uhh_ioh, OMAP_USBHOST_UHH_HOSTCONFIG, reg); 366 367 /* If any of the ports are configured in TLL mode, enable them */ 368 for (i = 0; i < OMAP_HS_USB_PORTS; i++) 369 if (sc->port_mode[i] == EHCI_HCD_OMAP_MODE_PHY) 370 tll_ch_mask |= 1 << i; 371 372 /* Enable UTMI mode for required TLL channels */ 373 #ifdef notyet 374 if (tll_ch_mask) 375 omap_ehci_utmi_init(sc, tll_ch_mask); 376 #endif 377 378 /* Set the interrupt threshold control, it controls the maximum rate at 379 * which the host controller issues interrupts. We set it to 1 microframe 380 * at startup - the default is 8 mircoframes (equates to 1ms). 381 */ 382 reg = bus_space_read_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_USBCMD); 383 reg &= 0xff00ffff; 384 reg |= (1 << 16); 385 bus_space_write_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_USBCMD, reg); 386 387 /* Soft reset the PHY using PHY reset command over ULPI */ 388 for (i = 0; i < OMAP_HS_USB_PORTS; i++) 389 if (sc->port_mode[i] == EHCI_HCD_OMAP_MODE_PHY) 390 omehci_soft_phy_reset(sc, i); 391 392 return(0); 393 } 394 395 void 396 omehci_soft_phy_reset(struct omehci_softc *sc, unsigned int port) 397 { 398 unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); 399 uint32_t reg; 400 401 reg = ULPI_FUNC_CTRL_RESET 402 /* FUNCTION_CTRL_SET register */ 403 | (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT) 404 /* Write */ 405 | (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT) 406 /* PORTn */ 407 | ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT) 408 /* start ULPI access*/ 409 | (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT); 410 411 bus_space_write_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_INSNREG05_ULPI, reg); 412 413 timeout += 1000000; 414 /* Wait for ULPI access completion */ 415 while ((bus_space_read_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_INSNREG05_ULPI) 416 & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) { 417 418 /* Sleep for a tick */ 419 delay(10); 420 421 if (timeout-- == 0) { 422 printf("PHY reset operation timed out\n"); 423 break; 424 } 425 } 426 } 427 428 int 429 omehci_detach(struct device *self, int flags) 430 { 431 struct omehci_softc *sc = (struct omehci_softc *)self; 432 int rv; 433 434 rv = ehci_detach(self, flags); 435 if (rv) 436 return (rv); 437 438 if (sc->sc_ih != NULL) { 439 arm_intr_disestablish(sc->sc_ih); 440 sc->sc_ih = NULL; 441 } 442 443 if (sc->sc.sc_size) { 444 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 445 sc->sc.sc_size = 0; 446 } 447 448 /* XXX: stop clock */ 449 450 return (0); 451 } 452 453 int 454 omehci_activate(struct device *self, int act) 455 { 456 struct omehci_softc *sc = (struct omehci_softc *)self; 457 458 switch (act) { 459 case DVACT_SUSPEND: 460 sc->sc.sc_bus.use_polling++; 461 /* FIXME */ 462 sc->sc.sc_bus.use_polling--; 463 break; 464 case DVACT_RESUME: 465 sc->sc.sc_bus.use_polling++; 466 /* FIXME */ 467 sc->sc.sc_bus.use_polling--; 468 break; 469 case DVACT_POWERDOWN: 470 ehci_reset(&sc->sc); 471 break; 472 } 473 return 0; 474 } 475