xref: /netbsd-src/sys/arch/sandpoint/stand/altboot/pci.c (revision c874b8909d1d376f2fc383853803f4f444fcd6fe)
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