Deleted Added
sdiff udiff text old ( 11378 ) new ( 11524 )
full compact
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}