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