pci_cfgreg.c revision 165126
1139790Simp/*-
226159Sse * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
366529Smsmith * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
466529Smsmith * Copyright (c) 2000, BSDi
5138468Sscottl * Copyright (c) 2004, Scott Long <scottl@freebsd.org>
626159Sse * All rights reserved.
726159Sse *
826159Sse * Redistribution and use in source and binary forms, with or without
926159Sse * modification, are permitted provided that the following conditions
1026159Sse * are met:
1126159Sse * 1. Redistributions of source code must retain the above copyright
1226159Sse *    notice unmodified, this list of conditions, and the following
1326159Sse *    disclaimer.
1426159Sse * 2. Redistributions in binary form must reproduce the above copyright
1526159Sse *    notice, this list of conditions and the following disclaimer in the
1626159Sse *    documentation and/or other materials provided with the distribution.
1726159Sse *
1826159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1926159Sse * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2026159Sse * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2126159Sse * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2226159Sse * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2326159Sse * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2426159Sse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2526159Sse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2626159Sse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2726159Sse * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2826159Sse */
296104Sse
30115706Sobrien#include <sys/cdefs.h>
31115706Sobrien__FBSDID("$FreeBSD: head/sys/i386/pci/pci_cfgreg.c 165126 2006-12-12 19:23:52Z jhb $");
32115706Sobrien
33152219Simp#include "opt_xbox.h"
34152219Simp
35125981Sjhb#include <sys/param.h>
366734Sbde#include <sys/systm.h>
3747307Speter#include <sys/bus.h>
38111068Speter#include <sys/lock.h>
39111068Speter#include <sys/mutex.h>
40138429Sscottl#include <sys/malloc.h>
41138429Sscottl#include <sys/queue.h>
42100435Simp#include <dev/pci/pcivar.h>
43100435Simp#include <dev/pci/pcireg.h>
4466529Smsmith#include <machine/pci_cfgreg.h>
4559294Smsmith#include <machine/pc/bios.h>
4659294Smsmith
47138429Sscottl#include <vm/vm.h>
48138429Sscottl#include <vm/vm_param.h>
49138429Sscottl#include <vm/vm_kern.h>
50138429Sscottl#include <vm/vm_extern.h>
51138429Sscottl#include <vm/pmap.h>
52138429Sscottl#include <machine/pmap.h>
53138429Sscottl
54152219Simp#ifdef XBOX
55152219Simp#include <machine/xbox.h>
56152219Simp#endif
57152219Simp
58103868Sjhb#define PRVERB(a) do {							\
59103868Sjhb	if (bootverbose)						\
60103868Sjhb		printf a ;						\
61103868Sjhb} while(0)
6282441Simp
63138429Sscottl#define PCIE_CACHE 8
64138429Sscottlstruct pcie_cfg_elem {
65138429Sscottl	TAILQ_ENTRY(pcie_cfg_elem)	elem;
66138429Sscottl	vm_offset_t	vapage;
67138429Sscottl	vm_paddr_t	papage;
68138429Sscottl};
69138429Sscottl
70138429Sscottlenum {
71138429Sscottl	CFGMECH_NONE = 0,
72138429Sscottl	CFGMECH_1,
73138429Sscottl	CFGMECH_2,
74138429Sscottl	CFGMECH_PCIE,
75138429Sscottl};
76138429Sscottl
77138429Sscottlstatic TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
78138429Sscottlstatic uint32_t pciebar;
7926159Ssestatic int cfgmech;
8026159Ssestatic int devmax;
81138429Sscottlstatic struct mtx pcicfg_mtx;
826104Sse
8366529Smsmithstatic int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
8466529Smsmithstatic void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
8559294Smsmithstatic int	pcireg_cfgopen(void);
8659294Smsmith
87138429Sscottlstatic int	pciereg_cfgopen(void);
88138429Sscottlstatic int	pciereg_cfgread(int bus, int slot, int func, int reg,
89138429Sscottl				int bytes);
90138429Sscottlstatic void	pciereg_cfgwrite(int bus, int slot, int func, int reg,
91138429Sscottl				 int data, int bytes);
92111068Speter
9397694Simp/*
9497694Simp * Some BIOS writers seem to want to ignore the spec and put
9597694Simp * 0 in the intline rather than 255 to indicate none.  Some use
9697694Simp * numbers in the range 128-254 to indicate something strange and
9797694Simp * apparently undocumented anywhere.  Assume these are completely bogus
9897694Simp * and map them to 255, which means "none".
9997694Simp */
100131575Sstefanfstatic __inline int
10197694Simppci_i386_map_intline(int line)
10297694Simp{
103100435Simp	if (line == 0 || line >= 128)
104100435Simp		return (PCI_INVALID_IRQ);
105100435Simp	return (line);
10697694Simp}
10797694Simp
10882441Simpstatic u_int16_t
10982441Simppcibios_get_version(void)
11082441Simp{
111100435Simp	struct bios_regs args;
11282441Simp
113102976Sjhb	if (PCIbios.ventry == 0) {
114100435Simp		PRVERB(("pcibios: No call entry point\n"));
115100435Simp		return (0);
116100435Simp	}
117100435Simp	args.eax = PCIBIOS_BIOS_PRESENT;
118100435Simp	if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
119100435Simp		PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
120100435Simp		return (0);
121100435Simp	}
122100435Simp	if (args.edx != 0x20494350) {
123100435Simp		PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
124100435Simp		return (0);
125100435Simp	}
126100435Simp	return (args.ebx & 0xffff);
12782441Simp}
12882441Simp
12966529Smsmith/*
13066529Smsmith * Initialise access to PCI configuration space
13166529Smsmith */
13266529Smsmithint
13366529Smsmithpci_cfgregopen(void)
13459294Smsmith{
135100435Simp	static int		opened = 0;
136138429Sscottl	u_int16_t		vid, did;
137111068Speter	u_int16_t		v;
13865176Sdfr
139100435Simp	if (opened)
140100435Simp		return(1);
14166529Smsmith
142111068Speter	if (pcireg_cfgopen() == 0)
143100435Simp		return(0);
14467185Simp
145111068Speter	v = pcibios_get_version();
146111068Speter	if (v > 0)
147131398Sjhb		PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
148131398Sjhb		    v & 0xff));
149111068Speter	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
150100435Simp	opened = 1;
151125981Sjhb
152125981Sjhb	/* $PIR requires PCI BIOS 2.10 or greater. */
153125981Sjhb	if (v >= 0x0210)
154125981Sjhb		pci_pir_open();
155138429Sscottl
156138429Sscottl	/*
157138429Sscottl	 * Grope around in the PCI config space to see if this is a
158138429Sscottl	 * chipset that is capable of doing memory-mapped config cycles.
159138429Sscottl	 * This also implies that it can do PCIe extended config cycles.
160138429Sscottl	 */
161138429Sscottl
162153243Srodrigc	/* Check for supported chipsets */
163165126Sjhb	vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
164165126Sjhb	did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
165153243Srodrigc	if (vid == 0x8086) {
166153243Srodrigc		if (did == 0x3590 || did == 0x3592) {
167153243Srodrigc			/* Intel 7520 or 7320 */
168153243Srodrigc			pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
169153243Srodrigc			pciereg_cfgopen();
170153243Srodrigc		} else if (did == 0x2580 || did == 0x2584) {
171153243Srodrigc			/* Intel 915 or 925 */
172153243Srodrigc			pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
173153243Srodrigc			pciereg_cfgopen();
174153243Srodrigc		}
175138429Sscottl	}
176138429Sscottl
177100435Simp	return(1);
17859294Smsmith}
17959294Smsmith
18066529Smsmith/*
18169783Smsmith * Read configuration space register
18266529Smsmith */
18369783Smsmithu_int32_t
18469783Smsmithpci_cfgregread(int bus, int slot, int func, int reg, int bytes)
18569783Smsmith{
186100435Simp	uint32_t line;
18769783Smsmith
188100435Simp	/*
189100435Simp	 * Some BIOS writers seem to want to ignore the spec and put
190100435Simp	 * 0 in the intline rather than 255 to indicate none.  The rest of
191100435Simp	 * the code uses 255 as an invalid IRQ.
192100435Simp	 */
193100435Simp	if (reg == PCIR_INTLINE && bytes == 1) {
194111068Speter		line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
195121986Sjhb		return (pci_i386_map_intline(line));
196100435Simp	}
197121986Sjhb	return (pcireg_cfgread(bus, slot, func, reg, bytes));
19869783Smsmith}
19969783Smsmith
20066529Smsmith/*
20166529Smsmith * Write configuration space register
20266529Smsmith */
20366529Smsmithvoid
20466529Smsmithpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
20559294Smsmith{
206111068Speter
207104598Simp	pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
20859294Smsmith}
20959294Smsmith
21066529Smsmith/*
21166529Smsmith * Configuration space access using direct register operations
21266529Smsmith */
21359294Smsmith
21426159Sse/* enable configuration space accesses and return data port address */
21510887Ssestatic int
21626159Ssepci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
21726159Sse{
218100435Simp	int dataport = 0;
21910887Sse
220152219Simp#ifdef XBOX
221152219Simp	if (arch_i386_is_xbox) {
222152219Simp		/*
223152219Simp		 * The Xbox MCPX chipset is a derivative of the nForce 1
224152219Simp		 * chipset. It almost has the same bus layout; some devices
225152219Simp		 * cannot be used, because they have been removed.
226152219Simp		 */
227152219Simp
228152219Simp		/*
229152219Simp		 * Devices 00:00.1 and 00:00.2 used to be memory controllers on
230152219Simp		 * the nForce chipset, but on the Xbox, using them will lockup
231152219Simp		 * the chipset.
232152219Simp		 */
233152219Simp		if (bus == 0 && slot == 0 && (func == 1 || func == 2))
234152219Simp			return dataport;
235152219Simp
236152219Simp		/*
237152219Simp		 * Bus 1 only contains a VGA controller at 01:00.0. When you try
238152219Simp		 * to probe beyond that device, you only get garbage, which
239152219Simp		 * could cause lockups.
240152219Simp		 */
241152219Simp		if (bus == 1 && (slot != 0 || func != 0))
242152219Simp			return dataport;
243152219Simp
244152219Simp		/*
245152219Simp		 * Bus 2 used to contain the AGP controller, but the Xbox MCPX
246152219Simp		 * doesn't have one. Probing it can cause lockups.
247152219Simp		 */
248152219Simp		if (bus >= 2)
249152219Simp			return dataport;
250152219Simp	}
251152219Simp#endif
252152219Simp
253100435Simp	if (bus <= PCI_BUSMAX
254100435Simp	    && slot < devmax
255100435Simp	    && func <= PCI_FUNCMAX
256100435Simp	    && reg <= PCI_REGMAX
257100435Simp	    && bytes != 3
258100435Simp	    && (unsigned) bytes <= 4
259106901Simp	    && (reg & (bytes - 1)) == 0) {
260100435Simp		switch (cfgmech) {
261138429Sscottl		case CFGMECH_1:
262100435Simp			outl(CONF1_ADDR_PORT, (1 << 31)
263100435Simp			    | (bus << 16) | (slot << 11)
264100435Simp			    | (func << 8) | (reg & ~0x03));
265100435Simp			dataport = CONF1_DATA_PORT + (reg & 0x03);
266100435Simp			break;
267138429Sscottl		case CFGMECH_2:
268100435Simp			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
269100435Simp			outb(CONF2_FORWARD_PORT, bus);
270100435Simp			dataport = 0xc000 | (slot << 8) | reg;
271100435Simp			break;
272100435Simp		}
27326159Sse	}
274100435Simp	return (dataport);
27526159Sse}
2766104Sse
27726159Sse/* disable configuration space accesses */
2786104Ssestatic void
27926159Ssepci_cfgdisable(void)
28026159Sse{
281100435Simp	switch (cfgmech) {
282138429Sscottl	case CFGMECH_1:
283151644Swpaul		outl(CONF1_ADDR_PORT, 0);
284152075Speter		break;
285138429Sscottl	case CFGMECH_2:
286100435Simp		outb(CONF2_ENABLE_PORT, 0);
287151644Swpaul		outb(CONF2_FORWARD_PORT, 0);
288100435Simp		break;
289100435Simp	}
29026159Sse}
2916104Sse
29259294Smsmithstatic int
29365176Sdfrpcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
29426159Sse{
295100435Simp	int data = -1;
296100435Simp	int port;
2977234Sse
298138429Sscottl	if (cfgmech == CFGMECH_PCIE) {
299138429Sscottl		data = pciereg_cfgread(bus, slot, func, reg, bytes);
300138429Sscottl		return (data);
301138429Sscottl	}
302138429Sscottl
303111068Speter	mtx_lock_spin(&pcicfg_mtx);
304100435Simp	port = pci_cfgenable(bus, slot, func, reg, bytes);
305100435Simp	if (port != 0) {
306100435Simp		switch (bytes) {
307100435Simp		case 1:
308100435Simp			data = inb(port);
309100435Simp			break;
310100435Simp		case 2:
311100435Simp			data = inw(port);
312100435Simp			break;
313100435Simp		case 4:
314100435Simp			data = inl(port);
315100435Simp			break;
316100435Simp		}
317100435Simp		pci_cfgdisable();
31826159Sse	}
319111068Speter	mtx_unlock_spin(&pcicfg_mtx);
320100435Simp	return (data);
32126159Sse}
3227234Sse
32359294Smsmithstatic void
32465176Sdfrpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
32526159Sse{
326100435Simp	int port;
3276104Sse
328138429Sscottl	if (cfgmech == CFGMECH_PCIE) {
329138429Sscottl		pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
330138429Sscottl		return;
331138429Sscottl	}
332138429Sscottl
333111068Speter	mtx_lock_spin(&pcicfg_mtx);
334100435Simp	port = pci_cfgenable(bus, slot, func, reg, bytes);
335100435Simp	if (port != 0) {
336100435Simp		switch (bytes) {
337100435Simp		case 1:
338100435Simp			outb(port, data);
339100435Simp			break;
340100435Simp		case 2:
341100435Simp			outw(port, data);
342100435Simp			break;
343100435Simp		case 4:
344100435Simp			outl(port, data);
345100435Simp			break;
346100435Simp		}
347100435Simp		pci_cfgdisable();
34826159Sse	}
349111068Speter	mtx_unlock_spin(&pcicfg_mtx);
35026159Sse}
3516104Sse
35266529Smsmith/* check whether the configuration mechanism has been correctly identified */
35310887Ssestatic int
35426159Ssepci_cfgcheck(int maxdev)
35510887Sse{
356106357Simp	uint32_t id, class;
357106357Simp	uint8_t header;
358106357Simp	uint8_t device;
359111068Speter	int port;
36010735Sse
36126159Sse	if (bootverbose)
362100435Simp		printf("pci_cfgcheck:\tdevice ");
36310960Sse
364100435Simp	for (device = 0; device < maxdev; device++) {
365100435Simp		if (bootverbose)
366100435Simp			printf("%d ", device);
36726159Sse
368111068Speter		port = pci_cfgenable(0, device, 0, 0, 4);
369111068Speter		id = inl(port);
370106357Simp		if (id == 0 || id == 0xffffffff)
371100435Simp			continue;
37223415Sse
373111068Speter		port = pci_cfgenable(0, device, 0, 8, 4);
374111068Speter		class = inl(port) >> 8;
375100435Simp		if (bootverbose)
376100435Simp			printf("[class=%06x] ", class);
377100435Simp		if (class == 0 || (class & 0xf870ff) != 0)
378100435Simp			continue;
379100435Simp
380111068Speter		port = pci_cfgenable(0, device, 0, 14, 1);
381111068Speter		header = inb(port);
382106357Simp		if (bootverbose)
383100435Simp			printf("[hdr=%02x] ", header);
384100435Simp		if ((header & 0x7e) != 0)
385100435Simp			continue;
386100435Simp
387100435Simp		if (bootverbose)
388100435Simp			printf("is there (id=%08x)\n", id);
389100435Simp
390100435Simp		pci_cfgdisable();
391100435Simp		return (1);
392100435Simp	}
39366529Smsmith	if (bootverbose)
394100435Simp		printf("-- nothing found\n");
39523415Sse
39666529Smsmith	pci_cfgdisable();
397100435Simp	return (0);
39810887Sse}
39910887Sse
40047307Speterstatic int
40159294Smsmithpcireg_cfgopen(void)
4026104Sse{
403106357Simp	uint32_t mode1res, oldval1;
404106357Simp	uint8_t mode2res, oldval2;
4056104Sse
406100435Simp	oldval1 = inl(CONF1_ADDR_PORT);
40710960Sse
408100435Simp	if (bootverbose) {
409106357Simp		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
410100435Simp		    oldval1);
411100435Simp	}
41210960Sse
413100435Simp	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
41410960Sse
415138429Sscottl		cfgmech = CFGMECH_1;
416100435Simp		devmax = 32;
41710960Sse
418100435Simp		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
419124021Sjhb		DELAY(1);
420100435Simp		mode1res = inl(CONF1_ADDR_PORT);
421100435Simp		outl(CONF1_ADDR_PORT, oldval1);
42210960Sse
423100435Simp		if (bootverbose)
424106357Simp			printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
425100435Simp			    mode1res, CONF1_ENABLE_CHK);
4266104Sse
427100435Simp		if (mode1res) {
428100435Simp			if (pci_cfgcheck(32))
429100435Simp				return (cfgmech);
430100435Simp		}
43110807Sse
432100435Simp		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
433100435Simp		mode1res = inl(CONF1_ADDR_PORT);
434100435Simp		outl(CONF1_ADDR_PORT, oldval1);
43510887Sse
436100435Simp		if (bootverbose)
437106357Simp			printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
438100435Simp			    mode1res, CONF1_ENABLE_CHK1);
43910887Sse
440100435Simp		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
441100435Simp			if (pci_cfgcheck(32))
442100435Simp				return (cfgmech);
443100435Simp		}
44411524Sse	}
44511524Sse
446100435Simp	oldval2 = inb(CONF2_ENABLE_PORT);
44747307Speter
448100435Simp	if (bootverbose) {
449100435Simp		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
450100435Simp		    oldval2);
451100435Simp	}
45247307Speter
453100435Simp	if ((oldval2 & 0xf0) == 0) {
45448832Smsmith
455138429Sscottl		cfgmech = CFGMECH_2;
456100435Simp		devmax = 16;
45748832Smsmith
458100435Simp		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
459100435Simp		mode2res = inb(CONF2_ENABLE_PORT);
460100435Simp		outb(CONF2_ENABLE_PORT, oldval2);
46148832Smsmith
462100435Simp		if (bootverbose)
463100435Simp			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
464100435Simp			    mode2res, CONF2_ENABLE_CHK);
46552480Salc
466100435Simp		if (mode2res == CONF2_ENABLE_RES) {
467100435Simp			if (bootverbose)
468100435Simp				printf("pci_open(2a):\tnow trying mechanism 2\n");
46949404Speter
470100435Simp			if (pci_cfgcheck(16))
471100435Simp				return (cfgmech);
472100435Simp		}
47348832Smsmith	}
47448832Smsmith
475138429Sscottl	cfgmech = CFGMECH_NONE;
476100435Simp	devmax = 0;
477100435Simp	return (cfgmech);
47848832Smsmith}
47948832Smsmith
480138429Sscottlstatic int
481138429Sscottlpciereg_cfgopen(void)
482138429Sscottl{
483138429Sscottl	struct pcie_cfg_list *pcielist;
484138429Sscottl	struct pcie_cfg_elem *pcie_array, *elem;
485138429Sscottl#ifdef SMP
486138429Sscottl	struct pcpu *pc;
487138429Sscottl#endif
488138429Sscottl	vm_offset_t va;
489138429Sscottl	int i;
490138429Sscottl
491138429Sscottl	if (bootverbose)
492138429Sscottl		printf("Setting up PCIe mappings for BAR 0x%x\n", pciebar);
493138429Sscottl
494138429Sscottl#ifdef SMP
495138429Sscottl	SLIST_FOREACH(pc, &cpuhead, pc_allcpu)
496138429Sscottl#endif
497138429Sscottl	{
498138429Sscottl
499138429Sscottl		pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE,
500138429Sscottl		    M_DEVBUF, M_NOWAIT);
501138429Sscottl		if (pcie_array == NULL)
502138429Sscottl			return (0);
503138429Sscottl
504138429Sscottl		va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE);
505138429Sscottl		if (va == 0) {
506138429Sscottl			free(pcie_array, M_DEVBUF);
507138429Sscottl			return (0);
508138429Sscottl		}
509138429Sscottl
510138429Sscottl#ifdef SMP
511138429Sscottl		pcielist = &pcie_list[pc->pc_cpuid];
512138429Sscottl#else
513138429Sscottl		pcielist = &pcie_list[0];
514138429Sscottl#endif
515138429Sscottl		TAILQ_INIT(pcielist);
516138429Sscottl		for (i = 0; i < PCIE_CACHE; i++) {
517138429Sscottl			elem = &pcie_array[i];
518138429Sscottl			elem->vapage = va + (i * PAGE_SIZE);
519138429Sscottl			elem->papage = 0;
520138429Sscottl			TAILQ_INSERT_HEAD(pcielist, elem, elem);
521138429Sscottl		}
522138429Sscottl	}
523138429Sscottl
524138429Sscottl
525138429Sscottl	cfgmech = CFGMECH_PCIE;
526138429Sscottl	devmax = 32;
527138429Sscottl	return (1);
528138429Sscottl}
529138429Sscottl
530138429Sscottl#define PCIE_PADDR(bar, reg, bus, slot, func)	\
531138429Sscottl	((bar)				|	\
532138429Sscottl	(((bus) & 0xff) << 20)		|	\
533138429Sscottl	(((slot) & 0x1f) << 15)		|	\
534138429Sscottl	(((func) & 0x7) << 12)		|	\
535138429Sscottl	((reg) & 0xfff))
536138429Sscottl
537138429Sscottl/*
538138429Sscottl * Find an element in the cache that matches the physical page desired, or
539138429Sscottl * create a new mapping from the least recently used element.
540138429Sscottl * A very simple LRU algorithm is used here, does it need to be more
541138429Sscottl * efficient?
542138429Sscottl */
543138429Sscottlstatic __inline struct pcie_cfg_elem *
544138429Sscottlpciereg_findelem(vm_paddr_t papage)
545138429Sscottl{
546138429Sscottl	struct pcie_cfg_list *pcielist;
547138429Sscottl	struct pcie_cfg_elem *elem;
548138429Sscottl
549138429Sscottl	pcielist = &pcie_list[PCPU_GET(cpuid)];
550138429Sscottl	TAILQ_FOREACH(elem, pcielist, elem) {
551138429Sscottl		if (elem->papage == papage)
552138429Sscottl			break;
553138429Sscottl	}
554138429Sscottl
555138429Sscottl	if (elem == NULL) {
556138429Sscottl		elem = TAILQ_LAST(pcielist, pcie_cfg_list);
557138429Sscottl		if (elem->papage != 0) {
558138429Sscottl			pmap_kremove(elem->vapage);
559138429Sscottl			invlpg(elem->vapage);
560138429Sscottl		}
561138429Sscottl		pmap_kenter(elem->vapage, papage);
562138429Sscottl		elem->papage = papage;
563138429Sscottl	}
564138429Sscottl
565138429Sscottl	if (elem != TAILQ_FIRST(pcielist)) {
566138429Sscottl		TAILQ_REMOVE(pcielist, elem, elem);
567138429Sscottl		TAILQ_INSERT_HEAD(pcielist, elem, elem);
568138429Sscottl	}
569138429Sscottl	return (elem);
570138429Sscottl}
571138429Sscottl
572138429Sscottlstatic int
573138429Sscottlpciereg_cfgread(int bus, int slot, int func, int reg, int bytes)
574138429Sscottl{
575138429Sscottl	struct pcie_cfg_elem *elem;
576138429Sscottl	volatile vm_offset_t va;
577138429Sscottl	vm_paddr_t pa, papage;
578138662Sscottl	int data;
579138429Sscottl
580138662Sscottl	critical_enter();
581138429Sscottl	pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
582138429Sscottl	papage = pa & ~PAGE_MASK;
583138429Sscottl	elem = pciereg_findelem(papage);
584138429Sscottl	va = elem->vapage | (pa & PAGE_MASK);
585138429Sscottl
586138429Sscottl	switch (bytes) {
587138429Sscottl	case 4:
588138662Sscottl		data = *(volatile uint32_t *)(va);
589138662Sscottl		break;
590138429Sscottl	case 2:
591138662Sscottl		data = *(volatile uint16_t *)(va);
592138662Sscottl		break;
593138429Sscottl	case 1:
594138662Sscottl		data = *(volatile uint8_t *)(va);
595138662Sscottl		break;
596138429Sscottl	default:
597138429Sscottl		panic("pciereg_cfgread: invalid width");
598138429Sscottl	}
599138662Sscottl
600138662Sscottl	critical_exit();
601138662Sscottl	return (data);
602138429Sscottl}
603138429Sscottl
604138429Sscottlstatic void
605138429Sscottlpciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
606138429Sscottl{
607138429Sscottl	struct pcie_cfg_elem *elem;
608138429Sscottl	volatile vm_offset_t va;
609138429Sscottl	vm_paddr_t pa, papage;
610138429Sscottl
611138662Sscottl	critical_enter();
612138429Sscottl	pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
613138429Sscottl	papage = pa & ~PAGE_MASK;
614138429Sscottl	elem = pciereg_findelem(papage);
615138429Sscottl	va = elem->vapage | (pa & PAGE_MASK);
616138429Sscottl
617138429Sscottl	switch (bytes) {
618138429Sscottl	case 4:
619138429Sscottl		*(volatile uint32_t *)(va) = data;
620138429Sscottl		break;
621138429Sscottl	case 2:
622138429Sscottl		*(volatile uint16_t *)(va) = data;
623138429Sscottl		break;
624138429Sscottl	case 1:
625138429Sscottl		*(volatile uint8_t *)(va) = data;
626138429Sscottl		break;
627138429Sscottl	default:
628138429Sscottl		panic("pciereg_cfgwrite: invalid width");
629138429Sscottl	}
630138662Sscottl
631138662Sscottl	critical_exit();
632138429Sscottl}
633