exynos5_xhci.c revision 308401
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: stable/11/sys/arm/samsung/exynos/exynos5_xhci.c 308401 2016-11-07 08:36:06Z 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 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 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, 0)) { 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 int err; 292 293 /* During module unload there are lots of children leftover */ 294 device_delete_children(dev); 295 296 xhci_halt_controller(&esc->base); 297 298 if (esc->res[2] && esc->base.sc_intr_hdl) { 299 err = bus_teardown_intr(dev, esc->res[2], 300 esc->base.sc_intr_hdl); 301 if (err) { 302 device_printf(dev, "Could not tear down IRQ," 303 " %d\n", err); 304 return (err); 305 } 306 } 307 308 bus_release_resources(dev, exynos_xhci_spec, esc->res); 309 310 xhci_uninit(&esc->base); 311 312 return (0); 313} 314