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
This commit is contained in:
Jérôme Duval 2011-07-29 10:06:34 +00:00
parent 3b1fd3270e
commit d6e4f54f2d
6 changed files with 1002 additions and 91 deletions

View File

@ -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;

View File

@ -28,6 +28,7 @@ KernelAddon <usb>ehci :
KernelAddon <usb>xhci :
xhci.cpp
xhci_rh.cpp
: libusb.a
: xhci.rdef
;

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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

View File

@ -0,0 +1,281 @@
/*
* Copyright 2011, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* Jian Chiang <j.jian.chiang@gmail.com>
*/
#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;
}