* Changed EHCI register access from port to memory mapped io (which is required)

* Added individual register operations for operational and capability registers
* Added resetting the host controller which appearantly actually works

Note that you shouldn't install the ehci module if you want uhci to work.
It disables the companion host controller drivers (uhci and ohci) because it takes port ownership and does not yet give it back for low and fullspeed devices.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18648 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2006-08-26 15:48:41 +00:00
parent a17c3a48b8
commit 01595708f3
4 changed files with 120 additions and 91 deletions

View File

@ -11,6 +11,7 @@
#include <USB3.h>
#include <KernelExport.h>
#define TRACE_USB
#include "ehci.h"
pci_module_info *EHCI::sPCIModule = NULL;
@ -60,8 +61,6 @@ EHCI::EHCI(pci_info *info, Stack *stack)
fStack(stack),
fPeriodicFrameListArea(-1),
fPeriodicFrameList(NULL),
fAsyncFrameListArea(-1),
fAsyncFrameList(NULL),
fFirstTransfer(NULL),
fLastTransfer(NULL),
fFinishTransfers(false),
@ -70,7 +69,7 @@ EHCI::EHCI(pci_info *info, Stack *stack)
fRootHub(NULL),
fRootHubAddress(0)
{
if (!fInitOK) {
if (BusManager::InitCheck() < B_OK) {
TRACE_ERROR(("usb_ehci: bus manager failed to init\n"));
return;
}
@ -78,49 +77,49 @@ EHCI::EHCI(pci_info *info, Stack *stack)
TRACE(("usb_ehci: constructing new EHCI Host Controller Driver\n"));
fInitOK = false;
fRegisterBase = sPCIModule->read_pci_config(fPCIInfo->bus,
fPCIInfo->device, fPCIInfo->function, PCI_memory_base, 4);
fRegisterBase &= PCI_address_io_mask;
TRACE(("usb_ehci: register base: 0x%08x\n", fRegisterBase));
// enable pci address access
uint16 command = PCI_command_io | PCI_command_master | PCI_command_memory;
command |= sPCIModule->read_pci_config(fPCIInfo->bus, fPCIInfo->device,
fPCIInfo->function, PCI_command, 2);
sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
fPCIInfo->function, PCI_command, 2, command);
// make sure we take the controller away from BIOS
sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, 2,
PCI_LEGSUP, 2, PCI_LEGSUP_USBPIRQDEN);
// disable interrupts
WriteReg16(EHCI_USBINTR, 0);
// enable busmaster and memory mapped access
uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus,
fPCIInfo->device, fPCIInfo->function, PCI_command, 2);
command &= ~PCI_command_io;
command |= PCI_command_master | PCI_command_memory;
// reset the host controller
// ToDo...
sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
fPCIInfo->function, PCI_command, 2, command);
// allocate the periodic frame list
void *physicalAddress;
fPeriodicFrameListArea = fStack->AllocateArea((void **)&fPeriodicFrameList,
&physicalAddress, B_PAGE_SIZE, "USB EHCI Periodic Framelist");
if (fPeriodicFrameListArea < B_OK) {
TRACE_ERROR(("usb_ehci: unable to allocate periodic framelist\n"));
// 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;
size_t mapSize = (fPCIInfo->u.h0.base_register_sizes[0] + offset
+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
TRACE(("usb_ehci: map physical memory 0x%08x (base: 0x%08x; offset: %x); size: %d -> %d\n", fPCIInfo->u.h0.base_registers[0], physicalAddress, offset, fPCIInfo->u.h0.base_register_sizes[0], mapSize));
fRegisterArea = map_physical_memory("EHCI memory mapped registers",
(void *)physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA,
(void **)&fCapabilityRegisters);
if (fRegisterArea < B_OK) {
TRACE(("usb_ehci: failed to map register memory\n"));
return;
}
WriteReg32(EHCI_PERIODICLISTBASE, (uint32)physicalAddress);
fCapabilityRegisters += offset;
fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(EHCI_CAPLENGTH);
TRACE(("usb_ehci: mapped capability registers: 0x%08x\n", fCapabilityRegisters));
TRACE(("usb_ehci: mapped operational registers: 0x%08x\n", fOperationalRegisters));
// allocate the async frame list
fAsyncFrameListArea = fStack->AllocateArea((void **)&fAsyncFrameList,
&physicalAddress, B_PAGE_SIZE, "USB EHCI Async Framelist");
if (fAsyncFrameListArea < B_OK) {
TRACE_ERROR(("usb_ehci: unable to allocate async framelist\n"));
// disable interrupts
WriteOpReg(EHCI_USBINTR, 0);
// reset the host controller
if (ControllerReset() < B_OK) {
TRACE_ERROR(("usb_ehci: host controller failed to reset\n"));
return;
}
WriteReg32(EHCI_ASYNCLISTADDR, (uint32)physicalAddress);
// create finisher service thread
fFinishThread = spawn_kernel_thread(FinishThread, "ehci finish thread",
B_NORMAL_PRIORITY, (void *)this);
@ -129,9 +128,26 @@ EHCI::EHCI(pci_info *info, Stack *stack)
// install the interrupt handler and enable interrupts
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
InterruptHandler, (void *)this, 0);
WriteReg16(EHCI_USBINTR, EHCI_USBINTR_HOSTSYSERR
WriteOpReg(EHCI_USBINTR, EHCI_USBINTR_HOSTSYSERR
| EHCI_USBINTR_USBERRINT | EHCI_USBINTR_USBINT);
// allocate the periodic frame list
fPeriodicFrameListArea = fStack->AllocateArea((void **)&fPeriodicFrameList,
(void **)&physicalAddress, B_PAGE_SIZE, "USB EHCI Periodic Framelist");
if (fPeriodicFrameListArea < B_OK) {
TRACE_ERROR(("usb_ehci: unable to allocate periodic framelist\n"));
return;
}
// terminate all elements
for (int32 i = 0; i < 1024; i++)
fPeriodicFrameList[i] = EHCI_PFRAMELIST_TERM;
WriteOpReg(EHCI_PERIODICLISTBASE, (uint32)physicalAddress);
// route all ports to us
WriteOpReg(EHCI_CONFIGFLAG, EHCI_CONFIGFLAG_FLAG);
TRACE(("usb_ehci: EHCI Host Controller Driver constructed\n"));
fInitOK = true;
}
@ -149,8 +165,7 @@ EHCI::~EHCI()
delete fRootHub;
delete_area(fPeriodicFrameListArea);
delete_area(fAsyncFrameListArea);
delete_area(fRegisterArea);
put_module(B_PCI_MODULE_NAME);
}
@ -159,13 +174,13 @@ status_t
EHCI::Start()
{
TRACE(("usb_ehci: starting EHCI Host Controller\n"));
TRACE(("usb_ehci: usbcmd: 0x%08x; usbsts: 0x%08x\n", ReadReg32(EHCI_USBCMD), ReadReg32(EHCI_USBSTS)));
TRACE(("usb_ehci: usbcmd: 0x%08x; usbsts: 0x%08x\n", ReadOpReg(EHCI_USBCMD), ReadOpReg(EHCI_USBSTS)));
WriteReg32(EHCI_USBCMD, ReadReg32(EHCI_USBCMD) | EHCI_USBCMD_RUNSTOP);
WriteOpReg(EHCI_USBCMD, ReadOpReg(EHCI_USBCMD) | EHCI_USBCMD_RUNSTOP);
bool running = false;
for (int32 i = 0; i < 10; i++) {
uint32 status = ReadReg32(EHCI_USBSTS);
uint32 status = ReadOpReg(EHCI_USBSTS);
TRACE(("usb_ehci: try %ld: status 0x%08x\n", i, status));
if (status & EHCI_USBSTS_HCHALTED) {
@ -295,7 +310,16 @@ EHCI::ResetPort(int32 index)
status_t
EHCI::ControllerReset()
{
return B_ERROR;
WriteOpReg(EHCI_USBCMD, EHCI_USBCMD_HCRESET);
int32 tries = 5;
while (ReadOpReg(EHCI_USBCMD) & EHCI_USBCMD_HCRESET) {
snooze(10000);
if (tries-- < 0)
return B_ERROR;
}
return B_OK;
}
@ -534,42 +558,35 @@ EHCI::ReadActualLength(ehci_qtd *topDescriptor, uint8 *lastDataToggle)
inline void
EHCI::WriteReg8(uint32 reg, uint8 value)
EHCI::WriteOpReg(uint32 reg, uint32 value)
{
sPCIModule->write_io_8(fRegisterBase + reg, value);
}
inline void
EHCI::WriteReg16(uint32 reg, uint16 value)
{
sPCIModule->write_io_16(fRegisterBase + reg, value);
}
inline void
EHCI::WriteReg32(uint32 reg, uint32 value)
{
sPCIModule->write_io_32(fRegisterBase + reg, value);
}
inline uint8
EHCI::ReadReg8(uint32 reg)
{
return sPCIModule->read_io_8(fRegisterBase + reg);
}
inline uint16
EHCI::ReadReg16(uint32 reg)
{
return sPCIModule->read_io_16(fRegisterBase + reg);
*(volatile uint32 *)(fOperationalRegisters + reg) = value;
}
inline uint32
EHCI::ReadReg32(uint32 reg)
EHCI::ReadOpReg(uint32 reg)
{
return sPCIModule->read_io_32(fRegisterBase + reg);
return *(volatile uint32 *)(fOperationalRegisters + reg);
}
inline uint8
EHCI::ReadCapReg8(uint32 reg)
{
return *(volatile uint8 *)(fCapabilityRegisters + reg);
}
inline uint16
EHCI::ReadCapReg16(uint32 reg)
{
return *(volatile uint16 *)(fCapabilityRegisters + reg);
}
inline uint32
EHCI::ReadCapReg32(uint32 reg)
{
return *(volatile uint32 *)(fCapabilityRegisters + reg);
}

View File

@ -87,25 +87,26 @@ static int32 FinishThread(void *data);
size_t ReadActualLength(ehci_qtd *topDescriptor,
uint8 *lastDataToggle);
// Register functions
inline void WriteReg8(uint32 reg, uint8 value);
inline void WriteReg16(uint32 reg, uint16 value);
inline void WriteReg32(uint32 reg, uint32 value);
inline uint8 ReadReg8(uint32 reg);
inline uint16 ReadReg16(uint32 reg);
inline uint32 ReadReg32(uint32 reg);
// Operational register functions
inline void WriteOpReg(uint32 reg, uint32 value);
inline uint32 ReadOpReg(uint32 reg);
// Capability register functions
inline uint8 ReadCapReg8(uint32 reg);
inline uint16 ReadCapReg16(uint32 reg);
inline uint32 ReadCapReg32(uint32 reg);
static pci_module_info *sPCIModule;
uint32 fRegisterBase;
uint8 *fCapabilityRegisters;
uint8 *fOperationalRegisters;
area_id fRegisterArea;
pci_info *fPCIInfo;
Stack *fStack;
// Framelist memory
area_id fPeriodicFrameListArea;
addr_t *fPeriodicFrameList;
area_id fAsyncFrameListArea;
addr_t *fAsyncFrameList;
// Maintain a linked list of transfers
transfer_data *fFirstTransfer;

View File

@ -9,6 +9,14 @@
#ifndef EHCI_HARDWARE_H
#define EHCI_HARDWARE_H
// Host Controller Capability Registers (EHCI Spec 2.2)
#define EHCI_CAPLENGTH 0x00 // Capability Register Length
#define EHCI_HCIVERSION 0x02 // Interface Version Number
#define EHCI_HCSPARAMS 0x04 // Structural Parameters
#define EHCI_HCCPARAMS 0x08 // Capability Parameters
#define EHCI_HCSP_PORTROUTE 0x0c // Companion Port Route Description
// Host Controller Operational Registers (EHCI Spec 2.3)
#define EHCI_USBCMD 0x00 // USB Command
#define EHCI_USBSTS 0x04 // USB Status
@ -88,16 +96,17 @@
// PCI Registers
#define PCI_LEGSUP 0xc0 // PCI Legacy Support
#define PCI_LEGSUP_USBPIRQDEN 0x2000 // USBP IRQ Deny
#define PCI_LEGSUP_USBPIRQDEN 0x2000 // USB PIRQ
// Data Structures (EHCI Spec 3)
// Periodic Frame List Element Flags (EHCI Spec 3.1)
#define EHCI_PFRAMELIST_ITD 0x00 // Isochronous Transfer Descriptor
#define EHCI_PFRAMELIST_QH 0x01 // Queue Head
#define EHCI_PFRAMELIST_SITD 0x10 // Split Transaction Isochronous TD
#define EHCI_PFRAMELIST_FSTN 0x11 // Frame Span Traversal Node
#define EHCI_PFRAMELIST_TERM (1 << 0) // Terminate
#define EHCI_PFRAMELIST_ITD (0 << 1) // Isochronous Transfer Descriptor
#define EHCI_PFRAMELIST_QH (1 << 1) // Queue Head
#define EHCI_PFRAMELIST_SITD (2 << 1) // Split Transaction Isochronous TD
#define EHCI_PFRAMELIST_FSTN (3 << 1) // Frame Span Traversal Node
// ToDo: Isochronous (High-Speed) Transfer Descriptors (iTD, EHCI Spec 3.2)

View File

@ -339,10 +339,12 @@ UHCI::UHCI(pci_info *info, Stack *stack)
fRegisterBase = sPCIModule->read_pci_config(fPCIInfo->bus,
fPCIInfo->device, fPCIInfo->function, PCI_memory_base, 4);
fRegisterBase &= PCI_address_io_mask;
TRACE(("usb_uhci: iospace offset: 0x%08x\n", fRegisterBase));
TRACE_ERROR(("usb_uhci: iospace offset: 0x%08x\n", fRegisterBase));
//fRegisterBase = fPCIInfo->u.h0.base_registers[0];
//TRACE(("usb_uhci: register base: 0x%08x\n", fRegisterBase));
if (fRegisterBase == 0) {
fRegisterBase = fPCIInfo->u.h0.base_registers[0];
TRACE_ERROR(("usb_uhci: register base: 0x%08x\n", fRegisterBase));
}
// enable pci address access
uint16 command = PCI_command_io | PCI_command_master | PCI_command_memory;