exynos5_xhci.c revision 279542
1236769Sobrien/*- 2236769Sobrien * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 3236769Sobrien * All rights reserved. 4236769Sobrien * 5236769Sobrien * Redistribution and use in source and binary forms, with or without 6236769Sobrien * modification, are permitted provided that the following conditions 7236769Sobrien * are met: 8236769Sobrien * 1. Redistributions of source code must retain the above copyright 9236769Sobrien * notice, this list of conditions and the following disclaimer. 10236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 11236769Sobrien * notice, this list of conditions and the following disclaimer in the 12236769Sobrien * documentation and/or other materials provided with the distribution. 13236769Sobrien * 14236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17236769Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24236769Sobrien * SUCH DAMAGE. 25236769Sobrien */ 26236769Sobrien 27236769Sobrien#include <sys/cdefs.h> 28236769Sobrien__FBSDID("$FreeBSD: head/sys/arm/samsung/exynos/exynos5_xhci.c 279542 2015-03-02 20:38:17Z hselasky $"); 29236769Sobrien 30236769Sobrien#include "opt_bus.h" 31236769Sobrien 32236769Sobrien#include <sys/param.h> 33236769Sobrien#include <sys/systm.h> 34236769Sobrien#include <sys/kernel.h> 35236769Sobrien#include <sys/module.h> 36236769Sobrien#include <sys/bus.h> 37236769Sobrien#include <sys/condvar.h> 38236769Sobrien#include <sys/rman.h> 39236769Sobrien 40236769Sobrien#include <dev/ofw/ofw_bus.h> 41236769Sobrien#include <dev/ofw/ofw_bus_subr.h> 42236769Sobrien 43236769Sobrien#include <dev/usb/usb.h> 44236769Sobrien#include <dev/usb/usbdi.h> 45236769Sobrien#include <dev/usb/usb_busdma.h> 46236769Sobrien#include <dev/usb/usb_process.h> 47236769Sobrien#include <dev/usb/usb_controller.h> 48236769Sobrien#include <dev/usb/usb_bus.h> 49236769Sobrien#include <dev/usb/controller/xhci.h> 50236769Sobrien#include <dev/usb/controller/xhcireg.h> 51236769Sobrien 52236769Sobrien#include <machine/bus.h> 53236769Sobrien#include <machine/resource.h> 54236769Sobrien 55236769Sobrien#include <arm/samsung/exynos/exynos5_common.h> 56236769Sobrien 57236769Sobrien#include "opt_platform.h" 58236769Sobrien 59236769Sobrien#define GSNPSID 0x20 60236769Sobrien#define GSNPSID_MASK 0xffff0000 61236769Sobrien#define REVISION_MASK 0xffff 62236769Sobrien#define GCTL 0x10 63236769Sobrien#define GCTL_PWRDNSCALE(n) ((n) << 19) 64236769Sobrien#define GCTL_U2RSTECN (1 << 16) 65236769Sobrien#define GCTL_CLK_BUS (0) 66236769Sobrien#define GCTL_CLK_PIPE (1) 67236769Sobrien#define GCTL_CLK_PIPEHALF (2) 68236769Sobrien#define GCTL_CLK_M (3) 69236769Sobrien#define GCTL_CLK_S (6) 70236769Sobrien#define GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) 71236769Sobrien#define GCTL_PRTCAPDIR(n) ((n) << 12) 72236769Sobrien#define GCTL_PRTCAP_HOST 1 73236769Sobrien#define GCTL_PRTCAP_DEVICE 2 74236769Sobrien#define GCTL_PRTCAP_OTG 3 75236769Sobrien#define GCTL_CORESOFTRESET (1 << 11) 76236769Sobrien#define GCTL_SCALEDOWN_MASK 3 77236769Sobrien#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 device_attach_t exynos_xhci_attach; 93static device_detach_t exynos_xhci_detach; 94static device_probe_t exynos_xhci_probe; 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 int hwparams1; 156 int rev; 157 int reg; 158 159 rev = READ4(esc, GSNPSID); 160 if ((rev & GSNPSID_MASK) != 0x55330000) { 161 printf("It is not DWC3 controller\n"); 162 return (-1); 163 } 164 165 /* Reset controller */ 166 WRITE4(esc, GCTL, GCTL_CORESOFTRESET); 167 WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST); 168 WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST); 169 170 DELAY(100000); 171 172 reg = READ4(esc, GUSB3PIPECTL(0)); 173 reg &= ~(GUSB3PIPECTL_PHYSOFTRST); 174 WRITE4(esc, GUSB3PIPECTL(0), reg); 175 176 reg = READ4(esc, GUSB2PHYCFG(0)); 177 reg &= ~(GUSB2PHYCFG_PHYSOFTRST); 178 WRITE4(esc, GUSB2PHYCFG(0), reg); 179 180 reg = READ4(esc, GCTL); 181 reg &= ~GCTL_CORESOFTRESET; 182 WRITE4(esc, GCTL, reg); 183 184 hwparams1 = READ4(esc, GHWPARAMS1); 185 186 reg = READ4(esc, GCTL); 187 reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT); 188 reg &= ~(GCTL_DISSCRAMBLE); 189 190 if (GHWPARAMS1_EN_PWROPT(hwparams1) == \ 191 GHWPARAMS1_EN_PWROPT_CLK) 192 reg &= ~(GCTL_DSBLCLKGTNG); 193 194 if ((rev & REVISION_MASK) < 0x190a) 195 reg |= (GCTL_U2RSTECN); 196 WRITE4(esc, GCTL, reg); 197 198 /* Set host mode */ 199 reg = READ4(esc, GCTL); 200 reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG)); 201 reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST); 202 WRITE4(esc, GCTL, reg); 203 204 return (0); 205} 206 207static int 208exynos_xhci_attach(device_t dev) 209{ 210 struct exynos_xhci_softc *esc = device_get_softc(dev); 211 bus_space_handle_t bsh; 212 int err; 213 214 esc->dev = dev; 215 216 if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) { 217 device_printf(dev, "could not allocate resources\n"); 218 return (ENXIO); 219 } 220 221 /* XHCI registers */ 222 esc->base.sc_io_tag = rman_get_bustag(esc->res[0]); 223 bsh = rman_get_bushandle(esc->res[0]); 224 esc->base.sc_io_size = rman_get_size(esc->res[0]); 225 226 /* DWC3 ctrl registers */ 227 esc->bst = rman_get_bustag(esc->res[1]); 228 esc->bsh = rman_get_bushandle(esc->res[1]); 229 230 /* 231 * Set handle to USB related registers subregion used by 232 * generic XHCI driver. 233 */ 234 err = bus_space_subregion(esc->base.sc_io_tag, bsh, 0x0, 235 esc->base.sc_io_size, &esc->base.sc_io_hdl); 236 if (err != 0) { 237 device_printf(dev, "Subregion failed\n"); 238 bus_release_resources(dev, exynos_xhci_spec, esc->res); 239 return (ENXIO); 240 } 241 242 if (xhci_init(&esc->base, dev)) { 243 device_printf(dev, "Could not initialize softc\n"); 244 bus_release_resources(dev, exynos_xhci_spec, esc->res); 245 return (ENXIO); 246 } 247 248 /* Setup interrupt handler */ 249 err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, 250 NULL, (driver_intr_t *)xhci_interrupt, &esc->base, 251 &esc->base.sc_intr_hdl); 252 if (err) { 253 device_printf(dev, "Could not setup irq, %d\n", err); 254 esc->base.sc_intr_hdl = NULL; 255 goto error; 256 } 257 258 /* Add USB device */ 259 esc->base.sc_bus.bdev = device_add_child(dev, "usbus", -1); 260 if (esc->base.sc_bus.bdev == NULL) { 261 device_printf(dev, "Could not add USB device\n"); 262 goto error; 263 } 264 device_set_ivars(esc->base.sc_bus.bdev, &esc->base.sc_bus); 265 strlcpy(esc->base.sc_vendor, "Samsung", sizeof(esc->base.sc_vendor)); 266 267 dwc3_init(esc); 268 269 err = xhci_halt_controller(&esc->base); 270 if (err == 0) { 271 device_printf(dev, "Starting controller\n"); 272 err = xhci_start_controller(&esc->base); 273 } 274 if (err == 0) { 275 device_printf(dev, "Controller started\n"); 276 err = device_probe_and_attach(esc->base.sc_bus.bdev); 277 } 278 if (err != 0) 279 goto error; 280 return (0); 281 282error: 283 exynos_xhci_detach(dev); 284 return (ENXIO); 285} 286 287static int 288exynos_xhci_detach(device_t dev) 289{ 290 struct exynos_xhci_softc *esc = device_get_softc(dev); 291 device_t bdev; 292 int err; 293 294 if (esc->base.sc_bus.bdev != NULL) { 295 bdev = esc->base.sc_bus.bdev; 296 device_detach(bdev); 297 device_delete_child(dev, bdev); 298 } 299 /* During module unload there are lots of children leftover */ 300 device_delete_children(dev); 301 302 xhci_halt_controller(&esc->base); 303 304 if (esc->res[2] && esc->base.sc_intr_hdl) { 305 err = bus_teardown_intr(dev, esc->res[2], 306 esc->base.sc_intr_hdl); 307 if (err) { 308 device_printf(dev, "Could not tear down IRQ," 309 " %d\n", err); 310 return (err); 311 } 312 } 313 314 bus_release_resources(dev, exynos_xhci_spec, esc->res); 315 316 xhci_uninit(&esc->base); 317 318 return (0); 319} 320