pci_cfgreg.c revision 6281
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
63extern int printf();
64
65static 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
101static int
102pcibus_mode (void);
103
104static pcici_t
105pcibus_tag (u_char bus, u_char device, u_char func);
106
107static u_long
108pcibus_read (pcici_t tag, u_long reg);
109
110static void
111pcibus_write (pcici_t tag, u_long reg, u_long data);
112
113static int
114pcibus_regint (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr);
115
116struct 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
129DATA_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
182static int
183pcibus_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
232static pcici_t
233pcibus_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
267static u_long
268pcibus_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
316static void
317pcibus_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 */
364typedef 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 */
371typedef 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
385static int
386register_intr __P((int intr, int device_id, unsigned int flags,
387		       inthand2_t *handler, unsigned int * mptr, int unit));
388extern unsigned intr_mask[ICU_LEN];
389
390#endif /* !__FreeBSD2__ */
391static	unsigned int	pci_int_mask [16];
392
393int 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