1246057Sganbold/*- 2266337Sian * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@freebsd.org> 3246057Sganbold * All rights reserved. 4246057Sganbold * 5246057Sganbold * Redistribution and use in source and binary forms, with or without 6246057Sganbold * modification, are permitted provided that the following conditions 7246057Sganbold * are met: 8246057Sganbold * 1. Redistributions of source code must retain the above copyright 9246057Sganbold * notice, this list of conditions and the following disclaimer. 10246057Sganbold * 2. Redistributions in binary form must reproduce the above copyright 11246057Sganbold * notice, this list of conditions and the following disclaimer in the 12246057Sganbold * documentation and/or other materials provided with the distribution. 13246057Sganbold * 14246057Sganbold * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15246057Sganbold * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16246057Sganbold * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17246057Sganbold * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18246057Sganbold * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19246057Sganbold * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20246057Sganbold * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21246057Sganbold * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22246057Sganbold * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23246057Sganbold * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24246057Sganbold * SUCH DAMAGE. 25246057Sganbold */ 26246057Sganbold 27246057Sganbold/* 28246057Sganbold * Allwinner A10 attachment driver for the USB Enhanced Host Controller. 29246057Sganbold */ 30246057Sganbold 31246057Sganbold#include <sys/cdefs.h> 32246057Sganbold__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/a10_ehci.c 308402 2016-11-07 09:19:04Z hselasky $"); 33246057Sganbold 34246057Sganbold#include "opt_bus.h" 35246057Sganbold 36246057Sganbold#include <sys/param.h> 37246057Sganbold#include <sys/systm.h> 38246057Sganbold#include <sys/bus.h> 39246057Sganbold#include <sys/rman.h> 40246057Sganbold#include <sys/condvar.h> 41246057Sganbold#include <sys/kernel.h> 42246057Sganbold#include <sys/module.h> 43246375Sganbold#include <sys/gpio.h> 44246057Sganbold 45246057Sganbold#include <machine/bus.h> 46246057Sganbold#include <dev/ofw/ofw_bus.h> 47246057Sganbold#include <dev/ofw/ofw_bus_subr.h> 48246057Sganbold 49246057Sganbold#include <dev/usb/usb.h> 50246057Sganbold#include <dev/usb/usbdi.h> 51246057Sganbold 52246057Sganbold#include <dev/usb/usb_core.h> 53246057Sganbold#include <dev/usb/usb_busdma.h> 54246057Sganbold#include <dev/usb/usb_process.h> 55246057Sganbold#include <dev/usb/usb_util.h> 56246057Sganbold 57246057Sganbold#include <dev/usb/usb_controller.h> 58246057Sganbold#include <dev/usb/usb_bus.h> 59246057Sganbold#include <dev/usb/controller/ehci.h> 60246057Sganbold#include <dev/usb/controller/ehcireg.h> 61246057Sganbold 62246375Sganbold#include "gpio_if.h" 63246375Sganbold 64246057Sganbold#include "a10_clk.h" 65246057Sganbold 66246057Sganbold#define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" 67246057Sganbold 68246057Sganbold#define SW_USB_PMU_IRQ_ENABLE 0x800 69246057Sganbold 70246057Sganbold#define SW_SDRAM_REG_HPCR_USB1 (0x250 + ((1 << 2) * 4)) 71246057Sganbold#define SW_SDRAM_REG_HPCR_USB2 (0x250 + ((1 << 2) * 5)) 72246057Sganbold#define SW_SDRAM_BP_HPCR_ACCESS (1 << 0) 73246057Sganbold 74246057Sganbold#define SW_ULPI_BYPASS (1 << 0) 75246057Sganbold#define SW_AHB_INCRX_ALIGN (1 << 8) 76246375Sganbold#define SW_AHB_INCR4 (1 << 9) 77246057Sganbold#define SW_AHB_INCR8 (1 << 10) 78246851Sgonzo#define GPIO_USB1_PWR 230 79246375Sganbold#define GPIO_USB2_PWR 227 80246057Sganbold 81246057Sganbold#define A10_READ_4(sc, reg) \ 82246057Sganbold bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) 83246057Sganbold 84246057Sganbold#define A10_WRITE_4(sc, reg, data) \ 85246057Sganbold bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) 86246057Sganbold 87246057Sganboldstatic device_attach_t a10_ehci_attach; 88246057Sganboldstatic device_detach_t a10_ehci_detach; 89246057Sganbold 90246057Sganboldbs_r_1_proto(reversed); 91246057Sganboldbs_w_1_proto(reversed); 92246057Sganbold 93246057Sganboldstatic int 94246057Sganbolda10_ehci_probe(device_t self) 95246057Sganbold{ 96266152Sian 97266152Sian if (!ofw_bus_status_okay(self)) 98266152Sian return (ENXIO); 99266152Sian 100246057Sganbold if (!ofw_bus_is_compatible(self, "allwinner,usb-ehci")) 101246057Sganbold return (ENXIO); 102246057Sganbold 103246057Sganbold device_set_desc(self, EHCI_HC_DEVSTR); 104246057Sganbold 105246057Sganbold return (BUS_PROBE_DEFAULT); 106246057Sganbold} 107246057Sganbold 108246057Sganboldstatic int 109246057Sganbolda10_ehci_attach(device_t self) 110246057Sganbold{ 111246057Sganbold ehci_softc_t *sc = device_get_softc(self); 112246057Sganbold bus_space_handle_t bsh; 113246375Sganbold device_t sc_gpio_dev; 114246057Sganbold int err; 115246057Sganbold int rid; 116246057Sganbold uint32_t reg_value = 0; 117246057Sganbold 118246057Sganbold /* initialise some bus fields */ 119246057Sganbold sc->sc_bus.parent = self; 120246057Sganbold sc->sc_bus.devices = sc->sc_devices; 121246057Sganbold sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 122278278Shselasky sc->sc_bus.dma_bits = 32; 123246057Sganbold 124246057Sganbold /* get all DMA memory */ 125246057Sganbold if (usb_bus_mem_alloc_all(&sc->sc_bus, 126246057Sganbold USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { 127246057Sganbold return (ENOMEM); 128246057Sganbold } 129246057Sganbold 130246057Sganbold sc->sc_bus.usbrev = USB_REV_2_0; 131246057Sganbold 132246057Sganbold rid = 0; 133246057Sganbold sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); 134246057Sganbold if (!sc->sc_io_res) { 135246057Sganbold device_printf(self, "Could not map memory\n"); 136246057Sganbold goto error; 137246057Sganbold } 138246057Sganbold 139246057Sganbold sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 140246057Sganbold sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 141246057Sganbold bsh = rman_get_bushandle(sc->sc_io_res); 142246057Sganbold 143246057Sganbold sc->sc_io_size = rman_get_size(sc->sc_io_res); 144246057Sganbold 145246057Sganbold if (bus_space_subregion(sc->sc_io_tag, bsh, 0x00, 146246057Sganbold sc->sc_io_size, &sc->sc_io_hdl) != 0) 147246057Sganbold panic("%s: unable to subregion USB host registers", 148246057Sganbold device_get_name(self)); 149246057Sganbold 150246057Sganbold rid = 0; 151246057Sganbold sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 152246057Sganbold RF_SHAREABLE | RF_ACTIVE); 153246057Sganbold if (sc->sc_irq_res == NULL) { 154246057Sganbold device_printf(self, "Could not allocate irq\n"); 155246057Sganbold goto error; 156246057Sganbold } 157246057Sganbold sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 158246057Sganbold if (!sc->sc_bus.bdev) { 159246057Sganbold device_printf(self, "Could not add USB device\n"); 160246057Sganbold goto error; 161246057Sganbold } 162246057Sganbold device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 163246057Sganbold device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 164246057Sganbold 165246057Sganbold sprintf(sc->sc_vendor, "Allwinner"); 166246057Sganbold 167246375Sganbold /* Get the GPIO device, we need this to give power to USB */ 168246375Sganbold sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 169246375Sganbold if (sc_gpio_dev == NULL) { 170246375Sganbold device_printf(self, "Error: failed to get the GPIO device\n"); 171246375Sganbold goto error; 172246375Sganbold } 173246375Sganbold 174246057Sganbold err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 175246057Sganbold NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 176246057Sganbold if (err) { 177246057Sganbold device_printf(self, "Could not setup irq, %d\n", err); 178246057Sganbold sc->sc_intr_hdl = NULL; 179246057Sganbold goto error; 180246057Sganbold } 181246057Sganbold 182246057Sganbold sc->sc_flags |= EHCI_SCFLG_DONTRESET; 183246057Sganbold 184246057Sganbold /* Enable clock for USB */ 185246057Sganbold a10_clk_usb_activate(); 186246057Sganbold 187246375Sganbold /* Give power to USB */ 188246375Sganbold GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_OUTPUT); 189246375Sganbold GPIO_PIN_SET(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_HIGH); 190246375Sganbold 191246851Sgonzo /* Give power to USB */ 192246851Sgonzo GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_OUTPUT); 193246851Sgonzo GPIO_PIN_SET(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_HIGH); 194246851Sgonzo 195246057Sganbold /* Enable passby */ 196246057Sganbold reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); 197246057Sganbold reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ 198246057Sganbold reg_value |= SW_AHB_INCR4; /* AHB burst type INCR4 enable */ 199246057Sganbold reg_value |= SW_AHB_INCRX_ALIGN; /* AHB INCRX align enable */ 200246057Sganbold reg_value |= SW_ULPI_BYPASS; /* ULPI bypass enable */ 201246057Sganbold A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); 202246057Sganbold 203246057Sganbold /* Configure port */ 204246057Sganbold reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); 205246057Sganbold reg_value |= SW_SDRAM_BP_HPCR_ACCESS; 206246057Sganbold A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); 207246057Sganbold 208246057Sganbold err = ehci_init(sc); 209246057Sganbold if (!err) { 210246057Sganbold err = device_probe_and_attach(sc->sc_bus.bdev); 211246057Sganbold } 212246057Sganbold if (err) { 213246057Sganbold device_printf(self, "USB init failed err=%d\n", err); 214246057Sganbold goto error; 215246057Sganbold } 216246057Sganbold return (0); 217246057Sganbold 218246057Sganbolderror: 219246057Sganbold a10_ehci_detach(self); 220246057Sganbold return (ENXIO); 221246057Sganbold} 222246057Sganbold 223246057Sganboldstatic int 224246057Sganbolda10_ehci_detach(device_t self) 225246057Sganbold{ 226246057Sganbold ehci_softc_t *sc = device_get_softc(self); 227246057Sganbold int err; 228246057Sganbold uint32_t reg_value = 0; 229246057Sganbold 230246057Sganbold /* during module unload there are lots of children leftover */ 231246057Sganbold device_delete_children(self); 232246057Sganbold 233246057Sganbold if (sc->sc_irq_res && sc->sc_intr_hdl) { 234246057Sganbold /* 235246057Sganbold * only call ehci_detach() after ehci_init() 236246057Sganbold */ 237246057Sganbold ehci_detach(sc); 238246057Sganbold 239246057Sganbold err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 240246057Sganbold 241246057Sganbold if (err) 242246057Sganbold /* XXX or should we panic? */ 243246057Sganbold device_printf(self, "Could not tear down irq, %d\n", 244246057Sganbold err); 245246057Sganbold sc->sc_intr_hdl = NULL; 246246057Sganbold } 247246057Sganbold 248246057Sganbold if (sc->sc_irq_res) { 249246057Sganbold bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); 250246057Sganbold sc->sc_irq_res = NULL; 251246057Sganbold } 252246057Sganbold if (sc->sc_io_res) { 253246057Sganbold bus_release_resource(self, SYS_RES_MEMORY, 0, 254246057Sganbold sc->sc_io_res); 255246057Sganbold sc->sc_io_res = NULL; 256246057Sganbold } 257246057Sganbold usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 258246057Sganbold 259246057Sganbold /* Disable configure port */ 260246057Sganbold reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); 261246057Sganbold reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; 262246057Sganbold A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); 263246057Sganbold 264246057Sganbold /* Disable passby */ 265246057Sganbold reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); 266246057Sganbold reg_value &= ~SW_AHB_INCR8; /* AHB INCR8 disable */ 267246057Sganbold reg_value &= ~SW_AHB_INCR4; /* AHB burst type INCR4 disable */ 268246057Sganbold reg_value &= ~SW_AHB_INCRX_ALIGN; /* AHB INCRX align disable */ 269246057Sganbold reg_value &= ~SW_ULPI_BYPASS; /* ULPI bypass disable */ 270246057Sganbold A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); 271246057Sganbold 272246057Sganbold /* Disable clock for USB */ 273246057Sganbold a10_clk_usb_deactivate(); 274246057Sganbold 275246057Sganbold return (0); 276246057Sganbold} 277246057Sganbold 278246057Sganboldstatic device_method_t ehci_methods[] = { 279246057Sganbold /* Device interface */ 280246057Sganbold DEVMETHOD(device_probe, a10_ehci_probe), 281246057Sganbold DEVMETHOD(device_attach, a10_ehci_attach), 282246057Sganbold DEVMETHOD(device_detach, a10_ehci_detach), 283246057Sganbold DEVMETHOD(device_suspend, bus_generic_suspend), 284246057Sganbold DEVMETHOD(device_resume, bus_generic_resume), 285246057Sganbold DEVMETHOD(device_shutdown, bus_generic_shutdown), 286246057Sganbold 287246057Sganbold DEVMETHOD_END 288246057Sganbold}; 289246057Sganbold 290246057Sganboldstatic driver_t ehci_driver = { 291246057Sganbold .name = "ehci", 292246057Sganbold .methods = ehci_methods, 293246057Sganbold .size = sizeof(ehci_softc_t), 294246057Sganbold}; 295246057Sganbold 296246057Sganboldstatic devclass_t ehci_devclass; 297246057Sganbold 298246057SganboldDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 299246057SganboldMODULE_DEPEND(ehci, usb, 1, 1, 1); 300