xref: /freebsd/sys/amd64/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 
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 		outl(CONF1_ADDR_PORT, 0);
143 		break;
144 	case 2:
145 		outb(CONF2_ENABLE_PORT, 0);
146 		outb(CONF2_FORWARD_PORT, 0);
147 		break;
148 	}
149 }
150 
151 static int
152 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
153 {
154 	int data = -1;
155 	int port;
156 
157 	mtx_lock_spin(&pcicfg_mtx);
158 	port = pci_cfgenable(bus, slot, func, reg, bytes);
159 	if (port != 0) {
160 		switch (bytes) {
161 		case 1:
162 			data = inb(port);
163 			break;
164 		case 2:
165 			data = inw(port);
166 			break;
167 		case 4:
168 			data = inl(port);
169 			break;
170 		}
171 		pci_cfgdisable();
172 	}
173 	mtx_unlock_spin(&pcicfg_mtx);
174 	return (data);
175 }
176 
177 static void
178 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
179 {
180 	int port;
181 
182 	mtx_lock_spin(&pcicfg_mtx);
183 	port = pci_cfgenable(bus, slot, func, reg, bytes);
184 	if (port != 0) {
185 		switch (bytes) {
186 		case 1:
187 			outb(port, data);
188 			break;
189 		case 2:
190 			outw(port, data);
191 			break;
192 		case 4:
193 			outl(port, data);
194 			break;
195 		}
196 		pci_cfgdisable();
197 	}
198 	mtx_unlock_spin(&pcicfg_mtx);
199 }
200 
201 /* check whether the configuration mechanism has been correctly identified */
202 static int
203 pci_cfgcheck(int maxdev)
204 {
205 	uint32_t id, class;
206 	uint8_t header;
207 	uint8_t device;
208 	int port;
209 
210 	if (bootverbose)
211 		printf("pci_cfgcheck:\tdevice ");
212 
213 	for (device = 0; device < maxdev; device++) {
214 		if (bootverbose)
215 			printf("%d ", device);
216 
217 		port = pci_cfgenable(0, device, 0, 0, 4);
218 		id = inl(port);
219 		if (id == 0 || id == 0xffffffff)
220 			continue;
221 
222 		port = pci_cfgenable(0, device, 0, 8, 4);
223 		class = inl(port) >> 8;
224 		if (bootverbose)
225 			printf("[class=%06x] ", class);
226 		if (class == 0 || (class & 0xf870ff) != 0)
227 			continue;
228 
229 		port = pci_cfgenable(0, device, 0, 14, 1);
230 		header = inb(port);
231 		if (bootverbose)
232 			printf("[hdr=%02x] ", header);
233 		if ((header & 0x7e) != 0)
234 			continue;
235 
236 		if (bootverbose)
237 			printf("is there (id=%08x)\n", id);
238 
239 		pci_cfgdisable();
240 		return (1);
241 	}
242 	if (bootverbose)
243 		printf("-- nothing found\n");
244 
245 	pci_cfgdisable();
246 	return (0);
247 }
248 
249 static int
250 pcireg_cfgopen(void)
251 {
252 	uint32_t mode1res, oldval1;
253 	uint8_t mode2res, oldval2;
254 
255 	oldval1 = inl(CONF1_ADDR_PORT);
256 
257 	if (bootverbose) {
258 		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
259 		    oldval1);
260 	}
261 
262 	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
263 
264 		cfgmech = 1;
265 		devmax = 32;
266 
267 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
268 		DELAY(1);
269 		mode1res = inl(CONF1_ADDR_PORT);
270 		outl(CONF1_ADDR_PORT, oldval1);
271 
272 		if (bootverbose)
273 			printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
274 			    mode1res, CONF1_ENABLE_CHK);
275 
276 		if (mode1res) {
277 			if (pci_cfgcheck(32))
278 				return (cfgmech);
279 		}
280 
281 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
282 		mode1res = inl(CONF1_ADDR_PORT);
283 		outl(CONF1_ADDR_PORT, oldval1);
284 
285 		if (bootverbose)
286 			printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
287 			    mode1res, CONF1_ENABLE_CHK1);
288 
289 		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
290 			if (pci_cfgcheck(32))
291 				return (cfgmech);
292 		}
293 	}
294 
295 	oldval2 = inb(CONF2_ENABLE_PORT);
296 
297 	if (bootverbose) {
298 		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
299 		    oldval2);
300 	}
301 
302 	if ((oldval2 & 0xf0) == 0) {
303 
304 		cfgmech = 2;
305 		devmax = 16;
306 
307 		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
308 		mode2res = inb(CONF2_ENABLE_PORT);
309 		outb(CONF2_ENABLE_PORT, oldval2);
310 
311 		if (bootverbose)
312 			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
313 			    mode2res, CONF2_ENABLE_CHK);
314 
315 		if (mode2res == CONF2_ENABLE_RES) {
316 			if (bootverbose)
317 				printf("pci_open(2a):\tnow trying mechanism 2\n");
318 
319 			if (pci_cfgcheck(16))
320 				return (cfgmech);
321 		}
322 	}
323 
324 	cfgmech = 0;
325 	devmax = 0;
326 	return (cfgmech);
327 }
328 
329