xref: /freebsd/sys/amd64/pci/pci_cfgreg.c (revision 17ee9d00bc1ae1e598c38f25826f861e4bc6c3ce)
1 /**************************************************************************
2 **
3 **  $Id: pcibus.c,v 1.1 1995/02/01 23:06:58 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 #ifndef __FreeBSD2__
39 #if __FreeBSD__ >= 2
40 #define __FreeBSD2__
41 #endif
42 #endif
43 
44 #ifdef __FreeBSD2__
45 #define HAS_CPUFUNC_H
46 #endif
47 
48 #include <types.h>
49 #include <param.h>
50 #include <kernel.h>
51 #include <i386/isa/isa.h>
52 #include <i386/isa/isa_device.h>
53 #include <i386/isa/icu.h>
54 
55 #ifdef HAS_CPUFUNC_H
56 #include <i386/include/cpufunc.h>
57 #endif
58 
59 #include <pci/pcivar.h>
60 #include <pci/pcireg.h>
61 #include <pci/pcibus.h>
62 
63 extern int printf();
64 
65 static char pci_mode;
66 
67 /*-----------------------------------------------------------------
68 **
69 **	The following functions are provided by the pci bios.
70 **	They are used only by the pci configuration.
71 **
72 **	pcibus_mode():
73 **		Probes for a pci system.
74 **		Returns 1 or 2 for pci configuration mechanism.
75 **		Returns 0 if no pci system.
76 **
77 **	pcibus_tag():
78 **		Gets a handle for accessing the pci configuration
79 **		space.
80 **		This handle is given to the mapping functions (see
81 **		above) or to the read/write functions.
82 **
83 **	pcibus_read():
84 **		Read a long word from the pci configuration space.
85 **		Requires a tag (from pcitag) and the register
86 **		number (should be a long word alligned one).
87 **
88 **	pcibus_write():
89 **		Writes a long word to the pci configuration space.
90 **		Requires a tag (from pcitag), the register number
91 **		(should be a long word alligned one), and a value.
92 **
93 **	pcibus_regirq():
94 **		Register an interupt handler for a pci device.
95 **		Requires a tag (from pcitag), the register number
96 **		(should be a long word alligned one), and a value.
97 **
98 **-----------------------------------------------------------------
99 */
100 
101 static int
102 pcibus_mode (void);
103 
104 static pcici_t
105 pcibus_tag (u_char bus, u_char device, u_char func);
106 
107 static u_long
108 pcibus_read (pcici_t tag, u_long reg);
109 
110 static void
111 pcibus_write (pcici_t tag, u_long reg, u_long data);
112 
113 static int
114 pcibus_regint (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr);
115 
116 struct pcibus i386pci = {
117 	"pci",
118 	pcibus_mode,
119 	pcibus_tag,
120 	pcibus_read,
121 	pcibus_write,
122 	pcibus_regint,
123 };
124 
125 /*
126 **	Announce structure to generic driver
127 */
128 
129 DATA_SET (pcibus_set, i386pci);
130 
131 /*--------------------------------------------------------------------
132 **
133 **      Port access
134 **
135 **--------------------------------------------------------------------
136 */
137 
138 #ifndef HAS_CPUFUNC_H
139 
140 #undef inl
141 #define inl(port) \
142 ({ u_long data; \
143 	__asm __volatile("inl %1, %0": "=a" (data): "d" ((u_short)(port))); \
144 	data; })
145 
146 
147 #undef outl
148 #define outl(port, data) \
149 {__asm __volatile("outl %0, %1"::"a" ((u_long)(data)), "d" ((u_short)(port)));}
150 
151 
152 #undef inb
153 #define inb(port) \
154 ({ u_char data; \
155 	__asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)(port))); \
156 	data; })
157 
158 
159 #undef outb
160 #define outb(port, data) \
161 {__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)(port)));}
162 
163 #endif /* HAS_CPUFUNC_H */
164 
165 /*--------------------------------------------------------------------
166 **
167 **      Determine configuration mode
168 **
169 **--------------------------------------------------------------------
170 */
171 
172 
173 #define CONF1_ENABLE       0x80000000ul
174 #define CONF1_ADDR_PORT    0x0cf8
175 #define CONF1_DATA_PORT    0x0cfc
176 
177 
178 #define CONF2_ENABLE_PORT  0x0cf8
179 #define CONF2_FORWARD_PORT 0x0cfa
180 
181 
182 static int
183 pcibus_mode (void)
184 {
185 #ifdef PCI_CONF_MODE
186 	return (PCI_CONF_MODE)
187 #else /* PCI_CONF_MODE */
188 	u_long result, oldval;
189 
190 	/*---------------------------------------
191 	**      Configuration mode 2 ?
192 	**---------------------------------------
193 	*/
194 
195 	outb (CONF2_ENABLE_PORT,  0);
196 	outb (CONF2_FORWARD_PORT, 0);
197 	if (!inb (CONF2_ENABLE_PORT) && !inb (CONF2_FORWARD_PORT)) {
198 		pci_mode = 2;
199 		return (2);
200 	};
201 
202 	/*---------------------------------------
203 	**      Configuration mode 1 ?
204 	**---------------------------------------
205 	*/
206 
207 	oldval = inl (CONF1_ADDR_PORT);
208 	outl (CONF1_ADDR_PORT, CONF1_ENABLE);
209 	result = inl (CONF1_ADDR_PORT);
210 	outl (CONF1_ADDR_PORT, oldval);
211 
212 	if (result == CONF1_ENABLE) {
213 		pci_mode = 1;
214 		return (1);
215 	};
216 
217 	/*---------------------------------------
218 	**      No PCI bus available.
219 	**---------------------------------------
220 	*/
221 	return (0);
222 #endif /* PCI_CONF_MODE */
223 }
224 
225 /*--------------------------------------------------------------------
226 **
227 **      Build a pcitag from bus, device and function number
228 **
229 **--------------------------------------------------------------------
230 */
231 
232 static pcici_t
233 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
234 {
235 	pcici_t tag;
236 
237 	tag.cfg1 = 0;
238 	if (device >= 32) return tag;
239 	if (func   >=  8) return tag;
240 
241 	switch (pci_mode) {
242 
243 	case 1:
244 		tag.cfg1 = CONF1_ENABLE
245 			| (((u_long) bus   ) << 16ul)
246 			| (((u_long) device) << 11ul)
247 			| (((u_long) func  ) <<  8ul);
248 		break;
249 	case 2:
250 		if (device >= 16) break;
251 		tag.cfg2.port    = 0xc000 | (device << 8ul);
252 		tag.cfg2.enable  = 0xf1 | (func << 1ul);
253 		tag.cfg2.forward = bus;
254 		break;
255 	};
256 	return tag;
257 }
258 
259 /*--------------------------------------------------------------------
260 **
261 **      Read register from configuration space.
262 **
263 **--------------------------------------------------------------------
264 */
265 
266 
267 static u_long
268 pcibus_read (pcici_t tag, u_long reg)
269 {
270 	u_long addr, data = 0;
271 
272 	if (!tag.cfg1) return (0xfffffffful);
273 
274 	switch (pci_mode) {
275 
276 	case 1:
277 		addr = tag.cfg1 | (reg & 0xfc);
278 #ifdef PCI_DEBUG
279 		printf ("pci_conf_read(1): addr=%x ", addr);
280 #endif
281 		outl (CONF1_ADDR_PORT, addr);
282 		data = inl (CONF1_DATA_PORT);
283 		outl (CONF1_ADDR_PORT, 0   );
284 		break;
285 
286 	case 2:
287 		addr = tag.cfg2.port | (reg & 0xfc);
288 #ifdef PCI_DEBUG
289 		printf ("pci_conf_read(2): addr=%x ", addr);
290 #endif
291 		outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
292 		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
293 
294 		data = inl ((u_short) addr);
295 
296 		outb (CONF2_ENABLE_PORT,  0);
297 		outb (CONF2_FORWARD_PORT, 0);
298 		break;
299 	};
300 
301 #ifdef PCI_DEBUG
302 	printf ("data=%x\n", data);
303 #endif
304 
305 	return (data);
306 }
307 
308 /*--------------------------------------------------------------------
309 **
310 **      Write register into configuration space.
311 **
312 **--------------------------------------------------------------------
313 */
314 
315 
316 static void
317 pcibus_write (pcici_t tag, u_long reg, u_long data)
318 {
319 	u_long addr;
320 
321 	if (!tag.cfg1) return;
322 
323 	switch (pci_mode) {
324 
325 	case 1:
326 		addr = tag.cfg1 | (reg & 0xfc);
327 #ifdef PCI_DEBUG
328 		printf ("pci_conf_write(1): addr=%x data=%x\n",
329 			addr, data);
330 #endif
331 		outl (CONF1_ADDR_PORT, addr);
332 		outl (CONF1_DATA_PORT, data);
333 		outl (CONF1_ADDR_PORT,   0 );
334 		break;
335 
336 	case 2:
337 		addr = tag.cfg2.port | (reg & 0xfc);
338 #ifdef PCI_DEBUG
339 		printf ("pci_conf_write(2): addr=%x data=%x\n",
340 			addr, data);
341 #endif
342 		outb (CONF2_ENABLE_PORT,  tag.cfg2.enable);
343 		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
344 
345 		outl ((u_short) addr, data);
346 
347 		outb (CONF2_ENABLE_PORT,  0);
348 		outb (CONF2_FORWARD_PORT, 0);
349 		break;
350 	};
351 }
352 
353 /*-----------------------------------------------------------------------
354 **
355 **	Register an interupt handler for a pci device.
356 **
357 **-----------------------------------------------------------------------
358 */
359 
360 #ifndef __FreeBSD2__
361 /*
362  * Type of the first (asm) part of an interrupt handler.
363  */
364 typedef void inthand_t __P((u_int cs, u_int ef, u_int esp, u_int ss));
365 
366 /*
367  * Usual type of the second (C) part of an interrupt handler.  Some bogus
368  * ones need the arg to be the interrupt frame (and not a copy of it, which
369  * is all that is possible in C).
370  */
371 typedef void inthand2_t __P((int unit));
372 
373 /*
374 **	XXX	@FreeBSD2@
375 **
376 **	Unfortunately, the mptr argument is _no_ pointer in 2.0 FreeBSD.
377 **	We would prefer a pointer because it enables us to install
378 **	new interrupt handlers at any time.
379 **	(This is just going to be changed ... <se> :)
380 **	In 2.0 FreeBSD later installed interrupt handlers may change
381 **	the xyz_imask, but this would not be recognized by handlers
382 **	which are installed before.
383 */
384 
385 static int
386 register_intr __P((int intr, int device_id, unsigned int flags,
387 		       inthand2_t *handler, unsigned int * mptr, int unit));
388 extern unsigned intr_mask[ICU_LEN];
389 
390 #endif /* !__FreeBSD2__ */
391 static	unsigned int	pci_int_mask [16];
392 
393 int pcibus_regint (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr)
394 {
395 	int irq;
396 	unsigned mask, oldmask;
397 
398 	irq = PCI_INTERRUPT_LINE_EXTRACT(
399 		pci_conf_read (tag, PCI_INTERRUPT_REG));
400 
401 	mask = 1ul << irq;
402 
403 	if (!maskptr)
404 		maskptr = &pci_int_mask[irq];
405 	oldmask = *maskptr;
406 
407 	INTRMASK (*maskptr, mask);
408 
409 	register_intr(
410 		irq,		/* isa irq	*/
411 		0,		/* deviced??	*/
412 		0,		/* flags?	*/
413 		(inthand2_t*) func, /* handler	*/
414 		maskptr,	/* mask pointer	*/
415 		(int) arg);	/* handler arg	*/
416 
417 #ifdef __FreeBSD2__
418 	/*
419 	**	XXX See comment at beginning of file.
420 	**
421 	**	Have to update all the interrupt masks ... Grrrrr!!!
422 	*/
423 	{
424 		unsigned * mp = &intr_mask[0];
425 		/*
426 		**	update the isa interrupt masks.
427 		*/
428 		for (mp=&intr_mask[0]; mp<&intr_mask[ICU_LEN]; mp++)
429 			if ((~*mp & oldmask)==0)
430 				*mp |= mask;
431 		/*
432 		**	update the pci interrupt masks.
433 		*/
434 		for (mp=&pci_int_mask[0]; mp<&pci_int_mask[16]; mp++)
435 			if ((~*mp & oldmask)==0)
436 				*mp |= mask;
437 	};
438 #endif
439 
440 	INTREN (mask);
441 
442 	return (1);
443 }
444