pci_pir.c revision 10710
1/**************************************************************************
2**
3**  $Id: pcibus.c,v 1.10 1995/06/30 16:11:42 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 <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 void
87pcibus_setup (void);
88
89static pcici_t
90pcibus_tag (u_char bus, u_char device, u_char func);
91
92static pcici_t
93pcibus_ftag (pcici_t tag, u_char func);
94
95static u_long
96pcibus_read (pcici_t tag, u_long reg);
97
98static void
99pcibus_write (pcici_t tag, u_long reg, u_long data);
100
101static int
102pcibus_ihandler_attach (int irq, void(*ihandler)(), int arg, unsigned* maskp);
103
104static int
105pcibus_ihandler_detach (int irq, void(*handler)());
106
107static int
108pcibus_imask_include (int irq, unsigned* maskptr);
109
110static int
111pcibus_imask_exclude (int irq, unsigned* maskptr);
112
113struct pcibus i386pci = {
114	"pci",
115	pcibus_setup,
116	pcibus_tag,
117	pcibus_ftag,
118	pcibus_read,
119	pcibus_write,
120	ICU_LEN,
121	pcibus_ihandler_attach,
122	pcibus_ihandler_detach,
123	pcibus_imask_include,
124	pcibus_imask_exclude,
125};
126
127/*
128**	Announce structure to generic driver
129*/
130
131DATA_SET (pcibus_set, i386pci);
132
133/*--------------------------------------------------------------------
134**
135**      Determine configuration mode
136**
137**--------------------------------------------------------------------
138*/
139
140
141#define CONF1_ENABLE       0x80000000ul
142#define CONF1_ENABLE_CHK1  0xF0000000ul
143#define CONF1_ENABLE_CHK2  0xfffffffful
144#define CONF1_ENABLE_RES2  0x80fffffcul
145#define CONF1_ADDR_PORT    0x0cf8
146#define CONF1_DATA_PORT    0x0cfc
147
148
149#define CONF2_ENABLE_PORT  0x0cf8
150#define CONF2_FORWARD_PORT 0x0cfa
151
152
153static void
154pcibus_setup (void)
155{
156	u_long result, oldval;
157
158	/*---------------------------------------
159	**      Configuration mode 1 ?
160	**---------------------------------------
161	*/
162
163	oldval = inl (CONF1_ADDR_PORT);
164	outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
165	outb (CONF1_ADDR_PORT +3, 0);
166	result = inl (CONF1_ADDR_PORT);
167	outl (CONF1_ADDR_PORT, oldval);
168
169	if (result & CONF1_ENABLE) {
170		pci_mechanism = 1;
171		pci_maxdevice = 32;
172		if (pcibus_read (pcibus_tag (0,0,0), 0) != 0xfffffffful)
173			return;
174	};
175
176	/*---------------------------------------
177	**      Configuration mode 2 ?
178	**---------------------------------------
179	*/
180
181	outb (CONF2_ENABLE_PORT,  0);
182	outb (CONF2_FORWARD_PORT, 0);
183	if (!inb (CONF2_ENABLE_PORT) && !inb (CONF2_FORWARD_PORT)) {
184		pci_mechanism = 2;
185		pci_maxdevice = 16;
186		if (pcibus_read (pcibus_tag (0,0,0), 0) != 0xfffffffful)
187			return;
188	};
189
190
191	/*-----------------------------------------------------
192	**      Well, is it Configuration mode 1, after all ?
193	**-----------------------------------------------------
194	*/
195
196	oldval = inl (CONF1_ADDR_PORT);
197	outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK2);
198	result = inl (CONF1_ADDR_PORT);
199	outl (CONF1_ADDR_PORT, oldval);
200
201	if (result == CONF1_ENABLE_RES2) {
202		pci_mechanism = 1;
203		pci_maxdevice = 32;
204		if (pcibus_read (pcibus_tag (0,0,0), 0) != 0xfffffffful)
205			return;
206	}
207	if (result != 0xfffffffful)
208		printf ("pcibus_setup: "
209			"wrote 0x%08x, read back 0x%08x, expected 0x%08x\n",
210			CONF1_ENABLE_CHK2, result, CONF1_ENABLE_RES2);
211
212	/*---------------------------------------
213	**      No PCI bus host bridge found
214	**---------------------------------------
215	*/
216
217	pci_mechanism = 0;
218	pci_maxdevice = 0;
219}
220
221/*--------------------------------------------------------------------
222**
223**      Build a pcitag from bus, device and function number
224**
225**--------------------------------------------------------------------
226*/
227
228static pcici_t
229pcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
230{
231	pcici_t tag;
232
233	tag.cfg1 = 0;
234	if (device >= 32) return tag;
235	if (func   >=  8) return tag;
236
237	switch (pci_mechanism) {
238
239	case 1:
240		tag.cfg1 = CONF1_ENABLE
241			| (((u_long) bus   ) << 16ul)
242			| (((u_long) device) << 11ul)
243			| (((u_long) func  ) <<  8ul);
244		break;
245	case 2:
246		if (device >= 16) break;
247		tag.cfg2.port    = 0xc000 | (device << 8ul);
248		tag.cfg2.enable  = 0xf1 | (func << 1ul);
249		tag.cfg2.forward = bus;
250		break;
251	};
252	return tag;
253}
254
255static pcici_t
256pcibus_ftag (pcici_t tag, u_char func)
257{
258	switch (pci_mechanism) {
259
260	case 1:
261		tag.cfg1 &= ~0x700ul;
262		tag.cfg1 |= (((u_long) func) << 8ul);
263		break;
264	case 2:
265		tag.cfg2.enable  = 0xf1 | (func << 1ul);
266		break;
267	};
268	return tag;
269}
270
271/*--------------------------------------------------------------------
272**
273**      Read register from configuration space.
274**
275**--------------------------------------------------------------------
276*/
277
278static u_long
279pcibus_read (pcici_t tag, u_long reg)
280{
281	u_long addr, data = 0;
282
283	if (!tag.cfg1) return (0xfffffffful);
284
285	switch (pci_mechanism) {
286
287	case 1:
288		addr = tag.cfg1 | (reg & 0xfc);
289#ifdef PCI_DEBUG
290		printf ("pci_conf_read(1): addr=%x ", addr);
291#endif
292		outl (CONF1_ADDR_PORT, addr);
293		data = inl (CONF1_DATA_PORT);
294		outl (CONF1_ADDR_PORT, 0   );
295		break;
296
297	case 2:
298		addr = tag.cfg2.port | (reg & 0xfc);
299#ifdef PCI_DEBUG
300		printf ("pci_conf_read(2): addr=%x ", addr);
301#endif
302		outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
303		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
304
305		data = inl ((u_short) addr);
306
307		outb (CONF2_ENABLE_PORT,  0);
308		outb (CONF2_FORWARD_PORT, 0);
309		break;
310	};
311
312#ifdef PCI_DEBUG
313	printf ("data=%x\n", data);
314#endif
315
316	return (data);
317}
318
319/*--------------------------------------------------------------------
320**
321**      Write register into configuration space.
322**
323**--------------------------------------------------------------------
324*/
325
326static void
327pcibus_write (pcici_t tag, u_long reg, u_long data)
328{
329	u_long addr;
330
331	if (!tag.cfg1) return;
332
333	switch (pci_mechanism) {
334
335	case 1:
336		addr = tag.cfg1 | (reg & 0xfc);
337#ifdef PCI_DEBUG
338		printf ("pci_conf_write(1): addr=%x data=%x\n",
339			addr, data);
340#endif
341		outl (CONF1_ADDR_PORT, addr);
342		outl (CONF1_DATA_PORT, data);
343		outl (CONF1_ADDR_PORT,   0 );
344		break;
345
346	case 2:
347		addr = tag.cfg2.port | (reg & 0xfc);
348#ifdef PCI_DEBUG
349		printf ("pci_conf_write(2): addr=%x data=%x\n",
350			addr, data);
351#endif
352		outb (CONF2_ENABLE_PORT,  tag.cfg2.enable);
353		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
354
355		outl ((u_short) addr, data);
356
357		outb (CONF2_ENABLE_PORT,  0);
358		outb (CONF2_FORWARD_PORT, 0);
359		break;
360	};
361}
362
363/*-----------------------------------------------------------------------
364**
365**	Register an interupt handler for a pci device.
366**
367**-----------------------------------------------------------------------
368*/
369
370static int
371pcibus_ihandler_attach (int irq, void(*func)(), int arg, unsigned * maskptr)
372{
373	int result;
374	result = register_intr(
375		irq,		    /* isa irq	    */
376		0,		    /* deviced??    */
377		0,		    /* flags?	    */
378		(inthand2_t*) func, /* handler	    */
379		maskptr,	    /* mask pointer */
380		arg);		    /* handler arg  */
381
382	if (result) {
383		printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
384		return (result);
385	};
386	update_intr_masks();
387
388	INTREN ((1ul<<irq));
389	return (0);
390}
391
392static int
393pcibus_ihandler_detach (int irq, void(*func)())
394{
395	int result;
396
397	INTRDIS ((1ul<<irq));
398
399	result = unregister_intr (irq, (inthand2_t*) func);
400
401	if (result)
402		printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
403
404	update_intr_masks();
405
406	return (result);
407}
408
409static int
410pcibus_imask_include (int irq, unsigned* maskptr)
411{
412	unsigned mask;
413
414	if (!maskptr) return (0);
415
416	mask = 1ul << irq;
417
418	if (*maskptr & mask)
419		return (-1);
420
421	INTRMASK (*maskptr, mask);
422	update_intr_masks();
423
424	return (0);
425}
426
427static int
428pcibus_imask_exclude (int irq, unsigned* maskptr)
429{
430	unsigned mask;
431
432	if (!maskptr) return (0);
433
434	mask = 1ul << irq;
435
436	if (! (*maskptr & mask))
437		return (-1);
438
439	*maskptr &= ~mask;
440	update_intr_masks();
441
442	return (0);
443}
444