FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/pcibus.c
1 /**************************************************************************
2 **
3 ** $FreeBSD: src/sys/i386/isa/pcibus.c,v 1.27.2.3 1999/09/05 08:13:17 peter Exp $
4 **
5 ** pci bus subroutines for i386 architecture.
6 **
7 ** FreeBSD
8 **
9 **-------------------------------------------------------------------------
10 **
11 ** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved.
12 **
13 ** Redistribution and use in source and binary forms, with or without
14 ** modification, are permitted provided that the following conditions
15 ** are met:
16 ** 1. Redistributions of source code must retain the above copyright
17 ** notice, this list of conditions and the following disclaimer.
18 ** 2. Redistributions in binary form must reproduce the above copyright
19 ** notice, this list of conditions and the following disclaimer in the
20 ** documentation and/or other materials provided with the distribution.
21 ** 3. The name of the author may not be used to endorse or promote products
22 ** derived from this software without specific prior written permission.
23 **
24 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 **
35 ***************************************************************************
36 */
37
38 #include "vector.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43
44 #include <i386/isa/icu.h>
45 #include <i386/isa/isa_device.h>
46
47 #include <pci/pcivar.h>
48 #include <pci/pcireg.h>
49 #include <pci/pcibus.h>
50
51 /*-----------------------------------------------------------------
52 **
53 ** The following functions are provided by the pci bios.
54 ** They are used only by the pci configuration.
55 **
56 ** pcibus_setup():
57 ** Probes for a pci system.
58 ** Sets pci_maxdevice and pci_mechanism.
59 **
60 ** pcibus_tag():
61 ** Creates a handle for pci configuration space access.
62 ** This handle is given to the read/write functions.
63 **
64 ** pcibus_ftag():
65 ** Creates a modified handle.
66 **
67 ** pcibus_read():
68 ** Read a long word from the pci configuration space.
69 ** Requires a tag (from pcitag) and the register
70 ** number (should be a long word alligned one).
71 **
72 ** pcibus_write():
73 ** Writes a long word to the pci configuration space.
74 ** Requires a tag (from pcitag), the register number
75 ** (should be a long word alligned one), and a value.
76 **
77 ** pcibus_regirq():
78 ** Register an interupt handler for a pci device.
79 ** Requires a tag (from pcitag), the register number
80 ** (should be a long word alligned one), and a value.
81 **
82 **-----------------------------------------------------------------
83 */
84
85 static int
86 pcibus_check (void);
87
88 static void
89 pcibus_setup (void);
90
91 static pcici_t
92 pcibus_tag (u_char bus, u_char device, u_char func);
93
94 static pcici_t
95 pcibus_ftag (pcici_t tag, u_char func);
96
97 static int
98 pcibus_bus (pcici_t tag);
99
100 static int
101 pcibus_device (pcici_t tag);
102
103 static int
104 pcibus_function (pcici_t tag);
105
106 static u_long
107 pcibus_read (pcici_t tag, u_long reg);
108
109 static void
110 pcibus_write (pcici_t tag, u_long reg, u_long data);
111
112 static int
113 pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr);
114
115 static int
116 pcibus_ihandler_detach (int irq, inthand2_t *func);
117
118 static int
119 pcibus_imask_include (int irq, unsigned* maskptr);
120
121 static int
122 pcibus_imask_exclude (int irq, unsigned* maskptr);
123
124 static struct pcibus i386pci = {
125 "pci",
126 pcibus_setup,
127 pcibus_tag,
128 pcibus_ftag,
129 pcibus_bus,
130 pcibus_device,
131 pcibus_function,
132 pcibus_read,
133 pcibus_write,
134 pcibus_ihandler_attach,
135 pcibus_ihandler_detach,
136 pcibus_imask_include,
137 pcibus_imask_exclude,
138 };
139
140 /*
141 ** Announce structure to generic driver
142 */
143
144 DATA_SET (pcibus_set, i386pci);
145
146 /*--------------------------------------------------------------------
147 **
148 ** Determine configuration mode
149 **
150 **--------------------------------------------------------------------
151 */
152
153
154 #define CONF1_ADDR_PORT 0x0cf8
155 #define CONF1_DATA_PORT 0x0cfc
156
157 #define CONF1_ENABLE 0x80000000ul
158 #define CONF1_ENABLE_CHK 0x80000000ul
159 #define CONF1_ENABLE_MSK 0x7ff00000ul
160 #define CONF1_ENABLE_CHK1 0xff000001ul
161 #define CONF1_ENABLE_MSK1 0x80000001ul
162 #define CONF1_ENABLE_RES1 0x80000000ul
163
164 #define CONF2_ENABLE_PORT 0x0cf8
165 #ifdef PC98
166 #define CONF2_FORWARD_PORT 0x0cf9
167 #else
168 #define CONF2_FORWARD_PORT 0x0cfa
169 #endif
170
171 #define CONF2_ENABLE_CHK 0x0e
172 #define CONF2_ENABLE_RES 0x0e
173
174 static int
175 pcibus_check (void)
176 {
177 u_char device;
178
179 if (bootverbose) printf ("pcibus_check:\tdevice ");
180
181 for (device = 0; device < pci_maxdevice; device++) {
182 unsigned long id;
183 if (bootverbose)
184 printf ("%d ", device);
185 id = pcibus_read (pcibus_tag (0,device,0), 0);
186 if (id && id != 0xfffffffful) {
187 if (bootverbose) printf ("is there (id=%08lx)\n", id);
188 return 1;
189 }
190 }
191 if (bootverbose)
192 printf ("-- nothing found\n");
193 return 0;
194 }
195
196 static void
197 pcibus_setup (void)
198 {
199 unsigned long mode1res,oldval1;
200 unsigned char mode2res,oldval2;
201
202 oldval1 = inl (CONF1_ADDR_PORT);
203
204 if (bootverbose) {
205 printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1);
206 }
207
208 /*---------------------------------------
209 ** Assume configuration mechanism 1 for now ...
210 **---------------------------------------
211 */
212
213 if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
214
215 pci_mechanism = 1;
216 pci_maxdevice = 32;
217
218 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
219 outb (CONF1_ADDR_PORT +3, 0);
220 mode1res = inl (CONF1_ADDR_PORT);
221 outl (CONF1_ADDR_PORT, oldval1);
222
223 if (bootverbose)
224 printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n",
225 mode1res, CONF1_ENABLE_CHK);
226
227 if (mode1res) {
228 if (pcibus_check())
229 return;
230 };
231
232 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
233 mode1res = inl(CONF1_ADDR_PORT);
234 outl (CONF1_ADDR_PORT, oldval1);
235
236 if (bootverbose)
237 printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n",
238 mode1res, CONF1_ENABLE_CHK1);
239
240 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
241 if (pcibus_check())
242 return;
243 };
244 }
245
246 /*---------------------------------------
247 ** Try configuration mechanism 2 ...
248 **---------------------------------------
249 */
250
251 oldval2 = inb (CONF2_ENABLE_PORT);
252
253 if (bootverbose) {
254 printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2);
255 }
256
257 if ((oldval2 & 0xf0) == 0) {
258
259 pci_mechanism = 2;
260 pci_maxdevice = 16;
261
262 outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
263 mode2res = inb(CONF2_ENABLE_PORT);
264 outb (CONF2_ENABLE_PORT, oldval2);
265
266 if (bootverbose)
267 printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n",
268 mode2res, CONF2_ENABLE_CHK);
269
270 if (mode2res == CONF2_ENABLE_RES) {
271 if (bootverbose)
272 printf ("pcibus_setup(2a):\tnow trying mechanism 2\n");
273
274 if (pcibus_check())
275 return;
276 }
277 }
278
279 /*---------------------------------------
280 ** No PCI bus host bridge found
281 **---------------------------------------
282 */
283
284 pci_mechanism = 0;
285 pci_maxdevice = 0;
286 }
287
288 /*--------------------------------------------------------------------
289 **
290 ** Build a pcitag from bus, device and function number
291 **
292 **--------------------------------------------------------------------
293 */
294
295 static pcici_t
296 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
297 {
298 pcici_t tag;
299
300 tag.cfg1 = 0;
301 if (func >= 8) return tag;
302
303 switch (pci_mechanism) {
304
305 case 1:
306 if (device < 32) {
307 tag.cfg1 = CONF1_ENABLE
308 | (((u_long) bus ) << 16ul)
309 | (((u_long) device) << 11ul)
310 | (((u_long) func ) << 8ul);
311 }
312 break;
313 case 2:
314 if (device < 16) {
315 tag.cfg2.port = 0xc000 | (device << 8ul);
316 tag.cfg2.enable = 0xf0 | (func << 1ul);
317 tag.cfg2.forward = bus;
318 }
319 break;
320 };
321 return tag;
322 }
323
324 static pcici_t
325 pcibus_ftag (pcici_t tag, u_char func)
326 {
327 switch (pci_mechanism) {
328
329 case 1:
330 tag.cfg1 &= ~0x700ul;
331 tag.cfg1 |= (((u_long) func) << 8ul);
332 break;
333 case 2:
334 tag.cfg2.enable = 0xf0 | (func << 1ul);
335 break;
336 };
337 return tag;
338 }
339
340 static int
341 pcibus_bus (pcici_t tag)
342 {
343 int bus;
344
345 bus = 0;
346 switch (pci_mechanism) {
347 case 1:
348 bus = (tag.cfg1 >> 16) & 0xff;
349 break;
350 case 2:
351 bus = tag.cfg2.forward;
352 break;
353 }
354 return (bus);
355 }
356
357 static int
358 pcibus_device (pcici_t tag)
359 {
360 int device;
361
362 device = 0;
363 switch (pci_mechanism) {
364 case 1:
365 device = (tag.cfg1 >> 11) & 0x1f;
366 break;
367 case 2:
368 device = (tag.cfg2.port >> 8) & 0x3f;
369 break;
370 }
371 return (device);
372 }
373
374 static int
375 pcibus_function (pcici_t tag)
376 {
377 int function;
378
379 function = 0;
380 switch (pci_mechanism) {
381 case 1:
382 function = (tag.cfg1 >> 8) & 0x7;
383 break;
384 case 2:
385 function = (tag.cfg2.enable >> 1) & 0x7;
386 break;
387 }
388 return (function);
389 }
390
391
392 /*--------------------------------------------------------------------
393 **
394 ** Read register from configuration space.
395 **
396 **--------------------------------------------------------------------
397 */
398
399 static u_long
400 pcibus_read (pcici_t tag, u_long reg)
401 {
402 u_long addr, data = 0;
403
404 if (!tag.cfg1) return (0xfffffffful);
405
406 switch (pci_mechanism) {
407
408 case 1:
409 addr = tag.cfg1 | (reg & 0xfc);
410 #ifdef PCI_DEBUG
411 printf ("pci_conf_read(1): addr=%x ", addr);
412 #endif
413 outl (CONF1_ADDR_PORT, addr);
414 data = inl (CONF1_DATA_PORT);
415 outl (CONF1_ADDR_PORT, 0 );
416 break;
417
418 case 2:
419 addr = tag.cfg2.port | (reg & 0xfc);
420 #ifdef PCI_DEBUG
421 printf ("pci_conf_read(2): addr=%x ", addr);
422 #endif
423 outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
424 outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
425
426 data = inl ((u_short) addr);
427
428 outb (CONF2_ENABLE_PORT, 0);
429 outb (CONF2_FORWARD_PORT, 0);
430 break;
431 };
432
433 #ifdef PCI_DEBUG
434 printf ("data=%x\n", data);
435 #endif
436
437 return (data);
438 }
439
440 /*--------------------------------------------------------------------
441 **
442 ** Write register into configuration space.
443 **
444 **--------------------------------------------------------------------
445 */
446
447 static void
448 pcibus_write (pcici_t tag, u_long reg, u_long data)
449 {
450 u_long addr;
451
452 if (!tag.cfg1) return;
453
454 switch (pci_mechanism) {
455
456 case 1:
457 addr = tag.cfg1 | (reg & 0xfc);
458 #ifdef PCI_DEBUG
459 printf ("pci_conf_write(1): addr=%x data=%x\n",
460 addr, data);
461 #endif
462 outl (CONF1_ADDR_PORT, addr);
463 outl (CONF1_DATA_PORT, data);
464 outl (CONF1_ADDR_PORT, 0 );
465 break;
466
467 case 2:
468 addr = tag.cfg2.port | (reg & 0xfc);
469 #ifdef PCI_DEBUG
470 printf ("pci_conf_write(2): addr=%x data=%x\n",
471 addr, data);
472 #endif
473 outb (CONF2_ENABLE_PORT, tag.cfg2.enable);
474 outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
475
476 outl ((u_short) addr, data);
477
478 outb (CONF2_ENABLE_PORT, 0);
479 outb (CONF2_FORWARD_PORT, 0);
480 break;
481 };
482 }
483
484 /*-----------------------------------------------------------------------
485 **
486 ** Register an interupt handler for a pci device.
487 **
488 **-----------------------------------------------------------------------
489 */
490
491 static int
492 pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr)
493 {
494 char buf[16];
495 char *cp;
496 int free_id, id, result;
497
498 sprintf(buf, "pci irq%d", irq);
499 for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) {
500 if (strcmp(cp, buf) == 0)
501 break;
502 if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0)
503 free_id = id;
504 while (*cp++ != '\0')
505 ;
506 }
507 if (id == NR_DEVICES) {
508 id = free_id;
509 if (id == 0) {
510 /*
511 * All pci irq counters are in use, perhaps because
512 * config is old so there aren't any. Abuse the
513 * clk0 counter.
514 */
515 printf (
516 "pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n",
517 irq);
518 }
519 }
520 result = register_intr(
521 irq, /* isa irq */
522 id, /* device id */
523 0, /* flags? */
524 func, /* handler */
525 maskptr, /* mask pointer */
526 arg); /* handler arg */
527
528 if (result) {
529 printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
530 return (result);
531 };
532 update_intr_masks();
533
534 INTREN ((1ul<<irq));
535 return (0);
536 }
537
538 static int
539 pcibus_ihandler_detach (int irq, inthand2_t *func)
540 {
541 int result;
542
543 INTRDIS ((1ul<<irq));
544
545 result = unregister_intr (irq, func);
546
547 if (result)
548 printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
549
550 update_intr_masks();
551
552 return (result);
553 }
554
555 static int
556 pcibus_imask_include (int irq, unsigned* maskptr)
557 {
558 unsigned mask;
559
560 if (!maskptr) return (0);
561
562 mask = 1ul << irq;
563
564 if (*maskptr & mask)
565 return (-1);
566
567 INTRMASK (*maskptr, mask);
568 update_intr_masks();
569
570 return (0);
571 }
572
573 static int
574 pcibus_imask_exclude (int irq, unsigned* maskptr)
575 {
576 unsigned mask;
577
578 if (!maskptr) return (0);
579
580 mask = 1ul << irq;
581
582 if (! (*maskptr & mask))
583 return (-1);
584
585 *maskptr &= ~mask;
586 update_intr_masks();
587
588 return (0);
589 }
Cache object: 74f664f9d7185bd6dbe236c3b8a945cc
|