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