1269369Sbr/*- 2269369Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 3269369Sbr * All rights reserved. 4269369Sbr * 5269369Sbr * Redistribution and use in source and binary forms, with or without 6269369Sbr * modification, are permitted provided that the following conditions 7269369Sbr * are met: 8269369Sbr * 1. Redistributions of source code must retain the above copyright 9269369Sbr * notice, this list of conditions and the following disclaimer. 10269369Sbr * 2. Redistributions in binary form must reproduce the above copyright 11269369Sbr * notice, this list of conditions and the following disclaimer in the 12269369Sbr * documentation and/or other materials provided with the distribution. 13269369Sbr * 14269369Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15269369Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16269369Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17269369Sbr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18269369Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19269369Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20269369Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21269369Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22269369Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23269369Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24269369Sbr * SUCH DAMAGE. 25269369Sbr */ 26269369Sbr 27269369Sbr#include <sys/cdefs.h> 28269369Sbr__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_xhci.c 308401 2016-11-07 08:36:06Z hselasky $"); 29269369Sbr 30269369Sbr#include "opt_bus.h" 31269369Sbr 32269369Sbr#include <sys/param.h> 33269369Sbr#include <sys/systm.h> 34269369Sbr#include <sys/kernel.h> 35269369Sbr#include <sys/module.h> 36269369Sbr#include <sys/bus.h> 37269369Sbr#include <sys/condvar.h> 38269369Sbr#include <sys/rman.h> 39269369Sbr 40269369Sbr#include <dev/ofw/ofw_bus.h> 41269369Sbr#include <dev/ofw/ofw_bus_subr.h> 42269369Sbr 43269369Sbr#include <dev/usb/usb.h> 44269369Sbr#include <dev/usb/usbdi.h> 45269369Sbr#include <dev/usb/usb_busdma.h> 46269369Sbr#include <dev/usb/usb_process.h> 47269369Sbr#include <dev/usb/usb_controller.h> 48269369Sbr#include <dev/usb/usb_bus.h> 49269369Sbr#include <dev/usb/controller/xhci.h> 50269369Sbr#include <dev/usb/controller/xhcireg.h> 51269369Sbr 52269369Sbr#include <machine/bus.h> 53269369Sbr#include <machine/resource.h> 54269369Sbr 55269369Sbr#include <arm/samsung/exynos/exynos5_common.h> 56269369Sbr 57269369Sbr#include "opt_platform.h" 58269369Sbr 59269369Sbr#define GSNPSID 0x20 60269369Sbr#define GSNPSID_MASK 0xffff0000 61269369Sbr#define REVISION_MASK 0xffff 62269369Sbr#define GCTL 0x10 63269369Sbr#define GCTL_PWRDNSCALE(n) ((n) << 19) 64269369Sbr#define GCTL_U2RSTECN (1 << 16) 65269369Sbr#define GCTL_CLK_BUS (0) 66269369Sbr#define GCTL_CLK_PIPE (1) 67269369Sbr#define GCTL_CLK_PIPEHALF (2) 68269369Sbr#define GCTL_CLK_M (3) 69269369Sbr#define GCTL_CLK_S (6) 70269369Sbr#define GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) 71269369Sbr#define GCTL_PRTCAPDIR(n) ((n) << 12) 72269369Sbr#define GCTL_PRTCAP_HOST 1 73269369Sbr#define GCTL_PRTCAP_DEVICE 2 74269369Sbr#define GCTL_PRTCAP_OTG 3 75269369Sbr#define GCTL_CORESOFTRESET (1 << 11) 76269369Sbr#define GCTL_SCALEDOWN_MASK 3 77269369Sbr#define GCTL_SCALEDOWN_SHIFT 4 78269369Sbr#define GCTL_DISSCRAMBLE (1 << 3) 79269369Sbr#define GCTL_DSBLCLKGTNG (1 << 0) 80269369Sbr#define GHWPARAMS1 0x3c 81269369Sbr#define GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) 82269369Sbr#define GHWPARAMS1_EN_PWROPT_NO 0 83269369Sbr#define GHWPARAMS1_EN_PWROPT_CLK 1 84269369Sbr#define GUSB2PHYCFG(n) (0x100 + (n * 0x04)) 85269369Sbr#define GUSB2PHYCFG_PHYSOFTRST (1 << 31) 86269369Sbr#define GUSB2PHYCFG_SUSPHY (1 << 6) 87269369Sbr#define GUSB3PIPECTL(n) (0x1c0 + (n * 0x04)) 88269369Sbr#define GUSB3PIPECTL_PHYSOFTRST (1 << 31) 89269369Sbr#define GUSB3PIPECTL_SUSPHY (1 << 17) 90269369Sbr 91269369Sbr/* Forward declarations */ 92279542Shselaskystatic device_attach_t exynos_xhci_attach; 93279542Shselaskystatic device_detach_t exynos_xhci_detach; 94279542Shselaskystatic device_probe_t exynos_xhci_probe; 95269369Sbr 96269369Sbrstruct exynos_xhci_softc { 97269369Sbr device_t dev; 98269369Sbr struct xhci_softc base; 99269369Sbr struct resource *res[3]; 100269369Sbr bus_space_tag_t bst; 101269369Sbr bus_space_handle_t bsh; 102269369Sbr}; 103269369Sbr 104269369Sbrstatic struct resource_spec exynos_xhci_spec[] = { 105269369Sbr { SYS_RES_MEMORY, 0, RF_ACTIVE }, 106269369Sbr { SYS_RES_MEMORY, 1, RF_ACTIVE }, 107269369Sbr { SYS_RES_IRQ, 0, RF_ACTIVE }, 108269369Sbr { -1, 0 } 109269369Sbr}; 110269369Sbr 111269369Sbrstatic device_method_t xhci_methods[] = { 112269369Sbr /* Device interface */ 113269369Sbr DEVMETHOD(device_probe, exynos_xhci_probe), 114269369Sbr DEVMETHOD(device_attach, exynos_xhci_attach), 115269369Sbr DEVMETHOD(device_detach, exynos_xhci_detach), 116269369Sbr DEVMETHOD(device_suspend, bus_generic_suspend), 117269369Sbr DEVMETHOD(device_resume, bus_generic_resume), 118269369Sbr DEVMETHOD(device_shutdown, bus_generic_shutdown), 119269369Sbr 120269369Sbr DEVMETHOD_END 121269369Sbr}; 122269369Sbr 123269369Sbr/* kobj_class definition */ 124269369Sbrstatic driver_t xhci_driver = { 125269369Sbr "xhci", 126269369Sbr xhci_methods, 127269369Sbr sizeof(struct xhci_softc) 128269369Sbr}; 129269369Sbr 130269369Sbrstatic devclass_t xhci_devclass; 131269369Sbr 132269369SbrDRIVER_MODULE(xhci, simplebus, xhci_driver, xhci_devclass, 0, 0); 133269369SbrMODULE_DEPEND(xhci, usb, 1, 1, 1); 134269369Sbr 135269369Sbr/* 136269369Sbr * Public methods 137269369Sbr */ 138269369Sbrstatic int 139269369Sbrexynos_xhci_probe(device_t dev) 140269369Sbr{ 141269369Sbr 142269369Sbr if (!ofw_bus_status_okay(dev)) 143269369Sbr return (ENXIO); 144269369Sbr 145269369Sbr if (ofw_bus_is_compatible(dev, "samsung,exynos5250-dwusb3") == 0) 146269369Sbr return (ENXIO); 147269369Sbr 148269369Sbr device_set_desc(dev, "Exynos USB 3.0 controller"); 149269369Sbr return (BUS_PROBE_DEFAULT); 150269369Sbr} 151269369Sbr 152269369Sbrstatic int 153269369Sbrdwc3_init(struct exynos_xhci_softc *esc) 154269369Sbr{ 155269369Sbr int hwparams1; 156269369Sbr int rev; 157269369Sbr int reg; 158269369Sbr 159269369Sbr rev = READ4(esc, GSNPSID); 160269369Sbr if ((rev & GSNPSID_MASK) != 0x55330000) { 161269369Sbr printf("It is not DWC3 controller\n"); 162269369Sbr return (-1); 163269369Sbr } 164269369Sbr 165269369Sbr /* Reset controller */ 166269369Sbr WRITE4(esc, GCTL, GCTL_CORESOFTRESET); 167269369Sbr WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST); 168269369Sbr WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST); 169269369Sbr 170269369Sbr DELAY(100000); 171269369Sbr 172269369Sbr reg = READ4(esc, GUSB3PIPECTL(0)); 173269369Sbr reg &= ~(GUSB3PIPECTL_PHYSOFTRST); 174269369Sbr WRITE4(esc, GUSB3PIPECTL(0), reg); 175269369Sbr 176269369Sbr reg = READ4(esc, GUSB2PHYCFG(0)); 177269369Sbr reg &= ~(GUSB2PHYCFG_PHYSOFTRST); 178269369Sbr WRITE4(esc, GUSB2PHYCFG(0), reg); 179269369Sbr 180269369Sbr reg = READ4(esc, GCTL); 181269369Sbr reg &= ~GCTL_CORESOFTRESET; 182269369Sbr WRITE4(esc, GCTL, reg); 183269369Sbr 184269369Sbr hwparams1 = READ4(esc, GHWPARAMS1); 185269369Sbr 186269369Sbr reg = READ4(esc, GCTL); 187269369Sbr reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT); 188269369Sbr reg &= ~(GCTL_DISSCRAMBLE); 189269369Sbr 190269369Sbr if (GHWPARAMS1_EN_PWROPT(hwparams1) == \ 191269369Sbr GHWPARAMS1_EN_PWROPT_CLK) 192269369Sbr reg &= ~(GCTL_DSBLCLKGTNG); 193269369Sbr 194269369Sbr if ((rev & REVISION_MASK) < 0x190a) 195269369Sbr reg |= (GCTL_U2RSTECN); 196269369Sbr WRITE4(esc, GCTL, reg); 197269369Sbr 198269369Sbr /* Set host mode */ 199269369Sbr reg = READ4(esc, GCTL); 200269369Sbr reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG)); 201269369Sbr reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST); 202269369Sbr WRITE4(esc, GCTL, reg); 203269369Sbr 204269369Sbr return (0); 205269369Sbr} 206269369Sbr 207269369Sbrstatic int 208269369Sbrexynos_xhci_attach(device_t dev) 209269369Sbr{ 210279542Shselasky struct exynos_xhci_softc *esc = device_get_softc(dev); 211269369Sbr bus_space_handle_t bsh; 212269369Sbr int err; 213269369Sbr 214269369Sbr esc->dev = dev; 215269369Sbr 216269369Sbr if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) { 217269369Sbr device_printf(dev, "could not allocate resources\n"); 218269369Sbr return (ENXIO); 219269369Sbr } 220269369Sbr 221269369Sbr /* XHCI registers */ 222279542Shselasky esc->base.sc_io_tag = rman_get_bustag(esc->res[0]); 223269369Sbr bsh = rman_get_bushandle(esc->res[0]); 224279542Shselasky esc->base.sc_io_size = rman_get_size(esc->res[0]); 225269369Sbr 226269369Sbr /* DWC3 ctrl registers */ 227269369Sbr esc->bst = rman_get_bustag(esc->res[1]); 228269369Sbr esc->bsh = rman_get_bushandle(esc->res[1]); 229269369Sbr 230269369Sbr /* 231269369Sbr * Set handle to USB related registers subregion used by 232269369Sbr * generic XHCI driver. 233269369Sbr */ 234279542Shselasky err = bus_space_subregion(esc->base.sc_io_tag, bsh, 0x0, 235279542Shselasky esc->base.sc_io_size, &esc->base.sc_io_hdl); 236279542Shselasky if (err != 0) { 237279542Shselasky device_printf(dev, "Subregion failed\n"); 238279542Shselasky bus_release_resources(dev, exynos_xhci_spec, esc->res); 239269369Sbr return (ENXIO); 240279542Shselasky } 241269369Sbr 242279544Shselasky if (xhci_init(&esc->base, dev, 0)) { 243279542Shselasky device_printf(dev, "Could not initialize softc\n"); 244279542Shselasky bus_release_resources(dev, exynos_xhci_spec, esc->res); 245279542Shselasky return (ENXIO); 246279542Shselasky } 247279542Shselasky 248269369Sbr /* Setup interrupt handler */ 249269369Sbr err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, 250279542Shselasky NULL, (driver_intr_t *)xhci_interrupt, &esc->base, 251279542Shselasky &esc->base.sc_intr_hdl); 252269369Sbr if (err) { 253269369Sbr device_printf(dev, "Could not setup irq, %d\n", err); 254279542Shselasky esc->base.sc_intr_hdl = NULL; 255279542Shselasky goto error; 256269369Sbr } 257269369Sbr 258269369Sbr /* Add USB device */ 259279542Shselasky esc->base.sc_bus.bdev = device_add_child(dev, "usbus", -1); 260279542Shselasky if (esc->base.sc_bus.bdev == NULL) { 261269369Sbr device_printf(dev, "Could not add USB device\n"); 262279542Shselasky goto error; 263269369Sbr } 264279542Shselasky device_set_ivars(esc->base.sc_bus.bdev, &esc->base.sc_bus); 265279542Shselasky strlcpy(esc->base.sc_vendor, "Samsung", sizeof(esc->base.sc_vendor)); 266269369Sbr 267269369Sbr dwc3_init(esc); 268269369Sbr 269279542Shselasky err = xhci_halt_controller(&esc->base); 270269369Sbr if (err == 0) { 271269369Sbr device_printf(dev, "Starting controller\n"); 272279542Shselasky err = xhci_start_controller(&esc->base); 273269369Sbr } 274269369Sbr if (err == 0) { 275269369Sbr device_printf(dev, "Controller started\n"); 276279542Shselasky err = device_probe_and_attach(esc->base.sc_bus.bdev); 277269369Sbr } 278279542Shselasky if (err != 0) 279279542Shselasky goto error; 280279542Shselasky return (0); 281269369Sbr 282279542Shselaskyerror: 283279542Shselasky exynos_xhci_detach(dev); 284279542Shselasky return (ENXIO); 285269369Sbr} 286269369Sbr 287269369Sbrstatic int 288269369Sbrexynos_xhci_detach(device_t dev) 289269369Sbr{ 290279542Shselasky struct exynos_xhci_softc *esc = device_get_softc(dev); 291269369Sbr int err; 292269369Sbr 293279542Shselasky /* During module unload there are lots of children leftover */ 294279542Shselasky device_delete_children(dev); 295269369Sbr 296279542Shselasky xhci_halt_controller(&esc->base); 297279542Shselasky 298279542Shselasky if (esc->res[2] && esc->base.sc_intr_hdl) { 299269369Sbr err = bus_teardown_intr(dev, esc->res[2], 300279542Shselasky esc->base.sc_intr_hdl); 301269369Sbr if (err) { 302279542Shselasky device_printf(dev, "Could not tear down IRQ," 303269369Sbr " %d\n", err); 304269369Sbr return (err); 305269369Sbr } 306269369Sbr } 307269369Sbr 308269369Sbr bus_release_resources(dev, exynos_xhci_spec, esc->res); 309269369Sbr 310279542Shselasky xhci_uninit(&esc->base); 311279542Shselasky 312269369Sbr return (0); 313269369Sbr} 314