xref: /freebsd/sys/i386/pci/pci_cfgreg.c (revision 8655c70597b0e0918c82114b1186df5669b83eb6)
1 /*-
2  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3  * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4  * Copyright (c) 2000, BSDi
5  * Copyright (c) 2004, Scott Long <scottl@freebsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice unmodified, this list of conditions, and the following
13  *    disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include "opt_xbox.h"
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/lock.h>
39 #include <sys/kernel.h>
40 #include <sys/mutex.h>
41 #include <sys/malloc.h>
42 #include <sys/queue.h>
43 #include <dev/pci/pcivar.h>
44 #include <dev/pci/pcireg.h>
45 #include <machine/pci_cfgreg.h>
46 #include <machine/pc/bios.h>
47 
48 #include <vm/vm.h>
49 #include <vm/vm_param.h>
50 #include <vm/vm_kern.h>
51 #include <vm/vm_extern.h>
52 #include <vm/pmap.h>
53 #include <machine/pmap.h>
54 
55 #ifdef XBOX
56 #include <machine/xbox.h>
57 #endif
58 
59 #define PRVERB(a) do {							\
60 	if (bootverbose)						\
61 		printf a ;						\
62 } while(0)
63 
64 #define PCIE_CACHE 8
65 struct pcie_cfg_elem {
66 	TAILQ_ENTRY(pcie_cfg_elem)	elem;
67 	vm_offset_t	vapage;
68 	vm_paddr_t	papage;
69 };
70 
71 enum {
72 	CFGMECH_NONE = 0,
73 	CFGMECH_1,
74 	CFGMECH_2,
75 	CFGMECH_PCIE,
76 };
77 
78 static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
79 static uint64_t pcie_base;
80 static int pcie_minbus, pcie_maxbus;
81 static uint32_t pcie_badslots;
82 static int cfgmech;
83 static int devmax;
84 static struct mtx pcicfg_mtx;
85 static int mcfg_enable = 1;
86 TUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
87 
88 static uint32_t	pci_docfgregread(int bus, int slot, int func, int reg,
89 		    int bytes);
90 static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
91 static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
92 #ifndef XEN
93 static int	pcireg_cfgopen(void);
94 #endif
95 static int	pciereg_cfgread(int bus, unsigned slot, unsigned func,
96 		    unsigned reg, unsigned bytes);
97 static void	pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
98 		    unsigned reg, int data, unsigned bytes);
99 
100 /*
101  * Some BIOS writers seem to want to ignore the spec and put
102  * 0 in the intline rather than 255 to indicate none.  Some use
103  * numbers in the range 128-254 to indicate something strange and
104  * apparently undocumented anywhere.  Assume these are completely bogus
105  * and map them to 255, which means "none".
106  */
107 static __inline int
108 pci_i386_map_intline(int line)
109 {
110 	if (line == 0 || line >= 128)
111 		return (PCI_INVALID_IRQ);
112 	return (line);
113 }
114 
115 #ifndef XEN
116 static u_int16_t
117 pcibios_get_version(void)
118 {
119 	struct bios_regs args;
120 
121 	if (PCIbios.ventry == 0) {
122 		PRVERB(("pcibios: No call entry point\n"));
123 		return (0);
124 	}
125 	args.eax = PCIBIOS_BIOS_PRESENT;
126 	if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
127 		PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
128 		return (0);
129 	}
130 	if (args.edx != 0x20494350) {
131 		PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
132 		return (0);
133 	}
134 	return (args.ebx & 0xffff);
135 }
136 #endif
137 
138 /*
139  * Initialise access to PCI configuration space
140  */
141 int
142 pci_cfgregopen(void)
143 {
144 #ifdef XEN
145 	return (0);
146 #else
147 	static int		opened = 0;
148 	uint64_t		pciebar;
149 	u_int16_t		vid, did;
150 	u_int16_t		v;
151 
152 	if (opened)
153 		return (1);
154 
155 	if (cfgmech == CFGMECH_NONE && pcireg_cfgopen() == 0)
156 		return (0);
157 
158 	v = pcibios_get_version();
159 	if (v > 0)
160 		PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
161 		    v & 0xff));
162 	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
163 	opened = 1;
164 
165 	/* $PIR requires PCI BIOS 2.10 or greater. */
166 	if (v >= 0x0210)
167 		pci_pir_open();
168 
169 	if (cfgmech == CFGMECH_PCIE)
170 		return (1);
171 
172 	/*
173 	 * Grope around in the PCI config space to see if this is a
174 	 * chipset that is capable of doing memory-mapped config cycles.
175 	 * This also implies that it can do PCIe extended config cycles.
176 	 */
177 
178 	/* Check for supported chipsets */
179 	vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
180 	did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
181 	switch (vid) {
182 	case 0x8086:
183 		switch (did) {
184 		case 0x3590:
185 		case 0x3592:
186 			/* Intel 7520 or 7320 */
187 			pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
188 			pcie_cfgregopen(pciebar, 0, 255);
189 			break;
190 		case 0x2580:
191 		case 0x2584:
192 		case 0x2590:
193 			/* Intel 915, 925, or 915GM */
194 			pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
195 			pcie_cfgregopen(pciebar, 0, 255);
196 			break;
197 		}
198 	}
199 
200 	return(1);
201 #endif
202 }
203 
204 static uint32_t
205 pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
206 {
207 
208 	if (cfgmech == CFGMECH_PCIE &&
209 	    (bus != 0 || !(1 << slot & pcie_badslots)))
210 		return (pciereg_cfgread(bus, slot, func, reg, bytes));
211 	else
212 		return (pcireg_cfgread(bus, slot, func, reg, bytes));
213 }
214 
215 /*
216  * Read configuration space register
217  */
218 u_int32_t
219 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
220 {
221 	uint32_t line;
222 
223 	/*
224 	 * Some BIOS writers seem to want to ignore the spec and put
225 	 * 0 in the intline rather than 255 to indicate none.  The rest of
226 	 * the code uses 255 as an invalid IRQ.
227 	 */
228 	if (reg == PCIR_INTLINE && bytes == 1) {
229 		line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
230 		return (pci_i386_map_intline(line));
231 	}
232 	return (pci_docfgregread(bus, slot, func, reg, bytes));
233 }
234 
235 /*
236  * Write configuration space register
237  */
238 void
239 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
240 {
241 
242 	if (cfgmech == CFGMECH_PCIE &&
243 	    (bus != 0 || !(1 << slot & pcie_badslots)))
244 		pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
245 	else
246 		pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
247 }
248 
249 /*
250  * Configuration space access using direct register operations
251  */
252 
253 /* enable configuration space accesses and return data port address */
254 static int
255 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
256 {
257 	int dataport = 0;
258 
259 #ifdef XBOX
260 	if (arch_i386_is_xbox) {
261 		/*
262 		 * The Xbox MCPX chipset is a derivative of the nForce 1
263 		 * chipset. It almost has the same bus layout; some devices
264 		 * cannot be used, because they have been removed.
265 		 */
266 
267 		/*
268 		 * Devices 00:00.1 and 00:00.2 used to be memory controllers on
269 		 * the nForce chipset, but on the Xbox, using them will lockup
270 		 * the chipset.
271 		 */
272 		if (bus == 0 && slot == 0 && (func == 1 || func == 2))
273 			return dataport;
274 
275 		/*
276 		 * Bus 1 only contains a VGA controller at 01:00.0. When you try
277 		 * to probe beyond that device, you only get garbage, which
278 		 * could cause lockups.
279 		 */
280 		if (bus == 1 && (slot != 0 || func != 0))
281 			return dataport;
282 
283 		/*
284 		 * Bus 2 used to contain the AGP controller, but the Xbox MCPX
285 		 * doesn't have one. Probing it can cause lockups.
286 		 */
287 		if (bus >= 2)
288 			return dataport;
289 	}
290 #endif
291 
292 	if (bus <= PCI_BUSMAX
293 	    && slot < devmax
294 	    && func <= PCI_FUNCMAX
295 	    && reg <= PCI_REGMAX
296 	    && bytes != 3
297 	    && (unsigned) bytes <= 4
298 	    && (reg & (bytes - 1)) == 0) {
299 		switch (cfgmech) {
300 		case CFGMECH_PCIE:
301 		case CFGMECH_1:
302 			outl(CONF1_ADDR_PORT, (1 << 31)
303 			    | (bus << 16) | (slot << 11)
304 			    | (func << 8) | (reg & ~0x03));
305 			dataport = CONF1_DATA_PORT + (reg & 0x03);
306 			break;
307 		case CFGMECH_2:
308 			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
309 			outb(CONF2_FORWARD_PORT, bus);
310 			dataport = 0xc000 | (slot << 8) | reg;
311 			break;
312 		}
313 	}
314 	return (dataport);
315 }
316 
317 /* disable configuration space accesses */
318 static void
319 pci_cfgdisable(void)
320 {
321 	switch (cfgmech) {
322 	case CFGMECH_PCIE:
323 	case CFGMECH_1:
324 		/*
325 		 * Do nothing for the config mechanism 1 case.
326 		 * Writing a 0 to the address port can apparently
327 		 * confuse some bridges and cause spurious
328 		 * access failures.
329 		 */
330 		break;
331 	case CFGMECH_2:
332 		outb(CONF2_ENABLE_PORT, 0);
333 		break;
334 	}
335 }
336 
337 static int
338 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
339 {
340 	int data = -1;
341 	int port;
342 
343 	mtx_lock_spin(&pcicfg_mtx);
344 	port = pci_cfgenable(bus, slot, func, reg, bytes);
345 	if (port != 0) {
346 		switch (bytes) {
347 		case 1:
348 			data = inb(port);
349 			break;
350 		case 2:
351 			data = inw(port);
352 			break;
353 		case 4:
354 			data = inl(port);
355 			break;
356 		}
357 		pci_cfgdisable();
358 	}
359 	mtx_unlock_spin(&pcicfg_mtx);
360 	return (data);
361 }
362 
363 static void
364 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
365 {
366 	int port;
367 
368 	mtx_lock_spin(&pcicfg_mtx);
369 	port = pci_cfgenable(bus, slot, func, reg, bytes);
370 	if (port != 0) {
371 		switch (bytes) {
372 		case 1:
373 			outb(port, data);
374 			break;
375 		case 2:
376 			outw(port, data);
377 			break;
378 		case 4:
379 			outl(port, data);
380 			break;
381 		}
382 		pci_cfgdisable();
383 	}
384 	mtx_unlock_spin(&pcicfg_mtx);
385 }
386 
387 #ifndef XEN
388 /* check whether the configuration mechanism has been correctly identified */
389 static int
390 pci_cfgcheck(int maxdev)
391 {
392 	uint32_t id, class;
393 	uint8_t header;
394 	uint8_t device;
395 	int port;
396 
397 	if (bootverbose)
398 		printf("pci_cfgcheck:\tdevice ");
399 
400 	for (device = 0; device < maxdev; device++) {
401 		if (bootverbose)
402 			printf("%d ", device);
403 
404 		port = pci_cfgenable(0, device, 0, 0, 4);
405 		id = inl(port);
406 		if (id == 0 || id == 0xffffffff)
407 			continue;
408 
409 		port = pci_cfgenable(0, device, 0, 8, 4);
410 		class = inl(port) >> 8;
411 		if (bootverbose)
412 			printf("[class=%06x] ", class);
413 		if (class == 0 || (class & 0xf870ff) != 0)
414 			continue;
415 
416 		port = pci_cfgenable(0, device, 0, 14, 1);
417 		header = inb(port);
418 		if (bootverbose)
419 			printf("[hdr=%02x] ", header);
420 		if ((header & 0x7e) != 0)
421 			continue;
422 
423 		if (bootverbose)
424 			printf("is there (id=%08x)\n", id);
425 
426 		pci_cfgdisable();
427 		return (1);
428 	}
429 	if (bootverbose)
430 		printf("-- nothing found\n");
431 
432 	pci_cfgdisable();
433 	return (0);
434 }
435 
436 static int
437 pcireg_cfgopen(void)
438 {
439 	uint32_t mode1res, oldval1;
440 	uint8_t mode2res, oldval2;
441 
442 	/* Check for type #1 first. */
443 	oldval1 = inl(CONF1_ADDR_PORT);
444 
445 	if (bootverbose) {
446 		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
447 		    oldval1);
448 	}
449 
450 	cfgmech = CFGMECH_1;
451 	devmax = 32;
452 
453 	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
454 	DELAY(1);
455 	mode1res = inl(CONF1_ADDR_PORT);
456 	outl(CONF1_ADDR_PORT, oldval1);
457 
458 	if (bootverbose)
459 		printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",  mode1res,
460 		    CONF1_ENABLE_CHK);
461 
462 	if (mode1res) {
463 		if (pci_cfgcheck(32))
464 			return (cfgmech);
465 	}
466 
467 	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
468 	mode1res = inl(CONF1_ADDR_PORT);
469 	outl(CONF1_ADDR_PORT, oldval1);
470 
471 	if (bootverbose)
472 		printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",  mode1res,
473 		    CONF1_ENABLE_CHK1);
474 
475 	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
476 		if (pci_cfgcheck(32))
477 			return (cfgmech);
478 	}
479 
480 	/* Type #1 didn't work, so try type #2. */
481 	oldval2 = inb(CONF2_ENABLE_PORT);
482 
483 	if (bootverbose) {
484 		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
485 		    oldval2);
486 	}
487 
488 	if ((oldval2 & 0xf0) == 0) {
489 
490 		cfgmech = CFGMECH_2;
491 		devmax = 16;
492 
493 		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
494 		mode2res = inb(CONF2_ENABLE_PORT);
495 		outb(CONF2_ENABLE_PORT, oldval2);
496 
497 		if (bootverbose)
498 			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
499 			    mode2res, CONF2_ENABLE_CHK);
500 
501 		if (mode2res == CONF2_ENABLE_RES) {
502 			if (bootverbose)
503 				printf("pci_open(2a):\tnow trying mechanism 2\n");
504 
505 			if (pci_cfgcheck(16))
506 				return (cfgmech);
507 		}
508 	}
509 
510 	/* Nothing worked, so punt. */
511 	cfgmech = CFGMECH_NONE;
512 	devmax = 0;
513 	return (cfgmech);
514 }
515 
516 int
517 pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
518 {
519 	struct pcie_cfg_list *pcielist;
520 	struct pcie_cfg_elem *pcie_array, *elem;
521 #ifdef SMP
522 	struct pcpu *pc;
523 #endif
524 	vm_offset_t va;
525 	uint32_t val1, val2;
526 	int i, slot;
527 
528 	if (!mcfg_enable)
529 		return (0);
530 
531 	if (minbus != 0)
532 		return (0);
533 
534 #ifndef PAE
535 	if (base >= 0x100000000) {
536 		if (bootverbose)
537 			printf(
538 	    "PCI: Memory Mapped PCI configuration area base 0x%jx too high\n",
539 			    (uintmax_t)base);
540 		return (0);
541 	}
542 #endif
543 
544 	if (bootverbose)
545 		printf("PCIe: Memory Mapped configuration base @ 0x%jx\n",
546 		    (uintmax_t)base);
547 
548 #ifdef SMP
549 	SLIST_FOREACH(pc, &cpuhead, pc_allcpu)
550 #endif
551 	{
552 
553 		pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE,
554 		    M_DEVBUF, M_NOWAIT);
555 		if (pcie_array == NULL)
556 			return (0);
557 
558 		va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE);
559 		if (va == 0) {
560 			free(pcie_array, M_DEVBUF);
561 			return (0);
562 		}
563 
564 #ifdef SMP
565 		pcielist = &pcie_list[pc->pc_cpuid];
566 #else
567 		pcielist = &pcie_list[0];
568 #endif
569 		TAILQ_INIT(pcielist);
570 		for (i = 0; i < PCIE_CACHE; i++) {
571 			elem = &pcie_array[i];
572 			elem->vapage = va + (i * PAGE_SIZE);
573 			elem->papage = 0;
574 			TAILQ_INSERT_HEAD(pcielist, elem, elem);
575 		}
576 	}
577 
578 	pcie_base = base;
579 	pcie_minbus = minbus;
580 	pcie_maxbus = maxbus;
581 	cfgmech = CFGMECH_PCIE;
582 	devmax = 32;
583 
584 	/*
585 	 * On some AMD systems, some of the devices on bus 0 are
586 	 * inaccessible using memory-mapped PCI config access.  Walk
587 	 * bus 0 looking for such devices.  For these devices, we will
588 	 * fall back to using type 1 config access instead.
589 	 */
590 	if (pci_cfgregopen() != 0) {
591 		for (slot = 0; slot < 32; slot++) {
592 			val1 = pcireg_cfgread(0, slot, 0, 0, 4);
593 			if (val1 == 0xffffffff)
594 				continue;
595 
596 			val2 = pciereg_cfgread(0, slot, 0, 0, 4);
597 			if (val2 != val1)
598 				pcie_badslots |= (1 << slot);
599 		}
600 	}
601 
602 	return (1);
603 }
604 #endif /* !XEN */
605 
606 #define PCIE_PADDR(bar, reg, bus, slot, func)	\
607 	((bar)				|	\
608 	(((bus) & 0xff) << 20)		|	\
609 	(((slot) & 0x1f) << 15)		|	\
610 	(((func) & 0x7) << 12)		|	\
611 	((reg) & 0xfff))
612 
613 /*
614  * Find an element in the cache that matches the physical page desired, or
615  * create a new mapping from the least recently used element.
616  * A very simple LRU algorithm is used here, does it need to be more
617  * efficient?
618  */
619 static __inline struct pcie_cfg_elem *
620 pciereg_findelem(vm_paddr_t papage)
621 {
622 	struct pcie_cfg_list *pcielist;
623 	struct pcie_cfg_elem *elem;
624 
625 	pcielist = &pcie_list[PCPU_GET(cpuid)];
626 	TAILQ_FOREACH(elem, pcielist, elem) {
627 		if (elem->papage == papage)
628 			break;
629 	}
630 
631 	if (elem == NULL) {
632 		elem = TAILQ_LAST(pcielist, pcie_cfg_list);
633 		if (elem->papage != 0) {
634 			pmap_kremove(elem->vapage);
635 			invlpg(elem->vapage);
636 		}
637 		pmap_kenter(elem->vapage, papage);
638 		elem->papage = papage;
639 	}
640 
641 	if (elem != TAILQ_FIRST(pcielist)) {
642 		TAILQ_REMOVE(pcielist, elem, elem);
643 		TAILQ_INSERT_HEAD(pcielist, elem, elem);
644 	}
645 	return (elem);
646 }
647 
648 static int
649 pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
650     unsigned bytes)
651 {
652 	struct pcie_cfg_elem *elem;
653 	volatile vm_offset_t va;
654 	vm_paddr_t pa, papage;
655 	int data = -1;
656 
657 	if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
658 	    func > PCI_FUNCMAX || reg >= 0x1000 || bytes > 4 || bytes == 3)
659 		return (-1);
660 
661 	critical_enter();
662 	pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
663 	papage = pa & ~PAGE_MASK;
664 	elem = pciereg_findelem(papage);
665 	va = elem->vapage | (pa & PAGE_MASK);
666 
667 	switch (bytes) {
668 	case 4:
669 		data = *(volatile uint32_t *)(va);
670 		break;
671 	case 2:
672 		data = *(volatile uint16_t *)(va);
673 		break;
674 	case 1:
675 		data = *(volatile uint8_t *)(va);
676 		break;
677 	}
678 
679 	critical_exit();
680 	return (data);
681 }
682 
683 static void
684 pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
685     unsigned bytes)
686 {
687 	struct pcie_cfg_elem *elem;
688 	volatile vm_offset_t va;
689 	vm_paddr_t pa, papage;
690 
691 	if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
692 	    func > PCI_FUNCMAX || reg >= 0x1000)
693 		return;
694 
695 	critical_enter();
696 	pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
697 	papage = pa & ~PAGE_MASK;
698 	elem = pciereg_findelem(papage);
699 	va = elem->vapage | (pa & PAGE_MASK);
700 
701 	switch (bytes) {
702 	case 4:
703 		*(volatile uint32_t *)(va) = data;
704 		break;
705 	case 2:
706 		*(volatile uint16_t *)(va) = data;
707 		break;
708 	case 1:
709 		*(volatile uint8_t *)(va) = data;
710 		break;
711 	}
712 
713 	critical_exit();
714 }
715