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