diff --git a/drivers/usbhost/CMakeLists.txt b/drivers/usbhost/CMakeLists.txt index 9115f99e1c0..16917b44128 100644 --- a/drivers/usbhost/CMakeLists.txt +++ b/drivers/usbhost/CMakeLists.txt @@ -83,10 +83,14 @@ if(CONFIG_USBHOST) list(APPEND SRCS usbhost_bthci.c) endif() - if(CONFIG_USBHOST_XHCI_PCI) + if(CONFIG_USBHOST_XHCI) list(APPEND SRCS usbhost_xhci.c usbhost_xhci_trace.c) endif() + if(CONFIG_USBHOST_XHCI_PCI) + list(APPEND SRCS usbhost_xhci_pci.c) + endif() + # HCD debug/trace logic if(CONFIG_USBHOST_TRACE) diff --git a/drivers/usbhost/Kconfig b/drivers/usbhost/Kconfig index 00257b22a03..ccc6e63022d 100644 --- a/drivers/usbhost/Kconfig +++ b/drivers/usbhost/Kconfig @@ -813,15 +813,17 @@ config USBHOST_BTHCI ---help--- Select this option to build in support for USB Bluetooth HCI devices. -menuconfig USBHOST_XHCI_PCI - bool "USB xHCI PCI Host Driver Support" +config USBHOST_XHCI + bool default n - depends on PCI && PCI_MSIX && USBHOST_WAITER && SCHED_HPWORK && SCHED_LPWORK + depends on USBHOST_WAITER && SCHED_HPWORK && SCHED_LPWORK select USBHOST_HAVE_ASYNCH ---help--- - USB xHCI PCI host driver support. + The xHCI controller driver itself, which is the same wherever the + controller is fitted. Selected by whichever bus found one: PCI + below, or an SoC that wires one in. -if USBHOST_XHCI_PCI +if USBHOST_XHCI config USBHOST_XHCI_MAX_DEVS int "xHCI maximum supported devices" @@ -830,6 +832,14 @@ config USBHOST_XHCI_MAX_DEVS ---help--- How many USB devices will be supported by xHCI driver. -endif # USBHOST_XHCI_PCI +endif # USBHOST_XHCI + +menuconfig USBHOST_XHCI_PCI + bool "USB xHCI PCI Host Driver Support" + default n + depends on PCI && PCI_MSIX && USBHOST_WAITER && SCHED_HPWORK && SCHED_LPWORK + select USBHOST_XHCI + ---help--- + USB xHCI PCI host driver support. endif # USBHOST diff --git a/drivers/usbhost/Make.defs b/drivers/usbhost/Make.defs index 5bf8556d49e..a75f951863b 100644 --- a/drivers/usbhost/Make.defs +++ b/drivers/usbhost/Make.defs @@ -84,10 +84,14 @@ ifeq ($(CONFIG_USBHOST_BTHCI),y) CSRCS += usbhost_bthci.c endif -ifeq ($(CONFIG_USBHOST_XHCI_PCI),y) +ifeq ($(CONFIG_USBHOST_XHCI),y) CSRCS += usbhost_xhci.c usbhost_xhci_trace.c endif +ifeq ($(CONFIG_USBHOST_XHCI_PCI),y) +CSRCS += usbhost_xhci_pci.c +endif + # HCD debug/trace logic ifeq ($(CONFIG_USBHOST_TRACE),y) diff --git a/drivers/usbhost/usbhost_xhci.c b/drivers/usbhost/usbhost_xhci.c index e66718fb00a..ff628cf31fd 100644 --- a/drivers/usbhost/usbhost_xhci.c +++ b/drivers/usbhost/usbhost_xhci.c @@ -1,5 +1,5 @@ /**************************************************************************** - * drivers/usbhost/usbhost_xhci_pci.c + * drivers/usbhost/usbhost_xhci.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -36,8 +36,6 @@ #include #include -#include - #include #include #include @@ -69,13 +67,16 @@ #define XHCI_EVENT_MAX (232) #define XHCI_TD_MAX (8) -/* Milliseconds allowed for the controller to halt. The specification - * asks for 16. +/* How long to give the controller to stop, in milliseconds. The + * specification asks for it within 16; this is generous. */ #define XHCI_HALT_TIMEOUT_MS (100) -/* Milliseconds allowed for a port to enable after reset. */ +/* How long to give a port to come up after being reset, in milliseconds. + * USB 2.0 asks for the reset to be held 10ms and the port to be usable + * shortly after; this is generous. + */ #define XHCI_PORT_RESET_MS (500) #define XHCI_BUFSIZE (512) @@ -240,10 +241,11 @@ struct usbhost_xhci_s struct xhci_trb_s cmdres; /* Command result */ struct xhci_ring_s cmd; /* Command ring handler */ - /* PCI data */ + /* Bus hookup, supplied by whoever found this controller */ - FAR struct pci_device_s *pcidev; /* PCI device reference */ - int irq; /* IRQ number used by the device */ + FAR const struct xhci_bus_ops_s *ops; /* Bus operations */ + FAR void *arg; /* Bus private data */ + FAR const char *name; /* What to call this controller */ uint32_t pending; /* IRQ pending status */ struct work_s work; /* IRQ work */ struct work_s pscwork; /* Port status change work */ @@ -482,48 +484,6 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv); static int xhci_mem_free(FAR struct usbhost_xhci_s *priv); static int xhci_hw_initialize(FAR struct usbhost_xhci_s *priv); static int xhci_sw_initialize(FAR struct usbhost_xhci_s *priv); -static int pci_xhci_probe(FAR struct pci_device_s *dev); -static void pci_xhci_remove(FAR struct pci_device_s *dev); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* PCI device table */ - -static const struct pci_device_id_s g_pci_xhci_id_table[] = -{ - /* QEMU xHCI */ - - { - PCI_DEVICE(0x1b36, 0x000d), - .driver_data = 0 - }, - - /* Intel Alder Lake USB 3.2 xHCI controller */ - - { - PCI_DEVICE(0x8086, 0x51ed), - .driver_data = 0 - }, - - /* Intel Alder Lake-S USB 3.2 Gen 2x2 xHCI controller */ - - { - PCI_DEVICE(0x8086, 0x7ae0), - .driver_data = 0 - }, - { } -}; - -/* PCI driver */ - -static struct pci_driver_s g_pci_xhci_drv = -{ - .id_table = g_pci_xhci_id_table, - .probe = pci_xhci_probe, - .remove = pci_xhci_remove, -}; /**************************************************************************** * Private Functions @@ -723,7 +683,7 @@ static void xhci_door_putreg(FAR struct usbhost_xhci_s *priv, static void xhci_dump_capa_reg(FAR struct usbhost_xhci_s *priv, FAR const char *msg, unsigned int offset) { - pciinfo("\t%s:\t\t0x%" PRIx32 "\n", msg, xhci_capa_getreg(priv, offset)); + uinfo("\t%s:\t\t0x%" PRIx32 "\n", msg, xhci_capa_getreg(priv, offset)); } /**************************************************************************** @@ -733,7 +693,7 @@ static void xhci_dump_capa_reg(FAR struct usbhost_xhci_s *priv, static void xhci_dump_oper_reg(FAR struct usbhost_xhci_s *priv, FAR const char *msg, unsigned int offset) { - pciinfo("\t%s:\t\t0x%" PRIx32 "\n", msg, xhci_oper_getreg(priv, offset)); + uinfo("\t%s:\t\t0x%" PRIx32 "\n", msg, xhci_oper_getreg(priv, offset)); } /**************************************************************************** @@ -743,7 +703,7 @@ static void xhci_dump_oper_reg(FAR struct usbhost_xhci_s *priv, static void xhci_dump_runt_reg(FAR struct usbhost_xhci_s *priv, FAR const char *msg, unsigned int offset) { - pciinfo("\t%s:\t\t0x%" PRIx32 "\n", msg, xhci_runt_getreg(priv, offset)); + uinfo("\t%s:\t\t0x%" PRIx32 "\n", msg, xhci_runt_getreg(priv, offset)); } /**************************************************************************** @@ -755,9 +715,9 @@ static void xhci_dump_mem(FAR struct usbhost_xhci_s *priv, { int i; - pciinfo("Dump xHCI registers: %s\n", msg); + uinfo("Dump xHCI registers: %s\n", msg); - pciinfo("=== Host Controller Capability Registers ===\n"); + uinfo("=== Host Controller Capability Registers ===\n"); xhci_dump_capa_reg(priv, "CAPLENGTH ", XHCI_CAPLENGTH); xhci_dump_capa_reg(priv, "HCIVERSION ", XHCI_HCIVERSION); xhci_dump_capa_reg(priv, "HCSPARAMS1 ", XHCI_HCSPARAMS1); @@ -768,7 +728,7 @@ static void xhci_dump_mem(FAR struct usbhost_xhci_s *priv, xhci_dump_capa_reg(priv, "RTSOFF ", XHCI_RTSOFF); xhci_dump_capa_reg(priv, "HCCPARAMS2 ", XHCI_HCCPARAMS2); - pciinfo("=== Host Controller Operational Registers ===\n"); + uinfo("=== Host Controller Operational Registers ===\n"); xhci_dump_oper_reg(priv, "USBCMD ", XHCI_USBCMD); xhci_dump_oper_reg(priv, "USBSTS ", XHCI_USBSTS); xhci_dump_oper_reg(priv, "PAGESIZE ", XHCI_PAGESIZE); @@ -779,7 +739,7 @@ static void xhci_dump_mem(FAR struct usbhost_xhci_s *priv, for (i = 0; i < priv->no_ports; i++) { - pciinfo("port %d --------------------------------\n", i); + uinfo("port %d --------------------------------\n", i); xhci_dump_oper_reg(priv, "PORTSC ", XHCI_PORTSC(i)); xhci_dump_oper_reg(priv, "PORTPMSC ", XHCI_PORTPMSC(i)); xhci_dump_oper_reg(priv, "PORTLI ", XHCI_PORTLI(i)); @@ -787,7 +747,7 @@ static void xhci_dump_mem(FAR struct usbhost_xhci_s *priv, /* Only one interrupter used */ - pciinfo("=== Host Controller Runtime Registers ===\n"); + uinfo("=== Host Controller Runtime Registers ===\n"); xhci_dump_runt_reg(priv, "MFINDEX ", XHCI_MFINDEX); xhci_dump_runt_reg(priv, "IMAN(0) ", XHCI_IMAN(0)); xhci_dump_runt_reg(priv, "IMOD(0) ", XHCI_IMOD(0)); @@ -1097,7 +1057,7 @@ static int xhci_ctrl_start(FAR struct usbhost_xhci_s *priv) int ret; int i; - pciinfo("Start controller\n"); + uinfo("Start controller\n"); /* Reset controller before writing any Operational or Runtime registers */ @@ -1134,14 +1094,14 @@ static int xhci_ctrl_start(FAR struct usbhost_xhci_s *priv) ret = xhci_ring_init(&priv->cmd, XHCI_CMD_MAX); if (ret < 0) { - pcierr("cmd ring init failed\n"); + uerr("cmd ring init failed\n"); return ret; } ret = xhci_ring_init(&priv->evnt, XHCI_EVENT_MAX); if (ret < 0) { - pcierr("event ring init failed\n"); + uerr("event ring init failed\n"); return ret; } @@ -1216,7 +1176,7 @@ static int xhci_ctrl_start(FAR struct usbhost_xhci_s *priv) if (ret != OK) { - pcierr("Can't start controller!"); + uerr("Can't start controller!"); return ret; } @@ -1273,7 +1233,7 @@ static int xhci_ctrl_halt(FAR struct usbhost_xhci_s *priv) up_udelay(1000); } - pcierr("controller will not halt, USBSTS %08" PRIx32 "\n", regval); + uerr("controller will not halt, USBSTS %08" PRIx32 "\n", regval); return -EAGAIN; } @@ -1394,8 +1354,8 @@ static int xhci_port_enable(FAR struct usbhost_xhci_s *priv, if ((regval & XHCI_PORTSC_PED) == 0) { - pcierr("port %d will not enable, PORTSC %08" PRIx32 "\n", rhpndx, - regval); + uerr("port %d will not enable, PORTSC %08" PRIx32 "\n", rhpndx, + regval); return -ETIMEDOUT; } } @@ -1443,7 +1403,7 @@ static int xhci_port_enable(FAR struct usbhost_xhci_s *priv, default: { - pcierr("speed = 0x%x\n", speed); + uerr("speed = 0x%x\n", speed); hport->speed = USB_SPEED_UNKNOWN; return -EINVAL; } @@ -1746,7 +1706,7 @@ static int xhci_device_init(FAR struct usbhost_xhci_s *priv, ret = xhci_ring_init(&rhport->ep0.td, XHCI_TD_MAX); if (ret < 0) { - pcierr("ep0 ring init failed\n"); + uerr("ep0 ring init failed\n"); return ret; } @@ -1772,7 +1732,7 @@ static int xhci_device_init(FAR struct usbhost_xhci_s *priv, ret = xhci_address_set(priv, rhport, false); if (ret < 0) { - pcierr("failed to set address %d\n", ret); + uerr("failed to set address %d\n", ret); return ret; } @@ -1803,7 +1763,7 @@ static int xhci_device_deinit(FAR struct usbhost_xhci_s *priv, ret = xhci_cmd_slotdis(priv, slot); if (ret < 0) { - pcierr("xhci_cmd_slotdis failed %d\n", ret); + uerr("xhci_cmd_slotdis failed %d\n", ret); } /* Clear DCBAA entry for this slot */ @@ -1948,7 +1908,7 @@ static int xhci_command(FAR struct usbhost_xhci_s *priv, } else { - pcierr("event CC = %d\n", XHCI_TRB_D1_CC_GET(trb->d1)); + uerr("event CC = %d\n", XHCI_TRB_D1_CC_GET(trb->d1)); ret = -EIO; } @@ -2768,7 +2728,7 @@ static void xhci_transfer_complete(FAR struct usbhost_xhci_s *priv, { /* Report error */ - pcierr("transfer CC = %d\n", ret); + uerr("transfer CC = %d\n", ret); epinfo->status = ret; epinfo->result = -EIO; } @@ -2903,7 +2863,7 @@ static int xhci_events_poll(FAR struct usbhost_xhci_s *priv) default: { - pciinfo("ignored event %d\n", type); + uinfo("ignored event %d\n", type); break; } } @@ -2950,28 +2910,28 @@ static void xhci_interrupt_work(FAR void *arg) { /* Handled as event in xhci_events_poll() */ - pciinfo("Port Change Detect\n"); + uinfo("Port Change Detect\n"); } /* Host Controller Halted */ if (priv->pending & XHCI_USBSTS_HCH) { - pciinfo("Host Controller Halted\n"); + uinfo("Host Controller Halted\n"); } /* Host System Error */ if (priv->pending & XHCI_USBSTS_HSE) { - pciinfo("Host System Error\n"); + uinfo("Host System Error\n"); } /* Host Controller Error */ if (priv->pending & XHCI_USBSTS_HCE) { - pciinfo("Host Controller Error\n"); + uinfo("Host Controller Error\n"); } /* ACK interrupts */ @@ -3169,7 +3129,7 @@ static int xhci_rh_enumerate(FAR struct usbhost_connection_s *conn, { /* No, return an error */ - pcierr("not connected\n"); + uerr("not connected\n"); return -ENODEV; } @@ -3178,7 +3138,7 @@ static int xhci_rh_enumerate(FAR struct usbhost_connection_s *conn, ret = xhci_port_enable(priv, hport); if (ret < 0) { - pcierr("Failed to enable port %d\n", ret); + uerr("Failed to enable port %d\n", ret); return ret; } @@ -3187,7 +3147,7 @@ static int xhci_rh_enumerate(FAR struct usbhost_connection_s *conn, ret = xhci_device_init(priv, rhport); if (ret < 0) { - pcierr("Failed to initialize device %d\n", ret); + uerr("Failed to initialize device %d\n", ret); return ret; } @@ -3361,7 +3321,7 @@ static int xhci_epalloc(FAR struct usbhost_driver_s *drvr, #ifdef CONFIG_USBHOST_TRACE usbhost_vtrace2(XHCI_VTRACE2_EPALLOC, epdesc->addr, epdesc->xfrtype); #else - pciinfo("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d MaxPacket=%d\n", + uinfo("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d MaxPacket=%d\n", epdesc->addr, epdesc->in ? "IN" : "OUT", hport->funcaddr, epdesc->xfrtype, epdesc->interval, epdesc->mxpacketsize); #endif @@ -3401,7 +3361,7 @@ static int xhci_epalloc(FAR struct usbhost_driver_s *drvr, ret = xhci_ring_init(&epinfo->td, XHCI_TD_MAX); if (ret < 0) { - pcierr("ep ring init failed\n"); + uerr("ep ring init failed\n"); return ret; } @@ -3495,7 +3455,7 @@ static int xhci_epalloc(FAR struct usbhost_driver_s *drvr, up_addrenv_va_to_pa(dev->input), false); if (ret < 0) { - pcierr("failed to configure EP %d\n", ret); + uerr("failed to configure EP %d\n", ret); return ret; } @@ -3831,7 +3791,7 @@ static int xhci_ctrl_xfer(FAR struct usbhost_driver_s *drvr, ret = xhci_control_setup(rhport, ep0info, req, buffer, len); if (ret < 0) { - pcierr("ERROR: xhci_control_setup failed: %d\n", ret); + uerr("ERROR: xhci_control_setup failed: %d\n", ret); goto errout_with_iocwait; } @@ -4308,7 +4268,7 @@ static int xhci_hw_getparams(FAR struct usbhost_xhci_s *priv) regval = xhci_capa_getreg(priv, XHCI_HCCPARAMS1); if (regval & XHCI_HCCPARAMS1_CSZ) { - pcierr("Only 32 byte Context data structures supported!\n"); + uerr("Only 32 byte Context data structures supported!\n"); return -EIO; } @@ -4325,7 +4285,7 @@ static int xhci_hw_getparams(FAR struct usbhost_xhci_s *priv) priv->no_slots = CONFIG_USBHOST_XHCI_MAX_DEVS; } - pciinfo("no slots = %d, no ports = %d\n", + uinfo("no slots = %d, no ports = %d\n", priv->no_slots, priv->no_ports); /* Check if valid */ @@ -4340,11 +4300,11 @@ static int xhci_hw_getparams(FAR struct usbhost_xhci_s *priv) regval = xhci_capa_getreg(priv, XHCI_HCSPARAMS2); priv->no_scratch = XHCI_HCSPARAMS2_MAXSPB(regval); - pciinfo("no scratch = %d\n", priv->no_scratch); + uinfo("no scratch = %d\n", priv->no_scratch); priv->no_erst = 1 << XHCI_HCSPARAMS2_ERST(regval); - pciinfo("no_erst = %d\n", priv->no_erst); + uinfo("no_erst = %d\n", priv->no_erst); /* Limit event ring segment table to 1 */ @@ -4353,7 +4313,7 @@ static int xhci_hw_getparams(FAR struct usbhost_xhci_s *priv) priv->no_erst = XHCI_MAX_ERST; } - pciinfo("no erst = %d\n", priv->no_erst); + uinfo("no erst = %d\n", priv->no_erst); return OK; } @@ -4362,41 +4322,14 @@ static int xhci_hw_getparams(FAR struct usbhost_xhci_s *priv) * Name: xhci_irq_initialize * * Description: - * Initialize xHCI interrupts - require MSI-X support. + * Ask the bus this controller was found on for its interrupt. See + * struct xhci_bus_ops_s in include/nuttx/usb/xhci.h. * ****************************************************************************/ static int xhci_irq_initialize(FAR struct usbhost_xhci_s *priv) { - int ret; - - /* Allocate MSI */ - - ret = pci_alloc_irq(priv->pcidev, &priv->irq, 1); - if (ret != 1) - { - pcierr("Failed to allocate MSI %d\n", ret); - return ret; - } - - /* Attach IRQ */ - - irq_attach(priv->irq, xhci_interrupt, priv); - - /* Connect MSI-X */ - - ret = pci_connect_irq(priv->pcidev, &priv->irq, 1); - if (ret != OK) - { - pcierr("Failed to connect MSI %d\n", ret); - pci_release_irq(priv->pcidev, &priv->irq, 1); - - return -ENOTSUP; - } - - up_enable_irq(priv->irq); - - return OK; + return priv->ops->irq_attach(priv->arg, xhci_interrupt, priv); } /**************************************************************************** @@ -4422,7 +4355,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv) priv->pg_sb = kmm_memalign(XHCI_BUF_ALIGN, tmp); if (!priv->pg_sb) { - pcierr("pg_sb malloc failed\n"); + uerr("pg_sb malloc failed\n"); return -ENOMEM; } @@ -4437,7 +4370,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv) kmm_memalign(XHCI_PAGE_SIZE, XHCI_PAGE_SIZE)); if (!priv->pg_sb[i]) { - pcierr("pg_sb[i] malloc failed\n"); + uerr("pg_sb[i] malloc failed\n"); return -ENOMEM; } @@ -4455,7 +4388,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv) priv->pg_ctx = kmm_memalign(XHCI_BUF_ALIGN, tmp); if (!priv->pg_ctx) { - pcierr("pg_ctx malloc failed\n"); + uerr("pg_ctx malloc failed\n"); return -ENOMEM; } @@ -4469,7 +4402,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv) priv->pg_erst = kmm_memalign(XHCI_BUF_ALIGN, tmp); if (!priv->pg_erst) { - pcierr("priv->pg_erst malloc failed\n"); + uerr("priv->pg_erst malloc failed\n"); return -ENOMEM; } @@ -4480,7 +4413,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv) priv->rhport = kmm_zalloc(priv->no_ports * sizeof(struct xhci_rhport_s)); if (!priv->rhport) { - pcierr("rhport zalloc failed!\n"); + uerr("rhport zalloc failed!\n"); return -ENOMEM; } @@ -4489,7 +4422,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv) priv->devs = kmm_zalloc(priv->no_slots * sizeof(struct xhci_dev_s)); if (!priv->devs) { - pcierr("devs zalloc failed!\n"); + uerr("devs zalloc failed!\n"); return -ENOMEM; } @@ -4502,7 +4435,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv) priv->devs[i].ctx = kmm_zalloc(sizeof(struct xhci_dev_ctx_s)); if (!priv->devs[i].ctx) { - pcierr("dev ctx zalloc failed!\n"); + uerr("dev ctx zalloc failed!\n"); return -ENOMEM; } @@ -4514,7 +4447,7 @@ static int xhci_mem_alloc(FAR struct usbhost_xhci_s *priv) sizeof(struct xhci_input_dev_ctx_s)); if (!priv->devs[i].input) { - pcierr("dev input zalloc failed!\n"); + uerr("dev input zalloc failed!\n"); return -ENOMEM; } @@ -4592,7 +4525,7 @@ static int xhci_hw_initialize(FAR struct usbhost_xhci_s *priv) ret = xhci_bios_wait(priv); if (ret < 0) { - pcierr("Failed to get xhci controller!\n"); + uerr("Failed to get xhci controller!\n"); goto errout; } @@ -4733,66 +4666,67 @@ static inline int xhci_sw_initialize(FAR struct usbhost_xhci_s *priv) } /**************************************************************************** - * Name: pci_xhci_probe + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xhci_initialize * * Description: - * Initialize PCI device. + * Bring up an xHCI controller and start watching its root hub ports. + * + * The caller has already found the controller and knows how to reach it; + * everything this function does from here on is described by the xHCI + * specification and is the same on any bus. + * + * Input Parameters: + * base - Where the controller's register block starts. The capability + * registers are at the beginning of it and say where the rest are. + * ops - How to reach the interrupt this controller raises + * arg - Opaque value passed back to ops + * + * Returned Value: + * A connection to hand to usbhost_waiter_initialize() or to drive + * directly; NULL on failure. * ****************************************************************************/ -static int pci_xhci_probe(FAR struct pci_device_s *dev) +FAR struct usbhost_connection_s * +xhci_initialize(FAR const char *name, uintptr_t base, + FAR const struct xhci_bus_ops_s *ops, FAR void *arg) { FAR struct usbhost_conn_xhci_s *conn = NULL; FAR struct usbhost_xhci_s *priv = NULL; - int ret = -ENOMEM; + int ret; - /* Init PCI bus */ - - pci_set_master(dev); - pciinfo("Enabled bus mastering\n"); - pci_enable_device(dev); - pciinfo("Enabled memory resources\n"); - - /* Allocate connection structure */ + DEBUGASSERT(name != NULL && base != 0 && ops != NULL && + ops->irq_attach != NULL); conn = kmm_zalloc(sizeof(struct usbhost_conn_xhci_s)); - if (!conn) + if (conn == NULL) { - pcierr("zalloc failed!\n"); - goto errout; + return NULL; } - /* Allocate the driver structure */ - priv = kmm_zalloc(sizeof(struct usbhost_xhci_s)); - if (!priv) + if (priv == NULL) { - pcierr("zalloc failed!\n"); - goto errout; + kmm_free(conn); + return NULL; } - /* Initialize connection data */ - conn->conn.wait = xhci_wait; conn->conn.enumerate = xhci_enumerate; conn->priv = priv; - /* Connect PCI handler */ + priv->name = name; + priv->ops = ops; + priv->arg = arg; + priv->base = base; - priv->pcidev = dev; - dev->priv = conn; - - /* Get base address - BAR 0 */ - - priv->base = (uintptr_t)pci_map_bar(dev, 0); - if (!priv->base) - { - pcierr("Not found BAR 0!\n"); - ret = -EIO; - goto errout; - } - - /* Get register address */ + /* The capability registers are the only ones at a known offset. Every + * other block is where they say it is. + */ priv->capa_base = priv->base; priv->oper_base = priv->base + xhci_capa_getreg_1b(priv, XHCI_CAPLENGTH); @@ -4801,26 +4735,20 @@ static int pci_xhci_probe(FAR struct pci_device_s *dev) usbhost_vtrace1(XHCI_VTRACE1_INITIALIZING, 0); - /* Initialize HW */ - ret = xhci_hw_initialize(priv); if (ret < 0) { - pcierr("failed to initialize HW!\n"); + uerr("failed to initialize HW: %d\n", ret); goto errout; } - /* Initialize SW */ - ret = xhci_sw_initialize(priv); if (ret < 0) { - pcierr("failed to initialize SW!\n"); + uerr("failed to initialize SW: %d\n", ret); goto errout; } - /* Start controller */ - ret = xhci_ctrl_start(priv); if (ret < 0) { @@ -4829,106 +4757,59 @@ static int pci_xhci_probe(FAR struct pci_device_s *dev) } #ifdef CONFIG_DEBUG_USB_INFO - /* Dump xhci registers */ - xhci_dump_mem(priv, "after init"); #endif - /* If there is a USB device in the slot at power up, then we will not - * get the status change interrupt to signal us that the device is - * connected. We need to set the initial connected state accordingly. + /* A device already in a port at power up produces no status change + * interrupt to tell us it is there, so look for one. */ xhci_probe_ports(priv); - usbhost_vtrace1(XHCI_VTRACE1_INITIALIZING, 0); - - /* Initialize waiter */ - ret = usbhost_waiter_initialize(&conn->conn); if (ret < 0) { - pcierr("failed to initialize waiter!\n"); + uerr("failed to initialize waiter: %d\n", ret); goto errout; } - /* Store waiter PID */ - conn->pid = ret; - return OK; + return &conn->conn; errout: - - /* Free allocated xhci buffers */ - xhci_mem_free(priv); - - /* Free allocated data */ - kmm_free(conn); kmm_free(priv); - return ret; + return NULL; } /**************************************************************************** - * Name: pci_xhci_remove + * Name: xhci_uninitialize * * Description: - * Remove PCI device. + * Stop watching a controller's ports and give back everything + * xhci_initialize() took. * ****************************************************************************/ -static void pci_xhci_remove(FAR struct pci_device_s *dev) +void xhci_uninitialize(FAR struct usbhost_connection_s *conn) { - FAR struct usbhost_conn_xhci_s *conn = dev->priv; - FAR struct usbhost_xhci_s *priv = conn->priv; + FAR struct usbhost_conn_xhci_s *xconn = XHCI_XCONN_FROM_CONN(conn); + FAR struct usbhost_xhci_s *priv; - /* Free xhci interrupts */ + DEBUGASSERT(conn != NULL); + priv = xconn->priv; - irq_detach(priv->irq); - pci_release_irq(dev, &priv->irq, 1); + /* Stop the waiter before the memory it walks goes away */ - /* Disable PCI devicve */ + kthread_delete(xconn->pid); - pci_clear_master(dev); - pci_disable_device(dev); - - /* Delete waiter thread */ - - kthread_delete(conn->pid); - - /* Free xhci buffers */ + priv->ops->irq_detach(priv->arg); xhci_mem_free(priv); - /* Free driver data */ - - kmm_free(conn); kmm_free(priv); -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: pci_xhci_init - * - * Description: - * Initialize the USB host xHCI as PCI device. - * - * Input Parameters: - * None - * - * Returned Value: - * On success this function will return zero (OK); A negated errno value - * will be returned on failure. - * - ****************************************************************************/ - -int pci_xhci_init(void) -{ - return pci_register_driver(&g_pci_xhci_drv); + kmm_free(xconn); } diff --git a/drivers/usbhost/usbhost_xhci.h b/drivers/usbhost/usbhost_xhci.h index 3ece9affe41..8f940f8a82c 100644 --- a/drivers/usbhost/usbhost_xhci.h +++ b/drivers/usbhost/usbhost_xhci.h @@ -27,6 +27,10 @@ #include +#include + +#include + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ diff --git a/drivers/usbhost/usbhost_xhci_pci.c b/drivers/usbhost/usbhost_xhci_pci.c new file mode 100644 index 00000000000..95b794c4c26 --- /dev/null +++ b/drivers/usbhost/usbhost_xhci_pci.c @@ -0,0 +1,278 @@ +/**************************************************************************** + * drivers/usbhost/usbhost_xhci_pci.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Finding an xHCI controller on a PCI bus. + * + * The controller itself is described by its specification and driven by + * usbhost_xhci.c, which is the same code wherever the part is fitted. This + * file is only the part that is true of PCI and of nothing else: which + * device identifiers to answer to, how to switch a device on and find its + * register window, and how its interrupt is arranged. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "usbhost_xhci.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* What this file needs to remember about a controller, which is only the + * things the PCI layer will ask for again when the device goes away. + */ + +struct pci_xhci_s +{ + FAR struct pci_device_s *dev; /* The device we were given */ + FAR struct usbhost_connection_s *conn; /* What the controller gave back */ + int irq; /* Allocated MSI-X vector */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int pci_xhci_irq_attach(FAR void *arg, xcpt_t handler, + FAR void *priv); +static void pci_xhci_irq_detach(FAR void *arg); +static int pci_xhci_probe(FAR struct pci_device_s *dev); +static void pci_xhci_remove(FAR struct pci_device_s *dev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct xhci_bus_ops_s g_pci_xhci_ops = +{ + .irq_attach = pci_xhci_irq_attach, + .irq_detach = pci_xhci_irq_detach, +}; + +/* PCI device table */ + +static const struct pci_device_id_s g_pci_xhci_id_table[] = +{ + /* QEMU xHCI */ + + { + PCI_DEVICE(0x1b36, 0x000d), + .driver_data = 0 + }, + + /* Intel Alder Lake USB 3.2 xHCI controller */ + + { + PCI_DEVICE(0x8086, 0x51ed), + .driver_data = 0 + }, + + /* Intel Alder Lake-S USB 3.2 Gen 2x2 xHCI controller */ + + { + PCI_DEVICE(0x8086, 0x7ae0), + .driver_data = 0 + }, + { } +}; + +/* PCI driver */ + +static struct pci_driver_s g_pci_xhci_drv = +{ + .id_table = g_pci_xhci_id_table, + .probe = pci_xhci_probe, + .remove = pci_xhci_remove, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_xhci_irq_attach + * + * Description: + * Give the controller an interrupt. On PCI that means asking for a + * message rather than finding a wire, so the vector is allocated here and + * only then attached. + * + ****************************************************************************/ + +static int pci_xhci_irq_attach(FAR void *arg, xcpt_t handler, FAR void *priv) +{ + FAR struct pci_xhci_s *pcix = arg; + int ret; + + ret = pci_alloc_irq(pcix->dev, &pcix->irq, 1); + if (ret != 1) + { + pcierr("Failed to allocate MSI %d\n", ret); + return ret; + } + + irq_attach(pcix->irq, handler, priv); + + ret = pci_connect_irq(pcix->dev, &pcix->irq, 1); + if (ret != OK) + { + pcierr("Failed to connect MSI %d\n", ret); + pci_release_irq(pcix->dev, &pcix->irq, 1); + + return -ENOTSUP; + } + + up_enable_irq(pcix->irq); + + return OK; +} + +/**************************************************************************** + * Name: pci_xhci_irq_detach + ****************************************************************************/ + +static void pci_xhci_irq_detach(FAR void *arg) +{ + FAR struct pci_xhci_s *pcix = arg; + + up_disable_irq(pcix->irq); + irq_detach(pcix->irq); + pci_release_irq(pcix->dev, &pcix->irq, 1); +} + +/**************************************************************************** + * Name: pci_xhci_probe + * + * Description: + * Switch on a PCI xHCI controller and hand it to the controller driver. + * + ****************************************************************************/ + +static int pci_xhci_probe(FAR struct pci_device_s *dev) +{ + FAR struct pci_xhci_s *pcix; + uintptr_t base; + int ret; + + pcix = kmm_zalloc(sizeof(struct pci_xhci_s)); + if (pcix == NULL) + { + return -ENOMEM; + } + + pcix->dev = dev; + dev->priv = pcix; + + /* The controller has to be able to reach memory on its own, and its + * registers have to be answerable, before either is used. + */ + + pci_set_master(dev); + pciinfo("Enabled bus mastering\n"); + pci_enable_device(dev); + pciinfo("Enabled memory resources\n"); + + base = (uintptr_t)pci_map_bar(dev, 0); + if (base == 0) + { + pcierr("Not found BAR 0!\n"); + ret = -EIO; + goto errout; + } + + pcix->conn = xhci_initialize("usb", base, &g_pci_xhci_ops, pcix); + if (pcix->conn == NULL) + { + pcierr("xhci_initialize failed\n"); + ret = -EIO; + goto errout; + } + + return OK; + +errout: + pci_clear_master(dev); + pci_disable_device(dev); + kmm_free(pcix); + + return ret; +} + +/**************************************************************************** + * Name: pci_xhci_remove + ****************************************************************************/ + +static void pci_xhci_remove(FAR struct pci_device_s *dev) +{ + FAR struct pci_xhci_s *pcix = dev->priv; + + if (pcix == NULL) + { + return; + } + + xhci_uninitialize(pcix->conn); + + pci_clear_master(dev); + pci_disable_device(dev); + + dev->priv = NULL; + kmm_free(pcix); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pci_xhci_init + * + * Description: + * Initialize the USB host xHCI as PCI device. + * + * Input Parameters: + * None + * + * Returned Value: + * On success this function will return zero (OK); A negated errno value + * will be returned on failure. + * + ****************************************************************************/ + +int pci_xhci_init(void) +{ + return pci_register_driver(&g_pci_xhci_drv); +} diff --git a/include/nuttx/usb/xhci.h b/include/nuttx/usb/xhci.h new file mode 100644 index 00000000000..2368a5a9efe --- /dev/null +++ b/include/nuttx/usb/xhci.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * include/nuttx/usb/xhci.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_USB_XHCI_H +#define __INCLUDE_NUTTX_USB_XHCI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* How the controller was found *********************************************/ + +/* What a controller cannot work out about itself. + * + * xHCI is the same silicon on PCI or wired into an SoC, and everything the + * specification describes is reached through the register block. What + * differs is how the interrupt arrives: PCI needs a message the device is + * configured to send, a memory mapped bus has a wire the interrupt + * controller already knows. The bus answers that, and nothing else. + */ + +struct xhci_bus_ops_s +{ + /* Attach the handler and make interrupts start arriving. Enabling + * belongs here rather than in the controller driver because on some + * buses attaching and enabling are one operation. + */ + + CODE int (*irq_attach)(FAR void *arg, xcpt_t handler, FAR void *priv); + + /* Undo it, and release anything the bus allocated to make it work */ + + CODE void (*irq_detach)(FAR void *arg); +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: xhci_initialize + * + * Description: + * Bring up an xHCI controller and start watching its root hub ports. + * + * Input Parameters: + * name - What to call this controller when reporting what is attached to + * it, since a system may have more than one and "port 1" alone + * does not say which + * base - Where the controller's register block starts + * ops - How to reach the interrupt this controller raises + * arg - Opaque value passed back to ops + * + * Returned Value: + * A connection to hand to usbhost_waiter_initialize(); NULL on failure. + * + ****************************************************************************/ + +FAR struct usbhost_connection_s * +xhci_initialize(FAR const char *name, uintptr_t base, + FAR const struct xhci_bus_ops_s *ops, FAR void *arg); + +/**************************************************************************** + * Name: xhci_uninitialize + * + * Description: + * Stop watching a controller's ports and give back everything + * xhci_initialize() took. + * + ****************************************************************************/ + +void xhci_uninitialize(FAR struct usbhost_connection_s *conn); + +#endif /* __INCLUDE_NUTTX_USB_XHCI_H */