From ce44796e34f049cc20fb8a522c81f05c325b69a5 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Mon, 23 May 2011 20:50:21 +0000 Subject: [PATCH] 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 --- src/add-ons/kernel/busses/usb/ehci.cpp | 69 +++++++++++++++++++++----- src/add-ons/kernel/busses/usb/ehci.h | 8 ++- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index cebc5d27de..8f16aaf378 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -8,6 +8,7 @@ */ +#include #include #include #include @@ -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) diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index 7f10975fde..ce6184bc39 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -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; };