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