Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
256
pcicfgr8(Pcidev* pcidev, int rno)
257
{
258
	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
259
}
260
 
261
void
262
pcicfgw8(Pcidev* pcidev, int rno, int data)
263
{
264
	pcicfgrw8(pcidev->tbdf, rno, data, 0);
265
}
266
 
267
static int
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
311
pcicfgr16(Pcidev* pcidev, int rno)
312
{
313
	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
314
}
315
 
316
void
317
pcicfgw16(Pcidev* pcidev, int rno, int data)
318
{
319
	pcicfgrw16(pcidev->tbdf, rno, data, 0);
320
}
321
 
322
static int
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
365
pcicfgr32(Pcidev* pcidev, int rno)
366
{
367
	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
368
}
369
 
370
void
371
pcicfgw32(Pcidev* pcidev, int rno, int data)
372
{
373
	pcicfgrw32(pcidev->tbdf, rno, data, 0);
374
}
375
 
376
Pcidev*
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
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
}