pci_cfgreg.c revision 192342
1130803Smarcel/*-
2130803Smarcel * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3130803Smarcel * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4130803Smarcel * Copyright (c) 2000, BSDi
5130803Smarcel * All rights reserved.
6130803Smarcel *
7130803Smarcel * Redistribution and use in source and binary forms, with or without
8130803Smarcel * modification, are permitted provided that the following conditions
9130803Smarcel * are met:
10130803Smarcel * 1. Redistributions of source code must retain the above copyright
11130803Smarcel *    notice unmodified, this list of conditions, and the following
12130803Smarcel *    disclaimer.
13130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright
14130803Smarcel *    notice, this list of conditions and the following disclaimer in the
15130803Smarcel *    documentation and/or other materials provided with the distribution.
16130803Smarcel *
17130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18130803Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19130803Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20130803Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21130803Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22130803Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23130803Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24130803Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25130803Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26130803Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27130803Smarcel */
28130803Smarcel
29130803Smarcel#include <sys/cdefs.h>
30130803Smarcel__FBSDID("$FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 192342 2009-05-18 21:47:32Z jhb $");
31130803Smarcel
32130803Smarcel#include <sys/param.h>
33130803Smarcel#include <sys/systm.h>
34130803Smarcel#include <sys/bus.h>
35130803Smarcel#include <sys/lock.h>
36130803Smarcel#include <sys/kernel.h>
37130803Smarcel#include <sys/mutex.h>
38130803Smarcel#include <sys/sysctl.h>
39130803Smarcel#include <dev/pci/pcivar.h>
40130803Smarcel#include <dev/pci/pcireg.h>
41130803Smarcel#include <vm/vm.h>
42130803Smarcel#include <vm/pmap.h>
43130803Smarcel#include <machine/pci_cfgreg.h>
44130803Smarcel
45130803Smarcelenum {
46130803Smarcel	CFGMECH_NONE = 0,
47130803Smarcel	CFGMECH_1,
48130803Smarcel	CFGMECH_PCIE,
49130803Smarcel};
50130803Smarcel
51130803Smarcelstatic uint32_t	pci_docfgregread(int bus, int slot, int func, int reg,
52130803Smarcel		    int bytes);
53130803Smarcelstatic int	pciereg_cfgread(int bus, unsigned slot, unsigned func,
54130803Smarcel		    unsigned reg, unsigned bytes);
55130803Smarcelstatic void	pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
56130803Smarcel		    unsigned reg, int data, unsigned bytes);
57130803Smarcelstatic int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
58130803Smarcelstatic void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
59130803Smarcel
60130803SmarcelSYSCTL_DECL(_hw_pci);
61130803Smarcel
62130803Smarcelstatic int cfgmech;
63130803Smarcelstatic vm_offset_t pcie_base;
64130803Smarcelstatic int pcie_minbus, pcie_maxbus;
65130803Smarcelstatic uint32_t pcie_badslots;
66130803Smarcelstatic struct mtx pcicfg_mtx;
67130803Smarcelstatic int mcfg_enable = 1;
68130803SmarcelTUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
69130803SmarcelSYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
70130803Smarcel    "Enable support for PCI-e memory mapped config access");
71130803Smarcel
72130803Smarcel/*
73130803Smarcel * Initialise access to PCI configuration space
74130803Smarcel */
75130803Smarcelint
76130803Smarcelpci_cfgregopen(void)
77130803Smarcel{
78130803Smarcel	static int once = 0;
79130803Smarcel	uint64_t pciebar;
80130803Smarcel	uint16_t did, vid;
81130803Smarcel
82130803Smarcel	if (!once) {
83130803Smarcel		mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
84130803Smarcel		once = 1;
85130803Smarcel	}
86130803Smarcel
87130803Smarcel	if (cfgmech != CFGMECH_NONE)
88130803Smarcel		return (1);
89130803Smarcel	cfgmech = CFGMECH_1;
90130803Smarcel
91130803Smarcel	/*
92130803Smarcel	 * Grope around in the PCI config space to see if this is a
93130803Smarcel	 * chipset that is capable of doing memory-mapped config cycles.
94130803Smarcel	 * This also implies that it can do PCIe extended config cycles.
95130803Smarcel	 */
96130803Smarcel
97130803Smarcel	/* Check for supported chipsets */
98130803Smarcel	vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
99130803Smarcel	did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
100130803Smarcel	switch (vid) {
101130803Smarcel	case 0x8086:
102130803Smarcel		switch (did) {
103130803Smarcel		case 0x3590:
104130803Smarcel		case 0x3592:
105130803Smarcel			/* Intel 7520 or 7320 */
106130803Smarcel			pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
107130803Smarcel			pcie_cfgregopen(pciebar, 0, 255);
108130803Smarcel			break;
109130803Smarcel		case 0x2580:
110130803Smarcel		case 0x2584:
111130803Smarcel		case 0x2590:
112130803Smarcel			/* Intel 915, 925, or 915GM */
113130803Smarcel			pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
114130803Smarcel			pcie_cfgregopen(pciebar, 0, 255);
115130803Smarcel			break;
116130803Smarcel		}
117130803Smarcel	}
118130803Smarcel
119130803Smarcel	return (1);
120130803Smarcel}
121130803Smarcel
122130803Smarcelstatic uint32_t
123130803Smarcelpci_docfgregread(int bus, int slot, int func, int reg, int bytes)
124130803Smarcel{
125130803Smarcel
126130803Smarcel	if (cfgmech == CFGMECH_PCIE &&
127130803Smarcel	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
128130803Smarcel	    (bus != 0 || !(1 << slot & pcie_badslots)))
129130803Smarcel		return (pciereg_cfgread(bus, slot, func, reg, bytes));
130130803Smarcel	else
131130803Smarcel		return (pcireg_cfgread(bus, slot, func, reg, bytes));
132130803Smarcel}
133130803Smarcel
134130803Smarcel/*
135130803Smarcel * Read configuration space register
136130803Smarcel */
137130803Smarcelu_int32_t
138130803Smarcelpci_cfgregread(int bus, int slot, int func, int reg, int bytes)
139130803Smarcel{
140130803Smarcel	uint32_t line;
141130803Smarcel
142130803Smarcel	/*
143130803Smarcel	 * Some BIOS writers seem to want to ignore the spec and put
144130803Smarcel	 * 0 in the intline rather than 255 to indicate none.  Some use
145130803Smarcel	 * numbers in the range 128-254 to indicate something strange and
146130803Smarcel	 * apparently undocumented anywhere.  Assume these are completely bogus
147130803Smarcel	 * and map them to 255, which the rest of the PCI code recognizes as
148130803Smarcel	 * as an invalid IRQ.
149130803Smarcel	 */
150130803Smarcel	if (reg == PCIR_INTLINE && bytes == 1) {
151130803Smarcel		line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
152130803Smarcel		if (line == 0 || line >= 128)
153130803Smarcel			line = PCI_INVALID_IRQ;
154130803Smarcel		return (line);
155130803Smarcel	}
156130803Smarcel	return (pci_docfgregread(bus, slot, func, reg, bytes));
157130803Smarcel}
158130803Smarcel
159130803Smarcel/*
160130803Smarcel * Write configuration space register
161130803Smarcel */
162130803Smarcelvoid
163130803Smarcelpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
164130803Smarcel{
165130803Smarcel
166130803Smarcel	if (cfgmech == CFGMECH_PCIE &&
167130803Smarcel	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
168130803Smarcel	    (bus != 0 || !(1 << slot & pcie_badslots)))
169130803Smarcel		pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
170130803Smarcel	else
171130803Smarcel		pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
172130803Smarcel}
173130803Smarcel
174130803Smarcel/*
175130803Smarcel * Configuration space access using direct register operations
176130803Smarcel */
177130803Smarcel
178130803Smarcel/* enable configuration space accesses and return data port address */
179130803Smarcelstatic int
180130803Smarcelpci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
181130803Smarcel{
182130803Smarcel	int dataport = 0;
183130803Smarcel
184130803Smarcel	if (bus <= PCI_BUSMAX && slot < 32 && func <= PCI_FUNCMAX &&
185130803Smarcel	    reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 &&
186130803Smarcel	    (reg & (bytes - 1)) == 0) {
187130803Smarcel		outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11)
188130803Smarcel		    | (func << 8) | (reg & ~0x03));
189130803Smarcel		dataport = CONF1_DATA_PORT + (reg & 0x03);
190130803Smarcel	}
191130803Smarcel	return (dataport);
192130803Smarcel}
193130803Smarcel
194130803Smarcel/* disable configuration space accesses */
195130803Smarcelstatic void
196130803Smarcelpci_cfgdisable(void)
197130803Smarcel{
198130803Smarcel
199130803Smarcel	/*
200130803Smarcel	 * Do nothing.  Writing a 0 to the address port can apparently
201130803Smarcel	 * confuse some bridges and cause spurious access failures.
202130803Smarcel	 */
203130803Smarcel}
204130803Smarcel
205130803Smarcelstatic int
206130803Smarcelpcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
207130803Smarcel{
208130803Smarcel	int data = -1;
209130803Smarcel	int port;
210130803Smarcel
211130803Smarcel	mtx_lock_spin(&pcicfg_mtx);
212130803Smarcel	port = pci_cfgenable(bus, slot, func, reg, bytes);
213130803Smarcel	if (port != 0) {
214130803Smarcel		switch (bytes) {
215130803Smarcel		case 1:
216130803Smarcel			data = inb(port);
217130803Smarcel			break;
218130803Smarcel		case 2:
219130803Smarcel			data = inw(port);
220130803Smarcel			break;
221130803Smarcel		case 4:
222130803Smarcel			data = inl(port);
223130803Smarcel			break;
224130803Smarcel		}
225130803Smarcel		pci_cfgdisable();
226130803Smarcel	}
227130803Smarcel	mtx_unlock_spin(&pcicfg_mtx);
228130803Smarcel	return (data);
229130803Smarcel}
230130803Smarcel
231130803Smarcelstatic void
232130803Smarcelpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
233130803Smarcel{
234130803Smarcel	int port;
235130803Smarcel
236130803Smarcel	mtx_lock_spin(&pcicfg_mtx);
237130803Smarcel	port = pci_cfgenable(bus, slot, func, reg, bytes);
238130803Smarcel	if (port != 0) {
239130803Smarcel		switch (bytes) {
240130803Smarcel		case 1:
241130803Smarcel			outb(port, data);
242130803Smarcel			break;
243130803Smarcel		case 2:
244130803Smarcel			outw(port, data);
245130803Smarcel			break;
246130803Smarcel		case 4:
247130803Smarcel			outl(port, data);
248130803Smarcel			break;
249130803Smarcel		}
250130803Smarcel		pci_cfgdisable();
251130803Smarcel	}
252130803Smarcel	mtx_unlock_spin(&pcicfg_mtx);
253130803Smarcel}
254130803Smarcel
255130803Smarcelint
256130803Smarcelpcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
257130803Smarcel{
258130803Smarcel	uint32_t val1, val2;
259130803Smarcel	int slot;
260130803Smarcel
261130803Smarcel	if (!mcfg_enable)
262130803Smarcel		return (0);
263130803Smarcel
264130803Smarcel	if (minbus != 0)
265130803Smarcel		return (0);
266130803Smarcel
267130803Smarcel	if (bootverbose)
268130803Smarcel		printf("PCIe: Memory Mapped configuration base @ 0x%lx\n",
269130803Smarcel		    base);
270130803Smarcel
271130803Smarcel	/* XXX: We should make sure this really fits into the direct map. */
272130803Smarcel	pcie_base = (vm_offset_t)pmap_mapdev(base, (maxbus + 1) << 20);
273130803Smarcel	pcie_minbus = minbus;
274130803Smarcel	pcie_maxbus = maxbus;
275130803Smarcel	cfgmech = CFGMECH_PCIE;
276130803Smarcel
277130803Smarcel	/*
278130803Smarcel	 * On some AMD systems, some of the devices on bus 0 are
279130803Smarcel	 * inaccessible using memory-mapped PCI config access.  Walk
280130803Smarcel	 * bus 0 looking for such devices.  For these devices, we will
281130803Smarcel	 * fall back to using type 1 config access instead.
282130803Smarcel	 */
283130803Smarcel	if (pci_cfgregopen() != 0) {
284130803Smarcel		for (slot = 0; slot < 32; slot++) {
285130803Smarcel			val1 = pcireg_cfgread(0, slot, 0, 0, 4);
286130803Smarcel			if (val1 == 0xffffffff)
287130803Smarcel				continue;
288130803Smarcel
289130803Smarcel			val2 = pciereg_cfgread(0, slot, 0, 0, 4);
290130803Smarcel			if (val2 != val1)
291130803Smarcel				pcie_badslots |= (1 << slot);
292130803Smarcel		}
293130803Smarcel	}
294130803Smarcel
295130803Smarcel	return (1);
296130803Smarcel}
297130803Smarcel
298130803Smarcel#define PCIE_VADDR(base, reg, bus, slot, func)	\
299130803Smarcel	((base)				+	\
300130803Smarcel	((((bus) & 0xff) << 20)		|	\
301130803Smarcel	(((slot) & 0x1f) << 15)		|	\
302130803Smarcel	(((func) & 0x7) << 12)		|	\
303130803Smarcel	((reg) & 0xfff)))
304130803Smarcel
305130803Smarcelstatic int
306130803Smarcelpciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
307130803Smarcel    unsigned bytes)
308130803Smarcel{
309130803Smarcel	volatile vm_offset_t va;
310130803Smarcel	int data = -1;
311130803Smarcel
312130803Smarcel	if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
313130803Smarcel	    func > PCI_FUNCMAX || reg >= 0x1000)
314130803Smarcel		return (-1);
315130803Smarcel
316130803Smarcel	va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
317130803Smarcel
318130803Smarcel	switch (bytes) {
319130803Smarcel	case 4:
320130803Smarcel		data = *(volatile uint32_t *)(va);
321130803Smarcel		break;
322130803Smarcel	case 2:
323130803Smarcel		data = *(volatile uint16_t *)(va);
324130803Smarcel		break;
325130803Smarcel	case 1:
326130803Smarcel		data = *(volatile uint8_t *)(va);
327130803Smarcel		break;
328130803Smarcel	}
329130803Smarcel
330130803Smarcel	return (data);
331130803Smarcel}
332130803Smarcel
333130803Smarcelstatic void
334130803Smarcelpciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
335130803Smarcel    unsigned bytes)
336130803Smarcel{
337130803Smarcel	volatile vm_offset_t va;
338130803Smarcel
339130803Smarcel	if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
340130803Smarcel	    func > PCI_FUNCMAX || reg >= 0x1000)
341130803Smarcel		return;
342130803Smarcel
343130803Smarcel	va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
344130803Smarcel
345130803Smarcel	switch (bytes) {
346130803Smarcel	case 4:
347130803Smarcel		*(volatile uint32_t *)(va) = data;
348130803Smarcel		break;
349130803Smarcel	case 2:
350130803Smarcel		*(volatile uint16_t *)(va) = data;
351130803Smarcel		break;
352130803Smarcel	case 1:
353130803Smarcel		*(volatile uint8_t *)(va) = data;
354130803Smarcel		break;
355130803Smarcel	}
356130803Smarcel}
357130803Smarcel