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 /* I found the code and comments in the Linux EHCI driver - thanks guys :) 272 * 273 * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended 274 * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared 275 * (for example when we do omap_uhh_bus_suspend). This breaks suspend-resume if 276 * the root-hub is allowed to suspend. Writing 1 to this undocumented 277 * register bit disables this feature and restores normal behavior." 278 */ 279 #if 0 280 omap_uhh_write_4(isc, OMAP_USBHOST_INSNREG04, 281 OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); 282 #endif 283 tll_ch_mask = 0; 284 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 285 if (isc->port_mode[i] == EHCI_HCD_OMAP_MODE_TLL) 286 tll_ch_mask |= (1 << i); 287 } 288 if (tll_ch_mask) 289 omap_tll_utmi_enable(tll_ch_mask); 290 291 return(0); 292 } 293 294 /** 295 * omap_uhh_fini - shutdown the EHCI controller 296 * @isc: omap ehci device context 297 * 298 * 299 * 300 * LOCKING: 301 * none 302 * 303 * RETURNS: 304 * 0 on success, a negative error code on failure. 305 */ 306 static void 307 omap_uhh_fini(struct omap_uhh_softc *isc) 308 { 309 unsigned long timeout; 310 311 device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n"); 312 313 /* Set the timeout */ 314 if (hz < 10) 315 timeout = 1; 316 else 317 timeout = (100 * hz) / 1000; 318 319 /* Reset the UHH, OHCI and EHCI modules */ 320 omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002); 321 while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) { 322 /* Sleep for a tick */ 323 pause("USBRESET", 1); 324 325 if (timeout-- == 0) { 326 device_printf(isc->sc_dev, "operation timed out\n"); 327 break; 328 } 329 } 330 331 /* Disable functional and interface clocks for the TLL and HOST modules */ 332 ti_sysc_clock_disable(device_get_parent(isc->sc_dev)); 333 334 device_printf(isc->sc_dev, "Clock to USB host has been disabled\n"); 335 } 336 337 int 338 omap_usb_port_mode(device_t dev, int port) 339 { 340 struct omap_uhh_softc *isc; 341 342 isc = device_get_softc(dev); 343 if ((port < 0) || (port >= OMAP_HS_USB_PORTS)) 344 return (-1); 345 346 return isc->port_mode[port]; 347 } 348 349 static int 350 omap_uhh_probe(device_t dev) 351 { 352 353 if (!ofw_bus_status_okay(dev)) 354 return (ENXIO); 355 356 if (!ofw_bus_is_compatible(dev, "ti,usbhs-host")) 357 return (ENXIO); 358 359 device_set_desc(dev, "TI OMAP USB 2.0 Host module"); 360 361 return (BUS_PROBE_DEFAULT); 362 } 363 364 static int 365 omap_uhh_attach(device_t dev) 366 { 367 struct omap_uhh_softc *isc = device_get_softc(dev); 368 int err; 369 int rid; 370 int i; 371 phandle_t node; 372 char propname[16]; 373 char *mode; 374 375 /* save the device */ 376 isc->sc_dev = dev; 377 378 /* Allocate resource for the UHH register set */ 379 rid = 0; 380 isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 381 if (!isc->uhh_mem_res) { 382 device_printf(dev, "Error: Could not map UHH memory\n"); 383 goto error; 384 } 385 386 node = ofw_bus_get_node(dev); 387 388 if (node == -1) 389 goto error; 390 391 /* Get port modes from FDT */ 392 for (i = 0; i < OMAP_HS_USB_PORTS; i++) { 393 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN; 394 snprintf(propname, sizeof(propname), 395 "port%d-mode", i+1); 396 397 if (OF_getprop_alloc(node, propname, (void**)&mode) <= 0) 398 continue; 399 if (strcmp(mode, "ehci-phy") == 0) 400 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_PHY; 401 else if (strcmp(mode, "ehci-tll") == 0) 402 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_TLL; 403 else if (strcmp(mode, "ehci-hsic") == 0) 404 isc->port_mode[i] = EHCI_HCD_OMAP_MODE_HSIC; 405 } 406 407 /* Initialise the ECHI registers */ 408 err = omap_uhh_init(isc); 409 if (err) { 410 device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err); 411 goto error; 412 } 413 414 simplebus_init(dev, node); 415 416 /* 417 * Allow devices to identify. 418 */ 419 bus_generic_probe(dev); 420 421 /* 422 * Now walk the OFW tree and attach top-level devices. 423 */ 424 for (node = OF_child(node); node > 0; node = OF_peer(node)) 425 simplebus_add_device(dev, node, 0, NULL, -1, NULL); 426 return (bus_generic_attach(dev)); 427 428 error: 429 omap_uhh_detach(dev); 430 return (ENXIO); 431 } 432 433 static int 434 omap_uhh_detach(device_t dev) 435 { 436 struct omap_uhh_softc *isc = device_get_softc(dev); 437 438 /* during module unload there are lots of children leftover */ 439 device_delete_children(dev); 440 441 if (isc->uhh_mem_res) { 442 bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->uhh_mem_res); 443 isc->uhh_mem_res = NULL; 444 } 445 446 omap_uhh_fini(isc); 447 448 return (0); 449 } 450 451 static device_method_t omap_uhh_methods[] = { 452 /* Device interface */ 453 DEVMETHOD(device_probe, omap_uhh_probe), 454 DEVMETHOD(device_attach, omap_uhh_attach), 455 DEVMETHOD(device_detach, omap_uhh_detach), 456 457 DEVMETHOD(device_suspend, bus_generic_suspend), 458 DEVMETHOD(device_resume, bus_generic_resume), 459 DEVMETHOD(device_shutdown, bus_generic_shutdown), 460 461 DEVMETHOD_END 462 }; 463 464 DEFINE_CLASS_1(omap_uhh, omap_uhh_driver, omap_uhh_methods, 465 sizeof(struct omap_uhh_softc), simplebus_driver); 466 static devclass_t omap_uhh_devclass; 467 DRIVER_MODULE(omap_uhh, simplebus, omap_uhh_driver, omap_uhh_devclass, 0, 0); 468