xref: /freebsd/sys/amd64/pci/pci_cfgreg.c (revision 2a7b66a8dde4ece1a84d1d3b4f093be73ce42178)
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  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/lock.h>
36 #include <sys/kernel.h>
37 #include <sys/mutex.h>
38 #include <dev/pci/pcivar.h>
39 #include <dev/pci/pcireg.h>
40 #include <vm/vm.h>
41 #include <vm/pmap.h>
42 #include <machine/pci_cfgreg.h>
43 
44 enum {
45 	CFGMECH_NONE = 0,
46 	CFGMECH_1,
47 	CFGMECH_PCIE,
48 };
49 
50 static uint32_t	pci_docfgregread(int bus, int slot, int func, int reg,
51 		    int bytes);
52 static int	pciereg_cfgread(int bus, unsigned slot, unsigned func,
53 		    unsigned reg, unsigned bytes);
54 static void	pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
55 		    unsigned reg, int data, unsigned bytes);
56 static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
57 static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
58 
59 static int cfgmech;
60 static vm_offset_t pcie_base;
61 static int pcie_minbus, pcie_maxbus;
62 static uint32_t pcie_badslots;
63 static struct mtx pcicfg_mtx;
64 static int mcfg_enable = 1;
65 TUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
66 
67 /*
68  * Initialise access to PCI configuration space
69  */
70 int
71 pci_cfgregopen(void)
72 {
73 	static int once = 0;
74 	uint64_t pciebar;
75 	uint16_t did, vid;
76 
77 	if (!once) {
78 		mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
79 		once = 1;
80 	}
81 
82 	if (cfgmech != CFGMECH_NONE)
83 		return (1);
84 	cfgmech = CFGMECH_1;
85 
86 	/*
87 	 * Grope around in the PCI config space to see if this is a
88 	 * chipset that is capable of doing memory-mapped config cycles.
89 	 * This also implies that it can do PCIe extended config cycles.
90 	 */
91 
92 	/* Check for supported chipsets */
93 	vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
94 	did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
95 	switch (vid) {
96 	case 0x8086:
97 		switch (did) {
98 		case 0x3590:
99 		case 0x3592:
100 			/* Intel 7520 or 7320 */
101 			pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
102 			pcie_cfgregopen(pciebar, 0, 255);
103 			break;
104 		case 0x2580:
105 		case 0x2584:
106 		case 0x2590:
107 			/* Intel 915, 925, or 915GM */
108 			pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
109 			pcie_cfgregopen(pciebar, 0, 255);
110 			break;
111 		}
112 	}
113 
114 	return (1);
115 }
116 
117 static uint32_t
118 pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
119 {
120 
121 	if (cfgmech == CFGMECH_PCIE &&
122 	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
123 	    (bus != 0 || !(1 << slot & pcie_badslots)))
124 		return (pciereg_cfgread(bus, slot, func, reg, bytes));
125 	else
126 		return (pcireg_cfgread(bus, slot, func, reg, bytes));
127 }
128 
129 /*
130  * Read configuration space register
131  */
132 u_int32_t
133 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
134 {
135 	uint32_t line;
136 
137 	/*
138 	 * Some BIOS writers seem to want to ignore the spec and put
139 	 * 0 in the intline rather than 255 to indicate none.  Some use
140 	 * numbers in the range 128-254 to indicate something strange and
141 	 * apparently undocumented anywhere.  Assume these are completely bogus
142 	 * and map them to 255, which the rest of the PCI code recognizes as
143 	 * as an invalid IRQ.
144 	 */
145 	if (reg == PCIR_INTLINE && bytes == 1) {
146 		line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
147 		if (line == 0 || line >= 128)
148 			line = PCI_INVALID_IRQ;
149 		return (line);
150 	}
151 	return (pci_docfgregread(bus, slot, func, reg, bytes));
152 }
153 
154 /*
155  * Write configuration space register
156  */
157 void
158 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
159 {
160 
161 	if (cfgmech == CFGMECH_PCIE &&
162 	    (bus >= pcie_minbus && bus <= pcie_maxbus) &&
163 	    (bus != 0 || !(1 << slot & pcie_badslots)))
164 		pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
165 	else
166 		pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
167 }
168 
169 /*
170  * Configuration space access using direct register operations
171  */
172 
173 /* enable configuration space accesses and return data port address */
174 static int
175 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
176 {
177 	int dataport = 0;
178 
179 	if (bus <= PCI_BUSMAX && slot < 32 && func <= PCI_FUNCMAX &&
180 	    reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 &&
181 	    (reg & (bytes - 1)) == 0) {
182 		outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11)
183 		    | (func << 8) | (reg & ~0x03));
184 		dataport = CONF1_DATA_PORT + (reg & 0x03);
185 	}
186 	return (dataport);
187 }
188 
189 /* disable configuration space accesses */
190 static void
191 pci_cfgdisable(void)
192 {
193 
194 	/*
195 	 * Do nothing.  Writing a 0 to the address port can apparently
196 	 * confuse some bridges and cause spurious access failures.
197 	 */
198 }
199 
200 static int
201 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
202 {
203 	int data = -1;
204 	int port;
205 
206 	mtx_lock_spin(&pcicfg_mtx);
207 	port = pci_cfgenable(bus, slot, func, reg, bytes);
208 	if (port != 0) {
209 		switch (bytes) {
210 		case 1:
211 			data = inb(port);
212 			break;
213 		case 2:
214 			data = inw(port);
215 			break;
216 		case 4:
217 			data = inl(port);
218 			break;
219 		}
220 		pci_cfgdisable();
221 	}
222 	mtx_unlock_spin(&pcicfg_mtx);
223 	return (data);
224 }
225 
226 static void
227 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
228 {
229 	int port;
230 
231 	mtx_lock_spin(&pcicfg_mtx);
232 	port = pci_cfgenable(bus, slot, func, reg, bytes);
233 	if (port != 0) {
234 		switch (bytes) {
235 		case 1:
236 			outb(port, data);
237 			break;
238 		case 2:
239 			outw(port, data);
240 			break;
241 		case 4:
242 			outl(port, data);
243 			break;
244 		}
245 		pci_cfgdisable();
246 	}
247 	mtx_unlock_spin(&pcicfg_mtx);
248 }
249 
250 int
251 pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
252 {
253 	uint32_t val1, val2;
254 	int slot;
255 
256 	if (!mcfg_enable)
257 		return (0);
258 
259 	if (minbus != 0)
260 		return (0);
261 
262 	if (bootverbose)
263 		printf("PCIe: Memory Mapped configuration base @ 0x%lx\n",
264 		    base);
265 
266 	/* XXX: We should make sure this really fits into the direct map. */
267 	pcie_base = (vm_offset_t)pmap_mapdev(base, (maxbus + 1) << 20);
268 	pcie_minbus = minbus;
269 	pcie_maxbus = maxbus;
270 	cfgmech = CFGMECH_PCIE;
271 
272 	/*
273 	 * On some AMD systems, some of the devices on bus 0 are
274 	 * inaccessible using memory-mapped PCI config access.  Walk
275 	 * bus 0 looking for such devices.  For these devices, we will
276 	 * fall back to using type 1 config access instead.
277 	 */
278 	if (pci_cfgregopen() != 0) {
279 		for (slot = 0; slot < 32; slot++) {
280 			val1 = pcireg_cfgread(0, slot, 0, 0, 4);
281 			if (val1 == 0xffffffff)
282 				continue;
283 
284 			val2 = pciereg_cfgread(0, slot, 0, 0, 4);
285 			if (val2 != val1)
286 				pcie_badslots |= (1 << slot);
287 		}
288 	}
289 
290 	return (1);
291 }
292 
293 #define PCIE_VADDR(base, reg, bus, slot, func)	\
294 	((base)				+	\
295 	((((bus) & 0xff) << 20)		|	\
296 	(((slot) & 0x1f) << 15)		|	\
297 	(((func) & 0x7) << 12)		|	\
298 	((reg) & 0xfff)))
299 
300 static int
301 pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
302     unsigned bytes)
303 {
304 	volatile vm_offset_t va;
305 	int data = -1;
306 
307 	if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
308 	    func > PCI_FUNCMAX || reg >= 0x1000)
309 		return (-1);
310 
311 	va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
312 
313 	switch (bytes) {
314 	case 4:
315 		data = *(volatile uint32_t *)(va);
316 		break;
317 	case 2:
318 		data = *(volatile uint16_t *)(va);
319 		break;
320 	case 1:
321 		data = *(volatile uint8_t *)(va);
322 		break;
323 	}
324 
325 	return (data);
326 }
327 
328 static void
329 pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
330     unsigned bytes)
331 {
332 	volatile vm_offset_t va;
333 
334 	if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
335 	    func > PCI_FUNCMAX || reg >= 0x1000)
336 		return;
337 
338 	va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
339 
340 	switch (bytes) {
341 	case 4:
342 		*(volatile uint32_t *)(va) = data;
343 		break;
344 	case 2:
345 		*(volatile uint16_t *)(va) = data;
346 		break;
347 	case 1:
348 		*(volatile uint8_t *)(va) = data;
349 		break;
350 	}
351 }
352