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