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:
Michael Lotz 2011-05-23 20:50:21 +00:00
parent 9b2ea762db
commit ce44796e34
2 changed files with 63 additions and 14 deletions

View File

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

View File

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