xref: /haiku/src/add-ons/kernel/busses/usb/xhci.cpp (revision 1c09002cbee8e797a0f8bbfc5678dfadd39ee1a7)
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