xref: /plan9/sys/src/cmd/aux/vga/pci.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "pci.h"
6 #include "vga.h"
7 
8 /*
9  * PCI support code.
10  * There really should be a driver for this, it's not terribly safe
11  * without locks or restrictions on what can be poked (e.g. Axil NX801).
12  */
13 enum {					/* configuration mechanism #1 */
14 	PciADDR		= 0xCF8,	/* CONFIG_ADDRESS */
15 	PciDATA		= 0xCFC,	/* CONFIG_DATA */
16 
17 					/* configuration mechanism #2 */
18 	PciCSE		= 0xCF8,	/* configuration space enable */
19 	PciFORWARD	= 0xCFA,	/* which bus */
20 
21 	MaxFNO		= 7,
22 	MaxUBN		= 255,
23 };
24 
25 static int pcicfgmode = -1;
26 static int pcimaxdno;
27 static Pcidev* pciroot;
28 static Pcidev* pcilist;
29 static Pcidev* pcitail;
30 
31 static int pcicfgrw32(int, int, int, int);
32 
33 static int
pciscan(int bno,Pcidev ** list)34 pciscan(int bno, Pcidev** list)
35 {
36 	ulong v;
37 	Pcidev *p, *head, *tail;
38 	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
39 
40 	maxubn = bno;
41 	head = nil;
42 	tail = nil;
43 	for(dno = 0; dno <= pcimaxdno; dno++){
44 		maxfno = 0;
45 		for(fno = 0; fno <= maxfno; fno++){
46 			/*
47 			 * For this possible device, form the bus+device+function
48 			 * triplet needed to address it and try to read the vendor
49 			 * and device ID. If successful, allocate a device struct
50 			 * and start to fill it in with some useful information from
51 			 * the device's configuration space.
52 			 */
53 			tbdf = MKBUS(BusPCI, bno, dno, fno);
54 			l = pcicfgrw32(tbdf, PciVID, 0, 1);
55 			if(l == 0xFFFFFFFF || l == 0)
56 				continue;
57 			p = mallocz(sizeof(*p), 1);
58 			p->tbdf = tbdf;
59 			p->vid = l;
60 			p->did = l>>16;
61 			p->rid = pcicfgr8(p, PciRID);
62 
63 			if(pcilist != nil)
64 				pcitail->list = p;
65 			else
66 				pcilist = p;
67 			pcitail = p;
68 
69 			p->intl = pcicfgr8(p, PciINTL);
70 			p->ccru = pcicfgr16(p, PciCCRu);
71 
72 			/*
73 			 * If the device is a multi-function device adjust the
74 			 * loop count so all possible functions are checked.
75 			 */
76 			hdt = pcicfgr8(p, PciHDT);
77 			if(hdt & 0x80)
78 				maxfno = MaxFNO;
79 
80 			/*
81 			 * If appropriate, read the base address registers
82 			 * and work out the sizes.
83 			 */
84 			switch(p->ccru>>8){
85 
86 			case 0x01:		/* mass storage controller */
87 			case 0x02:		/* network controller */
88 			case 0x03:		/* display controller */
89 			case 0x04:		/* multimedia device */
90 			case 0x07:		/* simple communication controllers */
91 			case 0x08:		/* base system peripherals */
92 			case 0x09:		/* input devices */
93 			case 0x0A:		/* docking stations */
94 			case 0x0B:		/* processors */
95 			case 0x0C:		/* serial bus controllers */
96 				if((hdt & 0x7F) != 0)
97 					break;
98 				rno = PciBAR0 - 4;
99 				for(i = 0; i < nelem(p->mem); i++){
100 					rno += 4;
101 					p->mem[i].bar = pcicfgr32(p, rno);
102 					pcicfgw32(p, rno, -1);
103 					v = pcicfgr32(p, rno);
104 					pcicfgw32(p, rno, p->mem[i].bar);
105 					p->mem[i].size = -(v & ~0xF);
106 				}
107 				break;
108 
109 			case 0x00:
110 			case 0x05:		/* memory controller */
111 			case 0x06:		/* bridge device */
112 			default:
113 				break;
114 			}
115 
116 			if(head != nil)
117 				tail->link = p;
118 			else
119 				head = p;
120 			tail = p;
121 		}
122 	}
123 
124 	*list = head;
125 	for(p = head; p != nil; p = p->link){
126 		/*
127 		 * Find PCI-PCI bridges and recursively descend the tree.
128 		 */
129 		if(p->ccru != ((0x06<<8)|0x04))
130 			continue;
131 
132 		/*
133 		 * If the secondary or subordinate bus number is not initialised
134 		 * try to do what the PCI BIOS should have done and fill in the
135 		 * numbers as the tree is descended. On the way down the subordinate
136 		 * bus number is set to the maximum as it's not known how many
137 		 * buses are behind this one; the final value is set on the way
138 		 * back up.
139 		 */
140 		sbn = pcicfgr8(p, PciSBN);
141 		ubn = pcicfgr8(p, PciUBN);
142 		if(sbn == 0 || ubn == 0){
143 			sbn = maxubn+1;
144 			/*
145 			 * Make sure memory, I/O and master enables are off,
146 			 * set the primary, secondary and subordinate bus numbers
147 			 * and clear the secondary status before attempting to
148 			 * scan the secondary bus.
149 			 *
150 			 * Initialisation of the bridge should be done here.
151 			 */
152 			pcicfgw32(p, PciPCR, 0xFFFF0000);
153 			l = (MaxUBN<<16)|(sbn<<8)|bno;
154 			pcicfgw32(p, PciPBN, l);
155 			pcicfgw16(p, PciSPSR, 0xFFFF);
156 			maxubn = pciscan(sbn, &p->bridge);
157 			l = (maxubn<<16)|(sbn<<8)|bno;
158 			pcicfgw32(p, PciPBN, l);
159 		}
160 		else{
161 			maxubn = ubn;
162 			pciscan(sbn, &p->bridge);
163 		}
164 	}
165 
166 	return maxubn;
167 }
168 
169 static void
pcicfginit(void)170 pcicfginit(void)
171 {
172 #ifdef kernel
173 	char *p;
174 #endif /* kernel */
175 	int bno;
176 	Pcidev **list;
177 
178 	if(pcicfgmode == -1){
179 		/*
180 		 * Try to determine which PCI configuration mode is implemented.
181 		 * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
182 		 * a DWORD at 0xCF8 and another at 0xCFC and will pass through
183 		 * any non-DWORD accesses as normal I/O cycles. There shouldn't be
184 		 * a device behind these addresses so if Mode2 accesses fail try
185 		 * for Mode1 (which is preferred, Mode2 is deprecated).
186 		 */
187 		outportb(PciCSE, 0);
188 		if(inportb(PciCSE) == 0){
189 			pcicfgmode = 2;
190 			pcimaxdno = 15;
191 		}
192 		else{
193 			outportl(PciADDR, 0);
194 			if(inportl(PciADDR) == 0){
195 				pcicfgmode = 1;
196 				pcimaxdno = 31;
197 			}
198 		}
199 
200 		if(pcicfgmode > 0){
201 			list = &pciroot;
202 			for(bno = 0; bno < 256; bno++){
203 				bno = pciscan(bno, list);
204 				while(*list)
205 					list = &(*list)->link;
206 			}
207 
208 		}
209 	}
210 }
211 
212 static int
pcicfgrw8(int tbdf,int rno,int data,int read)213 pcicfgrw8(int tbdf, int rno, int data, int read)
214 {
215 	int o, type, x;
216 
217 	if(pcicfgmode == -1)
218 		pcicfginit();
219 
220 	if(BUSBNO(tbdf))
221 		type = 0x01;
222 	else
223 		type = 0x00;
224 	x = -1;
225 	if(BUSDNO(tbdf) > pcimaxdno)
226 		return x;
227 
228 	switch(pcicfgmode){
229 
230 	case 1:
231 		o = rno & 0x03;
232 		rno &= ~0x03;
233 		outportl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
234 		if(read)
235 			x = inportb(PciDATA+o);
236 		else
237 			outportb(PciDATA+o, data);
238 		outportl(PciADDR, 0);
239 		break;
240 
241 	case 2:
242 		outportb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
243 		outportb(PciFORWARD, BUSBNO(tbdf));
244 		if(read)
245 			x = inportb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
246 		else
247 			outportb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
248 		outportb(PciCSE, 0);
249 		break;
250 	}
251 
252 	return x;
253 }
254 
255 int
pcicfgr8(Pcidev * pcidev,int rno)256 pcicfgr8(Pcidev* pcidev, int rno)
257 {
258 	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
259 }
260 
261 void
pcicfgw8(Pcidev * pcidev,int rno,int data)262 pcicfgw8(Pcidev* pcidev, int rno, int data)
263 {
264 	pcicfgrw8(pcidev->tbdf, rno, data, 0);
265 }
266 
267 static int
pcicfgrw16(int tbdf,int rno,int data,int read)268 pcicfgrw16(int tbdf, int rno, int data, int read)
269 {
270 	int o, type, x;
271 
272 	if(pcicfgmode == -1)
273 		pcicfginit();
274 
275 	if(BUSBNO(tbdf))
276 		type = 0x01;
277 	else
278 		type = 0x00;
279 	x = -1;
280 	if(BUSDNO(tbdf) > pcimaxdno)
281 		return x;
282 
283 	switch(pcicfgmode){
284 
285 	case 1:
286 		o = rno & 0x02;
287 		rno &= ~0x03;
288 		outportl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
289 		if(read)
290 			x = inportw(PciDATA+o);
291 		else
292 			outportw(PciDATA+o, data);
293 		outportl(PciADDR, 0);
294 		break;
295 
296 	case 2:
297 		outportb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
298 		outportb(PciFORWARD, BUSBNO(tbdf));
299 		if(read)
300 			x = inportw((0xC000|(BUSDNO(tbdf)<<8)) + rno);
301 		else
302 			outportw((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
303 		outportb(PciCSE, 0);
304 		break;
305 	}
306 
307 	return x;
308 }
309 
310 int
pcicfgr16(Pcidev * pcidev,int rno)311 pcicfgr16(Pcidev* pcidev, int rno)
312 {
313 	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
314 }
315 
316 void
pcicfgw16(Pcidev * pcidev,int rno,int data)317 pcicfgw16(Pcidev* pcidev, int rno, int data)
318 {
319 	pcicfgrw16(pcidev->tbdf, rno, data, 0);
320 }
321 
322 static int
pcicfgrw32(int tbdf,int rno,int data,int read)323 pcicfgrw32(int tbdf, int rno, int data, int read)
324 {
325 	int type, x;
326 
327 	if(pcicfgmode == -1)
328 		pcicfginit();
329 
330 	if(BUSBNO(tbdf))
331 		type = 0x01;
332 	else
333 		type = 0x00;
334 	x = -1;
335 	if(BUSDNO(tbdf) > pcimaxdno)
336 		return x;
337 
338 	switch(pcicfgmode){
339 
340 	case 1:
341 		rno &= ~0x03;
342 		outportl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
343 		if(read)
344 			x = inportl(PciDATA);
345 		else
346 			outportl(PciDATA, data);
347 		outportl(PciADDR, 0);
348 		break;
349 
350 	case 2:
351 		outportb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
352 		outportb(PciFORWARD, BUSBNO(tbdf));
353 		if(read)
354 			x = inportl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
355 		else
356 			outportl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
357 		outportb(PciCSE, 0);
358 		break;
359 	}
360 
361 	return x;
362 }
363 
364 int
pcicfgr32(Pcidev * pcidev,int rno)365 pcicfgr32(Pcidev* pcidev, int rno)
366 {
367 	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
368 }
369 
370 void
pcicfgw32(Pcidev * pcidev,int rno,int data)371 pcicfgw32(Pcidev* pcidev, int rno, int data)
372 {
373 	pcicfgrw32(pcidev->tbdf, rno, data, 0);
374 }
375 
376 Pcidev*
pcimatch(Pcidev * prev,int vid,int did)377 pcimatch(Pcidev* prev, int vid, int did)
378 {
379 	if(pcicfgmode == -1)
380 		pcicfginit();
381 
382 	if(prev == nil)
383 		prev = pcilist;
384 	else
385 		prev = prev->list;
386 
387 	while(prev != nil) {
388 		if(prev->vid == vid && (did == 0 || prev->did == did))
389 			break;
390 		prev = prev->list;
391 	}
392 	return prev;
393 }
394 
395 void
pcihinv(Pcidev * p)396 pcihinv(Pcidev* p)
397 {
398 	int i;
399 	Pcidev *t;
400 
401 	if(pcicfgmode == -1)
402 		pcicfginit();
403 
404 
405 	if(p == nil) {
406 		p = pciroot;
407 		Bprint(&stdout, "bus dev type vid  did intl memory\n");
408 	}
409 	for(t = p; t != nil; t = t->link) {
410 		Bprint(&stdout, "%d  %2d/%d %.4ux %.4ux %.4ux %2d  ",
411 			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
412 			t->ccru, t->vid, t->did, t->intl);
413 
414 		for(i = 0; i < nelem(p->mem); i++) {
415 			if(t->mem[i].size == 0)
416 				continue;
417 			Bprint(&stdout, "%d:%.8lux %d ", i,
418 				t->mem[i].bar, t->mem[i].size);
419 		}
420 		Bprint(&stdout, "\n");
421 	}
422 	while(p != nil) {
423 		if(p->bridge != nil)
424 			pcihinv(p->bridge);
425 		p = p->link;
426 	}
427 }
428