exynos5_ehci.c revision 269703
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 269703 2014-08-08 06:30:17Z nwhitehorn $"); 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 <machine/bus.h> 54#include <machine/resource.h> 55 56#include <arm/samsung/exynos/exynos5_common.h> 57#include <arm/samsung/exynos/exynos5_pmu.h> 58 59#include "gpio_if.h" 60 61#include "opt_platform.h" 62 63/* GPIO control */ 64#define GPIO_OUTPUT 1 65#define GPIO_INPUT 0 66#define PIN_USB 161 67 68/* SYSREG */ 69#define EXYNOS5_SYSREG_USB2_PHY 0x0 70#define USB2_MODE_HOST 0x1 71 72/* USB HOST */ 73#define HOST_CTRL_CLK_24MHZ (5 << 16) 74#define HOST_CTRL_CLK_MASK (7 << 16) 75#define HOST_CTRL_SIDDQ (1 << 6) 76#define HOST_CTRL_SLEEP (1 << 5) 77#define HOST_CTRL_SUSPEND (1 << 4) 78#define HOST_CTRL_RESET_LINK (1 << 1) 79#define HOST_CTRL_RESET_PHY (1 << 0) 80#define HOST_CTRL_RESET_PHY_ALL (1U << 31) 81 82/* Forward declarations */ 83static int exynos_ehci_attach(device_t dev); 84static int exynos_ehci_detach(device_t dev); 85static int exynos_ehci_probe(device_t dev); 86 87struct exynos_ehci_softc { 88 device_t dev; 89 ehci_softc_t base; 90 struct resource *res[4]; 91 bus_space_tag_t host_bst; 92 bus_space_tag_t sysreg_bst; 93 bus_space_handle_t host_bsh; 94 bus_space_handle_t sysreg_bsh; 95 96}; 97 98static struct resource_spec exynos_ehci_spec[] = { 99 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 100 { SYS_RES_MEMORY, 1, RF_ACTIVE }, 101 { SYS_RES_MEMORY, 2, RF_ACTIVE }, 102 { SYS_RES_IRQ, 0, RF_ACTIVE }, 103 { -1, 0 } 104}; 105 106static device_method_t ehci_methods[] = { 107 /* Device interface */ 108 DEVMETHOD(device_probe, exynos_ehci_probe), 109 DEVMETHOD(device_attach, exynos_ehci_attach), 110 DEVMETHOD(device_detach, exynos_ehci_detach), 111 DEVMETHOD(device_suspend, bus_generic_suspend), 112 DEVMETHOD(device_resume, bus_generic_resume), 113 DEVMETHOD(device_shutdown, bus_generic_shutdown), 114 115 /* Bus interface */ 116 DEVMETHOD(bus_print_child, bus_generic_print_child), 117 118 { 0, 0 } 119}; 120 121/* kobj_class definition */ 122static driver_t ehci_driver = { 123 "ehci", 124 ehci_methods, 125 sizeof(ehci_softc_t) 126}; 127 128static devclass_t ehci_devclass; 129 130DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 131MODULE_DEPEND(ehci, usb, 1, 1, 1); 132 133/* 134 * Public methods 135 */ 136static int 137exynos_ehci_probe(device_t dev) 138{ 139 140 if (!ofw_bus_status_okay(dev)) 141 return (ENXIO); 142 143 if (ofw_bus_is_compatible(dev, "exynos,usb-ehci") == 0) 144 return (ENXIO); 145 146 device_set_desc(dev, "Exynos integrated USB controller"); 147 return (BUS_PROBE_DEFAULT); 148} 149 150static int 151gpio_ctrl(struct exynos_ehci_softc *esc, int dir, int power) 152{ 153 device_t gpio_dev; 154 155 /* Get the GPIO device, we need this to give power to USB */ 156 gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 157 if (gpio_dev == NULL) { 158 device_printf(esc->dev, "cant find gpio_dev\n"); 159 return (1); 160 } 161 162 if (power) 163 GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_HIGH); 164 else 165 GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_LOW); 166 167 if (dir) 168 GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_OUTPUT); 169 else 170 GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_INPUT); 171 172 return (0); 173} 174 175static int 176reset_hsic_hub(struct exynos_ehci_softc *esc, phandle_t hub) 177{ 178 device_t gpio_dev; 179 pcell_t pin; 180 181 /* TODO: check that hub is compatible with "smsc,usb3503" */ 182 if (!OF_hasprop(hub, "freebsd,reset-gpio")) { 183 return (1); 184 } 185 186 if (OF_getencprop(hub, "freebsd,reset-gpio", &pin, sizeof(pin)) < 0) { 187 device_printf(esc->dev, 188 "failed to decode reset GPIO pin number for HSIC hub\n"); 189 return (1); 190 } 191 192 /* Get the GPIO device, we need this to give power to USB */ 193 gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 194 if (gpio_dev == NULL) { 195 device_printf(esc->dev, "Cant find gpio device\n"); 196 return (1); 197 } 198 199 GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_LOW); 200 DELAY(100); 201 GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_HIGH); 202 203 return (0); 204} 205 206static int 207phy_init(struct exynos_ehci_softc *esc) 208{ 209 int reg; 210 phandle_t hub; 211 212 gpio_ctrl(esc, GPIO_INPUT, 1); 213 214 /* set USB HOST mode */ 215 bus_space_write_4(esc->sysreg_bst, esc->sysreg_bsh, 216 EXYNOS5_SYSREG_USB2_PHY, USB2_MODE_HOST); 217 218 /* Power ON phy */ 219 usb2_phy_power_on(); 220 221 reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); 222 reg &= ~(HOST_CTRL_CLK_MASK | 223 HOST_CTRL_RESET_PHY | 224 HOST_CTRL_RESET_PHY_ALL | 225 HOST_CTRL_SIDDQ | 226 HOST_CTRL_SUSPEND | 227 HOST_CTRL_SLEEP); 228 229 reg |= (HOST_CTRL_CLK_24MHZ | 230 HOST_CTRL_RESET_LINK); 231 bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); 232 233 DELAY(10); 234 235 reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); 236 reg &= ~(HOST_CTRL_RESET_LINK); 237 bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); 238 239 if ((hub = OF_finddevice("/hsichub")) != 0) { 240 reset_hsic_hub(esc, hub); 241 } 242 243 gpio_ctrl(esc, GPIO_OUTPUT, 1); 244 245 return (0); 246} 247 248static int 249exynos_ehci_attach(device_t dev) 250{ 251 struct exynos_ehci_softc *esc; 252 ehci_softc_t *sc; 253 bus_space_handle_t bsh; 254 int err; 255 256 esc = device_get_softc(dev); 257 esc->dev = dev; 258 sc = &esc->base; 259 sc->sc_bus.parent = dev; 260 sc->sc_bus.devices = sc->sc_devices; 261 sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 262 263 if (bus_alloc_resources(dev, exynos_ehci_spec, esc->res)) { 264 device_printf(dev, "could not allocate resources\n"); 265 return (ENXIO); 266 } 267 268 /* EHCI registers */ 269 sc->sc_io_tag = rman_get_bustag(esc->res[0]); 270 bsh = rman_get_bushandle(esc->res[0]); 271 sc->sc_io_size = rman_get_size(esc->res[0]); 272 273 /* EHCI HOST ctrl registers */ 274 esc->host_bst = rman_get_bustag(esc->res[1]); 275 esc->host_bsh = rman_get_bushandle(esc->res[1]); 276 277 /* SYSREG */ 278 esc->sysreg_bst = rman_get_bustag(esc->res[2]); 279 esc->sysreg_bsh = rman_get_bushandle(esc->res[2]); 280 281 /* get all DMA memory */ 282 if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), 283 &ehci_iterate_hw_softc)) 284 return (ENXIO); 285 286 /* 287 * Set handle to USB related registers subregion used by 288 * generic EHCI driver. 289 */ 290 err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0, 291 sc->sc_io_size, &sc->sc_io_hdl); 292 if (err != 0) 293 return (ENXIO); 294 295 phy_init(esc); 296 297 /* Setup interrupt handler */ 298 err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, 299 NULL, (driver_intr_t *)ehci_interrupt, sc, 300 &sc->sc_intr_hdl); 301 if (err) { 302 device_printf(dev, "Could not setup irq, " 303 "%d\n", err); 304 return (1); 305 } 306 307 /* Add USB device */ 308 sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); 309 if (!sc->sc_bus.bdev) { 310 device_printf(dev, "Could not add USB device\n"); 311 err = bus_teardown_intr(dev, esc->res[3], 312 sc->sc_intr_hdl); 313 if (err) 314 device_printf(dev, "Could not tear down irq," 315 " %d\n", err); 316 return (1); 317 } 318 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 319 320 strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); 321 322 err = ehci_init(sc); 323 if (!err) { 324 sc->sc_flags |= EHCI_SCFLG_DONEINIT; 325 err = device_probe_and_attach(sc->sc_bus.bdev); 326 } else { 327 device_printf(dev, "USB init failed err=%d\n", err); 328 329 device_delete_child(dev, sc->sc_bus.bdev); 330 sc->sc_bus.bdev = NULL; 331 332 err = bus_teardown_intr(dev, esc->res[3], 333 sc->sc_intr_hdl); 334 if (err) 335 device_printf(dev, "Could not tear down irq," 336 " %d\n", err); 337 return (1); 338 } 339 return (0); 340} 341 342static int 343exynos_ehci_detach(device_t dev) 344{ 345 struct exynos_ehci_softc *esc; 346 ehci_softc_t *sc; 347 int err; 348 349 esc = device_get_softc(dev); 350 sc = &esc->base; 351 352 if (sc->sc_flags & EHCI_SCFLG_DONEINIT) 353 return (0); 354 355 /* 356 * only call ehci_detach() after ehci_init() 357 */ 358 if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { 359 ehci_detach(sc); 360 sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; 361 } 362 363 /* 364 * Disable interrupts that might have been switched on in 365 * ehci_init. 366 */ 367 if (sc->sc_io_tag && sc->sc_io_hdl) 368 bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, 369 EHCI_USBINTR, 0); 370 371 if (esc->res[3] && sc->sc_intr_hdl) { 372 err = bus_teardown_intr(dev, esc->res[3], 373 sc->sc_intr_hdl); 374 if (err) { 375 device_printf(dev, "Could not tear down irq," 376 " %d\n", err); 377 return (err); 378 } 379 sc->sc_intr_hdl = NULL; 380 } 381 382 if (sc->sc_bus.bdev) { 383 device_delete_child(dev, sc->sc_bus.bdev); 384 sc->sc_bus.bdev = NULL; 385 } 386 387 /* During module unload there are lots of children leftover */ 388 device_delete_children(dev); 389 390 bus_release_resources(dev, exynos_ehci_spec, esc->res); 391 392 return (0); 393} 394