a10_ehci.c revision 304562
1246057Sganbold/*- 2263711Sganbold * 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/11/sys/arm/allwinner/a10_ehci.c 304562 2016-08-21 15:45:12Z manu $"); 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> 43246057Sganbold 44246057Sganbold#include <machine/bus.h> 45295464Sandrew#include <dev/ofw/ofw_bus.h> 46246057Sganbold#include <dev/ofw/ofw_bus_subr.h> 47246057Sganbold 48295464Sandrew#include <dev/usb/usb.h> 49246057Sganbold#include <dev/usb/usbdi.h> 50246057Sganbold 51246057Sganbold#include <dev/usb/usb_core.h> 52246057Sganbold#include <dev/usb/usb_busdma.h> 53246057Sganbold#include <dev/usb/usb_process.h> 54246057Sganbold#include <dev/usb/usb_util.h> 55246057Sganbold 56246057Sganbold#include <dev/usb/usb_controller.h> 57246057Sganbold#include <dev/usb/usb_bus.h> 58246057Sganbold#include <dev/usb/controller/ehci.h> 59246057Sganbold#include <dev/usb/controller/ehcireg.h> 60246057Sganbold 61296284Sjmcneill#include <arm/allwinner/allwinner_machdep.h> 62297627Sjmcneill#include <dev/extres/clk/clk.h> 63297627Sjmcneill#include <dev/extres/hwreset/hwreset.h> 64300728Sjmcneill#include <dev/extres/phy/phy.h> 65246375Sganbold 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) 78246057Sganbold 79296284Sjmcneill#define USB_CONF(d) \ 80296284Sjmcneill (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data 81296284Sjmcneill 82246057Sganbold#define A10_READ_4(sc, reg) \ 83246057Sganbold bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) 84246057Sganbold 85246057Sganbold#define A10_WRITE_4(sc, reg, data) \ 86246057Sganbold bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data) 87246057Sganbold 88246057Sganboldstatic device_attach_t a10_ehci_attach; 89246057Sganboldstatic device_detach_t a10_ehci_detach; 90246057Sganbold 91246057Sganboldbs_r_1_proto(reversed); 92246057Sganboldbs_w_1_proto(reversed); 93246057Sganbold 94297627Sjmcneillstruct aw_ehci_softc { 95297627Sjmcneill ehci_softc_t sc; 96297627Sjmcneill clk_t clk; 97297627Sjmcneill hwreset_t rst; 98300728Sjmcneill phy_t phy; 99297627Sjmcneill}; 100297627Sjmcneill 101296284Sjmcneillstruct aw_ehci_conf { 102296284Sjmcneill bool sdram_init; 103296284Sjmcneill}; 104296284Sjmcneill 105296284Sjmcneillstatic const struct aw_ehci_conf a10_ehci_conf = { 106296284Sjmcneill .sdram_init = true, 107296284Sjmcneill}; 108296284Sjmcneill 109296284Sjmcneillstatic const struct aw_ehci_conf a31_ehci_conf = { 110297627Sjmcneill .sdram_init = false, 111296284Sjmcneill}; 112296284Sjmcneill 113295464Sandrewstatic struct ofw_compat_data compat_data[] = { 114296284Sjmcneill { "allwinner,sun4i-a10-ehci", (uintptr_t)&a10_ehci_conf }, 115296284Sjmcneill { "allwinner,sun6i-a31-ehci", (uintptr_t)&a31_ehci_conf }, 116296284Sjmcneill { "allwinner,sun7i-a20-ehci", (uintptr_t)&a10_ehci_conf }, 117299113Sjmcneill { "allwinner,sun8i-a83t-ehci", (uintptr_t)&a31_ehci_conf }, 118299688Smanu { "allwinner,sun8i-h3-ehci", (uintptr_t)&a31_ehci_conf }, 119296284Sjmcneill { NULL, (uintptr_t)NULL } 120295464Sandrew}; 121295464Sandrew 122246057Sganboldstatic int 123246057Sganbolda10_ehci_probe(device_t self) 124246057Sganbold{ 125261410Sian 126261410Sian if (!ofw_bus_status_okay(self)) 127261410Sian return (ENXIO); 128261410Sian 129295464Sandrew if (ofw_bus_search_compatible(self, compat_data)->ocd_data == 0) 130246057Sganbold return (ENXIO); 131246057Sganbold 132246057Sganbold device_set_desc(self, EHCI_HC_DEVSTR); 133246057Sganbold 134246057Sganbold return (BUS_PROBE_DEFAULT); 135246057Sganbold} 136246057Sganbold 137246057Sganboldstatic int 138246057Sganbolda10_ehci_attach(device_t self) 139246057Sganbold{ 140297627Sjmcneill struct aw_ehci_softc *aw_sc = device_get_softc(self); 141297627Sjmcneill ehci_softc_t *sc = &aw_sc->sc; 142296284Sjmcneill const struct aw_ehci_conf *conf; 143246057Sganbold bus_space_handle_t bsh; 144246057Sganbold int err; 145246057Sganbold int rid; 146246057Sganbold uint32_t reg_value = 0; 147246057Sganbold 148296284Sjmcneill conf = USB_CONF(self); 149296284Sjmcneill 150246057Sganbold /* initialise some bus fields */ 151246057Sganbold sc->sc_bus.parent = self; 152246057Sganbold sc->sc_bus.devices = sc->sc_devices; 153246057Sganbold sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 154276717Shselasky sc->sc_bus.dma_bits = 32; 155246057Sganbold 156246057Sganbold /* get all DMA memory */ 157246057Sganbold if (usb_bus_mem_alloc_all(&sc->sc_bus, 158246057Sganbold USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { 159246057Sganbold return (ENOMEM); 160246057Sganbold } 161246057Sganbold 162246057Sganbold sc->sc_bus.usbrev = USB_REV_2_0; 163246057Sganbold 164246057Sganbold rid = 0; 165246057Sganbold sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); 166246057Sganbold if (!sc->sc_io_res) { 167246057Sganbold device_printf(self, "Could not map memory\n"); 168246057Sganbold goto error; 169246057Sganbold } 170246057Sganbold 171246057Sganbold sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 172246057Sganbold sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 173246057Sganbold bsh = rman_get_bushandle(sc->sc_io_res); 174246057Sganbold 175246057Sganbold sc->sc_io_size = rman_get_size(sc->sc_io_res); 176246057Sganbold 177246057Sganbold if (bus_space_subregion(sc->sc_io_tag, bsh, 0x00, 178246057Sganbold sc->sc_io_size, &sc->sc_io_hdl) != 0) 179246057Sganbold panic("%s: unable to subregion USB host registers", 180246057Sganbold device_get_name(self)); 181246057Sganbold 182246057Sganbold rid = 0; 183246057Sganbold sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 184246057Sganbold RF_SHAREABLE | RF_ACTIVE); 185246057Sganbold if (sc->sc_irq_res == NULL) { 186246057Sganbold device_printf(self, "Could not allocate irq\n"); 187246057Sganbold goto error; 188246057Sganbold } 189246057Sganbold sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 190246057Sganbold if (!sc->sc_bus.bdev) { 191246057Sganbold device_printf(self, "Could not add USB device\n"); 192246057Sganbold goto error; 193246057Sganbold } 194246057Sganbold device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 195246057Sganbold device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 196246057Sganbold 197246057Sganbold sprintf(sc->sc_vendor, "Allwinner"); 198246057Sganbold 199246057Sganbold err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 200246057Sganbold NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 201246057Sganbold if (err) { 202246057Sganbold device_printf(self, "Could not setup irq, %d\n", err); 203246057Sganbold sc->sc_intr_hdl = NULL; 204246057Sganbold goto error; 205246057Sganbold } 206246057Sganbold 207246057Sganbold sc->sc_flags |= EHCI_SCFLG_DONTRESET; 208246057Sganbold 209297627Sjmcneill /* De-assert reset */ 210297627Sjmcneill if (hwreset_get_by_ofw_idx(self, 0, &aw_sc->rst) == 0) { 211297627Sjmcneill err = hwreset_deassert(aw_sc->rst); 212297627Sjmcneill if (err != 0) { 213297627Sjmcneill device_printf(self, "Could not de-assert reset\n"); 214297627Sjmcneill goto error; 215297627Sjmcneill } 216297627Sjmcneill } 217297627Sjmcneill 218246057Sganbold /* Enable clock for USB */ 219297627Sjmcneill err = clk_get_by_ofw_index(self, 0, &aw_sc->clk); 220297627Sjmcneill if (err != 0) { 221297627Sjmcneill device_printf(self, "Could not get clock\n"); 222296284Sjmcneill goto error; 223296284Sjmcneill } 224297627Sjmcneill err = clk_enable(aw_sc->clk); 225297627Sjmcneill if (err != 0) { 226297627Sjmcneill device_printf(self, "Could not enable clock\n"); 227297627Sjmcneill goto error; 228297627Sjmcneill } 229246057Sganbold 230300728Sjmcneill /* Enable USB PHY */ 231300728Sjmcneill err = phy_get_by_ofw_name(self, "usb", &aw_sc->phy); 232300728Sjmcneill if (err != 0) { 233300728Sjmcneill device_printf(self, "Could not get phy\n"); 234300728Sjmcneill goto error; 235300728Sjmcneill } 236300728Sjmcneill err = phy_enable(self, aw_sc->phy); 237300728Sjmcneill if (err != 0) { 238300728Sjmcneill device_printf(self, "Could not enable phy\n"); 239300728Sjmcneill goto error; 240300728Sjmcneill } 241300728Sjmcneill 242246057Sganbold /* Enable passby */ 243246057Sganbold reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); 244246057Sganbold reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ 245246057Sganbold reg_value |= SW_AHB_INCR4; /* AHB burst type INCR4 enable */ 246246057Sganbold reg_value |= SW_AHB_INCRX_ALIGN; /* AHB INCRX align enable */ 247246057Sganbold reg_value |= SW_ULPI_BYPASS; /* ULPI bypass enable */ 248246057Sganbold A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); 249246057Sganbold 250246057Sganbold /* Configure port */ 251296284Sjmcneill if (conf->sdram_init) { 252296284Sjmcneill reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); 253296284Sjmcneill reg_value |= SW_SDRAM_BP_HPCR_ACCESS; 254296284Sjmcneill A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); 255296284Sjmcneill } 256246057Sganbold 257246057Sganbold err = ehci_init(sc); 258246057Sganbold if (!err) { 259246057Sganbold err = device_probe_and_attach(sc->sc_bus.bdev); 260246057Sganbold } 261246057Sganbold if (err) { 262246057Sganbold device_printf(self, "USB init failed err=%d\n", err); 263246057Sganbold goto error; 264246057Sganbold } 265246057Sganbold return (0); 266246057Sganbold 267246057Sganbolderror: 268297627Sjmcneill if (aw_sc->clk) 269297627Sjmcneill clk_release(aw_sc->clk); 270246057Sganbold a10_ehci_detach(self); 271246057Sganbold return (ENXIO); 272246057Sganbold} 273246057Sganbold 274246057Sganboldstatic int 275246057Sganbolda10_ehci_detach(device_t self) 276246057Sganbold{ 277297627Sjmcneill struct aw_ehci_softc *aw_sc = device_get_softc(self); 278297627Sjmcneill ehci_softc_t *sc = &aw_sc->sc; 279296284Sjmcneill const struct aw_ehci_conf *conf; 280246057Sganbold device_t bdev; 281246057Sganbold int err; 282246057Sganbold uint32_t reg_value = 0; 283246057Sganbold 284296284Sjmcneill conf = USB_CONF(self); 285296284Sjmcneill 286246057Sganbold if (sc->sc_bus.bdev) { 287246057Sganbold bdev = sc->sc_bus.bdev; 288246057Sganbold device_detach(bdev); 289246057Sganbold device_delete_child(self, bdev); 290246057Sganbold } 291246057Sganbold /* during module unload there are lots of children leftover */ 292246057Sganbold device_delete_children(self); 293246057Sganbold 294246057Sganbold if (sc->sc_irq_res && sc->sc_intr_hdl) { 295246057Sganbold /* 296246057Sganbold * only call ehci_detach() after ehci_init() 297246057Sganbold */ 298246057Sganbold ehci_detach(sc); 299246057Sganbold 300246057Sganbold err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 301246057Sganbold 302246057Sganbold if (err) 303246057Sganbold /* XXX or should we panic? */ 304246057Sganbold device_printf(self, "Could not tear down irq, %d\n", 305246057Sganbold err); 306246057Sganbold sc->sc_intr_hdl = NULL; 307246057Sganbold } 308246057Sganbold 309246057Sganbold if (sc->sc_irq_res) { 310246057Sganbold bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); 311246057Sganbold sc->sc_irq_res = NULL; 312246057Sganbold } 313246057Sganbold if (sc->sc_io_res) { 314246057Sganbold bus_release_resource(self, SYS_RES_MEMORY, 0, 315246057Sganbold sc->sc_io_res); 316246057Sganbold sc->sc_io_res = NULL; 317246057Sganbold } 318246057Sganbold usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 319246057Sganbold 320246057Sganbold /* Disable configure port */ 321296284Sjmcneill if (conf->sdram_init) { 322296284Sjmcneill reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); 323296284Sjmcneill reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; 324296284Sjmcneill A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); 325296284Sjmcneill } 326246057Sganbold 327246057Sganbold /* Disable passby */ 328246057Sganbold reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); 329246057Sganbold reg_value &= ~SW_AHB_INCR8; /* AHB INCR8 disable */ 330246057Sganbold reg_value &= ~SW_AHB_INCR4; /* AHB burst type INCR4 disable */ 331246057Sganbold reg_value &= ~SW_AHB_INCRX_ALIGN; /* AHB INCRX align disable */ 332246057Sganbold reg_value &= ~SW_ULPI_BYPASS; /* ULPI bypass disable */ 333246057Sganbold A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); 334246057Sganbold 335246057Sganbold /* Disable clock for USB */ 336297627Sjmcneill clk_disable(aw_sc->clk); 337297627Sjmcneill clk_release(aw_sc->clk); 338246057Sganbold 339297627Sjmcneill /* Assert reset */ 340297627Sjmcneill if (aw_sc->rst != NULL) { 341297627Sjmcneill hwreset_assert(aw_sc->rst); 342297627Sjmcneill hwreset_release(aw_sc->rst); 343297627Sjmcneill } 344297627Sjmcneill 345246057Sganbold return (0); 346246057Sganbold} 347246057Sganbold 348246057Sganboldstatic device_method_t ehci_methods[] = { 349246057Sganbold /* Device interface */ 350246057Sganbold DEVMETHOD(device_probe, a10_ehci_probe), 351246057Sganbold DEVMETHOD(device_attach, a10_ehci_attach), 352246057Sganbold DEVMETHOD(device_detach, a10_ehci_detach), 353246057Sganbold DEVMETHOD(device_suspend, bus_generic_suspend), 354246057Sganbold DEVMETHOD(device_resume, bus_generic_resume), 355246057Sganbold DEVMETHOD(device_shutdown, bus_generic_shutdown), 356246057Sganbold 357246057Sganbold DEVMETHOD_END 358246057Sganbold}; 359246057Sganbold 360246057Sganboldstatic driver_t ehci_driver = { 361246057Sganbold .name = "ehci", 362246057Sganbold .methods = ehci_methods, 363304562Smanu .size = sizeof(struct aw_ehci_softc), 364246057Sganbold}; 365246057Sganbold 366246057Sganboldstatic devclass_t ehci_devclass; 367246057Sganbold 368246057SganboldDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 369246057SganboldMODULE_DEPEND(ehci, usb, 1, 1, 1); 370