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