XHCI improvements

* remove the xhci cmd complete thread
* creation of the scratchpad area
* wait for running status after starting the controller
* increase delays on controller reset (250ms instead of 100ms)
* use of SpinLocker from AutoLock.h
* add a DoCommand() method to queue a command, ring and wait for the event
  response
* add a method for each command
* XHCI_PORTSC macro was off 1 port
* add definitions for TRB types, Completion Codes.
This commit is contained in:
Jérôme Duval
2012-05-01 22:13:20 +02:00
parent 9930418fd7
commit d11be97572
3 changed files with 475 additions and 164 deletions
+280 -108
View File
@@ -15,6 +15,8 @@
#include <USB3.h>
#include <KernelExport.h>
#include <util/AutoLock.h>
#define TRACE_USB
#include "xhci.h"
@@ -67,7 +69,6 @@ XHCI::XHCI(pci_info *info, Stack *stack)
fDcbaArea(-1),
fSpinlock(B_SPINLOCK_INITIALIZER),
fCmdCompSem(-1),
fCmdCompThread(-1),
fFinishTransfersSem(-1),
fFinishThread(-1),
fStopThreads(false),
@@ -75,6 +76,7 @@ XHCI::XHCI(pci_info *info, Stack *stack)
fRootHubAddress(0),
fPortCount(0),
fSlotCount(0),
fScratchpadCount(0),
fEventIdx(0),
fCmdIdx(0),
fEventCcs(1),
@@ -99,13 +101,13 @@ XHCI::XHCI(pci_info *info, Stack *stack)
// map the registers
uint32 offset = fPCIInfo->u.h0.base_registers[0] & (B_PAGE_SIZE - 1);
addr_t physicalAddress = fPCIInfo->u.h0.base_registers[0] - offset;
phys_addr_t physicalAddress = fPCIInfo->u.h0.base_registers[0] - offset;
size_t mapSize = (fPCIInfo->u.h0.base_register_sizes[0] + offset
+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
TRACE("map physical memory 0x%08lx (base: 0x%08lx; offset: %lx); size: %ld\n",
fPCIInfo->u.h0.base_registers[0], physicalAddress, offset,
fPCIInfo->u.h0.base_register_sizes[0]);
TRACE("map physical memory 0x%08lx (base: 0x%08" B_PRIxPHYSADDR "; offset:"
" %lx); size: %ld\n", fPCIInfo->u.h0.base_registers[0],
physicalAddress, offset, fPCIInfo->u.h0.base_register_sizes[0]);
fRegisterArea = map_physical_memory("XHCI memory mapped registers",
physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS,
@@ -191,11 +193,6 @@ XHCI::XHCI(pci_info *info, Stack *stack)
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,
@@ -218,8 +215,9 @@ XHCI::~XHCI()
delete_sem(fFinishTransfersSem);
delete_area(fRegisterArea);
delete_area(fErstArea);
for (uint32 i = 0; i < fScratchpadCount; i++)
delete_area(fScratchpadArea[i]);
delete_area(fDcbaArea);
wait_for_thread(fCmdCompThread, &result);
wait_for_thread(fFinishThread, &result);
put_module(B_PCI_MODULE_NAME);
}
@@ -240,29 +238,60 @@ XHCI::Start()
// 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);
fPortCount = HCS_MAX_PORTS(capabilities);
if (fPortCount == 0) {
TRACE_ERROR("Invalid number of ports: %u\n", fPortCount);
fPortCount = 0;
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");
uint32 params2 = ReadCapReg32(XHCI_HCSPARAMS2);
fScratchpadCount = HCS_MAX_SC_BUFFERS(params2);
if (fScratchpadCount > XHCI_MAX_SCRATCHPADS) {
TRACE_ERROR("Invalid number of scratchpads: %u\n", fScratchpadCount);
return B_ERROR;
}
WriteOpReg(XHCI_DNCTRL, 0);
// allocate Device Context Base Address array
addr_t dmaAddress;
fDcbaArea = fStack->AllocateArea((void **)&fDcba, (void**)&dmaAddress,
sizeof(*fDcba), "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);
memset(fDcba, 0, sizeof(*fDcba));
memset(fScratchpadArea, 0, sizeof(fScratchpadArea));
memset(fScratchpad, 0, sizeof(fScratchpad));
fErstArea = fStack->AllocateArea((void **)&fErst, &dmaAddress,
(MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb)
// setting the first address to the scratchpad array address
fDcba->baseAddress[0] = dmaAddress
+ offsetof(struct xhci_device_context_array, scratchpad);
// fill up the scratchpad array with scratchpad pages
for (uint32 i = 0; i < fScratchpadCount; i++) {
addr_t scratchDmaAddress;
fScratchpadArea[i] = fStack->AllocateArea((void **)&fScratchpad[i],
(void**)&scratchDmaAddress, B_PAGE_SIZE, "Scratchpad Area");
if (fScratchpadArea[i] < B_OK) {
TRACE_ERROR("unable to create the scratchpad area\n");
return B_ERROR;
}
fDcba->scratchpad[i] = scratchDmaAddress;
}
TRACE("setting DCBAAP %lx\n", dmaAddress);
WriteOpReg(XHCI_DCBAAP_LO, (uint32)dmaAddress);
WriteOpReg(XHCI_DCBAAP_HI, /*(uint32)(dmaAddress >> 32)*/0);
// allocate Event Ring Segment Table
uint8 *addr;
fErstArea = fStack->AllocateArea((void **)&addr, (void**)&dmaAddress,
(XHCI_MAX_COMMANDS + XHCI_MAX_EVENTS) * sizeof(xhci_trb)
+ sizeof(xhci_erst_element),
"USB XHCI ERST CMD_RING and EVENT_RING Area");
@@ -271,16 +300,18 @@ XHCI::Start()
delete_area(fDcbaArea);
return B_ERROR;
}
memset(fErst, 0, (MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb)
fErst = (xhci_erst_element *)addr;
memset(fErst, 0, (XHCI_MAX_COMMANDS + XHCI_MAX_EVENTS) * sizeof(xhci_trb)
+ sizeof(xhci_erst_element));
fErst->rs_addr = (uint32)dmaAddress + sizeof(xhci_erst_element);
fErst->rs_size = MAX_EVENTS;
// fill with Event Ring Segment Base Address and Event Ring Segment Size
fErst->rs_addr = (uint64)(dmaAddress + sizeof(xhci_erst_element));
fErst->rs_size = XHCI_MAX_EVENTS;
fErst->rsvdz = 0;
uint32 addr = (uint32)fErst + sizeof(xhci_erst_element);
addr += sizeof(xhci_erst_element);
fEventRing = (xhci_trb *)addr;
addr += MAX_EVENTS * sizeof(xhci_trb);
addr += XHCI_MAX_EVENTS * sizeof(xhci_trb);
fCmdRing = (xhci_trb *)addr;
TRACE("setting ERST size\n");
@@ -288,27 +319,37 @@ XHCI::Start()
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));
WriteRunReg32(XHCI_ERDP_HI(0), /*(uint32)(fErst->rs_addr >> 32)*/0);
TRACE("setting ERST base addr = 0x%llx\n", (uint64)dmaAddress);
TRACE("setting ERST base addr = 0x%lx\n", dmaAddress);
WriteRunReg32(XHCI_ERSTBA_LO(0), (uint32)dmaAddress);
WriteRunReg32(XHCI_ERSTBA_HI(0), 0);
WriteRunReg32(XHCI_ERSTBA_HI(0), /*(uint32)(dmaAddress >> 32)*/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);
dmaAddress += sizeof(xhci_erst_element) + XHCI_MAX_EVENTS * sizeof(xhci_trb);
TRACE("setting CRCR addr = 0x%lx\n", dmaAddress);
WriteOpReg(XHCI_CRCR_LO, (uint32)dmaAddress | CRCR_RCS);
WriteOpReg(XHCI_CRCR_HI, /*(uint32)(dmaAddress >> 32)*/0);
//link trb
fCmdRing[MAX_COMMANDS - 1].qwtrb0 = addr;
fCmdRing[XHCI_MAX_COMMANDS - 1].qwtrb0 = dmaAddress;
TRACE("setting interrupt rate\n");
WriteRunReg32(XHCI_IMOD(0), 160);//4000 irq/s
WriteRunReg32(XHCI_IMOD(0), 160); // 25000 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);
// wait for start up state
int32 tries = 100;
while ((ReadOpReg(XHCI_STS) & STS_HCH) != 0) {
snooze(1000);
if (tries-- < 0) {
TRACE_ERROR("start up timeout\n");
break;
}
}
fRootHubAddress = AllocateAddress();
fRootHub = new(std::nothrow) XHCIRootHub(RootObject(), fRootHubAddress);
if (!fRootHub) {
@@ -324,8 +365,10 @@ XHCI::Start()
SetRootHub(fRootHub);
TRACE_ALWAYS("successfully started the controller\n");
#ifdef TRACE_USB
TRACE("No-Op test\n");
QueueNoop();
Noop();
#endif
return BusManager::Start();
}
@@ -337,6 +380,11 @@ XHCI::SubmitTransfer(Transfer *transfer)
if (transfer->TransferPipe()->DeviceAddress() == fRootHubAddress)
return fRootHub->ProcessTransfer(this, transfer);
TRACE("SubmitTransfer()\n");
Pipe *pipe = transfer->TransferPipe();
if (pipe->Type() & USB_OBJECT_ISO_PIPE)
return B_UNSUPPORTED;
return B_OK;
}
@@ -458,7 +506,7 @@ XHCI::GetPortStatus(uint8 index, usb_port_status *status)
TRACE("port status=0x%08lx\n", portStatus);
// build the status
switch(PS_SPEED_GET(portStatus)) {
switch (PS_SPEED_GET(portStatus)) {
case 3:
status->status |= PORT_STATUS_HIGH_SPEED;
break;
@@ -502,32 +550,30 @@ XHCI::SetPortFeature(uint8 index, uint16 feature)
return B_BAD_INDEX;
uint32 portRegister = XHCI_PORTSC(index);
uint32 portStatus = ReadOpReg(portRegister);
uint32 portStatus = ReadOpReg(portRegister) & ~PS_CLEAR;
switch (feature) {
case PORT_SUSPEND:
if ((portStatus & PS_PED ) == 0 || (portStatus & PS_PR)
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;
WriteOpReg(portRegister, portStatus | PS_LWS | PS_XDEV_U3);
break;
case PORT_RESET:
portStatus &= ~PS_CLEAR;
WriteOpReg(portRegister, portStatus | PS_PR);
return B_OK;
break;
case PORT_POWER:
portStatus &= ~PS_CLEAR;
WriteOpReg(portRegister, portStatus | PS_PP);
return B_OK;
break;
default:
return B_BAD_VALUE;
}
return B_BAD_VALUE;
return B_OK;
}
@@ -539,8 +585,7 @@ XHCI::ClearPortFeature(uint8 index, uint16 feature)
return B_BAD_INDEX;
uint32 portRegister = XHCI_PORTSC(index);
uint32 portStatus = ReadOpReg(portRegister);
portStatus &= ~PS_CLEAR;
uint32 portStatus = ReadOpReg(portRegister) & ~PS_CLEAR;
switch (feature) {
case PORT_SUSPEND:
@@ -550,7 +595,6 @@ XHCI::ClearPortFeature(uint8 index, uint16 feature)
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);
}
@@ -598,20 +642,26 @@ XHCI::ControllerHalt()
status_t
XHCI::ControllerReset()
{
WriteOpReg(XHCI_CMD, CMD_HCRST);
TRACE("ControllerReset() cmd: 0x%lx sts: 0x%lx\n", ReadOpReg(XHCI_CMD),
ReadOpReg(XHCI_STS));
WriteOpReg(XHCI_CMD, ReadOpReg(XHCI_CMD) | CMD_HCRST);
int32 tries = 100;
int32 tries = 250;
while (ReadOpReg(XHCI_CMD) & CMD_HCRST) {
snooze(1000);
if (tries-- < 0)
if (tries-- < 0) {
TRACE("ControllerReset() failed CMD_HCRST\n");
return B_ERROR;
}
}
tries = 100;
tries = 250;
while (ReadOpReg(XHCI_STS) & STS_CNR) {
snooze(1000);
if (tries-- < 0)
if (tries-- < 0) {
TRACE("ControllerReset() failed STS_CNR\n");
return B_ERROR;
}
}
return B_OK;
@@ -628,7 +678,7 @@ XHCI::InterruptHandler(void *data)
int32
XHCI::Interrupt()
{
acquire_spinlock(&fSpinlock);
SpinLocker _(&fSpinlock);
uint32 status = ReadOpReg(XHCI_STS);
uint32 temp = ReadRunReg32(XHCI_IMAN(0));
@@ -637,7 +687,11 @@ XHCI::Interrupt()
TRACE("STS: %lx IRQ_PENDING: %lx\n", status, temp);
int32 result = B_HANDLED_INTERRUPT;
if (status & STS_HCH) {
TRACE_ERROR("Host Controller halted\n");
return result;
}
if (status & STS_HSE) {
TRACE_ERROR("Host System Error\n");
return result;
@@ -649,19 +703,19 @@ XHCI::Interrupt()
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);
uint8 event = TRB_3_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:
case TRB_TYPE_COMMAND_COMPLETION:
HandleCmdComplete(&fEventRing[i]);
result = B_INVOKE_SCHEDULER;
break;
@@ -671,7 +725,7 @@ XHCI::Interrupt()
}
i++;
if (i == MAX_EVENTS) {
if (i == XHCI_MAX_EVENTS) {
i = 0;
j ^= 1;
if (!--t)
@@ -687,9 +741,6 @@ XHCI::Interrupt()
WriteRunReg32(XHCI_ERDP_LO(0), (uint32)addr);
WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(addr >> 32));
release_spinlock(&fSpinlock);
return result;
}
@@ -714,7 +765,7 @@ XHCI::QueueCommand(xhci_trb *trb)
j = fCmdCcs;
TRACE("command[%u] = %lx (0x%016llx, 0x%08lx, 0x%08lx)\n",
i, TRB_TYPE_GET(trb->dwtrb3),
i, TRB_3_TYPE_GET(trb->dwtrb3),
trb->qwtrb0, trb->dwtrb2, trb->dwtrb3);
fCmdRing[i].qwtrb0 = trb->qwtrb0;
@@ -728,15 +779,14 @@ XHCI::QueueCommand(xhci_trb *trb)
temp &= ~TRB_3_TC_BIT;
fCmdRing[i].dwtrb3 = temp;
fCmdAddr = fErst->rs_addr + (MAX_EVENTS + i) * sizeof(xhci_trb);
fCmdAddr = fErst->rs_addr + (XHCI_MAX_EVENTS + i) * sizeof(xhci_trb);
i++;
if (i == (MAX_COMMANDS - 1)) {
if (i == (XHCI_MAX_COMMANDS - 1)) {
temp = TRB_3_TYPE(TRB_TYPE_LINK) | TRB_3_TC_BIT;
if (j)
temp = TRB_3_CYCLE_BIT | TRB_TYPE(TRB_LINK);
else
temp = TRB_TYPE(TRB_LINK);
temp |= TRB_3_CYCLE_BIT;
fCmdRing[i].dwtrb3 = temp;
i = 0;
@@ -761,54 +811,176 @@ XHCI::HandleCmdComplete(xhci_trb *trb)
}
void
XHCI::QueueNoop()
status_t
XHCI::DoCommand(xhci_trb *trb)
{
xhci_trb trb;
uint32 temp;
if (!Lock())
return B_ERROR;
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);
QueueCommand(trb);
Ring();
release_spinlock(&fSpinlock);
restore_interrupts(state);
if (acquire_sem(fCmdCompSem) < B_OK) {
Unlock();
return B_ERROR;
}
// 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);
status_t status = B_OK;
TRACE("Command Complete\n");
if (TRB_2_COMP_CODE_GET(fCmdResult[0]) != COMP_SUCCESS) {
TRACE_ERROR("unsuccessful no-op command %ld\n",
TRB_2_COMP_CODE_GET(fCmdResult[0]));
status = B_IO_ERROR;
}
trb->dwtrb2 = fCmdResult[0];
trb->dwtrb3 = fCmdResult[1];
Unlock();
return status;
}
int32
XHCI::CmdCompThread(void *data)
status_t
XHCI::Noop()
{
((XHCI *)data)->CmdComplete();
xhci_trb trb;
trb.qwtrb0 = 0;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_CMD_NOOP);
return DoCommand(&trb);
}
status_t
XHCI::EnableSlot(uint8 *slot)
{
xhci_trb trb;
trb.qwtrb0 = 0;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_ENABLE_SLOT);
status_t status = DoCommand(&trb);
if (status != B_OK)
return status;
*slot = TRB_3_SLOT_GET(trb.dwtrb3);
return B_OK;
}
void
XHCI::CmdComplete()
status_t
XHCI::DisableSlot(uint8 slot)
{
while (!fStopThreads) {
if (acquire_sem(fCmdCompSem) < B_OK)
continue;
xhci_trb trb;
trb.qwtrb0 = 0;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_DISABLE_SLOT) | TRB_3_SLOT(slot);
// 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);
return DoCommand(&trb);
}
TRACE("Command Complete\n");
if (COMP_CODE_GET(fCmdResult[0]) != COMP_SUCCESS) {
TRACE_ERROR("unsuccessful no-op command\n");
//continue;
}
snooze(1000000 * 5);
QueueNoop();
}
status_t
XHCI::SetAddress(uint64 inputContext, bool bsr, uint8 slot)
{
xhci_trb trb;
trb.qwtrb0 = inputContext;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_ADDRESS_DEVICE) | TRB_3_SLOT(slot);
if (bsr)
trb.dwtrb3 |= TRB_3_BSR_BIT;
return DoCommand(&trb);
}
status_t
XHCI::ConfigureEndpoint(uint64 inputContext, bool deconfigure, uint8 slot)
{
xhci_trb trb;
trb.qwtrb0 = inputContext;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_CONFIGURE_ENDPOINT) | TRB_3_SLOT(slot);
if (deconfigure)
trb.dwtrb3 |= TRB_3_DCEP_BIT;
return DoCommand(&trb);
}
status_t
XHCI::EvaluateContext(uint64 inputContext, uint8 slot)
{
xhci_trb trb;
trb.qwtrb0 = inputContext;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_EVALUATE_CONTEXT) | TRB_3_SLOT(slot);
return DoCommand(&trb);
}
status_t
XHCI::ResetEndpoint(bool preserve, uint8 endpoint, uint8 slot)
{
xhci_trb trb;
trb.qwtrb0 = 0;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_RESET_ENDPOINT) | TRB_3_SLOT(slot)
| TRB_3_ENDPOINT(endpoint);
if (preserve)
trb.dwtrb3 |= TRB_3_PRSV_BIT;
return DoCommand(&trb);
}
status_t
XHCI::StopEndpoint(bool suspend, uint8 endpoint, uint8 slot)
{
xhci_trb trb;
trb.qwtrb0 = 0;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_STOP_ENDPOINT) | TRB_3_SLOT(slot)
| TRB_3_ENDPOINT(endpoint);
if (suspend)
trb.dwtrb3 |= TRB_3_SUSPEND_ENDPOINT_BIT;
return DoCommand(&trb);
}
status_t
XHCI::SetTRDequeue(uint64 dequeue, uint16 stream, uint8 endpoint, uint8 slot)
{
xhci_trb trb;
trb.qwtrb0 = dequeue;
trb.dwtrb2 = TRB_2_STREAM(stream);
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_SET_TR_DEQUEUE) | TRB_3_SLOT(slot)
| TRB_3_ENDPOINT(endpoint);
return DoCommand(&trb);
}
status_t
XHCI::ResetDevice(uint8 slot)
{
xhci_trb trb;
trb.qwtrb0 = 0;
trb.dwtrb2 = 0;
trb.dwtrb3 = TRB_3_TYPE(TRB_TYPE_RESET_DEVICE) | TRB_3_SLOT(slot);
return DoCommand(&trb);
}
+26 -43
View File
@@ -14,45 +14,11 @@
#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);
@@ -68,12 +34,12 @@ public:
static status_t AddTo(Stack *stack);
// Port operations for root hub
uint8 PortCount() { return fPortCount; };
uint8 PortCount() const { 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);
virtual const char * TypeName() const { return "xhci"; };
virtual const char * TypeName() const { return "xhci"; }
private:
// Controller resets
@@ -91,14 +57,27 @@ private:
// Command
void QueueCommand(xhci_trb *trb);
void HandleCmdComplete(xhci_trb *trb);
status_t DoCommand(xhci_trb *trb);
//Doorbell
void Ring();
//no-op
void QueueNoop();
static int32 CmdCompThread(void *data);
void CmdComplete();
// Commands
status_t Noop();
status_t EnableSlot(uint8 *slot);
status_t DisableSlot(uint8 slot);
status_t SetAddress(uint64 inputContext, bool bsr,
uint8 slot);
status_t ConfigureEndpoint(uint64 inputContext,
bool deconfigure, uint8 slot);
status_t EvaluateContext(uint64 inputContext,
uint8 slot);
status_t ResetEndpoint(bool preserve, uint8 endpoint,
uint8 slot);
status_t StopEndpoint(bool suspend, uint8 endpoint,
uint8 slot);
status_t SetTRDequeue(uint64 dequeue, uint16 stream,
uint8 endpoint, uint8 slot);
status_t ResetDevice(uint8 slot);
// Operational register functions
inline void WriteOpReg(uint32 reg, uint32 value);
@@ -136,12 +115,11 @@ private:
uint32 fCmdResult[2];
area_id fDcbaArea;
uint8 * fDcba;
struct xhci_device_context_array * fDcba;
spinlock fSpinlock;
sem_id fCmdCompSem;
thread_id fCmdCompThread;
sem_id fFinishTransfersSem;
thread_id fFinishThread;
bool fStopThreads;
@@ -154,6 +132,11 @@ private:
uint8 fPortCount;
uint8 fSlotCount;
// Scratchpad
uint8 fScratchpadCount;
area_id fScratchpadArea[XHCI_MAX_SCRATCHPADS];
void * fScratchpad[XHCI_MAX_SCRATCHPADS];
uint16 fEventIdx;
uint16 fCmdIdx;
uint8 fEventCcs;
+169 -13
View File
@@ -14,10 +14,16 @@
#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 HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff)
#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f)
#define XHCI_HCSPARAMS2 0x08 // Structural Parameters 2
#define HCS_IST(p) (((p) >> 0) & 0xf)
#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
#define HCS_SPR(p) (((p) >> 26) & 0x1)
#define HCS_MAX_SC_BUFFERS(p) (((p) >> 27) & 0x1f)
#define XHCI_HCSPARAMS3 0x0C // Structural Parameters 3
#define HCS_U1_DEVICE_LATENCY(p) (((p) >> 0) & 0xff)
#define HCS_U2_DEVICE_LATENCY(p) (((p) >> 16) & 0xffff)
#define XHCI_HCCPARAMS 0x10 // Capability Parameters
#define XHCI_DBOFF 0x14 // Doorbell Register offset
#define XHCI_RTSOFF 0x18 // Runtime Register Space offset
@@ -39,6 +45,7 @@
#define STS_CNR (1<<11)
#define STS_HCE (1 << 12)
#define XHCI_PAGESIZE 0x08 // PAGE SIZE
#define XHCI_DNCTRL 0x14
// Section 5.4.5
#define XHCI_CRCR_LO 0x18
#define XHCI_CRCR_HI 0x1C
@@ -72,7 +79,7 @@
// Host Controller Doorbell Registers
#define XHCI_DOORBELL(n) (0x0000 + (4 * (n)))
#define XHCI_DOORBELL(n) (0x0000 + (4 * (n)))
// Extended Capabilities
#define XECP_ID(x) ((x) & 0xff)
@@ -88,7 +95,7 @@
// Port status Registers
// Section 5.4.8
#define XHCI_PORTSC(n) (0x3F0 + (0x10 * (n)))
#define XHCI_PORTSC(n) (0x400 + (0x10 * (n)))
#define PS_CCS (1 << 0)
#define PS_PED (1 << 1)
#define PS_OCA (1 << 3)
@@ -116,18 +123,167 @@
// Completion Code
#define COMP_CODE_GET(x) (((x) >> 24) & 0xff)
#define COMP_SUCCESS 0x01
#define TRB_2_COMP_CODE_GET(x) (((x) >> 24) & 0xff)
#define COMP_INVALID 0
#define COMP_SUCCESS 1
#define COMP_DATA_BUFFER 2
#define COMP_BABBLE 3
#define COMP_USB_TRANSACTION 4
#define COMP_TRB 5
#define COMP_STALL 6
#define COMP_RESOURCE 7
#define COMP_BANDWIDTH 8
#define COMP_NO_SLOTS 9
#define COMP_INVALID_STREAM 10
#define COMP_SLOT_NOT_ENABLED 11
#define COMP_ENDPOINT_NOT_ENABLED 12
#define COMP_SHORT_PACKET 13
#define COMP_RING_UNDERRUN 14
#define COMP_RING_OVERRUN 15
#define COMP_VF_RING_FULL 16
#define COMP_PARAMETER 17
#define COMP_BANDWIDTH_OVERRUN 18
#define COMP_CONTEXT_STATE 19
#define COMP_NO_PING_RESPONSE 20
#define COMP_EVENT_RING_FULL 21
#define COMP_INCOMPATIBLE_DEVICE 22
#define COMP_MISSED_SERVICE 23
#define COMP_COMMAND_RING_STOPPED 24
#define COMP_COMMAND_ABORTED 25
#define COMP_STOPPED 26
#define COMP_LENGTH_INVALID 27
#define COMP_MAX_EXIT_LATENCY 29
#define COMP_ISOC_OVERRUN 31
#define COMP_EVENT_LOST 32
#define COMP_UNDEFINED 33
#define COMP_INVALID_STREAM_ID 34
#define COMP_SECONDARY_BANDWIDTH 35
#define COMP_SPLIT_TRANSACTION 36
#define TRB_2_TD_SIZE(x) (((x) & 0x1F) << 17)
#define TRB_2_TD_SIZE_GET(x) (((x) >> 17) & 0x1F)
#define TRB_2_REM(x) ((x) & 0xFFFFFF)
#define TRB_2_REM_GET(x) ((x) & 0xFFFFFF)
#define TRB_2_BYTES(x) ((x) & 0x1FFFF)
#define TRB_2_BYTES_GET(x) ((x) & 0x1FFFF)
#define TRB_2_IRQ(x) (((x) & 0x3FF) << 22)
#define TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF)
#define TRB_2_STREAM(x) (((x) & 0xFF) << 16)
#define TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFF)
#define TRB_3_TYPE(x) (((x) & 0x3F) << 10)
#define TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F)
// TRB Type (table 131)
#define TRB_TYPE_NORMAL 1
#define TRB_TYPE_SETUP_STAGE 2
#define TRB_TYPE_DATA_STAGE 3
#define TRB_TYPE_STATUS_STAGE 4
#define TRB_TYPE_ISOCH 5
#define TRB_TYPE_LINK 6
#define TRB_TYPE_EVENT_DATA 7
#define TRB_TYPE_TR_NOOP 8
// commands
#define TRB_TYPE_ENABLE_SLOT 9
#define TRB_TYPE_DISABLE_SLOT 10
#define TRB_TYPE_ADDRESS_DEVICE 11
#define TRB_TYPE_CONFIGURE_ENDPOINT 12
#define TRB_TYPE_EVALUATE_CONTEXT 13
#define TRB_TYPE_RESET_ENDPOINT 14
#define TRB_TYPE_STOP_ENDPOINT 15
#define TRB_TYPE_SET_TR_DEQUEUE 16
#define TRB_TYPE_RESET_DEVICE 17
#define TRB_TYPE_FORCE_EVENT 18
#define TRB_TYPE_NEGOCIATE_BW 19
#define TRB_TYPE_SET_LATENCY_TOLERANCE 20
#define TRB_TYPE_GET_PORT_BW 21
#define TRB_TYPE_FORCE_HEADER 22
#define TRB_TYPE_CMD_NOOP 23
// events
#define TRB_TYPE_TRANSFER 32
#define TRB_TYPE_COMMAND_COMPLETION 33
#define TRB_TYPE_PORT_STATUS_CHANGE 34
#define TRB_TYPE_BANDWIDTH_REQUEST 35
#define TRB_TYPE_DOORBELL 36
#define TRB_TYPE_HOST_CONTROLLER 37
#define TRB_TYPE_DEVICE_NOTIFICATION 38
#define TRB_TYPE_MFINDEX_WRAP 39
// vendor
#define TRB_TYPE_NEC_COMMAND_COMPLETION 48
#define TRB_TYPE_NEC_GET_FIRMWARE_REV 49
// 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)
#define TRB_3_ENT_BIT (1U << 1)
#define TRB_3_ISP_BIT (1U << 2)
#define TRB_3_NSNOOP_BIT (1U << 3)
#define TRB_3_CHAIN_BIT (1U << 4)
#define TRB_3_IOC_BIT (1U << 5)
#define TRB_3_IDT_BIT (1U << 6)
#define TRB_3_BEI_BIT (1U << 9)
#define TRB_3_DCEP_BIT (1U << 9)
#define TRB_3_PRSV_BIT (1U << 9)
#define TRB_3_BSR_BIT (1U << 9)
#define TRB_3_TRT_MASK (3U << 16)
#define TRB_3_DIR_IN (1U << 16)
#define TRB_3_TRT_OUT (2U << 16)
#define TRB_3_TRT_IN (3U << 16)
#define TRB_3_SUSPEND_ENDPOINT_BIT (1U << 23)
#define TRB_3_ISO_SIA_BIT (1U << 31)
#define TRB_3_TBC(x) (((x) & 0x3) << 7)
#define TRB_3_TBC_GET(x) (((x) >> 7) & 0x3)
#define TRB_3_TLBPC(x) (((x) & 0xf) << 16)
#define TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xf)
#define TRB_3_ENDPOINT(x) (((x) & 0xf) << 16)
#define TRB_3_ENDPOINT_GET(x) (((x) >> 16) & 0xf)
#define TRB_3_FRID(x) (((x) & 0x7ff) << 20)
#define TRB_3_FRID_GET(x) (((x) >> 20) & 0x7ff)
#define TRB_3_SLOT(x) (((x) & 0xff) << 24)
#define TRB_3_SLOT_GET(x) (((x) >> 24) & 0xff)
#define XHCI_MAX_EVENTS (16 * 13)
#define XHCI_MAX_COMMANDS (16 * 1)
#define XHCI_MAX_SLOTS 256
#define XHCI_MAX_PORTS 127
#define XHCI_MAX_SCRATCHPADS 32
struct xhci_trb {
uint64 qwtrb0;
uint32 dwtrb2;
uint32 dwtrb3;
} __attribute__((__aligned__(4)));;
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)));
struct xhci_device_context_array {
uint64 baseAddress[XHCI_MAX_SLOTS];
struct {
uint64 padding;
} __attribute__((__aligned__(64)));
uint64 scratchpad[XHCI_MAX_SCRATCHPADS];
};
#endif // !XHCI_HARDWARE_H