pci_cfgreg.c revision 21438
1/**************************************************************************
2**
3**  $Id: pcibus.c,v 1.27 1996/10/30 22:38:55 asami 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 "vector.h"
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_device.h>
46
47#include <pci/pcivar.h>
48#include <pci/pcireg.h>
49#include <pci/pcibus.h>
50
51/*-----------------------------------------------------------------
52**
53**	The following functions are provided by the pci bios.
54**	They are used only by the pci configuration.
55**
56**	pcibus_setup():
57**		Probes for a pci system.
58**		Sets pci_maxdevice and pci_mechanism.
59**
60**	pcibus_tag():
61**		Creates a handle for pci configuration space access.
62**		This handle is given to the read/write functions.
63**
64**	pcibus_ftag():
65**		Creates a modified handle.
66**
67**	pcibus_read():
68**		Read a long word from the pci configuration space.
69**		Requires a tag (from pcitag) and the register
70**		number (should be a long word alligned one).
71**
72**	pcibus_write():
73**		Writes a long word to the pci configuration space.
74**		Requires a tag (from pcitag), the register number
75**		(should be a long word alligned one), and a value.
76**
77**	pcibus_regirq():
78**		Register an interupt handler for a pci device.
79**		Requires a tag (from pcitag), the register number
80**		(should be a long word alligned one), and a value.
81**
82**-----------------------------------------------------------------
83*/
84
85static int
86pcibus_check (void);
87
88static void
89pcibus_setup (void);
90
91static pcici_t
92pcibus_tag (u_char bus, u_char device, u_char func);
93
94static pcici_t
95pcibus_ftag (pcici_t tag, u_char func);
96
97static u_long
98pcibus_read (pcici_t tag, u_long reg);
99
100static void
101pcibus_write (pcici_t tag, u_long reg, u_long data);
102
103static int
104pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr);
105
106static int
107pcibus_ihandler_detach (int irq, inthand2_t *func);
108
109static int
110pcibus_imask_include (int irq, unsigned* maskptr);
111
112static int
113pcibus_imask_exclude (int irq, unsigned* maskptr);
114
115static struct pcibus i386pci = {
116	"pci",
117	pcibus_setup,
118	pcibus_tag,
119	pcibus_ftag,
120	pcibus_read,
121	pcibus_write,
122	ICU_LEN,
123	pcibus_ihandler_attach,
124	pcibus_ihandler_detach,
125	pcibus_imask_include,
126	pcibus_imask_exclude,
127};
128
129/*
130**	Announce structure to generic driver
131*/
132
133DATA_SET (pcibus_set, i386pci);
134
135/*--------------------------------------------------------------------
136**
137**      Determine configuration mode
138**
139**--------------------------------------------------------------------
140*/
141
142
143#define CONF1_ADDR_PORT    0x0cf8
144#define CONF1_DATA_PORT    0x0cfc
145
146#define CONF1_ENABLE       0x80000000ul
147#define CONF1_ENABLE_CHK   0x80000000ul
148#define CONF1_ENABLE_MSK   0x7ff00000ul
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#ifdef PC98
155#define CONF2_FORWARD_PORT 0x0cf9
156#else
157#define CONF2_FORWARD_PORT 0x0cfa
158#endif
159
160#define CONF2_ENABLE_CHK   0x0e
161#define CONF2_ENABLE_RES   0x0e
162
163static int
164pcibus_check (void)
165{
166	u_char device;
167
168	if (bootverbose) printf ("pcibus_check:\tdevice ");
169
170	for (device = 0; device < pci_maxdevice; device++) {
171		unsigned long id;
172		if (bootverbose)
173			printf ("%d ", device);
174		id = pcibus_read (pcibus_tag (0,device,0), 0);
175		if (id && id != 0xfffffffful) {
176			if (bootverbose) printf ("is there (id=%08lx)\n", id);
177			return 1;
178		}
179	}
180	if (bootverbose)
181		printf ("-- nothing found\n");
182	return 0;
183}
184
185static void
186pcibus_setup (void)
187{
188	unsigned long mode1res,oldval1;
189	unsigned char mode2res,oldval2;
190
191	oldval1 = inl (CONF1_ADDR_PORT);
192
193	if (bootverbose) {
194		printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1);
195	}
196
197	/*---------------------------------------
198	**      Assume configuration mechanism 1 for now ...
199	**---------------------------------------
200	*/
201
202	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
203
204		pci_mechanism = 1;
205		pci_maxdevice = 32;
206
207		outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
208		outb (CONF1_ADDR_PORT +3, 0);
209		mode1res = inl (CONF1_ADDR_PORT);
210		outl (CONF1_ADDR_PORT, oldval1);
211
212		if (bootverbose)
213		    printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n",
214			    mode1res, CONF1_ENABLE_CHK);
215
216		if (mode1res) {
217			if (pcibus_check())
218				return;
219		};
220
221		outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
222		mode1res = inl(CONF1_ADDR_PORT);
223		outl (CONF1_ADDR_PORT, oldval1);
224
225		if (bootverbose)
226		    printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n",
227			    mode1res, CONF1_ENABLE_CHK1);
228
229		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
230			if (pcibus_check())
231				return;
232		};
233	}
234
235	/*---------------------------------------
236	**      Try configuration mechanism 2 ...
237	**---------------------------------------
238	*/
239
240	oldval2 = inb (CONF2_ENABLE_PORT);
241
242	if (bootverbose) {
243		printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2);
244	}
245
246	if ((oldval2 & 0xf0) == 0) {
247
248		pci_mechanism = 2;
249		pci_maxdevice = 16;
250
251		outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
252		mode2res = inb(CONF2_ENABLE_PORT);
253		outb (CONF2_ENABLE_PORT, oldval2);
254
255		if (bootverbose)
256		    printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n",
257			    mode2res, CONF2_ENABLE_CHK);
258
259		if (mode2res == CONF2_ENABLE_RES) {
260		    if (bootverbose)
261			printf ("pcibus_setup(2a):\tnow trying mechanism 2\n");
262
263			if (pcibus_check())
264				return;
265		}
266	}
267
268	/*---------------------------------------
269	**      No PCI bus host bridge found
270	**---------------------------------------
271	*/
272
273	pci_mechanism = 0;
274	pci_maxdevice = 0;
275}
276
277/*--------------------------------------------------------------------
278**
279**      Build a pcitag from bus, device and function number
280**
281**--------------------------------------------------------------------
282*/
283
284static pcici_t
285pcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
286{
287	pcici_t tag;
288
289	tag.cfg1 = 0;
290	if (func   >=  8) return tag;
291
292	switch (pci_mechanism) {
293
294	case 1:
295		if (device < 32) {
296			tag.cfg1 = CONF1_ENABLE
297				| (((u_long) bus   ) << 16ul)
298				| (((u_long) device) << 11ul)
299				| (((u_long) func  ) <<  8ul);
300		}
301		break;
302	case 2:
303		if (device < 16) {
304			tag.cfg2.port    = 0xc000 | (device << 8ul);
305			tag.cfg2.enable  = 0xf0 | (func << 1ul);
306			tag.cfg2.forward = bus;
307		}
308		break;
309	};
310	return tag;
311}
312
313static pcici_t
314pcibus_ftag (pcici_t tag, u_char func)
315{
316	switch (pci_mechanism) {
317
318	case 1:
319		tag.cfg1 &= ~0x700ul;
320		tag.cfg1 |= (((u_long) func) << 8ul);
321		break;
322	case 2:
323		tag.cfg2.enable  = 0xf0 | (func << 1ul);
324		break;
325	};
326	return tag;
327}
328
329/*--------------------------------------------------------------------
330**
331**      Read register from configuration space.
332**
333**--------------------------------------------------------------------
334*/
335
336static u_long
337pcibus_read (pcici_t tag, u_long reg)
338{
339	u_long addr, data = 0;
340
341	if (!tag.cfg1) return (0xfffffffful);
342
343	switch (pci_mechanism) {
344
345	case 1:
346		addr = tag.cfg1 | (reg & 0xfc);
347#ifdef PCI_DEBUG
348		printf ("pci_conf_read(1): addr=%x ", addr);
349#endif
350		outl (CONF1_ADDR_PORT, addr);
351		data = inl (CONF1_DATA_PORT);
352		outl (CONF1_ADDR_PORT, 0   );
353		break;
354
355	case 2:
356		addr = tag.cfg2.port | (reg & 0xfc);
357#ifdef PCI_DEBUG
358		printf ("pci_conf_read(2): addr=%x ", addr);
359#endif
360		outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
361		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
362
363		data = inl ((u_short) addr);
364
365		outb (CONF2_ENABLE_PORT,  0);
366		outb (CONF2_FORWARD_PORT, 0);
367		break;
368	};
369
370#ifdef PCI_DEBUG
371	printf ("data=%x\n", data);
372#endif
373
374	return (data);
375}
376
377/*--------------------------------------------------------------------
378**
379**      Write register into configuration space.
380**
381**--------------------------------------------------------------------
382*/
383
384static void
385pcibus_write (pcici_t tag, u_long reg, u_long data)
386{
387	u_long addr;
388
389	if (!tag.cfg1) return;
390
391	switch (pci_mechanism) {
392
393	case 1:
394		addr = tag.cfg1 | (reg & 0xfc);
395#ifdef PCI_DEBUG
396		printf ("pci_conf_write(1): addr=%x data=%x\n",
397			addr, data);
398#endif
399		outl (CONF1_ADDR_PORT, addr);
400		outl (CONF1_DATA_PORT, data);
401		outl (CONF1_ADDR_PORT,   0 );
402		break;
403
404	case 2:
405		addr = tag.cfg2.port | (reg & 0xfc);
406#ifdef PCI_DEBUG
407		printf ("pci_conf_write(2): addr=%x data=%x\n",
408			addr, data);
409#endif
410		outb (CONF2_ENABLE_PORT,  tag.cfg2.enable);
411		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
412
413		outl ((u_short) addr, data);
414
415		outb (CONF2_ENABLE_PORT,  0);
416		outb (CONF2_FORWARD_PORT, 0);
417		break;
418	};
419}
420
421/*-----------------------------------------------------------------------
422**
423**	Register an interupt handler for a pci device.
424**
425**-----------------------------------------------------------------------
426*/
427
428static int
429pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr)
430{
431	char buf[16];
432	char *cp;
433	int free_id, id, result;
434
435	sprintf(buf, "pci irq%d", irq);
436	for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) {
437		if (strcmp(cp, buf) == 0)
438			break;
439		if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0)
440			free_id = id;
441		while (*cp++ != '\0')
442			;
443	}
444	if (id == NR_DEVICES) {
445		id = free_id;
446		if (id == 0) {
447			/*
448			 * All pci irq counters are in use, perhaps because
449			 * config is old so there aren't any.  Abuse the
450			 * clk0 counter.
451			 */
452			printf (
453		"pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n",
454				irq);
455		}
456	}
457	result = register_intr(
458		irq,		    /* isa irq	    */
459		id,		    /* device id    */
460		0,		    /* flags?	    */
461		func,		    /* handler	    */
462		maskptr,	    /* mask pointer */
463		arg);		    /* handler arg  */
464
465	if (result) {
466		printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
467		return (result);
468	};
469	update_intr_masks();
470
471	INTREN ((1ul<<irq));
472	return (0);
473}
474
475static int
476pcibus_ihandler_detach (int irq, inthand2_t *func)
477{
478	int result;
479
480	INTRDIS ((1ul<<irq));
481
482	result = unregister_intr (irq, func);
483
484	if (result)
485		printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
486
487	update_intr_masks();
488
489	return (result);
490}
491
492static int
493pcibus_imask_include (int irq, unsigned* maskptr)
494{
495	unsigned mask;
496
497	if (!maskptr) return (0);
498
499	mask = 1ul << irq;
500
501	if (*maskptr & mask)
502		return (-1);
503
504	INTRMASK (*maskptr, mask);
505	update_intr_masks();
506
507	return (0);
508}
509
510static int
511pcibus_imask_exclude (int irq, unsigned* maskptr)
512{
513	unsigned mask;
514
515	if (!maskptr) return (0);
516
517	mask = 1ul << irq;
518
519	if (! (*maskptr & mask))
520		return (-1);
521
522	INTRUNMASK (*maskptr, mask);
523	update_intr_masks();
524
525	return (0);
526}
527