1f9f848faSopenharmony_ci/*- 2f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause 3f9f848faSopenharmony_ci * 4f9f848faSopenharmony_ci * Copyright (c) 2010-2022 Hans Petter Selasky 5f9f848faSopenharmony_ci * 6f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without 7f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions 8f9f848faSopenharmony_ci * are met: 9f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright 10f9f848faSopenharmony_ci * notice, this list of conditions and the following disclaimer. 11f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 12f9f848faSopenharmony_ci * notice, this list of conditions and the following disclaimer in the 13f9f848faSopenharmony_ci * documentation and/or other materials provided with the distribution. 14f9f848faSopenharmony_ci * 15f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16f9f848faSopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17f9f848faSopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18f9f848faSopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19f9f848faSopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20f9f848faSopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21f9f848faSopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22f9f848faSopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23f9f848faSopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24f9f848faSopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25f9f848faSopenharmony_ci * SUCH DAMAGE. 26f9f848faSopenharmony_ci */ 27f9f848faSopenharmony_ci 28f9f848faSopenharmony_ci#include "implementation/global_implementation.h" 29f9f848faSopenharmony_ci#include "controller/xhci.h" 30f9f848faSopenharmony_ci#include "controller/xhcireg.h" 31f9f848faSopenharmony_ci 32f9f848faSopenharmony_ci#include <linux/kernel.h> 33f9f848faSopenharmony_ci 34f9f848faSopenharmony_ci#define PCI_XHCI_VENDORID_AMD 0x1022 35f9f848faSopenharmony_ci#define PCI_XHCI_VENDORID_INTEL 0x8086 36f9f848faSopenharmony_ci 37f9f848faSopenharmony_cistatic device_probe_t xhci_pci_probe; 38f9f848faSopenharmony_cistatic device_attach_t xhci_pci_attach; 39f9f848faSopenharmony_cistatic device_detach_t xhci_pci_detach; 40f9f848faSopenharmony_cistatic usb_take_controller_t xhci_pci_take_controller; 41f9f848faSopenharmony_ci 42f9f848faSopenharmony_cistatic device_method_t xhci_device_methods[] = { 43f9f848faSopenharmony_ci /* device interface */ 44f9f848faSopenharmony_ci DEVMETHOD(device_probe, xhci_pci_probe), 45f9f848faSopenharmony_ci DEVMETHOD(device_attach, xhci_pci_attach), 46f9f848faSopenharmony_ci DEVMETHOD(device_detach, xhci_pci_detach), 47f9f848faSopenharmony_ci DEVMETHOD(device_suspend, bus_generic_suspend), 48f9f848faSopenharmony_ci DEVMETHOD(device_resume, bus_generic_resume), 49f9f848faSopenharmony_ci DEVMETHOD(device_shutdown, bus_generic_shutdown), 50f9f848faSopenharmony_ci DEVMETHOD(usb_take_controller, xhci_pci_take_controller), 51f9f848faSopenharmony_ci 52f9f848faSopenharmony_ci DEVMETHOD_END 53f9f848faSopenharmony_ci}; 54f9f848faSopenharmony_ci 55f9f848faSopenharmony_cistatic driver_t xhci_driver = { 56f9f848faSopenharmony_ci .name = "xhci", 57f9f848faSopenharmony_ci .methods = xhci_device_methods, 58f9f848faSopenharmony_ci .size = sizeof(struct xhci_softc), 59f9f848faSopenharmony_ci}; 60f9f848faSopenharmony_ci 61f9f848faSopenharmony_cistatic devclass_t xhci_devclass; 62f9f848faSopenharmony_ci 63f9f848faSopenharmony_ciDRIVER_MODULE(xhci, nexus, xhci_driver, xhci_devclass, NULL, NULL); 64f9f848faSopenharmony_ci 65f9f848faSopenharmony_cistatic const char * 66f9f848faSopenharmony_cixhci_pci_match(device_t self) 67f9f848faSopenharmony_ci{ 68f9f848faSopenharmony_ci (void)self; 69f9f848faSopenharmony_ci return ("XHCI (generic) USB 3.0 controller"); 70f9f848faSopenharmony_ci} 71f9f848faSopenharmony_ci 72f9f848faSopenharmony_cistatic int 73f9f848faSopenharmony_cixhci_pci_probe(device_t self) 74f9f848faSopenharmony_ci{ 75f9f848faSopenharmony_ci const char *desc = xhci_pci_match(self); 76f9f848faSopenharmony_ci 77f9f848faSopenharmony_ci if (desc) { 78f9f848faSopenharmony_ci device_set_desc(self, desc); 79f9f848faSopenharmony_ci return (BUS_PROBE_DEFAULT); 80f9f848faSopenharmony_ci } else { 81f9f848faSopenharmony_ci return (ENXIO); 82f9f848faSopenharmony_ci } 83f9f848faSopenharmony_ci} 84f9f848faSopenharmony_ci 85f9f848faSopenharmony_cistatic int 86f9f848faSopenharmony_cixhci_pci_attach(device_t self) 87f9f848faSopenharmony_ci{ 88f9f848faSopenharmony_ci struct resource *res; 89f9f848faSopenharmony_ci struct xhci_softc *sc = device_get_softc(self);; 90f9f848faSopenharmony_ci int err = ENXIO; 91f9f848faSopenharmony_ci uint8_t usedma32 = 0; 92f9f848faSopenharmony_ci int unit = device_get_unit(self); 93f9f848faSopenharmony_ci 94f9f848faSopenharmony_ci usb_debug("+\n"); 95f9f848faSopenharmony_ci 96f9f848faSopenharmony_ci res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &unit, 0); 97f9f848faSopenharmony_ci if (res == NULL) { 98f9f848faSopenharmony_ci goto error; 99f9f848faSopenharmony_ci } 100f9f848faSopenharmony_ci 101f9f848faSopenharmony_ci sc->sc_io_res = ioremap(res->start, res->count); 102f9f848faSopenharmony_ci if (!sc->sc_io_res) { 103f9f848faSopenharmony_ci goto error; 104f9f848faSopenharmony_ci } 105f9f848faSopenharmony_ci sc->sc_io_tag = (void *)sc->sc_io_res; 106f9f848faSopenharmony_ci sc->sc_io_hdl = (uintptr_t)sc->sc_io_res; 107f9f848faSopenharmony_ci sc->sc_io_size = res->count; 108f9f848faSopenharmony_ci usb_debug("bus_setup_intr\n"); 109f9f848faSopenharmony_ci 110f9f848faSopenharmony_ci res = bus_alloc_resource_any(self, SYS_RES_IRQ, &unit, 0); 111f9f848faSopenharmony_ci if (res == NULL) { 112f9f848faSopenharmony_ci goto error; 113f9f848faSopenharmony_ci } 114f9f848faSopenharmony_ci 115f9f848faSopenharmony_ci sc->sc_irq_res = res; 116f9f848faSopenharmony_ci err = bus_setup_intr(res->start, 0, (driver_intr_t *)xhci_interrupt, sc); 117f9f848faSopenharmony_ci if (err) { 118f9f848faSopenharmony_ci goto error; 119f9f848faSopenharmony_ci } 120f9f848faSopenharmony_ci 121f9f848faSopenharmony_ci usb_debug("xhci_init\n"); 122f9f848faSopenharmony_ci HiUsb3StartHcd(); 123f9f848faSopenharmony_ci if (xhci_init(sc, self, usedma32)) { 124f9f848faSopenharmony_ci device_printf(self, "Could not initialize softc\n"); 125f9f848faSopenharmony_ci goto error; 126f9f848faSopenharmony_ci } 127f9f848faSopenharmony_ci usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0); 128f9f848faSopenharmony_ci 129f9f848faSopenharmony_ci usb_debug("add child to usbus\n"); 130f9f848faSopenharmony_ci sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 131f9f848faSopenharmony_ci if (sc->sc_bus.bdev == NULL) { 132f9f848faSopenharmony_ci device_printf(self, "Could not add USB device\n"); 133f9f848faSopenharmony_ci goto error; 134f9f848faSopenharmony_ci } 135f9f848faSopenharmony_ci device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 136f9f848faSopenharmony_ci 137f9f848faSopenharmony_ci usb_debug("xhci halt and start controller\n"); 138f9f848faSopenharmony_ci err = xhci_halt_controller(sc); 139f9f848faSopenharmony_ci 140f9f848faSopenharmony_ci if (err == 0) 141f9f848faSopenharmony_ci err = xhci_start_controller(sc); 142f9f848faSopenharmony_ci 143f9f848faSopenharmony_ci usb_debug("device_probe_and_attach bus_dev\n"); 144f9f848faSopenharmony_ci if (err == 0) 145f9f848faSopenharmony_ci err = device_probe_and_attach(sc->sc_bus.bdev); 146f9f848faSopenharmony_ci 147f9f848faSopenharmony_ci if (err) { 148f9f848faSopenharmony_ci device_printf(self, "XHCI halt/start/probe failed err=%d\n", err); 149f9f848faSopenharmony_ci goto error; 150f9f848faSopenharmony_ci } 151f9f848faSopenharmony_ci 152f9f848faSopenharmony_ci usb_debug("-\n"); 153f9f848faSopenharmony_ci 154f9f848faSopenharmony_ci return (0); 155f9f848faSopenharmony_cierror: 156f9f848faSopenharmony_ci device_printf(self, "XHCI halt/start/probe failed err=%d\n", err); 157f9f848faSopenharmony_ci if (sc) { 158f9f848faSopenharmony_ci iounmap((void *)sc->sc_io_res); 159f9f848faSopenharmony_ci } 160f9f848faSopenharmony_ci (void)xhci_pci_detach(self); 161f9f848faSopenharmony_ci return (err); 162f9f848faSopenharmony_ci} 163f9f848faSopenharmony_ci 164f9f848faSopenharmony_cistatic int 165f9f848faSopenharmony_cixhci_pci_detach(device_t self) 166f9f848faSopenharmony_ci{ 167f9f848faSopenharmony_ci 168f9f848faSopenharmony_ci struct xhci_softc *sc = device_get_softc(self); 169f9f848faSopenharmony_ci 170f9f848faSopenharmony_ci /* during module unload there are lots of children leftover */ 171f9f848faSopenharmony_ci (void)device_delete_children(self); 172f9f848faSopenharmony_ci 173f9f848faSopenharmony_ci usb_callout_drain(&sc->sc_callout); 174f9f848faSopenharmony_ci (void)xhci_halt_controller(sc); 175f9f848faSopenharmony_ci (void)xhci_reset_controller(sc); 176f9f848faSopenharmony_ci 177f9f848faSopenharmony_ci // release resouce 178f9f848faSopenharmony_ci if (sc->sc_irq_res) { 179f9f848faSopenharmony_ci (void)bus_teardown_intr(sc->sc_irq_res->start, sc); 180f9f848faSopenharmony_ci sc->sc_irq_res = NULL; 181f9f848faSopenharmony_ci } 182f9f848faSopenharmony_ci if (sc->sc_io_res) { 183f9f848faSopenharmony_ci iounmap((void *)sc->sc_io_res); 184f9f848faSopenharmony_ci sc->sc_io_res = NULL; 185f9f848faSopenharmony_ci sc->sc_io_tag = NULL; 186f9f848faSopenharmony_ci sc->sc_io_hdl = (uintptr_t)NULL; 187f9f848faSopenharmony_ci sc->sc_io_size = 0; 188f9f848faSopenharmony_ci } 189f9f848faSopenharmony_ci 190f9f848faSopenharmony_ci xhci_uninit(sc); 191f9f848faSopenharmony_ci 192f9f848faSopenharmony_ci return (0); 193f9f848faSopenharmony_ci} 194f9f848faSopenharmony_ci 195f9f848faSopenharmony_cistatic int 196f9f848faSopenharmony_cixhci_pci_take_controller(device_t self) 197f9f848faSopenharmony_ci{ 198f9f848faSopenharmony_ci struct xhci_softc *sc = device_get_softc(self); 199f9f848faSopenharmony_ci uint32_t cparams; 200f9f848faSopenharmony_ci uint32_t eecp; 201f9f848faSopenharmony_ci uint32_t eec; 202f9f848faSopenharmony_ci uint16_t to; 203f9f848faSopenharmony_ci uint8_t bios_sem; 204f9f848faSopenharmony_ci 205f9f848faSopenharmony_ci cparams = XREAD4(sc, capa, XHCI_HCSPARAMS0); 206f9f848faSopenharmony_ci 207f9f848faSopenharmony_ci eec = (uint32_t)(-1); 208f9f848faSopenharmony_ci 209f9f848faSopenharmony_ci /* Synchronise with the BIOS if it owns the controller. */ 210f9f848faSopenharmony_ci for (eecp = XHCI_HCS0_XECP(cparams) << 2; (eecp != 0) && XHCI_XECP_NEXT(eec); 211f9f848faSopenharmony_ci eecp += (XHCI_XECP_NEXT(eec) << 2)) { 212f9f848faSopenharmony_ci eec = XREAD4(sc, capa, eecp); 213f9f848faSopenharmony_ci 214f9f848faSopenharmony_ci if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY) 215f9f848faSopenharmony_ci continue; 216f9f848faSopenharmony_ci bios_sem = XREAD1(sc, capa, eecp + 217f9f848faSopenharmony_ci XHCI_XECP_BIOS_SEM); 218f9f848faSopenharmony_ci if (bios_sem == 0) 219f9f848faSopenharmony_ci continue; 220f9f848faSopenharmony_ci device_printf(sc->sc_bus.bdev, "waiting for BIOS " 221f9f848faSopenharmony_ci "to give up control\n"); 222f9f848faSopenharmony_ci XWRITE1(sc, capa, eecp + 223f9f848faSopenharmony_ci XHCI_XECP_OS_SEM, 1); 224f9f848faSopenharmony_ci to = 500; 225f9f848faSopenharmony_ci while (1) { 226f9f848faSopenharmony_ci bios_sem = XREAD1(sc, capa, eecp + 227f9f848faSopenharmony_ci XHCI_XECP_BIOS_SEM); 228f9f848faSopenharmony_ci if (bios_sem == 0) 229f9f848faSopenharmony_ci break; 230f9f848faSopenharmony_ci 231f9f848faSopenharmony_ci if (--to == 0) { 232f9f848faSopenharmony_ci device_printf(sc->sc_bus.bdev, 233f9f848faSopenharmony_ci "timed out waiting for BIOS\n"); 234f9f848faSopenharmony_ci break; 235f9f848faSopenharmony_ci } 236f9f848faSopenharmony_ci usb_pause_mtx(NULL, hz / 100); /* wait 10ms */ 237f9f848faSopenharmony_ci } 238f9f848faSopenharmony_ci } 239f9f848faSopenharmony_ci return (0); 240f9f848faSopenharmony_ci} 241f9f848faSopenharmony_ci 242f9f848faSopenharmony_ciint 243f9f848faSopenharmony_cihixhci_init(void) 244f9f848faSopenharmony_ci{ 245f9f848faSopenharmony_ci DPRINTF("hixhci_init"); 246f9f848faSopenharmony_ci return driver_module_handler(NULL, MOD_LOAD, &xhci_nexus_driver_mod); 247f9f848faSopenharmony_ci} 248f9f848faSopenharmony_ci 249f9f848faSopenharmony_civoid 250f9f848faSopenharmony_cihixhci_exit(void) 251f9f848faSopenharmony_ci{ 252f9f848faSopenharmony_ci DPRINTF("hixhci_exit"); 253f9f848faSopenharmony_ci (void)driver_module_handler(NULL, MOD_UNLOAD, &xhci_nexus_driver_mod); 254f9f848faSopenharmony_ci} 255