exynos5_ehci.c revision 276717
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 276717 2015-01-05 20:22:18Z hselasky $"); 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 sc->sc_bus.dma_bits = 32; 263 264 if (bus_alloc_resources(dev, exynos_ehci_spec, esc->res)) { 265 device_printf(dev, "could not allocate resources\n"); 266 return (ENXIO); 267 } 268 269 /* EHCI registers */ 270 sc->sc_io_tag = rman_get_bustag(esc->res[0]); 271 bsh = rman_get_bushandle(esc->res[0]); 272 sc->sc_io_size = rman_get_size(esc->res[0]); 273 274 /* EHCI HOST ctrl registers */ 275 esc->host_bst = rman_get_bustag(esc->res[1]); 276 esc->host_bsh = rman_get_bushandle(esc->res[1]); 277 278 /* SYSREG */ 279 esc->sysreg_bst = rman_get_bustag(esc->res[2]); 280 esc->sysreg_bsh = rman_get_bushandle(esc->res[2]); 281 282 /* get all DMA memory */ 283 if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), 284 &ehci_iterate_hw_softc)) 285 return (ENXIO); 286 287 /* 288 * Set handle to USB related registers subregion used by 289 * generic EHCI driver. 290 */ 291 err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0, 292 sc->sc_io_size, &sc->sc_io_hdl); 293 if (err != 0) 294 return (ENXIO); 295 296 phy_init(esc); 297 298 /* Setup interrupt handler */ 299 err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, 300 NULL, (driver_intr_t *)ehci_interrupt, sc, 301 &sc->sc_intr_hdl); 302 if (err) { 303 device_printf(dev, "Could not setup irq, " 304 "%d\n", err); 305 return (1); 306 } 307 308 /* Add USB device */ 309 sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); 310 if (!sc->sc_bus.bdev) { 311 device_printf(dev, "Could not add USB device\n"); 312 err = bus_teardown_intr(dev, esc->res[3], 313 sc->sc_intr_hdl); 314 if (err) 315 device_printf(dev, "Could not tear down irq," 316 " %d\n", err); 317 return (1); 318 } 319 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 320 321 strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); 322 323 err = ehci_init(sc); 324 if (!err) { 325 sc->sc_flags |= EHCI_SCFLG_DONEINIT; 326 err = device_probe_and_attach(sc->sc_bus.bdev); 327 } else { 328 device_printf(dev, "USB init failed err=%d\n", err); 329 330 device_delete_child(dev, sc->sc_bus.bdev); 331 sc->sc_bus.bdev = NULL; 332 333 err = bus_teardown_intr(dev, esc->res[3], 334 sc->sc_intr_hdl); 335 if (err) 336 device_printf(dev, "Could not tear down irq," 337 " %d\n", err); 338 return (1); 339 } 340 return (0); 341} 342 343static int 344exynos_ehci_detach(device_t dev) 345{ 346 struct exynos_ehci_softc *esc; 347 ehci_softc_t *sc; 348 int err; 349 350 esc = device_get_softc(dev); 351 sc = &esc->base; 352 353 if (sc->sc_flags & EHCI_SCFLG_DONEINIT) 354 return (0); 355 356 /* 357 * only call ehci_detach() after ehci_init() 358 */ 359 if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { 360 ehci_detach(sc); 361 sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; 362 } 363 364 /* 365 * Disable interrupts that might have been switched on in 366 * ehci_init. 367 */ 368 if (sc->sc_io_tag && sc->sc_io_hdl) 369 bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, 370 EHCI_USBINTR, 0); 371 372 if (esc->res[3] && sc->sc_intr_hdl) { 373 err = bus_teardown_intr(dev, esc->res[3], 374 sc->sc_intr_hdl); 375 if (err) { 376 device_printf(dev, "Could not tear down irq," 377 " %d\n", err); 378 return (err); 379 } 380 sc->sc_intr_hdl = NULL; 381 } 382 383 if (sc->sc_bus.bdev) { 384 device_delete_child(dev, sc->sc_bus.bdev); 385 sc->sc_bus.bdev = NULL; 386 } 387 388 /* During module unload there are lots of children leftover */ 389 device_delete_children(dev); 390 391 bus_release_resources(dev, exynos_ehci_spec, esc->res); 392 393 return (0); 394} 395