xref: /haiku/src/add-ons/kernel/busses/usb/xhci.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
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 		fErstArea(-1),
67 		fDcbaArea(-1),
68 		fSpinlock(B_SPINLOCK_INITIALIZER),
69 		fCmdCompSem(-1),
70 		fCmdCompThread(-1),
71 		fFinishTransfersSem(-1),
72 		fFinishThread(-1),
73 		fStopThreads(false),
74 		fRootHub(NULL),
75 		fRootHubAddress(0),
76 		fPortCount(0),
77 		fSlotCount(0),
78 		fEventIdx(0),
79 		fCmdIdx(0),
80 		fEventCcs(1),
81 		fCmdCcs(1)
82 {
83 	if (BusManager::InitCheck() < B_OK) {
84 		TRACE_ERROR("bus manager failed to init\n");
85 		return;
86 	}
87 
88 	TRACE("constructing new XHCI host controller driver\n");
89 	fInitOK = false;
90 
91 	// enable busmaster and memory mapped access
92 	uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus,
93 		fPCIInfo->device, fPCIInfo->function, PCI_command, 2);
94 	command &= ~(PCI_command_io | PCI_command_int_disable);
95 	command |= PCI_command_master | PCI_command_memory;
96 
97 	sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
98 		fPCIInfo->function, PCI_command, 2, command);
99 
100 	// map the registers
101 	uint32 offset = fPCIInfo->u.h0.base_registers[0] & (B_PAGE_SIZE - 1);
102 	addr_t physicalAddress = fPCIInfo->u.h0.base_registers[0] - offset;
103 	size_t mapSize = (fPCIInfo->u.h0.base_register_sizes[0] + offset
104 		+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
105 
106 	TRACE("map physical memory 0x%08lx (base: 0x%08lx; offset: %lx); size: %ld\n",
107 		fPCIInfo->u.h0.base_registers[0], physicalAddress, offset,
108 		fPCIInfo->u.h0.base_register_sizes[0]);
109 
110 	fRegisterArea = map_physical_memory("XHCI memory mapped registers",
111 		physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS,
112 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA,
113 		(void **)&fCapabilityRegisters);
114 	if (fRegisterArea < B_OK) {
115 		TRACE("failed to map register memory\n");
116 		return;
117 	}
118 
119 	fCapabilityRegisters += offset;
120 	fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(XHCI_CAPLENGTH);
121 	fRuntimeRegisters = fCapabilityRegisters + ReadCapReg32(XHCI_RTSOFF);
122 	fDoorbellRegisters = fCapabilityRegisters + ReadCapReg32(XHCI_DBOFF);
123 	TRACE("mapped capability registers: 0x%08lx\n", (uint32)fCapabilityRegisters);
124 	TRACE("mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters);
125 	TRACE("mapped rumtime registers: 0x%08lx\n", (uint32)fRuntimeRegisters);
126 	TRACE("mapped doorbell registers: 0x%08lx\n", (uint32)fDoorbellRegisters);
127 
128 	TRACE("structural parameters1: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS1));
129 	TRACE("structural parameters2: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS2));
130 	TRACE("structural parameters3: 0x%08lx\n", ReadCapReg32(XHCI_HCSPARAMS3));
131 	TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(XHCI_HCCPARAMS));
132 
133 	uint32 cparams = ReadCapReg32(XHCI_HCCPARAMS);
134 	uint32 eec = 0xffffffff;
135 	uint32 eecp = HCS0_XECP(cparams) << 2;
136 	for (; eecp != 0 && XECP_NEXT(eec); eecp += XECP_NEXT(eec) << 2) {
137 		eec = ReadCapReg32(eecp);
138 		if (XECP_ID(eec) != XHCI_LEGSUP_CAPID)
139 			continue;
140 	}
141 	if (eec & XHCI_LEGSUP_BIOSOWNED) {
142 		TRACE_ALWAYS("the host controller is bios owned, claiming"
143 			" ownership\n");
144 		WriteCapReg32(eecp, eec | XHCI_LEGSUP_OSOWNED);
145 
146 		for (int32 i = 0; i < 20; i++) {
147 			eec = ReadCapReg32(eecp);
148 
149 			if ((eec & XHCI_LEGSUP_BIOSOWNED) == 0)
150 				break;
151 
152 			TRACE_ALWAYS("controller is still bios owned, waiting\n");
153 			snooze(50000);
154 		}
155 
156 		if (eec & XHCI_LEGSUP_BIOSOWNED) {
157 			TRACE_ERROR("bios won't give up control over the host "
158 				"controller (ignoring)\n");
159 		} else if (eec & XHCI_LEGSUP_OSOWNED) {
160 			TRACE_ALWAYS("successfully took ownership of the host "
161 				"controller\n");
162 		}
163 
164 		// Force off the BIOS owned flag, and clear all SMIs. Some BIOSes
165 		// do indicate a successful handover but do not remove their SMIs
166 		// and then freeze the system when interrupts are generated.
167 		WriteCapReg32(eecp, eec & ~XHCI_LEGSUP_BIOSOWNED);
168 	}
169 	WriteCapReg32(eecp + XHCI_LEGCTLSTS, XHCI_LEGCTLSTS_DISABLE_SMI);
170 
171 	// halt the host controller
172 	if (ControllerHalt() < B_OK) {
173 		return;
174 	}
175 
176 	// reset the host controller
177 	if (ControllerReset() < B_OK) {
178 		TRACE_ERROR("host controller failed to reset\n");
179 		return;
180 	}
181 
182 	fCmdCompSem = create_sem(0, "XHCI Command Complete");
183 	fFinishTransfersSem = create_sem(0, "XHCI Finish Transfers");
184 	if (fFinishTransfersSem < B_OK || fCmdCompSem < B_OK) {
185 		TRACE_ERROR("failed to create semaphores\n");
186 		return;
187 	}
188 
189 	// create finisher service thread
190 	fFinishThread = spawn_kernel_thread(FinishThread, "xhci finish thread",
191 		B_NORMAL_PRIORITY, (void *)this);
192 	resume_thread(fFinishThread);
193 
194 	// create command complete service thread
195 	fCmdCompThread = spawn_kernel_thread(CmdCompThread, "xhci cmd complete thread",
196 		B_NORMAL_PRIORITY, (void *)this);
197 	resume_thread(fCmdCompThread);
198 
199 	// Install the interrupt handler
200 	TRACE("installing interrupt handler\n");
201 	install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
202 		InterruptHandler, (void *)this, 0);
203 
204 	fInitOK = true;
205 	TRACE("XHCI host controller driver constructed\n");
206 }
207 
208 
209 XHCI::~XHCI()
210 {
211 	TRACE("tear down XHCI host controller driver\n");
212 
213 	WriteOpReg(XHCI_CMD, 0);
214 
215 	int32 result = 0;
216 	fStopThreads = true;
217 	delete_sem(fCmdCompSem);
218 	delete_sem(fFinishTransfersSem);
219 	delete_area(fRegisterArea);
220 	delete_area(fErstArea);
221 	delete_area(fDcbaArea);
222 	wait_for_thread(fCmdCompThread, &result);
223 	wait_for_thread(fFinishThread, &result);
224 	put_module(B_PCI_MODULE_NAME);
225 }
226 
227 
228 status_t
229 XHCI::Start()
230 {
231 	TRACE("starting XHCI host controller\n");
232 	TRACE("usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(XHCI_CMD),
233 		ReadOpReg(XHCI_STS));
234 
235 	if ((ReadOpReg(XHCI_PAGESIZE) & (1 << 0)) == 0) {
236 		TRACE_ERROR("Controller does not support 4K page size.\n");
237 		return B_ERROR;
238 	}
239 
240 	// read port count from capability register
241 	uint32 capabilities = ReadCapReg32(XHCI_HCSPARAMS1);
242 
243 	uint8 portsCount = HCS_MAX_PORTS(capabilities);
244 	if (portsCount == 0) {
245 		TRACE_ERROR("Invalid number of ports: %u\n", portsCount);
246 		return B_ERROR;
247 	}
248 	fPortCount = portsCount;
249 	fSlotCount = HCS_MAX_SLOTS(capabilities);
250 	WriteOpReg(XHCI_CONFIG, fSlotCount);
251 
252 	void *dmaAddress;
253 	fDcbaArea = fStack->AllocateArea((void **)&fDcba, &dmaAddress,
254 		sizeof(uint64) * XHCI_MAX_SLOTS, "DCBA Area");
255 	if (fDcbaArea < B_OK) {
256 		TRACE_ERROR("unable to create the DCBA area\n");
257 		return B_ERROR;
258 	}
259 	memset(fDcba, 0, sizeof(uint64) * XHCI_MAX_SLOTS);
260 	TRACE("setting DCBAAP\n");
261 	WriteOpReg(XHCI_DCBAAP_LO, (uint32)dmaAddress);
262 	WriteOpReg(XHCI_DCBAAP_HI, 0);
263 
264 	fErstArea = fStack->AllocateArea((void **)&fErst, &dmaAddress,
265 		(MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb)
266 		+ sizeof(xhci_erst_element),
267 		"USB XHCI ERST CMD_RING and EVENT_RING Area");
268 
269 	if (fErstArea < B_OK) {
270 		TRACE_ERROR("unable to create the ERST AND RING area\n");
271 		delete_area(fDcbaArea);
272 		return B_ERROR;
273 	}
274 	memset(fErst, 0, (MAX_COMMANDS + MAX_EVENTS) * sizeof(xhci_trb)
275 		+ sizeof(xhci_erst_element));
276 
277 	fErst->rs_addr = (uint32)dmaAddress + sizeof(xhci_erst_element);
278 	fErst->rs_size = MAX_EVENTS;
279 	fErst->rsvdz = 0;
280 
281 	uint32 addr = (uint32)fErst + sizeof(xhci_erst_element);
282 	fEventRing = (xhci_trb *)addr;
283 	addr += MAX_EVENTS * sizeof(xhci_trb);
284 	fCmdRing = (xhci_trb *)addr;
285 
286 	TRACE("setting ERST size\n");
287 	WriteRunReg32(XHCI_ERSTSZ(0), XHCI_ERSTS_SET(1));
288 
289 	TRACE("setting ERDP addr = 0x%llx\n", fErst->rs_addr);
290 	WriteRunReg32(XHCI_ERDP_LO(0), (uint32)fErst->rs_addr);
291 	WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(fErst->rs_addr >> 32));
292 
293 	TRACE("setting ERST base addr = 0x%llx\n", (uint64)dmaAddress);
294 	WriteRunReg32(XHCI_ERSTBA_LO(0), (uint32)dmaAddress);
295 	WriteRunReg32(XHCI_ERSTBA_HI(0), 0);
296 
297 	addr = fErst->rs_addr + MAX_EVENTS * sizeof(xhci_trb);
298 	TRACE("setting CRCR addr = 0x%llx\n", (uint64)addr);
299 	WriteOpReg(XHCI_CRCR_LO, addr | CRCR_RCS);
300 	WriteOpReg(XHCI_CRCR_HI, 0);
301 	//link trb
302 	fCmdRing[MAX_COMMANDS - 1].qwtrb0 = addr;
303 
304 	TRACE("setting interrupt rate\n");
305 	WriteRunReg32(XHCI_IMOD(0), 160);//4000 irq/s
306 
307 	TRACE("enabling interrupt\n");
308 	WriteRunReg32(XHCI_IMAN(0), ReadRunReg32(XHCI_IMAN(0)) | IMAN_INTR_ENA);
309 
310 	WriteOpReg(XHCI_CMD, CMD_RUN | CMD_EIE | CMD_HSEIE);
311 
312 	fRootHubAddress = AllocateAddress();
313 	fRootHub = new(std::nothrow) XHCIRootHub(RootObject(), fRootHubAddress);
314 	if (!fRootHub) {
315 		TRACE_ERROR("no memory to allocate root hub\n");
316 		return B_NO_MEMORY;
317 	}
318 
319 	if (fRootHub->InitCheck() < B_OK) {
320 		TRACE_ERROR("root hub failed init check\n");
321 		return fRootHub->InitCheck();
322 	}
323 
324 	SetRootHub(fRootHub);
325 
326 	TRACE_ALWAYS("successfully started the controller\n");
327 	TRACE("No-Op test\n");
328 	QueueNoop();
329 	return BusManager::Start();
330 }
331 
332 
333 status_t
334 XHCI::SubmitTransfer(Transfer *transfer)
335 {
336 	// short circuit the root hub
337 	if (transfer->TransferPipe()->DeviceAddress() == fRootHubAddress)
338 		return fRootHub->ProcessTransfer(this, transfer);
339 
340 	return B_OK;
341 }
342 
343 
344 status_t
345 XHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
346 {
347 	return B_OK;
348 }
349 
350 
351 status_t
352 XHCI::NotifyPipeChange(Pipe *pipe, usb_change change)
353 {
354 	TRACE("pipe change %d for pipe %p\n", change, pipe);
355 	switch (change) {
356 		case USB_CHANGE_CREATED:
357 		case USB_CHANGE_DESTROYED: {
358 			// ToDo: we should create and keep a single queue head
359 			// for all transfers to/from this pipe
360 			break;
361 		}
362 
363 		case USB_CHANGE_PIPE_POLICY_CHANGED: {
364 			// ToDo: for isochronous pipes we might need to adapt to new
365 			// pipe policy settings here
366 			break;
367 		}
368 	}
369 
370 	return B_OK;
371 }
372 
373 
374 status_t
375 XHCI::AddTo(Stack *stack)
376 {
377 #ifdef TRACE_USB
378 	set_dprintf_enabled(true);
379 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
380 	load_driver_symbols("xhci");
381 #endif
382 #endif
383 
384 	if (!sPCIModule) {
385 		status_t status = get_module(B_PCI_MODULE_NAME,
386 			(module_info **)&sPCIModule);
387 		if (status < B_OK) {
388 			TRACE_MODULE_ERROR("getting pci module failed! 0x%08lx\n", status);
389 			return status;
390 		}
391 	}
392 
393 	TRACE_MODULE("searching devices\n");
394 	bool found = false;
395 	pci_info *item = new(std::nothrow) pci_info;
396 	if (!item) {
397 		sPCIModule = NULL;
398 		put_module(B_PCI_MODULE_NAME);
399 		return B_NO_MEMORY;
400 	}
401 
402 	for (int32 i = 0; sPCIModule->get_nth_pci_info(i, item) >= B_OK; i++) {
403 		if (item->class_base == PCI_serial_bus && item->class_sub == PCI_usb
404 			&& item->class_api == PCI_usb_xhci) {
405 			if (item->u.h0.interrupt_line == 0
406 				|| item->u.h0.interrupt_line == 0xFF) {
407 				TRACE_MODULE_ERROR("found device with invalid IRQ - check IRQ "
408 					"assignment\n");
409 				continue;
410 			}
411 
412 			TRACE_MODULE("found device at IRQ %u\n",
413 				item->u.h0.interrupt_line);
414 			XHCI *bus = new(std::nothrow) XHCI(item, stack);
415 			if (!bus) {
416 				delete item;
417 				sPCIModule = NULL;
418 				put_module(B_PCI_MODULE_NAME);
419 				return B_NO_MEMORY;
420 			}
421 
422 			if (bus->InitCheck() < B_OK) {
423 				TRACE_MODULE_ERROR("bus failed init check\n");
424 				delete bus;
425 				continue;
426 			}
427 
428 			// the bus took it away
429 			item = new(std::nothrow) pci_info;
430 
431 			bus->Start();
432 			stack->AddBusManager(bus);
433 			found = true;
434 		}
435 	}
436 
437 	if (!found) {
438 		TRACE_MODULE_ERROR("no devices found\n");
439 		delete item;
440 		sPCIModule = NULL;
441 		put_module(B_PCI_MODULE_NAME);
442 		return ENODEV;
443 	}
444 
445 	delete item;
446 	return B_OK;
447 }
448 
449 
450 status_t
451 XHCI::GetPortStatus(uint8 index, usb_port_status *status)
452 {
453 	if (index >= fPortCount)
454 		return B_BAD_INDEX;
455 
456 	status->status = status->change = 0;
457 	uint32 portStatus = ReadOpReg(XHCI_PORTSC(index));
458 	TRACE("port status=0x%08lx\n", portStatus);
459 
460 	// build the status
461 	switch(PS_SPEED_GET(portStatus)) {
462 	case 3:
463 		status->status |= PORT_STATUS_HIGH_SPEED;
464 		break;
465 	case 2:
466 		status->status |= PORT_STATUS_LOW_SPEED;
467 		break;
468 	default:
469 		break;
470 	}
471 
472 	if (portStatus & PS_CCS)
473 		status->status |= PORT_STATUS_CONNECTION;
474 	if (portStatus & PS_PED)
475 		status->status |= PORT_STATUS_ENABLE;
476 	if (portStatus & PS_OCA)
477 		status->status |= PORT_STATUS_OVER_CURRENT;
478 	if (portStatus & PS_PR)
479 		status->status |= PORT_STATUS_RESET;
480 	if (portStatus & PS_PP)
481 		status->status |= PORT_STATUS_POWER;
482 
483 	// build the change
484 	if (portStatus & PS_CSC)
485 		status->change |= PORT_STATUS_CONNECTION;
486 	if (portStatus & PS_PEC)
487 		status->change |= PORT_STATUS_ENABLE;
488 	if (portStatus & PS_OCC)
489 		status->change |= PORT_STATUS_OVER_CURRENT;
490 	if (portStatus & PS_PRC)
491 		status->change |= PORT_STATUS_RESET;
492 
493 	return B_OK;
494 }
495 
496 
497 status_t
498 XHCI::SetPortFeature(uint8 index, uint16 feature)
499 {
500 	TRACE("set port feature index %u feature %u\n", index, feature);
501 	if (index >= fPortCount)
502 		return B_BAD_INDEX;
503 
504 	uint32 portRegister = XHCI_PORTSC(index);
505 	uint32 portStatus = ReadOpReg(portRegister);
506 
507 	switch (feature) {
508 	case PORT_SUSPEND:
509 		if ((portStatus & PS_PED ) == 0 || (portStatus & PS_PR)
510 			|| (portStatus & PS_PLS_MASK) >= PS_XDEV_U3) {
511 			TRACE_ERROR("USB core suspending device not in U0/U1/U2.\n");
512 			return B_BAD_VALUE;
513 		}
514 		portStatus &= ~PS_CLEAR;
515 		portStatus &= ~PS_PLS_MASK;
516 		portStatus |= PS_LWS | PS_XDEV_U3;
517 		WriteOpReg(portRegister, portStatus);
518 		return B_OK;
519 
520 	case PORT_RESET:
521 		portStatus &= ~PS_CLEAR;
522 		WriteOpReg(portRegister, portStatus | PS_PR);
523 		return B_OK;
524 
525 	case PORT_POWER:
526 		portStatus &= ~PS_CLEAR;
527 		WriteOpReg(portRegister, portStatus | PS_PP);
528 		return B_OK;
529 	}
530 	return B_BAD_VALUE;
531 }
532 
533 
534 status_t
535 XHCI::ClearPortFeature(uint8 index, uint16 feature)
536 {
537 	TRACE("clear port feature index %u feature %u\n", index, feature);
538 	if (index >= fPortCount)
539 		return B_BAD_INDEX;
540 
541 	uint32 portRegister = XHCI_PORTSC(index);
542 	uint32 portStatus = ReadOpReg(portRegister);
543 	portStatus &= ~PS_CLEAR;
544 
545 	switch (feature) {
546 	case PORT_SUSPEND:
547 		portStatus = ReadOpReg(portRegister);
548 		if (portStatus & PS_PR)
549 			return B_BAD_VALUE;
550 		if (portStatus & PS_XDEV_U3) {
551 			if ((portStatus & PS_PED) == 0)
552 				return B_BAD_VALUE;
553 			portStatus &= ~PS_CLEAR;
554 			portStatus &= ~PS_PLS_MASK;
555 			WriteOpReg(portRegister, portStatus | PS_XDEV_U0 | PS_LWS);
556 		}
557 		return B_OK;
558 	case PORT_ENABLE:
559 		WriteOpReg(portRegister, portStatus | PS_PED);
560 		return B_OK;
561 	case PORT_POWER:
562 		WriteOpReg(portRegister, portStatus & ~PS_PP);
563 		return B_OK;
564 	case C_PORT_CONNECTION:
565 		WriteOpReg(portRegister, portStatus | PS_CSC);
566 		return B_OK;
567 	case C_PORT_ENABLE:
568 		WriteOpReg(portRegister, portStatus | PS_PEC);
569 		return B_OK;
570 	case C_PORT_OVER_CURRENT:
571 		WriteOpReg(portRegister, portStatus | PS_OCC);
572 		return B_OK;
573 	case C_PORT_RESET:
574 		WriteOpReg(portRegister, portStatus | PS_PRC);
575 		return B_OK;
576 	}
577 
578 	return B_BAD_VALUE;
579 }
580 
581 
582 status_t
583 XHCI::ControllerHalt()
584 {
585 	WriteOpReg(XHCI_CMD, 0);
586 
587 	int32 tries = 100;
588 	while ((ReadOpReg(XHCI_STS) & STS_HCH) == 0) {
589 		snooze(1000);
590 		if (tries-- < 0)
591 			return B_ERROR;
592 	}
593 
594 	return B_OK;
595 }
596 
597 
598 status_t
599 XHCI::ControllerReset()
600 {
601 	WriteOpReg(XHCI_CMD, CMD_HCRST);
602 
603 	int32 tries = 100;
604 	while (ReadOpReg(XHCI_CMD) & CMD_HCRST) {
605 		snooze(1000);
606 		if (tries-- < 0)
607 			return B_ERROR;
608 	}
609 
610 	tries = 100;
611 	while (ReadOpReg(XHCI_STS) & STS_CNR) {
612 		snooze(1000);
613 		if (tries-- < 0)
614 			return B_ERROR;
615 	}
616 
617 	return B_OK;
618 }
619 
620 
621 int32
622 XHCI::InterruptHandler(void *data)
623 {
624 	return ((XHCI *)data)->Interrupt();
625 }
626 
627 
628 int32
629 XHCI::Interrupt()
630 {
631 	acquire_spinlock(&fSpinlock);
632 
633 	uint32 status = ReadOpReg(XHCI_STS);
634 	uint32 temp = ReadRunReg32(XHCI_IMAN(0));
635 	WriteOpReg(XHCI_STS, status);
636 	WriteRunReg32(XHCI_IMAN(0), temp);
637 	TRACE("STS: %lx IRQ_PENDING: %lx\n", status, temp);
638 
639 	int32 result = B_HANDLED_INTERRUPT;
640 
641 	if (status & STS_HSE) {
642 		TRACE_ERROR("Host System Error\n");
643 		return result;
644 	}
645 	if (status & STS_HCE) {
646 		TRACE_ERROR("Host Controller Error\n");
647 		return result;
648 	}
649 	uint16 i = fEventIdx;
650 	uint8 j = fEventCcs;
651 	uint8 t = 2;
652 
653 	while (1) {
654 		temp = fEventRing[i].dwtrb3;
655 		uint8 k = (temp & TRB_3_CYCLE_BIT) ? 1 : 0;
656 		if (j != k)
657 			break;
658 
659 		uint8 event = TRB_TYPE_GET(temp);
660 
661 		TRACE("event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n", i, event,
662 			fEventRing[i].qwtrb0, fEventRing[i].dwtrb2, fEventRing[i].dwtrb3);
663 		switch (event) {
664 		case TRB_COMPLETION:
665 			HandleCmdComplete(&fEventRing[i]);
666 			result = B_INVOKE_SCHEDULER;
667 			break;
668 		default:
669 			TRACE_ERROR("Unhandled event = %u\n", event);
670 			break;
671 		}
672 
673 		i++;
674 		if (i == MAX_EVENTS) {
675 			i = 0;
676 			j ^= 1;
677 			if (!--t)
678 				break;
679 		}
680 	}
681 
682 	fEventIdx = i;
683 	fEventCcs = j;
684 
685 	uint64 addr = fErst->rs_addr + i * sizeof(xhci_trb);
686 	addr |= ERST_EHB;
687 	WriteRunReg32(XHCI_ERDP_LO(0), (uint32)addr);
688 	WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(addr >> 32));
689 
690 
691 	release_spinlock(&fSpinlock);
692 
693 	return result;
694 }
695 
696 
697 void
698 XHCI::Ring()
699 {
700 	TRACE("Ding Dong!\n")
701 	WriteDoorReg32(XHCI_DOORBELL(0), 0);
702 	/* Flush PCI posted writes */
703 	ReadDoorReg32(XHCI_DOORBELL(0));
704 }
705 
706 
707 void
708 XHCI::QueueCommand(xhci_trb *trb)
709 {
710 	uint8 i, j;
711 	uint32 temp;
712 
713 	i = fCmdIdx;
714 	j = fCmdCcs;
715 
716 	TRACE("command[%u] = %lx (0x%016llx, 0x%08lx, 0x%08lx)\n",
717 		i, TRB_TYPE_GET(trb->dwtrb3),
718 		trb->qwtrb0, trb->dwtrb2, trb->dwtrb3);
719 
720 	fCmdRing[i].qwtrb0 = trb->qwtrb0;
721 	fCmdRing[i].dwtrb2 = trb->dwtrb2;
722 	temp = trb->dwtrb3;
723 
724 	if (j)
725 		temp |= TRB_3_CYCLE_BIT;
726 	else
727 		temp &= ~TRB_3_CYCLE_BIT;
728 	temp &= ~TRB_3_TC_BIT;
729 	fCmdRing[i].dwtrb3 = temp;
730 
731 	fCmdAddr = fErst->rs_addr + (MAX_EVENTS + i) * sizeof(xhci_trb);
732 
733 	i++;
734 
735 	if (i == (MAX_COMMANDS - 1)) {
736 		if (j)
737 			temp = TRB_3_CYCLE_BIT | TRB_TYPE(TRB_LINK);
738 		else
739 			temp = TRB_TYPE(TRB_LINK);
740 		fCmdRing[i].dwtrb3 = temp;
741 
742 		i = 0;
743 		j ^= 1;
744 	}
745 
746 	fCmdIdx = i;
747 	fCmdCcs = j;
748 }
749 
750 
751 void
752 XHCI::HandleCmdComplete(xhci_trb *trb)
753 {
754 	if (fCmdAddr == trb->qwtrb0) {
755 		TRACE("Received command event\n");
756 		fCmdResult[0] = trb->dwtrb2;
757 		fCmdResult[1] = trb->dwtrb3;
758 		release_sem_etc(fCmdCompSem, 1, B_DO_NOT_RESCHEDULE);
759 	}
760 
761 }
762 
763 
764 void
765 XHCI::QueueNoop()
766 {
767 	xhci_trb trb;
768 	uint32 temp;
769 
770 	trb.qwtrb0 = 0;
771 	trb.dwtrb2 = 0;
772 	temp = TRB_TYPE(TRB_TR_NOOP);
773 	trb.dwtrb3 = temp;
774 	cpu_status state = disable_interrupts();
775 	acquire_spinlock(&fSpinlock);
776 	QueueCommand(&trb);
777 	Ring();
778 	release_spinlock(&fSpinlock);
779 	restore_interrupts(state);
780 }
781 
782 
783 int32
784 XHCI::CmdCompThread(void *data)
785 {
786 	((XHCI *)data)->CmdComplete();
787 	return B_OK;
788 }
789 
790 
791 void
792 XHCI::CmdComplete()
793 {
794 	while (!fStopThreads) {
795 		if (acquire_sem(fCmdCompSem) < B_OK)
796 			continue;
797 
798 		// eat up sems that have been released by multiple interrupts
799 		int32 semCount = 0;
800 		get_sem_count(fCmdCompSem, &semCount);
801 		if (semCount > 0)
802 			acquire_sem_etc(fCmdCompSem, semCount, B_RELATIVE_TIMEOUT, 0);
803 
804 		TRACE("Command Complete\n");
805 		if (COMP_CODE_GET(fCmdResult[0]) != COMP_SUCCESS) {
806 			TRACE_ERROR("unsuccessful no-op command\n");
807 			//continue;
808 		}
809 		snooze(1000000 * 5);
810 		QueueNoop();
811 	}
812 }
813 
814 
815 int32
816 XHCI::FinishThread(void *data)
817 {
818 	((XHCI *)data)->FinishTransfers();
819 	return B_OK;
820 }
821 
822 
823 void
824 XHCI::FinishTransfers()
825 {
826 	while (!fStopThreads) {
827 		if (acquire_sem(fFinishTransfersSem) < B_OK)
828 			continue;
829 
830 		// eat up sems that have been released by multiple interrupts
831 		int32 semCount = 0;
832 		get_sem_count(fFinishTransfersSem, &semCount);
833 		if (semCount > 0)
834 			acquire_sem_etc(fFinishTransfersSem, semCount, B_RELATIVE_TIMEOUT, 0);
835 
836 		TRACE("finishing transfers\n");
837 	}
838 }
839 
840 inline void
841 XHCI::WriteOpReg(uint32 reg, uint32 value)
842 {
843 	*(volatile uint32 *)(fOperationalRegisters + reg) = value;
844 }
845 
846 
847 inline uint32
848 XHCI::ReadOpReg(uint32 reg)
849 {
850 	return *(volatile uint32 *)(fOperationalRegisters + reg);
851 }
852 
853 
854 inline uint8
855 XHCI::ReadCapReg8(uint32 reg)
856 {
857 	return *(volatile uint8 *)(fCapabilityRegisters + reg);
858 }
859 
860 
861 inline uint16
862 XHCI::ReadCapReg16(uint32 reg)
863 {
864 	return *(volatile uint16 *)(fCapabilityRegisters + reg);
865 }
866 
867 
868 inline uint32
869 XHCI::ReadCapReg32(uint32 reg)
870 {
871 	return *(volatile uint32 *)(fCapabilityRegisters + reg);
872 }
873 
874 
875 inline void
876 XHCI::WriteCapReg32(uint32 reg, uint32 value)
877 {
878 	*(volatile uint32 *)(fCapabilityRegisters + reg) = value;
879 }
880 
881 
882 inline uint32
883 XHCI::ReadRunReg32(uint32 reg)
884 {
885 	return *(volatile uint32 *)(fRuntimeRegisters + reg);
886 }
887 
888 
889 inline void
890 XHCI::WriteRunReg32(uint32 reg, uint32 value)
891 {
892 	*(volatile uint32 *)(fRuntimeRegisters + reg) = value;
893 }
894 
895 
896 inline uint32
897 XHCI::ReadDoorReg32(uint32 reg)
898 {
899 	return *(volatile uint32 *)(fDoorbellRegisters + reg);
900 }
901 
902 
903 inline void
904 XHCI::WriteDoorReg32(uint32 reg, uint32 value)
905 {
906 	*(volatile uint32 *)(fDoorbellRegisters + reg) = value;
907 }
908