1252391Sray/*- 2252391Sray * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> 3252391Sray * All rights reserved. 4252391Sray * 5252391Sray * Redistribution and use in source and binary forms, with or without 6252391Sray * modification, are permitted provided that the following conditions 7252391Sray * are met: 8252391Sray * 1. Redistributions of source code must retain the above copyright 9252391Sray * notice, this list of conditions and the following disclaimer. 10252391Sray * 2. Redistributions in binary form must reproduce the above copyright 11252391Sray * notice, this list of conditions and the following disclaimer in the 12252391Sray * documentation and/or other materials provided with the distribution. 13252391Sray * 14252391Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15252391Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16252391Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17252391Sray * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18252391Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19252391Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20252391Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21252391Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22252391Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23252391Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24252391Sray * SUCH DAMAGE. 25252391Sray */ 26252391Sray 27252391Sray#include <sys/cdefs.h> 28252391Sray__FBSDID("$FreeBSD: releng/10.2/sys/arm/samsung/exynos/exynos5_ehci.c 278278 2015-02-05 20:03:02Z hselasky $"); 29252391Sray 30252391Sray#include "opt_bus.h" 31252391Sray 32252391Sray#include <sys/param.h> 33252391Sray#include <sys/systm.h> 34252391Sray#include <sys/kernel.h> 35252391Sray#include <sys/module.h> 36252391Sray#include <sys/bus.h> 37252391Sray#include <sys/condvar.h> 38252391Sray#include <sys/rman.h> 39266341Sian#include <sys/gpio.h> 40252391Sray 41252391Sray#include <dev/ofw/ofw_bus.h> 42252391Sray#include <dev/ofw/ofw_bus_subr.h> 43252391Sray 44252391Sray#include <dev/usb/usb.h> 45252391Sray#include <dev/usb/usbdi.h> 46252391Sray#include <dev/usb/usb_busdma.h> 47252391Sray#include <dev/usb/usb_process.h> 48252391Sray#include <dev/usb/usb_controller.h> 49252391Sray#include <dev/usb/usb_bus.h> 50252391Sray#include <dev/usb/controller/ehci.h> 51252391Sray#include <dev/usb/controller/ehcireg.h> 52252391Sray 53252391Sray#include <dev/fdt/fdt_common.h> 54252391Sray 55252391Sray#include <machine/bus.h> 56252391Sray#include <machine/resource.h> 57252391Sray 58266341Sian#include "gpio_if.h" 59266341Sian 60252391Sray#include "opt_platform.h" 61252391Sray 62252391Sray/* GPIO control */ 63252391Sray#define GPIO_OUTPUT 1 64252391Sray#define GPIO_INPUT 0 65266341Sian#define PIN_USB 161 66252391Sray 67252391Sray/* PWR control */ 68252391Sray#define EXYNOS5_PWR_USBHOST_PHY 0x708 69252391Sray#define PHY_POWER_ON 1 70252391Sray#define PHY_POWER_OFF 0 71252391Sray 72252391Sray/* SYSREG */ 73263425Sbr#define EXYNOS5_SYSREG_USB2_PHY 0x0 74252391Sray#define USB2_MODE_HOST 0x1 75252391Sray 76252391Sray/* USB HOST */ 77252391Sray#define HOST_CTRL_CLK_24MHZ (5 << 16) 78252391Sray#define HOST_CTRL_CLK_MASK (7 << 16) 79252391Sray#define HOST_CTRL_SIDDQ (1 << 6) 80252391Sray#define HOST_CTRL_SLEEP (1 << 5) 81252391Sray#define HOST_CTRL_SUSPEND (1 << 4) 82252391Sray#define HOST_CTRL_RESET_LINK (1 << 1) 83252391Sray#define HOST_CTRL_RESET_PHY (1 << 0) 84258780Seadler#define HOST_CTRL_RESET_PHY_ALL (1U << 31) 85252391Sray 86252391Sray/* Forward declarations */ 87252391Sraystatic int exynos_ehci_attach(device_t dev); 88252391Sraystatic int exynos_ehci_detach(device_t dev); 89252391Sraystatic int exynos_ehci_probe(device_t dev); 90252391Sray 91252391Sraystruct exynos_ehci_softc { 92266341Sian device_t dev; 93252391Sray ehci_softc_t base; 94266341Sian struct resource *res[5]; 95252391Sray bus_space_tag_t host_bst; 96252391Sray bus_space_tag_t pwr_bst; 97252391Sray bus_space_tag_t sysreg_bst; 98252391Sray bus_space_handle_t host_bsh; 99252391Sray bus_space_handle_t pwr_bsh; 100252391Sray bus_space_handle_t sysreg_bsh; 101252391Sray 102252391Sray}; 103252391Sray 104252391Sraystatic struct resource_spec exynos_ehci_spec[] = { 105252391Sray { SYS_RES_MEMORY, 0, RF_ACTIVE }, 106252391Sray { SYS_RES_MEMORY, 1, RF_ACTIVE }, 107252391Sray { SYS_RES_MEMORY, 2, RF_ACTIVE }, 108252391Sray { SYS_RES_MEMORY, 3, RF_ACTIVE }, 109252391Sray { SYS_RES_IRQ, 0, RF_ACTIVE }, 110252391Sray { -1, 0 } 111252391Sray}; 112252391Sray 113252391Sraystatic device_method_t ehci_methods[] = { 114252391Sray /* Device interface */ 115252391Sray DEVMETHOD(device_probe, exynos_ehci_probe), 116252391Sray DEVMETHOD(device_attach, exynos_ehci_attach), 117252391Sray DEVMETHOD(device_detach, exynos_ehci_detach), 118252391Sray DEVMETHOD(device_suspend, bus_generic_suspend), 119252391Sray DEVMETHOD(device_resume, bus_generic_resume), 120252391Sray DEVMETHOD(device_shutdown, bus_generic_shutdown), 121252391Sray 122252391Sray /* Bus interface */ 123252391Sray DEVMETHOD(bus_print_child, bus_generic_print_child), 124252391Sray 125252391Sray { 0, 0 } 126252391Sray}; 127252391Sray 128252391Sray/* kobj_class definition */ 129252391Sraystatic driver_t ehci_driver = { 130252391Sray "ehci", 131252391Sray ehci_methods, 132252391Sray sizeof(ehci_softc_t) 133252391Sray}; 134252391Sray 135252391Sraystatic devclass_t ehci_devclass; 136252391Sray 137252391SrayDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 138252391SrayMODULE_DEPEND(ehci, usb, 1, 1, 1); 139252391Sray 140252391Sray/* 141252391Sray * Public methods 142252391Sray */ 143252391Sraystatic int 144252391Srayexynos_ehci_probe(device_t dev) 145252391Sray{ 146252391Sray 147261410Sian if (!ofw_bus_status_okay(dev)) 148261410Sian return (ENXIO); 149261410Sian 150252391Sray if (ofw_bus_is_compatible(dev, "exynos,usb-ehci") == 0) 151252391Sray return (ENXIO); 152252391Sray 153252391Sray device_set_desc(dev, "Exynos integrated USB controller"); 154252391Sray return (BUS_PROBE_DEFAULT); 155252391Sray} 156252391Sray 157252391Sraystatic int 158252391Sraygpio_ctrl(struct exynos_ehci_softc *esc, int dir, int power) 159252391Sray{ 160266341Sian device_t gpio_dev; 161252391Sray 162266341Sian /* Get the GPIO device, we need this to give power to USB */ 163266341Sian gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 164266341Sian if (gpio_dev == NULL) { 165266341Sian device_printf(esc->dev, "cant find gpio_dev\n"); 166266341Sian return (1); 167266341Sian } 168252391Sray 169266341Sian if (power) 170266341Sian GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_HIGH); 171266341Sian else 172266341Sian GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_LOW); 173252391Sray 174266341Sian if (dir) 175266341Sian GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_OUTPUT); 176266341Sian else 177266341Sian GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_INPUT); 178266341Sian 179252391Sray return (0); 180252391Sray} 181252391Sray 182252391Sraystatic int 183252391Srayphy_init(struct exynos_ehci_softc *esc) 184252391Sray{ 185252391Sray int reg; 186252391Sray 187252391Sray gpio_ctrl(esc, GPIO_INPUT, 1); 188252391Sray 189252391Sray /* set USB HOST mode */ 190252391Sray bus_space_write_4(esc->sysreg_bst, esc->sysreg_bsh, 191252391Sray EXYNOS5_SYSREG_USB2_PHY, USB2_MODE_HOST); 192252391Sray 193252391Sray /* Power ON phy */ 194252391Sray bus_space_write_4(esc->pwr_bst, esc->pwr_bsh, 195252391Sray EXYNOS5_PWR_USBHOST_PHY, PHY_POWER_ON); 196252391Sray 197252391Sray reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); 198252391Sray reg &= ~(HOST_CTRL_CLK_MASK | 199252391Sray HOST_CTRL_RESET_PHY | 200252391Sray HOST_CTRL_RESET_PHY_ALL | 201252391Sray HOST_CTRL_SIDDQ | 202252391Sray HOST_CTRL_SUSPEND | 203252391Sray HOST_CTRL_SLEEP); 204252391Sray 205252391Sray reg |= (HOST_CTRL_CLK_24MHZ | 206252391Sray HOST_CTRL_RESET_LINK); 207252391Sray bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); 208252391Sray 209252391Sray DELAY(10); 210252391Sray 211252391Sray reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); 212252391Sray reg &= ~(HOST_CTRL_RESET_LINK); 213252391Sray bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg); 214252391Sray 215252391Sray gpio_ctrl(esc, GPIO_OUTPUT, 1); 216252391Sray 217252391Sray return (0); 218252391Sray} 219252391Sray 220252391Sraystatic int 221252391Srayexynos_ehci_attach(device_t dev) 222252391Sray{ 223252391Sray struct exynos_ehci_softc *esc; 224252391Sray ehci_softc_t *sc; 225252391Sray bus_space_handle_t bsh; 226252391Sray int err; 227252391Sray 228252391Sray esc = device_get_softc(dev); 229266341Sian esc->dev = dev; 230252391Sray sc = &esc->base; 231252391Sray sc->sc_bus.parent = dev; 232252391Sray sc->sc_bus.devices = sc->sc_devices; 233252391Sray sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 234278278Shselasky sc->sc_bus.dma_bits = 32; 235252391Sray 236252391Sray if (bus_alloc_resources(dev, exynos_ehci_spec, esc->res)) { 237252391Sray device_printf(dev, "could not allocate resources\n"); 238252391Sray return (ENXIO); 239252391Sray } 240252391Sray 241252391Sray /* EHCI registers */ 242252391Sray sc->sc_io_tag = rman_get_bustag(esc->res[0]); 243252391Sray bsh = rman_get_bushandle(esc->res[0]); 244252391Sray sc->sc_io_size = rman_get_size(esc->res[0]); 245252391Sray 246252391Sray /* EHCI HOST ctrl registers */ 247252391Sray esc->host_bst = rman_get_bustag(esc->res[1]); 248252391Sray esc->host_bsh = rman_get_bushandle(esc->res[1]); 249252391Sray 250252391Sray /* PWR registers */ 251252391Sray esc->pwr_bst = rman_get_bustag(esc->res[2]); 252252391Sray esc->pwr_bsh = rman_get_bushandle(esc->res[2]); 253252391Sray 254252391Sray /* SYSREG */ 255252391Sray esc->sysreg_bst = rman_get_bustag(esc->res[3]); 256252391Sray esc->sysreg_bsh = rman_get_bushandle(esc->res[3]); 257252391Sray 258252391Sray /* get all DMA memory */ 259252391Sray if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), 260252391Sray &ehci_iterate_hw_softc)) 261252391Sray return (ENXIO); 262252391Sray 263252391Sray /* 264252391Sray * Set handle to USB related registers subregion used by 265252391Sray * generic EHCI driver. 266252391Sray */ 267252391Sray err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0, 268252391Sray sc->sc_io_size, &sc->sc_io_hdl); 269252391Sray if (err != 0) 270252391Sray return (ENXIO); 271252391Sray 272252391Sray phy_init(esc); 273252391Sray 274252391Sray /* Setup interrupt handler */ 275266341Sian err = bus_setup_intr(dev, esc->res[4], INTR_TYPE_BIO | INTR_MPSAFE, 276252391Sray NULL, (driver_intr_t *)ehci_interrupt, sc, 277252391Sray &sc->sc_intr_hdl); 278252391Sray if (err) { 279252391Sray device_printf(dev, "Could not setup irq, " 280252391Sray "%d\n", err); 281252391Sray return (1); 282252391Sray } 283252391Sray 284252391Sray /* Add USB device */ 285252391Sray sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); 286252391Sray if (!sc->sc_bus.bdev) { 287252391Sray device_printf(dev, "Could not add USB device\n"); 288266341Sian err = bus_teardown_intr(dev, esc->res[4], 289252391Sray sc->sc_intr_hdl); 290252391Sray if (err) 291252391Sray device_printf(dev, "Could not tear down irq," 292252391Sray " %d\n", err); 293252391Sray return (1); 294252391Sray } 295252391Sray device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 296252391Sray 297252391Sray strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); 298252391Sray 299252391Sray err = ehci_init(sc); 300252391Sray if (!err) { 301252391Sray sc->sc_flags |= EHCI_SCFLG_DONEINIT; 302252391Sray err = device_probe_and_attach(sc->sc_bus.bdev); 303252391Sray } else { 304252391Sray device_printf(dev, "USB init failed err=%d\n", err); 305252391Sray 306252391Sray device_delete_child(dev, sc->sc_bus.bdev); 307252391Sray sc->sc_bus.bdev = NULL; 308252391Sray 309266341Sian err = bus_teardown_intr(dev, esc->res[4], 310252391Sray sc->sc_intr_hdl); 311252391Sray if (err) 312252391Sray device_printf(dev, "Could not tear down irq," 313252391Sray " %d\n", err); 314252391Sray return (1); 315252391Sray } 316252391Sray return (0); 317252391Sray} 318252391Sray 319252391Sraystatic int 320252391Srayexynos_ehci_detach(device_t dev) 321252391Sray{ 322252391Sray struct exynos_ehci_softc *esc; 323252391Sray ehci_softc_t *sc; 324252391Sray int err; 325252391Sray 326252391Sray esc = device_get_softc(dev); 327252391Sray sc = &esc->base; 328252391Sray 329252391Sray if (sc->sc_flags & EHCI_SCFLG_DONEINIT) 330252391Sray return (0); 331252391Sray 332252391Sray /* 333252391Sray * only call ehci_detach() after ehci_init() 334252391Sray */ 335252391Sray if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { 336252391Sray ehci_detach(sc); 337252391Sray sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; 338252391Sray } 339252391Sray 340252391Sray /* 341252391Sray * Disable interrupts that might have been switched on in 342252391Sray * ehci_init. 343252391Sray */ 344252391Sray if (sc->sc_io_tag && sc->sc_io_hdl) 345252391Sray bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, 346252391Sray EHCI_USBINTR, 0); 347252391Sray 348266341Sian if (esc->res[4] && sc->sc_intr_hdl) { 349266341Sian err = bus_teardown_intr(dev, esc->res[4], 350252391Sray sc->sc_intr_hdl); 351252391Sray if (err) { 352252391Sray device_printf(dev, "Could not tear down irq," 353252391Sray " %d\n", err); 354252391Sray return (err); 355252391Sray } 356252391Sray sc->sc_intr_hdl = NULL; 357252391Sray } 358252391Sray 359252391Sray if (sc->sc_bus.bdev) { 360252391Sray device_delete_child(dev, sc->sc_bus.bdev); 361252391Sray sc->sc_bus.bdev = NULL; 362252391Sray } 363252391Sray 364252391Sray /* During module unload there are lots of children leftover */ 365252391Sray device_delete_children(dev); 366252391Sray 367252391Sray bus_release_resources(dev, exynos_ehci_spec, esc->res); 368252391Sray 369252391Sray return (0); 370252391Sray} 371