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 346524 2019-04-22 04:56:41Z ian $"); 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 61308274Smanu#include <arm/allwinner/aw_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 91297627Sjmcneillstruct aw_ehci_softc { 92297627Sjmcneill ehci_softc_t sc; 93297627Sjmcneill clk_t clk; 94297627Sjmcneill hwreset_t rst; 95300728Sjmcneill phy_t phy; 96297627Sjmcneill}; 97297627Sjmcneill 98296284Sjmcneillstruct aw_ehci_conf { 99296284Sjmcneill bool sdram_init; 100296284Sjmcneill}; 101296284Sjmcneill 102296284Sjmcneillstatic const struct aw_ehci_conf a10_ehci_conf = { 103296284Sjmcneill .sdram_init = true, 104296284Sjmcneill}; 105296284Sjmcneill 106296284Sjmcneillstatic const struct aw_ehci_conf a31_ehci_conf = { 107297627Sjmcneill .sdram_init = false, 108296284Sjmcneill}; 109296284Sjmcneill 110295464Sandrewstatic struct ofw_compat_data compat_data[] = { 111296284Sjmcneill { "allwinner,sun4i-a10-ehci", (uintptr_t)&a10_ehci_conf }, 112305436Smanu { "allwinner,sun5i-a13-ehci", (uintptr_t)&a10_ehci_conf }, 113296284Sjmcneill { "allwinner,sun6i-a31-ehci", (uintptr_t)&a31_ehci_conf }, 114296284Sjmcneill { "allwinner,sun7i-a20-ehci", (uintptr_t)&a10_ehci_conf }, 115299113Sjmcneill { "allwinner,sun8i-a83t-ehci", (uintptr_t)&a31_ehci_conf }, 116299688Smanu { "allwinner,sun8i-h3-ehci", (uintptr_t)&a31_ehci_conf }, 117296284Sjmcneill { NULL, (uintptr_t)NULL } 118295464Sandrew}; 119295464Sandrew 120246057Sganboldstatic int 121246057Sganbolda10_ehci_probe(device_t self) 122246057Sganbold{ 123261410Sian 124261410Sian if (!ofw_bus_status_okay(self)) 125261410Sian return (ENXIO); 126261410Sian 127295464Sandrew if (ofw_bus_search_compatible(self, compat_data)->ocd_data == 0) 128246057Sganbold return (ENXIO); 129246057Sganbold 130246057Sganbold device_set_desc(self, EHCI_HC_DEVSTR); 131246057Sganbold 132246057Sganbold return (BUS_PROBE_DEFAULT); 133246057Sganbold} 134246057Sganbold 135246057Sganboldstatic int 136246057Sganbolda10_ehci_attach(device_t self) 137246057Sganbold{ 138297627Sjmcneill struct aw_ehci_softc *aw_sc = device_get_softc(self); 139297627Sjmcneill ehci_softc_t *sc = &aw_sc->sc; 140296284Sjmcneill const struct aw_ehci_conf *conf; 141246057Sganbold bus_space_handle_t bsh; 142246057Sganbold int err; 143246057Sganbold int rid; 144246057Sganbold uint32_t reg_value = 0; 145246057Sganbold 146296284Sjmcneill conf = USB_CONF(self); 147296284Sjmcneill 148246057Sganbold /* initialise some bus fields */ 149246057Sganbold sc->sc_bus.parent = self; 150246057Sganbold sc->sc_bus.devices = sc->sc_devices; 151246057Sganbold sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 152276717Shselasky sc->sc_bus.dma_bits = 32; 153246057Sganbold 154246057Sganbold /* get all DMA memory */ 155246057Sganbold if (usb_bus_mem_alloc_all(&sc->sc_bus, 156246057Sganbold USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { 157246057Sganbold return (ENOMEM); 158246057Sganbold } 159246057Sganbold 160246057Sganbold sc->sc_bus.usbrev = USB_REV_2_0; 161246057Sganbold 162246057Sganbold rid = 0; 163246057Sganbold sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); 164246057Sganbold if (!sc->sc_io_res) { 165246057Sganbold device_printf(self, "Could not map memory\n"); 166246057Sganbold goto error; 167246057Sganbold } 168246057Sganbold 169246057Sganbold sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 170246057Sganbold sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 171246057Sganbold bsh = rman_get_bushandle(sc->sc_io_res); 172246057Sganbold 173246057Sganbold sc->sc_io_size = rman_get_size(sc->sc_io_res); 174246057Sganbold 175246057Sganbold if (bus_space_subregion(sc->sc_io_tag, bsh, 0x00, 176246057Sganbold sc->sc_io_size, &sc->sc_io_hdl) != 0) 177246057Sganbold panic("%s: unable to subregion USB host registers", 178246057Sganbold device_get_name(self)); 179246057Sganbold 180246057Sganbold rid = 0; 181246057Sganbold sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 182246057Sganbold RF_SHAREABLE | RF_ACTIVE); 183246057Sganbold if (sc->sc_irq_res == NULL) { 184246057Sganbold device_printf(self, "Could not allocate irq\n"); 185246057Sganbold goto error; 186246057Sganbold } 187246057Sganbold sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 188246057Sganbold if (!sc->sc_bus.bdev) { 189246057Sganbold device_printf(self, "Could not add USB device\n"); 190246057Sganbold goto error; 191246057Sganbold } 192246057Sganbold device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 193246057Sganbold device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 194246057Sganbold 195246057Sganbold sprintf(sc->sc_vendor, "Allwinner"); 196246057Sganbold 197246057Sganbold err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 198246057Sganbold NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 199246057Sganbold if (err) { 200246057Sganbold device_printf(self, "Could not setup irq, %d\n", err); 201246057Sganbold sc->sc_intr_hdl = NULL; 202246057Sganbold goto error; 203246057Sganbold } 204246057Sganbold 205246057Sganbold sc->sc_flags |= EHCI_SCFLG_DONTRESET; 206246057Sganbold 207297627Sjmcneill /* De-assert reset */ 208308324Smmel if (hwreset_get_by_ofw_idx(self, 0, 0, &aw_sc->rst) == 0) { 209297627Sjmcneill err = hwreset_deassert(aw_sc->rst); 210297627Sjmcneill if (err != 0) { 211297627Sjmcneill device_printf(self, "Could not de-assert reset\n"); 212297627Sjmcneill goto error; 213297627Sjmcneill } 214297627Sjmcneill } 215297627Sjmcneill 216246057Sganbold /* Enable clock for USB */ 217308324Smmel err = clk_get_by_ofw_index(self, 0, 0, &aw_sc->clk); 218297627Sjmcneill if (err != 0) { 219297627Sjmcneill device_printf(self, "Could not get clock\n"); 220296284Sjmcneill goto error; 221296284Sjmcneill } 222297627Sjmcneill err = clk_enable(aw_sc->clk); 223297627Sjmcneill if (err != 0) { 224297627Sjmcneill device_printf(self, "Could not enable clock\n"); 225297627Sjmcneill goto error; 226297627Sjmcneill } 227246057Sganbold 228300728Sjmcneill /* Enable USB PHY */ 229308324Smmel err = phy_get_by_ofw_name(self, 0, "usb", &aw_sc->phy); 230300728Sjmcneill if (err != 0) { 231300728Sjmcneill device_printf(self, "Could not get phy\n"); 232300728Sjmcneill goto error; 233300728Sjmcneill } 234332025Smmel err = phy_enable(aw_sc->phy); 235300728Sjmcneill if (err != 0) { 236300728Sjmcneill device_printf(self, "Could not enable phy\n"); 237300728Sjmcneill goto error; 238300728Sjmcneill } 239300728Sjmcneill 240246057Sganbold /* Enable passby */ 241246057Sganbold reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); 242246057Sganbold reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ 243246057Sganbold reg_value |= SW_AHB_INCR4; /* AHB burst type INCR4 enable */ 244246057Sganbold reg_value |= SW_AHB_INCRX_ALIGN; /* AHB INCRX align enable */ 245246057Sganbold reg_value |= SW_ULPI_BYPASS; /* ULPI bypass enable */ 246246057Sganbold A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); 247246057Sganbold 248246057Sganbold /* Configure port */ 249296284Sjmcneill if (conf->sdram_init) { 250296284Sjmcneill reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); 251296284Sjmcneill reg_value |= SW_SDRAM_BP_HPCR_ACCESS; 252296284Sjmcneill A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); 253296284Sjmcneill } 254246057Sganbold 255246057Sganbold err = ehci_init(sc); 256246057Sganbold if (!err) { 257246057Sganbold err = device_probe_and_attach(sc->sc_bus.bdev); 258246057Sganbold } 259246057Sganbold if (err) { 260246057Sganbold device_printf(self, "USB init failed err=%d\n", err); 261246057Sganbold goto error; 262246057Sganbold } 263246057Sganbold return (0); 264246057Sganbold 265246057Sganbolderror: 266309755Smanu if (aw_sc->clk != NULL) { 267309755Smanu clk_disable(aw_sc->clk); 268297627Sjmcneill clk_release(aw_sc->clk); 269309755Smanu } 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 int err; 281246057Sganbold uint32_t reg_value = 0; 282246057Sganbold 283296284Sjmcneill conf = USB_CONF(self); 284296284Sjmcneill 285246057Sganbold /* during module unload there are lots of children leftover */ 286246057Sganbold device_delete_children(self); 287246057Sganbold 288246057Sganbold if (sc->sc_irq_res && sc->sc_intr_hdl) { 289246057Sganbold /* 290246057Sganbold * only call ehci_detach() after ehci_init() 291246057Sganbold */ 292246057Sganbold ehci_detach(sc); 293246057Sganbold 294246057Sganbold err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 295246057Sganbold 296246057Sganbold if (err) 297246057Sganbold /* XXX or should we panic? */ 298246057Sganbold device_printf(self, "Could not tear down irq, %d\n", 299246057Sganbold err); 300246057Sganbold sc->sc_intr_hdl = NULL; 301246057Sganbold } 302246057Sganbold 303246057Sganbold if (sc->sc_irq_res) { 304246057Sganbold bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); 305246057Sganbold sc->sc_irq_res = NULL; 306246057Sganbold } 307246057Sganbold if (sc->sc_io_res) { 308246057Sganbold bus_release_resource(self, SYS_RES_MEMORY, 0, 309246057Sganbold sc->sc_io_res); 310246057Sganbold sc->sc_io_res = NULL; 311246057Sganbold } 312246057Sganbold usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 313246057Sganbold 314246057Sganbold /* Disable configure port */ 315296284Sjmcneill if (conf->sdram_init) { 316296284Sjmcneill reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); 317296284Sjmcneill reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; 318296284Sjmcneill A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); 319296284Sjmcneill } 320246057Sganbold 321246057Sganbold /* Disable passby */ 322246057Sganbold reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); 323246057Sganbold reg_value &= ~SW_AHB_INCR8; /* AHB INCR8 disable */ 324246057Sganbold reg_value &= ~SW_AHB_INCR4; /* AHB burst type INCR4 disable */ 325246057Sganbold reg_value &= ~SW_AHB_INCRX_ALIGN; /* AHB INCRX align disable */ 326246057Sganbold reg_value &= ~SW_ULPI_BYPASS; /* ULPI bypass disable */ 327246057Sganbold A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); 328246057Sganbold 329246057Sganbold /* Disable clock for USB */ 330309755Smanu if (aw_sc->clk != NULL) { 331309755Smanu clk_disable(aw_sc->clk); 332309755Smanu clk_release(aw_sc->clk); 333309755Smanu } 334246057Sganbold 335297627Sjmcneill /* Assert reset */ 336297627Sjmcneill if (aw_sc->rst != NULL) { 337297627Sjmcneill hwreset_assert(aw_sc->rst); 338297627Sjmcneill hwreset_release(aw_sc->rst); 339297627Sjmcneill } 340297627Sjmcneill 341246057Sganbold return (0); 342246057Sganbold} 343246057Sganbold 344246057Sganboldstatic device_method_t ehci_methods[] = { 345246057Sganbold /* Device interface */ 346246057Sganbold DEVMETHOD(device_probe, a10_ehci_probe), 347246057Sganbold DEVMETHOD(device_attach, a10_ehci_attach), 348246057Sganbold DEVMETHOD(device_detach, a10_ehci_detach), 349246057Sganbold DEVMETHOD(device_suspend, bus_generic_suspend), 350246057Sganbold DEVMETHOD(device_resume, bus_generic_resume), 351246057Sganbold DEVMETHOD(device_shutdown, bus_generic_shutdown), 352246057Sganbold 353246057Sganbold DEVMETHOD_END 354246057Sganbold}; 355246057Sganbold 356246057Sganboldstatic driver_t ehci_driver = { 357246057Sganbold .name = "ehci", 358246057Sganbold .methods = ehci_methods, 359304562Smanu .size = sizeof(struct aw_ehci_softc), 360246057Sganbold}; 361246057Sganbold 362246057Sganboldstatic devclass_t ehci_devclass; 363246057Sganbold 364346524SianDRIVER_MODULE(a10_ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); 365346524SianMODULE_DEPEND(a10_ehci, usb, 1, 1, 1); 366