xref: /freebsd/sys/i386/pci/pci_pir.c (revision bcd92649c9952c9c9e8845dbd34276a60dd16664)
1 /**************************************************************************
2 **
3 **  $Id: pcibus.c,v 1.26 1996/06/18 01:22:28 bde 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 u_long
98 pcibus_read (pcici_t tag, u_long reg);
99 
100 static void
101 pcibus_write (pcici_t tag, u_long reg, u_long data);
102 
103 static int
104 pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr);
105 
106 static int
107 pcibus_ihandler_detach (int irq, inthand2_t *func);
108 
109 static int
110 pcibus_imask_include (int irq, unsigned* maskptr);
111 
112 static int
113 pcibus_imask_exclude (int irq, unsigned* maskptr);
114 
115 static struct pcibus i386pci = {
116 	"pci",
117 	pcibus_setup,
118 	pcibus_tag,
119 	pcibus_ftag,
120 	pcibus_read,
121 	pcibus_write,
122 	ICU_LEN,
123 	pcibus_ihandler_attach,
124 	pcibus_ihandler_detach,
125 	pcibus_imask_include,
126 	pcibus_imask_exclude,
127 };
128 
129 /*
130 **	Announce structure to generic driver
131 */
132 
133 DATA_SET (pcibus_set, i386pci);
134 
135 /*--------------------------------------------------------------------
136 **
137 **      Determine configuration mode
138 **
139 **--------------------------------------------------------------------
140 */
141 
142 
143 #define CONF1_ADDR_PORT    0x0cf8
144 #define CONF1_DATA_PORT    0x0cfc
145 
146 #define CONF1_ENABLE       0x80000000ul
147 #define CONF1_ENABLE_CHK   0x80000000ul
148 #define CONF1_ENABLE_MSK   0x7ff00000ul
149 #define CONF1_ENABLE_CHK1  0xff000001ul
150 #define CONF1_ENABLE_MSK1  0x80000001ul
151 #define CONF1_ENABLE_RES1  0x80000000ul
152 
153 #define CONF2_ENABLE_PORT  0x0cf8
154 #ifdef PC98
155 #define CONF2_FORWARD_PORT 0x0cf9
156 #else
157 #define CONF2_FORWARD_PORT 0x0cfa
158 #endif
159 
160 #define CONF2_ENABLE_CHK   0x0e
161 #define CONF2_ENABLE_RES   0x0e
162 
163 static int
164 pcibus_check (void)
165 {
166 	u_char device;
167 
168 	if (bootverbose) printf ("pcibus_check:\tdevice ");
169 
170 	for (device = 0; device < pci_maxdevice; device++) {
171 		unsigned long id;
172 		if (bootverbose)
173 			printf ("%d ", device);
174 		id = pcibus_read (pcibus_tag (0,device,0), 0);
175 		if (id && id != 0xfffffffful) {
176 			if (bootverbose) printf ("is there (id=%08lx)\n", id);
177 			return 1;
178 		}
179 	}
180 	if (bootverbose)
181 		printf ("-- nothing found\n");
182 	return 0;
183 }
184 
185 static void
186 pcibus_setup (void)
187 {
188 	unsigned long mode1res,oldval1;
189 	unsigned char mode2res,oldval2;
190 
191 	oldval1 = inl (CONF1_ADDR_PORT);
192 
193 	if (bootverbose) {
194 		printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1);
195 	}
196 
197 	/*---------------------------------------
198 	**      Assume configuration mechanism 1 for now ...
199 	**---------------------------------------
200 	*/
201 
202 	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
203 
204 		pci_mechanism = 1;
205 		pci_maxdevice = 32;
206 
207 		outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
208 		outb (CONF1_ADDR_PORT +3, 0);
209 		mode1res = inl (CONF1_ADDR_PORT);
210 		outl (CONF1_ADDR_PORT, oldval1);
211 
212 		if (bootverbose)
213 		    printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n",
214 			    mode1res, CONF1_ENABLE_CHK);
215 
216 		if (mode1res) {
217 			if (pcibus_check())
218 				return;
219 		};
220 
221 		outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
222 		mode1res = inl(CONF1_ADDR_PORT);
223 		outl (CONF1_ADDR_PORT, oldval1);
224 
225 		if (bootverbose)
226 		    printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n",
227 			    mode1res, CONF1_ENABLE_CHK1);
228 
229 		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
230 			if (pcibus_check())
231 				return;
232 		};
233 	}
234 
235 	/*---------------------------------------
236 	**      Try configuration mechanism 2 ...
237 	**---------------------------------------
238 	*/
239 
240 	oldval2 = inb (CONF2_ENABLE_PORT);
241 
242 	if (bootverbose) {
243 		printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2);
244 	}
245 
246 	if ((oldval2 & 0xf0) == 0) {
247 
248 		pci_mechanism = 2;
249 		pci_maxdevice = 16;
250 
251 		outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
252 		mode2res = inb(CONF2_ENABLE_PORT);
253 		outb (CONF2_ENABLE_PORT, oldval2);
254 
255 		if (bootverbose)
256 		    printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n",
257 			    mode2res, CONF2_ENABLE_CHK);
258 
259 		if (mode2res == CONF2_ENABLE_RES) {
260 		    if (bootverbose)
261 			printf ("pcibus_setup(2a):\tnow trying mechanism 2\n");
262 
263 			if (pcibus_check())
264 				return;
265 		}
266 	}
267 
268 	/*---------------------------------------
269 	**      No PCI bus host bridge found
270 	**---------------------------------------
271 	*/
272 
273 	pci_mechanism = 0;
274 	pci_maxdevice = 0;
275 }
276 
277 /*--------------------------------------------------------------------
278 **
279 **      Build a pcitag from bus, device and function number
280 **
281 **--------------------------------------------------------------------
282 */
283 
284 static pcici_t
285 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
286 {
287 	pcici_t tag;
288 
289 	tag.cfg1 = 0;
290 	if (func   >=  8) return tag;
291 
292 	switch (pci_mechanism) {
293 
294 	case 1:
295 		if (device < 32) {
296 			tag.cfg1 = CONF1_ENABLE
297 				| (((u_long) bus   ) << 16ul)
298 				| (((u_long) device) << 11ul)
299 				| (((u_long) func  ) <<  8ul);
300 		}
301 		break;
302 	case 2:
303 		if (device < 16) {
304 			tag.cfg2.port    = 0xc000 | (device << 8ul);
305 			tag.cfg2.enable  = 0xf0 | (func << 1ul);
306 			tag.cfg2.forward = bus;
307 		}
308 		break;
309 	};
310 	return tag;
311 }
312 
313 static pcici_t
314 pcibus_ftag (pcici_t tag, u_char func)
315 {
316 	switch (pci_mechanism) {
317 
318 	case 1:
319 		tag.cfg1 &= ~0x700ul;
320 		tag.cfg1 |= (((u_long) func) << 8ul);
321 		break;
322 	case 2:
323 		tag.cfg2.enable  = 0xf0 | (func << 1ul);
324 		break;
325 	};
326 	return tag;
327 }
328 
329 /*--------------------------------------------------------------------
330 **
331 **      Read register from configuration space.
332 **
333 **--------------------------------------------------------------------
334 */
335 
336 static u_long
337 pcibus_read (pcici_t tag, u_long reg)
338 {
339 	u_long addr, data = 0;
340 
341 	if (!tag.cfg1) return (0xfffffffful);
342 
343 	switch (pci_mechanism) {
344 
345 	case 1:
346 		addr = tag.cfg1 | (reg & 0xfc);
347 #ifdef PCI_DEBUG
348 		printf ("pci_conf_read(1): addr=%x ", addr);
349 #endif
350 		outl (CONF1_ADDR_PORT, addr);
351 		data = inl (CONF1_DATA_PORT);
352 		outl (CONF1_ADDR_PORT, 0   );
353 		break;
354 
355 	case 2:
356 		addr = tag.cfg2.port | (reg & 0xfc);
357 #ifdef PCI_DEBUG
358 		printf ("pci_conf_read(2): addr=%x ", addr);
359 #endif
360 		outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
361 		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
362 
363 		data = inl ((u_short) addr);
364 
365 		outb (CONF2_ENABLE_PORT,  0);
366 		outb (CONF2_FORWARD_PORT, 0);
367 		break;
368 	};
369 
370 #ifdef PCI_DEBUG
371 	printf ("data=%x\n", data);
372 #endif
373 
374 	return (data);
375 }
376 
377 /*--------------------------------------------------------------------
378 **
379 **      Write register into configuration space.
380 **
381 **--------------------------------------------------------------------
382 */
383 
384 static void
385 pcibus_write (pcici_t tag, u_long reg, u_long data)
386 {
387 	u_long addr;
388 
389 	if (!tag.cfg1) return;
390 
391 	switch (pci_mechanism) {
392 
393 	case 1:
394 		addr = tag.cfg1 | (reg & 0xfc);
395 #ifdef PCI_DEBUG
396 		printf ("pci_conf_write(1): addr=%x data=%x\n",
397 			addr, data);
398 #endif
399 		outl (CONF1_ADDR_PORT, addr);
400 		outl (CONF1_DATA_PORT, data);
401 		outl (CONF1_ADDR_PORT,   0 );
402 		break;
403 
404 	case 2:
405 		addr = tag.cfg2.port | (reg & 0xfc);
406 #ifdef PCI_DEBUG
407 		printf ("pci_conf_write(2): addr=%x data=%x\n",
408 			addr, data);
409 #endif
410 		outb (CONF2_ENABLE_PORT,  tag.cfg2.enable);
411 		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
412 
413 		outl ((u_short) addr, data);
414 
415 		outb (CONF2_ENABLE_PORT,  0);
416 		outb (CONF2_FORWARD_PORT, 0);
417 		break;
418 	};
419 }
420 
421 /*-----------------------------------------------------------------------
422 **
423 **	Register an interupt handler for a pci device.
424 **
425 **-----------------------------------------------------------------------
426 */
427 
428 static int
429 pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr)
430 {
431 	char buf[16];
432 	char *cp;
433 	int free_id, id, result;
434 
435 	sprintf(buf, "pci irq%d", irq);
436 	for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) {
437 		if (strcmp(cp, buf) == 0)
438 			break;
439 		if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0)
440 			free_id = id;
441 		while (*cp++ != '\0')
442 			;
443 	}
444 	if (id == NR_DEVICES) {
445 		id = free_id;
446 		if (id == 0) {
447 			/*
448 			 * All pci irq counters are in use, perhaps because
449 			 * config is old so there aren't any.  Abuse the
450 			 * clk0 counter.
451 			 */
452 			printf (
453 		"pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n",
454 				irq);
455 		}
456 	}
457 	result = register_intr(
458 		irq,		    /* isa irq	    */
459 		id,		    /* device id    */
460 		0,		    /* flags?	    */
461 		func,		    /* handler	    */
462 		maskptr,	    /* mask pointer */
463 		arg);		    /* handler arg  */
464 
465 	if (result) {
466 		printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
467 		return (result);
468 	};
469 	update_intr_masks();
470 
471 	INTREN ((1ul<<irq));
472 	return (0);
473 }
474 
475 static int
476 pcibus_ihandler_detach (int irq, inthand2_t *func)
477 {
478 	int result;
479 
480 	INTRDIS ((1ul<<irq));
481 
482 	result = unregister_intr (irq, func);
483 
484 	if (result)
485 		printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
486 
487 	update_intr_masks();
488 
489 	return (result);
490 }
491 
492 static int
493 pcibus_imask_include (int irq, unsigned* maskptr)
494 {
495 	unsigned mask;
496 
497 	if (!maskptr) return (0);
498 
499 	mask = 1ul << irq;
500 
501 	if (*maskptr & mask)
502 		return (-1);
503 
504 	INTRMASK (*maskptr, mask);
505 	update_intr_masks();
506 
507 	return (0);
508 }
509 
510 static int
511 pcibus_imask_exclude (int irq, unsigned* maskptr)
512 {
513 	unsigned mask;
514 
515 	if (!maskptr) return (0);
516 
517 	mask = 1ul << irq;
518 
519 	if (! (*maskptr & mask))
520 		return (-1);
521 
522 	*maskptr &= ~mask;
523 	update_intr_masks();
524 
525 	return (0);
526 }
527