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