1*c874b890Sphx /* $NetBSD: pci.c,v 1.5 2011/03/10 21:11:49 phx Exp $ */
2b75de019Snisimura
3b75de019Snisimura /*-
4b75de019Snisimura * Copyright (c) 2007 The NetBSD Foundation, Inc.
5b75de019Snisimura * All rights reserved.
6b75de019Snisimura *
7b75de019Snisimura * This code is derived from software contributed to The NetBSD Foundation
8b75de019Snisimura * by Tohru Nishimura.
9b75de019Snisimura *
10b75de019Snisimura * Redistribution and use in source and binary forms, with or without
11b75de019Snisimura * modification, are permitted provided that the following conditions
12b75de019Snisimura * are met:
13b75de019Snisimura * 1. Redistributions of source code must retain the above copyright
14b75de019Snisimura * notice, this list of conditions and the following disclaimer.
15b75de019Snisimura * 2. Redistributions in binary form must reproduce the above copyright
16b75de019Snisimura * notice, this list of conditions and the following disclaimer in the
17b75de019Snisimura * documentation and/or other materials provided with the distribution.
18b75de019Snisimura *
19b75de019Snisimura * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20b75de019Snisimura * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21b75de019Snisimura * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22b75de019Snisimura * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23b75de019Snisimura * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24b75de019Snisimura * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25b75de019Snisimura * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26b75de019Snisimura * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27b75de019Snisimura * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28b75de019Snisimura * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29b75de019Snisimura * POSSIBILITY OF SUCH DAMAGE.
30b75de019Snisimura */
31b75de019Snisimura
32b75de019Snisimura #include <sys/param.h>
33b75de019Snisimura
34b75de019Snisimura #include <lib/libsa/stand.h>
35b75de019Snisimura
36b75de019Snisimura
37b75de019Snisimura #define MAXNDEVS 32
38b75de019Snisimura
39b75de019Snisimura #include "globals.h"
40b75de019Snisimura
41b75de019Snisimura static unsigned cfgread(int, int, int, int);
42b75de019Snisimura static void cfgwrite(int, int, int, int, unsigned);
43b75de019Snisimura static void _buswalk(int,
44b75de019Snisimura int (*)(int, int, int, unsigned long), unsigned long);
45b75de019Snisimura static int _pcilookup(int,
46b75de019Snisimura int (*)(int, int, int, unsigned long), unsigned long,
4702a02bc0Snisimura struct pcidev *, int, int);
48b75de019Snisimura static int deviceinit(int, int, int, unsigned long);
49b75de019Snisimura static void memassign(int, int, int);
50b75de019Snisimura static int devmatch(int, int, int, unsigned long);
51b75de019Snisimura static int clsmatch(int, int, int, unsigned long);
52b75de019Snisimura
53b75de019Snisimura unsigned memstart, memlimit;
54b75de019Snisimura unsigned iostart, iolimit;
55b75de019Snisimura unsigned maxbus;
56b75de019Snisimura
57b75de019Snisimura void
pcisetup(void)58b75de019Snisimura pcisetup(void)
59b75de019Snisimura {
60b75de019Snisimura
61b75de019Snisimura memstart = PCI_MEMBASE;
62b75de019Snisimura memlimit = PCI_MEMLIMIT;
63b75de019Snisimura iostart = PCI_IOBASE;
64b75de019Snisimura iolimit = PCI_IOLIMIT;
65b75de019Snisimura maxbus = 0;
66b75de019Snisimura
67b75de019Snisimura (void)_buswalk(0, deviceinit, 0UL);
68b75de019Snisimura }
69b75de019Snisimura
70b75de019Snisimura int
pcifinddev(unsigned vend,unsigned prod,unsigned * tag)71b75de019Snisimura pcifinddev(unsigned vend, unsigned prod, unsigned *tag)
72b75de019Snisimura {
7302a02bc0Snisimura unsigned pciid;
7402a02bc0Snisimura struct pcidev target;
75b75de019Snisimura
76b75de019Snisimura pciid = PCI_DEVICE(vend, prod);
7702a02bc0Snisimura if (_pcilookup(0, devmatch, pciid, &target, 0, 1)) {
7802a02bc0Snisimura *tag = target.bdf;
79b75de019Snisimura return 0;
80b75de019Snisimura }
81b75de019Snisimura *tag = ~0;
82b75de019Snisimura return -1;
83b75de019Snisimura }
84b75de019Snisimura
85b75de019Snisimura int
pcilookup(type,list,max)86b75de019Snisimura pcilookup(type, list, max)
87b75de019Snisimura unsigned type;
8802a02bc0Snisimura struct pcidev *list;
89b75de019Snisimura int max;
90b75de019Snisimura {
91b75de019Snisimura
92b75de019Snisimura return _pcilookup(0, clsmatch, type, list, 0, max);
93b75de019Snisimura }
94b75de019Snisimura
95b75de019Snisimura unsigned
pcimaketag(int b,int d,int f)96b75de019Snisimura pcimaketag(int b, int d, int f)
97b75de019Snisimura {
98b75de019Snisimura
99b75de019Snisimura return (1U << 31) | (b << 16) | (d << 11) | (f << 8);
100b75de019Snisimura }
101b75de019Snisimura
102b75de019Snisimura void
pcidecomposetag(unsigned tag,int * b,int * d,int * f)103b75de019Snisimura pcidecomposetag(unsigned tag, int *b, int *d, int *f)
104b75de019Snisimura {
105b75de019Snisimura
106b75de019Snisimura if (b != NULL)
107b75de019Snisimura *b = (tag >> 16) & 0xff;
108b75de019Snisimura if (d != NULL)
109b75de019Snisimura *d = (tag >> 11) & 0x1f;
110b75de019Snisimura if (f != NULL)
111b75de019Snisimura *f = (tag >> 8) & 0x7;
112b75de019Snisimura return;
113b75de019Snisimura }
114b75de019Snisimura
115b75de019Snisimura unsigned
pcicfgread(unsigned tag,int off)116b75de019Snisimura pcicfgread(unsigned tag, int off)
117b75de019Snisimura {
118b75de019Snisimura unsigned cfg;
119b75de019Snisimura
120b75de019Snisimura cfg = tag | (off &~ 03);
121b75de019Snisimura iohtole32(CONFIG_ADDR, cfg);
122b75de019Snisimura return iole32toh(CONFIG_DATA);
123b75de019Snisimura }
124b75de019Snisimura
125b75de019Snisimura void
pcicfgwrite(unsigned tag,int off,unsigned val)126b75de019Snisimura pcicfgwrite(unsigned tag, int off, unsigned val)
127b75de019Snisimura {
128b75de019Snisimura unsigned cfg;
129b75de019Snisimura
130b75de019Snisimura cfg = tag | (off &~ 03);
131b75de019Snisimura iohtole32(CONFIG_ADDR, cfg);
132b75de019Snisimura iohtole32(CONFIG_DATA, val);
133b75de019Snisimura }
134b75de019Snisimura
135b75de019Snisimura static unsigned
cfgread(int b,int d,int f,int off)136b75de019Snisimura cfgread(int b, int d, int f, int off)
137b75de019Snisimura {
138b75de019Snisimura unsigned cfg;
139b75de019Snisimura
140b75de019Snisimura off &= ~03;
141b75de019Snisimura cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
142b75de019Snisimura iohtole32(CONFIG_ADDR, cfg);
143b75de019Snisimura return iole32toh(CONFIG_DATA);
144b75de019Snisimura }
145b75de019Snisimura
146b75de019Snisimura static void
cfgwrite(int b,int d,int f,int off,unsigned val)147b75de019Snisimura cfgwrite(int b, int d, int f, int off, unsigned val)
148b75de019Snisimura {
149b75de019Snisimura unsigned cfg;
150b75de019Snisimura
151b75de019Snisimura off &= ~03;
152b75de019Snisimura cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
153b75de019Snisimura iohtole32(CONFIG_ADDR, cfg);
154b75de019Snisimura iohtole32(CONFIG_DATA, val);
155b75de019Snisimura }
156b75de019Snisimura
157b75de019Snisimura static void
_buswalk(int bus,int (* proc)(int,int,int,unsigned long),unsigned long data)158b75de019Snisimura _buswalk(int bus, int (*proc)(int, int, int, unsigned long), unsigned long data)
159b75de019Snisimura {
160b75de019Snisimura int device, function, nfunctions;
161b75de019Snisimura unsigned pciid, bhlcr;
162b75de019Snisimura
163b75de019Snisimura for (device = 0; device < MAXNDEVS; device++) {
164b75de019Snisimura pciid = cfgread(bus, device, 0, PCI_ID_REG);
165b75de019Snisimura if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
166b75de019Snisimura continue;
167b75de019Snisimura if (PCI_VENDOR(pciid) == 0)
168b75de019Snisimura continue;
169b75de019Snisimura bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
170b75de019Snisimura nfunctions = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
171b75de019Snisimura for (function = 0; function < nfunctions; function++) {
172b75de019Snisimura pciid = cfgread(bus, device, function, PCI_ID_REG);
173b75de019Snisimura if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
174b75de019Snisimura continue;
175b75de019Snisimura if (PCI_VENDOR(pciid) == 0)
176b75de019Snisimura continue;
177b75de019Snisimura
178b75de019Snisimura if ((*proc)(bus, device, function, data) != 0)
179b75de019Snisimura goto out; /* early exit */
180b75de019Snisimura }
181b75de019Snisimura }
182b75de019Snisimura out:;
183b75de019Snisimura }
184b75de019Snisimura
185b75de019Snisimura static int
deviceinit(int bus,int dev,int func,unsigned long data)186b75de019Snisimura deviceinit(int bus, int dev, int func, unsigned long data)
187b75de019Snisimura {
188b75de019Snisimura unsigned val;
189b75de019Snisimura
190b75de019Snisimura /* 0x00 */
191f752ef75Sphx #ifdef DEBUG
192b75de019Snisimura printf("%02d:%02d:%02d:", bus, dev, func);
193*c874b890Sphx val = cfgread(bus, dev, func, PCI_ID_REG);
194b75de019Snisimura printf(" chip %04x.%04x", val & 0xffff, val>>16);
195b75de019Snisimura val = cfgread(bus, dev, func, 0x2c);
196b75de019Snisimura printf(" card %04x.%04x", val & 0xffff, val>>16);
197*c874b890Sphx val = cfgread(bus, dev, func, PCI_CLASS_REG);
198b75de019Snisimura printf(" rev %02x class %02x.%02x.%02x",
199*c874b890Sphx PCI_REVISION(val), (val>>24), (val>>16) & 0xff,
200*c874b890Sphx PCI_INTERFACE(val));
201*c874b890Sphx val = cfgread(bus, dev, func, PCI_BHLC_REG);
202b75de019Snisimura printf(" hdr %02x\n", (val>>16) & 0xff);
203f752ef75Sphx #endif
204b75de019Snisimura
205b75de019Snisimura /* 0x04 */
206*c874b890Sphx val = cfgread(bus, dev, func, PCI_COMMAND_STATUS_REG);
207b75de019Snisimura val |= 0xffff0107; /* enable IO,MEM,MASTER,SERR */
208b75de019Snisimura cfgwrite(bus, dev, func, 0x04, val);
209b75de019Snisimura
210b75de019Snisimura /* 0x0c */
211b75de019Snisimura val = 0x80 << 8 | 0x08 /* 32B cache line */;
212*c874b890Sphx cfgwrite(bus, dev, func, PCI_BHLC_REG, val);
213b75de019Snisimura
21401a00b1bSnisimura /* 0x3c */
21501a00b1bSnisimura val = cfgread(bus, dev, func, 0x3c) & ~0xff;
21601a00b1bSnisimura val |= dev; /* assign IDSEL */
21701a00b1bSnisimura cfgwrite(bus, dev, func, 0x3c, val);
21801a00b1bSnisimura
219b75de019Snisimura /* skip legacy mode IDE controller BAR assignment */
220b75de019Snisimura val = cfgread(bus, dev, func, PCI_CLASS_REG);
221*c874b890Sphx if (PCI_CLASS(val) == PCI_CLASS_IDE && (PCI_INTERFACE(val) & 0x05) == 0)
222b75de019Snisimura return 0;
223b75de019Snisimura
224b75de019Snisimura memassign(bus, dev, func);
225b75de019Snisimura
226b75de019Snisimura /* descending toward PCI-PCI bridge */
227*c874b890Sphx if ((cfgread(bus, dev, func, PCI_CLASS_REG) >> 16) == PCI_CLASS_PPB) {
228b75de019Snisimura unsigned new;
229b75de019Snisimura
230b75de019Snisimura /* 0x18 */
231b75de019Snisimura new = (maxbus += 1);
232b75de019Snisimura val = (0xff << 16) | (new << 8) | bus;
233b75de019Snisimura cfgwrite(bus, dev, func, 0x18, val);
234b75de019Snisimura
235b75de019Snisimura /* 0x1c and 0x30 */
236b75de019Snisimura val = (iostart + (0xfff)) & ~0xfff; /* 4KB boundary */
237b75de019Snisimura iostart = val;
238b75de019Snisimura val = 0xffff0000 | (iolimit & 0xf000) | (val & 0xf000) >> 8;
239b75de019Snisimura cfgwrite(bus, dev, func, 0x1c, val);
240b75de019Snisimura val = (iolimit & 0xffff0000) | (val & 0xffff0000) >> 16;
241b75de019Snisimura cfgwrite(bus, dev, func, 0x30, val);
242b75de019Snisimura
243b75de019Snisimura /* 0x20 */
244b75de019Snisimura val = (memstart + 0xfffff) &~ 0xfffff; /* 1MB boundary */
245b75de019Snisimura memstart = val;
246b75de019Snisimura val = (memlimit & 0xffff0000) | (val & 0xffff0000) >> 16;
247b75de019Snisimura cfgwrite(bus, dev, func, 0x20, val);
248b75de019Snisimura
249b75de019Snisimura /* redo 0x04 */
250b75de019Snisimura val = cfgread(bus, dev, func, 0x04);
251b75de019Snisimura val |= 0xffff0107;
252b75de019Snisimura cfgwrite(bus, dev, func, 0x04, val);
253b75de019Snisimura
254b75de019Snisimura _buswalk(new, deviceinit, data);
255b75de019Snisimura
256b75de019Snisimura /* adjust 0x18 */
257b75de019Snisimura val = cfgread(bus, dev, func, 0x18);
258b75de019Snisimura val = (maxbus << 16) | (val & 0xffff);
259b75de019Snisimura cfgwrite(bus, dev, func, 0x18, val);
260b75de019Snisimura }
261b75de019Snisimura return 0;
262b75de019Snisimura }
263b75de019Snisimura
264b75de019Snisimura static void
memassign(int bus,int dev,int func)265b75de019Snisimura memassign(int bus, int dev, int func)
266b75de019Snisimura {
267b75de019Snisimura unsigned val, maxbar, mapr, req, mapbase, size;
268b75de019Snisimura
269b75de019Snisimura val = cfgread(bus, dev, func, 0x0c);
270b75de019Snisimura switch (PCI_HDRTYPE_TYPE(val)) {
271b75de019Snisimura case 0:
272b75de019Snisimura maxbar = 0x10 + 6 * 4; break;
273b75de019Snisimura case 1:
274b75de019Snisimura maxbar = 0x10 + 2 * 4; break;
275b75de019Snisimura default:
276b75de019Snisimura maxbar = 0x10 + 1 * 4; break;
277b75de019Snisimura }
278b75de019Snisimura for (mapr = 0x10; mapr < maxbar; mapr += 4) {
279b75de019Snisimura cfgwrite(bus, dev, func, mapr, 0xffffffff);
280b75de019Snisimura val = cfgread(bus, dev, func, mapr);
281b75de019Snisimura if (val & 01) {
282b75de019Snisimura /* PCI IO space */
283b75de019Snisimura req = ~(val & 0xfffffffc) + 1;
284b75de019Snisimura if (req & (req - 1)) /* power of 2 */
285b75de019Snisimura continue;
286b75de019Snisimura if (req == 0) /* ever exists */
287b75de019Snisimura continue;
288b75de019Snisimura size = (req > 0x10) ? req : 0x10;
289b75de019Snisimura mapbase = (iostart + size - 1) & ~(size - 1);
290b75de019Snisimura if (mapbase + size > iolimit)
291b75de019Snisimura continue;
292b75de019Snisimura
293b75de019Snisimura iostart = mapbase + size;
294b75de019Snisimura /* establish IO space */
295b75de019Snisimura cfgwrite(bus, dev, func, mapr, mapbase | 01);
296b75de019Snisimura }
297b75de019Snisimura else {
298b75de019Snisimura /* PCI memory space */
299b75de019Snisimura req = ~(val & 0xfffffff0) + 1;
300b75de019Snisimura if (req & (req - 1)) /* power of 2 */
301b75de019Snisimura continue;
302b75de019Snisimura if (req == 0) /* ever exists */
303b75de019Snisimura continue;
304b75de019Snisimura val &= 0x6;
305b75de019Snisimura if (val == 2 || val == 6)
306b75de019Snisimura continue;
307b75de019Snisimura size = (req > 0x1000) ? req : 0x1000;
308b75de019Snisimura mapbase = (memstart + size - 1) & ~(size - 1);
309b75de019Snisimura if (mapbase + size > memlimit)
310b75de019Snisimura continue;
311b75de019Snisimura
312b75de019Snisimura memstart = mapbase + size;
313b75de019Snisimura /* establish memory space */
314b75de019Snisimura cfgwrite(bus, dev, func, mapr, mapbase);
315b75de019Snisimura if (val == 4) {
316b75de019Snisimura mapr += 4;
317b75de019Snisimura cfgwrite(bus, dev, func, mapr, 0);
318b75de019Snisimura }
319b75de019Snisimura }
320f752ef75Sphx DPRINTF(("%s base %x size %x\n", (val & 01) ? "i/o" : "mem",
321f752ef75Sphx mapbase, size));
322b75de019Snisimura }
323b75de019Snisimura }
324b75de019Snisimura
325b75de019Snisimura static int
devmatch(int bus,int dev,int func,unsigned long data)326b75de019Snisimura devmatch(int bus, int dev, int func, unsigned long data)
327b75de019Snisimura {
328b75de019Snisimura unsigned pciid;
329b75de019Snisimura
330b75de019Snisimura pciid = cfgread(bus, dev, func, PCI_ID_REG);
331b75de019Snisimura return (pciid == (unsigned)data);
332b75de019Snisimura }
333b75de019Snisimura
334b75de019Snisimura static int
clsmatch(int bus,int dev,int func,unsigned long data)335b75de019Snisimura clsmatch(int bus, int dev, int func, unsigned long data)
336b75de019Snisimura {
337b75de019Snisimura unsigned class;
338b75de019Snisimura
339b75de019Snisimura class = cfgread(bus, dev, func, PCI_CLASS_REG);
340*c874b890Sphx return PCI_CLASS(class) == (unsigned)data;
341b75de019Snisimura }
342b75de019Snisimura
343b75de019Snisimura static int
_pcilookup(int bus,int (* match)(int,int,int,unsigned long),unsigned long data,struct pcidev * list,int index,int limit)34402a02bc0Snisimura _pcilookup(int bus, int (*match)(int, int, int, unsigned long), unsigned long data, struct pcidev *list, int index, int limit)
345b75de019Snisimura {
346b75de019Snisimura int device, function, nfuncs;
347b75de019Snisimura unsigned pciid, bhlcr, class;
348b75de019Snisimura
349b75de019Snisimura for (device = 0; device < MAXNDEVS; device++) {
350b75de019Snisimura pciid = cfgread(bus, device, 0, PCI_ID_REG);
351b75de019Snisimura if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
352b75de019Snisimura continue;
353b75de019Snisimura if (PCI_VENDOR(pciid) == 0)
354b75de019Snisimura continue;
355b75de019Snisimura class = cfgread(bus, device, 0, PCI_CLASS_REG);
356*c874b890Sphx if (PCI_CLASS(class) == PCI_CLASS_PPB) {
357b75de019Snisimura /* exploring bus beyond PCI-PCI bridge */
358b75de019Snisimura index = _pcilookup(bus + 1,
359b75de019Snisimura match, data, list, index, limit);
360b75de019Snisimura if (index >= limit)
361b75de019Snisimura goto out;
362b75de019Snisimura continue;
363b75de019Snisimura }
364b75de019Snisimura bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
365b75de019Snisimura nfuncs = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
366b75de019Snisimura for (function = 0; function < nfuncs; function++) {
367b75de019Snisimura pciid = cfgread(bus, device, function, PCI_ID_REG);
368b75de019Snisimura if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
369b75de019Snisimura continue;
370b75de019Snisimura if (PCI_VENDOR(pciid) == 0)
371b75de019Snisimura continue;
372b75de019Snisimura if ((*match)(bus, device, function, data)) {
37302a02bc0Snisimura list[index].pvd = pciid;
37402a02bc0Snisimura list[index].bdf =
375b75de019Snisimura pcimaketag(bus, device, function);
376b75de019Snisimura index += 1;
377b75de019Snisimura if (index >= limit)
378b75de019Snisimura goto out;
379b75de019Snisimura }
380b75de019Snisimura }
381b75de019Snisimura }
382b75de019Snisimura out:
383b75de019Snisimura return index;
384b75de019Snisimura }
385