xref: /haiku/src/libs/compat/freebsd_network/bus.cpp (revision 4b7e219688450694efc9d1890f83f816758c16d3)
1 /*
2  * Copyright 2007, Hugo Santos. All Rights Reserved.
3  * Copyright 2004, Marcus Overhagen. All Rights Reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 extern "C" {
9 #include "device.h"
10 }
11 
12 #include <stdlib.h>
13 
14 #include <algorithm>
15 
16 #include <arch/cpu.h>
17 
18 extern "C" {
19 #include <compat/dev/pci/pcireg.h>
20 #include <compat/dev/pci/pcivar.h>
21 #include <compat/machine/resource.h>
22 #include <compat/sys/mutex.h>
23 #include <compat/machine/bus.h>
24 #include <compat/sys/rman.h>
25 #include <compat/sys/bus.h>
26 }
27 
28 // private kernel header to get B_NO_HANDLED_INFO
29 #include <int.h>
30 
31 #include <PCI_x86.h>
32 
33 
34 //#define DEBUG_BUS_SPACE_RW
35 #ifdef DEBUG_BUS_SPACE_RW
36 #	define TRACE_BUS_SPACE_RW(x) driver_printf x
37 #else
38 #	define TRACE_BUS_SPACE_RW(x)
39 #endif
40 
41 //#define DEBUG_PCI
42 #ifdef DEBUG_PCI
43 #	define TRACE_PCI(dev, format, args...) device_printf(dev, format , ##args)
44 #else
45 #	define TRACE_PCI(dev, format, args...) do { } while (0)
46 #endif
47 
48 
49 #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1))
50 
51 
52 struct internal_intr {
53 	device_t		dev;
54 	driver_filter_t	filter;
55 	driver_intr_t	*handler;
56 	void			*arg;
57 	int				irq;
58 	uint32			flags;
59 
60 	thread_id		thread;
61 	sem_id			sem;
62 	int32			handling;
63 };
64 
65 static int32 intr_wrapper(void *data);
66 
67 
68 static int
69 fls(int mask)
70 {
71 	int bit;
72 	if (mask == 0)
73 		return (0);
74 	for (bit = 1; mask != 1; bit++)
75 		mask = (unsigned int)mask >> 1;
76 	return (bit);
77 }
78 
79 
80 static area_id
81 map_mem(void **virtualAddr, phys_addr_t _phy, size_t size, uint32 protection,
82 	const char *name)
83 {
84 	uint32 offset = _phy & (B_PAGE_SIZE - 1);
85 	phys_addr_t physicalAddr = _phy - offset;
86 	area_id area;
87 
88 	size = ROUNDUP(size + offset, B_PAGE_SIZE);
89 	area = map_physical_memory(name, physicalAddr, size, B_ANY_KERNEL_ADDRESS,
90 		protection, virtualAddr);
91 	if (area < B_OK)
92 		return area;
93 
94 	*virtualAddr = (uint8 *)(*virtualAddr) + offset;
95 
96 	return area;
97 }
98 
99 
100 static int
101 bus_alloc_irq_resource(device_t dev, struct resource *res)
102 {
103 	uint8 irq = pci_read_config(dev, PCI_interrupt_line, 1);
104 	if (irq == 0 || irq == 0xff)
105 		return -1;
106 
107 	/* TODO: IRQ resources! */
108 	res->r_bustag = 0;
109 	res->r_bushandle = irq;
110 
111 	return 0;
112 }
113 
114 
115 static int
116 bus_alloc_mem_resource(device_t dev, struct resource *res, int regid)
117 {
118 	uint32 addr = pci_read_config(dev, regid, 4) & PCI_address_memory_32_mask;
119 	uint32 size = 128 * 1024; /* XXX */
120 	void *virtualAddr;
121 
122 	res->r_mapped_area = map_mem(&virtualAddr, addr, size, 0,
123 		"bus_alloc_resource(MEMORY)");
124 	if (res->r_mapped_area < B_OK)
125 		return -1;
126 
127 	res->r_bustag = I386_BUS_SPACE_MEM;
128 	res->r_bushandle = (bus_space_handle_t)virtualAddr;
129 	return 0;
130 }
131 
132 
133 static int
134 bus_alloc_ioport_resource(device_t dev, struct resource *res, int regid)
135 {
136 	res->r_bustag = I386_BUS_SPACE_IO;
137 	res->r_bushandle = pci_read_config(dev, regid, 4) & PCI_address_io_mask;
138 	return 0;
139 }
140 
141 
142 struct resource *
143 bus_alloc_resource(device_t dev, int type, int *rid, unsigned long start,
144 	unsigned long end, unsigned long count, uint32 flags)
145 {
146 	struct resource *res;
147 	int result = -1;
148 
149 	if (type != SYS_RES_IRQ && type != SYS_RES_MEMORY
150 		&& type != SYS_RES_IOPORT)
151 		return NULL;
152 
153 	device_printf(dev, "bus_alloc_resource(%i, [%i], 0x%lx, 0x%lx, 0x%lx,"
154 		"0x%" B_PRIx32 ")\n", type, *rid, start, end, count, flags);
155 
156 	// maybe a local array of resources is enough
157 	res = (struct resource *)malloc(sizeof(struct resource));
158 	if (res == NULL)
159 		return NULL;
160 
161 	if (type == SYS_RES_IRQ) {
162 		if (*rid == 0) {
163 			// pinned interrupt
164 			result = bus_alloc_irq_resource(dev, res);
165 		} else {
166 			// msi or msi-x interrupt at index *rid - 1
167 			pci_info *info;
168 			info = &((struct root_device_softc *)dev->root->softc)->pci_info;
169 			res->r_bustag = 1;
170 			res->r_bushandle = info->u.h0.interrupt_line + *rid - 1;
171 			result = 0;
172 		}
173 	} else if (type == SYS_RES_MEMORY)
174 		result = bus_alloc_mem_resource(dev, res, *rid);
175 	else if (type == SYS_RES_IOPORT)
176 		result = bus_alloc_ioport_resource(dev, res, *rid);
177 
178 	if (result < 0) {
179 		free(res);
180 		return NULL;
181 	}
182 
183 	res->r_type = type;
184 	return res;
185 }
186 
187 
188 int
189 bus_release_resource(device_t dev, int type, int rid, struct resource *res)
190 {
191 	if (res->r_type != type)
192 		panic("bus_release_resource: mismatch");
193 
194 	if (type == SYS_RES_MEMORY)
195 		delete_area(res->r_mapped_area);
196 
197 	free(res);
198 	return 0;
199 }
200 
201 
202 int
203 bus_alloc_resources(device_t dev, struct resource_spec *resourceSpec,
204 	struct resource **resources)
205 {
206 	int i;
207 
208 	for (i = 0; resourceSpec[i].type != -1; i++) {
209 		resources[i] = bus_alloc_resource_any(dev,
210 			resourceSpec[i].type, &resourceSpec[i].rid, resourceSpec[i].flags);
211 		if (resources[i] == NULL
212 			&& (resourceSpec[i].flags & RF_OPTIONAL) == 0) {
213 			for (++i; resourceSpec[i].type != -1; i++) {
214 				resources[i] = NULL;
215 			}
216 
217 			bus_release_resources(dev, resourceSpec, resources);
218 			return ENXIO;
219 		}
220 	}
221 	return 0;
222 }
223 
224 
225 void
226 bus_release_resources(device_t dev, const struct resource_spec *resourceSpec,
227 	struct resource **resources)
228 {
229 	int i;
230 
231 	for (i = 0; resourceSpec[i].type != -1; i++) {
232 		if (resources[i] == NULL)
233 			continue;
234 
235 		bus_release_resource(dev, resourceSpec[i].type, resourceSpec[i].rid,
236 			resources[i]);
237 		resources[i] = NULL;
238 	}
239 }
240 
241 
242 bus_space_handle_t
243 rman_get_bushandle(struct resource *res)
244 {
245 	return res->r_bushandle;
246 }
247 
248 
249 bus_space_tag_t
250 rman_get_bustag(struct resource *res)
251 {
252 	return res->r_bustag;
253 }
254 
255 
256 int
257 rman_get_rid(struct resource *res)
258 {
259 	return 0;
260 }
261 
262 
263 //	#pragma mark - Interrupt handling
264 
265 
266 static int32
267 intr_wrapper(void *data)
268 {
269 	struct internal_intr *intr = (struct internal_intr *)data;
270 
271 	//device_printf(intr->dev, "in interrupt handler.\n");
272 
273 	if (!HAIKU_CHECK_DISABLE_INTERRUPTS(intr->dev))
274 		return B_UNHANDLED_INTERRUPT;
275 
276 	release_sem_etc(intr->sem, 1, B_DO_NOT_RESCHEDULE);
277 	return intr->handling ? B_HANDLED_INTERRUPT : B_INVOKE_SCHEDULER;
278 }
279 
280 
281 static int32
282 intr_fast_wrapper(void *data)
283 {
284 	struct internal_intr *intr = (struct internal_intr *)data;
285 
286 	intr->handler(intr->arg);
287 
288 	// We don't know if the interrupt has been handled.
289 	return B_UNHANDLED_INTERRUPT;
290 }
291 
292 
293 static int32
294 intr_handler(void *data)
295 {
296 	struct internal_intr *intr = (struct internal_intr *)data;
297 	status_t status;
298 
299 	while (1) {
300 		status = acquire_sem(intr->sem);
301 		if (status < B_OK)
302 			break;
303 
304 		//device_printf(intr->dev, "in soft interrupt handler.\n");
305 
306 		atomic_or(&intr->handling, 1);
307 		intr->handler(intr->arg);
308 		atomic_and(&intr->handling, 0);
309 		HAIKU_REENABLE_INTERRUPTS(intr->dev);
310 	}
311 
312 	return 0;
313 }
314 
315 
316 static void
317 free_internal_intr(struct internal_intr *intr)
318 {
319 	if (intr->sem >= B_OK) {
320 		status_t status;
321 		delete_sem(intr->sem);
322 		wait_for_thread(intr->thread, &status);
323 	}
324 
325 	free(intr);
326 }
327 
328 
329 int
330 bus_setup_intr(device_t dev, struct resource *res, int flags,
331 	driver_filter_t filter, driver_intr_t handler, void *arg, void **_cookie)
332 {
333 	/* TODO check MPSAFE etc */
334 
335 	struct internal_intr *intr = (struct internal_intr *)malloc(
336 		sizeof(struct internal_intr));
337 	char semName[64];
338 	status_t status;
339 
340 	if (intr == NULL)
341 		return B_NO_MEMORY;
342 
343 	intr->dev = dev;
344 	intr->filter = filter;
345 	intr->handler = handler;
346 	intr->arg = arg;
347 	intr->irq = res->r_bushandle;
348 	intr->flags = flags;
349 	intr->sem = -1;
350 	intr->thread = -1;
351 
352 	if (filter != NULL) {
353 		status = install_io_interrupt_handler(intr->irq,
354 			(interrupt_handler)intr->filter, intr->arg, 0);
355 	} else if ((flags & INTR_FAST) != 0) {
356 		status = install_io_interrupt_handler(intr->irq,
357 			intr_fast_wrapper, intr, B_NO_HANDLED_INFO);
358 	} else {
359 		snprintf(semName, sizeof(semName), "%s intr", dev->device_name);
360 
361 		intr->sem = create_sem(0, semName);
362 		if (intr->sem < B_OK) {
363 			free(intr);
364 			return B_NO_MEMORY;
365 		}
366 
367 		snprintf(semName, sizeof(semName), "%s intr handler", dev->device_name);
368 
369 		intr->thread = spawn_kernel_thread(intr_handler, semName,
370 			B_REAL_TIME_DISPLAY_PRIORITY, intr);
371 		if (intr->thread < B_OK) {
372 			delete_sem(intr->sem);
373 			free(intr);
374 			return B_NO_MEMORY;
375 		}
376 
377 		status = install_io_interrupt_handler(intr->irq,
378 			intr_wrapper, intr, B_NO_HANDLED_INFO);
379 	}
380 
381 	if (status == B_OK && res->r_bustag == 1 && gPCIx86 != NULL) {
382 		// this is an msi, enable it
383 		pci_info *info
384 			= &((struct root_device_softc *)dev->root->softc)->pci_info;
385 		if (((struct root_device_softc *)dev->root->softc)->is_msi) {
386 			if (gPCIx86->enable_msi(info->bus, info->device,
387 					info->function) != B_OK) {
388 				device_printf(dev, "enabling msi failed\n");
389 				bus_teardown_intr(dev, res, intr);
390 				return ENODEV;
391 			}
392 		} else if (((struct root_device_softc *)dev->root->softc)->is_msix) {
393 			if (gPCIx86->enable_msix(info->bus, info->device,
394 					info->function) != B_OK) {
395 				device_printf(dev, "enabling msix failed\n");
396 				bus_teardown_intr(dev, res, intr);
397 				return ENODEV;
398 			}
399 		}
400 	}
401 
402 	if (status < B_OK) {
403 		free_internal_intr(intr);
404 		return status;
405 	}
406 
407 	resume_thread(intr->thread);
408 
409 	*_cookie = intr;
410 	return 0;
411 }
412 
413 
414 int
415 bus_teardown_intr(device_t dev, struct resource *res, void *arg)
416 {
417 	struct internal_intr *intr = (struct internal_intr *)arg;
418 	struct root_device_softc *root = (struct root_device_softc *)dev->root->softc;
419 
420 	if ((root->is_msi || root->is_msix) && gPCIx86 != NULL) {
421 		// disable msi generation
422 		pci_info *info = &root->pci_info;
423 		gPCIx86->disable_msi(info->bus, info->device, info->function);
424 	}
425 
426 	if (intr->filter != NULL) {
427 		remove_io_interrupt_handler(intr->irq, (interrupt_handler)intr->filter,
428 			intr->arg);
429 	} else if (intr->flags & INTR_FAST) {
430 		remove_io_interrupt_handler(intr->irq, intr_fast_wrapper, intr);
431 	} else {
432 		remove_io_interrupt_handler(intr->irq, intr_wrapper, intr);
433 	}
434 
435 	free_internal_intr(intr);
436 	return 0;
437 }
438 
439 
440 //	#pragma mark - bus functions
441 
442 
443 bus_dma_tag_t
444 bus_get_dma_tag(device_t dev)
445 {
446 	return NULL;
447 }
448 
449 
450 int
451 bus_generic_suspend(device_t dev)
452 {
453 	UNIMPLEMENTED();
454 	return B_ERROR;
455 }
456 
457 
458 int
459 bus_generic_resume(device_t dev)
460 {
461 	UNIMPLEMENTED();
462 	return B_ERROR;
463 }
464 
465 
466 void
467 bus_generic_shutdown(device_t dev)
468 {
469 	UNIMPLEMENTED();
470 }
471 
472 
473 int
474 bus_print_child_header(device_t dev, device_t child)
475 {
476 	UNIMPLEMENTED();
477 	return B_ERROR;
478 }
479 
480 
481 int
482 bus_print_child_footer(device_t dev, device_t child)
483 {
484 	UNIMPLEMENTED();
485 	return B_ERROR;
486 }
487 
488 
489 int
490 bus_generic_print_child(device_t dev, device_t child)
491 {
492 	UNIMPLEMENTED();
493 	return B_ERROR;
494 }
495 
496 
497 void
498 bus_generic_driver_added(device_t dev, driver_t *driver)
499 {
500 	UNIMPLEMENTED();
501 }
502 
503 
504 #define BUS_SPACE_READ(size, type, fun) \
505 	type bus_space_read_##size(bus_space_tag_t tag, \
506 		bus_space_handle_t handle, bus_size_t offset) \
507 	{ \
508 		type value; \
509 		if (tag == I386_BUS_SPACE_IO) \
510 			value = fun(handle + offset); \
511 		else \
512 			value = *(volatile type *)(handle + offset); \
513 		if (tag == I386_BUS_SPACE_IO) \
514 			TRACE_BUS_SPACE_RW(("bus_space_read_%s(0x%lx, 0x%lx, 0x%lx) = 0x%lx\n", \
515 				#size, (uint32)tag, (uint32)handle, (uint32)offset, (uint32)value)); \
516 		return value; \
517 	}
518 
519 #define BUS_SPACE_WRITE(size, type, fun) \
520 	void bus_space_write_##size(bus_space_tag_t tag, \
521 		bus_space_handle_t handle, bus_size_t offset, type value) \
522 	{ \
523 		if (tag == I386_BUS_SPACE_IO) \
524 			TRACE_BUS_SPACE_RW(("bus_space_write_%s(0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", \
525 				#size, (uint32)tag, (uint32)handle, (uint32)offset, (uint32)value)); \
526 		if (tag == I386_BUS_SPACE_IO) \
527 			fun(value, handle + offset); \
528 		else \
529 			*(volatile type *)(handle + offset) = value; \
530 	}
531 
532 BUS_SPACE_READ(1, uint8_t, in8)
533 BUS_SPACE_READ(2, uint16_t, in16)
534 BUS_SPACE_READ(4, uint32_t, in32)
535 
536 BUS_SPACE_WRITE(1, uint8_t, out8)
537 BUS_SPACE_WRITE(2, uint16_t, out16)
538 BUS_SPACE_WRITE(4, uint32_t, out32)
539 
540 int
541 bus_child_present(device_t child)
542 {
543 	device_t parent = device_get_parent(child);
544 	if (parent == NULL)
545 		return 0;
546 
547 	return bus_child_present(parent);
548 }
549 
550 
551 //	#pragma mark - PCI functions
552 
553 
554 uint32_t
555 pci_read_config(device_t dev, int offset, int size)
556 {
557 	pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info;
558 
559 	uint32_t value = gPci->read_pci_config(info->bus, info->device,
560 		info->function, offset, size);
561 	TRACE_PCI(dev, "pci_read_config(%i, %i) = 0x%x\n", offset, size, value);
562 	return value;
563 }
564 
565 
566 void
567 pci_write_config(device_t dev, int offset, uint32_t value, int size)
568 {
569 	pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info;
570 
571 	TRACE_PCI(dev, "pci_write_config(%i, 0x%x, %i)\n", offset, value, size);
572 
573 	gPci->write_pci_config(info->bus, info->device, info->function, offset,
574 		size, value);
575 }
576 
577 
578 uint16_t
579 pci_get_vendor(device_t dev)
580 {
581 	return pci_read_config(dev, PCI_vendor_id, 2);
582 }
583 
584 
585 uint16_t
586 pci_get_device(device_t dev)
587 {
588 	return pci_read_config(dev, PCI_device_id, 2);
589 }
590 
591 
592 uint16_t
593 pci_get_subvendor(device_t dev)
594 {
595 	return pci_read_config(dev, PCI_subsystem_vendor_id, 2);
596 }
597 
598 
599 uint16_t
600 pci_get_subdevice(device_t dev)
601 {
602 	return pci_read_config(dev, PCI_subsystem_id, 2);
603 }
604 
605 
606 uint8_t
607 pci_get_revid(device_t dev)
608 {
609 	return pci_read_config(dev, PCI_revision, 1);
610 }
611 
612 
613 uint32_t
614 pci_get_domain(device_t dev)
615 {
616 	return 0;
617 }
618 
619 uint32_t
620 pci_get_devid(device_t dev)
621 {
622 	return pci_read_config(dev, PCI_device_id, 2) << 16 |
623 		pci_read_config(dev, PCI_vendor_id, 2);
624 }
625 
626 uint8_t
627 pci_get_cachelnsz(device_t dev)
628 {
629 	return pci_read_config(dev, PCI_line_size, 1);
630 }
631 
632 uint8_t *
633 pci_get_ether(device_t dev)
634 {
635 	/* used in if_dc to get the MAC from CardBus CIS for Xircom card */
636 	return NULL; /* NULL is handled in the caller correctly */
637 }
638 
639 uint8_t
640 pci_get_bus(device_t dev)
641 {
642 	pci_info *info
643 		= &((struct root_device_softc *)dev->root->softc)->pci_info;
644 	return info->bus;
645 }
646 
647 
648 uint8_t
649 pci_get_slot(device_t dev)
650 {
651 	pci_info *info
652 		= &((struct root_device_softc *)dev->root->softc)->pci_info;
653 	return info->device;
654 }
655 
656 
657 uint8_t
658 pci_get_function(device_t dev)
659 {
660 	pci_info *info
661 		= &((struct root_device_softc *)dev->root->softc)->pci_info;
662 	return info->function;
663 }
664 
665 
666 device_t
667 pci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func)
668 {
669 	// We don't support that yet - if we want to support the multi port
670 	// feature of the Broadcom BCM 570x driver, we would have to change
671 	// that.
672 	return NULL;
673 }
674 
675 
676 static void
677 pci_set_command_bit(device_t dev, uint16_t bit)
678 {
679 	uint16_t command = pci_read_config(dev, PCI_command, 2);
680 	pci_write_config(dev, PCI_command, command | bit, 2);
681 }
682 
683 
684 int
685 pci_enable_busmaster(device_t dev)
686 {
687 	pci_set_command_bit(dev, PCI_command_master);
688 	return 0;
689 }
690 
691 
692 int
693 pci_enable_io(device_t dev, int space)
694 {
695 	/* adapted from FreeBSD's pci_enable_io_method */
696 	int bit = 0;
697 
698 	switch (space) {
699 		case SYS_RES_IOPORT:
700 			bit = PCI_command_io;
701 			break;
702 		case SYS_RES_MEMORY:
703 			bit = PCI_command_memory;
704 			break;
705 		default:
706 			return EINVAL;
707 	}
708 
709 	pci_set_command_bit(dev, bit);
710 	if (pci_read_config(dev, PCI_command, 2) & bit)
711 		return 0;
712 
713 	device_printf(dev, "pci_enable_io(%d) failed.\n", space);
714 
715 	return ENXIO;
716 }
717 
718 
719 int
720 pci_find_cap(device_t dev, int capability, int *capreg)
721 {
722 	return pci_find_extcap(dev, capability, capreg);
723 }
724 
725 
726 int
727 pci_find_extcap(device_t child, int capability, int *_capabilityRegister)
728 {
729 	uint8 capabilityPointer;
730 	uint8 headerType;
731 	uint16 status;
732 
733 	status = pci_read_config(child, PCIR_STATUS, 2);
734 	if ((status & PCIM_STATUS_CAPPRESENT) == 0)
735 		return ENXIO;
736 
737 	headerType = pci_read_config(child, PCI_header_type, 1);
738 	switch (headerType & PCIM_HDRTYPE) {
739 		case 0:
740 		case 1:
741 			capabilityPointer = PCIR_CAP_PTR;
742 			break;
743 		case 2:
744 			capabilityPointer = PCIR_CAP_PTR_2;
745 			break;
746 		default:
747 			return ENXIO;
748 	}
749 	capabilityPointer = pci_read_config(child, capabilityPointer, 1);
750 
751 	while (capabilityPointer != 0) {
752 		if (pci_read_config(child, capabilityPointer + PCICAP_ID, 1)
753 				== capability) {
754 			if (_capabilityRegister != NULL)
755 				*_capabilityRegister = capabilityPointer;
756 			return 0;
757 		}
758 		capabilityPointer = pci_read_config(child,
759 			capabilityPointer + PCICAP_NEXTPTR, 1);
760 	}
761 
762 	return ENOENT;
763 }
764 
765 
766 int
767 pci_msi_count(device_t dev)
768 {
769 	pci_info *info;
770 	if (gPCIx86 == NULL)
771 		return 0;
772 
773 	info = &((struct root_device_softc *)dev->root->softc)->pci_info;
774 	return gPCIx86->get_msi_count(info->bus, info->device, info->function);
775 }
776 
777 
778 int
779 pci_alloc_msi(device_t dev, int *count)
780 {
781 	pci_info *info;
782 	uint8 startVector = 0;
783 	if (gPCIx86 == NULL)
784 		return ENODEV;
785 
786 	info = &((struct root_device_softc *)dev->root->softc)->pci_info;
787 
788 	if (gPCIx86->configure_msi(info->bus, info->device, info->function, *count,
789 			&startVector) != B_OK) {
790 		return ENODEV;
791 	}
792 
793 	((struct root_device_softc *)dev->root->softc)->is_msi = true;
794 	info->u.h0.interrupt_line = startVector;
795 	return EOK;
796 }
797 
798 
799 int
800 pci_release_msi(device_t dev)
801 {
802 	pci_info *info;
803 	if (gPCIx86 == NULL)
804 		return ENODEV;
805 
806 	info = &((struct root_device_softc *)dev->root->softc)->pci_info;
807 	gPCIx86->unconfigure_msi(info->bus, info->device, info->function);
808 	((struct root_device_softc *)dev->root->softc)->is_msi = false;
809 	((struct root_device_softc *)dev->root->softc)->is_msix = false;
810 	return EOK;
811 }
812 
813 
814 int
815 pci_msix_count(device_t dev)
816 {
817 	pci_info *info;
818 	if (gPCIx86 == NULL)
819 		return 0;
820 
821 	info = &((struct root_device_softc *)dev->root->softc)->pci_info;
822 	return gPCIx86->get_msix_count(info->bus, info->device, info->function);
823 }
824 
825 
826 int
827 pci_alloc_msix(device_t dev, int *count)
828 {
829 	pci_info *info;
830 	uint8 startVector = 0;
831 	if (gPCIx86 == NULL)
832 		return ENODEV;
833 
834 	info = &((struct root_device_softc *)dev->root->softc)->pci_info;
835 
836 	if (gPCIx86->configure_msix(info->bus, info->device, info->function, *count,
837 			&startVector) != B_OK) {
838 		return ENODEV;
839 	}
840 
841 	((struct root_device_softc *)dev->root->softc)->is_msix = true;
842 	info->u.h0.interrupt_line = startVector;
843 	return EOK;
844 }
845 
846 
847 int
848 pci_get_max_read_req(device_t dev)
849 {
850 	int cap;
851 	uint16_t val;
852 
853 	if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0)
854 		return (0);
855 	val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2);
856 	val &= PCIM_EXP_CTL_MAX_READ_REQUEST;
857 	val >>= 12;
858 	return (1 << (val + 7));
859 }
860 
861 
862 int
863 pci_set_max_read_req(device_t dev, int size)
864 {
865 	int cap;
866 	uint16_t val;
867 
868 	if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0)
869 		return (0);
870 	if (size < 128)
871 		size = 128;
872 	if (size > 4096)
873 		size = 4096;
874 	size = (1 << (fls(size) - 1));
875 	val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2);
876 	val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST;
877 	val |= (fls(size) - 8) << 12;
878 	pci_write_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, val, 2);
879 	return (size);
880 }
881 
882 
883 int
884 pci_get_powerstate(device_t dev)
885 {
886 	int capabilityRegister;
887 	uint16 status;
888 	int powerState = PCI_POWERSTATE_D0;
889 
890 	if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK)
891 		return powerState;
892 
893 	status = pci_read_config(dev, capabilityRegister + PCIR_POWER_STATUS, 2);
894 	switch (status & PCI_pm_mask) {
895 		case PCI_pm_state_d0:
896 			break;
897 		case PCI_pm_state_d1:
898 			powerState = PCI_POWERSTATE_D1;
899 			break;
900 		case PCI_pm_state_d2:
901 			powerState = PCI_POWERSTATE_D2;
902 			break;
903 		case PCI_pm_state_d3:
904 			powerState = PCI_POWERSTATE_D3;
905 			break;
906 		default:
907 			powerState = PCI_POWERSTATE_UNKNOWN;
908 			break;
909 	}
910 
911 	TRACE_PCI(dev, "%s: D%i\n", __func__, powerState);
912 	return powerState;
913 }
914 
915 
916 int
917 pci_set_powerstate(device_t dev, int newPowerState)
918 {
919 	int capabilityRegister;
920 	int oldPowerState;
921 	uint8 currentPowerManagementStatus;
922 	uint8 newPowerManagementStatus;
923 	uint16 powerManagementCapabilities;
924 	bigtime_t stateTransitionDelayInUs = 0;
925 
926 	if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK)
927 		return EOPNOTSUPP;
928 
929 	oldPowerState = pci_get_powerstate(dev);
930 	if (oldPowerState == newPowerState)
931 		return EOK;
932 
933 	switch (std::max(oldPowerState, newPowerState)) {
934 		case PCI_POWERSTATE_D2:
935 			stateTransitionDelayInUs = 200;
936 			break;
937 		case PCI_POWERSTATE_D3:
938 			stateTransitionDelayInUs = 10000;
939 			break;
940 	}
941 
942 	currentPowerManagementStatus = pci_read_config(dev, capabilityRegister
943 		+ PCIR_POWER_STATUS, 2);
944 	newPowerManagementStatus = currentPowerManagementStatus & ~PCI_pm_mask;
945 	powerManagementCapabilities = pci_read_config(dev, capabilityRegister
946 		+ PCIR_POWER_CAP, 2);
947 
948 	switch (newPowerState) {
949 		case PCI_POWERSTATE_D0:
950 			newPowerManagementStatus |= PCIM_PSTAT_D0;
951 			break;
952 		case PCI_POWERSTATE_D1:
953 			if ((powerManagementCapabilities & PCI_pm_d1supp) == 0)
954 				return EOPNOTSUPP;
955 			newPowerManagementStatus |= PCIM_PSTAT_D1;
956 			break;
957 		case PCI_POWERSTATE_D2:
958 			if ((powerManagementCapabilities & PCI_pm_d2supp) == 0)
959 				return EOPNOTSUPP;
960 			newPowerManagementStatus |= PCIM_PSTAT_D2;
961 			break;
962 		case PCI_POWERSTATE_D3:
963 			newPowerManagementStatus |= PCIM_PSTAT_D3;
964 			break;
965 		default:
966 			return EINVAL;
967 	}
968 
969 	TRACE_PCI(dev, "%s: D%i -> D%i\n", __func__, oldPowerState, newPowerState);
970 	pci_write_config(dev, capabilityRegister + PCIR_POWER_STATUS, newPowerState,
971 		2);
972 	if (stateTransitionDelayInUs != 0)
973 		snooze(stateTransitionDelayInUs);
974 
975 	return EOK;
976 }
977