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:
@@ -90,7 +90,8 @@ typedef enum {
|
|||||||
USB_SPEED_LOWSPEED = 0,
|
USB_SPEED_LOWSPEED = 0,
|
||||||
USB_SPEED_FULLSPEED,
|
USB_SPEED_FULLSPEED,
|
||||||
USB_SPEED_HIGHSPEED,
|
USB_SPEED_HIGHSPEED,
|
||||||
USB_SPEED_MAX = USB_SPEED_HIGHSPEED
|
USB_SPEED_SUPER,
|
||||||
|
USB_SPEED_MAX = USB_SPEED_SUPER
|
||||||
} usb_speed;
|
} usb_speed;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ KernelAddon <usb>ehci :
|
|||||||
|
|
||||||
KernelAddon <usb>xhci :
|
KernelAddon <usb>xhci :
|
||||||
xhci.cpp
|
xhci.cpp
|
||||||
|
xhci_rh.cpp
|
||||||
: libusb.a
|
: libusb.a
|
||||||
: xhci.rdef
|
: xhci.rdef
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -63,7 +63,22 @@ XHCI::XHCI(pci_info *info, Stack *stack)
|
|||||||
fRegisterArea(-1),
|
fRegisterArea(-1),
|
||||||
fPCIInfo(info),
|
fPCIInfo(info),
|
||||||
fStack(stack),
|
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) {
|
if (BusManager::InitCheck() < B_OK) {
|
||||||
TRACE_ERROR("bus manager failed to init\n");
|
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
|
// enable busmaster and memory mapped access
|
||||||
uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus,
|
uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus,
|
||||||
fPCIInfo->device, fPCIInfo->function, PCI_command, 2);
|
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;
|
command |= PCI_command_master | PCI_command_memory;
|
||||||
|
|
||||||
sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
|
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_registers[0], physicalAddress, offset,
|
||||||
fPCIInfo->u.h0.base_register_sizes[0]);
|
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,
|
physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS,
|
||||||
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA,
|
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA,
|
||||||
(void **)&fCapabilityRegisters);
|
(void **)&fCapabilityRegisters);
|
||||||
@@ -103,62 +118,55 @@ XHCI::XHCI(pci_info *info, Stack *stack)
|
|||||||
|
|
||||||
fCapabilityRegisters += offset;
|
fCapabilityRegisters += offset;
|
||||||
fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_CAPLENGTH);
|
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 capability registers: 0x%08lx\n", (uint32)fCapabilityRegisters);
|
||||||
TRACE("mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters);
|
TRACE("mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters);
|
||||||
TRACE("mapped rumtime registers: 0x%08lx\n", (uint32)fRuntimeRegisters);
|
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 parameters1: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS1));
|
||||||
TRACE("structural parameters2: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS2));
|
TRACE("structural parameters2: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS2));
|
||||||
TRACE("structural parameters3: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS3));
|
TRACE("structural parameters3: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS3));
|
||||||
TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(XHCI_HCCPARAMS));
|
TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(XHCI_HCCPARAMS));
|
||||||
|
|
||||||
// read port count from capability register
|
uint32 cparams = ReadCapReg32(XHCI_HCCPARAMS);
|
||||||
fPortCount = (ReadCapReg32(XHCI_HCSPARAMS1) >> 24) & 0x7f;
|
uint32 eec = 0xffffffff;
|
||||||
|
uint32 eecp = HCS0_XECP(cparams) << 2;
|
||||||
uint32 extendedCapPointer = ((ReadCapReg32(XHCI_HCCPARAMS) >> 16) & 0xffff)
|
for (; eecp != 0 && XECP_NEXT(eec); eecp += XECP_NEXT(eec) << 2) {
|
||||||
<< 2;
|
eec = ReadCapReg32(eecp);
|
||||||
if (extendedCapPointer > 0) {
|
if (XECP_ID(eec) != XHCI_LEGSUP_CAPID)
|
||||||
TRACE("extended capabilities register at %ld\n", extendedCapPointer);
|
continue;
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
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
|
// halt the host controller
|
||||||
if (ControllerHalt() < B_OK) {
|
if (ControllerHalt() < B_OK) {
|
||||||
@@ -171,6 +179,28 @@ XHCI::XHCI(pci_info *info, Stack *stack)
|
|||||||
return;
|
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;
|
fInitOK = true;
|
||||||
TRACE("XHCI host controller driver constructed\n");
|
TRACE("XHCI host controller driver constructed\n");
|
||||||
}
|
}
|
||||||
@@ -182,7 +212,15 @@ XHCI::~XHCI()
|
|||||||
|
|
||||||
WriteOpReg(XHCI_CMD, 0);
|
WriteOpReg(XHCI_CMD, 0);
|
||||||
|
|
||||||
|
int32 result = 0;
|
||||||
|
fStopThreads = true;
|
||||||
|
delete_sem(fCmdCompSem);
|
||||||
|
delete_sem(fFinishTransfersSem);
|
||||||
delete_area(fRegisterArea);
|
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);
|
put_module(B_PCI_MODULE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +232,100 @@ XHCI::Start()
|
|||||||
TRACE("usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(XHCI_CMD),
|
TRACE("usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(XHCI_CMD),
|
||||||
ReadOpReg(XHCI_STS));
|
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_ALWAYS("successfully started the controller\n");
|
||||||
|
TRACE("No-Op test\n");
|
||||||
|
QueueNoop();
|
||||||
return BusManager::Start();
|
return BusManager::Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +333,10 @@ XHCI::Start()
|
|||||||
status_t
|
status_t
|
||||||
XHCI::SubmitTransfer(Transfer *transfer)
|
XHCI::SubmitTransfer(Transfer *transfer)
|
||||||
{
|
{
|
||||||
|
// short circuit the root hub
|
||||||
|
if (transfer->TransferPipe()->DeviceAddress() == fRootHubAddress)
|
||||||
|
return fRootHub->ProcessTransfer(this, transfer);
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,6 +450,46 @@ XHCI::AddTo(Stack *stack)
|
|||||||
status_t
|
status_t
|
||||||
XHCI::GetPortStatus(uint8 index, usb_port_status *status)
|
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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,6 +497,36 @@ XHCI::GetPortStatus(uint8 index, usb_port_status *status)
|
|||||||
status_t
|
status_t
|
||||||
XHCI::SetPortFeature(uint8 index, uint16 feature)
|
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;
|
return B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,26 +534,51 @@ XHCI::SetPortFeature(uint8 index, uint16 feature)
|
|||||||
status_t
|
status_t
|
||||||
XHCI::ClearPortFeature(uint8 index, uint16 feature)
|
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;
|
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
|
status_t
|
||||||
XHCI::ControllerHalt()
|
XHCI::ControllerHalt()
|
||||||
{
|
{
|
||||||
@@ -388,13 +618,6 @@ XHCI::ControllerReset()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
XHCI::LightReset()
|
|
||||||
{
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
XHCI::InterruptHandler(void *data)
|
XHCI::InterruptHandler(void *data)
|
||||||
{
|
{
|
||||||
@@ -405,11 +628,215 @@ XHCI::InterruptHandler(void *data)
|
|||||||
int32
|
int32
|
||||||
XHCI::Interrupt()
|
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;
|
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;
|
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
|
inline void
|
||||||
XHCI::WriteOpReg(uint32 reg, uint32 value)
|
XHCI::WriteOpReg(uint32 reg, uint32 value)
|
||||||
{
|
{
|
||||||
@@ -451,3 +878,30 @@ XHCI::WriteCapReg32(uint32 reg, uint32 value)
|
|||||||
*(volatile uint32 *)(fCapabilityRegisters + reg) = 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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,8 +14,43 @@
|
|||||||
#include "xhci_hardware.h"
|
#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_info;
|
||||||
struct pci_module_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 {
|
class XHCI : public BusManager {
|
||||||
@@ -32,51 +67,107 @@ public:
|
|||||||
|
|
||||||
static status_t AddTo(Stack *stack);
|
static status_t AddTo(Stack *stack);
|
||||||
|
|
||||||
// Port operations for root hub
|
// Port operations for root hub
|
||||||
uint8 PortCount() { return fPortCount; };
|
uint8 PortCount() { return fPortCount; };
|
||||||
status_t GetPortStatus(uint8 index, usb_port_status *status);
|
status_t GetPortStatus(uint8 index, usb_port_status *status);
|
||||||
status_t SetPortFeature(uint8 index, uint16 feature);
|
status_t SetPortFeature(uint8 index, uint16 feature);
|
||||||
status_t ClearPortFeature(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"; };
|
virtual const char * TypeName() const { return "xhci"; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Controller resets
|
// Controller resets
|
||||||
status_t ControllerReset();
|
status_t ControllerReset();
|
||||||
status_t ControllerHalt();
|
status_t ControllerHalt();
|
||||||
status_t LightReset();
|
|
||||||
|
|
||||||
// Interrupt functions
|
// Interrupt functions
|
||||||
static int32 InterruptHandler(void *data);
|
static int32 InterruptHandler(void *data);
|
||||||
int32 Interrupt();
|
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 void WriteOpReg(uint32 reg, uint32 value);
|
||||||
inline uint32 ReadOpReg(uint32 reg);
|
inline uint32 ReadOpReg(uint32 reg);
|
||||||
|
|
||||||
// Capability register functions
|
// Capability register functions
|
||||||
inline uint8 ReadCapReg8(uint32 reg);
|
inline uint8 ReadCapReg8(uint32 reg);
|
||||||
inline uint16 ReadCapReg16(uint32 reg);
|
inline uint16 ReadCapReg16(uint32 reg);
|
||||||
inline uint32 ReadCapReg32(uint32 reg);
|
inline uint32 ReadCapReg32(uint32 reg);
|
||||||
inline void WriteCapReg32(uint32 reg, uint32 value);
|
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;
|
static pci_module_info * sPCIModule;
|
||||||
|
|
||||||
uint8 * fCapabilityRegisters;
|
uint8 * fCapabilityRegisters;
|
||||||
uint8 * fOperationalRegisters;
|
uint8 * fOperationalRegisters;
|
||||||
uint8 * fRuntimeRegisters;
|
uint8 * fRuntimeRegisters;
|
||||||
|
uint8 * fDoorbellRegisters;
|
||||||
area_id fRegisterArea;
|
area_id fRegisterArea;
|
||||||
pci_info * fPCIInfo;
|
pci_info * fPCIInfo;
|
||||||
Stack * fStack;
|
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 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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,31 +13,71 @@
|
|||||||
#define XHCI_CAPLENGTH 0x00 // Capability Register Length
|
#define XHCI_CAPLENGTH 0x00 // Capability Register Length
|
||||||
#define XHCI_HCIVERSION 0x02 // Interface Version Number
|
#define XHCI_HCIVERSION 0x02 // Interface Version Number
|
||||||
#define XHCI_HCSPARAMS1 0x04 // Structural Parameters 1
|
#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_HCSPARAMS2 0x08 // Structural Parameters 2
|
||||||
#define XHCI_HCSPARAMS3 0x0C // Structural Parameters 3
|
#define XHCI_HCSPARAMS3 0x0C // Structural Parameters 3
|
||||||
#define XHCI_HCCPARAMS 0x10 // Capability Parameters
|
#define XHCI_HCCPARAMS 0x10 // Capability Parameters
|
||||||
|
#define XHCI_DBOFF 0x14 // Doorbell Register offset
|
||||||
#define XHCI_RTSOFF 0x18 // Runtime Register Space offset
|
#define XHCI_RTSOFF 0x18 // Runtime Register Space offset
|
||||||
|
|
||||||
|
|
||||||
// Host Controller Operational Registers
|
// Host Controller Operational Registers
|
||||||
#define XHCI_CMD 0x00 // USB Command
|
#define XHCI_CMD 0x00 // USB Command
|
||||||
#define XHCI_STS 0x04 // USB Status
|
|
||||||
|
|
||||||
|
|
||||||
// USB Command Register
|
// USB Command Register
|
||||||
|
#define CMD_RUN (1 << 0)
|
||||||
#define CMD_HCRST (1 << 1) // Host Controller Reset
|
#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
|
// 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_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
|
// 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_CAPID 0x01
|
||||||
#define XHCI_LEGSUP_OSOWNED (1 << 24) // OS Owned Semaphore
|
#define XHCI_LEGSUP_OSOWNED (1 << 24) // OS Owned Semaphore
|
||||||
#define XHCI_LEGSUP_BIOSOWNED (1 << 16) // BIOS 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))
|
#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
|
||||||
|
|||||||
@@ -0,0 +1,281 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011, Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Michael Lotz <[email protected]>
|
||||||
|
* Jian Chiang <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user