pci_pir.c revision 11544
1/**************************************************************************
2**
3**  $Id: pcibus.c,v 1.17 1995/10/15 23:43:08 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
86static int
87pcibus_check (void);
88
89static void
90pcibus_setup (void);
91
92static pcici_t
93pcibus_tag (u_char bus, u_char device, u_char func);
94
95static pcici_t
96pcibus_ftag (pcici_t tag, u_char func);
97
98static u_long
99pcibus_read (pcici_t tag, u_long reg);
100
101static void
102pcibus_write (pcici_t tag, u_long reg, u_long data);
103
104static int
105pcibus_ihandler_attach (int irq, void(*ihandler)(), int arg, unsigned* maskp);
106
107static int
108pcibus_ihandler_detach (int irq, void(*handler)());
109
110static int
111pcibus_imask_include (int irq, unsigned* maskptr);
112
113static int
114pcibus_imask_exclude (int irq, unsigned* maskptr);
115
116struct 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
134DATA_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   0x00ff07fful
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
160static int
161pcibus_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
182static void
183pcibus_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
281static pcici_t
282pcibus_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
310static pcici_t
311pcibus_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
333static u_long
334pcibus_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
381static void
382pcibus_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
425static int
426pcibus_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
447static int
448pcibus_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
464static int
465pcibus_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
482static int
483pcibus_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