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