diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile --- a/stand/efi/loader/Makefile +++ b/stand/efi/loader/Makefile @@ -1,6 +1,7 @@ LOADER_NET_SUPPORT?= yes LOADER_MSDOS_SUPPORT?= yes LOADER_UFS_SUPPORT?= yes +LOADER_USBDBC_SUPPORT?= no LOADER_CD9660_SUPPORT?= no LOADER_EXT2FS_SUPPORT?= no @@ -30,6 +31,13 @@ gfx_fb.c \ 8x16.c +.if ${LOADER_USBDBC_SUPPORT} == "yes" +.PATH: ${SRCTOP}/sys/dev/usb/controller +SRCS+= xhci_dbc.c \ + xhci_dbc_cons.c \ + xhci_dbc_dma.c +.endif + SRCS+= acpi_detect.c .PATH: ${EFISRC}/acpica CFLAGS+= -I${EFISRC}/acpica/include diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c --- a/stand/efi/loader/bootinfo.c +++ b/stand/efi/loader/bootinfo.c @@ -64,6 +64,15 @@ #include "geliboot.h" #endif +#if defined(LOADER_USBDBC_SUPPORT) +#include +#include +#include "xhci_dbc_pci.h" /* device_t */ +#include +#include +#include "xhci_dbc_cons.h" /* udb_sc */ +#endif /* usb dbc */ + static int bi_getboothowto(char *kargs) { @@ -449,6 +458,16 @@ file_addmetadata(kfp, MODINFOMD_EFI_ARCH, sizeof(MACHINE_ARCH), MACHINE_ARCH); #endif +#if defined(LOADER_USBDBC_SUPPORT) + /* + * Pass the xhci debug struct through to the kernel if we have managed + * to configure it. + */ + if (udb_sc0 != NULL) { + file_addmetadata(kfp, MODINFOMD_XHCI_DEBUG, + sizeof(struct xhci_debug_softc *), &udb_sc0); + } +#endif #endif #ifdef LOADER_GELI_SUPPORT geli_export_key_metadata(kfp); diff --git a/stand/efi/loader/conf.c b/stand/efi/loader/conf.c --- a/stand/efi/loader/conf.c +++ b/stand/efi/loader/conf.c @@ -78,6 +78,9 @@ extern struct console efi_console; extern struct console eficom; +#if defined(LOADER_USBDBC_SUPPORT) +extern struct console udb_console; +#endif #if defined(__aarch64__) && __FreeBSD_version < 1500000 /* Hack for backward compatibility -- but only for a while */ extern struct console comconsole; @@ -91,6 +94,9 @@ struct console *consoles[] = { &efi_console, &eficom, +#if defined(LOADER_USBDBC_SUPPORT) + &udb_console, +#endif #if defined(__aarch64__) && __FreeBSD_version < 1500000 &comconsole, #endif diff --git a/stand/efi/loader/xhci_dbc_cons.h b/stand/efi/loader/xhci_dbc_cons.h new file mode 100644 --- /dev/null +++ b/stand/efi/loader/xhci_dbc_cons.h @@ -0,0 +1,34 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +extern struct console udb_console; + +extern struct xhci_debug_softc *udb_sc0; +extern struct xhci_debug_softc *udb_sc; + +extern const char *udb_hostname; +extern const char *udb_serial; diff --git a/stand/efi/loader/xhci_dbc_cons.c b/stand/efi/loader/xhci_dbc_cons.c new file mode 100644 --- /dev/null +++ b/stand/efi/loader/xhci_dbc_cons.c @@ -0,0 +1,309 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include +#include +#include + +#include +#include +#include + +#include "xhci_dbc_cons.h" +#include "xhci_dbc_pci.h" +#include "xhci_dbc_dma.h" +#include +#include +#include +#include + +static void udbc_probe(struct console *); +static bool udbc_alloc(struct console *, EFI_PCI_IO_PROTOCOL *, EFI_HANDLE *); + +static int udbc_getc(void); +static void udbc_putc(int); +static int udbc_ischar(void); +static int udbc_init(int); +struct console udb_console = { + .c_name = "udbc", + .c_desc = "USB DbC serial port", + .c_flags = 0, + .c_probe = udbc_probe, + .c_init = udbc_init, + .c_out = udbc_putc, + .c_in = udbc_getc, + .c_ready = udbc_ischar +}; + +struct xhci_debug_softc *udb_sc0; /* linked-list */ +struct xhci_debug_softc *udb_sc; /* active instance */ +const char *udb_hostname; +const char *udb_serial; + +static void +udbc_probe(struct console *cons) +{ + EFI_STATUS status; + EFI_PCI_IO_PROTOCOL *pciio; + EFI_HANDLE *h0, *h; + UINTN hlen; + device_t dev; + + h0 = h = NULL; + if (udb_sc0 != NULL) + return; + + /* Get PCI I/O handle. */ + hlen = 0; + status = BS->LocateHandleBuffer( + ByProtocol, + &pciio_guid, + NULL, + &hlen, + &h0); + if (EFI_ERROR(status)) + goto error; + + for (UINTN i = 0; i < hlen; i++) { + h = h0[i]; + status = BS->HandleProtocol( + h, + &pciio_guid, + (VOID **)&pciio); + if (EFI_ERROR(status)) + continue; + status = pciio->Pci.Read( + pciio, + EfiPciIoWidthUint32, + 0, + sizeof(dev) >> 2, /* 32-bit access */ + &dev); + if (EFI_ERROR(status)) + continue; + if (pci_get_headertype(dev) != PCIM_HDRTYPE_NORMAL && + pci_get_headertype(dev) != PCIM_MFDEV) + continue; + if (xhci_pci_match(dev) != NULL) + (void) udbc_alloc(cons, pciio, h); + } + + if (udb_sc0 != NULL) { + cons->c_flags = C_PRESENTIN | C_PRESENTOUT; + return; + } +error: + DEBUG_PRINTF(1, ("%s: Compatible xHC not found.\n", __func__)); + (void) BS->FreePool(h0); + return; +} + +static bool +udbc_alloc(struct console *cons, EFI_PCI_IO_PROTOCOL *pciio, EFI_HANDLE *h) +{ + struct xhci_debug_softc *sc; + UINTN SegmentNumber, BusNumber, DeviceNumber, FunctionNumber; + EFI_STATUS status; + int error; + + if (pciio == NULL || h == NULL) + return (false); + status = pciio->GetLocation( + pciio, + &SegmentNumber, + &BusNumber, + &DeviceNumber, + &FunctionNumber); + if (EFI_ERROR(status)) + return (false); + sc = udb_alloc_softc(sizeof(*sc), h, pciio); + if (sc == NULL) + return (false); + sc->sc_pci_rid = PCI_RID(BusNumber, DeviceNumber, FunctionNumber); + sc->sc_efi_pciio = pciio; + sc->sc_efi_hand = h; + + sc->sc_dbc_off = xhci_debug_get_xecp(sc); + if (sc->sc_dbc_off == 0) + goto error; + + xhci_debug_disable(sc); + xhci_debug_update_state(sc); + + DEBUG_PRINTF(2, ("%s: before init\n", __func__)); + if (sc->sc_init == false) { + error = xhci_debug_init_dma(sc); + if (error) { + DEBUG_PRINTF(1, ("USB DbC DMA configuration error\n")); + goto error; + } + error = xhci_debug_init_ring(sc); + if (error) { + DEBUG_PRINTF(1, ("USB DbC TRB ring configuration error\n")); + goto error; + } + sc->sc_init = true; + sc->sc_cookie = XHCI_DC_COOKIE; + } + xhci_debug_enable(sc); + + /* Add a new entry */ + if (udb_sc0 != NULL) + sc->sc_next = udb_sc0; + udb_sc0 = sc; + + (void) udbc_init(0); /* XXX: Asks udbc to init now */ + + return (true); +error: + /* XXX: free softc */ + return (false); +} + +static int +udbc_init(int arg) +{ + struct xhci_debug_softc *sc; + uint32_t rid; + char *p, *endp; + bool gdb; + + if (udb_sc != NULL) /* already initialized */ + return (CMD_OK); + + /* Initialize all instances */ + for (sc = udb_sc0; sc != NULL; sc = sc->sc_next) + xhci_debug_enable(sc); + + /* If called in c_probe, udb_sc will not be initialized. */ + if (arg == 1) + return (CMD_OK); + + p = getenv("hw.usb.xhci.dbc.enable"); + if (p != NULL && p[0] == '0') + return (CMD_OK); + + p = getenv("hw.usb.xhci.dbc.debug"); + if (p != NULL) { + dbc_debug = strtol(p, &endp, 0); + if (*endp != '\0') + dbc_debug = 0; + } + + rid = 0; + p = getenv("hw.usb.xhci.dbc.pci_rid"); + if (p != NULL) { + rid = strtol(p, &endp, 0); + if (*endp != '\0') + rid = 0; + } + + /* XXX: too late */ + udb_hostname = getenv("hw.usb.xhci.dbc.hostname"); + udb_serial = getenv("hw.usb.xhci.dbc.serial"); + p = getenv("hw.usb.xhci.dbc.gdb"); + gdb = (p != NULL && p[0] != '0'); + + /* Initialize flags */ + for (sc = udb_sc0; sc != NULL; sc = sc->sc_next) + if (gdb) + sc->sc_flags |= XHCI_DEBUG_FLAGS_GDB; + + for (sc = udb_sc0; sc != NULL; sc = sc->sc_next) { + /* If RID is not specified, use the first one. */ + if ((rid == 0 || rid == sc->sc_pci_rid) && + sc->sc_dbc_off != 0) + break; + } + if (sc == NULL) { + DEBUG_PRINTF(1, ("USB DbC not found\n")); + return (CMD_ERROR); + } + + /* Define the console */ + udb_sc = sc; + xhci_debug_event_dequeue(sc); + + return (CMD_OK); +} + +static void +udbc_putc(int c0) +{ + struct xhci_debug_softc *sc = udb_sc; + u_char c = (0xff & c0); + + if (sc == NULL) + return; + + /* XXXTHJ: Try to avoid getting stuck */ + while (DC_WORK_RING_FULL(&sc->udb_oring)) { + xhci_debug_bulk_transfer(sc); + xhci_debug_event_dequeue(sc); + delay(100); + } + + if (work_enqueue(&sc->udb_oring, &c, sizeof(c)) != sizeof(c)) + return; + + xhci_debug_bulk_transfer(sc); + /* + * This dequeue just after the transfer is important. + * Do not remove this. + */ + xhci_debug_event_dequeue(sc); +} + +/* udbc_getc() is called periodically. */ +static int +udbc_getc(void) +{ + struct xhci_debug_softc *sc = udb_sc; + + if (sc == NULL) + return (-1); + + /* Use udbc_ischar() to receive data */ + return (udbc_ischar() ? work_dequeue(&sc->udb_iring) : -1); +} + +static int +udbc_ischar(void) +{ + struct xhci_debug_softc *sc = udb_sc; + struct xhci_debug_ring *iring; + + if (sc == NULL) + return (0); + iring = &sc->udb_iring; + if (!DC_WORK_RING_EMPTY(iring)) + return (1); + + xhci_debug_bulk_transfer(sc); + xhci_debug_event_dequeue(sc); + + return (!DC_WORK_RING_EMPTY(iring)); +} diff --git a/stand/efi/loader/xhci_dbc_dma.h b/stand/efi/loader/xhci_dbc_dma.h new file mode 100644 --- /dev/null +++ b/stand/efi/loader/xhci_dbc_dma.h @@ -0,0 +1,32 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +int xhci_debug_init_dma(struct xhci_debug_softc *); +void xhci_debug_uninit_dma(struct xhci_debug_softc *); + +struct xhci_debug_softc *udb_alloc_softc(size_t, EFI_HANDLE *, + EFI_PCI_IO_PROTOCOL *); diff --git a/stand/efi/loader/xhci_dbc_dma.c b/stand/efi/loader/xhci_dbc_dma.c new file mode 100644 --- /dev/null +++ b/stand/efi/loader/xhci_dbc_dma.c @@ -0,0 +1,298 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include "xhci_dbc_pci.h" +#include +#include +#include "xhci_dbc_cons.h" +#include "xhci_dbc_dma.h" + +EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID; + +static void *udb_alloc_dma(struct xhci_debug_softc *, uint64_t); +static void udb_free_dma(struct xhci_debug_softc *, VOID *); +static uint64_t v2p(struct xhci_debug_softc *, void *); + +uint64_t +v2p(struct xhci_debug_softc *sc, void *virt) +{ + UINTN i; + UINTN p; + UINTN offset; + UINTN needed; + UINTN mapped; + struct dma *dma; + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS paddr; + VOID *mapping; + + dma = NULL; + for (i = 0; i < nitems(sc->dma_desc); i++) { + dma = &sc->dma_desc[i]; + + DEBUG_PRINTF(1, ("%s: dma_desc[%lu].cpu_addr = %p\n", __func__, + i, dma->cpu_addr)); + for (p = 0; p < dma->pages; p++) { + UINTN addr = (UINTN)dma->cpu_addr + (p * PAGE_SIZE); + + DEBUG_PRINTF(1, ("%s: check addr: %lu = %lu + %lu * %u\n", + __func__, addr, (UINTN)dma->cpu_addr, p, PAGE_SIZE)); + if ((UINTN)virt == addr) { + offset = addr - (UINTN)dma->cpu_addr; + goto found; + } + } + dma = NULL; + } + if (dma == NULL) { + DEBUG_PRINTF(1, ("CPU addr %p not found in DMA descriptor\n", virt)); + return 0; + } + +found: +#if 0 + DEBUG_PRINTF(1, "%s: found, i = %lu, dma->paddr = %lu, offset = %lu\n", + __func__, i, dma->paddr, offset); +#endif + if (dma->paddr && dma->mapping) + return (dma->paddr + offset); + + needed = dma->pages << EFI_PAGE_SHIFT; + mapped = needed; + status = sc->sc_efi_pciio->Map(sc->sc_efi_pciio, + EfiPciIoOperationBusMasterCommonBuffer, + (void *)virt, &mapped, &paddr, &mapping); + if (EFI_ERROR(status) || mapped != needed) { + DEBUG_PRINTF(1, ("pciio->Map failed: rc: 0x%lx, mapped: %lu, needed: %lu\n", + status, mapped, needed)); + return (0); + } + dma->paddr = paddr; + dma->mapping = mapping; + + if ((const void *)paddr != virt) { + DEBUG_PRINTF(1, ("Non-identity DMA mapping: dma: 0x%lx cpu: %p\n", + paddr, virt)); + } + + return (paddr); +} + +struct xhci_debug_softc * +udb_alloc_softc(size_t len, EFI_HANDLE *h, EFI_PCI_IO_PROTOCOL *pciio) +{ + struct xhci_debug_softc *sc; + EFI_STATUS status; + VOID *addr; + int pages; + + DEBUG_PRINTF(2, ("%s: called\n", __func__)); + sc = NULL; + pages = sizeof(*sc) / PAGE_SIZE + 1; + status = pciio->AllocateBuffer(pciio, + AllocateAnyPages, + EfiRuntimeServicesData, + sizeof(*sc) / PAGE_SIZE + 1, + &addr, + EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED); + if (EFI_ERROR(status)) { + DEBUG_PRINTF(1, ("%s: AllocateBuffer failed: 0x%lx\n", + __func__, status)); + return (NULL); + } + memset(addr, 0, sizeof(*sc)); + sc = (struct xhci_debug_softc *)addr; + + sc->dma_desc[0].pages = pages; + sc->dma_desc[0].cpu_addr = addr; + sc->sc_efi_pciio = pciio; + sc->sc_efi_hand = h; + sc->sc_init_dma = false; + sc->sc_init = false; + if (udb_hostname != NULL) + strlcpy(sc->sc_hostname, udb_hostname, sizeof(sc->sc_hostname)); + if (udb_serial != NULL) + strlcpy(sc->sc_serialno, udb_serial, sizeof(sc->sc_serialno)); + + return (sc); +} + +int +xhci_debug_init_dma(struct xhci_debug_softc *sc) +{ +#define ALLOC_DMA(p, type, order) \ + do { \ + p = (type)udb_alloc_dma(sc, order); \ + if (p == NULL) { \ + xhci_debug_uninit_dma(sc); \ + return (1); \ + } \ + } while (0) + + if (sc->sc_init_dma) + return (0); + + ALLOC_DMA(sc->udb_ctx, struct xhci_debug_ctx *, 0); + ALLOC_DMA(sc->udb_erst, struct xhci_event_ring_seg *, 0); + ALLOC_DMA(sc->udb_str, char *, 0); + ALLOC_DMA(sc->udb_ering.trb, struct xhci_trb *, DC_TRB_RING_ORDER); + ALLOC_DMA(sc->udb_iring.trb, struct xhci_trb *, DC_TRB_RING_ORDER); + ALLOC_DMA(sc->udb_oring.trb, struct xhci_trb *, DC_TRB_RING_ORDER); + ALLOC_DMA(sc->udb_ering.work.buf, uint8_t *, DC_WORK_RING_ORDER); + ALLOC_DMA(sc->udb_iring.work.buf, uint8_t *, DC_WORK_RING_ORDER); + ALLOC_DMA(sc->udb_oring.work.buf, uint8_t *, DC_WORK_RING_ORDER); + + /* Export CTX, ERST, and STR */ +#define SETADDR(n, len) do { \ + sc->udb_##n##_paddr = (uintptr_t)v2p(sc, sc->udb_##n); \ + sc->udb_##n##_len = len; \ + } while (0) + SETADDR(ctx, PAGE_SIZE); + SETADDR(erst, PAGE_SIZE); + SETADDR(str, PAGE_SIZE); + + /* Export TRB rings. */ +#define SETADDR_RING(n, paddr, lenp, buf, len) do { \ + sc->udb_##paddr = (uintptr_t)v2p(sc, sc->udb_##buf); \ + sc->udb_##lenp = len; \ + } while (0) + + SETADDR_RING(ering, ering.paddr, ering.len, ering.trb, + PAGE_SIZE * (1UL << DC_TRB_RING_ORDER)); + SETADDR_RING(iring, iring.paddr, iring.len, iring.trb, + PAGE_SIZE * (1UL << DC_TRB_RING_ORDER)); + SETADDR_RING(oring, oring.paddr, oring.len, oring.trb, + PAGE_SIZE * (1UL << DC_TRB_RING_ORDER)); + SETADDR_RING(ering.work, ering.work.paddr, ering.work.len, + ering.work.buf, PAGE_SIZE * (1UL << DC_WORK_RING_ORDER)); + SETADDR_RING(iring.work, iring.work.paddr, iring.work.len, + iring.work.buf, PAGE_SIZE * (1UL << DC_WORK_RING_ORDER)); + SETADDR_RING(oring.work, oring.work.paddr, oring.work.len, + oring.work.buf, PAGE_SIZE * (1UL << DC_WORK_RING_ORDER)); + + sc->sc_init_dma = true; + + return (0); +#undef ALLOC_DMA +} + +static void * +udb_alloc_dma(struct xhci_debug_softc *sc, uint64_t order) +{ + struct dma *dma; + EFI_STATUS status; + VOID *addr; + UINTN pages = 1UL << order; + UINTN i; + + for (i = 0; i < nitems(sc->dma_desc); i++) { + dma = &sc->dma_desc[i]; + if (dma->cpu_addr == NULL) + break; + } + if (i == nitems(sc->dma_desc)) { + DEBUG_PRINTF(1, ("%s: Out of DMA descriptor\n", __func__)); + return (NULL); + } + status = sc->sc_efi_pciio->AllocateBuffer(sc->sc_efi_pciio, + AllocateAnyPages, + EfiRuntimeServicesData, + pages, + &addr, + EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED); + if (EFI_ERROR(status)) { + DEBUG_PRINTF(1, ("%s: AllocateBuffer failed: 0x%lx\n", + __func__, status)); + return (NULL); + } + + dma->pages = pages; + dma->cpu_addr = addr; + DEBUG_PRINTF(1, ("%s: i = %lu, cpu_addr = %p, pages = %lu\n", __func__, + i, dma->cpu_addr, pages)); + + return (addr); +} + +static void +udb_free_dma(struct xhci_debug_softc *sc, VOID *addr) +{ + struct dma *dma; + EFI_STATUS status; + UINTN i; + + for (i = 0; i < nitems(sc->dma_desc); i++) { + dma = &sc->dma_desc[i]; + if (dma->cpu_addr == addr) + break; + } + if (i == nitems(sc->dma_desc)) + return; + if (dma->mapping) { + status = sc->sc_efi_pciio->Unmap(sc->sc_efi_pciio, + dma->mapping); + if (EFI_ERROR(status)) { + DEBUG_PRINTF(1, ("%s: Unmap failed: 0x%lx\n", + __func__, status)); + return; + } + } + status = sc->sc_efi_pciio->FreeBuffer(sc->sc_efi_pciio, dma->pages, + addr); + if (EFI_ERROR(status)) { + DEBUG_PRINTF(1, ("%s: FreeBuffer failed: 0x%lx\n", + __func__, status)); + return; + } + memset(&dma, 0, sizeof(*dma)); +} + +void +xhci_debug_uninit_dma(struct xhci_debug_softc *sc) +{ + udb_free_dma(sc, sc->udb_ctx); + udb_free_dma(sc, sc->udb_erst); + udb_free_dma(sc, sc->udb_ering.trb); + udb_free_dma(sc, sc->udb_oring.trb); + udb_free_dma(sc, sc->udb_iring.trb); + udb_free_dma(sc, sc->udb_ering.work.buf); + udb_free_dma(sc, sc->udb_oring.work.buf); + udb_free_dma(sc, sc->udb_iring.work.buf); + + sc->sc_init_dma = false; +} diff --git a/stand/efi/loader/xhci_dbc_pci.h b/stand/efi/loader/xhci_dbc_pci.h new file mode 100644 --- /dev/null +++ b/stand/efi/loader/xhci_dbc_pci.h @@ -0,0 +1,94 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +extern EFI_GUID pciio_guid; + +/* + * Dummy functions to use xhci_pci_match(). Note that device_t is defined + * as PCI Type 00 header. + */ +typedef struct { + UINT16 VendorId; + UINT16 DeviceId; + UINT16 Command; + UINT16 Status; + UINT8 RevisionID; + UINT8 ClassCode[3]; + UINT8 CacheLineSize; + UINT8 LatencyTimer; + UINT8 HeaderType; + UINT8 BIST; +} PCI_DEVICE_INDEPENDENT_REGION; + +typedef struct { + UINT32 Bar[6]; + UINT32 CISPtr; + UINT16 SubsystemVendorID; + UINT16 SubsystemID; + UINT32 ExpansionRomBar; + UINT8 CapabilityPtr; + UINT8 Reserved1[3]; + UINT32 Reserved2; + UINT8 InterruptLine; + UINT8 InterruptPin; + UINT8 MinGnt; + UINT8 MaxLat; +} PCI_DEVICE_HEADER_TYPE_REGION; + +typedef struct { + PCI_DEVICE_INDEPENDENT_REGION Hdr; + PCI_DEVICE_HEADER_TYPE_REGION Device; +} PCI_TYPE00; + +typedef PCI_TYPE00 device_t; + +static inline uint32_t +pci_get_devid(device_t dev) +{ + return ((dev.Hdr.DeviceId << 16) | dev.Hdr.VendorId); +} +static inline uint8_t +pci_get_class(device_t dev) +{ + return (dev.Hdr.ClassCode[0]); +} +static inline uint8_t +pci_get_subclass(device_t dev) +{ + return (dev.Hdr.ClassCode[1]); +} +static inline uint8_t +pci_get_progif(device_t dev) +{ + return (dev.Hdr.ClassCode[2]); +} +static inline uint8_t +pci_get_headertype(device_t dev) +{ + return (dev.Hdr.HeaderType); +} diff --git a/stand/efi/loader_ia32/Makefile b/stand/efi/loader_ia32/Makefile --- a/stand/efi/loader_ia32/Makefile +++ b/stand/efi/loader_ia32/Makefile @@ -1,5 +1,6 @@ DO32=1 INSTALL_LOADER_HELP_FILE=no +LOADER_USBDBC_SUPPORT=no NEWVERSWHAT?= "EFI loader" amd64-ia32 diff --git a/stand/loader.mk b/stand/loader.mk --- a/stand/loader.mk +++ b/stand/loader.mk @@ -53,6 +53,7 @@ # LOADER_NFS_SUPPORT Add NFS support # LOADER_TFTP_SUPPORT Add TFTP support # LOADER_UFS_SUPPORT Add support for UFS filesystems +# LOADER_USBDBC_SUPPORT Add USB debug capability support # LOADER_ZFS_SUPPORT Add support for ZFS filesystems # @@ -131,6 +132,11 @@ CFLAGS+= -DLOADER_BZIP2_SUPPORT .endif +# Debugging +.if ${LOADER_USBDBC_SUPPORT:Uno} == "yes" +CFLAGS+= -DLOADER_USBDBC_SUPPORT +.endif + # Network related things .if ${LOADER_NET_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_NET_SUPPORT diff --git a/sys/conf/NOTES b/sys/conf/NOTES --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2508,6 +2508,8 @@ device uark # USB support for Belkin F5U103 and compatible serial adapters device ubsa +# USB support for XHCI USB DbC.GP serial device +device udbc # USB support for serial adapters based on the FT8U100AX and FT8U232AM device uftdi # USB support for some Windows CE based serial communication. diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -3396,6 +3396,7 @@ dev/usb/serial/ubser.c optional ubser dev/usb/serial/uchcom.c optional uchcom dev/usb/serial/ucycom.c optional ucycom +dev/usb/serial/udbc.c optional udbc dev/usb/serial/ufoma.c optional ufoma dev/usb/serial/uftdi.c optional uftdi dev/usb/serial/ugensa.c optional ugensa @@ -3410,10 +3411,10 @@ dev/usb/serial/uvisor.c optional uvisor dev/usb/serial/uvscom.c optional uvscom dev/usb/serial/usb_serial.c optional ucom | u3g | uark | ubsa | ubser | \ - uchcom | ucycom | ufoma | uftdi | \ - ugensa | uipaq | umcs | umct | \ - umodem | umoscom | uplcom | usie | \ - uslcom | uvisor | uvscom + uchcom | ucycom | udbc | ufoma | \ + uftdi | ugensa | uipaq | umcs | \ + umct | umodem | umoscom | uplcom | \ + usie | uslcom | uvisor | uvscom # # USB misc drivers # diff --git a/sys/dev/usb/controller/xhci_dbc.h b/sys/dev/usb/controller/xhci_dbc.h new file mode 100644 --- /dev/null +++ b/sys/dev/usb/controller/xhci_dbc.h @@ -0,0 +1,231 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +extern int dbc_debug; + +#ifndef _KERNEL +/* for struct mtx in xhci_debug_softc */ +#include +#include + +inline void mtx_lock_spin(struct mtx *m) { } +inline int mtx_trylock_spin(struct mtx *m) { return (1); } +inline void mtx_unlock_spin(struct mtx *m) { } +#endif + +/* DbC idVendor and idProduct */ +#define DC_VENDOR 0x1d6b /* Linux Foundation */ +#define DC_PRODUCT 0x0011 /* Linux */ +#define DC_PROTOCOL 0x0000 /* GNU GDB = 1 */ +#define DC_REVISION 0x0010 /* 0.10 */ + +#define DC_STRING_MANUFACTURER "The FreeBSD Foundation" +#define DC_STRING_PRODUCT "FreeBSD USB DbC.GP Device" +#define DC_STRING_SERIAL "12345678" + +/* DbC endpoint types */ +enum { + EP_BULK_OUT = 2, + EP_BULK_IN = 6 +}; + +#define DC_TRB_PER_PAGE (XHCI_PAGE_SIZE / sizeof(struct xhci_trb)) + +/* Defines the size in bytes of TRB rings as 2^DC_TRB_RING_ORDER * 4096 */ +#define DC_TRB_RING_ORDER 4 +#define DC_TRB_RING_LEN (DC_TRB_PER_PAGE * (1ULL << DC_TRB_RING_ORDER)) +#define DC_TRB_RING_OFFSET_MASK (DC_TRB_RING_LEN - 1U) +#define DC_TRB_RING_LAST (DC_TRB_RING_LEN - 1U) +#define DC_TRB_RING_BYTES (DC_TRB_RING_LEN * sizeof(struct xhci_trb)) +#define DC_TRB_RING_MASK (DC_TRB_RING_BYTES - 1U) + +/* Defines the size in bytes of work rings as 2^WORK_RING_ORDER * 4096 */ +#define DC_WORK_RING_ORDER 3 +#define DC_WORK_RING_LEN (XHCI_PAGE_SIZE * (1ULL << DC_WORK_RING_ORDER)) +#define DC_WORK_RING_OFFSET_MASK (DC_WORK_RING_LEN - 1U) +#define DC_WORK_RING_LAST (DC_WORK_RING_LEN - 1U) +#define DC_WORK_RING_BYTES DC_WORK_RING_LEN + +#define DC_WORK_RING_FULL(r) \ + ((((r)->work.w_enq + 1) & DC_WORK_RING_OFFSET_MASK) == (r)->work.w_deq) +#define DC_WORK_RING_EMPTY(r) \ + ((r)->work.w_enq == (r)->work.w_deq) +#define XHCI_DC_RING_FULL(r) \ + ((((r)->enq + 1) & DC_TRB_RING_OFFSET_MASK) == (r)->deq) +#define XHCI_DC_RING_EMPTY(r) \ + ((r)->enq == (r)->deq) +/* TRB ring has a link TRB at the tail of the queue. */ +#define XHCI_DC_RING_SLOTS(r) ( \ + (((r)->deq <= (r)->enq) ? (r)->deq + DC_TRB_RING_LEN - 1 : (r)->deq) \ + - (r)->enq) +#define DC_WORK_RING_SLOTS(r) ( \ + (((r)->w_deq <= (r)->w_enq) ? (r)->w_deq + DC_WORK_RING_LEN : (r)->w_deq) \ + - (r)->w_enq) + +struct xhci_debug_ctx { + uint32_t info[16]; + struct xhci_endp_ctx64 ep_out; + struct xhci_endp_ctx64 ep_in; +}; + +struct xhci_debug_work_ring { + uint8_t *buf; + uint32_t w_enq; + uint32_t w_deq; + uint64_t paddr; + uint64_t len; +}; + +struct xhci_debug_ring { + struct xhci_trb *trb; /* Array of TRBs */ + uint64_t paddr; /* base address of TRBs */ + uint64_t len; /* length of TRBs */ + uint32_t enq; /* The offset of the enqueue ptr */ + uint32_t deq; /* The offset of the dequeue ptr */ + uint8_t cyc; /* Cycle state toggled on each wrap-around */ + uint32_t doorbell; /* Doorbell target */ +#define XHCI_DC_RING_IN(ring) ((ring)->doorbell == XHCI_DCDB_IN) +#define XHCI_DC_RING_OUT(ring) ((ring)->doorbell == XHCI_DCDB_OUT) +#define XHCI_DC_RING_EV(ring) ((ring)->doorbell == XHCI_DCDB_INVALID) + struct xhci_debug_work_ring work; + + struct mtx mtx; + uint32_t flags; +#define XHCI_DC_RING_F_IN_PROGRESS 0x0001 + uint32_t lasterror; + uint32_t lastenq; + uint32_t lastdeq; +/* + * Bitmap for completion report. + * + * Set when enqueuing a normal TRB, and + * reset when receiving the corrensponding transfer event TRB. + */ + uint8_t iocbitmap[DC_TRB_RING_LEN / 8 + 1]; +#define IOCBITMAP_GET(ring, n) \ + (((ring)->iocbitmap[(n) / 8] >> ((uint8_t)(n) % 8)) & (uint8_t)1) +#define IOCBITMAP_SET(ring, n) \ + ((ring)->iocbitmap[(n) / 8] |= ((uint8_t)1 << (uint8_t)((n) % 8))) +#define IOCBITMAP_CLEAR(ring, n) \ + ((ring)->iocbitmap[(n) / 8] &= ~((uint8_t)1 << (uint8_t)((n) % 8))) + + uint64_t ec[256]; /* error counter */ +#define XHCI_TRB_ERROR_EVLOCKED 0x30 +#define XHCI_TRB_ERROR_CALLED 0x31 +#define XHCI_TRB_ERROR_WORKQ_FULL 0x32 +#define XHCI_TRB_ERROR_UNKNOWN 0xff +}; + +#ifndef _KERNEL +#define DMA_DESC_CAP 16 +struct dma { + UINTN pages; + EFI_PHYSICAL_ADDRESS paddr; + VOID *cpu_addr; + VOID *mapping; +}; +#endif + +/* + * This softc is used in kernel and loader. + */ +struct xhci_softc; +struct udbcons_priv; +#define XHCI_DC_COOKIE 0x4d79b718 +struct xhci_debug_softc { + struct xhci_debug_softc *sc_next; + bool sc_next_fixup; + struct mtx sc_mtx; + uint32_t sc_cookie; + + struct xhci_debug_ctx *udb_ctx; + uint64_t udb_ctx_paddr; + uint64_t udb_ctx_len; + + struct xhci_event_ring_seg *udb_erst; + uint64_t udb_erst_paddr; + uint64_t udb_erst_len; + + struct xhci_debug_ring udb_ering; + struct xhci_debug_ring udb_oring; + struct xhci_debug_ring udb_iring; + + char *udb_str; + uint64_t udb_str_paddr; + uint64_t udb_str_len; + + uint32_t sc_state; + uint32_t sc_flags; +#define XHCI_DEBUG_FLAGS_GDB 0x0001 + uint64_t sc_polling_count; +#define XHCI_DC_POLLING_TIME 50 + uint32_t sc_polling_time; + uint64_t sc_dequeue_count; + bool sc_init; + bool sc_init_dma; + bool sc_fixup_done; + bool sc_open; + char sc_hostname[256]; + char sc_serialno[256]; + + /* In kernel, sc_*_off in struct xhci_softc is used. */ + struct xhci_softc *sc_xhci; /* used in _X{READ,WRITE} */ + uint32_t sc_dbc_off; + uint32_t sc_capa_off; /* zero */ + uint32_t sc_pci_rid; + +#ifdef _KERNEL + device_t sc_dev; /* used in kernel only */ + struct udbcons_priv *sc_cons; /* used in kernel only */ + uint8_t dummy[1456]; /* XXX: pad to 10240 bytes */ +#else + struct console *sc_cons; /* used in loader only */ + EFI_PCI_IO_PROTOCOL *sc_efi_pciio; + EFI_HANDLE sc_efi_hand; + struct dma dma_desc[DMA_DESC_CAP]; + uint8_t dummy[936]; /* XXX: pad to 10240 bytes */ +#endif +}; + + +void xhci_debug_reg_read(struct xhci_debug_softc *); +void xhci_debug_reg_restore(struct xhci_debug_softc *); + +uint32_t xhci_debug_get_xecp(struct xhci_debug_softc *); +bool xhci_debug_enable(struct xhci_debug_softc *); +void xhci_debug_disable(struct xhci_debug_softc *); + +int xhci_debug_init_ring(struct xhci_debug_softc *); +uint32_t xhci_debug_update_state(struct xhci_debug_softc *); + +uint64_t xhci_debug_bulk_transfer(struct xhci_debug_softc *); +void xhci_debug_event_dequeue(struct xhci_debug_softc *); + +int work_dequeue(struct xhci_debug_ring *); +int64_t work_enqueue(struct xhci_debug_ring *, const char *, int64_t); + +void udb_dump_info(struct xhci_debug_softc *); diff --git a/sys/dev/usb/controller/xhci_dbc.c b/sys/dev/usb/controller/xhci_dbc.c new file mode 100644 --- /dev/null +++ b/sys/dev/usb/controller/xhci_dbc.c @@ -0,0 +1,1126 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include +#include +#include /* getenv_quad */ +#include +#include +#include + +#ifndef _KERNEL +#include +#include +#include +#include /* device_t */ +#include +#include +#include "xhci_dbc_cons.h" +#include "xhci_dbc_dma.h" +#endif + +static void flush_range(void *, uint32_t); + +static bool xhci_debug_update_regbit(struct xhci_debug_softc *, int, int, int, + bool); +static bool xhci_debug_set_regbit(struct xhci_debug_softc *, int, int, int); +static bool xhci_debug_reset_regbit(struct xhci_debug_softc *, int, int, int); + +static bool xhci_debug_wait_connection(struct xhci_debug_softc *, uint32_t); + +static struct xhci_debug_softc * xhci_debug_alloc_softc(device_t); +static bool xhci_debug_softc_fixup(struct xhci_debug_softc *); +static void xhci_debug_ring_init(struct xhci_debug_softc *, + struct xhci_debug_ring *, int, uint32_t); +struct usb_strdesc; +static void atou16cpy(struct usb_strdesc *, const char *); +static void xhci_debug_init_strings(struct xhci_debug_softc *, uint32_t [16]); + +static void xhci_debug_ring_doorbell(struct xhci_debug_softc *, uint32_t); +static uint64_t xhci_debug_bulk_transfer0(struct xhci_debug_softc *, + struct xhci_debug_ring *); + +static void xhci_debug_init_ep(struct xhci_endp_ctx64 *, uint64_t, uint32_t, + uint64_t); + +#if 0 +static bool xhci_debug_event_ready(struct xhci_debug_softc *); +#endif +static uint32_t xhci_debug_ring_boundary(struct xhci_debug_ring *, bool); + +static void xhci_debug_transfer_event_handler(struct xhci_debug_softc *, + uint64_t, uint32_t, uint32_t); +static uint64_t trb_tx_enqueue_locked(struct xhci_debug_softc *sc, + struct xhci_debug_ring *); +static uint64_t trb_rx_enqueue_locked(struct xhci_debug_softc *sc, + struct xhci_debug_ring *); +static uint64_t trb_push_locked(struct xhci_debug_softc *sc, + struct xhci_debug_ring *, uint64_t, uint64_t); + +/* sc_state */ +#define STRSTATE(x) [XHCI_DCPORT_ST_##x] = #x +static const char *strstate[] = { + STRSTATE(OFF), + STRSTATE(DISCONNECTED_RUNNING), /* not in spec */ + STRSTATE(DISCONNECTED), + STRSTATE(DISABLED), + STRSTATE(RESETTING), + STRSTATE(ENABLED), + STRSTATE(CONFIGURED) +}; + +/* sysctls */ + +int dbc_debug; +int dbc_reset; +int dbc_enable = 1; +int dbc_baud = 115200; +int dbc_gdb; +uint32_t dbc_pci_rid; + +vm_paddr_t udbc_console; + +static struct xhci_debug_reg { + uint32_t erstsz; + uint64_t erstba; + uint64_t erdp; + uint64_t cp; + uint32_t ddi1; + uint32_t ddi2; +} udbc_reg; + +void +xhci_debug_reg_read(struct xhci_debug_softc *sc) +{ + + if (sc == NULL) + return; + + udbc_reg.erstsz = _XREAD4(sc, dbc, XHCI_DCERSTSZ); + udbc_reg.erstba = _XREAD44LH(sc, dbc, XHCI_DCERSTBA); + udbc_reg.erdp = _XREAD44LH(sc, dbc, XHCI_DCERDP); + udbc_reg.cp = _XREAD44LH(sc, dbc, XHCI_DCCP); + udbc_reg.ddi1 = _XREAD4(sc, dbc, XHCI_DCDDI1); + udbc_reg.ddi2 = _XREAD4(sc, dbc, XHCI_DCDDI2); +} +void +xhci_debug_reg_restore(struct xhci_debug_softc *sc) +{ + + if (sc == NULL) + return; + + _XWRITE4(sc, dbc, XHCI_DCERSTSZ, udbc_reg.erstsz); + _XWRITE44LH(sc, dbc, XHCI_DCERSTBA, udbc_reg.erstba); + _XWRITE44LH(sc, dbc, XHCI_DCERDP, udbc_reg.erdp); + _XWRITE44LH(sc, dbc, XHCI_DCCP, udbc_reg.cp); + _XWRITE4(sc, dbc, XHCI_DCDDI1, udbc_reg.ddi1); + _XWRITE4(sc, dbc, XHCI_DCDDI2, udbc_reg.ddi2); +} + +static void +flush_range(void *ptr, uint32_t bytes) +{ +#ifndef _KERNEL + uint32_t i; + const uint32_t clshft = 6; + const uint32_t clsize = (1UL << clshft); + const uint32_t clmask = clsize - 1; + uint32_t lines = (bytes >> clshft); + lines += (bytes & clmask) != 0; + + if (bytes == 0) + return; + +# if defined(__amd64__) +#define CLFLUSH(ptr) \ + __asm volatile("clflush %0" : "+m"(*(volatile char *)ptr)) + + for (i = 0; i < lines; i++) + CLFLUSH((void *)((uint64_t)ptr + (i * clsize))); +#undef CLFLUSH +#endif +#endif +} + +uint32_t +xhci_debug_update_state(struct xhci_debug_softc *sc) +{ + uint32_t ctrl; + uint32_t portsc; + uint32_t sc_state_old; + + ctrl = _XREAD4(sc, dbc, XHCI_DCCTRL); + portsc = _XREAD4(sc, dbc, XHCI_DCPORTSC); + sc_state_old = sc->sc_state; + sc->sc_state = XHCI_DCSTATUS(ctrl, portsc); + if (sc->sc_state != sc_state_old) + device_printf(sc->sc_dev, "state: %s(0x%02x) -> %s(0x%02x)\n", + strstate[sc_state_old], sc_state_old, + strstate[sc->sc_state], sc->sc_state); + return (sc->sc_state); +} + +static bool +xhci_debug_update_regbit(struct xhci_debug_softc *sc, int reg, int bit, + int timeout, bool reset) +{ + uint32_t temp; + int expected; + + timeout *= 10; /* ms */ + temp = le32toh(_XREAD4(sc, dbc, reg)); + if (reset) + temp &= ~bit; + else + temp |= bit; + _XWRITE4(sc, dbc, reg, htole32(temp)); + expected = (reset) ? 0 : bit; + while ((le32toh(_XREAD4(sc, dbc, reg)) & bit) != expected && + timeout != 0) { + delay(100); + timeout--; + } + return (timeout > 0); +} +static bool +xhci_debug_set_regbit(struct xhci_debug_softc *sc, int reg, int bit, + int timeout) +{ + + return (xhci_debug_update_regbit(sc, reg, bit, timeout, false)); +} +static bool +xhci_debug_reset_regbit(struct xhci_debug_softc *sc, int reg, int bit, + int timeout) +{ + + return (xhci_debug_update_regbit(sc, reg, bit, timeout, true)); +} + +bool +xhci_debug_enable(struct xhci_debug_softc *sc) +{ + uint32_t state; + + if (sc == NULL) + return (false); + + state = xhci_debug_update_state(sc); + if (state == XHCI_DCPORT_ST_OFF || + state == XHCI_DCPORT_ST_DISCONNECTED) { + if (!xhci_debug_set_regbit(sc, XHCI_DCCTRL, XHCI_DCCTRL_DCE, + 1)) { + device_printf(sc->sc_dev, "initialization failed\n"); + return (false); + } + + } + + /* If success, check the cable. */ + return (xhci_debug_wait_connection(sc, 1)); +} + +static bool +xhci_debug_wait_connection(struct xhci_debug_softc *sc, uint32_t timeout) +{ + bool ret; + + if (xhci_debug_update_state(sc) == XHCI_DCPORT_ST_DISCONNECTED) { + ret = xhci_debug_set_regbit(sc, XHCI_DCPORTSC, + XHCI_DCPORTSC_PED, 1000); /* XXX: 1 was not enough */ + if (ret == false) { + device_printf(sc->sc_dev, "No DbC cable detected\n"); + return (ret); + } + } + timeout = 10000; + device_printf(sc->sc_dev, "waiting for a cable\n"); + while (xhci_debug_update_state(sc) != XHCI_DCPORT_ST_CONFIGURED) { + delay(500); + if (--timeout == 0) { + device_printf(sc->sc_dev, + "DbC cable detection timed out\n"); + return (false); + } + } + + device_printf(sc->sc_dev, "DbC cable detected\n"); + + return (true); +} + +void +xhci_debug_disable(struct xhci_debug_softc *sc) +{ + bool ret; + + xhci_debug_reset_regbit(sc, XHCI_DCPORTSC, XHCI_DCPORTSC_PED, 0); + ret = xhci_debug_reset_regbit(sc, XHCI_DCCTRL, XHCI_DCCTRL_DCE, 1000); + if (ret == false) + DEBUG_PRINTF(1, ("%s: clearing XHCI_DCCTRL_DCE failed\n", + __func__)); + xhci_debug_update_state(sc); +} + +int +xhci_debug_init_ring(struct xhci_debug_softc *sc) +{ + uint64_t erdp, out, in; + uint32_t temp; + + if (sc->sc_dbc_off == 0) { + DEBUG_PRINTF(1, ("%s: register is empty\n", __func__)); + return (-1); + } + /* Create an event ring. */ + xhci_debug_ring_init(sc, &sc->udb_ering, 0, XHCI_DCDB_INVAL); + erdp = sc->udb_ering.paddr; + memset(sc->udb_erst, 0, sizeof(*sc->udb_erst)); + sc->udb_erst->qwEvrsTablePtr = htole64(erdp); + sc->udb_erst->dwEvrsTableSize = htole32(DC_TRB_RING_LEN); + DEBUG_PRINTF(2, ("%s: ERDP=0x%016llx\n", __func__, + (unsigned long long)erdp)); + + /* Create an output ring. */ + xhci_debug_ring_init(sc, &sc->udb_oring, 1, XHCI_DCDB_OUT); + out = sc->udb_oring.paddr; + + /* Create an input ring. */ + xhci_debug_ring_init(sc, &sc->udb_iring, 1, XHCI_DCDB_IN); + in = sc->udb_iring.paddr; + + /* Initialize DbC context. */ + memset(sc->udb_ctx, 0, sizeof(*sc->udb_ctx)); + xhci_debug_init_strings(sc, sc->udb_ctx->info); + + /* Initialize endpoints. */ + temp = _XREAD4(sc, dbc, XHCI_DCCTRL); + xhci_debug_init_ep(&sc->udb_ctx->ep_out, + (uint64_t)XHCI_DCCTRL_MBS_GET(le32toh(temp)), EP_BULK_OUT, out); + xhci_debug_init_ep(&sc->udb_ctx->ep_in, + (uint64_t)XHCI_DCCTRL_MBS_GET(le32toh(temp)), EP_BULK_IN, in); + + /* Configure register values. */ + _XWRITE4(sc, dbc, XHCI_DCERSTSZ, htole32(1)); + _XWRITE44LH(sc, dbc, XHCI_DCERSTBA, htole64(sc->udb_erst_paddr)); + _XWRITE44LH(sc, dbc, XHCI_DCERDP, htole64(erdp)); + _XWRITE44LH(sc, dbc, XHCI_DCCP, htole64(sc->udb_ctx_paddr)); + _XWRITE4(sc, dbc, XHCI_DCDDI1, + htole32((DC_VENDOR << 16) | DC_PROTOCOL)); + _XWRITE4(sc, dbc, XHCI_DCDDI2, + htole32((DC_REVISION << 16) | DC_PRODUCT)); + flush_range(sc->udb_ctx, sizeof(*sc->udb_ctx)); + flush_range(sc->udb_erst, sizeof(*sc->udb_erst)); + flush_range(sc->udb_ering.trb, DC_TRB_RING_BYTES); + flush_range(sc->udb_oring.trb, DC_TRB_RING_BYTES); + flush_range(sc->udb_iring.trb, DC_TRB_RING_BYTES); + flush_range(sc->udb_oring.work.buf, DC_WORK_RING_BYTES); + flush_range(sc->udb_iring.work.buf, DC_WORK_RING_BYTES); + + return (0); +} + +uint32_t +xhci_debug_get_xecp(struct xhci_debug_softc *sc) +{ + uint32_t hccp1; + uint32_t eec; + uint32_t eecp; + + if (sc == NULL) + return (0); + + hccp1 = _XREAD4(sc, capa, XHCI_HCSPARAMS0); + + if (XHCI_HCS0_XECP(hccp1) == 0) + goto notfound; + + eec = -1; /* XXX */ + for (eecp = XHCI_HCS0_XECP(hccp1) << 2; + eecp != 0 && XHCI_XECP_NEXT(eec) != 0; + eecp += XHCI_XECP_NEXT(eec) << 2) { + eec = _XREAD4(sc, capa, eecp); + + DEBUG_PRINTF(1, ("%s: Looking for xECP: " + "expected=%02x, found=%02x, next=%04x\n", __func__, + XHCI_ID_USB_DEBUG, XHCI_XECP_ID(eec), + XHCI_XECP_NEXT(eec) << 2)); + + if (XHCI_XECP_ID(eec) == XHCI_ID_USB_DEBUG) + break; + } + if (eecp == 0 || XHCI_XECP_ID(eec) == 0) + goto notfound; + + DEBUG_PRINTF(1, ("%s: DbC was found at %08x\n", __func__, eecp)); + + return (eecp); +notfound: + DEBUG_PRINTF(1, ("%s: DbC was not found\n", __func__)); + + return (0); +} + +static bool +xhci_debug_softc_fixup(struct xhci_debug_softc *sc) +{ + return (true); +} + +static void +xhci_debug_ring_init(struct xhci_debug_softc *sc, + struct xhci_debug_ring *ring, int producer, uint32_t doorbell) +{ + memset(ring->trb, 0, DC_TRB_RING_BYTES); + memset(ring->work.buf, 0, DC_WORK_RING_BYTES); + + ring->enq = 0; + ring->deq = 0; + ring->cyc = 1; + ring->doorbell = doorbell; + ring->work.w_enq = 0; + ring->work.w_deq = 0; + + /* Place a link TRB at the tail if producer == 1. */ + if (producer) { + /* Fields for link TRBs (section 6.4.4.1) */ + + ring->trb[DC_TRB_RING_LAST] = (struct xhci_trb){ + .qwTrb0 = htole64(ring->paddr), + .dwTrb3 = htole32( + XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) | + XHCI_TRB_3_TC_BIT + ) + }; + } +} + +#define DC_STRDESC_STRING0 0 +#define DC_STRDESC_MANUFACTURER 1 +#define DC_STRDESC_PRODUCT 2 +#define DC_STRDESC_SERIAL 3 +#define USB_DESC_TYPE_STRING 3 +static struct usb_strdesc { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t chars[62]; /* UTF-16LE */ +} strdesc[] = { + [DC_STRDESC_STRING0] = { + .bLength = 2 + 4, + .bDescriptorType = USB_DESC_TYPE_STRING, + .chars = { 9, 0, 4, 0 }, /* 0x0409 = English */ + }, + [DC_STRDESC_MANUFACTURER] = { + .bLength = 2, + .bDescriptorType = USB_DESC_TYPE_STRING, + }, + [DC_STRDESC_PRODUCT] = { + .bLength = 2, + .bDescriptorType = USB_DESC_TYPE_STRING, + }, + [DC_STRDESC_SERIAL] = { + .bLength = 2, + .bDescriptorType = USB_DESC_TYPE_STRING, + } +}; +static void +atou16cpy(struct usb_strdesc *desc, const char *str) +{ + int i; + + for (i = 0; i < strlen(str) && i * 2 < sizeof(desc->chars) - 1; i++) { + desc->chars[i * 2] = str[i]; + desc->chars[i * 2 + 1] = '\0'; + } + desc->bLength += i * 2; + desc->chars[sizeof(desc->chars) - 1] = '\0'; +} + +/* Initialize the DbC info with USB string descriptor addresses. */ +static void +xhci_debug_init_strings(struct xhci_debug_softc *sc, uint32_t info[16]) +{ + const char *serial; + char hostname[256]; + char *p; + + /* + * The product string will be "DC_STRING_PRODUCT (hostname)". + */ + p = hostname; + strlcpy(hostname, DC_STRING_PRODUCT, sizeof(hostname)); + if (sc->sc_hostname[0] != '\0' && + strlen(sc->sc_hostname) + < sizeof(hostname) - sizeof(DC_STRING_PRODUCT) - 4) { + p = hostname + strlen(hostname); + *p++ = ' '; + *p++ = '('; + strlcpy(p, sc->sc_hostname, sizeof(hostname) - (p - hostname)); + p = hostname + strlen(hostname); + *p++ = ')'; + *p++ = '\0'; + } + serial = (sc->sc_serialno[0] != '\0') + ? sc->sc_serialno : DC_STRING_SERIAL; + atou16cpy(&strdesc[DC_STRDESC_MANUFACTURER], DC_STRING_MANUFACTURER); + atou16cpy(&strdesc[DC_STRDESC_PRODUCT], hostname); + atou16cpy(&strdesc[DC_STRDESC_SERIAL], serial); + + /* udb_str is 1 page buffer longer than sizeof(strdesc) */ + memcpy(sc->udb_str, strdesc, sizeof(strdesc)); + +#define STRADDR0(index) \ + ((uint64_t)sc->udb_str_paddr + index * sizeof(struct usb_strdesc)) +#define STRADDR_LO(index) \ + ((uint32_t)(STRADDR0(index) & 0xffffffff)) +#define STRADDR_HI(index) \ + ((uint32_t)((STRADDR0(index) >> 32) & 0xffffffff)) + + info[XHCI_DCDBCIC_STR0DESC_LO] = STRADDR_LO(DC_STRDESC_STRING0); + info[XHCI_DCDBCIC_STR0DESC_HI] = STRADDR_HI(DC_STRDESC_STRING0); + info[XHCI_DCDBCIC_MANUDESC_LO] = STRADDR_LO(DC_STRDESC_MANUFACTURER); + info[XHCI_DCDBCIC_MANUDESC_HI] = STRADDR_HI(DC_STRDESC_MANUFACTURER); + info[XHCI_DCDBCIC_PRODDESC_LO] = STRADDR_LO(DC_STRDESC_PRODUCT); + info[XHCI_DCDBCIC_PRODDESC_HI] = STRADDR_HI(DC_STRDESC_PRODUCT); + info[XHCI_DCDBCIC_SERIALDESC_LO] = STRADDR_LO(DC_STRDESC_SERIAL); + info[XHCI_DCDBCIC_SERIALDESC_HI] = STRADDR_HI(DC_STRDESC_SERIAL); + info[XHCI_DCDBCIC_DESCLEN] = + XHCI_DCDBCIC_STR0DESC_LEN_SET(strdesc[DC_STRDESC_STRING0].bLength) | + XHCI_DCDBCIC_MANUDESC_LEN_SET(strdesc[DC_STRDESC_MANUFACTURER].bLength) | + XHCI_DCDBCIC_PRODDESC_LEN_SET(strdesc[DC_STRDESC_PRODUCT].bLength) | + XHCI_DCDBCIC_SERIALDESC_LEN_SET(strdesc[DC_STRDESC_SERIAL].bLength); + +#undef STRADDR0 +#undef STRADDR_LO +#undef STRADDR_HI +} + +static void +xhci_debug_ring_doorbell(struct xhci_debug_softc *sc, uint32_t target) +{ + uint32_t doorbell; + + /* The other bits in DCDB are RsvdP. */ + /* XXXHRS: the spec has contradiction between Figure 7-9 and Table 7-18. */ + doorbell = _XREAD4(sc, dbc, XHCI_DCDB) & ~XHCI_DCDB_MASK; + doorbell |= target; + _XWRITE4(sc, dbc, XHCI_DCDB, doorbell); + + DEBUG_PRINTF(2, ("%s: doorbell = %04x\n", __func__, doorbell)); +} + +uint64_t +xhci_debug_bulk_transfer(struct xhci_debug_softc *sc) +{ + return ( + xhci_debug_bulk_transfer0(sc, &sc->udb_iring) + + xhci_debug_bulk_transfer0(sc, &sc->udb_oring)); +} + +static uint64_t +xhci_debug_bulk_transfer0(struct xhci_debug_softc *sc, + struct xhci_debug_ring *ring) +{ + uint64_t len; + uint32_t temp; + + xhci_debug_event_dequeue(sc); + + len = 0; + if (mtx_trylock_spin(&sc->udb_ering.mtx) == 0) + goto end0; + if (mtx_trylock_spin(&ring->mtx) == 0) + goto end1; + if (XHCI_DC_RING_FULL(ring)) /* TRB queue is full */ + goto end2; + /* + * DRC bit is asserted when it exits the CONFIGURED state. + * Clear the bit because DCDB is disabled until clearing the DRC bit. + * (sec 7.6.8.4) + * The timeout is ignored because this is a best-effort attempt. + */ + temp = _XREAD4(sc, dbc, XHCI_DCCTRL); + if ((le32toh(temp) & XHCI_DCCTRL_DRC) != 0) + xhci_debug_reset_regbit(sc, XHCI_DCCTRL, XHCI_DCCTRL_DRC, 1); + /* + * Updating the state in bulk_transfer() is critical in loader. + * Do not remove this. + */ + xhci_debug_update_state(sc); + + len = (XHCI_DC_RING_IN(ring)) + ? trb_rx_enqueue_locked(sc, ring) + : trb_tx_enqueue_locked(sc, ring); +end2: + mtx_unlock_spin(&ring->mtx); +end1: + mtx_unlock_spin(&sc->udb_ering.mtx); +end0: + return (len); +} + +/* + * Initializes the endpoint as specified in sections 7.6.3.2 and 7.6.9.2. + * Each endpoint is Bulk, so the MaxPStreams, LSA, HID, CErr, FE, + * Interval, Mult, and Max ESIT Payload fields are all 0. + * + * Max packet size: 1024 + * Max burst size: mbs + * EP type: 2 for OUT bulk, 6 for IN bulk + * TR dequeue ptr: physical base address of transfer ring + * Avg TRB length: software defined (see 4.14.1.1 for suggested defaults, 3kB) + */ +static void +xhci_debug_init_ep(struct xhci_endp_ctx64 *ep64, uint64_t mbs, + uint32_t type, uint64_t ring_paddr) +{ + struct xhci_endp_ctx *ep; + + if (ep64 == NULL) + return; + ep = &ep64->ctx; + memset(ep64, 0, sizeof(*ep64)); + + /* Do we need XHCI_EPCTX_1_CERR_SET(3) in Sec 6.2.3 ? */ + ep->dwEpCtx1 = htole32( + XHCI_EPCTX_1_MAXP_SIZE_SET(1024) | + XHCI_EPCTX_1_MAXB_SET((uint32_t)mbs) | + XHCI_EPCTX_1_EPTYPE_SET(type)); + ep->qwEpCtx2 = htole64( + (ring_paddr & XHCI_EPCTX_2_TR_DQ_PTR_MASK) | + XHCI_EPCTX_2_DCS_SET(1)); + ep->dwEpCtx4 = htole32( + XHCI_EPCTX_4_AVG_TRB_LEN_SET(1024 * 3)); +} + +#if 0 +static bool +xhci_debug_event_ready(struct xhci_debug_softc *sc) +{ + uint32_t temp; + + /* Seems inaccurate */ + temp = _XREAD4(sc, dbc, XHCI_DCST); + return ((le32toh(temp) & XHCI_DCST_ER) == XHCI_DCST_ER); +} +#endif + +static uint32_t +xhci_debug_ring_boundary(struct xhci_debug_ring *ring, bool update_enq) +{ + struct xhci_trb *trb = &ring->trb[ring->deq]; + uint32_t dwTrb3; + uint32_t cyc; + uint32_t deq; + + cyc = ring->cyc; + deq = ring->deq; + rmb(); + dwTrb3 = le32toh(trb->dwTrb3); + + while ((dwTrb3 & XHCI_TRB_3_CYCLE_BIT) == cyc) { + cyc = (deq == DC_TRB_RING_LAST) + ? cyc ^ 1 + : cyc; + deq = (deq + 1) & DC_TRB_RING_OFFSET_MASK; + trb = &ring->trb[deq]; + rmb(); + dwTrb3 = le32toh(trb->dwTrb3); + } + if (update_enq) + ring->enq = deq; + + return (deq); +} + +static void +xhci_debug_transfer_event_handler(struct xhci_debug_softc *sc, + uint64_t qwTrb0, uint32_t dwTrb2, uint32_t dwTrb3) +{ + struct xhci_trb *trb; + struct xhci_debug_ring *ring; + uint64_t qwEpCtx2; + uint32_t epid; + uint32_t error, len; + uint32_t deq_old; + + /* EP ID is an index of context. */ + epid = XHCI_TRB_3_EP_GET(dwTrb3); + error = XHCI_TRB_2_ERROR_GET(dwTrb2); + + switch (epid) { + case XHCI_DC_EPID_OUT: + case XHCI_DC_EPID_OUT_INTEL: + ring = &sc->udb_oring; + break; + case XHCI_DC_EPID_IN: + case XHCI_DC_EPID_IN_INTEL: + ring = &sc->udb_iring; + break; + default: + ring = &sc->udb_ering; + } + ring->lasterror = error; + + if (error != XHCI_TRB_ERROR_SUCCESS && + error != XHCI_TRB_ERROR_SHORT_PKT) { + DEBUG_PRINTF(1, ("%s: transfer error (%u)", __func__, error)); + ring->ec[error]++; + + switch (error) { + case XHCI_TRB_ERROR_RING_UNDERRUN: + DEBUG_PRINTF(1, (": RING_UNDERRUN\n")); + break; + case XHCI_TRB_ERROR_RING_OVERRUN: + DEBUG_PRINTF(1, (": RING_OVERRUN\n")); + break; + case XHCI_TRB_ERROR_TRB: + DEBUG_PRINTF(1, (": TRB\n")); + break; + case XHCI_TRB_ERROR_STALL: + DEBUG_PRINTF(1, (": STALL\n")); + break; + default: + DEBUG_PRINTF(1, ("\n")); + } + } + if (ring != &sc->udb_ering) + mtx_lock_spin(&ring->mtx); + if (epid == XHCI_DC_EPID_OUT || epid == XHCI_DC_EPID_OUT_INTEL || + epid == XHCI_DC_EPID_IN || epid == XHCI_DC_EPID_IN_INTEL) { + if ((dwTrb3 & XHCI_TRB_3_ISP_BIT) != 0) { + /* Event Data */ + goto end; + } + deq_old = ring->deq; + rmb(); + + if (error == XHCI_TRB_ERROR_STALL) { + /* Halted state (sec 7.6.4.3) */ + /* Update the deq pointer using ctx. */ + + rmb(); + qwEpCtx2 = (ring == &sc->udb_oring) + ? le64toh(sc->udb_ctx-> + ep_out.ctx.qwEpCtx2) & + XHCI_EPCTX_2_TR_DQ_PTR_MASK + : le64toh(sc->udb_ctx-> + ep_in.ctx.qwEpCtx2) & + XHCI_EPCTX_2_TR_DQ_PTR_MASK; + + ring->deq = qwEpCtx2 - (uint64_t)ring->paddr; + ring->deq /= sizeof(struct xhci_trb); + ring->deq &= DC_TRB_RING_OFFSET_MASK; + /* + * XXXHRS: Rollback the enqueued TRB. + * + * I am unsure of whether this is correct or not, but + * the enqueued old TRB will not be processed or + * generate another event TRB with + * XHCI_TRB_ERROR_SUCCESS after this event, + * while the internal dequeue pointer seems to remain. + * Enqueuing another TRB into the same address + * and ringing the doorbell seems to work. + */ + ring->enq = ring->deq; + IOCBITMAP_CLEAR(ring, ring->enq); + + trb = &ring->trb[ring->deq]; + } else if (error == XHCI_TRB_ERROR_INVALID) { + + /* do nothing */ + + trb = NULL; + } else if (error == XHCI_TRB_ERROR_SUCCESS && + XHCI_TRB_2_BYTES_GET(dwTrb2) == 0 && qwTrb0 == 0) { + /* + * Use the current deq value + * because Transfer Event TRBs with + * zero length have no valid TRB + * pointer. + * XXXHRS: where is this in the spec? + */ + + /* + * Use the next of deq_old because the request TRB + * seems be ignored in this case. + */ + ring->deq = deq_old + 1; + ring->deq &= DC_TRB_RING_OFFSET_MASK; + IOCBITMAP_CLEAR(ring, ring->deq); + + ring->lastdeq = ring->deq; + trb = NULL; + } else { + /* Update the deq pointer using ER. */ + /* XXX: should be optimized. */ + /* (qwTrb0 & MASK) >> 4 (16 bytes) ? */ + + ring->deq = qwTrb0 - (uint64_t)ring->paddr; + ring->deq /= sizeof(struct xhci_trb); + ring->deq &= DC_TRB_RING_OFFSET_MASK; + + IOCBITMAP_CLEAR(ring, ring->deq); + ring->lastdeq = ring->deq; + trb = &ring->trb[ring->deq]; + + /* + * XXX: Workaround for Forward jump. This is + * the biggest problem. Dequeue pointers reported + * in event TRBs are not sometimes monotonic. + * If a jump is detected, use the +1 address instead. + */ + if (ring->deq < deq_old && + 1 < (ring->deq + DC_TRB_RING_LEN) - deq_old) { + ring->deq = deq_old + 1; + ring->deq &= DC_TRB_RING_OFFSET_MASK; + } else if (deq_old < ring->deq && + 1 < ring->deq - deq_old) { + + ring->deq = deq_old + 1; + ring->deq &= DC_TRB_RING_OFFSET_MASK; + } + } + } + /* debug printf() */ + if (epid == XHCI_DC_EPID_OUT || epid == XHCI_DC_EPID_OUT_INTEL || + epid == XHCI_DC_EPID_IN || epid == XHCI_DC_EPID_IN_INTEL) { + if ((error == XHCI_TRB_ERROR_SUCCESS || + error == XHCI_TRB_ERROR_SHORT_PKT) && + trb != NULL) { + len = XHCI_TRB_2_BYTES_GET(le32toh(trb->dwTrb2)) + - XHCI_TRB_2_BYTES_GET(dwTrb2); + + DEBUG_PRINTF(2, ("%s: er->deq=%u, transfer length = " + "%u - %u = %u\n", + __func__, sc->udb_ering.deq, + XHCI_TRB_2_BYTES_GET(le32toh(trb->dwTrb2)), + XHCI_TRB_2_BYTES_GET(dwTrb2), + len)); + } + } + if (epid == XHCI_DC_EPID_IN || epid == XHCI_DC_EPID_IN_INTEL) { + if (error == XHCI_TRB_ERROR_SUCCESS || + error == XHCI_TRB_ERROR_SHORT_PKT) { + /* + * The new dequeue pointer is the next of + * the reported one. + */ + if (!XHCI_DC_RING_EMPTY(ring)) + ring->deq = (ring->deq + 1) & + DC_TRB_RING_OFFSET_MASK; + + if (trb != NULL) { + len = XHCI_TRB_2_BYTES_GET(le32toh(trb->dwTrb2)) + - XHCI_TRB_2_BYTES_GET(dwTrb2); + + /* Advance the enq of work q */ + flush_range(&ring->work.buf[ring->work.w_enq], + len); + ring->work.w_enq = (ring->work.w_enq + len) + & DC_WORK_RING_OFFSET_MASK; + } + } + } + if (epid == XHCI_DC_EPID_OUT || epid == XHCI_DC_EPID_OUT_INTEL || + epid == XHCI_DC_EPID_IN || epid == XHCI_DC_EPID_IN_INTEL) { + /* printf() debug */ + } + if (epid != XHCI_DC_EPID_OUT && epid != XHCI_DC_EPID_OUT_INTEL && + epid != XHCI_DC_EPID_IN && epid != XHCI_DC_EPID_IN_INTEL) { + ring->ec[XHCI_TRB_ERROR_UNKNOWN]++; + } +end: + if (ring != &sc->udb_ering) + mtx_unlock_spin(&ring->mtx); +} + +void +xhci_debug_event_dequeue(struct xhci_debug_softc *sc) +{ + struct xhci_debug_ring *er; + struct xhci_trb *ev; + uint64_t qwTrb0; + uint32_t dwTrb3, dwTrb2; + uint64_t erdp; + bool erdp_update; + uint32_t temp; + + er = &sc->udb_ering; + er->ec[XHCI_TRB_ERROR_CALLED]++; + if (mtx_trylock_spin(&er->mtx) == 0) { + er->ec[XHCI_TRB_ERROR_EVLOCKED]++; + return; + } + + ev = &er->trb[er->deq]; + sc->sc_dequeue_count++; + erdp_update = false; + + rmb(); + qwTrb0 = le64toh(ev->qwTrb0); + dwTrb2 = le32toh(ev->dwTrb2); + dwTrb3 = le32toh(ev->dwTrb3); + while ((dwTrb3 & XHCI_TRB_3_CYCLE_BIT) == er->cyc) { + switch (XHCI_TRB_3_TYPE_GET(dwTrb3)) { + case XHCI_TRB_EVENT_TRANSFER: + xhci_debug_transfer_event_handler(sc, qwTrb0, dwTrb2, + dwTrb3); + break; + case XHCI_TRB_EVENT_PORT_STS_CHANGE: + temp = _XREAD4(sc, dbc, XHCI_DCPORTSC); + if (XHCI_DCPORTSC_CSC_GET(le32toh(temp))) { + device_printf(sc->sc_dev, + "DbC cable status changed%s\n", + (XHCI_DCPORTSC_CCS_GET(le32toh(temp))) + ? "" + : ": unplugged"); + } + _XWRITE4(sc, dbc, XHCI_DCPORTSC, + temp & htole32(XHCI_DCPORTSC_ACK_MASK)); + break; + default: + er->ec[XHCI_TRB_ERROR_UNKNOWN]++; + + DEBUG_PRINTF(1, + ("%s: er->deq=%u" + ", er: qwTrb0=%lx" + ", dwTrb2=%x" + ", dwTrb3=%x" + ", type=%d (unknown)" + ", ec_unknown=%lu\n", + __func__, + er->deq, + qwTrb0, + dwTrb2, + dwTrb3, + XHCI_TRB_3_TYPE_GET(dwTrb3), + er->ec[XHCI_TRB_ERROR_UNKNOWN])); + break; + } + if (er->deq == DC_TRB_RING_LAST) + er->cyc ^= 1; + er->deq = (er->deq + 1) & DC_TRB_RING_OFFSET_MASK; + erdp_update = true; + + ev = &er->trb[er->deq]; + rmb(); + qwTrb0 = le64toh(ev->qwTrb0); + dwTrb2 = le32toh(ev->dwTrb2); + dwTrb3 = le32toh(ev->dwTrb3); + } + if (erdp_update) { + erdp = _XREAD44LH(sc, dbc, XHCI_DCERDP); + erdp &= ~DC_TRB_RING_MASK; + erdp |= er->deq * sizeof(struct xhci_trb); + _XWRITE44LH(sc, dbc, XHCI_DCERDP, erdp); + } + mtx_unlock_spin(&er->mtx); +} + +static uint64_t +trb_rx_enqueue_locked(struct xhci_debug_softc *sc, struct xhci_debug_ring *ring) +{ + struct xhci_debug_work_ring *work; + uint64_t addr; + uint64_t len; + + if (ring == NULL) + return (0); + if ((work = &ring->work) == NULL) + return (0); + /* Skip if in-progress */ + if (ring->flags != 0) + return (0); + /* Skip if there are pending TRB */ + if (!XHCI_DC_RING_EMPTY(ring)) + return (0); + /* Skip if there are received data */ + if (!DC_WORK_RING_EMPTY(ring)) + return (0); + + addr = work->paddr + work->w_enq; + len = (work->w_enq < work->w_deq) ? work->w_deq : DC_WORK_RING_LEN; + len -= work->w_enq; + + return (trb_push_locked(sc, ring, addr, len)); +} + +static uint64_t +trb_tx_enqueue_locked(struct xhci_debug_softc *sc, struct xhci_debug_ring *ring) +{ + struct xhci_debug_work_ring *work; + uint64_t addr; + uint64_t len0, len1; + + if (ring == NULL) + return (0); + if ((work = &ring->work) == NULL) + return (0); + if (ring->flags != 0) + return (0); + if (DC_WORK_RING_EMPTY(ring)) + return (0); + + /* XXX: pending check? */ + + len0 = len1 = 0; + if (work->w_deq < work->w_enq) { + addr = work->paddr + work->w_deq; + len0 = trb_push_locked(sc, ring, addr, work->w_enq - work->w_deq); + + work->w_deq += len0; + } else { + addr = work->paddr + work->w_deq; + len0 = trb_push_locked(sc, ring, addr, + DC_WORK_RING_LEN - work->w_deq); + + work->w_deq += len0; + work->w_deq &= DC_WORK_RING_OFFSET_MASK; + + if (work->w_deq == 0 && + !DC_WORK_RING_EMPTY(ring) && + !XHCI_DC_RING_FULL(ring)) { + addr = work->paddr; + len1 = trb_push_locked(sc, ring, addr, work->w_enq); + + work->w_deq += len1; + } + } + return (len0 + len1); +} + +static uint64_t +trb_push_locked(struct xhci_debug_softc *sc, struct xhci_debug_ring *ring, + uint64_t addr, uint64_t len) +{ + struct xhci_trb trb; + + if (ring->enq == DC_TRB_RING_LAST) { + /* + * If it is the last entry, whose TRB type is LINK, + * cyc is set and ring->enq is advanced. + * The next enq should always be zero. + */ + ring->trb[ring->enq].dwTrb3 &= ~htole32(XHCI_TRB_3_CYCLE_BIT); + ring->trb[ring->enq].dwTrb3 |= htole32(ring->cyc); + flush_range(&ring->trb[ring->enq], sizeof(ring->trb[0])); + + /* Must be zero */ + ring->enq = (ring->enq + 1) & DC_TRB_RING_OFFSET_MASK; + ring->cyc ^= 1; + } + trb = (struct xhci_trb){ + .qwTrb0 = htole64(addr), + .dwTrb2 = htole32(XHCI_TRB_2_BYTES_SET((uint32_t)len)), + .dwTrb3 = htole32( + XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL) + | ring->cyc | XHCI_TRB_3_IOC_BIT + ) + }; + + /* XXX: overwrite check of iocbitmap? */ + + /* Note that the last entry never have a bit in the iocbitmap. */ + IOCBITMAP_SET(ring, ring->enq); + ring->lastenq = ring->enq; + + DEBUG_PRINTF(1, ("%s: enqueue: %s: enq=%d, len=%lu, deq=%d\n", __func__, + label, ring->enq, len, ring->deq)); + memcpy(&ring->trb[ring->enq], &trb, sizeof(ring->trb[0])); + flush_range(&ring->trb[ring->enq], sizeof(ring->trb[0])); + ring->enq = (ring->enq + 1) & DC_TRB_RING_OFFSET_MASK; + + xhci_debug_ring_doorbell(sc, ring->doorbell); + + return (len); +} + +int +work_dequeue(struct xhci_debug_ring *ring) +{ + struct xhci_debug_work_ring *work; + int c; + + if (ring == NULL) + return (-1); + if ((work = &ring->work) == NULL) + return (-1); + + c = -1; + mtx_lock_spin(&ring->mtx); + if (!DC_WORK_RING_EMPTY(ring)) { + c = (int)work->buf[work->w_deq]; + work->w_deq = (work->w_deq + 1) & DC_WORK_RING_OFFSET_MASK; + DEBUG_PRINTF(2, ("%s: [%c(%02x)]\n", __func__, + (char)c, (char)c)); + } + mtx_unlock_spin(&ring->mtx); + return (c); +} + +int64_t +work_enqueue(struct xhci_debug_ring *ring, const char *buf, int64_t len) +{ + struct xhci_debug_work_ring *work; + uint32_t start, end; + int64_t i; + + if (ring == NULL) + return (0); + if ((work = &ring->work) == NULL) + return (0); + + i = 0; + end = 0; + mtx_lock_spin(&ring->mtx); + if (DC_WORK_RING_SLOTS(work) < len) { + ring->ec[XHCI_TRB_ERROR_WORKQ_FULL]++; + goto end; + } + start = work->w_enq; + DEBUG_PRINTF(1, ("%s: %s: len=%lu: ", __func__, + XHCI_DC_RING_IN(ring) ? "IR" : "OR", len)); + while (!DC_WORK_RING_FULL(ring) && i < len) { + DEBUG_PRINTF(2, ("[%c(%02x)]", buf[i], buf[i])); + work->buf[work->w_enq++] = buf[i++]; + work->w_enq &= DC_WORK_RING_OFFSET_MASK; + } + end = work->w_enq; + DEBUG_PRINTF(1, (": start=%u, end=%u\n", start, end)); + + if (start < end) + flush_range(&work->buf[start], end - start); + else if (0 < i) { + flush_range(&work->buf[start], DC_WORK_RING_LEN - start); + flush_range(&work->buf[0], end); + } +end: + mtx_unlock_spin(&ring->mtx); + return (i); +} diff --git a/sys/dev/usb/controller/xhci_dbc_private.h b/sys/dev/usb/controller/xhci_dbc_private.h new file mode 100644 --- /dev/null +++ b/sys/dev/usb/controller/xhci_dbc_private.h @@ -0,0 +1,101 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +extern int dbc_debug; + +#if _KERNEL + +#define DEBUG_PRINTF(level, args )do { \ + if (dbc_debug >= level) printf args; \ + } while (0) + +/* PCI register access via bus_dma in kernel */ +#define _XREAD4(sc, what, a) XREAD4((sc)->sc_xhci, what, a) +#define _XWRITE4(sc, what, a, x) XWRITE4((sc)->sc_xhci, what, a, (x)) +#define _XREAD44LH(sc, what, a) XREAD44LH((sc)->sc_xhci, what, a) +#define _XWRITE44LH(sc, what, a, x) XWRITE44LH((sc)->sc_xhci, what, a, (x)) +/* sys/systm.h */ +#define delay(x) DELAY(x) + +#else + +#define device_printf(dev, ...) \ + printf(__VA_ARGS__) + +/* PCI register access via UEFI service in loader */ +static inline volatile uint32_t +_XREAD4_0(struct xhci_debug_softc *sc, uint64_t offset) +{ + volatile uint32_t temp; + + sc->sc_efi_pciio->Mem.Read( + sc->sc_efi_pciio, + EfiPciIoWidthUint32, + 0, + offset, + 1, + (VOID *)&temp); + + return (temp); +} +static inline void +_XWRITE4_0(struct xhci_debug_softc *sc, uint64_t offset, uint32_t temp) +{ + sc->sc_efi_pciio->Mem.Write( + sc->sc_efi_pciio, + EfiPciIoWidthUint32, + 0, + offset, + 1, + (VOID *)&temp); +#if 0 + /* + * wmb is defined in atomics.h, which we don't have. On amd64 it will + * issue an sfench. I am not entirely sure if this is needed or if it + * is the correct ordering primitive. + * + * The pci memory is mapped as cacheable for the dma structures and the + * softc (EFI_PCI_ATTRIBUTE_MEMORY_CACHED). + * + * On my test systems this functions as far as I can tell. + */ + wmb(); +#endif +} +#define _XREAD4(sc, what, a) \ + _XREAD4_0(sc, (a) + (sc)->sc_##what##_off) +#define _XREAD44LH(sc, what, a) \ + ((volatile uint64_t)_XREAD4(sc, what, a##_LO) | \ + ((volatile uint64_t)_XREAD4(sc, what, a##_HI) << 32)) +#define _XWRITE4(sc, what, a, x) \ + _XWRITE4_0(sc, (a) + (sc)->sc_##what##_off, (x)) +#define _XWRITE44LH(sc, what, a, x) \ + do { \ + _XWRITE4(sc, what, a##_LO, (uint32_t)((x) & 0xffffffff)); \ + _XWRITE4(sc, what, a##_HI, (uint32_t)((x) >> 32)); \ + } while (0) +#endif diff --git a/sys/dev/usb/controller/xhci_pci.h b/sys/dev/usb/controller/xhci_pci.h new file mode 100644 --- /dev/null +++ b/sys/dev/usb/controller/xhci_pci.h @@ -0,0 +1,187 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2010-2022 Hans Petter Selasky + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PCI_XHCI_VENDORID_AMD 0x1022 +#define PCI_XHCI_VENDORID_INTEL 0x8086 +#define PCI_XHCI_VENDORID_VMWARE 0x15ad +#define PCI_XHCI_VENDORID_ZHAOXIN 0x1d17 + +static inline const char * +xhci_pci_match(device_t self) +{ + uint32_t device_id = pci_get_devid(self); + + switch (device_id) { + case 0x145c1022: + return ("AMD KERNCZ USB 3.0 controller"); + case 0x148c1022: + return ("AMD Starship USB 3.0 controller"); + case 0x149c1022: + return ("AMD Matisse USB 3.0 controller"); + case 0x15e01022: + case 0x15e11022: + return ("AMD Raven USB 3.1 controller"); + case 0x43ba1022: + return ("AMD X399 USB 3.0 controller"); + case 0x43b91022: /* X370 */ + case 0x43bb1022: /* B350 */ + return ("AMD 300 Series USB 3.1 controller"); + case 0x43d51022: + return ("AMD 400 Series USB 3.1 controller"); + case 0x78121022: + case 0x78141022: + case 0x79141022: + return ("AMD FCH USB 3.0 controller"); + + case 0x077815ad: + case 0x077915ad: + return ("VMware USB 3.0 controller"); + + case 0x145f1d94: + return ("Hygon USB 3.0 controller"); + + case 0x01941033: + return ("NEC uPD720200 USB 3.0 controller"); + case 0x00151912: + return ("NEC uPD720202 USB 3.0 controller"); + + case 0x10001b73: + return ("Fresco Logic FL1000G USB 3.0 controller"); + case 0x10091b73: + return ("Fresco Logic FL1009 USB 3.0 controller"); + case 0x11001b73: + return ("Fresco Logic FL1100 USB 3.0 controller"); + + case 0x10421b21: + return ("ASMedia ASM1042 USB 3.0 controller"); + case 0x11421b21: + return ("ASMedia ASM1042A USB 3.0 controller"); + case 0x13431b21: + return ("ASMedia ASM1143 USB 3.1 controller"); + case 0x32421b21: + return ("ASMedia ASM3242 USB 3.2 controller"); + + case 0x02ed8086: + return ("Intel Comet Lake PCH-LP USB 3.1 controller"); + case 0x0b278086: + return ("Intel Goshen Ridge Thunderbolt 4 USB controller"); + case 0x0f358086: + return ("Intel BayTrail USB 3.0 controller"); + case 0x11388086: + return ("Intel Maple Ridge Thunderbolt 4 USB controller"); + case 0x15c18086: + case 0x15d48086: + case 0x15db8086: + return ("Intel Alpine Ridge Thunderbolt 3 USB controller"); + case 0x15e98086: + case 0x15ec8086: + case 0x15f08086: + return ("Intel Titan Ridge Thunderbolt 3 USB controller"); + case 0x19d08086: + return ("Intel Denverton USB 3.0 controller"); + case 0x9c318086: + case 0x1e318086: + return ("Intel Panther Point USB 3.0 controller"); + case 0x22b58086: + return ("Intel Braswell USB 3.0 controller"); + case 0x31a88086: + return ("Intel Gemini Lake USB 3.0 controller"); + case 0x34ed8086: + return ("Intel Ice Lake-LP USB 3.1 controller"); + case 0x43ed8086: + return ("Intel Tiger Lake-H USB 3.2 controller"); + case 0x461e8086: + return ("Intel Alder Lake-P Thunderbolt 4 USB controller"); + case 0x51ed8086: + case 0x54ed8086: + case 0x5fed8086: + return ("Intel Alder Lake PCH USB 3.2 controller"); + case 0x5aa88086: + return ("Intel Apollo Lake USB 3.0 controller"); + case 0x7ae08086: + return ("Intel Alder Lake USB 3.2 controller"); + case 0x8a138086: + return ("Intel Ice Lake Thunderbolt 3 USB controller"); + case 0x8c318086: + return ("Intel Lynx Point USB 3.0 controller"); + case 0x8cb18086: + return ("Intel Wildcat Point USB 3.0 controller"); + case 0x8d318086: + return ("Intel Wellsburg USB 3.0 controller"); + case 0x9a138086: + return ("Intel Tiger Lake-LP Thunderbolt 4 USB controller"); + case 0x9a178086: + return ("Intel Tiger Lake-H Thunderbolt 4 USB controller"); + case 0x9cb18086: + return ("Broadwell Integrated PCH-LP chipset USB 3.0 controller"); + case 0x9d2f8086: + return ("Intel Sunrise Point-LP USB 3.0 controller"); + case 0xa0ed8086: + return ("Intel Tiger Lake-LP USB 3.2 controller"); + case 0xa12f8086: + return ("Intel Sunrise Point USB 3.0 controller"); + case 0xa1af8086: + return ("Intel Lewisburg USB 3.0 controller"); + case 0xa2af8086: + return ("Intel Union Point USB 3.0 controller"); + case 0xa36d8086: + return ("Intel Cannon Lake USB 3.1 controller"); + case 0xa71e8086: + return ("Intel Raptor Lake-P Thunderbolt 4 USB Controller"); + + case 0xa01b177d: + return ("Cavium ThunderX USB 3.0 controller"); + + case 0x1ada10de: + return ("NVIDIA TU106 USB 3.1 controller"); + + case 0x92021d17: + return ("Zhaoxin ZX-100 USB 3.0 controller"); + case 0x92031d17: + return ("Zhaoxin ZX-200 USB 3.0 controller"); + case 0x92041d17: + return ("Zhaoxin ZX-E USB 3.0 controller"); + + default: + break; + } + + if ((pci_get_class(self) == PCIC_SERIALBUS) + && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) + && (pci_get_progif(self) == PCIP_SERIALBUS_USB_XHCI)) { + return ("XHCI (generic) USB 3.0 controller"); + } + return (NULL); /* dunno */ +} diff --git a/sys/dev/usb/controller/xhci_pci.c b/sys/dev/usb/controller/xhci_pci.c --- a/sys/dev/usb/controller/xhci_pci.c +++ b/sys/dev/usb/controller/xhci_pci.c @@ -56,14 +56,10 @@ #include #include #include +#include #include #include "usb_if.h" -#define PCI_XHCI_VENDORID_AMD 0x1022 -#define PCI_XHCI_VENDORID_INTEL 0x8086 -#define PCI_XHCI_VENDORID_VMWARE 0x15ad -#define PCI_XHCI_VENDORID_ZHAOXIN 0x1d17 - static device_probe_t xhci_pci_probe; static device_detach_t xhci_pci_detach; static usb_take_controller_t xhci_pci_take_controller; @@ -87,157 +83,6 @@ DRIVER_MODULE(xhci, pci, xhci_pci_driver, NULL, NULL); MODULE_DEPEND(xhci, usb, 1, 1, 1); -static const char * -xhci_pci_match(device_t self) -{ - uint32_t device_id = pci_get_devid(self); - - switch (device_id) { - case 0x145c1022: - return ("AMD KERNCZ USB 3.0 controller"); - case 0x148c1022: - return ("AMD Starship USB 3.0 controller"); - case 0x149c1022: - return ("AMD Matisse USB 3.0 controller"); - case 0x15b61022: - case 0x15b71022: - return ("AMD Raphael/Granite Ridge USB 3.1 controller"); - case 0x15b81022: - return ("AMD Raphael/Granite Ridge USB 2.0 controller"); - case 0x15e01022: - case 0x15e11022: - return ("AMD Raven USB 3.1 controller"); - case 0x43ba1022: - return ("AMD X399 USB 3.0 controller"); - case 0x43b91022: /* X370 */ - case 0x43bb1022: /* B350 */ - return ("AMD 300 Series USB 3.1 controller"); - case 0x43d51022: - return ("AMD 400 Series USB 3.1 controller"); - case 0x43f71022: - return ("AMD 600 Series USB 3.2 controller"); - case 0x78121022: - case 0x78141022: - case 0x79141022: - return ("AMD FCH USB 3.0 controller"); - - case 0x077815ad: - case 0x077915ad: - return ("VMware USB 3.0 controller"); - - case 0x145f1d94: - return ("Hygon USB 3.0 controller"); - - case 0x01941033: - return ("NEC uPD720200 USB 3.0 controller"); - case 0x00151912: - return ("NEC uPD720202 USB 3.0 controller"); - - case 0x10001b73: - return ("Fresco Logic FL1000G USB 3.0 controller"); - case 0x10091b73: - return ("Fresco Logic FL1009 USB 3.0 controller"); - case 0x11001b73: - return ("Fresco Logic FL1100 USB 3.0 controller"); - - case 0x10421b21: - return ("ASMedia ASM1042 USB 3.0 controller"); - case 0x11421b21: - return ("ASMedia ASM1042A USB 3.0 controller"); - case 0x13431b21: - return ("ASMedia ASM1143 USB 3.1 controller"); - case 0x32421b21: - return ("ASMedia ASM3242 USB 3.2 controller"); - - case 0x0b278086: - return ("Intel Goshen Ridge Thunderbolt 4 USB controller"); - case 0x0f358086: - return ("Intel BayTrail USB 3.0 controller"); - case 0x11388086: - return ("Intel Maple Ridge Thunderbolt 4 USB controller"); - case 0x15c18086: - case 0x15d48086: - case 0x15db8086: - return ("Intel Alpine Ridge Thunderbolt 3 USB controller"); - case 0x15e98086: - case 0x15ec8086: - case 0x15f08086: - return ("Intel Titan Ridge Thunderbolt 3 USB controller"); - case 0x19d08086: - return ("Intel Denverton USB 3.0 controller"); - case 0x9c318086: - case 0x1e318086: - return ("Intel Panther Point USB 3.0 controller"); - case 0x22b58086: - return ("Intel Braswell USB 3.0 controller"); - case 0x31a88086: - return ("Intel Gemini Lake USB 3.0 controller"); - case 0x34ed8086: - return ("Intel Ice Lake-LP USB 3.1 controller"); - case 0x43ed8086: - return ("Intel Tiger Lake-H USB 3.2 controller"); - case 0x461e8086: - return ("Intel Alder Lake-P Thunderbolt 4 USB controller"); - case 0x4b7d8086: - return ("Intel Elkhart Lake USB 3.1 controller"); - case 0x51ed8086: - return ("Intel Alder Lake USB 3.2 controller"); - case 0x5aa88086: - return ("Intel Apollo Lake USB 3.0 controller"); - case 0x7ae08086: - return ("Intel Alder Lake USB 3.2 controller"); - case 0x8a138086: - return ("Intel Ice Lake Thunderbolt 3 USB controller"); - case 0x8c318086: - return ("Intel Lynx Point USB 3.0 controller"); - case 0x8cb18086: - return ("Intel Wildcat Point USB 3.0 controller"); - case 0x8d318086: - return ("Intel Wellsburg USB 3.0 controller"); - case 0x9a138086: - return ("Intel Tiger Lake-LP Thunderbolt 4 USB controller"); - case 0x9a178086: - return ("Intel Tiger Lake-H Thunderbolt 4 USB controller"); - case 0x9cb18086: - return ("Broadwell Integrated PCH-LP chipset USB 3.0 controller"); - case 0x9d2f8086: - return ("Intel Sunrise Point-LP USB 3.0 controller"); - case 0xa0ed8086: - return ("Intel Tiger Lake-LP USB 3.2 controller"); - case 0xa12f8086: - return ("Intel Sunrise Point USB 3.0 controller"); - case 0xa1af8086: - return ("Intel Lewisburg USB 3.0 controller"); - case 0xa2af8086: - return ("Intel Union Point USB 3.0 controller"); - case 0xa36d8086: - return ("Intel Cannon Lake USB 3.1 controller"); - - case 0xa01b177d: - return ("Cavium ThunderX USB 3.0 controller"); - - case 0x1ada10de: - return ("NVIDIA TU106 USB 3.1 controller"); - - case 0x92021d17: - return ("Zhaoxin ZX-100 USB 3.0 controller"); - case 0x92031d17: - return ("Zhaoxin ZX-200 USB 3.0 controller"); - case 0x92041d17: - return ("Zhaoxin ZX-E USB 3.0 controller"); - - default: - break; - } - - if ((pci_get_class(self) == PCIC_SERIALBUS) - && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) - && (pci_get_progif(self) == PCIP_SERIALBUS_USB_XHCI)) { - return ("XHCI (generic) USB 3.0 controller"); - } - return (NULL); /* dunno */ -} - static int xhci_pci_probe(device_t self) { diff --git a/sys/dev/usb/controller/xhci_private.h b/sys/dev/usb/controller/xhci_private.h new file mode 100644 --- /dev/null +++ b/sys/dev/usb/controller/xhci_private.h @@ -0,0 +1,228 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2010-2022 Hans Petter Selasky + * Copyright (c) 2020-2024 Hiroki Sato + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _USB_XHCI_PRIVATE_H_ +#define _USB_XHCI_PRIVATE_H_ + +#define XHCI_DEV_CTX_ADDR_ALIGN 64 /* bytes */ +#define XHCI_DEV_CTX_ALIGN 64 /* bytes */ +#define XHCI_INPUT_CTX_ALIGN 64 /* bytes */ +#define XHCI_SLOT_CTX_ALIGN 32 /* bytes */ +#define XHCI_ENDP_CTX_ALIGN 32 /* bytes */ +#define XHCI_STREAM_CTX_ALIGN 16 /* bytes */ +#define XHCI_TRANS_RING_SEG_ALIGN 16 /* bytes */ +#define XHCI_CMD_RING_SEG_ALIGN 64 /* bytes */ +#define XHCI_EVENT_RING_SEG_ALIGN 64 /* bytes */ +#define XHCI_SCRATCH_BUF_ARRAY_ALIGN 64 /* bytes */ +#define XHCI_SCRATCH_BUFFER_ALIGN USB_PAGE_SIZE +#define XHCI_TRB_ALIGN 16 /* bytes */ +#define XHCI_TD_ALIGN 64 /* bytes */ +#define XHCI_PAGE_SIZE 4096 /* bytes */ + +struct xhci_endp_ctx { + volatile uint32_t dwEpCtx0; +#define XHCI_EPCTX_0_EPSTATE_SET(x) ((x) & 0x7) +#define XHCI_EPCTX_0_EPSTATE_GET(x) ((x) & 0x7) +#define XHCI_EPCTX_0_EPSTATE_DISABLED 0 +#define XHCI_EPCTX_0_EPSTATE_RUNNING 1 +#define XHCI_EPCTX_0_EPSTATE_HALTED 2 +#define XHCI_EPCTX_0_EPSTATE_STOPPED 3 +#define XHCI_EPCTX_0_EPSTATE_ERROR 4 +#define XHCI_EPCTX_0_EPSTATE_RESERVED_5 5 +#define XHCI_EPCTX_0_EPSTATE_RESERVED_6 6 +#define XHCI_EPCTX_0_EPSTATE_RESERVED_7 7 +#define XHCI_EPCTX_0_MULT_SET(x) (((x) & 0x3) << 8) +#define XHCI_EPCTX_0_MULT_GET(x) (((x) >> 8) & 0x3) +#define XHCI_EPCTX_0_MAXP_STREAMS_SET(x) (((x) & 0x1F) << 10) +#define XHCI_EPCTX_0_MAXP_STREAMS_GET(x) (((x) >> 10) & 0x1F) +#define XHCI_EPCTX_0_LSA_SET(x) (((x) & 0x1) << 15) +#define XHCI_EPCTX_0_LSA_GET(x) (((x) >> 15) & 0x1) +#define XHCI_EPCTX_0_IVAL_SET(x) (((x) & 0xFF) << 16) +#define XHCI_EPCTX_0_IVAL_GET(x) (((x) >> 16) & 0xFF) + volatile uint32_t dwEpCtx1; +#define XHCI_EPCTX_1_CERR_SET(x) (((x) & 0x3) << 1) +#define XHCI_EPCTX_1_CERR_GET(x) (((x) >> 1) & 0x3) +#define XHCI_EPCTX_1_EPTYPE_SET(x) (((x) & 0x7) << 3) +#define XHCI_EPCTX_1_EPTYPE_GET(x) (((x) >> 3) & 0x7) +#define XHCI_EPCTX_1_HID_SET(x) (((x) & 0x1) << 7) +#define XHCI_EPCTX_1_HID_GET(x) (((x) >> 7) & 0x1) +#define XHCI_EPCTX_1_MAXB_SET(x) (((x) & 0xFF) << 8) +#define XHCI_EPCTX_1_MAXB_GET(x) (((x) >> 8) & 0xFF) +#define XHCI_EPCTX_1_MAXP_SIZE_SET(x) (((x) & 0xFFFF) << 16) +#define XHCI_EPCTX_1_MAXP_SIZE_GET(x) (((x) >> 16) & 0xFFFF) + volatile uint64_t qwEpCtx2; +#define XHCI_EPCTX_2_DCS_SET(x) ((x) & 0x1) +#define XHCI_EPCTX_2_DCS_GET(x) ((x) & 0x1) +#define XHCI_EPCTX_2_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U + volatile uint32_t dwEpCtx4; +#define XHCI_EPCTX_4_AVG_TRB_LEN_SET(x) ((x) & 0xFFFF) +#define XHCI_EPCTX_4_AVG_TRB_LEN_GET(x) ((x) & 0xFFFF) +#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(x) (((x) & 0xFFFF) << 16) +#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_GET(x) (((x) >> 16) & 0xFFFF) + volatile uint32_t dwEpCtx5; + volatile uint32_t dwEpCtx6; + volatile uint32_t dwEpCtx7; +}; + +struct xhci_endp_ctx64 { + struct xhci_endp_ctx ctx; + volatile uint8_t padding[32]; +}; + +struct xhci_trb { + volatile uint64_t qwTrb0; +#define XHCI_TRB_0_DIR_IN_MASK (0x80ULL << 0) +#define XHCI_TRB_0_WLENGTH_MASK (0xFFFFULL << 48) + volatile uint32_t dwTrb2; +#define XHCI_TRB_2_ERROR_GET(x) (((x) >> 24) & 0xFF) +#define XHCI_TRB_2_ERROR_SET(x) (((x) & 0xFF) << 24) +#define XHCI_TRB_2_TDSZ_GET(x) (((x) >> 17) & 0x1F) +#define XHCI_TRB_2_TDSZ_SET(x) (((x) & 0x1F) << 17) +#define XHCI_TRB_2_REM_GET(x) ((x) & 0xFFFFFF) +#define XHCI_TRB_2_REM_SET(x) ((x) & 0xFFFFFF) +#define XHCI_TRB_2_BYTES_GET(x) ((x) & 0x1FFFF) +#define XHCI_TRB_2_BYTES_SET(x) ((x) & 0x1FFFF) +#define XHCI_TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF) +#define XHCI_TRB_2_IRQ_SET(x) (((x) & 0x3FF) << 22) +#define XHCI_TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFFFF) +#define XHCI_TRB_2_STREAM_SET(x) (((x) & 0xFFFF) << 16) + + volatile uint32_t dwTrb3; +#define XHCI_TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F) +#define XHCI_TRB_3_TYPE_SET(x) (((x) & 0x3F) << 10) +#define XHCI_TRB_3_CYCLE_BIT (1U << 0) +#define XHCI_TRB_3_TC_BIT (1U << 1) /* command ring only */ +#define XHCI_TRB_3_ENT_BIT (1U << 1) /* transfer ring only */ +#define XHCI_TRB_3_ISP_BIT (1U << 2) +#define XHCI_TRB_3_NSNOOP_BIT (1U << 3) +#define XHCI_TRB_3_CHAIN_BIT (1U << 4) +#define XHCI_TRB_3_IOC_BIT (1U << 5) +#define XHCI_TRB_3_IDT_BIT (1U << 6) +#define XHCI_TRB_3_TBC_GET(x) (((x) >> 7) & 3) +#define XHCI_TRB_3_TBC_SET(x) (((x) & 3) << 7) +#define XHCI_TRB_3_BEI_BIT (1U << 9) +#define XHCI_TRB_3_DCEP_BIT (1U << 9) +#define XHCI_TRB_3_PRSV_BIT (1U << 9) +#define XHCI_TRB_3_BSR_BIT (1U << 9) +#define XHCI_TRB_3_TRT_MASK (3U << 16) +#define XHCI_TRB_3_TRT_NONE (0U << 16) +#define XHCI_TRB_3_TRT_OUT (2U << 16) +#define XHCI_TRB_3_TRT_IN (3U << 16) +#define XHCI_TRB_3_DIR_IN (1U << 16) +#define XHCI_TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xF) +#define XHCI_TRB_3_TLBPC_SET(x) (((x) & 0xF) << 16) +#define XHCI_TRB_3_EP_GET(x) (((x) >> 16) & 0x1F) +#define XHCI_TRB_3_EP_SET(x) (((x) & 0x1F) << 16) +#define XHCI_TRB_3_FRID_GET(x) (((x) >> 20) & 0x7FF) +#define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20) +#define XHCI_TRB_3_ISO_SIA_BIT (1U << 31) +#define XHCI_TRB_3_SUSP_EP_BIT (1U << 23) +#define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF) +#define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24) + +/* Commands */ +#define XHCI_TRB_TYPE_RESERVED 0x00 +#define XHCI_TRB_TYPE_NORMAL 0x01 +#define XHCI_TRB_TYPE_SETUP_STAGE 0x02 +#define XHCI_TRB_TYPE_DATA_STAGE 0x03 +#define XHCI_TRB_TYPE_STATUS_STAGE 0x04 +#define XHCI_TRB_TYPE_ISOCH 0x05 +#define XHCI_TRB_TYPE_LINK 0x06 +#define XHCI_TRB_TYPE_EVENT_DATA 0x07 +#define XHCI_TRB_TYPE_NOOP 0x08 +#define XHCI_TRB_TYPE_ENABLE_SLOT 0x09 +#define XHCI_TRB_TYPE_DISABLE_SLOT 0x0A +#define XHCI_TRB_TYPE_ADDRESS_DEVICE 0x0B +#define XHCI_TRB_TYPE_CONFIGURE_EP 0x0C +#define XHCI_TRB_TYPE_EVALUATE_CTX 0x0D +#define XHCI_TRB_TYPE_RESET_EP 0x0E +#define XHCI_TRB_TYPE_STOP_EP 0x0F +#define XHCI_TRB_TYPE_SET_TR_DEQUEUE 0x10 +#define XHCI_TRB_TYPE_RESET_DEVICE 0x11 +#define XHCI_TRB_TYPE_FORCE_EVENT 0x12 +#define XHCI_TRB_TYPE_NEGOTIATE_BW 0x13 +#define XHCI_TRB_TYPE_SET_LATENCY_TOL 0x14 +#define XHCI_TRB_TYPE_GET_PORT_BW 0x15 +#define XHCI_TRB_TYPE_FORCE_HEADER 0x16 +#define XHCI_TRB_TYPE_NOOP_CMD 0x17 + +/* Events */ +#define XHCI_TRB_EVENT_TRANSFER 0x20 +#define XHCI_TRB_EVENT_CMD_COMPLETE 0x21 +#define XHCI_TRB_EVENT_PORT_STS_CHANGE 0x22 +#define XHCI_TRB_EVENT_BW_REQUEST 0x23 +#define XHCI_TRB_EVENT_DOORBELL 0x24 +#define XHCI_TRB_EVENT_HOST_CTRL 0x25 +#define XHCI_TRB_EVENT_DEVICE_NOTIFY 0x26 +#define XHCI_TRB_EVENT_MFINDEX_WRAP 0x27 + +/* Error codes */ +#define XHCI_TRB_ERROR_INVALID 0x00 +#define XHCI_TRB_ERROR_SUCCESS 0x01 +#define XHCI_TRB_ERROR_DATA_BUF 0x02 +#define XHCI_TRB_ERROR_BABBLE 0x03 +#define XHCI_TRB_ERROR_XACT 0x04 +#define XHCI_TRB_ERROR_TRB 0x05 +#define XHCI_TRB_ERROR_STALL 0x06 +#define XHCI_TRB_ERROR_RESOURCE 0x07 +#define XHCI_TRB_ERROR_BANDWIDTH 0x08 +#define XHCI_TRB_ERROR_NO_SLOTS 0x09 +#define XHCI_TRB_ERROR_STREAM_TYPE 0x0A +#define XHCI_TRB_ERROR_SLOT_NOT_ON 0x0B +#define XHCI_TRB_ERROR_ENDP_NOT_ON 0x0C +#define XHCI_TRB_ERROR_SHORT_PKT 0x0D +#define XHCI_TRB_ERROR_RING_UNDERRUN 0x0E +#define XHCI_TRB_ERROR_RING_OVERRUN 0x0F +#define XHCI_TRB_ERROR_VF_RING_FULL 0x10 +#define XHCI_TRB_ERROR_PARAMETER 0x11 +#define XHCI_TRB_ERROR_BW_OVERRUN 0x12 +#define XHCI_TRB_ERROR_CONTEXT_STATE 0x13 +#define XHCI_TRB_ERROR_NO_PING_RESP 0x14 +#define XHCI_TRB_ERROR_EV_RING_FULL 0x15 +#define XHCI_TRB_ERROR_INCOMPAT_DEV 0x16 +#define XHCI_TRB_ERROR_MISSED_SERVICE 0x17 +#define XHCI_TRB_ERROR_CMD_RING_STOP 0x18 +#define XHCI_TRB_ERROR_CMD_ABORTED 0x19 +#define XHCI_TRB_ERROR_STOPPED 0x1A +#define XHCI_TRB_ERROR_LENGTH 0x1B +#define XHCI_TRB_ERROR_BAD_MELAT 0x1D +#define XHCI_TRB_ERROR_ISOC_OVERRUN 0x1F +#define XHCI_TRB_ERROR_EVENT_LOST 0x20 +#define XHCI_TRB_ERROR_UNDEFINED 0x21 +#define XHCI_TRB_ERROR_INVALID_SID 0x22 +#define XHCI_TRB_ERROR_SEC_BW 0x23 +#define XHCI_TRB_ERROR_SPLIT_XACT 0x24 +} __aligned(4); + +struct xhci_event_ring_seg { + volatile uint64_t qwEvrsTablePtr; + volatile uint32_t dwEvrsTableSize; + volatile uint32_t dwEvrsReserved; +}; + +#endif /* _USB_XHCI_PRIVATE_H_ */ diff --git a/sys/dev/usb/controller/xhcireg.h b/sys/dev/usb/controller/xhcireg.h --- a/sys/dev/usb/controller/xhcireg.h +++ b/sys/dev/usb/controller/xhcireg.h @@ -211,6 +211,129 @@ #define XHCI_ID_EXT_MSI 0x0011 #define XHCI_ID_USB3_TUN 0x0012 +/* + * XHCI Debug Capability + * From Section 7.6 of xHCI April 2023 Revision 1.2b. + */ +#define XHCI_DCID 0x0000 + +#define XHCI_DCDB 0x0004 +#define XHCI_DCDB_GET(x) (((x) >> 8) & 0xFF) +#define XHCI_DCDB_MASK 0x0000FF00 +#define XHCI_DCDB_OUT 0x0000 +#define XHCI_DCDB_IN 0x0100 +#define XHCI_DCDB_INVAL 0xFF00 + +#define XHCI_DCERSTSZ 0x0008 /* Event Ring Segment Table size */ +#define XHCI_DCERSTBA_LO 0x0010 +#define XHCI_DCERSTBA_HI 0x0014 +#define XHCI_DCERDP_LO 0x0018 +#define XHCI_DCERDP_HI 0x001C + +#define XHCI_DCCTRL 0x0020 /* Debug Control */ +#define XHCI_DCCTRL_DCR 0x00000001 +#define XHCI_DCCTRL_DCR_GET(x) (((x) ) & 0x01) +#define XHCI_DCCTRL_LSE 0x00000002 +#define XHCI_DCCTRL_LSE_GET(x) (((x) >> 1) & 0x01) +#define XHCI_DCCTRL_HOT 0x00000004 +#define XHCI_DCCTRL_HOT_GET(x) (((x) >> 2) & 0x01) +#define XHCI_DCCTRL_HIT 0x00000008 +#define XHCI_DCCTRL_HIT_GET(x) (((x) >> 3) & 0x01) +#define XHCI_DCCTRL_DRC 0x00000010 +#define XHCI_DCCTRL_DRC_GET(x) (((x) >> 4) & 0x01) +#define XHCI_DCCTRL_MBS_GET(x) (((x) >> 16) & 0xFF) +#define XHCI_DCCTRL_ADDR_GET(x) (((x) >> 24) & 0x07) +#define XHCI_DCCTRL_DCE 0x80000000 +#define XHCI_DCCTRL_DCE_GET(x) (((x) >> 31) & 0x01) + +#define XHCI_DCST 0x0024 /* Status */ +#define XHCI_DCST_ER 0x00000001 +#define XHCI_DCST_ER_GET(x) (((x) ) & 0x01) +#define XHCI_DCST_SBR 0x00000004 +#define XHCI_DCST_SBR_GET(x) (((x) >> 2) & 0x01) +#define XHCI_DCST_PORT_GET(x) (((x) >> 24) & 0xFF) + +#define XHCI_DCPORTSC 0x0028 /* Port Control */ +#define XHCI_DCPORTSC_CCS 0x00000001 +#define XHCI_DCPORTSC_CCS_GET(x) (((x) ) & 0x01) +#define XHCI_DCPORTSC_PED 0x00000002 +#define XHCI_DCPORTSC_PED_GET(x) (((x) >> 1) & 0x01) +#define XHCI_DCPORTSC_PR 0x00000010 +#define XHCI_DCPORTSC_PR_GET(x) (((x) >> 4) & 0x01) +#define XHCI_DCPORTSC_PLS_GET(x) (((x) >> 5) & 0x0F) +#define XHCI_DCPORTSC_PLS_U0 0x00 +#define XHCI_DCPORTSC_PLS_U1 0x01 +#define XHCI_DCPORTSC_PLS_U2 0x02 +#define XHCI_DCPORTSC_PLS_U3 0x03 +#define XHCI_DCPORTSC_PLS_DISABLED 0x04 +#define XHCI_DCPORTSC_PLS_RXDETECTED 0x05 +#define XHCI_DCPORTSC_PLS_INACTIVE 0x06 +#define XHCI_DCPORTSC_PLS_POLLING 0x07 +#define XHCI_DCPORTSC_PLS_RECOVERY 0x08 +#define XHCI_DCPORTSC_PLS_HOTRESET 0x09 +#define XHCI_DCPORTSC_SPEED_GET(x) (((x) >> 10) & 0x0F) +#define XHCI_DCPORTSC_CSC 0x00020000 +#define XHCI_DCPORTSC_CSC_GET(x) (((x) >> 17) & 0x01) +#define XHCI_DCPORTSC_PRC 0x00200000 +#define XHCI_DCPORTSC_PRC_GET(x) (((x) >> 21) & 0x01) +#define XHCI_DCPORTSC_PLC 0x00400000 +#define XHCI_DCPORTSC_PLC_GET(x) (((x) >> 22) & 0x01) +#define XHCI_DCPORTSC_CEC 0x00800000 +#define XHCI_DCPORTSC_CEC_GET(x) (((x) >> 23) & 0x01) +#define XHCI_DCCP_LO 0x0030 /* Context Pointer */ +#define XHCI_DCCP_HI 0x0034 +#define XHCI_DCDDI1 0x0038 /* Device Descriptor Info */ +#define XHCI_DCDDI2 0x003C /* Device Descriptor Info */ + +/* DbC CIC offset in uint32 */ +#define XHCI_DCDBCIC_STR0DESC_LO 0x0000 +#define XHCI_DCDBCIC_STR0DESC_HI 0x0001 +#define XHCI_DCDBCIC_MANUDESC_LO 0x0002 +#define XHCI_DCDBCIC_MANUDESC_HI 0x0003 +#define XHCI_DCDBCIC_PRODDESC_LO 0x0004 +#define XHCI_DCDBCIC_PRODDESC_HI 0x0005 +#define XHCI_DCDBCIC_SERIALDESC_LO 0x0006 +#define XHCI_DCDBCIC_SERIALDESC_HI 0x0007 +#define XHCI_DCDBCIC_DESCLEN 0x0008 +#define XHCI_DCDBCIC_STR0DESC_LEN_GET(x) (((x) >> 0) & 0xff) +#define XHCI_DCDBCIC_STR0DESC_LEN_SET(x) (((x) & 0xff) << 0) +#define XHCI_DCDBCIC_MANUDESC_LEN_GET(x) (((x) >> 8) & 0xff) +#define XHCI_DCDBCIC_MANUDESC_LEN_SET(x) (((x) & 0xff) << 8) +#define XHCI_DCDBCIC_PRODDESC_LEN_GET(x) (((x) >> 16) & 0xff) +#define XHCI_DCDBCIC_PRODDESC_LEN_SET(x) (((x) & 0xff) << 16) +#define XHCI_DCDBCIC_SERIALDESC_LEN_GET(x) (((x) >> 24) & 0xff) +#define XHCI_DCDBCIC_SERIALDESC_LEN_SET(x) (((x) & 0xff) << 24) + +#define XHCI_DCSTATUS(ctrl, portsc) \ + (XHCI_DCCTRL_DCE_GET(ctrl) << 4 | \ + XHCI_DCPORTSC_CCS_GET(portsc) << 3 | \ + XHCI_DCPORTSC_PED_GET(portsc) << 2 | \ + XHCI_DCPORTSC_PR_GET(portsc) << 1 | \ + XHCI_DCCTRL_DCR_GET(ctrl)) +#define XHCI_DCPORTSC_ACK_MASK \ + (XHCI_DCPORTSC_PED | \ + XHCI_DCPORTSC_CSC | XHCI_DCPORTSC_PRC | \ + XHCI_DCPORTSC_PLC | XHCI_DCPORTSC_CEC) + +#define XHCI_DCPORT_ST_OFF 0x00 +#define XHCI_DCPORT_ST_DISCONNECTED 0x10 /* DCE only */ +#define XHCI_DCPORT_ST_DISCONNECTED_RUNNING 0x11 /* XXX: DCE + DCR */ +#define XHCI_DCPORT_ST_DISABLED 0x18 /* DCE + CCS */ +#define XHCI_DCPORT_ST_RESETTING 0x1a /* DCE + CCS + PR */ +#define XHCI_DCPORT_ST_ENABLED 0x1c /* DCE + CCS + PED */ +#define XHCI_DCPORT_ST_CONFIGURED 0x1d /* DCE + CCS + PED + DCR */ + +#define XHCI_DC_MAXPACKETLEN 1024 +/* + * While Sec 7.6.3.2 describes Endpoint IDs should be 0 or 1, + * Intel chips use Device Context Index (Sec 4.5.1) instead. + */ +#define XHCI_DC_EPID_OUT 0 +#define XHCI_DC_EPID_IN 1 +#define XHCI_DC_EPID_OUT_INTEL 2 +#define XHCI_DC_EPID_IN_INTEL 3 +#define XHCI_DC_SLOT 1 + /* XHCI register R/W wrappers */ #define XREAD1(sc, what, a) \ bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \ @@ -221,6 +344,9 @@ #define XREAD4(sc, what, a) \ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off) +#define XREAD44LH(sc, what, a) \ + ((uint64_t)XREAD4((sc), what, a##_LO) | \ + ((uint64_t)XREAD4((sc), what, a##_HI) << 32)) #define XWRITE1(sc, what, a, x) \ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off, (x)) @@ -230,5 +356,10 @@ #define XWRITE4(sc, what, a, x) \ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \ (a) + (sc)->sc_##what##_off, (x)) +#define XWRITE44LH(sc, what, a, x) \ + do { \ + XWRITE4((sc), what, a##_LO, (uint32_t)((x) & 0xffffffff)); \ + XWRITE4((sc), what, a##_HI, (uint32_t)((x) >> 32)); \ + } while (0) #endif /* _XHCIREG_H_ */ diff --git a/sys/x86/include/metadata.h b/sys/x86/include/metadata.h --- a/sys/x86/include/metadata.h +++ b/sys/x86/include/metadata.h @@ -35,6 +35,7 @@ #define MODINFOMD_MODULEP 0x1006 #define MODINFOMD_VBE_FB 0x1007 #define MODINFOMD_EFI_ARCH 0x1008 +#define MODINFOMD_XHCI_DEBUG 0x1009 /* * This is not the same as the UEFI standard EFI_MEMORY_ATTRIBUTES_TABLE, though