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