1275970Scy/*- 2275970Scy * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@freebsd.org> 3275970Scy * All rights reserved. 4275970Scy * 5275970Scy * Redistribution and use in source and binary forms, with or without 6275970Scy * modification, are permitted provided that the following conditions 7275970Scy * are met: 8275970Scy * 1. Redistributions of source code must retain the above copyright 9275970Scy * notice, this list of conditions and the following disclaimer. 10275970Scy * 2. Redistributions in binary form must reproduce the above copyright 11275970Scy * notice, this list of conditions and the following disclaimer in the 12275970Scy * documentation and/or other materials provided with the distribution. 13275970Scy * 14275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15275970Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16275970Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17275970Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18275970Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19275970Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20275970Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21275970Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22275970Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23275970Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24275970Scy * SUCH DAMAGE. 25275970Scy */ 26275970Scy 27275970Scy/* 28275970Scy * Allwinner A10 attachment driver for the USB Enhanced Host Controller. 29275970Scy */ 30275970Scy 31275970Scy#include <sys/cdefs.h> 32275970Scy__FBSDID("$FreeBSD$"); 33275970Scy 34275970Scy#include "opt_bus.h" 35275970Scy 36275970Scy#include <sys/param.h> 37275970Scy#include <sys/systm.h> 38275970Scy#include <sys/bus.h> 39275970Scy#include <sys/rman.h> 40275970Scy#include <sys/condvar.h> 41275970Scy#include <sys/kernel.h> 42275970Scy#include <sys/module.h> 43275970Scy#include <sys/gpio.h> 44275970Scy 45275970Scy#include <machine/bus.h> 46275970Scy#include <dev/ofw/ofw_bus.h> 47275970Scy#include <dev/ofw/ofw_bus_subr.h> 48275970Scy 49275970Scy#include <dev/usb/usb.h> 50275970Scy#include <dev/usb/usbdi.h> 51275970Scy 52275970Scy#include <dev/usb/usb_core.h> 53275970Scy#include <dev/usb/usb_busdma.h> 54275970Scy#include <dev/usb/usb_process.h> 55275970Scy#include <dev/usb/usb_util.h> 56275970Scy 57275970Scy#include <dev/usb/usb_controller.h> 58275970Scy#include <dev/usb/usb_bus.h> 59275970Scy#include <dev/usb/controller/ehci.h> 60275970Scy#include <dev/usb/controller/ehcireg.h> 61275970Scy 62275970Scy#include "gpio_if.h" 63275970Scy 64275970Scy#include "a10_clk.h" 65275970Scy 66275970Scy#define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" 67275970Scy 68275970Scy#define SW_USB_PMU_IRQ_ENABLE 0x800 69275970Scy 70275970Scy#define SW_SDRAM_REG_HPCR_USB1 (0x250 + ((1 << 2) * 4)) 71275970Scy#define SW_SDRAM_REG_HPCR_USB2 (0x250 + ((1 << 2) * 5)) 72275970Scy#define SW_SDRAM_BP_HPCR_ACCESS (1 << 0) 73275970Scy 74275970Scy#define SW_ULPI_BYPASS (1 << 0) 75275970Scy#define SW_AHB_INCRX_ALIGN (1 << 8) 76275970Scy#define SW_AHB_INCR4 (1 << 9) 77275970Scy#define SW_AHB_INCR8 (1 << 10) 78275970Scy#define GPIO_USB1_PWR 230 79275970Scy#define GPIO_USB2_PWR 227 80275970Scy 81275970Scy#define A10_READ_4(sc, reg) \ 82275970Scy bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) 83275970Scy 84275970Scy#define A10_WRITE_4(sc, reg, data) \ 85275970Scy bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) 86275970Scy 87275970Scystatic device_attach_t a10_ehci_attach; 88275970Scystatic device_detach_t a10_ehci_detach; 89275970Scy 90275970Scybs_r_1_proto(reversed); 91275970Scybs_w_1_proto(reversed); 92275970Scy 93275970Scystatic int 94275970Scya10_ehci_probe(device_t self) 95275970Scy{ 96275970Scy 97275970Scy if (!ofw_bus_status_okay(self)) 98275970Scy return (ENXIO); 99275970Scy 100275970Scy if (!ofw_bus_is_compatible(self, "allwinner,usb-ehci")) 101275970Scy return (ENXIO); 102275970Scy 103275970Scy device_set_desc(self, EHCI_HC_DEVSTR); 104275970Scy 105275970Scy return (BUS_PROBE_DEFAULT); 106275970Scy} 107275970Scy 108275970Scystatic int 109275970Scya10_ehci_attach(device_t self) 110275970Scy{ 111275970Scy ehci_softc_t *sc = device_get_softc(self); 112275970Scy bus_space_handle_t bsh; 113275970Scy device_t sc_gpio_dev; 114275970Scy int err; 115275970Scy int rid; 116275970Scy uint32_t reg_value = 0; 117275970Scy 118275970Scy /* initialise some bus fields */ 119275970Scy sc->sc_bus.parent = self; 120275970Scy sc->sc_bus.devices = sc->sc_devices; 121275970Scy sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 122275970Scy 123275970Scy /* get all DMA memory */ 124275970Scy if (usb_bus_mem_alloc_all(&sc->sc_bus, 125275970Scy USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { 126275970Scy return (ENOMEM); 127275970Scy } 128275970Scy 129275970Scy sc->sc_bus.usbrev = USB_REV_2_0; 130275970Scy 131275970Scy rid = 0; 132275970Scy sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); 133275970Scy if (!sc->sc_io_res) { 134275970Scy device_printf(self, "Could not map memory\n"); 135275970Scy goto error; 136275970Scy } 137275970Scy 138275970Scy sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 139275970Scy sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 140275970Scy bsh = rman_get_bushandle(sc->sc_io_res); 141275970Scy 142275970Scy sc->sc_io_size = rman_get_size(sc->sc_io_res); 143275970Scy 144275970Scy if (bus_space_subregion(sc->sc_io_tag, bsh, 0x00, 145275970Scy sc->sc_io_size, &sc->sc_io_hdl) != 0) 146275970Scy panic("%s: unable to subregion USB host registers", 147275970Scy device_get_name(self)); 148275970Scy 149275970Scy rid = 0; 150275970Scy sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 151275970Scy RF_SHAREABLE | RF_ACTIVE); 152275970Scy if (sc->sc_irq_res == NULL) { 153275970Scy device_printf(self, "Could not allocate irq\n"); 154275970Scy goto error; 155275970Scy } 156275970Scy sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 157275970Scy if (!sc->sc_bus.bdev) { 158275970Scy device_printf(self, "Could not add USB device\n"); 159275970Scy goto error; 160275970Scy } 161275970Scy device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 162275970Scy device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 163275970Scy 164275970Scy sprintf(sc->sc_vendor, "Allwinner"); 165275970Scy 166275970Scy /* Get the GPIO device, we need this to give power to USB */ 167275970Scy sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); 168275970Scy if (sc_gpio_dev == NULL) { 169275970Scy device_printf(self, "Error: failed to get the GPIO device\n"); 170275970Scy goto error; 171275970Scy } 172275970Scy 173275970Scy err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 174275970Scy NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 175275970Scy if (err) { 176275970Scy device_printf(self, "Could not setup irq, %d\n", err); 177275970Scy sc->sc_intr_hdl = NULL; 178275970Scy goto error; 179275970Scy } 180275970Scy 181275970Scy sc->sc_flags |= EHCI_SCFLG_DONTRESET; 182275970Scy 183275970Scy /* Enable clock for USB */ 184275970Scy a10_clk_usb_activate(); 185275970Scy 186275970Scy /* Give power to USB */ 187275970Scy GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_OUTPUT); 188275970Scy GPIO_PIN_SET(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_HIGH); 189275970Scy 190275970Scy /* Give power to USB */ 191275970Scy GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_OUTPUT); 192275970Scy GPIO_PIN_SET(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_HIGH); 193275970Scy 194275970Scy /* Enable passby */ 195275970Scy reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); 196275970Scy reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ 197275970Scy reg_value |= SW_AHB_INCR4; /* AHB burst type INCR4 enable */ 198275970Scy reg_value |= SW_AHB_INCRX_ALIGN; /* AHB INCRX align enable */ 199275970Scy reg_value |= SW_ULPI_BYPASS; /* ULPI bypass enable */ 200275970Scy A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); 201275970Scy 202275970Scy /* Configure port */ 203275970Scy reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); 204275970Scy reg_value |= SW_SDRAM_BP_HPCR_ACCESS; 205275970Scy A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); 206275970Scy 207275970Scy err = ehci_init(sc); 208 if (!err) { 209 err = device_probe_and_attach(sc->sc_bus.bdev); 210 } 211 if (err) { 212 device_printf(self, "USB init failed err=%d\n", err); 213 goto error; 214 } 215 return (0); 216 217error: 218 a10_ehci_detach(self); 219 return (ENXIO); 220} 221 222static int 223a10_ehci_detach(device_t self) 224{ 225 ehci_softc_t *sc = device_get_softc(self); 226 device_t bdev; 227 int err; 228 uint32_t reg_value = 0; 229 230 if (sc->sc_bus.bdev) { 231 bdev = sc->sc_bus.bdev; 232 device_detach(bdev); 233 device_delete_child(self, bdev); 234 } 235 /* during module unload there are lots of children leftover */ 236 device_delete_children(self); 237 238 if (sc->sc_irq_res && sc->sc_intr_hdl) { 239 /* 240 * only call ehci_detach() after ehci_init() 241 */ 242 ehci_detach(sc); 243 244 err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 245 246 if (err) 247 /* XXX or should we panic? */ 248 device_printf(self, "Could not tear down irq, %d\n", 249 err); 250 sc->sc_intr_hdl = NULL; 251 } 252 253 if (sc->sc_irq_res) { 254 bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); 255 sc->sc_irq_res = NULL; 256 } 257 if (sc->sc_io_res) { 258 bus_release_resource(self, SYS_RES_MEMORY, 0, 259 sc->sc_io_res); 260 sc->sc_io_res = NULL; 261 } 262 usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 263 264 /* Disable configure port */ 265 reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); 266 reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; 267 A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); 268 269 /* Disable passby */ 270 reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); 271 reg_value &= ~SW_AHB_INCR8; /* AHB INCR8 disable */ 272 reg_value &= ~SW_AHB_INCR4; /* AHB burst type INCR4 disable */ 273 reg_value &= ~SW_AHB_INCRX_ALIGN; /* AHB INCRX align disable */ 274 reg_value &= ~SW_ULPI_BYPASS; /* ULPI bypass disable */ 275 A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); 276 277 /* Disable clock for USB */ 278 a10_clk_usb_deactivate(); 279 280 return (0); 281} 282 283static device_method_t ehci_methods[] = { 284 /* Device interface */ 285 DEVMETHOD(device_probe, a10_ehci_probe), 286 DEVMETHOD(device_attach, a10_ehci_attach), 287 DEVMETHOD(device_detach, a10_ehci_detach), 288 DEVMETHOD(device_suspend, bus_generic_suspend), 289 DEVMETHOD(device_resume, bus_generic_resume), 290 DEVMETHOD(device_shutdown, bus_generic_shutdown), 291 292 DEVMETHOD_END 293}; 294 295static driver_t ehci_driver = { 296 .name = "ehci", 297 .methods = ehci_methods, 298 .size = sizeof(ehci_softc_t), 299}; 300 301static devclass_t ehci_devclass; 302 303DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 304MODULE_DEPEND(ehci, usb, 1, 1, 1); 305