pci.c revision 1.2 1 /* $NetBSD: pci.c,v 1.2 2011/01/27 17:38:04 phx Exp $ */
2
3 /*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tohru Nishimura.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
18 *
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.
30 */
31
32 #include <sys/param.h>
33
34 #include <lib/libsa/stand.h>
35
36
37 #define MAXNDEVS 32
38
39 #include "globals.h"
40
41 static unsigned cfgread(int, int, int, int);
42 static void cfgwrite(int, int, int, int, unsigned);
43 static void _buswalk(int,
44 int (*)(int, int, int, unsigned long), unsigned long);
45 static int _pcilookup(int,
46 int (*)(int, int, int, unsigned long), unsigned long,
47 unsigned [][2], int, int);
48 static int deviceinit(int, int, int, unsigned long);
49 static void memassign(int, int, int);
50 static int devmatch(int, int, int, unsigned long);
51 static int clsmatch(int, int, int, unsigned long);
52
53 unsigned memstart, memlimit;
54 unsigned iostart, iolimit;
55 unsigned maxbus;
56
57 void
58 pcisetup(void)
59 {
60
61 memstart = PCI_MEMBASE;
62 memlimit = PCI_MEMLIMIT;
63 iostart = PCI_IOBASE;
64 iolimit = PCI_IOLIMIT;
65 maxbus = 0;
66
67 (void)_buswalk(0, deviceinit, 0UL);
68 }
69
70 int
71 pcifinddev(unsigned vend, unsigned prod, unsigned *tag)
72 {
73 unsigned pciid, target[1][2];
74
75 pciid = PCI_DEVICE(vend, prod);
76 if (_pcilookup(0, devmatch, pciid, target, 0, 1)) {
77 *tag = target[0][1];
78 return 0;
79 }
80 *tag = ~0;
81 return -1;
82 }
83
84 int
85 pcilookup(type, list, max)
86 unsigned type;
87 unsigned list[][2];
88 int max;
89 {
90
91 return _pcilookup(0, clsmatch, type, list, 0, max);
92 }
93
94 unsigned
95 pcimaketag(int b, int d, int f)
96 {
97
98 return (1U << 31) | (b << 16) | (d << 11) | (f << 8);
99 }
100
101 void
102 pcidecomposetag(unsigned tag, int *b, int *d, int *f)
103 {
104
105 if (b != NULL)
106 *b = (tag >> 16) & 0xff;
107 if (d != NULL)
108 *d = (tag >> 11) & 0x1f;
109 if (f != NULL)
110 *f = (tag >> 8) & 0x7;
111 return;
112 }
113
114 unsigned
115 pcicfgread(unsigned tag, int off)
116 {
117 unsigned cfg;
118
119 cfg = tag | (off &~ 03);
120 iohtole32(CONFIG_ADDR, cfg);
121 return iole32toh(CONFIG_DATA);
122 }
123
124 void
125 pcicfgwrite(unsigned tag, int off, unsigned val)
126 {
127 unsigned cfg;
128
129 cfg = tag | (off &~ 03);
130 iohtole32(CONFIG_ADDR, cfg);
131 iohtole32(CONFIG_DATA, val);
132 }
133
134 static unsigned
135 cfgread(int b, int d, int f, int off)
136 {
137 unsigned cfg;
138
139 off &= ~03;
140 cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
141 iohtole32(CONFIG_ADDR, cfg);
142 return iole32toh(CONFIG_DATA);
143 }
144
145 static void
146 cfgwrite(int b, int d, int f, int off, unsigned val)
147 {
148 unsigned cfg;
149
150 off &= ~03;
151 cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
152 iohtole32(CONFIG_ADDR, cfg);
153 iohtole32(CONFIG_DATA, val);
154 }
155
156 static void
157 _buswalk(int bus, int (*proc)(int, int, int, unsigned long), unsigned long data)
158 {
159 int device, function, nfunctions;
160 unsigned pciid, bhlcr;
161
162 for (device = 0; device < MAXNDEVS; device++) {
163 pciid = cfgread(bus, device, 0, PCI_ID_REG);
164 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
165 continue;
166 if (PCI_VENDOR(pciid) == 0)
167 continue;
168 bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
169 nfunctions = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
170 for (function = 0; function < nfunctions; function++) {
171 pciid = cfgread(bus, device, function, PCI_ID_REG);
172 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
173 continue;
174 if (PCI_VENDOR(pciid) == 0)
175 continue;
176
177 if ((*proc)(bus, device, function, data) != 0)
178 goto out; /* early exit */
179 }
180 }
181 out:;
182 }
183
184 static int
185 deviceinit(int bus, int dev, int func, unsigned long data)
186 {
187 unsigned val;
188
189 /* 0x00 */
190 #ifdef DEBUG
191 printf("%02d:%02d:%02d:", bus, dev, func);
192 val = cfgread(bus, dev, func, 0x00);
193 printf(" chip %04x.%04x", val & 0xffff, val>>16);
194 val = cfgread(bus, dev, func, 0x2c);
195 printf(" card %04x.%04x", val & 0xffff, val>>16);
196 val = cfgread(bus, dev, func, 0x08);
197 printf(" rev %02x class %02x.%02x.%02x",
198 val & 0xff, (val>>24), (val>>16) & 0xff, (val>>8) & 0xff);
199 val = cfgread(bus, dev, func, 0x0c);
200 printf(" hdr %02x\n", (val>>16) & 0xff);
201 #endif
202
203 /* 0x04 */
204 val = cfgread(bus, dev, func, 0x04);
205 val |= 0xffff0107; /* enable IO,MEM,MASTER,SERR */
206 cfgwrite(bus, dev, func, 0x04, val);
207
208 /* 0x0c */
209 val = 0x80 << 8 | 0x08 /* 32B cache line */;
210 cfgwrite(bus, dev, func, 0x0c, val);
211
212 /* skip legacy mode IDE controller BAR assignment */
213 val = cfgread(bus, dev, func, PCI_CLASS_REG);
214 if ((val >> 16) == PCI_CLASS_IDE && ((val >> 8) & 0x05) == 0)
215 return 0;
216
217 memassign(bus, dev, func);
218
219 /* descending toward PCI-PCI bridge */
220 if ((cfgread(bus, dev, func, 0x08) >> 16) == PCI_CLASS_PPB) {
221 unsigned new;
222
223 /* 0x18 */
224 new = (maxbus += 1);
225 val = (0xff << 16) | (new << 8) | bus;
226 cfgwrite(bus, dev, func, 0x18, val);
227
228 /* 0x1c and 0x30 */
229 val = (iostart + (0xfff)) & ~0xfff; /* 4KB boundary */
230 iostart = val;
231 val = 0xffff0000 | (iolimit & 0xf000) | (val & 0xf000) >> 8;
232 cfgwrite(bus, dev, func, 0x1c, val);
233 val = (iolimit & 0xffff0000) | (val & 0xffff0000) >> 16;
234 cfgwrite(bus, dev, func, 0x30, val);
235
236 /* 0x20 */
237 val = (memstart + 0xfffff) &~ 0xfffff; /* 1MB boundary */
238 memstart = val;
239 val = (memlimit & 0xffff0000) | (val & 0xffff0000) >> 16;
240 cfgwrite(bus, dev, func, 0x20, val);
241
242 /* redo 0x04 */
243 val = cfgread(bus, dev, func, 0x04);
244 val |= 0xffff0107;
245 cfgwrite(bus, dev, func, 0x04, val);
246
247 _buswalk(new, deviceinit, data);
248
249 /* adjust 0x18 */
250 val = cfgread(bus, dev, func, 0x18);
251 val = (maxbus << 16) | (val & 0xffff);
252 cfgwrite(bus, dev, func, 0x18, val);
253 }
254 return 0;
255 }
256
257 static void
258 memassign(int bus, int dev, int func)
259 {
260 unsigned val, maxbar, mapr, req, mapbase, size;
261
262 val = cfgread(bus, dev, func, 0x0c);
263 switch (PCI_HDRTYPE_TYPE(val)) {
264 case 0:
265 maxbar = 0x10 + 6 * 4; break;
266 case 1:
267 maxbar = 0x10 + 2 * 4; break;
268 default:
269 maxbar = 0x10 + 1 * 4; break;
270 }
271 for (mapr = 0x10; mapr < maxbar; mapr += 4) {
272 cfgwrite(bus, dev, func, mapr, 0xffffffff);
273 val = cfgread(bus, dev, func, mapr);
274 if (val & 01) {
275 /* PCI IO space */
276 req = ~(val & 0xfffffffc) + 1;
277 if (req & (req - 1)) /* power of 2 */
278 continue;
279 if (req == 0) /* ever exists */
280 continue;
281 size = (req > 0x10) ? req : 0x10;
282 mapbase = (iostart + size - 1) & ~(size - 1);
283 if (mapbase + size > iolimit)
284 continue;
285
286 iostart = mapbase + size;
287 /* establish IO space */
288 cfgwrite(bus, dev, func, mapr, mapbase | 01);
289 }
290 else {
291 /* PCI memory space */
292 req = ~(val & 0xfffffff0) + 1;
293 if (req & (req - 1)) /* power of 2 */
294 continue;
295 if (req == 0) /* ever exists */
296 continue;
297 val &= 0x6;
298 if (val == 2 || val == 6)
299 continue;
300 size = (req > 0x1000) ? req : 0x1000;
301 mapbase = (memstart + size - 1) & ~(size - 1);
302 if (mapbase + size > memlimit)
303 continue;
304
305 memstart = mapbase + size;
306 /* establish memory space */
307 cfgwrite(bus, dev, func, mapr, mapbase);
308 if (val == 4) {
309 mapr += 4;
310 cfgwrite(bus, dev, func, mapr, 0);
311 }
312 }
313 DPRINTF(("%s base %x size %x\n", (val & 01) ? "i/o" : "mem",
314 mapbase, size));
315 }
316 }
317
318 static int
319 devmatch(int bus, int dev, int func, unsigned long data)
320 {
321 unsigned pciid;
322
323 pciid = cfgread(bus, dev, func, PCI_ID_REG);
324 return (pciid == (unsigned)data);
325 }
326
327 static int
328 clsmatch(int bus, int dev, int func, unsigned long data)
329 {
330 unsigned class;
331
332 class = cfgread(bus, dev, func, PCI_CLASS_REG);
333 return ((class >> 16) == (unsigned)data);
334 }
335
336 static int
337 _pcilookup(int bus, int (*match)(int, int, int, unsigned long), unsigned long data, unsigned list[][2], int index, int limit)
338 {
339 int device, function, nfuncs;
340 unsigned pciid, bhlcr, class;
341
342 for (device = 0; device < MAXNDEVS; device++) {
343 pciid = cfgread(bus, device, 0, PCI_ID_REG);
344 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
345 continue;
346 if (PCI_VENDOR(pciid) == 0)
347 continue;
348 class = cfgread(bus, device, 0, PCI_CLASS_REG);
349 if ((class >> 16) == PCI_CLASS_PPB) {
350 /* exploring bus beyond PCI-PCI bridge */
351 index = _pcilookup(bus + 1,
352 match, data, list, index, limit);
353 if (index >= limit)
354 goto out;
355 continue;
356 }
357 bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
358 nfuncs = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
359 for (function = 0; function < nfuncs; function++) {
360 pciid = cfgread(bus, device, function, PCI_ID_REG);
361 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
362 continue;
363 if (PCI_VENDOR(pciid) == 0)
364 continue;
365 if ((*match)(bus, device, function, data)) {
366 list[index][0] = pciid;
367 list[index][1] =
368 pcimaketag(bus, device, function);
369 index += 1;
370 if (index >= limit)
371 goto out;
372 }
373 }
374 }
375 out:
376 return index;
377 }
378