1 /* $NetBSD: pci.c,v 1.11 2009/03/18 10:22:35 cegger Exp $ */
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/param.h>
34 #include <lib/libsa/stand.h>
39 * practice direct mode configuration scheme with CONFIG_ADDR
40 * (0xfec0'0000) and CONFIG_DATA (0xfee0'0000).
42 #define PCI_MEMBASE 0x80000000
43 #define PCI_MEMLIMIT 0xfbffffff /* EUMB is next to this */
44 #define PCI_IOBASE 0x00001000 /* reserves room for via 686B */
45 #define PCI_IOLIMIT 0x000fffff
46 #define CONFIG_ADDR 0xfec00000
47 #define CONFIG_DATA 0xfee00000
53 static unsigned cfgread(int, int, int, int);
54 static void cfgwrite(int, int, int, int, unsigned);
55 static void _buswalk(int,
56 int (*)(int, int, int, unsigned long), unsigned long);
57 static int _pcilookup(int,
58 int (*)(int, int, int, unsigned long), unsigned long,
59 unsigned [][2], int, int);
60 static int deviceinit(int, int, int, unsigned long);
61 static void memassign(int, int, int);
62 static int devmatch(int, int, int, unsigned long);
63 static int clsmatch(int, int, int, unsigned long);
65 unsigned memstart
, memlimit
;
66 unsigned iostart
, iolimit
;
73 memstart
= PCI_MEMBASE
;
74 memlimit
= PCI_MEMLIMIT
;
76 iolimit
= PCI_IOLIMIT
;
79 (void)_buswalk(0, deviceinit
, 0UL);
83 pcifinddev(unsigned vend
, unsigned prod
, unsigned *tag
)
85 unsigned pciid
, target
[1][2];
87 pciid
= PCI_DEVICE(vend
, prod
);
88 if (_pcilookup(0, devmatch
, pciid
, target
, 0, 1)) {
97 pcilookup(type
, list
, max
)
103 return _pcilookup(0, clsmatch
, type
, list
, 0, max
);
107 pcimaketag(int b
, int d
, int f
)
110 return (1U << 31) | (b
<< 16) | (d
<< 11) | (f
<< 8);
114 pcidecomposetag(unsigned tag
, int *b
, int *d
, int *f
)
118 *b
= (tag
>> 16) & 0xff;
120 *d
= (tag
>> 11) & 0x1f;
122 *f
= (tag
>> 8) & 0x7;
127 pcicfgread(unsigned tag
, int off
)
131 cfg
= tag
| (off
&~ 03);
132 iohtole32(CONFIG_ADDR
, cfg
);
133 return iole32toh(CONFIG_DATA
);
137 pcicfgwrite(unsigned tag
, int off
, unsigned val
)
141 cfg
= tag
| (off
&~ 03);
142 iohtole32(CONFIG_ADDR
, cfg
);
143 iohtole32(CONFIG_DATA
, val
);
147 cfgread(int b
, int d
, int f
, int off
)
152 cfg
= (1U << 31) | (b
<< 16) | (d
<< 11) | (f
<< 8) | off
| 0;
153 iohtole32(CONFIG_ADDR
, cfg
);
154 return iole32toh(CONFIG_DATA
);
158 cfgwrite(int b
, int d
, int f
, int off
, unsigned val
)
163 cfg
= (1U << 31) | (b
<< 16) | (d
<< 11) | (f
<< 8) | off
| 0;
164 iohtole32(CONFIG_ADDR
, cfg
);
165 iohtole32(CONFIG_DATA
, val
);
169 _buswalk(int bus
, int (*proc
)(int, int, int, unsigned long), unsigned long data
)
171 int device
, function
, nfunctions
;
172 unsigned pciid
, bhlcr
;
174 for (device
= 0; device
< MAXNDEVS
; device
++) {
175 pciid
= cfgread(bus
, device
, 0, PCI_ID_REG
);
176 if (PCI_VENDOR(pciid
) == PCI_VENDOR_INVALID
)
178 if (PCI_VENDOR(pciid
) == 0)
180 bhlcr
= cfgread(bus
, device
, 0, PCI_BHLC_REG
);
181 nfunctions
= (PCI_HDRTYPE_MULTIFN(bhlcr
)) ? 8 : 1;
182 for (function
= 0; function
< nfunctions
; function
++) {
183 pciid
= cfgread(bus
, device
, function
, PCI_ID_REG
);
184 if (PCI_VENDOR(pciid
) == PCI_VENDOR_INVALID
)
186 if (PCI_VENDOR(pciid
) == 0)
189 if ((*proc
)(bus
, device
, function
, data
) != 0)
190 goto out
; /* early exit */
197 deviceinit(int bus
, int dev
, int func
, unsigned long data
)
202 printf("%02d:%02d:%02d:", bus
, dev
, func
);
203 val
= cfgread(bus
, dev
, func
, 0x00);
204 printf(" chip %04x.%04x", val
& 0xffff, val
>>16);
205 val
= cfgread(bus
, dev
, func
, 0x2c);
206 printf(" card %04x.%04x", val
& 0xffff, val
>>16);
207 val
= cfgread(bus
, dev
, func
, 0x08);
208 printf(" rev %02x class %02x.%02x.%02x",
209 val
& 0xff, (val
>>24), (val
>>16) & 0xff, (val
>>8) & 0xff);
210 val
= cfgread(bus
, dev
, func
, 0x0c);
211 printf(" hdr %02x\n", (val
>>16) & 0xff);
214 val
= cfgread(bus
, dev
, func
, 0x04);
215 val
|= 0xffff0107; /* enable IO,MEM,MASTER,SERR */
216 cfgwrite(bus
, dev
, func
, 0x04, val
);
219 val
= 0x80 << 8 | 0x08 /* 32B cache line */;
220 cfgwrite(bus
, dev
, func
, 0x0c, val
);
223 /* skip IDE controller BAR assignment */
224 val
= cfgread(bus
, dev
, func
, PCI_CLASS_REG
);
225 if ((val
>> 16) == PCI_CLASS_IDE
)
228 memassign(bus
, dev
, func
);
230 /* descending toward PCI-PCI bridge */
231 if ((cfgread(bus
, dev
, func
, 0x08) >> 16) == PCI_CLASS_PPB
) {
236 val
= (0xff << 16) | (new << 8) | bus
;
237 cfgwrite(bus
, dev
, func
, 0x18, val
);
240 val
= (iostart
+ (0xfff)) & ~0xfff; /* 4KB boundary */
242 val
= 0xffff0000 | (iolimit
& 0xf000) | (val
& 0xf000) >> 8;
243 cfgwrite(bus
, dev
, func
, 0x1c, val
);
244 val
= (iolimit
& 0xffff0000) | (val
& 0xffff0000) >> 16;
245 cfgwrite(bus
, dev
, func
, 0x30, val
);
248 val
= (memstart
+ 0xfffff) &~ 0xfffff; /* 1MB boundary */
250 val
= (memlimit
& 0xffff0000) | (val
& 0xffff0000) >> 16;
251 cfgwrite(bus
, dev
, func
, 0x20, val
);
254 val
= cfgread(bus
, dev
, func
, 0x04);
256 cfgwrite(bus
, dev
, func
, 0x04, val
);
258 _buswalk(new, deviceinit
, data
);
261 val
= cfgread(bus
, dev
, func
, 0x18);
262 val
= (maxbus
<< 16) | (val
& 0xffff);
263 cfgwrite(bus
, dev
, func
, 0x18, val
);
269 memassign(int bus
, int dev
, int func
)
271 unsigned val
, maxbar
, mapr
, req
, mapbase
, size
;
273 val
= cfgread(bus
, dev
, func
, 0x0c);
274 switch (PCI_HDRTYPE_TYPE(val
)) {
276 maxbar
= 0x10 + 6 * 4; break;
278 maxbar
= 0x10 + 2 * 4; break;
280 maxbar
= 0x10 + 1 * 4; break;
282 for (mapr
= 0x10; mapr
< maxbar
; mapr
+= 4) {
283 cfgwrite(bus
, dev
, func
, mapr
, 0xffffffff);
284 val
= cfgread(bus
, dev
, func
, mapr
);
287 req
= ~(val
& 0xfffffffc) + 1;
288 if (req
& (req
- 1)) /* power of 2 */
290 if (req
== 0) /* ever exists */
292 size
= (req
> 0x10) ? req
: 0x10;
293 mapbase
= (iostart
+ size
- 1) & ~(size
- 1);
294 if (mapbase
+ size
> iolimit
)
297 iostart
= mapbase
+ size
;
298 /* establish IO space */
299 cfgwrite(bus
, dev
, func
, mapr
, mapbase
| 01);
302 /* PCI memory space */
303 req
= ~(val
& 0xfffffff0) + 1;
304 if (req
& (req
- 1)) /* power of 2 */
306 if (req
== 0) /* ever exists */
309 if (val
== 2 || val
== 6)
311 size
= (req
> 0x1000) ? req
: 0x1000;
312 mapbase
= (memstart
+ size
- 1) & ~(size
- 1);
313 if (mapbase
+ size
> memlimit
)
316 memstart
= mapbase
+ size
;
317 /* establish memory space */
318 cfgwrite(bus
, dev
, func
, mapr
, mapbase
);
321 cfgwrite(bus
, dev
, func
, mapr
, 0);
324 printf("%s base %x size %x\n", (val
& 01) ? "i/o" : "mem", mapbase
, size
);
329 devmatch(int bus
, int dev
, int func
, unsigned long data
)
333 pciid
= cfgread(bus
, dev
, func
, PCI_ID_REG
);
334 return (pciid
== (unsigned)data
);
338 clsmatch(int bus
, int dev
, int func
, unsigned long data
)
342 class = cfgread(bus
, dev
, func
, PCI_CLASS_REG
);
343 return ((class >> 16) == (unsigned)data
);
347 _pcilookup(int bus
, int (*match
)(int, int, int, unsigned long), unsigned long data
, unsigned list
[][2], int index
, int limit
)
349 int device
, function
, nfuncs
;
350 unsigned pciid
, bhlcr
, class;
352 for (device
= 0; device
< MAXNDEVS
; device
++) {
353 pciid
= cfgread(bus
, device
, 0, PCI_ID_REG
);
354 if (PCI_VENDOR(pciid
) == PCI_VENDOR_INVALID
)
356 if (PCI_VENDOR(pciid
) == 0)
358 class = cfgread(bus
, device
, 0, PCI_CLASS_REG
);
359 if ((class >> 16) == PCI_CLASS_PPB
) {
360 /* exploring bus beyond PCI-PCI bridge */
361 index
= _pcilookup(bus
+ 1,
362 match
, data
, list
, index
, limit
);
367 bhlcr
= cfgread(bus
, device
, 0, PCI_BHLC_REG
);
368 nfuncs
= (PCI_HDRTYPE_MULTIFN(bhlcr
)) ? 8 : 1;
369 for (function
= 0; function
< nfuncs
; function
++) {
370 pciid
= cfgread(bus
, device
, function
, PCI_ID_REG
);
371 if (PCI_VENDOR(pciid
) == PCI_VENDOR_INVALID
)
373 if (PCI_VENDOR(pciid
) == 0)
375 if ((*match
)(bus
, device
, function
, data
)) {
376 list
[index
][0] = pciid
;
378 pcimaketag(bus
, device
, function
);