exynos5_ehci.c revision 269369
1/*- 2 * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/arm/samsung/exynos/exynos5_ehci.c 269369 2014-08-01 06:20:25Z br $"); 29 30#include "opt_bus.h" 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35#include <sys/module.h> 36#include <sys/bus.h> 37#include <sys/condvar.h> 38#include <sys/rman.h> 39#include <sys/gpio.h> 40 41#include <dev/ofw/ofw_bus.h> 42#include <dev/ofw/ofw_bus_subr.h> 43 44#include <dev/usb/usb.h> 45#include <dev/usb/usbdi.h> 46#include <dev/usb/usb_busdma.h> 47#include <dev/usb/usb_process.h> 48#include <dev/usb/usb_controller.h> 49#include <dev/usb/usb_bus.h> 50#include <dev/usb/controller/ehci.h> 51#include <dev/usb/controller/ehcireg.h> 52 53#include <dev/fdt/fdt_common.h> 54 55#include <machine/bus.h> 56#include <machine/resource.h> 57 58#include <arm/samsung/exynos/exynos5_common.h> 59#include <arm/samsung/exynos/exynos5_pmu.h> 60 61#include "gpio_if.h" 62 63#include "opt_platform.h" 64 65/* GPIO control */ 66#define GPIO_OUTPUT 1 67#define GPIO_INPUT 0 68#define PIN_USB 161 69 70/* SYSREG */ 71#define EXYNOS5_SYSREG_USB2_PHY 0x0 72#define USB2_MODE_HOST 0x1 73 74/* USB HOST */ 75#define HOST_CTRL_CLK_24MHZ (5 << 16) 76#define HOST_CTRL_CLK_MASK (7 << 16) 77#define HOST_CTRL_SIDDQ (1 << 6) 78#define HOST_CTRL_SLEEP (1 << 5) 79#define HOST_CTRL_SUSPEND (1 << 4) 80#define HOST_CTRL_RESET_LINK (1 << 1) 81#define HOST_CTRL_RESET_PHY (1 << 0) 82#define HOST_CTRL_RESET_PHY_ALL (1U << 31) 83 84/* Forward declarations */ 85static int exynos_ehci_attach(device_t dev); 86static int exynos_ehci_detach(device_t dev); 87static int exynos_ehci_probe(device_t dev); 88 89struct exynos_ehci_softc { 90 device_t dev; 91 ehci_softc_t base; 92 struct resource *res[4]; 93 bus_space_tag_t host_bst; 94 bus_space_tag_t sysreg_bst; 95 bus_space_handle_t host_bsh; 96 bus_space_handle_t sysreg_bsh; 97 98}; 99 100static struct resource_spec exynos_ehci_spec[] = { 101 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 102 { SYS_RES_MEMORY, 1, RF_ACTIVE }, 103 { SYS_RES_MEMORY, 2, RF_ACTIVE }, 104 { SYS_RES_IRQ, 0, RF_ACTIVE }, 105 { -1, 0 } 106}; 107 108static device_method_t ehci_methods[] = { 109 /* Device interface */ 110 DEVMETHOD(device_probe, exynos_ehci_probe), 111 DEVMETHOD(device_attach, exynos_ehci_attach), 112 DEVMETHOD(device_detach, exynos_ehci_detach), 113 DEVMETHOD(device_suspend, bus_generic_suspend), 114 DEVMETHOD(device_resume, bus_generic_resume), 115 DEVMETHOD(device_shutdown, bus_generic_shutdown), 116 117 /* Bus interface */ 118 DEVMETHOD(bus_print_child, bus_generic_print_child), 119 120 { 0, 0 } 121}; 122 123/* kobj_class definition */ 124static driver_t ehci_driver = { 125 "ehci", 126 ehci_methods, 127 sizeof(ehci_softc_t) 128}; 129 130static devclass_t ehci_devclass; 131 132DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 133MODULE_DEPEND(ehci, usb, 1, 1, 1); 134 135/* 136 * Public methods 137 */ 138static int 139exynos_ehci_probe(device_t dev) 140{ 141 142 if (!ofw_bus_status_okay(dev)) 143 return (ENXIO); 144 145 if (ofw_bus_is_compatible(dev, "exynos,usb-ehci") == 0) 146 return (ENXIO); 147 148 device_set_desc(dev, "Exynos integrated USB controller"); 149 return (BUS_PROBE_DEFAULT); 150} 151 152static int 153gpio_ctrl(struct exynos_ehci_softc *esc, int dir, int power) 154{ 155 device_t gpio_dev; 156 157 /* Get the GPIO device, we need this to give power to USB */ 158 gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 159 if (gpio_dev == NULL) { 160 device_printf(esc->dev, "cant find gpio_dev\n"); 161 return (1); 162 } 163 164 if (power) 165 GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_HIGH); 166 else 167 GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_LOW); 168 169 if (dir) 170 GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_OUTPUT); 171 else 172 GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_INPUT); 173 174 return (0); 175} 176 177static int 178reset_hsic_hub(struct exynos_ehci_softc *esc, phandle_t hub) 179{ 180 device_t gpio_dev; 181 pcell_t pin; 182 183 /* TODO: check that hub is compatible with "smsc,usb3503" */ 184 if (!OF_hasprop(hub, "freebsd,reset-gpio")) { 185 return (1); 186 } 187 188 if (OF_getencprop(hub, "freebsd,reset-gpio", &pin, sizeof(pin)) < 0) { 189 device_printf(esc->dev, 190 "failed to decode reset GPIO pin number for HSIC hub\n"); 191 return (1); 192 } 193 194 /* Get the GPIO device, we need this to give power to USB */ 195 gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 196 if (gpio_dev == NULL) { 197 device_printf(esc->dev, "Cant find gpio device\n"); 198 return (1); 199 } 200 201 GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_LOW); 202 DELAY(100); 203 GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_HIGH); 204 205 return (0); 206} 207 208static int 209phy_init(struct exynos_ehci_softc *esc) 210{ 211 int reg; 212 phandle_t hub; 213 214 gpio_ctrl(esc, GPIO_INPUT, 1); 215 216 /* set USB HOST mode */ 217 bus_space_write_4(esc->sysreg_bst, esc->sysreg_bsh, 218 EXYNOS5_SYSREG_USB2_PHY, USB2_MODE_HOST); 219 220 /* Power ON phy */ 221 usb2_phy_power_on(); 222 223 reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); 224 reg &= ~(HOST_CTRL_CLK_MASK | 225 HOST_CTRL_RESET_PHY | 226 HOST_CTRL_RESET_PHY_ALL | 227 HOST_CTRL_SIDDQ | 228 HOST_CTRL_SUSPEND | 229 HOST_CTRL_SLEEP); 230 231 reg |= (HOST_CTRL_CLK_24MHZ | 232 HOST_CTRL_RESET_LINK); 233 bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); 234 235 DELAY(10); 236 237 reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); 238 reg &= ~(HOST_CTRL_RESET_LINK); 239 bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); 240 241 if ((hub = OF_finddevice("/hsichub")) != 0) { 242 reset_hsic_hub(esc, hub); 243 } 244 245 gpio_ctrl(esc, GPIO_OUTPUT, 1); 246 247 return (0); 248} 249 250static int 251exynos_ehci_attach(device_t dev) 252{ 253 struct exynos_ehci_softc *esc; 254 ehci_softc_t *sc; 255 bus_space_handle_t bsh; 256 int err; 257 258 esc = device_get_softc(dev); 259 esc->dev = dev; 260 sc = &esc->base; 261 sc->sc_bus.parent = dev; 262 sc->sc_bus.devices = sc->sc_devices; 263 sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 264 265 if (bus_alloc_resources(dev, exynos_ehci_spec, esc->res)) { 266 device_printf(dev, "could not allocate resources\n"); 267 return (ENXIO); 268 } 269 270 /* EHCI registers */ 271 sc->sc_io_tag = rman_get_bustag(esc->res[0]); 272 bsh = rman_get_bushandle(esc->res[0]); 273 sc->sc_io_size = rman_get_size(esc->res[0]); 274 275 /* EHCI HOST ctrl registers */ 276 esc->host_bst = rman_get_bustag(esc->res[1]); 277 esc->host_bsh = rman_get_bushandle(esc->res[1]); 278 279 /* SYSREG */ 280 esc->sysreg_bst = rman_get_bustag(esc->res[2]); 281 esc->sysreg_bsh = rman_get_bushandle(esc->res[2]); 282 283 /* get all DMA memory */ 284 if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), 285 &ehci_iterate_hw_softc)) 286 return (ENXIO); 287 288 /* 289 * Set handle to USB related registers subregion used by 290 * generic EHCI driver. 291 */ 292 err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0, 293 sc->sc_io_size, &sc->sc_io_hdl); 294 if (err != 0) 295 return (ENXIO); 296 297 phy_init(esc); 298 299 /* Setup interrupt handler */ 300 err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, 301 NULL, (driver_intr_t *)ehci_interrupt, sc, 302 &sc->sc_intr_hdl); 303 if (err) { 304 device_printf(dev, "Could not setup irq, " 305 "%d\n", err); 306 return (1); 307 } 308 309 /* Add USB device */ 310 sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); 311 if (!sc->sc_bus.bdev) { 312 device_printf(dev, "Could not add USB device\n"); 313 err = bus_teardown_intr(dev, esc->res[3], 314 sc->sc_intr_hdl); 315 if (err) 316 device_printf(dev, "Could not tear down irq," 317 " %d\n", err); 318 return (1); 319 } 320 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 321 322 strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); 323 324 err = ehci_init(sc); 325 if (!err) { 326 sc->sc_flags |= EHCI_SCFLG_DONEINIT; 327 err = device_probe_and_attach(sc->sc_bus.bdev); 328 } else { 329 device_printf(dev, "USB init failed err=%d\n", err); 330 331 device_delete_child(dev, sc->sc_bus.bdev); 332 sc->sc_bus.bdev = NULL; 333 334 err = bus_teardown_intr(dev, esc->res[3], 335 sc->sc_intr_hdl); 336 if (err) 337 device_printf(dev, "Could not tear down irq," 338 " %d\n", err); 339 return (1); 340 } 341 return (0); 342} 343 344static int 345exynos_ehci_detach(device_t dev) 346{ 347 struct exynos_ehci_softc *esc; 348 ehci_softc_t *sc; 349 int err; 350 351 esc = device_get_softc(dev); 352 sc = &esc->base; 353 354 if (sc->sc_flags & EHCI_SCFLG_DONEINIT) 355 return (0); 356 357 /* 358 * only call ehci_detach() after ehci_init() 359 */ 360 if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { 361 ehci_detach(sc); 362 sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; 363 } 364 365 /* 366 * Disable interrupts that might have been switched on in 367 * ehci_init. 368 */ 369 if (sc->sc_io_tag && sc->sc_io_hdl) 370 bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, 371 EHCI_USBINTR, 0); 372 373 if (esc->res[3] && sc->sc_intr_hdl) { 374 err = bus_teardown_intr(dev, esc->res[3], 375 sc->sc_intr_hdl); 376 if (err) { 377 device_printf(dev, "Could not tear down irq," 378 " %d\n", err); 379 return (err); 380 } 381 sc->sc_intr_hdl = NULL; 382 } 383 384 if (sc->sc_bus.bdev) { 385 device_delete_child(dev, sc->sc_bus.bdev); 386 sc->sc_bus.bdev = NULL; 387 } 388 389 /* During module unload there are lots of children leftover */ 390 device_delete_children(dev); 391 392 bus_release_resources(dev, exynos_ehci_spec, esc->res); 393 394 return (0); 395} 396