xref: /freebsd/sys/i386/pci/pci_pir.c (revision 953a3198a35204535cc9d450f04da982a4fea59b)
1 /**************************************************************************
2 **
3 **  $Id: pcibus.c,v 1.15 1995/09/22 19:10:54 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 <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 
42 #include <machine/cpu.h> /* bootverbose */
43 
44 #include <i386/isa/icu.h>
45 #include <i386/isa/isa.h>
46 #include <i386/isa/isa_device.h>
47 
48 #include <pci/pcivar.h>
49 #include <pci/pcireg.h>
50 #include <pci/pcibus.h>
51 
52 /*-----------------------------------------------------------------
53 **
54 **	The following functions are provided by the pci bios.
55 **	They are used only by the pci configuration.
56 **
57 **	pcibus_setup():
58 **		Probes for a pci system.
59 **		Sets pci_maxdevice and pci_mechanism.
60 **
61 **	pcibus_tag():
62 **		Creates a handle for pci configuration space access.
63 **		This handle is given to the read/write functions.
64 **
65 **	pcibus_ftag():
66 **		Creates a modified handle.
67 **
68 **	pcibus_read():
69 **		Read a long word from the pci configuration space.
70 **		Requires a tag (from pcitag) and the register
71 **		number (should be a long word alligned one).
72 **
73 **	pcibus_write():
74 **		Writes a long word to the pci configuration space.
75 **		Requires a tag (from pcitag), the register number
76 **		(should be a long word alligned one), and a value.
77 **
78 **	pcibus_regirq():
79 **		Register an interupt handler for a pci device.
80 **		Requires a tag (from pcitag), the register number
81 **		(should be a long word alligned one), and a value.
82 **
83 **-----------------------------------------------------------------
84 */
85 
86 static int
87 pcibus_check (void);
88 
89 static void
90 pcibus_setup (void);
91 
92 static pcici_t
93 pcibus_tag (u_char bus, u_char device, u_char func);
94 
95 static pcici_t
96 pcibus_ftag (pcici_t tag, u_char func);
97 
98 static u_long
99 pcibus_read (pcici_t tag, u_long reg);
100 
101 static void
102 pcibus_write (pcici_t tag, u_long reg, u_long data);
103 
104 static int
105 pcibus_ihandler_attach (int irq, void(*ihandler)(), int arg, unsigned* maskp);
106 
107 static int
108 pcibus_ihandler_detach (int irq, void(*handler)());
109 
110 static int
111 pcibus_imask_include (int irq, unsigned* maskptr);
112 
113 static int
114 pcibus_imask_exclude (int irq, unsigned* maskptr);
115 
116 struct pcibus i386pci = {
117 	"pci",
118 	pcibus_setup,
119 	pcibus_tag,
120 	pcibus_ftag,
121 	pcibus_read,
122 	pcibus_write,
123 	ICU_LEN,
124 	pcibus_ihandler_attach,
125 	pcibus_ihandler_detach,
126 	pcibus_imask_include,
127 	pcibus_imask_exclude,
128 };
129 
130 /*
131 **	Announce structure to generic driver
132 */
133 
134 DATA_SET (pcibus_set, i386pci);
135 
136 /*--------------------------------------------------------------------
137 **
138 **      Determine configuration mode
139 **
140 **--------------------------------------------------------------------
141 */
142 
143 
144 #define CONF1_ADDR_PORT    0x0cf8
145 #define CONF1_DATA_PORT    0x0cfc
146 
147 #define CONF1_ENABLE       0x80000000ul
148 #define CONF1_ENABLE_CHK   0x80000000ul
149 #define CONF1_ENABLE_CHK1  0xFF000001ul
150 #define CONF1_ENABLE_MSK1  0x80000000ul
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 != 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,oldval;
185 	unsigned char mode2res;
186 
187 	oldval = inl (CONF1_ADDR_PORT);
188 	outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
189 	outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
190 	mode1res = inl(CONF1_ADDR_PORT);
191 	mode2res = inb(CONF2_ENABLE_PORT);
192 	outb (CONF2_ENABLE_PORT, 0);
193 	outl (CONF1_ADDR_PORT, oldval);
194 
195 	if (bootverbose) {
196 		printf ("pcibus_setup(1):\tmode1res=0x%08lx (0x%08lx), "
197 			"mode2res=0x%02x (0x%02x)\n",
198 			mode1res,CONF1_ENABLE_CHK,
199 			(int)mode2res,CONF2_ENABLE_CHK);
200 	}
201 
202 	/*---------------------------------------
203 	**	No PCI, if neither mode1res nor mode2res could be read back
204 	**---------------------------------------
205 	*/
206 
207 	if ((mode1res != CONF1_ENABLE_CHK) && (mode2res != CONF2_ENABLE_CHK)) {
208 		return;
209 	}
210 
211 	/*---------------------------------------
212 	**      Assume configuration mechanism 1 for now ...
213 	**---------------------------------------
214 	*/
215 
216 	pci_mechanism = 1;
217 	pci_maxdevice = 32;
218 
219 	outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
220 	outb (CONF1_ADDR_PORT +3, 0);
221 	mode1res = inl (CONF1_ADDR_PORT);
222 	outl (CONF1_ADDR_PORT, oldval);
223 
224 	if (bootverbose)
225 		printf ("pcibus_setup(2):\tmode1res=0x%08lx (0x%08lx)\n",
226 			mode1res, CONF1_ENABLE_CHK);
227 
228 	if (mode1res) {
229 		if (pcibus_check())
230 			return;
231 	};
232 
233 	outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
234 	outl (CONF1_DATA_PORT, 0);
235 	mode1res = inl(CONF1_ADDR_PORT);
236 	outl (CONF1_ADDR_PORT, oldval);
237 
238 	if (bootverbose)
239 		printf ("pcibus_setup(3):\tmode1res=0x%08lx (0x%08lx)\n",
240 			mode1res, CONF1_ENABLE_CHK1);
241 
242 	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
243 		if (pcibus_check())
244 			return;
245 	};
246 
247 	/*---------------------------------------
248 	**      Try configuration mechanism 2 ...
249 	**---------------------------------------
250 	*/
251 
252 	if (bootverbose)
253 		printf ("pcibus_setup(4):\tnow trying mechanism 2\n");
254 
255 	pci_mechanism = 2;
256 	pci_maxdevice = 16;
257 
258 	if (pcibus_check())
259 	    return;
260 
261 	/*---------------------------------------
262 	**      No PCI bus host bridge found
263 	**---------------------------------------
264 	*/
265 
266 	pci_mechanism = 0;
267 	pci_maxdevice = 0;
268 }
269 
270 /*--------------------------------------------------------------------
271 **
272 **      Build a pcitag from bus, device and function number
273 **
274 **--------------------------------------------------------------------
275 */
276 
277 static pcici_t
278 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
279 {
280 	pcici_t tag;
281 
282 	tag.cfg1 = 0;
283 	if (func   >=  8) return tag;
284 
285 	switch (pci_mechanism) {
286 
287 	case 1:
288 		if (device < 32) {
289 			tag.cfg1 = CONF1_ENABLE
290 				| (((u_long) bus   ) << 16ul)
291 				| (((u_long) device) << 11ul)
292 				| (((u_long) func  ) <<  8ul);
293 		}
294 		break;
295 	case 2:
296 		if (device < 16) {
297 			tag.cfg2.port    = 0xc000 | (device << 8ul);
298 			tag.cfg2.enable  = 0xf0 | (func << 1ul);
299 			tag.cfg2.forward = bus;
300 		}
301 		break;
302 	};
303 	return tag;
304 }
305 
306 static pcici_t
307 pcibus_ftag (pcici_t tag, u_char func)
308 {
309 	switch (pci_mechanism) {
310 
311 	case 1:
312 		tag.cfg1 &= ~0x700ul;
313 		tag.cfg1 |= (((u_long) func) << 8ul);
314 		break;
315 	case 2:
316 		tag.cfg2.enable  = 0xf0 | (func << 1ul);
317 		break;
318 	};
319 	return tag;
320 }
321 
322 /*--------------------------------------------------------------------
323 **
324 **      Read register from configuration space.
325 **
326 **--------------------------------------------------------------------
327 */
328 
329 static u_long
330 pcibus_read (pcici_t tag, u_long reg)
331 {
332 	u_long addr, data = 0;
333 
334 	if (!tag.cfg1) return (0xfffffffful);
335 
336 	switch (pci_mechanism) {
337 
338 	case 1:
339 		addr = tag.cfg1 | (reg & 0xfc);
340 #ifdef PCI_DEBUG
341 		printf ("pci_conf_read(1): addr=%x ", addr);
342 #endif
343 		outl (CONF1_ADDR_PORT, addr);
344 		data = inl (CONF1_DATA_PORT);
345 		outl (CONF1_ADDR_PORT, 0   );
346 		break;
347 
348 	case 2:
349 		addr = tag.cfg2.port | (reg & 0xfc);
350 #ifdef PCI_DEBUG
351 		printf ("pci_conf_read(2): addr=%x ", addr);
352 #endif
353 		outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
354 		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
355 
356 		data = inl ((u_short) addr);
357 
358 		outb (CONF2_ENABLE_PORT,  0);
359 		outb (CONF2_FORWARD_PORT, 0);
360 		break;
361 	};
362 
363 #ifdef PCI_DEBUG
364 	printf ("data=%x\n", data);
365 #endif
366 
367 	return (data);
368 }
369 
370 /*--------------------------------------------------------------------
371 **
372 **      Write register into configuration space.
373 **
374 **--------------------------------------------------------------------
375 */
376 
377 static void
378 pcibus_write (pcici_t tag, u_long reg, u_long data)
379 {
380 	u_long addr;
381 
382 	if (!tag.cfg1) return;
383 
384 	switch (pci_mechanism) {
385 
386 	case 1:
387 		addr = tag.cfg1 | (reg & 0xfc);
388 #ifdef PCI_DEBUG
389 		printf ("pci_conf_write(1): addr=%x data=%x\n",
390 			addr, data);
391 #endif
392 		outl (CONF1_ADDR_PORT, addr);
393 		outl (CONF1_DATA_PORT, data);
394 		outl (CONF1_ADDR_PORT,   0 );
395 		break;
396 
397 	case 2:
398 		addr = tag.cfg2.port | (reg & 0xfc);
399 #ifdef PCI_DEBUG
400 		printf ("pci_conf_write(2): addr=%x data=%x\n",
401 			addr, data);
402 #endif
403 		outb (CONF2_ENABLE_PORT,  tag.cfg2.enable);
404 		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
405 
406 		outl ((u_short) addr, data);
407 
408 		outb (CONF2_ENABLE_PORT,  0);
409 		outb (CONF2_FORWARD_PORT, 0);
410 		break;
411 	};
412 }
413 
414 /*-----------------------------------------------------------------------
415 **
416 **	Register an interupt handler for a pci device.
417 **
418 **-----------------------------------------------------------------------
419 */
420 
421 static int
422 pcibus_ihandler_attach (int irq, void(*func)(), int arg, unsigned * maskptr)
423 {
424 	int result;
425 	result = register_intr(
426 		irq,		    /* isa irq	    */
427 		0,		    /* deviced??    */
428 		0,		    /* flags?	    */
429 		(inthand2_t*) func, /* handler	    */
430 		maskptr,	    /* mask pointer */
431 		arg);		    /* handler arg  */
432 
433 	if (result) {
434 		printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
435 		return (result);
436 	};
437 	update_intr_masks();
438 
439 	INTREN ((1ul<<irq));
440 	return (0);
441 }
442 
443 static int
444 pcibus_ihandler_detach (int irq, void(*func)())
445 {
446 	int result;
447 
448 	INTRDIS ((1ul<<irq));
449 
450 	result = unregister_intr (irq, (inthand2_t*) func);
451 
452 	if (result)
453 		printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
454 
455 	update_intr_masks();
456 
457 	return (result);
458 }
459 
460 static int
461 pcibus_imask_include (int irq, unsigned* maskptr)
462 {
463 	unsigned mask;
464 
465 	if (!maskptr) return (0);
466 
467 	mask = 1ul << irq;
468 
469 	if (*maskptr & mask)
470 		return (-1);
471 
472 	INTRMASK (*maskptr, mask);
473 	update_intr_masks();
474 
475 	return (0);
476 }
477 
478 static int
479 pcibus_imask_exclude (int irq, unsigned* maskptr)
480 {
481 	unsigned mask;
482 
483 	if (!maskptr) return (0);
484 
485 	mask = 1ul << irq;
486 
487 	if (! (*maskptr & mask))
488 		return (-1);
489 
490 	*maskptr &= ~mask;
491 	update_intr_masks();
492 
493 	return (0);
494 }
495