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