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