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: releng/5.3/sys/i386/pci/pci_cfgreg.c 131575 2004-07-04 16:11:03Z stefanf $");
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
Cache object: 01e10652cac2fcfc2e2b6102b362fb38
|