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