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