From d6e4f54f2de4c76fbfbe85fc348a8fde8c296dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Fri, 29 Jul 2011 10:06:34 +0000 Subject: [PATCH] Patch by Jian Chiang as part of his GSoc Project (coding style fixes by myself): * xhci controller start operation * command ring and event ring initialization * No-Op Command test and real xhci irq handle * xhci root hub support * add Super Speed enumeration and xhci_rh.cpp into jamfile git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42511 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/bus_managers/usb/usb_private.h | 3 +- src/add-ons/kernel/busses/usb/Jamfile | 1 + src/add-ons/kernel/busses/usb/xhci.cpp | 598 +++++++++++++++--- src/add-ons/kernel/busses/usb/xhci.h | 113 +++- src/add-ons/kernel/busses/usb/xhci_hardware.h | 97 ++- src/add-ons/kernel/busses/usb/xhci_rh.cpp | 281 ++++++++ 6 files changed, 1002 insertions(+), 91 deletions(-) create mode 100644 src/add-ons/kernel/busses/usb/xhci_rh.cpp diff --git a/src/add-ons/kernel/bus_managers/usb/usb_private.h b/src/add-ons/kernel/bus_managers/usb/usb_private.h index 5b745d1f86..191d1602f1 100644 --- a/src/add-ons/kernel/bus_managers/usb/usb_private.h +++ b/src/add-ons/kernel/bus_managers/usb/usb_private.h @@ -90,7 +90,8 @@ typedef enum { USB_SPEED_LOWSPEED = 0, USB_SPEED_FULLSPEED, USB_SPEED_HIGHSPEED, - USB_SPEED_MAX = USB_SPEED_HIGHSPEED + USB_SPEED_SUPER, + USB_SPEED_MAX = USB_SPEED_SUPER } usb_speed; diff --git a/src/add-ons/kernel/busses/usb/Jamfile b/src/add-ons/kernel/busses/usb/Jamfile index 0afb4610f9..f1a2252030 100644 --- a/src/add-ons/kernel/busses/usb/Jamfile +++ b/src/add-ons/kernel/busses/usb/Jamfile @@ -28,6 +28,7 @@ KernelAddon ehci : KernelAddon xhci : xhci.cpp + xhci_rh.cpp : libusb.a : xhci.rdef ; diff --git a/src/add-ons/kernel/busses/usb/xhci.cpp b/src/add-ons/kernel/busses/usb/xhci.cpp index fe1069c406..0d5fb986e1 100644 --- a/src/add-ons/kernel/busses/usb/xhci.cpp +++ b/src/add-ons/kernel/busses/usb/xhci.cpp @@ -63,7 +63,22 @@ XHCI::XHCI(pci_info *info, Stack *stack) fRegisterArea(-1), fPCIInfo(info), fStack(stack), - fPortCount(0) + fErstArea(-1), + fDcbaArea(-1), + fSpinlock(B_SPINLOCK_INITIALIZER), + fCmdCompSem(-1), + fCmdCompThread(-1), + fFinishTransfersSem(-1), + fFinishThread(-1), + fStopThreads(false), + fRootHub(NULL), + fRootHubAddress(0), + fPortCount(0), + fSlotCount(0), + fEventIdx(0), + fCmdIdx(0), + fEventCcs(1), + fCmdCcs(1) { if (BusManager::InitCheck() < B_OK) { TRACE_ERROR("bus manager failed to init\n"); @@ -76,7 +91,7 @@ XHCI::XHCI(pci_info *info, Stack *stack) // enable busmaster and memory mapped access uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus, fPCIInfo->device, fPCIInfo->function, PCI_command, 2); - command &= ~PCI_command_io; + command &= ~(PCI_command_io | PCI_command_int_disable); command |= PCI_command_master | PCI_command_memory; sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, @@ -92,7 +107,7 @@ XHCI::XHCI(pci_info *info, Stack *stack) fPCIInfo->u.h0.base_registers[0], physicalAddress, offset, fPCIInfo->u.h0.base_register_sizes[0]); - fRegisterArea = map_physical_memory("EHCI memory mapped registers", + fRegisterArea = map_physical_memory("XHCI memory mapped registers", physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA, (void **)&fCapabilityRegisters); @@ -103,62 +118,55 @@ XHCI::XHCI(pci_info *info, Stack *stack) fCapabilityRegisters += offset; fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_CAPLENGTH); - fRuntimeRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_RTSOFF); + fRuntimeRegisters = fCapabilityRegisters + ReadCapReg32(XHCI_RTSOFF); + fDoorbellRegisters = fCapabilityRegisters + ReadCapReg32(XHCI_DBOFF); TRACE("mapped capability registers: 0x%08lx\n", (uint32)fCapabilityRegisters); TRACE("mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters); TRACE("mapped rumtime registers: 0x%08lx\n", (uint32)fRuntimeRegisters); + TRACE("mapped doorbell registers: 0x%08lx\n", (uint32)fDoorbellRegisters); TRACE("structural parameters1: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS1)); TRACE("structural parameters2: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS2)); TRACE("structural parameters3: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS3)); TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(XHCI_HCCPARAMS)); - // read port count from capability register - fPortCount = (ReadCapReg32(XHCI_HCSPARAMS1) >> 24) & 0x7f; - - uint32 extendedCapPointer = ((ReadCapReg32(XHCI_HCCPARAMS) >> 16) & 0xffff) - << 2; - if (extendedCapPointer > 0) { - TRACE("extended capabilities register at %ld\n", extendedCapPointer); - - uint32 legacySupport = ReadCapReg32(extendedCapPointer); - if ((legacySupport & XHCI_LEGSUP_CAPID_MASK) == XHCI_LEGSUP_CAPID) { - if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) != 0) { - TRACE_ALWAYS("the host controller is bios owned, claiming" - " ownership\n"); - WriteCapReg32(extendedCapPointer, legacySupport - | XHCI_LEGSUP_OSOWNED); - for (int32 i = 0; i < 20; i++) { - legacySupport = ReadCapReg32(extendedCapPointer); - - if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) == 0) - break; - - TRACE_ALWAYS("controller is still bios owned, waiting\n"); - snooze(50000); - } - } - - if (legacySupport & XHCI_LEGSUP_BIOSOWNED) { - TRACE_ERROR("bios won't give up control over the host " - "controller (ignoring)\n"); - } else if (legacySupport & XHCI_LEGSUP_OSOWNED) { - TRACE_ALWAYS("successfully took ownership of the host " - "controller\n"); - } - - // Force off the BIOS owned flag, and clear all SMIs. Some BIOSes - // do indicate a successful handover but do not remove their SMIs - // and then freeze the system when interrupts are generated. - WriteCapReg32(extendedCapPointer, legacySupport & ~XHCI_LEGSUP_BIOSOWNED); - WriteCapReg32(extendedCapPointer + XHCI_LEGCTLSTS, - XHCI_LEGCTLSTS_DISABLE_SMI); - } else { - TRACE("extended capability is not a legacy support register\n"); - } - } else { - TRACE("no extended capabilities register\n"); + uint32 cparams = ReadCapReg32(XHCI_HCCPARAMS); + uint32 eec = 0xffffffff; + uint32 eecp = HCS0_XECP(cparams) << 2; + for (; eecp != 0 && XECP_NEXT(eec); eecp += XECP_NEXT(eec) << 2) { + eec = ReadCapReg32(eecp); + if (XECP_ID(eec) != XHCI_LEGSUP_CAPID) + continue; } + if (eec & XHCI_LEGSUP_BIOSOWNED) { + TRACE_ALWAYS("the host controller is bios owned, claiming" + " ownership\n"); + WriteCapReg32(eecp, eec | XHCI_LEGSUP_OSOWNED); + + for (int32 i = 0; i < 20; i++) { + eec = ReadCapReg32(eecp); + + if ((eec & XHCI_LEGSUP_BIOSOWNED) == 0) + break; + + TRACE_ALWAYS("controller is still bios owned, waiting\n"); + snooze(50000); + } + + if (eec & XHCI_LEGSUP_BIOSOWNED) { + TRACE_ERROR("bios won't give up control over the host " + "controller (ignoring)\n"); + } else if (eec & XHCI_LEGSUP_OSOWNED) { + TRACE_ALWAYS("successfully took ownership of the host " + "controller\n"); + } + + // Force off the BIOS owned flag, and clear all SMIs. Some BIOSes + // do indicate a successful handover but do not remove their SMIs + // and then freeze the system when interrupts are generated. + WriteCapReg32(eecp, eec & ~XHCI_LEGSUP_BIOSOWNED); + } + WriteCapReg32(eecp + XHCI_LEGCTLSTS, XHCI_LEGCTLSTS_DISABLE_SMI); // halt the host controller if (ControllerHalt() < B_OK) { @@ -171,6 +179,28 @@ XHCI::XHCI(pci_info *info, Stack *stack) return; } + fCmdCompSem = create_sem(0, "XHCI Command Complete"); + fFinishTransfersSem = create_sem(0, "XHCI Finish Transfers"); + if (fFinishTransfersSem < B_OK || fCmdCompSem < B_OK) { + TRACE_ERROR("failed to create semaphores\n"); + return; + } + + // create finisher service thread + fFinishThread = spawn_kernel_thread(FinishThread, "xhci finish thread", + B_NORMAL_PRIORITY, (void *)this); + resume_thread(fFinishThread); + + // create command complete service thread + fCmdCompThread = spawn_kernel_thread(CmdCompThread, "xhci cmd complete thread", + B_NORMAL_PRIORITY, (void *)this); + resume_thread(fCmdCompThread); + + // Install the interrupt handler + TRACE("installing interrupt handler\n"); + install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line, + InterruptHandler, (void *)this, 0); + fInitOK = true; TRACE("XHCI host controller driver constructed\n"); } @@ -182,7 +212,15 @@ XHCI::~XHCI() WriteOpReg(XHCI_CMD, 0); + int32 result = 0; + fStopThreads = true; + delete_sem(fCmdCompSem); + delete_sem(fFinishTransfersSem); delete_area(fRegisterArea); + delete_area(fErstArea); + delete_area(fDcbaArea); + wait_for_thread(fCmdCompThread, &result); + wait_for_thread(fFinishThread, &result); put_module(B_PCI_MODULE_NAME); } @@ -194,7 +232,100 @@ XHCI::Start() TRACE("usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(XHCI_CMD), ReadOpReg(XHCI_STS)); + if ((ReadOpReg(XHCI_PAGESIZE) & (1 << 0)) == 0) { + TRACE_ERROR("Controller does not support 4K page size.\n"); + return B_ERROR; + } + + // read port count from capability register + uint32 capabilities = ReadCapReg32(XHCI_HCSPARAMS1); + + uint8 portsCount = HCS_MAX_PORTS(capabilities); + if (portsCount == 0) { + TRACE_ERROR("Invalid number of ports: %u\n", portsCount); + return B_ERROR; + } + fPortCount = portsCount; + fSlotCount = HCS_MAX_SLOTS(capabilities); + WriteOpReg(XHCI_CONFIG, fSlotCount); + + void *dmaAddress; + fDcbaArea = fStack->AllocateArea((void **)&fDcba, &dmaAddress, + sizeof(uint64) * XHCI_MAX_SLOTS, "DCBA Area"); + if (fDcbaArea < B_OK) { + TRACE_ERROR("unable to create the DCBA area\n"); + return B_ERROR; + } + memset(fDcba, 0, sizeof(uint64) * XHCI_MAX_SLOTS); + TRACE("setting DCBAAP\n"); + WriteOpReg(XHCI_DCBAAP_LO, (uint32)dmaAddress); + WriteOpReg(XHCI_DCBAAP_HI, 0); + + fErstArea = fStack->AllocateArea((void **)&fErst, &dmaAddress, + (MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb) + + sizeof(xhci_erst_element), + "USB XHCI ERST CMD_RING and EVENT_RING Area"); + + if (fErstArea < B_OK) { + TRACE_ERROR("unable to create the ERST AND RING area\n"); + delete_area(fDcbaArea); + return B_ERROR; + } + memset(fErst, 0, (MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb) + + sizeof(xhci_erst_element)); + + fErst->rs_addr = (uint32)dmaAddress + sizeof(xhci_erst_element); + fErst->rs_size = MAX_EVENTS; + fErst->rsvdz = 0; + + uint32 addr = (uint32)fErst + sizeof(xhci_erst_element); + fEventRing = (xhci_trb *)addr; + addr += MAX_EVENTS * sizeof(xhci_trb); + fCmdRing = (xhci_trb *)addr; + + TRACE("setting ERST size\n"); + WriteRunReg32(XHCI_ERSTSZ(0), XHCI_ERSTS_SET(1)); + + TRACE("setting ERDP addr = 0x%llx\n", fErst->rs_addr); + WriteRunReg32(XHCI_ERDP_LO(0), (uint32)fErst->rs_addr); + WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(fErst->rs_addr >> 32)); + + TRACE("setting ERST base addr = 0x%llx\n", (uint64)dmaAddress); + WriteRunReg32(XHCI_ERSTBA_LO(0), (uint32)dmaAddress); + WriteRunReg32(XHCI_ERSTBA_HI(0), 0); + + addr = fErst->rs_addr + MAX_EVENTS * sizeof(xhci_trb); + TRACE("setting CRCR addr = 0x%llx\n", (uint64)addr); + WriteOpReg(XHCI_CRCR_LO, addr | CRCR_RCS); + WriteOpReg(XHCI_CRCR_HI, 0); + //link trb + fCmdRing[MAX_COMMANDS - 1].qwtrb0 = addr; + + TRACE("setting interrupt rate\n"); + WriteRunReg32(XHCI_IMOD(0), 160);//4000 irq/s + + TRACE("enabling interrupt\n"); + WriteRunReg32(XHCI_IMAN(0), ReadRunReg32(XHCI_IMAN(0)) | IMAN_INTR_ENA); + + WriteOpReg(XHCI_CMD, CMD_RUN | CMD_EIE | CMD_HSEIE); + + fRootHubAddress = AllocateAddress(); + fRootHub = new(std::nothrow) XHCIRootHub(RootObject(), fRootHubAddress); + if (!fRootHub) { + TRACE_ERROR("no memory to allocate root hub\n"); + return B_NO_MEMORY; + } + + if (fRootHub->InitCheck() < B_OK) { + TRACE_ERROR("root hub failed init check\n"); + return fRootHub->InitCheck(); + } + + SetRootHub(fRootHub); + TRACE_ALWAYS("successfully started the controller\n"); + TRACE("No-Op test\n"); + QueueNoop(); return BusManager::Start(); } @@ -202,6 +333,10 @@ XHCI::Start() status_t XHCI::SubmitTransfer(Transfer *transfer) { + // short circuit the root hub + if (transfer->TransferPipe()->DeviceAddress() == fRootHubAddress) + return fRootHub->ProcessTransfer(this, transfer); + return B_OK; } @@ -315,6 +450,46 @@ XHCI::AddTo(Stack *stack) status_t XHCI::GetPortStatus(uint8 index, usb_port_status *status) { + if (index >= fPortCount) + return B_BAD_INDEX; + + status->status = status->change = 0; + uint32 portStatus = ReadOpReg(XHCI_PORTSC(index)); + TRACE("port status=0x%08lx\n", portStatus); + + // build the status + switch(PS_SPEED_GET(portStatus)) { + case 3: + status->status |= PORT_STATUS_HIGH_SPEED; + break; + case 2: + status->status |= PORT_STATUS_LOW_SPEED; + break; + default: + break; + } + + if (portStatus & PS_CCS) + status->status |= PORT_STATUS_CONNECTION; + if (portStatus & PS_PED) + status->status |= PORT_STATUS_ENABLE; + if (portStatus & PS_OCA) + status->status |= PORT_STATUS_OVER_CURRENT; + if (portStatus & PS_PR) + status->status |= PORT_STATUS_RESET; + if (portStatus & PS_PP) + status->status |= PORT_STATUS_POWER; + + // build the change + if (portStatus & PS_CSC) + status->change |= PORT_STATUS_CONNECTION; + if (portStatus & PS_PEC) + status->change |= PORT_STATUS_ENABLE; + if (portStatus & PS_OCC) + status->change |= PORT_STATUS_OVER_CURRENT; + if (portStatus & PS_PRC) + status->change |= PORT_STATUS_RESET; + return B_OK; } @@ -322,6 +497,36 @@ XHCI::GetPortStatus(uint8 index, usb_port_status *status) status_t XHCI::SetPortFeature(uint8 index, uint16 feature) { + TRACE("set port feature index %u feature %u\n", index, feature); + if (index >= fPortCount) + return B_BAD_INDEX; + + uint32 portRegister = XHCI_PORTSC(index); + uint32 portStatus = ReadOpReg(portRegister); + + switch (feature) { + case PORT_SUSPEND: + if ((portStatus & PS_PED ) == 0 || (portStatus & PS_PR) + || (portStatus & PS_PLS_MASK) >= PS_XDEV_U3) { + TRACE_ERROR("USB core suspending device not in U0/U1/U2.\n"); + return B_BAD_VALUE; + } + portStatus &= ~PS_CLEAR; + portStatus &= ~PS_PLS_MASK; + portStatus |= PS_LWS | PS_XDEV_U3; + WriteOpReg(portRegister, portStatus); + return B_OK; + + case PORT_RESET: + portStatus &= ~PS_CLEAR; + WriteOpReg(portRegister, portStatus | PS_PR); + return B_OK; + + case PORT_POWER: + portStatus &= ~PS_CLEAR; + WriteOpReg(portRegister, portStatus | PS_PP); + return B_OK; + } return B_BAD_VALUE; } @@ -329,26 +534,51 @@ XHCI::SetPortFeature(uint8 index, uint16 feature) status_t XHCI::ClearPortFeature(uint8 index, uint16 feature) { + TRACE("clear port feature index %u feature %u\n", index, feature); + if (index >= fPortCount) + return B_BAD_INDEX; + + uint32 portRegister = XHCI_PORTSC(index); + uint32 portStatus = ReadOpReg(portRegister); + portStatus &= ~PS_CLEAR; + + switch (feature) { + case PORT_SUSPEND: + portStatus = ReadOpReg(portRegister); + if (portStatus & PS_PR) + return B_BAD_VALUE; + if (portStatus & PS_XDEV_U3) { + if ((portStatus & PS_PED) == 0) + return B_BAD_VALUE; + portStatus &= ~PS_CLEAR; + portStatus &= ~PS_PLS_MASK; + WriteOpReg(portRegister, portStatus | PS_XDEV_U0 | PS_LWS); + } + return B_OK; + case PORT_ENABLE: + WriteOpReg(portRegister, portStatus | PS_PED); + return B_OK; + case PORT_POWER: + WriteOpReg(portRegister, portStatus & ~PS_PP); + return B_OK; + case C_PORT_CONNECTION: + WriteOpReg(portRegister, portStatus | PS_CSC); + return B_OK; + case C_PORT_ENABLE: + WriteOpReg(portRegister, portStatus | PS_PEC); + return B_OK; + case C_PORT_OVER_CURRENT: + WriteOpReg(portRegister, portStatus | PS_OCC); + return B_OK; + case C_PORT_RESET: + WriteOpReg(portRegister, portStatus | PS_PRC); + return B_OK; + } + return B_BAD_VALUE; } -status_t -XHCI::ResetPort(uint8 index) -{ - TRACE("reset port %d\n", index); - - return B_OK; -} - - -status_t -XHCI::SuspendPort(uint8 index) -{ - return B_OK; -} - - status_t XHCI::ControllerHalt() { @@ -388,13 +618,6 @@ XHCI::ControllerReset() } -status_t -XHCI::LightReset() -{ - return B_ERROR; -} - - int32 XHCI::InterruptHandler(void *data) { @@ -405,11 +628,215 @@ XHCI::InterruptHandler(void *data) int32 XHCI::Interrupt() { + acquire_spinlock(&fSpinlock); + + uint32 status = ReadOpReg(XHCI_STS); + uint32 temp = ReadRunReg32(XHCI_IMAN(0)); + WriteOpReg(XHCI_STS, status); + WriteRunReg32(XHCI_IMAN(0), temp); + TRACE("STS: %lx IRQ_PENDING: %lx\n", status, temp); + int32 result = B_HANDLED_INTERRUPT; + + if (status & STS_HSE) { + TRACE_ERROR("Host System Error\n"); + return result; + } + if (status & STS_HCE) { + TRACE_ERROR("Host Controller Error\n"); + return result; + } + uint16 i = fEventIdx; + uint8 j = fEventCcs; + uint8 t = 2; + + while (1) { + temp = fEventRing[i].dwtrb3; + uint8 k = (temp & TRB_3_CYCLE_BIT) ? 1 : 0; + if (j != k) + break; + + uint8 event = TRB_TYPE_GET(temp); + + TRACE("event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n", i, event, + fEventRing[i].qwtrb0, fEventRing[i].dwtrb2, fEventRing[i].dwtrb3); + switch (event) { + case TRB_COMPLETION: + HandleCmdComplete(&fEventRing[i]); + result = B_INVOKE_SCHEDULER; + break; + default: + TRACE_ERROR("Unhandled event = %u\n", event); + break; + } + + i++; + if (i == MAX_EVENTS) { + i = 0; + j ^= 1; + if (!--t) + break; + } + } + + fEventIdx = i; + fEventCcs = j; + + uint64 addr = fErst->rs_addr + i * sizeof(xhci_trb); + addr |= ERST_EHB; + WriteRunReg32(XHCI_ERDP_LO(0), (uint32)addr); + WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(addr >> 32)); + + + release_spinlock(&fSpinlock); return result; } + +void +XHCI::Ring() +{ + TRACE("Ding Dong!\n") + WriteDoorReg32(XHCI_DOORBELL(0), 0); + /* Flush PCI posted writes */ + ReadDoorReg32(XHCI_DOORBELL(0)); +} + + +void +XHCI::QueueCommand(xhci_trb *trb) +{ + uint8 i, j; + uint32 temp; + + i = fCmdIdx; + j = fCmdCcs; + + TRACE("command[%u] = %lx (0x%016llx, 0x%08lx, 0x%08lx)\n", + i, TRB_TYPE_GET(trb->dwtrb3), + trb->qwtrb0, trb->dwtrb2, trb->dwtrb3); + + fCmdRing[i].qwtrb0 = trb->qwtrb0; + fCmdRing[i].dwtrb2 = trb->dwtrb2; + temp = trb->dwtrb3; + + if (j) + temp |= TRB_3_CYCLE_BIT; + else + temp &= ~TRB_3_CYCLE_BIT; + temp &= ~TRB_3_TC_BIT; + fCmdRing[i].dwtrb3 = temp; + + fCmdAddr = fErst->rs_addr + (MAX_EVENTS + i) * sizeof(xhci_trb); + + i++; + + if (i == (MAX_COMMANDS - 1)) { + if (j) + temp = TRB_3_CYCLE_BIT | TRB_TYPE(TRB_LINK); + else + temp = TRB_TYPE(TRB_LINK); + fCmdRing[i].dwtrb3 = temp; + + i = 0; + j ^= 1; + } + + fCmdIdx = i; + fCmdCcs = j; +} + + +void +XHCI::HandleCmdComplete(xhci_trb *trb) +{ + if (fCmdAddr == trb->qwtrb0) { + TRACE("Received command event\n"); + fCmdResult[0] = trb->dwtrb2; + fCmdResult[1] = trb->dwtrb3; + release_sem_etc(fCmdCompSem, 1, B_DO_NOT_RESCHEDULE); + } + +} + + +void +XHCI::QueueNoop() +{ + xhci_trb trb; + uint32 temp; + + trb.qwtrb0 = 0; + trb.dwtrb2 = 0; + temp = TRB_TYPE(TRB_TR_NOOP); + trb.dwtrb3 = temp; + cpu_status state = disable_interrupts(); + acquire_spinlock(&fSpinlock); + QueueCommand(&trb); + Ring(); + release_spinlock(&fSpinlock); + restore_interrupts(state); +} + + +int32 +XHCI::CmdCompThread(void *data) +{ + ((XHCI *)data)->CmdComplete(); + return B_OK; +} + + +void +XHCI::CmdComplete() +{ + while (!fStopThreads) { + if (acquire_sem(fCmdCompSem) < B_OK) + continue; + + // eat up sems that have been released by multiple interrupts + int32 semCount = 0; + get_sem_count(fCmdCompSem, &semCount); + if (semCount > 0) + acquire_sem_etc(fCmdCompSem, semCount, B_RELATIVE_TIMEOUT, 0); + + TRACE("Command Complete\n"); + if (COMP_CODE_GET(fCmdResult[0]) != COMP_SUCCESS) { + TRACE_ERROR("unsuccessful no-op command\n"); + //continue; + } + snooze(1000000 * 5); + QueueNoop(); + } +} + + +int32 +XHCI::FinishThread(void *data) +{ + ((XHCI *)data)->FinishTransfers(); + return B_OK; +} + + +void +XHCI::FinishTransfers() +{ + while (!fStopThreads) { + if (acquire_sem(fFinishTransfersSem) < B_OK) + continue; + + // eat up sems that have been released by multiple interrupts + int32 semCount = 0; + get_sem_count(fFinishTransfersSem, &semCount); + if (semCount > 0) + acquire_sem_etc(fFinishTransfersSem, semCount, B_RELATIVE_TIMEOUT, 0); + + TRACE("finishing transfers\n"); + } +} + inline void XHCI::WriteOpReg(uint32 reg, uint32 value) { @@ -451,3 +878,30 @@ XHCI::WriteCapReg32(uint32 reg, uint32 value) *(volatile uint32 *)(fCapabilityRegisters + reg) = value; } + +inline uint32 +XHCI::ReadRunReg32(uint32 reg) +{ + return *(volatile uint32 *)(fRuntimeRegisters + reg); +} + + +inline void +XHCI::WriteRunReg32(uint32 reg, uint32 value) +{ + *(volatile uint32 *)(fRuntimeRegisters + reg) = value; +} + + +inline uint32 +XHCI::ReadDoorReg32(uint32 reg) +{ + return *(volatile uint32 *)(fDoorbellRegisters + reg); +} + + +inline void +XHCI::WriteDoorReg32(uint32 reg, uint32 value) +{ + *(volatile uint32 *)(fDoorbellRegisters + reg) = value; +} diff --git a/src/add-ons/kernel/busses/usb/xhci.h b/src/add-ons/kernel/busses/usb/xhci.h index 6a8867ae88..1bc99ab79e 100644 --- a/src/add-ons/kernel/busses/usb/xhci.h +++ b/src/add-ons/kernel/busses/usb/xhci.h @@ -14,8 +14,43 @@ #include "xhci_hardware.h" +#define MAX_EVENTS (16 * 13) +#define MAX_COMMANDS (16 * 1) +#define XHCI_MAX_SLOTS 256 +#define XHCI_MAX_PORTS 127 + + struct pci_info; struct pci_module_info; +class XHCIRootHub; + + +struct xhci_trb { + uint64 qwtrb0; + uint32 dwtrb2; + uint32 dwtrb3; +}; + + +struct xhci_segment { + xhci_trb * trbs; + xhci_segment * next; +}; + + +struct xhci_ring { + xhci_segment * first_seg; + xhci_trb * enqueue; + xhci_trb * dequeue; +}; + + +// Section 6.5 +struct xhci_erst_element { + uint64 rs_addr; + uint32 rs_size; + uint32 rsvdz; +} __attribute__((__aligned__(64))); class XHCI : public BusManager { @@ -32,51 +67,107 @@ public: static status_t AddTo(Stack *stack); - // Port operations for root hub + // Port operations for root hub uint8 PortCount() { return fPortCount; }; status_t GetPortStatus(uint8 index, usb_port_status *status); status_t SetPortFeature(uint8 index, uint16 feature); status_t ClearPortFeature(uint8 index, uint16 feature); - status_t ResetPort(uint8 index); - status_t SuspendPort(uint8 index); - virtual const char * TypeName() const { return "xhci"; }; private: - // Controller resets + // Controller resets status_t ControllerReset(); status_t ControllerHalt(); - status_t LightReset(); - // Interrupt functions + // Interrupt functions static int32 InterruptHandler(void *data); int32 Interrupt(); + // Transfer management + static int32 FinishThread(void *data); + void FinishTransfers(); - // Operational register functions + // Command + void QueueCommand(xhci_trb *trb); + void HandleCmdComplete(xhci_trb *trb); + + //Doorbell + void Ring(); + + //no-op + void QueueNoop(); + static int32 CmdCompThread(void *data); + void CmdComplete(); + + // Operational register functions inline void WriteOpReg(uint32 reg, uint32 value); inline uint32 ReadOpReg(uint32 reg); - // Capability register functions + // Capability register functions inline uint8 ReadCapReg8(uint32 reg); inline uint16 ReadCapReg16(uint32 reg); inline uint32 ReadCapReg32(uint32 reg); inline void WriteCapReg32(uint32 reg, uint32 value); + // Runtime register functions + inline uint32 ReadRunReg32(uint32 reg); + inline void WriteRunReg32(uint32 reg, uint32 value); + + // Doorbell register functions + inline uint32 ReadDoorReg32(uint32 reg); + inline void WriteDoorReg32(uint32 reg, uint32 value); + static pci_module_info * sPCIModule; uint8 * fCapabilityRegisters; uint8 * fOperationalRegisters; uint8 * fRuntimeRegisters; + uint8 * fDoorbellRegisters; area_id fRegisterArea; pci_info * fPCIInfo; Stack * fStack; - // Root Hub + area_id fErstArea; + xhci_erst_element * fErst; + xhci_trb * fEventRing; + xhci_trb * fCmdRing; + uint64 fCmdAddr; + uint32 fCmdResult[2]; - // Port management + area_id fDcbaArea; + uint8 * fDcba; + + spinlock fSpinlock; + + sem_id fCmdCompSem; + thread_id fCmdCompThread; + sem_id fFinishTransfersSem; + thread_id fFinishThread; + bool fStopThreads; + + // Root Hub + XHCIRootHub * fRootHub; + uint8 fRootHubAddress; + + // Port management uint8 fPortCount; + uint8 fSlotCount; + + uint16 fEventIdx; + uint16 fCmdIdx; + uint8 fEventCcs; + uint8 fCmdCcs; +}; + + +class XHCIRootHub : public Hub { +public: + XHCIRootHub(Object *rootObject, + int8 deviceAddress); + +static status_t ProcessTransfer(XHCI *ehci, + Transfer *transfer); }; diff --git a/src/add-ons/kernel/busses/usb/xhci_hardware.h b/src/add-ons/kernel/busses/usb/xhci_hardware.h index 7d832362b0..ea4238c54d 100644 --- a/src/add-ons/kernel/busses/usb/xhci_hardware.h +++ b/src/add-ons/kernel/busses/usb/xhci_hardware.h @@ -13,31 +13,71 @@ #define XHCI_CAPLENGTH 0x00 // Capability Register Length #define XHCI_HCIVERSION 0x02 // Interface Version Number #define XHCI_HCSPARAMS1 0x04 // Structural Parameters 1 +// HCSPARAMS1 +#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) +#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) #define XHCI_HCSPARAMS2 0x08 // Structural Parameters 2 #define XHCI_HCSPARAMS3 0x0C // Structural Parameters 3 #define XHCI_HCCPARAMS 0x10 // Capability Parameters +#define XHCI_DBOFF 0x14 // Doorbell Register offset #define XHCI_RTSOFF 0x18 // Runtime Register Space offset // Host Controller Operational Registers #define XHCI_CMD 0x00 // USB Command -#define XHCI_STS 0x04 // USB Status - - // USB Command Register +#define CMD_RUN (1 << 0) #define CMD_HCRST (1 << 1) // Host Controller Reset +#define CMD_EIE (1 << 2) +#define CMD_HSEIE (1 << 3) - +#define XHCI_STS 0x04 // USB Status // USB Status Register -#define STS_HCH (1<<0) +#define STS_HCH (1 << 0) +#define STS_HSE (1 << 2) +#define STS_PCD (1 << 4) #define STS_CNR (1<<11) +#define STS_HCE (1 << 12) +#define XHCI_PAGESIZE 0x08 // PAGE SIZE +// Section 5.4.5 +#define XHCI_CRCR_LO 0x18 +#define XHCI_CRCR_HI 0x1C +#define CRCR_RCS (1<<0) +// Section 5.4.6 +#define XHCI_DCBAAP_LO 0x30 +#define XHCI_DCBAAP_HI 0x34 +// Section 5.4.7 +#define XHCI_CONFIG 0x38 +// Host Controller Runtime Registers +// Section 5.5.2.1 +#define XHCI_IMAN(n) (0x0020 + (0x20 * (n))) +// IMAN +#define IMAN_INTR_ENA 0x00000002 +// Section 5.5.2.2 +#define XHCI_IMOD(n) (0x0024 + (0x20 * (n))) +// Section 5.5.2.3.1 +#define XHCI_ERSTSZ(n) (0x0028 + (0x20 * (n))) +// ERSTSZ +#define XHCI_ERSTS_SET(x) ((x) & 0xFFFF) +// Section 5.5.2.3.2 +#define XHCI_ERSTBA_LO(n) (0x0030 + (0x20 * (n))) +#define XHCI_ERSTBA_HI(n) (0x0034 + (0x20 * (n))) +// Section 5.5.2.3.3 +#define XHCI_ERDP_LO(n) (0x0038 + (0x20 * (n))) +#define XHCI_ERDP_HI(n) (0x003C + (0x20 * (n))) +// Event Handler Busy (EHB) +#define ERST_EHB (1 << 3) +// Host Controller Doorbell Registers +#define XHCI_DOORBELL(n) (0x0000 + (4 * (n))) // Extended Capabilities -#define XHCI_LEGSUP_CAPID_MASK 0xff +#define XECP_ID(x) ((x) & 0xff) +#define HCS0_XECP(x) (((x) >> 16) & 0xffff) +#define XECP_NEXT(x) (((x) >> 8) & 0xff) #define XHCI_LEGSUP_CAPID 0x01 #define XHCI_LEGSUP_OSOWNED (1 << 24) // OS Owned Semaphore #define XHCI_LEGSUP_BIOSOWNED (1 << 16) // BIOS Owned Semaphore @@ -46,5 +86,48 @@ #define XHCI_LEGCTLSTS_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) -#endif // !XHCI_HARDWARE_H +// Port status Registers +// Section 5.4.8 +#define XHCI_PORTSC(n) (0x3F0 + (0x10 * (n))) +#define PS_CCS (1 << 0) +#define PS_PED (1 << 1) +#define PS_OCA (1 << 3) +#define PS_PR (1 << 4) +#define PS_PP (1 << 9) +#define PS_SPEED_GET(x) (((x) >> 10) & 0xF) +#define PS_LWS (1 << 16) +#define PS_CSC (1 << 17) +#define PS_PEC (1 << 18) +#define PS_WRC (1 << 19) +#define PS_OCC (1 << 20) +#define PS_PRC (1 << 21) +#define PS_PLC (1 << 22) +#define PS_CEC (1 << 23) +#define PS_CAS (1 << 24) +#define PS_WCE (1 << 25) +#define PS_WDE (1 << 26) +#define PS_WPR (1 << 30) +#define PS_CLEAR 0x80FF00F7U + +#define PS_PLS_MASK (0xf << 5) +#define PS_XDEV_U0 (0x0 << 5) +#define PS_XDEV_U3 (0x3 << 5) + + +// Completion Code +#define COMP_CODE_GET(x) (((x) >> 24) & 0xff) +#define COMP_SUCCESS 0x01 + + +// TRB Type +#define TRB_TYPE(x) ((x) << 10) +#define TRB_TYPE_GET(x) (((x) >> 10) & 0x3F) +#define TRB_LINK 6 +#define TRB_TR_NOOP 8 +#define TRB_TRANSFER 32 +#define TRB_COMPLETION 33 +#define TRB_3_CYCLE_BIT (1U << 0) +#define TRB_3_TC_BIT (1U << 1) + +#endif // !XHCI_HARDWARE_H diff --git a/src/add-ons/kernel/busses/usb/xhci_rh.cpp b/src/add-ons/kernel/busses/usb/xhci_rh.cpp new file mode 100644 index 0000000000..7b7cbd3894 --- /dev/null +++ b/src/add-ons/kernel/busses/usb/xhci_rh.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 2011, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * Jian Chiang + */ + + +#define TRACE_USB +#include "xhci.h" + +#define USB_MODULE_NAME "xhci roothub" + +static usb_device_descriptor sXHCIRootHubDevice = +{ + 18, // Descriptor length + USB_DESCRIPTOR_DEVICE, // Descriptor type + 0x300, // USB 3.0 + 0x09, // Class (9 = Hub) + 0, // Subclass + 3, // Protocol + 9, // Max packet size on endpoint 0 + 0, // Vendor ID + 0, // Product ID + 0x003, // Version + 1, // Index of manufacturer string + 2, // Index of product string + 0, // Index of serial number string + 1 // Number of configurations +}; + + +struct usb_endpoint_ss_comp_descriptor { + uint8 length; + uint8 descriptor_type; + uint16 burst; + uint8 attributes; + uint16 internal; +} _PACKED; + + +struct xhci_root_hub_configuration_s { + usb_configuration_descriptor configuration; + usb_interface_descriptor interface; + usb_endpoint_descriptor endpoint; + usb_endpoint_ss_comp_descriptor endpc; + usb_hub_descriptor hub; +} _PACKED; + + +static xhci_root_hub_configuration_s sXHCIRootHubConfig = +{ + { // configuration descriptor + 9, // Descriptor length + USB_DESCRIPTOR_CONFIGURATION, // Descriptor type + sizeof(sXHCIRootHubConfig), // Total length of configuration (including + // interface, endpoint and hub descriptors) + 1, // Number of interfaces + 1, // Value of this configuration + 0, // Index of configuration string + 0x40, // Attributes (0x40 = self powered) + 0 // Max power (0, since self powered) + }, + + { // interface descriptor + 9, // Descriptor length + USB_DESCRIPTOR_INTERFACE, // Descriptor type + 0, // Interface number + 0, // Alternate setting + 1, // Number of endpoints + 0x09, // Interface class (9 = Hub) + 0, // Interface subclass + 0, // Interface protocol + 0 // Index of interface string + }, + + { // endpoint descriptor + 7, // Descriptor length + USB_DESCRIPTOR_ENDPOINT, // Descriptor type + USB_REQTYPE_DEVICE_IN | 1, // Endpoint address (first in IN endpoint) + 0x03, // Attributes (0x03 = interrupt endpoint) + 2, // Max packet size + 0xff // Interval + }, + + { // endpoint companion descriptor + 7, + 0x30, + 0, + 0, + 0 + }, + + { // hub descriptor + 9, // Descriptor length (including + // deprecated power control mask) + USB_DESCRIPTOR_HUB, // Descriptor type + 0x0f, // Number of ports + 0x0000, // Hub characteristics + 10, // Power on to power good (in 2ms units) + 0, // Maximum current (in mA) + 0x00, // All ports are removable + 0xff // Deprecated power control mask + } +}; + + +struct xhci_root_hub_string_s { + uint8 length; + uint8 descriptor_type; + uint16 unicode_string[12]; +} _PACKED; + + +static xhci_root_hub_string_s sXHCIRootHubStrings[3] = { + { + 4, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + { + 0x0409 // Supported language IDs (English US) + } + }, + + { + 22, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + { + 'H', 'A', 'I', 'K', 'U', // Characters + ' ', 'I', 'n', 'c', '.' + } + }, + + { + 26, // Descriptor length + USB_DESCRIPTOR_STRING, // Descriptor type + { + 'X', 'H', 'C', 'I', ' ', // Characters + 'R', 'o', 'o', 't', 'H', + 'u', 'b' + } + } +}; + + +XHCIRootHub::XHCIRootHub(Object *rootObject, int8 deviceAddress) + : Hub(rootObject, 0, rootObject->GetStack()->IndexOfBusManager(rootObject->GetBusManager()), + sXHCIRootHubDevice, deviceAddress, USB_SPEED_SUPER, true) +{ +} + + +status_t +XHCIRootHub::ProcessTransfer(XHCI *xhci, Transfer *transfer) +{ + if ((transfer->TransferPipe()->Type() & USB_OBJECT_CONTROL_PIPE) == 0) + return B_ERROR; + + usb_request_data *request = transfer->RequestData(); + TRACE_MODULE("request: %d\n", request->Request); + + status_t status = B_TIMED_OUT; + size_t actualLength = 0; + switch (request->Request) { + case USB_REQUEST_GET_STATUS: { + if (request->Index == 0) { + // get hub status + actualLength = MIN(sizeof(usb_port_status), + transfer->DataLength()); + // the hub reports whether the local power failed (bit 0) + // and if there is a over-current condition (bit 1). + // everything as 0 means all is ok. + memset(transfer->Data(), 0, actualLength); + status = B_OK; + break; + } + + usb_port_status portStatus; + if (xhci->GetPortStatus(request->Index - 1, &portStatus) >= B_OK) { + actualLength = MIN(sizeof(usb_port_status), transfer->DataLength()); + memcpy(transfer->Data(), (void *)&portStatus, actualLength); + status = B_OK; + } + + break; + } + + case USB_REQUEST_SET_ADDRESS: + if (request->Value >= 128) { + status = B_TIMED_OUT; + break; + } + + TRACE_MODULE("set address: %d\n", request->Value); + status = B_OK; + break; + + case USB_REQUEST_GET_DESCRIPTOR: + TRACE_MODULE("get descriptor: %d\n", request->Value >> 8); + + switch (request->Value >> 8) { + case USB_DESCRIPTOR_DEVICE: { + actualLength = MIN(sizeof(usb_device_descriptor), + transfer->DataLength()); + memcpy(transfer->Data(), (void *)&sXHCIRootHubDevice, + actualLength); + status = B_OK; + break; + } + + case USB_DESCRIPTOR_CONFIGURATION: { + actualLength = MIN(sizeof(xhci_root_hub_configuration_s), + transfer->DataLength()); + sXHCIRootHubConfig.hub.num_ports = xhci->PortCount(); + memcpy(transfer->Data(), (void *)&sXHCIRootHubConfig, + actualLength); + status = B_OK; + break; + } + + case USB_DESCRIPTOR_STRING: { + uint8 index = request->Value & 0x00ff; + if (index > 2) + break; + + actualLength = MIN(sXHCIRootHubStrings[index].length, + transfer->DataLength()); + memcpy(transfer->Data(), (void *)&sXHCIRootHubStrings[index], + actualLength); + status = B_OK; + break; + } + + case USB_DESCRIPTOR_HUB: { + actualLength = MIN(sizeof(usb_hub_descriptor), + transfer->DataLength()); + sXHCIRootHubConfig.hub.num_ports = xhci->PortCount(); + memcpy(transfer->Data(), (void *)&sXHCIRootHubConfig.hub, + actualLength); + status = B_OK; + break; + } + } + break; + + case USB_REQUEST_SET_CONFIGURATION: + status = B_OK; + break; + + case USB_REQUEST_CLEAR_FEATURE: { + if (request->Index == 0) { + // we don't support any hub changes + TRACE_MODULE_ERROR("clear feature: no hub changes\n"); + break; + } + + TRACE_MODULE("clear feature: %d\n", request->Value); + if (xhci->ClearPortFeature(request->Index - 1, request->Value) >= B_OK) + status = B_OK; + break; + } + + case USB_REQUEST_SET_FEATURE: { + if (request->Index == 0) { + // we don't support any hub changes + TRACE_MODULE_ERROR("set feature: no hub changes\n"); + break; + } + + TRACE_MODULE("set feature: %d\n", request->Value); + if (xhci->SetPortFeature(request->Index - 1, request->Value) >= B_OK) + status = B_OK; + break; + } + } + + transfer->Finished(status, actualLength); + delete transfer; + return B_OK; +}