exynos5_xhci.c revision 269703
1/*- 2 * Copyright (c) 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_xhci.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 40#include <dev/ofw/ofw_bus.h> 41#include <dev/ofw/ofw_bus_subr.h> 42 43#include <dev/usb/usb.h> 44#include <dev/usb/usbdi.h> 45#include <dev/usb/usb_busdma.h> 46#include <dev/usb/usb_process.h> 47#include <dev/usb/usb_controller.h> 48#include <dev/usb/usb_bus.h> 49#include <dev/usb/controller/xhci.h> 50#include <dev/usb/controller/xhcireg.h> 51 52#include <machine/bus.h> 53#include <machine/resource.h> 54 55#include <arm/samsung/exynos/exynos5_common.h> 56 57#include "opt_platform.h" 58 59#define GSNPSID 0x20 60#define GSNPSID_MASK 0xffff0000 61#define REVISION_MASK 0xffff 62#define GCTL 0x10 63#define GCTL_PWRDNSCALE(n) ((n) << 19) 64#define GCTL_U2RSTECN (1 << 16) 65#define GCTL_CLK_BUS (0) 66#define GCTL_CLK_PIPE (1) 67#define GCTL_CLK_PIPEHALF (2) 68#define GCTL_CLK_M (3) 69#define GCTL_CLK_S (6) 70#define GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) 71#define GCTL_PRTCAPDIR(n) ((n) << 12) 72#define GCTL_PRTCAP_HOST 1 73#define GCTL_PRTCAP_DEVICE 2 74#define GCTL_PRTCAP_OTG 3 75#define GCTL_CORESOFTRESET (1 << 11) 76#define GCTL_SCALEDOWN_MASK 3 77#define GCTL_SCALEDOWN_SHIFT 4 78#define GCTL_DISSCRAMBLE (1 << 3) 79#define GCTL_DSBLCLKGTNG (1 << 0) 80#define GHWPARAMS1 0x3c 81#define GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) 82#define GHWPARAMS1_EN_PWROPT_NO 0 83#define GHWPARAMS1_EN_PWROPT_CLK 1 84#define GUSB2PHYCFG(n) (0x100 + (n * 0x04)) 85#define GUSB2PHYCFG_PHYSOFTRST (1 << 31) 86#define GUSB2PHYCFG_SUSPHY (1 << 6) 87#define GUSB3PIPECTL(n) (0x1c0 + (n * 0x04)) 88#define GUSB3PIPECTL_PHYSOFTRST (1 << 31) 89#define GUSB3PIPECTL_SUSPHY (1 << 17) 90 91/* Forward declarations */ 92static int exynos_xhci_attach(device_t dev); 93static int exynos_xhci_detach(device_t dev); 94static int exynos_xhci_probe(device_t dev); 95 96struct exynos_xhci_softc { 97 device_t dev; 98 struct xhci_softc base; 99 struct resource *res[3]; 100 bus_space_tag_t bst; 101 bus_space_handle_t bsh; 102}; 103 104static struct resource_spec exynos_xhci_spec[] = { 105 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 106 { SYS_RES_MEMORY, 1, RF_ACTIVE }, 107 { SYS_RES_IRQ, 0, RF_ACTIVE }, 108 { -1, 0 } 109}; 110 111static device_method_t xhci_methods[] = { 112 /* Device interface */ 113 DEVMETHOD(device_probe, exynos_xhci_probe), 114 DEVMETHOD(device_attach, exynos_xhci_attach), 115 DEVMETHOD(device_detach, exynos_xhci_detach), 116 DEVMETHOD(device_suspend, bus_generic_suspend), 117 DEVMETHOD(device_resume, bus_generic_resume), 118 DEVMETHOD(device_shutdown, bus_generic_shutdown), 119 120 DEVMETHOD_END 121}; 122 123/* kobj_class definition */ 124static driver_t xhci_driver = { 125 "xhci", 126 xhci_methods, 127 sizeof(struct xhci_softc) 128}; 129 130static devclass_t xhci_devclass; 131 132DRIVER_MODULE(xhci, simplebus, xhci_driver, xhci_devclass, 0, 0); 133MODULE_DEPEND(xhci, usb, 1, 1, 1); 134 135/* 136 * Public methods 137 */ 138static int 139exynos_xhci_probe(device_t dev) 140{ 141 142 if (!ofw_bus_status_okay(dev)) 143 return (ENXIO); 144 145 if (ofw_bus_is_compatible(dev, "samsung,exynos5250-dwusb3") == 0) 146 return (ENXIO); 147 148 device_set_desc(dev, "Exynos USB 3.0 controller"); 149 return (BUS_PROBE_DEFAULT); 150} 151 152static int 153dwc3_init(struct exynos_xhci_softc *esc) 154{ 155 struct xhci_softc *sc; 156 int hwparams1; 157 int rev; 158 int reg; 159 160 sc = &esc->base; 161 162 rev = READ4(esc, GSNPSID); 163 if ((rev & GSNPSID_MASK) != 0x55330000) { 164 printf("It is not DWC3 controller\n"); 165 return (-1); 166 } 167 168 /* Reset controller */ 169 WRITE4(esc, GCTL, GCTL_CORESOFTRESET); 170 WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST); 171 WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST); 172 173 DELAY(100000); 174 175 reg = READ4(esc, GUSB3PIPECTL(0)); 176 reg &= ~(GUSB3PIPECTL_PHYSOFTRST); 177 WRITE4(esc, GUSB3PIPECTL(0), reg); 178 179 reg = READ4(esc, GUSB2PHYCFG(0)); 180 reg &= ~(GUSB2PHYCFG_PHYSOFTRST); 181 WRITE4(esc, GUSB2PHYCFG(0), reg); 182 183 reg = READ4(esc, GCTL); 184 reg &= ~GCTL_CORESOFTRESET; 185 WRITE4(esc, GCTL, reg); 186 187 hwparams1 = READ4(esc, GHWPARAMS1); 188 189 reg = READ4(esc, GCTL); 190 reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT); 191 reg &= ~(GCTL_DISSCRAMBLE); 192 193 if (GHWPARAMS1_EN_PWROPT(hwparams1) == \ 194 GHWPARAMS1_EN_PWROPT_CLK) 195 reg &= ~(GCTL_DSBLCLKGTNG); 196 197 if ((rev & REVISION_MASK) < 0x190a) 198 reg |= (GCTL_U2RSTECN); 199 WRITE4(esc, GCTL, reg); 200 201 /* Set host mode */ 202 reg = READ4(esc, GCTL); 203 reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG)); 204 reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST); 205 WRITE4(esc, GCTL, reg); 206 207 return (0); 208} 209 210static int 211exynos_xhci_attach(device_t dev) 212{ 213 struct exynos_xhci_softc *esc; 214 struct xhci_softc *sc; 215 bus_space_handle_t bsh; 216 int err; 217 218 esc = device_get_softc(dev); 219 esc->dev = dev; 220 221 sc = &esc->base; 222 223 if (xhci_init(sc, dev)) { 224 device_printf(dev, "Could not initialize softc\n"); 225 return (ENXIO); 226 } 227 228 if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) { 229 device_printf(dev, "could not allocate resources\n"); 230 return (ENXIO); 231 } 232 233 usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0); 234 235 /* XHCI registers */ 236 sc->sc_io_tag = rman_get_bustag(esc->res[0]); 237 bsh = rman_get_bushandle(esc->res[0]); 238 sc->sc_io_size = rman_get_size(esc->res[0]); 239 240 /* DWC3 ctrl registers */ 241 esc->bst = rman_get_bustag(esc->res[1]); 242 esc->bsh = rman_get_bushandle(esc->res[1]); 243 244 /* 245 * Set handle to USB related registers subregion used by 246 * generic XHCI driver. 247 */ 248 err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0, 249 sc->sc_io_size, &sc->sc_io_hdl); 250 if (err != 0) 251 return (ENXIO); 252 253 /* Setup interrupt handler */ 254 err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, 255 NULL, (driver_intr_t *)xhci_interrupt, sc, 256 &sc->sc_intr_hdl); 257 if (err) { 258 device_printf(dev, "Could not setup irq, %d\n", err); 259 return (1); 260 } 261 262 /* Add USB device */ 263 sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); 264 if (!sc->sc_bus.bdev) { 265 device_printf(dev, "Could not add USB device\n"); 266 err = bus_teardown_intr(dev, esc->res[2], 267 sc->sc_intr_hdl); 268 if (err) 269 device_printf(dev, "Could not tear down irq," 270 " %d\n", err); 271 return (1); 272 } 273 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 274 strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); 275 276 dwc3_init(esc); 277 278 err = xhci_halt_controller(sc); 279 if (err == 0) { 280 device_printf(dev, "Starting controller\n"); 281 err = xhci_start_controller(sc); 282 } 283 284 if (err == 0) { 285 device_printf(dev, "Controller started\n"); 286 err = device_probe_and_attach(sc->sc_bus.bdev); 287 } 288 289 if (err == 0) { 290 device_printf(dev, "Attached success\n"); 291 } else { 292 device_printf(dev, "USB 3.0 init failed err=%d\n", err); 293 294 device_delete_child(dev, sc->sc_bus.bdev); 295 sc->sc_bus.bdev = NULL; 296 297 err = bus_teardown_intr(dev, esc->res[2], 298 sc->sc_intr_hdl); 299 if (err) 300 device_printf(dev, "Could not tear down irq," 301 " %d\n", err); 302 return (1); 303 } 304 return (0); 305} 306 307static int 308exynos_xhci_detach(device_t dev) 309{ 310 struct exynos_xhci_softc *esc; 311 struct xhci_softc *sc; 312 int err; 313 314 esc = device_get_softc(dev); 315 sc = &esc->base; 316 317 if (esc->res[2] && sc->sc_intr_hdl) { 318 err = bus_teardown_intr(dev, esc->res[2], 319 sc->sc_intr_hdl); 320 if (err) { 321 device_printf(dev, "Could not tear down irq," 322 " %d\n", err); 323 return (err); 324 } 325 sc->sc_intr_hdl = NULL; 326 } 327 328 if (sc->sc_bus.bdev) { 329 device_delete_child(dev, sc->sc_bus.bdev); 330 sc->sc_bus.bdev = NULL; 331 } 332 333 /* During module unload there are lots of children leftover */ 334 device_delete_children(dev); 335 336 bus_release_resources(dev, exynos_xhci_spec, esc->res); 337 338 return (0); 339} 340