Introduce simplistic poll mode for EHCI. If enabled a thread will run the
interrupt handler every millisecond so it can check for interrupts to handle. While this is certainly not ideal, it allows EHCI to run (surprisingly well even) on chipsets that experience interrupt routing issues. It can be enabled with the safemode setting "ehci_polling on" that can either be entered using the new advanced debug option entry in the bootloader and/or in the kernel settings file. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41688 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
9b2ea762db
commit
ce44796e34
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <driver_settings.h>
|
||||
#include <module.h>
|
||||
#include <PCI.h>
|
||||
#include <USB3.h>
|
||||
@ -131,7 +132,8 @@ EHCI::EHCI(pci_info *info, Stack *stack)
|
||||
fRootHubAddress(0),
|
||||
fPortCount(0),
|
||||
fPortResetChange(0),
|
||||
fPortSuspendChange(0)
|
||||
fPortSuspendChange(0),
|
||||
fInterruptPollThread(-1)
|
||||
{
|
||||
if (BusManager::InitCheck() < B_OK) {
|
||||
TRACE_ERROR("bus manager failed to init\n");
|
||||
@ -319,23 +321,44 @@ EHCI::EHCI(pci_info *info, Stack *stack)
|
||||
B_NORMAL_PRIORITY, (void *)this);
|
||||
resume_thread(fCleanupThread);
|
||||
|
||||
// install the interrupt handler and enable interrupts
|
||||
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
|
||||
InterruptHandler, (void *)this, 0);
|
||||
fEnabledInterrupts = EHCI_USBINTR_HOSTSYSERR | EHCI_USBINTR_USBERRINT
|
||||
| EHCI_USBINTR_USBINT | EHCI_USBINTR_INTONAA;
|
||||
WriteOpReg(EHCI_USBINTR, fEnabledInterrupts);
|
||||
// set up interrupts or interrupt polling now that the controller is ready
|
||||
bool polling = false;
|
||||
void *settings = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS);
|
||||
if (settings != NULL) {
|
||||
polling = get_driver_boolean_parameter(settings, "ehci_polling", false,
|
||||
false);
|
||||
unload_driver_settings(settings);
|
||||
}
|
||||
|
||||
// ensure that interrupts are enabled on the PCI device as well
|
||||
if (polling) {
|
||||
// create and run the polling thread
|
||||
TRACE_ALWAYS("enabling ehci polling\n");
|
||||
fInterruptPollThread = spawn_kernel_thread(InterruptPollThread,
|
||||
"ehci interrupt poll thread", B_NORMAL_PRIORITY, (void *)this);
|
||||
resume_thread(fInterruptPollThread);
|
||||
} else {
|
||||
// install the interrupt handler and enable interrupts
|
||||
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
|
||||
InterruptHandler, (void *)this, 0);
|
||||
}
|
||||
|
||||
// ensure that interrupts are en-/disabled on the PCI device
|
||||
command = sPCIModule->read_pci_config(fPCIInfo->bus, fPCIInfo->device,
|
||||
fPCIInfo->function, PCI_command, 2);
|
||||
if ((command & PCI_command_int_disable) != 0) {
|
||||
TRACE_ALWAYS("PCI interrupts were disabled, enabling\n");
|
||||
command &= ~PCI_command_int_disable;
|
||||
if (polling == ((command & PCI_command_int_disable) == 0)) {
|
||||
if (polling)
|
||||
command &= ~PCI_command_int_disable;
|
||||
else
|
||||
command |= PCI_command_int_disable;
|
||||
|
||||
sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
|
||||
fPCIInfo->function, PCI_command, 2, command);
|
||||
}
|
||||
|
||||
fEnabledInterrupts = EHCI_USBINTR_HOSTSYSERR | EHCI_USBINTR_USBERRINT
|
||||
| EHCI_USBINTR_USBINT | EHCI_USBINTR_INTONAA;
|
||||
WriteOpReg(EHCI_USBINTR, fEnabledInterrupts);
|
||||
|
||||
// structures don't span page boundaries
|
||||
size_t itdListSize = EHCI_VFRAMELIST_ENTRIES_COUNT
|
||||
/ (B_PAGE_SIZE / sizeof(itd_entry)) * B_PAGE_SIZE;
|
||||
@ -458,7 +481,6 @@ EHCI::EHCI(pci_info *info, Stack *stack)
|
||||
fFrameBandwidth[i] = MAX_AVAILABLE_BANDWIDTH;
|
||||
}
|
||||
|
||||
|
||||
// allocate a queue head that will always stay in the async frame list
|
||||
fAsyncQueueHead = CreateQueueHead();
|
||||
if (!fAsyncQueueHead) {
|
||||
@ -499,6 +521,9 @@ EHCI::~EHCI()
|
||||
wait_for_thread(fCleanupThread, &result);
|
||||
wait_for_thread(fFinishIsochronousThread, &result);
|
||||
|
||||
if (fInterruptPollThread >= 0)
|
||||
wait_for_thread(fInterruptPollThread, &result);
|
||||
|
||||
LockIsochronous();
|
||||
isochronous_transfer_data *isoTransfer = fFirstIsochronousTransfer;
|
||||
while (isoTransfer) {
|
||||
@ -584,6 +609,7 @@ EHCI::Start()
|
||||
}
|
||||
|
||||
SetRootHub(fRootHub);
|
||||
|
||||
TRACE_ALWAYS("successfully started the controller\n");
|
||||
return BusManager::Start();
|
||||
}
|
||||
@ -1181,6 +1207,25 @@ EHCI::Interrupt()
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
EHCI::InterruptPollThread(void *data)
|
||||
{
|
||||
EHCI *ehci = (EHCI *)data;
|
||||
|
||||
while (!ehci->fStopThreads) {
|
||||
// TODO: this could be handled much better by only polling when there
|
||||
// are actual transfers going on...
|
||||
snooze(1000);
|
||||
|
||||
cpu_status status = disable_interrupts();
|
||||
ehci->Interrupt();
|
||||
restore_interrupts(status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
EHCI::AddPendingTransfer(Transfer *transfer, ehci_qh *queueHead,
|
||||
ehci_qtd *dataDescriptor, bool directionIn)
|
||||
|
@ -57,7 +57,7 @@ virtual status_t SubmitTransfer(Transfer *transfer);
|
||||
virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force);
|
||||
status_t CancelQueuedIsochronousTransfers(Pipe *pipe, bool force);
|
||||
status_t SubmitIsochronous(Transfer *transfer);
|
||||
|
||||
|
||||
virtual status_t NotifyPipeChange(Pipe *pipe,
|
||||
usb_change change);
|
||||
|
||||
@ -82,6 +82,7 @@ private:
|
||||
// Interrupt functions
|
||||
static int32 InterruptHandler(void *data);
|
||||
int32 Interrupt();
|
||||
static int32 InterruptPollThread(void *data);
|
||||
|
||||
// Transfer management
|
||||
status_t AddPendingTransfer(Transfer *transfer,
|
||||
@ -228,7 +229,7 @@ static pci_module_info * sPCIModule;
|
||||
sem_id fFinishIsochronousTransfersSem;
|
||||
thread_id fFinishIsochronousThread;
|
||||
mutex fIsochronousLock;
|
||||
|
||||
|
||||
// Root Hub
|
||||
EHCIRootHub * fRootHub;
|
||||
uint8 fRootHubAddress;
|
||||
@ -237,6 +238,9 @@ static pci_module_info * sPCIModule;
|
||||
uint8 fPortCount;
|
||||
uint16 fPortResetChange;
|
||||
uint16 fPortSuspendChange;
|
||||
|
||||
// Interrupt polling
|
||||
thread_id fInterruptPollThread;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user