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/param.h> 29 #include <sys/systm.h> 30 #include <sys/conf.h> 31 #include <sys/kernel.h> 32 #include <sys/rman.h> 33 #include <sys/module.h> 34 35 #include <dev/fdt/simplebus.h> 36 #include <dev/ofw/ofw_bus_subr.h> 37 38 #include <machine/bus.h> 39 40 #include <arm/ti/ti_sysc.h> 41 #include <arm/ti/usb/omap_usb.h> 42 43 /* 44 * USB Host Module 45 */ 46 47 /* UHH */ 48 #define OMAP_USBHOST_UHH_REVISION 0x0000 49 #define OMAP_USBHOST_UHH_SYSCONFIG 0x0010 50 #define OMAP_USBHOST_UHH_SYSSTATUS 0x0014 51 #define OMAP_USBHOST_UHH_HOSTCONFIG 0x0040 52 #define OMAP_USBHOST_UHH_DEBUG_CSR 0x0044 53 54 /* UHH Register Set */ 55 #define UHH_SYSCONFIG_MIDLEMODE_MASK (3UL << 12) 56 #define UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY (2UL << 12) 57 #define UHH_SYSCONFIG_MIDLEMODE_NOSTANDBY (1UL << 12) 58 #define UHH_SYSCONFIG_MIDLEMODE_FORCESTANDBY (0UL << 12) 59 #define UHH_SYSCONFIG_CLOCKACTIVITY (1UL << 8) 60 #define UHH_SYSCONFIG_SIDLEMODE_MASK (3UL << 3) 61 #define UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE (2UL << 3) 62 #define UHH_SYSCONFIG_SIDLEMODE_NOIDLE (1UL << 3) 63 #define UHH_SYSCONFIG_SIDLEMODE_FORCEIDLE (0UL << 3) 64 #define UHH_SYSCONFIG_ENAWAKEUP (1UL << 2) 65 #define UHH_SYSCONFIG_SOFTRESET (1UL << 1) 66 #define UHH_SYSCONFIG_AUTOIDLE (1UL << 0) 67 68 #define UHH_HOSTCONFIG_APP_START_CLK (1UL << 31) 69 #define UHH_HOSTCONFIG_P3_CONNECT_STATUS (1UL << 10) 70 #define UHH_HOSTCONFIG_P2_CONNECT_STATUS (1UL << 9) 71 #define UHH_HOSTCONFIG_P1_CONNECT_STATUS (1UL << 8) 72 #define UHH_HOSTCONFIG_ENA_INCR_ALIGN (1UL << 5) 73 #define UHH_HOSTCONFIG_ENA_INCR16 (1UL << 4) 74 #define UHH_HOSTCONFIG_ENA_INCR8 (1UL << 3) 75 #define UHH_HOSTCONFIG_ENA_INCR4 (1UL << 2) 76 #define UHH_HOSTCONFIG_AUTOPPD_ON_OVERCUR_EN (1UL << 1) 77 #define UHH_HOSTCONFIG_P1_ULPI_BYPASS (1UL << 0) 78 79 /* The following are on rev2 (OMAP44xx) of the EHCI only */ 80 #define UHH_SYSCONFIG_IDLEMODE_MASK (3UL << 2) 81 #define UHH_SYSCONFIG_IDLEMODE_NOIDLE (1UL << 2) 82 #define UHH_SYSCONFIG_STANDBYMODE_MASK (3UL << 4) 83 #define UHH_SYSCONFIG_STANDBYMODE_NOSTDBY (1UL << 4) 84 85 #define UHH_HOSTCONFIG_P1_MODE_MASK (3UL << 16) 86 #define UHH_HOSTCONFIG_P1_MODE_ULPI_PHY (0UL << 16) 87 #define UHH_HOSTCONFIG_P1_MODE_UTMI_PHY (1UL << 16) 88 #define UHH_HOSTCONFIG_P1_MODE_HSIC (3UL << 16) 89 #define UHH_HOSTCONFIG_P2_MODE_MASK (3UL << 18) 90 #define UHH_HOSTCONFIG_P2_MODE_ULPI_PHY (0UL << 18) 91 #define UHH_HOSTCONFIG_P2_MODE_UTMI_PHY (1UL << 18) 92 #define UHH_HOSTCONFIG_P2_MODE_HSIC (3UL << 18) 93 94 /* 95 * Values of UHH_REVISION - Note: these are not given in the TRM but taken 96 * from the linux OMAP EHCI driver (thanks guys). It has been verified on 97 * a Panda and Beagle board. 98 */ 99 #define OMAP_UHH_REV1 0x00000010 /* OMAP3 */ 100 #define OMAP_UHH_REV2 0x50700100 /* OMAP4 */ 101 102 struct omap_uhh_softc { 103 struct simplebus_softc simplebus_sc; 104 device_t sc_dev; 105 106 /* UHH register set */ 107 struct resource* uhh_mem_res; 108 109 /* The revision of the HS USB HOST read from UHH_REVISION */ 110 uint32_t uhh_rev; 111 112 /* The following details are provided by conf hints */ 113 int port_mode[3]; 114 }; 115 116 static device_attach_t omap_uhh_attach; 117 static device_detach_t omap_uhh_detach; 118 119 static inline uint32_t 120 omap_uhh_read_4(struct omap_uhh_softc *sc, bus_size_t off) 121 { 122 return bus_read_4(sc->uhh_mem_res, off); 123 } 124 125 static inline void 126 omap_uhh_write_4(struct omap_uhh_softc *sc, bus_size_t off, uint32_t val) 127 { 128 bus_write_4(sc->uhh_mem_res, off, val); 129 } 130 131 static int 132 omap_uhh_init(struct omap_uhh_softc *isc) 133 { 134 uint8_t tll_ch_mask; 135 uint32_t reg; 136 int i; 137 138 /* Enable Clocks for high speed USBHOST */ 139 ti_sysc_clock_enable(device_get_parent(isc->sc_dev)); 140 141 /* Read the UHH revision */ 142 isc->uhh_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION); 143 device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->uhh_rev); 144 145 /* FIXME */ 146 #if 0 147 if (isc->uhh_rev == OMAP_UHH_REV2) { 148 /* For OMAP44xx devices you have to enable the per-port clocks: 149 * PHY_MODE - External ULPI clock 150 * TTL_MODE - Internal UTMI clock 151 * HSIC_MODE - Internal 480Mhz and 60Mhz clocks 152 */ 153 switch(isc->port_mode[0]) { 154 case EHCI_HCD_OMAP_MODE_UNKNOWN: 155 break; 156 case EHCI_HCD_OMAP_MODE_PHY: 157 if (ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK)) 158 device_printf(isc->sc_dev, 159 "failed to set clock source for port 0\n"); 160 if (ti_prcm_clk_enable(USBP1_PHY_CLK)) 161 device_printf(isc->sc_dev, 162 "failed to set clock USBP1_PHY_CLK source for port 0\n"); 163 break; 164 case EHCI_HCD_OMAP_MODE_TLL: 165 if (ti_prcm_clk_enable(USBP1_UTMI_CLK)) 166 device_printf(isc->sc_dev, 167 "failed to set clock USBP1_PHY_CLK source for port 0\n"); 168 break; 169 case EHCI_HCD_OMAP_MODE_HSIC: 170 if (ti_prcm_clk_enable(USBP1_HSIC_CLK)) 171 device_printf(isc->sc_dev, 172 "failed to set clock USBP1_PHY_CLK source for port 0\n"); 173 break; 174 default: 175 device_printf(isc->sc_dev, "unknown port mode %d for port 0\n", isc->port_mode[0]); 176 } 177 switch(isc->port_mode[1]) { 178 case EHCI_HCD_OMAP_MODE_UNKNOWN: 179 break; 180 case EHCI_HCD_OMAP_MODE_PHY: 181 if (ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK)) 182 device_printf(isc->sc_dev, 183 "failed to set clock source for port 0\n"); 184 if (ti_prcm_clk_enable(USBP2_PHY_CLK)) 185 device_printf(isc->sc_dev, 186 "failed to set clock USBP2_PHY_CLK source for port 1\n"); 187 break; 188 case EHCI_HCD_OMAP_MODE_TLL: 189 if (ti_prcm_clk_enable(USBP2_UTMI_CLK)) 190 device_printf(isc->sc_dev, 191 "failed to set clock USBP2_UTMI_CLK source for port 1\n"); 192 break; 193 case EHCI_HCD_OMAP_MODE_HSIC: 194 if (ti_prcm_clk_enable(USBP2_HSIC_CLK)) 195 device_printf(isc->sc_dev, 196 "failed to set clock USBP2_HSIC_CLK source for port 1\n"); 197 break; 198 default: 199 device_printf(isc->sc_dev, "unknown port mode %d for port 1\n", isc->port_mode[1]); 200 } 201 } 202 #endif 203 204 /* Put UHH in SmartIdle/SmartStandby mode */ 205 reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG); 206 if (isc->uhh_rev == OMAP_UHH_REV1) { 207 reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK | 208 UHH_SYSCONFIG_MIDLEMODE_MASK); 209 reg |= (UHH_SYSCONFIG_ENAWAKEUP | 210 UHH_SYSCONFIG_AUTOIDLE | 211 UHH_SYSCONFIG_CLOCKACTIVITY | 212 UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE | 213 UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY); 214 } else if (isc->uhh_rev == OMAP_UHH_REV2) { 215 reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; 216 reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; 217 reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; 218 reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; 219 } 220 omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg); 221 device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg); 222 223 reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG); 224 225 /* Setup ULPI bypass and burst configurations */ 226 reg |= (UHH_HOSTCONFIG_ENA_INCR4 | 227 UHH_HOSTCONFIG_ENA_INCR8 | 228 UHH_HOSTCONFIG_ENA_INCR16); 229 reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; 230 231 if (isc->uhh_rev == OMAP_UHH_REV1) { 232 if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) 233 reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; 234 if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) 235 reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; 236 if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) 237 reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; 238 239 /* Bypass the TLL module for PHY mode operation */ 240 if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || 241 (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || 242 (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) 243 reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; 244 else 245 reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; 246 247 } else if (isc->uhh_rev == OMAP_UHH_REV2) { 248 reg |= UHH_HOSTCONFIG_APP_START_CLK; 249 250 /* Clear port mode fields for PHY mode*/ 251 reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK; 252 reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK; 253 254 if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) 255 reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; 256 else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) 257 reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; 258 259 if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) 260 reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; 261 else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) 262 reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; 263 } 264 265 omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg); 266 device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg); 267 268 /* I found the code and comments in the Linux EHCI driver - thanks guys :) 269 * 270 * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended 271 * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared 272 * (for example when we do omap_uhh_bus_suspend). This breaks suspend-resume if 273 * the root-hub is allowed to suspend. Writing 1 to this undocumented 274 * register bit disables this feature and restores normal behavior." 275 */ 276 #if 0 277 omap_uhh_write_4(isc, OMAP_USBHOST_INSNREG04, 278 OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); 279 #endif 280 tll_ch_mask = 0; 281 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 282 if (isc->port_mode[i] == EHCI_HCD_OMAP_MODE_TLL) 283 tll_ch_mask |= (1 << i); 284 } 285 if (tll_ch_mask) 286 omap_tll_utmi_enable(tll_ch_mask); 287 288 return(0); 289 } 290 291 /** 292 * omap_uhh_fini - shutdown the EHCI controller 293 * @isc: omap ehci device context 294 * 295 * 296 * 297 * LOCKING: 298 * none 299 * 300 * RETURNS: 301 * 0 on success, a negative error code on failure. 302 */ 303 static void 304 omap_uhh_fini(struct omap_uhh_softc *isc) 305 { 306 unsigned long timeout; 307 308 device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n"); 309 310 /* Set the timeout */ 311 if (hz < 10) 312 timeout = 1; 313 else 314 timeout = (100 * hz) / 1000; 315 316 /* Reset the UHH, OHCI and EHCI modules */ 317 omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002); 318 while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) { 319 /* Sleep for a tick */ 320 pause("USBRESET", 1); 321 322 if (timeout-- == 0) { 323 device_printf(isc->sc_dev, "operation timed out\n"); 324 break; 325 } 326 } 327 328 /* Disable functional and interface clocks for the TLL and HOST modules */ 329 ti_sysc_clock_disable(device_get_parent(isc->sc_dev)); 330 331 device_printf(isc->sc_dev, "Clock to USB host has been disabled\n"); 332 } 333 334 int 335 omap_usb_port_mode(device_t dev, int port) 336 { 337 struct omap_uhh_softc *isc; 338 339 isc = device_get_softc(dev); 340 if ((port < 0) || (port >= OMAP_HS_USB_PORTS)) 341 return (-1); 342 343 return isc->port_mode[port]; 344 } 345 346 static int 347 omap_uhh_probe(device_t dev) 348 { 349 350 if (!ofw_bus_status_okay(dev)) 351 return (ENXIO); 352 353 if (!ofw_bus_is_compatible(dev, "ti,usbhs-host")) 354 return (ENXIO); 355 356 device_set_desc(dev, "TI OMAP USB 2.0 Host module"); 357 358 return (BUS_PROBE_DEFAULT); 359 } 360 361 static int 362 omap_uhh_attach(device_t dev) 363 { 364 struct omap_uhh_softc *isc = device_get_softc(dev); 365 int err; 366 int rid; 367 int i; 368 phandle_t node; 369 char propname[16]; 370 char *mode; 371 372 /* save the device */ 373 isc->sc_dev = dev; 374 375 /* Allocate resource for the UHH register set */ 376 rid = 0; 377 isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 378 if (!isc->uhh_mem_res) { 379 device_printf(dev, "Error: Could not map UHH memory\n"); 380 goto error; 381 } 382 383 node = ofw_bus_get_node(dev); 384 385 if (node == -1) 386 goto error; 387 388 /* Get port modes from FDT */ 389 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 390 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; 391 snprintf(propname, sizeof(propname), 392 "port%d-mode", i+1); 393 394 if (OF_getprop_alloc(node, propname, (void**)&mode) <= 0) 395 continue; 396 if (strcmp(mode, "ehci-phy") == 0) 397 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_PHY; 398 else if (strcmp(mode, "ehci-tll") == 0) 399 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_TLL; 400 else if (strcmp(mode, "ehci-hsic") == 0) 401 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_HSIC; 402 } 403 404 /* Initialise the ECHI registers */ 405 err = omap_uhh_init(isc); 406 if (err) { 407 device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); 408 goto error; 409 } 410 411 simplebus_init(dev, node); 412 413 /* 414 * Allow devices to identify. 415 */ 416 bus_identify_children(dev); 417 418 /* 419 * Now walk the OFW tree and attach top-level devices. 420 */ 421 for (node = OF_child(node); node > 0; node = OF_peer(node)) 422 simplebus_add_device(dev, node, 0, NULL, -1, NULL); 423 bus_attach_children(dev); 424 return (0); 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