xref: /freebsd/sys/i386/pci/pci_cfgreg.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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/mutex.h>
37 #include <dev/pci/pcivar.h>
38 #include <dev/pci/pcireg.h>
39 #include <machine/pci_cfgreg.h>
40 #include <machine/pc/bios.h>
41 
42 #define PRVERB(a) do {							\
43 	if (bootverbose)						\
44 		printf a ;						\
45 } while(0)
46 
47 static int cfgmech;
48 static int devmax;
49 
50 static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
51 static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
52 static int	pcireg_cfgopen(void);
53 
54 static struct mtx pcicfg_mtx;
55 
56 /*
57  * Some BIOS writers seem to want to ignore the spec and put
58  * 0 in the intline rather than 255 to indicate none.  Some use
59  * numbers in the range 128-254 to indicate something strange and
60  * apparently undocumented anywhere.  Assume these are completely bogus
61  * and map them to 255, which means "none".
62  */
63 static __inline int
64 pci_i386_map_intline(int line)
65 {
66 	if (line == 0 || line >= 128)
67 		return (PCI_INVALID_IRQ);
68 	return (line);
69 }
70 
71 static u_int16_t
72 pcibios_get_version(void)
73 {
74 	struct bios_regs args;
75 
76 	if (PCIbios.ventry == 0) {
77 		PRVERB(("pcibios: No call entry point\n"));
78 		return (0);
79 	}
80 	args.eax = PCIBIOS_BIOS_PRESENT;
81 	if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
82 		PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
83 		return (0);
84 	}
85 	if (args.edx != 0x20494350) {
86 		PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
87 		return (0);
88 	}
89 	return (args.ebx & 0xffff);
90 }
91 
92 /*
93  * Initialise access to PCI configuration space
94  */
95 int
96 pci_cfgregopen(void)
97 {
98 	static int		opened = 0;
99 	u_int16_t		v;
100 
101 	if (opened)
102 		return(1);
103 
104 	if (pcireg_cfgopen() == 0)
105 		return(0);
106 
107 	v = pcibios_get_version();
108 	if (v > 0)
109 		PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
110 		    v & 0xff));
111 	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
112 	opened = 1;
113 
114 	/* $PIR requires PCI BIOS 2.10 or greater. */
115 	if (v >= 0x0210)
116 		pci_pir_open();
117 	return(1);
118 }
119 
120 /*
121  * Read configuration space register
122  */
123 u_int32_t
124 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
125 {
126 	uint32_t line;
127 
128 	/*
129 	 * Some BIOS writers seem to want to ignore the spec and put
130 	 * 0 in the intline rather than 255 to indicate none.  The rest of
131 	 * the code uses 255 as an invalid IRQ.
132 	 */
133 	if (reg == PCIR_INTLINE && bytes == 1) {
134 		line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
135 		return (pci_i386_map_intline(line));
136 	}
137 	return (pcireg_cfgread(bus, slot, func, reg, bytes));
138 }
139 
140 /*
141  * Write configuration space register
142  */
143 void
144 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
145 {
146 
147 	pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
148 }
149 
150 /*
151  * Configuration space access using direct register operations
152  */
153 
154 /* enable configuration space accesses and return data port address */
155 static int
156 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
157 {
158 	int dataport = 0;
159 
160 	if (bus <= PCI_BUSMAX
161 	    && slot < devmax
162 	    && func <= PCI_FUNCMAX
163 	    && reg <= PCI_REGMAX
164 	    && bytes != 3
165 	    && (unsigned) bytes <= 4
166 	    && (reg & (bytes - 1)) == 0) {
167 		switch (cfgmech) {
168 		case 1:
169 			outl(CONF1_ADDR_PORT, (1 << 31)
170 			    | (bus << 16) | (slot << 11)
171 			    | (func << 8) | (reg & ~0x03));
172 			dataport = CONF1_DATA_PORT + (reg & 0x03);
173 			break;
174 		case 2:
175 			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
176 			outb(CONF2_FORWARD_PORT, bus);
177 			dataport = 0xc000 | (slot << 8) | reg;
178 			break;
179 		}
180 	}
181 	return (dataport);
182 }
183 
184 /* disable configuration space accesses */
185 static void
186 pci_cfgdisable(void)
187 {
188 	switch (cfgmech) {
189 	case 1:
190 		outl(CONF1_ADDR_PORT, 0);
191 		break;
192 	case 2:
193 		outb(CONF2_ENABLE_PORT, 0);
194 		outb(CONF2_FORWARD_PORT, 0);
195 		break;
196 	}
197 }
198 
199 static int
200 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
201 {
202 	int data = -1;
203 	int port;
204 
205 	mtx_lock_spin(&pcicfg_mtx);
206 	port = pci_cfgenable(bus, slot, func, reg, bytes);
207 	if (port != 0) {
208 		switch (bytes) {
209 		case 1:
210 			data = inb(port);
211 			break;
212 		case 2:
213 			data = inw(port);
214 			break;
215 		case 4:
216 			data = inl(port);
217 			break;
218 		}
219 		pci_cfgdisable();
220 	}
221 	mtx_unlock_spin(&pcicfg_mtx);
222 	return (data);
223 }
224 
225 static void
226 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
227 {
228 	int port;
229 
230 	mtx_lock_spin(&pcicfg_mtx);
231 	port = pci_cfgenable(bus, slot, func, reg, bytes);
232 	if (port != 0) {
233 		switch (bytes) {
234 		case 1:
235 			outb(port, data);
236 			break;
237 		case 2:
238 			outw(port, data);
239 			break;
240 		case 4:
241 			outl(port, data);
242 			break;
243 		}
244 		pci_cfgdisable();
245 	}
246 	mtx_unlock_spin(&pcicfg_mtx);
247 }
248 
249 /* check whether the configuration mechanism has been correctly identified */
250 static int
251 pci_cfgcheck(int maxdev)
252 {
253 	uint32_t id, class;
254 	uint8_t header;
255 	uint8_t device;
256 	int port;
257 
258 	if (bootverbose)
259 		printf("pci_cfgcheck:\tdevice ");
260 
261 	for (device = 0; device < maxdev; device++) {
262 		if (bootverbose)
263 			printf("%d ", device);
264 
265 		port = pci_cfgenable(0, device, 0, 0, 4);
266 		id = inl(port);
267 		if (id == 0 || id == 0xffffffff)
268 			continue;
269 
270 		port = pci_cfgenable(0, device, 0, 8, 4);
271 		class = inl(port) >> 8;
272 		if (bootverbose)
273 			printf("[class=%06x] ", class);
274 		if (class == 0 || (class & 0xf870ff) != 0)
275 			continue;
276 
277 		port = pci_cfgenable(0, device, 0, 14, 1);
278 		header = inb(port);
279 		if (bootverbose)
280 			printf("[hdr=%02x] ", header);
281 		if ((header & 0x7e) != 0)
282 			continue;
283 
284 		if (bootverbose)
285 			printf("is there (id=%08x)\n", id);
286 
287 		pci_cfgdisable();
288 		return (1);
289 	}
290 	if (bootverbose)
291 		printf("-- nothing found\n");
292 
293 	pci_cfgdisable();
294 	return (0);
295 }
296 
297 static int
298 pcireg_cfgopen(void)
299 {
300 	uint32_t mode1res, oldval1;
301 	uint8_t mode2res, oldval2;
302 
303 	oldval1 = inl(CONF1_ADDR_PORT);
304 
305 	if (bootverbose) {
306 		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
307 		    oldval1);
308 	}
309 
310 	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
311 
312 		cfgmech = 1;
313 		devmax = 32;
314 
315 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
316 		DELAY(1);
317 		mode1res = inl(CONF1_ADDR_PORT);
318 		outl(CONF1_ADDR_PORT, oldval1);
319 
320 		if (bootverbose)
321 			printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
322 			    mode1res, CONF1_ENABLE_CHK);
323 
324 		if (mode1res) {
325 			if (pci_cfgcheck(32))
326 				return (cfgmech);
327 		}
328 
329 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
330 		mode1res = inl(CONF1_ADDR_PORT);
331 		outl(CONF1_ADDR_PORT, oldval1);
332 
333 		if (bootverbose)
334 			printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
335 			    mode1res, CONF1_ENABLE_CHK1);
336 
337 		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
338 			if (pci_cfgcheck(32))
339 				return (cfgmech);
340 		}
341 	}
342 
343 	oldval2 = inb(CONF2_ENABLE_PORT);
344 
345 	if (bootverbose) {
346 		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
347 		    oldval2);
348 	}
349 
350 	if ((oldval2 & 0xf0) == 0) {
351 
352 		cfgmech = 2;
353 		devmax = 16;
354 
355 		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
356 		mode2res = inb(CONF2_ENABLE_PORT);
357 		outb(CONF2_ENABLE_PORT, oldval2);
358 
359 		if (bootverbose)
360 			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
361 			    mode2res, CONF2_ENABLE_CHK);
362 
363 		if (mode2res == CONF2_ENABLE_RES) {
364 			if (bootverbose)
365 				printf("pci_open(2a):\tnow trying mechanism 2\n");
366 
367 			if (pci_cfgcheck(16))
368 				return (cfgmech);
369 		}
370 	}
371 
372 	cfgmech = 0;
373 	devmax = 0;
374 	return (cfgmech);
375 }
376 
377