1 /*- 2 * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org> 3 * Copyright (c) 2011 Ben Gray <ben.r.gray@gmail.com>. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/conf.h> 32 #include <sys/kernel.h> 33 #include <sys/rman.h> 34 #include <sys/module.h> 35 36 #include <dev/fdt/simplebus.h> 37 #include <dev/ofw/ofw_bus_subr.h> 38 39 #include <machine/bus.h> 40 41 #include <arm/ti/ti_sysc.h> 42 #include <arm/ti/usb/omap_usb.h> 43 44 /* 45 * USB Host Module 46 */ 47 48 /* UHH */ 49 #define OMAP_USBHOST_UHH_REVISION 0x0000 50 #define OMAP_USBHOST_UHH_SYSCONFIG 0x0010 51 #define OMAP_USBHOST_UHH_SYSSTATUS 0x0014 52 #define OMAP_USBHOST_UHH_HOSTCONFIG 0x0040 53 #define OMAP_USBHOST_UHH_DEBUG_CSR 0x0044 54 55 /* UHH Register Set */ 56 #define UHH_SYSCONFIG_MIDLEMODE_MASK (3UL << 12) 57 #define UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY (2UL << 12) 58 #define UHH_SYSCONFIG_MIDLEMODE_NOSTANDBY (1UL << 12) 59 #define UHH_SYSCONFIG_MIDLEMODE_FORCESTANDBY (0UL << 12) 60 #define UHH_SYSCONFIG_CLOCKACTIVITY (1UL << 8) 61 #define UHH_SYSCONFIG_SIDLEMODE_MASK (3UL << 3) 62 #define UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE (2UL << 3) 63 #define UHH_SYSCONFIG_SIDLEMODE_NOIDLE (1UL << 3) 64 #define UHH_SYSCONFIG_SIDLEMODE_FORCEIDLE (0UL << 3) 65 #define UHH_SYSCONFIG_ENAWAKEUP (1UL << 2) 66 #define UHH_SYSCONFIG_SOFTRESET (1UL << 1) 67 #define UHH_SYSCONFIG_AUTOIDLE (1UL << 0) 68 69 #define UHH_HOSTCONFIG_APP_START_CLK (1UL << 31) 70 #define UHH_HOSTCONFIG_P3_CONNECT_STATUS (1UL << 10) 71 #define UHH_HOSTCONFIG_P2_CONNECT_STATUS (1UL << 9) 72 #define UHH_HOSTCONFIG_P1_CONNECT_STATUS (1UL << 8) 73 #define UHH_HOSTCONFIG_ENA_INCR_ALIGN (1UL << 5) 74 #define UHH_HOSTCONFIG_ENA_INCR16 (1UL << 4) 75 #define UHH_HOSTCONFIG_ENA_INCR8 (1UL << 3) 76 #define UHH_HOSTCONFIG_ENA_INCR4 (1UL << 2) 77 #define UHH_HOSTCONFIG_AUTOPPD_ON_OVERCUR_EN (1UL << 1) 78 #define UHH_HOSTCONFIG_P1_ULPI_BYPASS (1UL << 0) 79 80 /* The following are on rev2 (OMAP44xx) of the EHCI only */ 81 #define UHH_SYSCONFIG_IDLEMODE_MASK (3UL << 2) 82 #define UHH_SYSCONFIG_IDLEMODE_NOIDLE (1UL << 2) 83 #define UHH_SYSCONFIG_STANDBYMODE_MASK (3UL << 4) 84 #define UHH_SYSCONFIG_STANDBYMODE_NOSTDBY (1UL << 4) 85 86 #define UHH_HOSTCONFIG_P1_MODE_MASK (3UL << 16) 87 #define UHH_HOSTCONFIG_P1_MODE_ULPI_PHY (0UL << 16) 88 #define UHH_HOSTCONFIG_P1_MODE_UTMI_PHY (1UL << 16) 89 #define UHH_HOSTCONFIG_P1_MODE_HSIC (3UL << 16) 90 #define UHH_HOSTCONFIG_P2_MODE_MASK (3UL << 18) 91 #define UHH_HOSTCONFIG_P2_MODE_ULPI_PHY (0UL << 18) 92 #define UHH_HOSTCONFIG_P2_MODE_UTMI_PHY (1UL << 18) 93 #define UHH_HOSTCONFIG_P2_MODE_HSIC (3UL << 18) 94 95 /* 96 * Values of UHH_REVISION - Note: these are not given in the TRM but taken 97 * from the linux OMAP EHCI driver (thanks guys). It has been verified on 98 * a Panda and Beagle board. 99 */ 100 #define OMAP_UHH_REV1 0x00000010 /* OMAP3 */ 101 #define OMAP_UHH_REV2 0x50700100 /* OMAP4 */ 102 103 struct omap_uhh_softc { 104 struct simplebus_softc simplebus_sc; 105 device_t sc_dev; 106 107 /* UHH register set */ 108 struct resource* uhh_mem_res; 109 110 /* The revision of the HS USB HOST read from UHH_REVISION */ 111 uint32_t uhh_rev; 112 113 /* The following details are provided by conf hints */ 114 int port_mode[3]; 115 }; 116 117 static device_attach_t omap_uhh_attach; 118 static device_detach_t omap_uhh_detach; 119 120 static inline uint32_t 121 omap_uhh_read_4(struct omap_uhh_softc *sc, bus_size_t off) 122 { 123 return bus_read_4(sc->uhh_mem_res, off); 124 } 125 126 static inline void 127 omap_uhh_write_4(struct omap_uhh_softc *sc, bus_size_t off, uint32_t val) 128 { 129 bus_write_4(sc->uhh_mem_res, off, val); 130 } 131 132 static int 133 omap_uhh_init(struct omap_uhh_softc *isc) 134 { 135 uint8_t tll_ch_mask; 136 uint32_t reg; 137 int i; 138 139 /* Enable Clocks for high speed USBHOST */ 140 ti_sysc_clock_enable(device_get_parent(isc->sc_dev)); 141 142 /* Read the UHH revision */ 143 isc->uhh_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION); 144 device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->uhh_rev); 145 146 /* FIXME */ 147 #if 0 148 if (isc->uhh_rev == OMAP_UHH_REV2) { 149 /* For OMAP44xx devices you have to enable the per-port clocks: 150 * PHY_MODE - External ULPI clock 151 * TTL_MODE - Internal UTMI clock 152 * HSIC_MODE - Internal 480Mhz and 60Mhz clocks 153 */ 154 switch(isc->port_mode[0]) { 155 case EHCI_HCD_OMAP_MODE_UNKNOWN: 156 break; 157 case EHCI_HCD_OMAP_MODE_PHY: 158 if (ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK)) 159 device_printf(isc->sc_dev, 160 "failed to set clock source for port 0\n"); 161 if (ti_prcm_clk_enable(USBP1_PHY_CLK)) 162 device_printf(isc->sc_dev, 163 "failed to set clock USBP1_PHY_CLK source for port 0\n"); 164 break; 165 case EHCI_HCD_OMAP_MODE_TLL: 166 if (ti_prcm_clk_enable(USBP1_UTMI_CLK)) 167 device_printf(isc->sc_dev, 168 "failed to set clock USBP1_PHY_CLK source for port 0\n"); 169 break; 170 case EHCI_HCD_OMAP_MODE_HSIC: 171 if (ti_prcm_clk_enable(USBP1_HSIC_CLK)) 172 device_printf(isc->sc_dev, 173 "failed to set clock USBP1_PHY_CLK source for port 0\n"); 174 break; 175 default: 176 device_printf(isc->sc_dev, "unknown port mode %d for port 0\n", isc->port_mode[0]); 177 } 178 switch(isc->port_mode[1]) { 179 case EHCI_HCD_OMAP_MODE_UNKNOWN: 180 break; 181 case EHCI_HCD_OMAP_MODE_PHY: 182 if (ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK)) 183 device_printf(isc->sc_dev, 184 "failed to set clock source for port 0\n"); 185 if (ti_prcm_clk_enable(USBP2_PHY_CLK)) 186 device_printf(isc->sc_dev, 187 "failed to set clock USBP2_PHY_CLK source for port 1\n"); 188 break; 189 case EHCI_HCD_OMAP_MODE_TLL: 190 if (ti_prcm_clk_enable(USBP2_UTMI_CLK)) 191 device_printf(isc->sc_dev, 192 "failed to set clock USBP2_UTMI_CLK source for port 1\n"); 193 break; 194 case EHCI_HCD_OMAP_MODE_HSIC: 195 if (ti_prcm_clk_enable(USBP2_HSIC_CLK)) 196 device_printf(isc->sc_dev, 197 "failed to set clock USBP2_HSIC_CLK source for port 1\n"); 198 break; 199 default: 200 device_printf(isc->sc_dev, "unknown port mode %d for port 1\n", isc->port_mode[1]); 201 } 202 } 203 #endif 204 205 /* Put UHH in SmartIdle/SmartStandby mode */ 206 reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG); 207 if (isc->uhh_rev == OMAP_UHH_REV1) { 208 reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK | 209 UHH_SYSCONFIG_MIDLEMODE_MASK); 210 reg |= (UHH_SYSCONFIG_ENAWAKEUP | 211 UHH_SYSCONFIG_AUTOIDLE | 212 UHH_SYSCONFIG_CLOCKACTIVITY | 213 UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE | 214 UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY); 215 } else if (isc->uhh_rev == OMAP_UHH_REV2) { 216 reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; 217 reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; 218 reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; 219 reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; 220 } 221 omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg); 222 device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg); 223 224 reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG); 225 226 /* Setup ULPI bypass and burst configurations */ 227 reg |= (UHH_HOSTCONFIG_ENA_INCR4 | 228 UHH_HOSTCONFIG_ENA_INCR8 | 229 UHH_HOSTCONFIG_ENA_INCR16); 230 reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; 231 232 if (isc->uhh_rev == OMAP_UHH_REV1) { 233 if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) 234 reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; 235 if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) 236 reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; 237 if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) 238 reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; 239 240 /* Bypass the TLL module for PHY mode operation */ 241 if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || 242 (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || 243 (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) 244 reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; 245 else 246 reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; 247 248 } else if (isc->uhh_rev == OMAP_UHH_REV2) { 249 reg |= UHH_HOSTCONFIG_APP_START_CLK; 250 251 /* Clear port mode fields for PHY mode*/ 252 reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK; 253 reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK; 254 255 if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) 256 reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; 257 else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) 258 reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; 259 260 if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) 261 reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; 262 else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) 263 reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; 264 } 265 266 omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg); 267 device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg); 268 269 /* I found the code and comments in the Linux EHCI driver - thanks guys :) 270 * 271 * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended 272 * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared 273 * (for example when we do omap_uhh_bus_suspend). This breaks suspend-resume if 274 * the root-hub is allowed to suspend. Writing 1 to this undocumented 275 * register bit disables this feature and restores normal behavior." 276 */ 277 #if 0 278 omap_uhh_write_4(isc, OMAP_USBHOST_INSNREG04, 279 OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); 280 #endif 281 tll_ch_mask = 0; 282 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 283 if (isc->port_mode[i] == EHCI_HCD_OMAP_MODE_TLL) 284 tll_ch_mask |= (1 << i); 285 } 286 if (tll_ch_mask) 287 omap_tll_utmi_enable(tll_ch_mask); 288 289 return(0); 290 } 291 292 /** 293 * omap_uhh_fini - shutdown the EHCI controller 294 * @isc: omap ehci device context 295 * 296 * 297 * 298 * LOCKING: 299 * none 300 * 301 * RETURNS: 302 * 0 on success, a negative error code on failure. 303 */ 304 static void 305 omap_uhh_fini(struct omap_uhh_softc *isc) 306 { 307 unsigned long timeout; 308 309 device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n"); 310 311 /* Set the timeout */ 312 if (hz < 10) 313 timeout = 1; 314 else 315 timeout = (100 * hz) / 1000; 316 317 /* Reset the UHH, OHCI and EHCI modules */ 318 omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002); 319 while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) { 320 /* Sleep for a tick */ 321 pause("USBRESET", 1); 322 323 if (timeout-- == 0) { 324 device_printf(isc->sc_dev, "operation timed out\n"); 325 break; 326 } 327 } 328 329 /* Disable functional and interface clocks for the TLL and HOST modules */ 330 ti_sysc_clock_disable(device_get_parent(isc->sc_dev)); 331 332 device_printf(isc->sc_dev, "Clock to USB host has been disabled\n"); 333 } 334 335 int 336 omap_usb_port_mode(device_t dev, int port) 337 { 338 struct omap_uhh_softc *isc; 339 340 isc = device_get_softc(dev); 341 if ((port < 0) || (port >= OMAP_HS_USB_PORTS)) 342 return (-1); 343 344 return isc->port_mode[port]; 345 } 346 347 static int 348 omap_uhh_probe(device_t dev) 349 { 350 351 if (!ofw_bus_status_okay(dev)) 352 return (ENXIO); 353 354 if (!ofw_bus_is_compatible(dev, "ti,usbhs-host")) 355 return (ENXIO); 356 357 device_set_desc(dev, "TI OMAP USB 2.0 Host module"); 358 359 return (BUS_PROBE_DEFAULT); 360 } 361 362 static int 363 omap_uhh_attach(device_t dev) 364 { 365 struct omap_uhh_softc *isc = device_get_softc(dev); 366 int err; 367 int rid; 368 int i; 369 phandle_t node; 370 char propname[16]; 371 char *mode; 372 373 /* save the device */ 374 isc->sc_dev = dev; 375 376 /* Allocate resource for the UHH register set */ 377 rid = 0; 378 isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 379 if (!isc->uhh_mem_res) { 380 device_printf(dev, "Error: Could not map UHH memory\n"); 381 goto error; 382 } 383 384 node = ofw_bus_get_node(dev); 385 386 if (node == -1) 387 goto error; 388 389 /* Get port modes from FDT */ 390 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 391 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; 392 snprintf(propname, sizeof(propname), 393 "port%d-mode", i+1); 394 395 if (OF_getprop_alloc(node, propname, (void**)&mode) <= 0) 396 continue; 397 if (strcmp(mode, "ehci-phy") == 0) 398 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_PHY; 399 else if (strcmp(mode, "ehci-tll") == 0) 400 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_TLL; 401 else if (strcmp(mode, "ehci-hsic") == 0) 402 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_HSIC; 403 } 404 405 /* Initialise the ECHI registers */ 406 err = omap_uhh_init(isc); 407 if (err) { 408 device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); 409 goto error; 410 } 411 412 simplebus_init(dev, node); 413 414 /* 415 * Allow devices to identify. 416 */ 417 bus_generic_probe(dev); 418 419 /* 420 * Now walk the OFW tree and attach top-level devices. 421 */ 422 for (node = OF_child(node); node > 0; node = OF_peer(node)) 423 simplebus_add_device(dev, node, 0, NULL, -1, NULL); 424 return (bus_generic_attach(dev)); 425 426 error: 427 omap_uhh_detach(dev); 428 return (ENXIO); 429 } 430 431 static int 432 omap_uhh_detach(device_t dev) 433 { 434 struct omap_uhh_softc *isc = device_get_softc(dev); 435 436 /* during module unload there are lots of children leftover */ 437 device_delete_children(dev); 438 439 if (isc->uhh_mem_res) { 440 bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->uhh_mem_res); 441 isc->uhh_mem_res = NULL; 442 } 443 444 omap_uhh_fini(isc); 445 446 return (0); 447 } 448 449 static device_method_t omap_uhh_methods[] = { 450 /* Device interface */ 451 DEVMETHOD(device_probe, omap_uhh_probe), 452 DEVMETHOD(device_attach, omap_uhh_attach), 453 DEVMETHOD(device_detach, omap_uhh_detach), 454 455 DEVMETHOD(device_suspend, bus_generic_suspend), 456 DEVMETHOD(device_resume, bus_generic_resume), 457 DEVMETHOD(device_shutdown, bus_generic_shutdown), 458 459 DEVMETHOD_END 460 }; 461 462 DEFINE_CLASS_1(omap_uhh, omap_uhh_driver, omap_uhh_methods, 463 sizeof(struct omap_uhh_softc), simplebus_driver); 464 DRIVER_MODULE(omap_uhh, simplebus, omap_uhh_driver, 0, 0); 465