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