Author: korli Date: 2011-07-29 12:06:34 +0200 (Fri, 29 Jul 2011) New Revision: 42511 Changeset: https://dev.haiku-os.org/changeset/42511 Added: haiku/trunk/src/add-ons/kernel/busses/usb/xhci_rh.cpp Modified: haiku/trunk/src/add-ons/kernel/bus_managers/usb/usb_private.h haiku/trunk/src/add-ons/kernel/busses/usb/Jamfile haiku/trunk/src/add-ons/kernel/busses/usb/xhci.cpp haiku/trunk/src/add-ons/kernel/busses/usb/xhci.h haiku/trunk/src/add-ons/kernel/busses/usb/xhci_hardware.h Log: 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 Modified: haiku/trunk/src/add-ons/kernel/bus_managers/usb/usb_private.h =================================================================== --- haiku/trunk/src/add-ons/kernel/bus_managers/usb/usb_private.h 2011-07-29 06:12:01 UTC (rev 42510) +++ haiku/trunk/src/add-ons/kernel/bus_managers/usb/usb_private.h 2011-07-29 10:06:34 UTC (rev 42511) @@ -90,7 +90,8 @@ 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; Modified: haiku/trunk/src/add-ons/kernel/busses/usb/Jamfile =================================================================== --- haiku/trunk/src/add-ons/kernel/busses/usb/Jamfile 2011-07-29 06:12:01 UTC (rev 42510) +++ haiku/trunk/src/add-ons/kernel/busses/usb/Jamfile 2011-07-29 10:06:34 UTC (rev 42511) @@ -28,6 +28,7 @@ KernelAddon <usb>xhci : xhci.cpp + xhci_rh.cpp : libusb.a : xhci.rdef ; Modified: haiku/trunk/src/add-ons/kernel/busses/usb/xhci.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/busses/usb/xhci.cpp 2011-07-29 06:12:01 UTC (rev 42510) +++ haiku/trunk/src/add-ons/kernel/busses/usb/xhci.cpp 2011-07-29 10:06:34 UTC (rev 42511) @@ -63,7 +63,22 @@ 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 @@ // 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 @@ 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 @@ 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 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); - uint32 extendedCapPointer = ((ReadCapReg32(XHCI_HCCPARAMS) >> 16) & 0xffff) - << 2; - if (extendedCapPointer > 0) { - TRACE("extended capabilities register at %ld\n", extendedCapPointer); + for (int32 i = 0; i < 20; i++) { + eec = ReadCapReg32(eecp); - 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 ((eec & XHCI_LEGSUP_BIOSOWNED) == 0) + break; - if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) == 0) - break; + TRACE_ALWAYS("controller is still bios owned, waiting\n"); + snooze(50000); + } - 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"); + } - 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"); + // 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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,30 +497,85 @@ status_t XHCI::SetPortFeature(uint8 index, uint16 feature) { - return B_BAD_VALUE; -} + 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); -status_t -XHCI::ClearPortFeature(uint8 index, uint16 feature) -{ + 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; } status_t -XHCI::ResetPort(uint8 index) +XHCI::ClearPortFeature(uint8 index, uint16 feature) { - TRACE("reset port %d\n", index); + TRACE("clear port feature index %u feature %u\n", index, feature); + if (index >= fPortCount) + return B_BAD_INDEX; - return B_OK; -} + 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; + } -status_t -XHCI::SuspendPort(uint8 index) -{ - return B_OK; + return B_BAD_VALUE; } @@ -388,13 +618,6 @@ } -status_t -XHCI::LightReset() -{ - return B_ERROR; -} - - int32 XHCI::InterruptHandler(void *data) { @@ -405,11 +628,215 @@ 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 @@ *(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; +} Modified: haiku/trunk/src/add-ons/kernel/busses/usb/xhci.h =================================================================== --- haiku/trunk/src/add-ons/kernel/busses/usb/xhci.h 2011-07-29 06:12:01 UTC (rev 42510) +++ haiku/trunk/src/add-ons/kernel/busses/usb/xhci.h 2011-07-29 10:06:34 UTC (rev 42511) @@ -14,10 +14,45 @@ #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 { public: XHCI(pci_info *info, Stack *stack); @@ -32,52 +67,108 @@ 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); +}; + + #endif // !XHCI_H Modified: haiku/trunk/src/add-ons/kernel/busses/usb/xhci_hardware.h =================================================================== --- haiku/trunk/src/add-ons/kernel/busses/usb/xhci_hardware.h 2011-07-29 06:12:01 UTC (rev 42510) +++ haiku/trunk/src/add-ons/kernel/busses/usb/xhci_hardware.h 2011-07-29 10:06:34 UTC (rev 42511) @@ -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)) +// 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) [... truncated: 327 lines follow ...]