1 /* 2 * Copyright 2006-2011, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Some code borrowed from the Haiku EHCI driver 6 * 7 * Authors: 8 * Michael Lotz <mmlr@mlotz.ch> 9 * Jian Chiang <j.jian.chiang@gmail.com> 10 */ 11 12 13 #include <module.h> 14 #include <PCI.h> 15 #include <USB3.h> 16 #include <KernelExport.h> 17 18 #define TRACE_USB 19 #include "xhci.h" 20 21 #define USB_MODULE_NAME "xhci" 22 23 pci_module_info *XHCI::sPCIModule = NULL; 24 25 26 static int32 27 xhci_std_ops(int32 op, ...) 28 { 29 switch (op) { 30 case B_MODULE_INIT: 31 TRACE_MODULE("xhci init module\n"); 32 return B_OK; 33 case B_MODULE_UNINIT: 34 TRACE_MODULE("xhci uninit module\n"); 35 return B_OK; 36 } 37 38 return EINVAL; 39 } 40 41 42 usb_host_controller_info xhci_module = { 43 { 44 "busses/usb/xhci", 45 0, 46 xhci_std_ops 47 }, 48 NULL, 49 XHCI::AddTo 50 }; 51 52 53 module_info *modules[] = { 54 (module_info *)&xhci_module, 55 NULL 56 }; 57 58 59 XHCI::XHCI(pci_info *info, Stack *stack) 60 : BusManager(stack), 61 fCapabilityRegisters(NULL), 62 fOperationalRegisters(NULL), 63 fRegisterArea(-1), 64 fPCIInfo(info), 65 fStack(stack), 66 fPortCount(0) 67 { 68 if (BusManager::InitCheck() < B_OK) { 69 TRACE_ERROR("bus manager failed to init\n"); 70 return; 71 } 72 73 TRACE("constructing new XHCI host controller driver\n"); 74 fInitOK = false; 75 76 // enable busmaster and memory mapped access 77 uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus, 78 fPCIInfo->device, fPCIInfo->function, PCI_command, 2); 79 command &= ~PCI_command_io; 80 command |= PCI_command_master | PCI_command_memory; 81 82 sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, 83 fPCIInfo->function, PCI_command, 2, command); 84 85 // map the registers 86 uint32 offset = fPCIInfo->u.h0.base_registers[0] & (B_PAGE_SIZE - 1); 87 addr_t physicalAddress = fPCIInfo->u.h0.base_registers[0] - offset; 88 size_t mapSize = (fPCIInfo->u.h0.base_register_sizes[0] + offset 89 + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 90 91 TRACE("map physical memory 0x%08lx (base: 0x%08lx; offset: %lx); size: %ld\n", 92 fPCIInfo->u.h0.base_registers[0], physicalAddress, offset, 93 fPCIInfo->u.h0.base_register_sizes[0]); 94 95 fRegisterArea = map_physical_memory("EHCI memory mapped registers", 96 physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS, 97 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA, 98 (void **)&fCapabilityRegisters); 99 if (fRegisterArea < B_OK) { 100 TRACE("failed to map register memory\n"); 101 return; 102 } 103 104 fCapabilityRegisters += offset; 105 fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_CAPLENGTH); 106 fRuntimeRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_RTSOFF); 107 TRACE("mapped capability registers: 0x%08lx\n", (uint32)fCapabilityRegisters); 108 TRACE("mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters); 109 TRACE("mapped rumtime registers: 0x%08lx\n", (uint32)fRuntimeRegisters); 110 111 TRACE("structural parameters1: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS1)); 112 TRACE("structural parameters2: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS2)); 113 TRACE("structural parameters3: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS3)); 114 TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(XHCI_HCCPARAMS)); 115 116 // read port count from capability register 117 fPortCount = (ReadCapReg32(XHCI_HCSPARAMS1) >> 24) & 0x7f; 118 119 uint32 extendedCapPointer = ((ReadCapReg32(XHCI_HCCPARAMS) >> 16) & 0xffff) 120 << 2; 121 if (extendedCapPointer > 0) { 122 TRACE("extended capabilities register at %ld\n", extendedCapPointer); 123 124 uint32 legacySupport = ReadCapReg32(extendedCapPointer); 125 if ((legacySupport & XHCI_LEGSUP_CAPID_MASK) == XHCI_LEGSUP_CAPID) { 126 if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) != 0) { 127 TRACE_ALWAYS("the host controller is bios owned, claiming" 128 " ownership\n"); 129 WriteCapReg32(extendedCapPointer, legacySupport 130 | XHCI_LEGSUP_OSOWNED); 131 for (int32 i = 0; i < 20; i++) { 132 legacySupport = ReadCapReg32(extendedCapPointer); 133 134 if ((legacySupport & XHCI_LEGSUP_BIOSOWNED) == 0) 135 break; 136 137 TRACE_ALWAYS("controller is still bios owned, waiting\n"); 138 snooze(50000); 139 } 140 } 141 142 if (legacySupport & XHCI_LEGSUP_BIOSOWNED) { 143 TRACE_ERROR("bios won't give up control over the host " 144 "controller (ignoring)\n"); 145 } else if (legacySupport & XHCI_LEGSUP_OSOWNED) { 146 TRACE_ALWAYS("successfully took ownership of the host " 147 "controller\n"); 148 } 149 150 // Force off the BIOS owned flag, and clear all SMIs. Some BIOSes 151 // do indicate a successful handover but do not remove their SMIs 152 // and then freeze the system when interrupts are generated. 153 WriteCapReg32(extendedCapPointer, legacySupport & ~XHCI_LEGSUP_BIOSOWNED); 154 WriteCapReg32(extendedCapPointer + XHCI_LEGCTLSTS, 155 XHCI_LEGCTLSTS_DISABLE_SMI); 156 } else { 157 TRACE("extended capability is not a legacy support register\n"); 158 } 159 } else { 160 TRACE("no extended capabilities register\n"); 161 } 162 163 // halt the host controller 164 if (ControllerHalt() < B_OK) { 165 return; 166 } 167 168 // reset the host controller 169 if (ControllerReset() < B_OK) { 170 TRACE_ERROR("host controller failed to reset\n"); 171 return; 172 } 173 174 fInitOK = true; 175 TRACE("XHCI host controller driver constructed\n"); 176 } 177 178 179 XHCI::~XHCI() 180 { 181 TRACE("tear down XHCI host controller driver\n"); 182 183 WriteOpReg(XHCI_CMD, 0); 184 185 delete_area(fRegisterArea); 186 put_module(B_PCI_MODULE_NAME); 187 } 188 189 190 status_t 191 XHCI::Start() 192 { 193 TRACE("starting XHCI host controller\n"); 194 TRACE("usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(XHCI_CMD), 195 ReadOpReg(XHCI_STS)); 196 197 TRACE_ALWAYS("successfully started the controller\n"); 198 return BusManager::Start(); 199 } 200 201 202 status_t 203 XHCI::SubmitTransfer(Transfer *transfer) 204 { 205 return B_OK; 206 } 207 208 209 status_t 210 XHCI::CancelQueuedTransfers(Pipe *pipe, bool force) 211 { 212 return B_OK; 213 } 214 215 216 status_t 217 XHCI::NotifyPipeChange(Pipe *pipe, usb_change change) 218 { 219 TRACE("pipe change %d for pipe %p\n", change, pipe); 220 switch (change) { 221 case USB_CHANGE_CREATED: 222 case USB_CHANGE_DESTROYED: { 223 // ToDo: we should create and keep a single queue head 224 // for all transfers to/from this pipe 225 break; 226 } 227 228 case USB_CHANGE_PIPE_POLICY_CHANGED: { 229 // ToDo: for isochronous pipes we might need to adapt to new 230 // pipe policy settings here 231 break; 232 } 233 } 234 235 return B_OK; 236 } 237 238 239 status_t 240 XHCI::AddTo(Stack *stack) 241 { 242 #ifdef TRACE_USB 243 set_dprintf_enabled(true); 244 #ifndef HAIKU_TARGET_PLATFORM_HAIKU 245 load_driver_symbols("xhci"); 246 #endif 247 #endif 248 249 if (!sPCIModule) { 250 status_t status = get_module(B_PCI_MODULE_NAME, 251 (module_info **)&sPCIModule); 252 if (status < B_OK) { 253 TRACE_MODULE_ERROR("getting pci module failed! 0x%08lx\n", status); 254 return status; 255 } 256 } 257 258 TRACE_MODULE("searching devices\n"); 259 bool found = false; 260 pci_info *item = new(std::nothrow) pci_info; 261 if (!item) { 262 sPCIModule = NULL; 263 put_module(B_PCI_MODULE_NAME); 264 return B_NO_MEMORY; 265 } 266 267 for (int32 i = 0; sPCIModule->get_nth_pci_info(i, item) >= B_OK; i++) { 268 if (item->class_base == PCI_serial_bus && item->class_sub == PCI_usb 269 && item->class_api == PCI_usb_xhci) { 270 if (item->u.h0.interrupt_line == 0 271 || item->u.h0.interrupt_line == 0xFF) { 272 TRACE_MODULE_ERROR("found device with invalid IRQ - check IRQ " 273 "assignment\n"); 274 continue; 275 } 276 277 TRACE_MODULE("found device at IRQ %u\n", 278 item->u.h0.interrupt_line); 279 XHCI *bus = new(std::nothrow) XHCI(item, stack); 280 if (!bus) { 281 delete item; 282 sPCIModule = NULL; 283 put_module(B_PCI_MODULE_NAME); 284 return B_NO_MEMORY; 285 } 286 287 if (bus->InitCheck() < B_OK) { 288 TRACE_MODULE_ERROR("bus failed init check\n"); 289 delete bus; 290 continue; 291 } 292 293 // the bus took it away 294 item = new(std::nothrow) pci_info; 295 296 bus->Start(); 297 stack->AddBusManager(bus); 298 found = true; 299 } 300 } 301 302 if (!found) { 303 TRACE_MODULE_ERROR("no devices found\n"); 304 delete item; 305 sPCIModule = NULL; 306 put_module(B_PCI_MODULE_NAME); 307 return ENODEV; 308 } 309 310 delete item; 311 return B_OK; 312 } 313 314 315 status_t 316 XHCI::GetPortStatus(uint8 index, usb_port_status *status) 317 { 318 return B_OK; 319 } 320 321 322 status_t 323 XHCI::SetPortFeature(uint8 index, uint16 feature) 324 { 325 return B_BAD_VALUE; 326 } 327 328 329 status_t 330 XHCI::ClearPortFeature(uint8 index, uint16 feature) 331 { 332 return B_BAD_VALUE; 333 } 334 335 336 status_t 337 XHCI::ResetPort(uint8 index) 338 { 339 TRACE("reset port %d\n", index); 340 341 return B_OK; 342 } 343 344 345 status_t 346 XHCI::SuspendPort(uint8 index) 347 { 348 return B_OK; 349 } 350 351 352 status_t 353 XHCI::ControllerHalt() 354 { 355 WriteOpReg(XHCI_CMD, 0); 356 357 int32 tries = 100; 358 while ((ReadOpReg(XHCI_STS) & STS_HCH) == 0) { 359 snooze(1000); 360 if (tries-- < 0) 361 return B_ERROR; 362 } 363 364 return B_OK; 365 } 366 367 368 status_t 369 XHCI::ControllerReset() 370 { 371 WriteOpReg(XHCI_CMD, CMD_HCRST); 372 373 int32 tries = 100; 374 while (ReadOpReg(XHCI_CMD) & CMD_HCRST) { 375 snooze(1000); 376 if (tries-- < 0) 377 return B_ERROR; 378 } 379 380 tries = 100; 381 while (ReadOpReg(XHCI_STS) & STS_CNR) { 382 snooze(1000); 383 if (tries-- < 0) 384 return B_ERROR; 385 } 386 387 return B_OK; 388 } 389 390 391 status_t 392 XHCI::LightReset() 393 { 394 return B_ERROR; 395 } 396 397 398 int32 399 XHCI::InterruptHandler(void *data) 400 { 401 return ((XHCI *)data)->Interrupt(); 402 } 403 404 405 int32 406 XHCI::Interrupt() 407 { 408 int32 result = B_HANDLED_INTERRUPT; 409 410 return result; 411 } 412 413 inline void 414 XHCI::WriteOpReg(uint32 reg, uint32 value) 415 { 416 *(volatile uint32 *)(fOperationalRegisters + reg) = value; 417 } 418 419 420 inline uint32 421 XHCI::ReadOpReg(uint32 reg) 422 { 423 return *(volatile uint32 *)(fOperationalRegisters + reg); 424 } 425 426 427 inline uint8 428 XHCI::ReadCapReg8(uint32 reg) 429 { 430 return *(volatile uint8 *)(fCapabilityRegisters + reg); 431 } 432 433 434 inline uint16 435 XHCI::ReadCapReg16(uint32 reg) 436 { 437 return *(volatile uint16 *)(fCapabilityRegisters + reg); 438 } 439 440 441 inline uint32 442 XHCI::ReadCapReg32(uint32 reg) 443 { 444 return *(volatile uint32 *)(fCapabilityRegisters + reg); 445 } 446 447 448 inline void 449 XHCI::WriteCapReg32(uint32 reg, uint32 value) 450 { 451 *(volatile uint32 *)(fCapabilityRegisters + reg) = value; 452 } 453 454