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