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