From 18451a0c360c0db068891588dc0f6d97c5ee9cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 30 Mar 2011 18:00:30 +0000 Subject: [PATCH] Added skeleton XHCI driver by Jian Chiang, based on the specs and the EHCI driver. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41142 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/busses/usb/Jamfile | 7 + src/add-ons/kernel/busses/usb/xhci.cpp | 453 ++++++++++++++++++ src/add-ons/kernel/busses/usb/xhci.h | 83 ++++ src/add-ons/kernel/busses/usb/xhci.rdef | 15 + src/add-ons/kernel/busses/usb/xhci_hardware.h | 50 ++ 5 files changed, 608 insertions(+) create mode 100644 src/add-ons/kernel/busses/usb/xhci.cpp create mode 100644 src/add-ons/kernel/busses/usb/xhci.h create mode 100644 src/add-ons/kernel/busses/usb/xhci.rdef create mode 100644 src/add-ons/kernel/busses/usb/xhci_hardware.h diff --git a/src/add-ons/kernel/busses/usb/Jamfile b/src/add-ons/kernel/busses/usb/Jamfile index a80d199ca2..0afb4610f9 100644 --- a/src/add-ons/kernel/busses/usb/Jamfile +++ b/src/add-ons/kernel/busses/usb/Jamfile @@ -25,3 +25,10 @@ KernelAddon ehci : : libusb.a : ehci.rdef ; + +KernelAddon xhci : + xhci.cpp + : libusb.a + : xhci.rdef + ; + diff --git a/src/add-ons/kernel/busses/usb/xhci.cpp b/src/add-ons/kernel/busses/usb/xhci.cpp new file mode 100644 index 0000000000..fe1069c406 --- /dev/null +++ b/src/add-ons/kernel/busses/usb/xhci.cpp @@ -0,0 +1,453 @@ +/* + * Copyright 2006-2011, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Some code borrowed from the Haiku EHCI driver + * + * Authors: + * Michael Lotz + * Jian Chiang + */ + + +#include +#include +#include +#include + +#define TRACE_USB +#include "xhci.h" + +#define USB_MODULE_NAME "xhci" + +pci_module_info *XHCI::sPCIModule = NULL; + + +static int32 +xhci_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + TRACE_MODULE("xhci init module\n"); + return B_OK; + case B_MODULE_UNINIT: + TRACE_MODULE("xhci uninit module\n"); + return B_OK; + } + + return EINVAL; +} + + +usb_host_controller_info xhci_module = { + { + "busses/usb/xhci", + 0, + xhci_std_ops + }, + NULL, + XHCI::AddTo +}; + + +module_info *modules[] = { + (module_info *)&xhci_module, + NULL +}; + + +XHCI::XHCI(pci_info *info, Stack *stack) + : BusManager(stack), + fCapabilityRegisters(NULL), + fOperationalRegisters(NULL), + fRegisterArea(-1), + fPCIInfo(info), + fStack(stack), + fPortCount(0) +{ + if (BusManager::InitCheck() < B_OK) { + TRACE_ERROR("bus manager failed to init\n"); + return; + } + + TRACE("constructing new XHCI host controller driver\n"); + fInitOK = false; + + // 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; + + sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, + fPCIInfo->function, PCI_command, 2, command); + + // 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("map physical memory 0x%08lx (base: 0x%08lx; offset: %lx); size: %ld\n", + fPCIInfo->u.h0.base_registers[0], physicalAddress, offset, + fPCIInfo->u.h0.base_register_sizes[0]); + + fRegisterArea = map_physical_memory("EHCI memory mapped registers", + 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("failed to map register memory\n"); + return; + } + + fCapabilityRegisters += offset; + fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_CAPLENGTH); + fRuntimeRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_RTSOFF); + TRACE("mapped capability registers: 0x%08lx\n", (uint32)fCapabilityRegisters); + TRACE("mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters); + TRACE("mapped rumtime registers: 0x%08lx\n", (uint32)fRuntimeRegisters); + + TRACE("structural parameters1: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS1)); + TRACE("structural parameters2: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS2)); + TRACE("structural parameters3: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS3)); + TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(XHCI_HCCPARAMS)); + + // read port count from capability register + fPortCount = (ReadCapReg32(XHCI_HCSPARAMS1) >> 24) & 0x7f; + + uint32 extendedCapPointer = ((ReadCapReg32(XHCI_HCCPARAMS) >> 16) & 0xffff) + << 2; + if (extendedCapPointer > 0) { + TRACE("extended capabilities register at %ld\n", extendedCapPointer); + + 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"); + } + + // halt the host controller + if (ControllerHalt() < B_OK) { + return; + } + + // reset the host controller + if (ControllerReset() < B_OK) { + TRACE_ERROR("host controller failed to reset\n"); + return; + } + + fInitOK = true; + TRACE("XHCI host controller driver constructed\n"); +} + + +XHCI::~XHCI() +{ + TRACE("tear down XHCI host controller driver\n"); + + WriteOpReg(XHCI_CMD, 0); + + delete_area(fRegisterArea); + put_module(B_PCI_MODULE_NAME); +} + + +status_t +XHCI::Start() +{ + TRACE("starting XHCI host controller\n"); + TRACE("usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(XHCI_CMD), + ReadOpReg(XHCI_STS)); + + TRACE_ALWAYS("successfully started the controller\n"); + return BusManager::Start(); +} + + +status_t +XHCI::SubmitTransfer(Transfer *transfer) +{ + return B_OK; +} + + +status_t +XHCI::CancelQueuedTransfers(Pipe *pipe, bool force) +{ + return B_OK; +} + + +status_t +XHCI::NotifyPipeChange(Pipe *pipe, usb_change change) +{ + TRACE("pipe change %d for pipe %p\n", change, pipe); + switch (change) { + case USB_CHANGE_CREATED: + case USB_CHANGE_DESTROYED: { + // ToDo: we should create and keep a single queue head + // for all transfers to/from this pipe + break; + } + + case USB_CHANGE_PIPE_POLICY_CHANGED: { + // ToDo: for isochronous pipes we might need to adapt to new + // pipe policy settings here + break; + } + } + + return B_OK; +} + + +status_t +XHCI::AddTo(Stack *stack) +{ +#ifdef TRACE_USB + set_dprintf_enabled(true); +#ifndef HAIKU_TARGET_PLATFORM_HAIKU + load_driver_symbols("xhci"); +#endif +#endif + + if (!sPCIModule) { + status_t status = get_module(B_PCI_MODULE_NAME, + (module_info **)&sPCIModule); + if (status < B_OK) { + TRACE_MODULE_ERROR("getting pci module failed! 0x%08lx\n", status); + return status; + } + } + + TRACE_MODULE("searching devices\n"); + bool found = false; + pci_info *item = new(std::nothrow) pci_info; + if (!item) { + sPCIModule = NULL; + put_module(B_PCI_MODULE_NAME); + return B_NO_MEMORY; + } + + for (int32 i = 0; sPCIModule->get_nth_pci_info(i, item) >= B_OK; i++) { + if (item->class_base == PCI_serial_bus && item->class_sub == PCI_usb + && item->class_api == PCI_usb_xhci) { + if (item->u.h0.interrupt_line == 0 + || item->u.h0.interrupt_line == 0xFF) { + TRACE_MODULE_ERROR("found device with invalid IRQ - check IRQ " + "assignment\n"); + continue; + } + + TRACE_MODULE("found device at IRQ %u\n", + item->u.h0.interrupt_line); + XHCI *bus = new(std::nothrow) XHCI(item, stack); + if (!bus) { + delete item; + sPCIModule = NULL; + put_module(B_PCI_MODULE_NAME); + return B_NO_MEMORY; + } + + if (bus->InitCheck() < B_OK) { + TRACE_MODULE_ERROR("bus failed init check\n"); + delete bus; + continue; + } + + // the bus took it away + item = new(std::nothrow) pci_info; + + bus->Start(); + stack->AddBusManager(bus); + found = true; + } + } + + if (!found) { + TRACE_MODULE_ERROR("no devices found\n"); + delete item; + sPCIModule = NULL; + put_module(B_PCI_MODULE_NAME); + return ENODEV; + } + + delete item; + return B_OK; +} + + +status_t +XHCI::GetPortStatus(uint8 index, usb_port_status *status) +{ + return B_OK; +} + + +status_t +XHCI::SetPortFeature(uint8 index, uint16 feature) +{ + return B_BAD_VALUE; +} + + +status_t +XHCI::ClearPortFeature(uint8 index, uint16 feature) +{ + 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 +XHCI::ControllerHalt() +{ + WriteOpReg(XHCI_CMD, 0); + + int32 tries = 100; + while ((ReadOpReg(XHCI_STS) & STS_HCH) == 0) { + snooze(1000); + if (tries-- < 0) + return B_ERROR; + } + + return B_OK; +} + + +status_t +XHCI::ControllerReset() +{ + WriteOpReg(XHCI_CMD, CMD_HCRST); + + int32 tries = 100; + while (ReadOpReg(XHCI_CMD) & CMD_HCRST) { + snooze(1000); + if (tries-- < 0) + return B_ERROR; + } + + tries = 100; + while (ReadOpReg(XHCI_STS) & STS_CNR) { + snooze(1000); + if (tries-- < 0) + return B_ERROR; + } + + return B_OK; +} + + +status_t +XHCI::LightReset() +{ + return B_ERROR; +} + + +int32 +XHCI::InterruptHandler(void *data) +{ + return ((XHCI *)data)->Interrupt(); +} + + +int32 +XHCI::Interrupt() +{ + int32 result = B_HANDLED_INTERRUPT; + + return result; +} + +inline void +XHCI::WriteOpReg(uint32 reg, uint32 value) +{ + *(volatile uint32 *)(fOperationalRegisters + reg) = value; +} + + +inline uint32 +XHCI::ReadOpReg(uint32 reg) +{ + return *(volatile uint32 *)(fOperationalRegisters + reg); +} + + +inline uint8 +XHCI::ReadCapReg8(uint32 reg) +{ + return *(volatile uint8 *)(fCapabilityRegisters + reg); +} + + +inline uint16 +XHCI::ReadCapReg16(uint32 reg) +{ + return *(volatile uint16 *)(fCapabilityRegisters + reg); +} + + +inline uint32 +XHCI::ReadCapReg32(uint32 reg) +{ + return *(volatile uint32 *)(fCapabilityRegisters + reg); +} + + +inline void +XHCI::WriteCapReg32(uint32 reg, uint32 value) +{ + *(volatile uint32 *)(fCapabilityRegisters + reg) = value; +} + diff --git a/src/add-ons/kernel/busses/usb/xhci.h b/src/add-ons/kernel/busses/usb/xhci.h new file mode 100644 index 0000000000..6a8867ae88 --- /dev/null +++ b/src/add-ons/kernel/busses/usb/xhci.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006-2011, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * Jian Chiang + */ +#ifndef XHCI_H +#define XHCI_H + + +#include "usb_private.h" +#include "xhci_hardware.h" + + +struct pci_info; +struct pci_module_info; + + +class XHCI : public BusManager { +public: + XHCI(pci_info *info, Stack *stack); + ~XHCI(); + + status_t Start(); + virtual status_t SubmitTransfer(Transfer *transfer); + virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force); + + virtual status_t NotifyPipeChange(Pipe *pipe, + usb_change change); + + static status_t AddTo(Stack *stack); + + // Port operations for root hub + uint8 PortCount() { return fPortCount; }; + status_t GetPortStatus(uint8 index, usb_port_status *status); + status_t SetPortFeature(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"; }; + +private: + // Controller resets + status_t ControllerReset(); + status_t ControllerHalt(); + status_t LightReset(); + + // Interrupt functions + static int32 InterruptHandler(void *data); + int32 Interrupt(); + + + // 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); + inline void WriteCapReg32(uint32 reg, uint32 value); + + static pci_module_info * sPCIModule; + + uint8 * fCapabilityRegisters; + uint8 * fOperationalRegisters; + uint8 * fRuntimeRegisters; + area_id fRegisterArea; + pci_info * fPCIInfo; + Stack * fStack; + + // Root Hub + + // Port management + uint8 fPortCount; +}; + + +#endif // !XHCI_H diff --git a/src/add-ons/kernel/busses/usb/xhci.rdef b/src/add-ons/kernel/busses/usb/xhci.rdef new file mode 100644 index 0000000000..ed9a1ef225 --- /dev/null +++ b/src/add-ons/kernel/busses/usb/xhci.rdef @@ -0,0 +1,15 @@ +/* + * xhci.rdef + */ + +resource app_signature "application/x-vnd.haiku-xhci"; + +resource app_version { + major = 1, + middle = 0, + minor = 0, + variety = 0, + internal = 0, + short_info = "XHCI host controller driver", + long_info = "Haiku XHCI HCD - Copyright 2011, Haiku Inc." +}; diff --git a/src/add-ons/kernel/busses/usb/xhci_hardware.h b/src/add-ons/kernel/busses/usb/xhci_hardware.h new file mode 100644 index 0000000000..7d832362b0 --- /dev/null +++ b/src/add-ons/kernel/busses/usb/xhci_hardware.h @@ -0,0 +1,50 @@ +/* + * Copyright 2011, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jian Chiang + */ +#ifndef XHCI_HARDWARE_H +#define XHCI_HARDWARE_H + + +// Host Controller Capability Registers +#define XHCI_CAPLENGTH 0x00 // Capability Register Length +#define XHCI_HCIVERSION 0x02 // Interface Version Number +#define XHCI_HCSPARAMS1 0x04 // Structural Parameters 1 +#define XHCI_HCSPARAMS2 0x08 // Structural Parameters 2 +#define XHCI_HCSPARAMS3 0x0C // Structural Parameters 3 +#define XHCI_HCCPARAMS 0x10 // Capability Parameters +#define XHCI_RTSOFF 0x18 // Runtime Register Space offset + + +// Host Controller Operational Registers +#define XHCI_CMD 0x00 // USB Command +#define XHCI_STS 0x04 // USB Status + + +// USB Command Register +#define CMD_HCRST (1 << 1) // Host Controller Reset + + +// USB Status Register +#define STS_HCH (1<<0) +#define STS_CNR (1<<11) + + + + + +// Extended Capabilities +#define XHCI_LEGSUP_CAPID_MASK 0xff +#define XHCI_LEGSUP_CAPID 0x01 +#define XHCI_LEGSUP_OSOWNED (1 << 24) // OS Owned Semaphore +#define XHCI_LEGSUP_BIOSOWNED (1 << 16) // BIOS Owned Semaphore + +#define XHCI_LEGCTLSTS 0x04 +#define XHCI_LEGCTLSTS_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) + + +#endif // !XHCI_HARDWARE_H +