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