xref: /plan9/sys/src/9/mtx/pci.c (revision d0f3faac644260b801b3ce61d6da0b1e8e492dac)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * PCI support code.
39a747e4fSDavid du Colombier  */
49a747e4fSDavid du Colombier #include "u.h"
59a747e4fSDavid du Colombier #include "../port/lib.h"
69a747e4fSDavid du Colombier #include "mem.h"
79a747e4fSDavid du Colombier #include "dat.h"
89a747e4fSDavid du Colombier #include "fns.h"
99a747e4fSDavid du Colombier #include "io.h"
109a747e4fSDavid du Colombier #include "../port/error.h"
119a747e4fSDavid du Colombier 
129a747e4fSDavid du Colombier #define DBG	if(0) pcilog
139a747e4fSDavid du Colombier 
149a747e4fSDavid du Colombier struct
159a747e4fSDavid du Colombier {
169a747e4fSDavid du Colombier 	char	output[16384];
179a747e4fSDavid du Colombier 	int	ptr;
189a747e4fSDavid du Colombier }PCICONS;
199a747e4fSDavid du Colombier 
209a747e4fSDavid du Colombier int
pcilog(char * fmt,...)219a747e4fSDavid du Colombier pcilog(char *fmt, ...)
229a747e4fSDavid du Colombier {
239a747e4fSDavid du Colombier 	int n;
249a747e4fSDavid du Colombier 	va_list arg;
259a747e4fSDavid du Colombier 	char buf[PRINTSIZE];
269a747e4fSDavid du Colombier 
279a747e4fSDavid du Colombier 	va_start(arg, fmt);
289a747e4fSDavid du Colombier 	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
299a747e4fSDavid du Colombier 	va_end(arg);
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier 	memmove(PCICONS.output+PCICONS.ptr, buf, n);
329a747e4fSDavid du Colombier 	PCICONS.ptr += n;
339a747e4fSDavid du Colombier 	return n;
349a747e4fSDavid du Colombier }
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier enum
379a747e4fSDavid du Colombier {					/* configuration mechanism #1 */
389a747e4fSDavid du Colombier 	PciADDR		= 0xCF8,	/* CONFIG_ADDRESS */
399a747e4fSDavid du Colombier 	PciDATA		= 0xCFC,	/* CONFIG_DATA */
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier 					/* configuration mechanism #2 */
429a747e4fSDavid du Colombier 	PciCSE		= 0xCF8,	/* configuration space enable */
439a747e4fSDavid du Colombier 	PciFORWARD	= 0xCFA,	/* which bus */
449a747e4fSDavid du Colombier 
459a747e4fSDavid du Colombier 	MaxFNO		= 7,
469a747e4fSDavid du Colombier 	MaxUBN		= 255,
479a747e4fSDavid du Colombier };
489a747e4fSDavid du Colombier 
499a747e4fSDavid du Colombier enum
509a747e4fSDavid du Colombier {					/* command register */
519a747e4fSDavid du Colombier 	IOen		= (1<<0),
529a747e4fSDavid du Colombier 	MEMen		= (1<<1),
539a747e4fSDavid du Colombier 	MASen		= (1<<2),
549a747e4fSDavid du Colombier 	MemWrInv	= (1<<4),
559a747e4fSDavid du Colombier 	PErrEn		= (1<<6),
569a747e4fSDavid du Colombier 	SErrEn		= (1<<8),
579a747e4fSDavid du Colombier };
589a747e4fSDavid du Colombier 
599a747e4fSDavid du Colombier static Lock pcicfglock;
609a747e4fSDavid du Colombier static QLock pcicfginitlock;
619a747e4fSDavid du Colombier static int pcicfgmode = -1;
629a747e4fSDavid du Colombier static int pcimaxbno = 7;
639a747e4fSDavid du Colombier static int pcimaxdno;
649a747e4fSDavid du Colombier static Pcidev* pciroot;
659a747e4fSDavid du Colombier static Pcidev* pcilist;
669a747e4fSDavid du Colombier static Pcidev* pcitail;
679a747e4fSDavid du Colombier 
689a747e4fSDavid du Colombier static int pcicfgrw32(int, int, int, int);
699a747e4fSDavid du Colombier static int pcicfgrw8(int, int, int, int);
709a747e4fSDavid du Colombier 
719a747e4fSDavid du Colombier static char* bustypes[] = {
729a747e4fSDavid du Colombier 	"CBUSI",
739a747e4fSDavid du Colombier 	"CBUSII",
749a747e4fSDavid du Colombier 	"EISA",
759a747e4fSDavid du Colombier 	"FUTURE",
769a747e4fSDavid du Colombier 	"INTERN",
779a747e4fSDavid du Colombier 	"ISA",
789a747e4fSDavid du Colombier 	"MBI",
799a747e4fSDavid du Colombier 	"MBII",
809a747e4fSDavid du Colombier 	"MCA",
819a747e4fSDavid du Colombier 	"MPI",
829a747e4fSDavid du Colombier 	"MPSA",
839a747e4fSDavid du Colombier 	"NUBUS",
849a747e4fSDavid du Colombier 	"PCI",
859a747e4fSDavid du Colombier 	"PCMCIA",
869a747e4fSDavid du Colombier 	"TC",
879a747e4fSDavid du Colombier 	"VL",
889a747e4fSDavid du Colombier 	"VME",
899a747e4fSDavid du Colombier 	"XPRESS",
909a747e4fSDavid du Colombier };
919a747e4fSDavid du Colombier 
929a747e4fSDavid du Colombier #pragma	varargck	type	"T"	int
939a747e4fSDavid du Colombier 
949a747e4fSDavid du Colombier static int
tbdffmt(Fmt * fmt)959a747e4fSDavid du Colombier tbdffmt(Fmt* fmt)
969a747e4fSDavid du Colombier {
979a747e4fSDavid du Colombier 	char *p;
989a747e4fSDavid du Colombier 	int l, r, type, tbdf;
999a747e4fSDavid du Colombier 
1009a747e4fSDavid du Colombier 	if((p = malloc(READSTR)) == nil)
1019a747e4fSDavid du Colombier 		return fmtstrcpy(fmt, "(tbdfconv)");
1029a747e4fSDavid du Colombier 
1039a747e4fSDavid du Colombier 	switch(fmt->r){
1049a747e4fSDavid du Colombier 	case 'T':
1059a747e4fSDavid du Colombier 		tbdf = va_arg(fmt->args, int);
1069a747e4fSDavid du Colombier 		type = BUSTYPE(tbdf);
1079a747e4fSDavid du Colombier 		if(type < nelem(bustypes))
1089a747e4fSDavid du Colombier 			l = snprint(p, READSTR, bustypes[type]);
1099a747e4fSDavid du Colombier 		else
1109a747e4fSDavid du Colombier 			l = snprint(p, READSTR, "%d", type);
1119a747e4fSDavid du Colombier 		snprint(p+l, READSTR-l, ".%d.%d.%d",
1129a747e4fSDavid du Colombier 			BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
1139a747e4fSDavid du Colombier 		break;
1149a747e4fSDavid du Colombier 
1159a747e4fSDavid du Colombier 	default:
1169a747e4fSDavid du Colombier 		snprint(p, READSTR, "(tbdfconv)");
1179a747e4fSDavid du Colombier 		break;
1189a747e4fSDavid du Colombier 	}
1199a747e4fSDavid du Colombier 	r = fmtstrcpy(fmt, p);
1209a747e4fSDavid du Colombier 	free(p);
1219a747e4fSDavid du Colombier 
1229a747e4fSDavid du Colombier 	return r;
1239a747e4fSDavid du Colombier }
1249a747e4fSDavid du Colombier 
1259a747e4fSDavid du Colombier ulong
pcibarsize(Pcidev * p,int rno)1269a747e4fSDavid du Colombier pcibarsize(Pcidev *p, int rno)
1279a747e4fSDavid du Colombier {
1289a747e4fSDavid du Colombier 	ulong v, size;
1299a747e4fSDavid du Colombier 
1309a747e4fSDavid du Colombier 	v = pcicfgrw32(p->tbdf, rno, 0, 1);
1319a747e4fSDavid du Colombier 	pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
1329a747e4fSDavid du Colombier 	size = pcicfgrw32(p->tbdf, rno, 0, 1);
1339a747e4fSDavid du Colombier 	if(v & 1)
1349a747e4fSDavid du Colombier 		size |= 0xFFFF0000;
1359a747e4fSDavid du Colombier 	pcicfgrw32(p->tbdf, rno, v, 0);
1369a747e4fSDavid du Colombier 
1379a747e4fSDavid du Colombier 	return -(size & ~0x0F);
1389a747e4fSDavid du Colombier }
1399a747e4fSDavid du Colombier 
1409a747e4fSDavid du Colombier static int
pcisizcmp(void * a,void * b)1419a747e4fSDavid du Colombier pcisizcmp(void *a, void *b)
1429a747e4fSDavid du Colombier {
1439a747e4fSDavid du Colombier 	Pcisiz *aa, *bb;
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier 	aa = a;
1469a747e4fSDavid du Colombier 	bb = b;
1479a747e4fSDavid du Colombier 	return aa->siz - bb->siz;
1489a747e4fSDavid du Colombier }
1499a747e4fSDavid du Colombier 
1509a747e4fSDavid du Colombier static ulong
pcimask(ulong v)1519a747e4fSDavid du Colombier pcimask(ulong v)
1529a747e4fSDavid du Colombier {
1539a747e4fSDavid du Colombier 	ulong m;
1549a747e4fSDavid du Colombier 
1559a747e4fSDavid du Colombier 	m = BI2BY*sizeof(v);
1569a747e4fSDavid du Colombier 	for(m = 1<<(m-1); m != 0; m >>= 1) {
1579a747e4fSDavid du Colombier 		if(m & v)
1589a747e4fSDavid du Colombier 			break;
1599a747e4fSDavid du Colombier 	}
1609a747e4fSDavid du Colombier 
1619a747e4fSDavid du Colombier 	m--;
1629a747e4fSDavid du Colombier 	if((v & m) == 0)
1639a747e4fSDavid du Colombier 		return v;
1649a747e4fSDavid du Colombier 
1659a747e4fSDavid du Colombier 	v |= m;
1669a747e4fSDavid du Colombier 	return v+1;
1679a747e4fSDavid du Colombier }
1689a747e4fSDavid du Colombier 
1699a747e4fSDavid du Colombier static void
pcibusmap(Pcidev * root,ulong * pmema,ulong * pioa,int wrreg)1709a747e4fSDavid du Colombier pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
1719a747e4fSDavid du Colombier {
1729a747e4fSDavid du Colombier 	Pcidev *p;
1739a747e4fSDavid du Colombier 	int ntb, i, size, rno, hole;
1749a747e4fSDavid du Colombier 	ulong v, mema, ioa, sioa, smema, base, limit;
1759a747e4fSDavid du Colombier 	Pcisiz *table, *tptr, *mtb, *itb;
1769a747e4fSDavid du Colombier 	extern void qsort(void*, long, long, int (*)(void*, void*));
1779a747e4fSDavid du Colombier 
1789a747e4fSDavid du Colombier 	ioa = *pioa;
1799a747e4fSDavid du Colombier 	mema = *pmema;
1809a747e4fSDavid du Colombier 
1819a747e4fSDavid du Colombier 	DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n",
1829a747e4fSDavid du Colombier 		wrreg, root->tbdf, mema, ioa);
1839a747e4fSDavid du Colombier 
1849a747e4fSDavid du Colombier 	ntb = 0;
1859a747e4fSDavid du Colombier 	for(p = root; p != nil; p = p->link)
1869a747e4fSDavid du Colombier 		ntb++;
1879a747e4fSDavid du Colombier 
1889a747e4fSDavid du Colombier 	ntb *= (PciCIS-PciBAR0)/4;
1899a747e4fSDavid du Colombier 	table = malloc(2*ntb*sizeof(Pcisiz));
1909a747e4fSDavid du Colombier 	itb = table;
1919a747e4fSDavid du Colombier 	mtb = table+ntb;
1929a747e4fSDavid du Colombier 
1939a747e4fSDavid du Colombier 	/*
1949a747e4fSDavid du Colombier 	 * Build a table of sizes
1959a747e4fSDavid du Colombier 	 */
1969a747e4fSDavid du Colombier 	for(p = root; p != nil; p = p->link) {
1979a747e4fSDavid du Colombier 		if(p->ccrb == 0x06) {
1989a747e4fSDavid du Colombier 			if(p->ccru == 0x04 && p->bridge != nil) {
1999a747e4fSDavid du Colombier 				sioa = ioa;
2009a747e4fSDavid du Colombier 				smema = mema;
2019a747e4fSDavid du Colombier 				pcibusmap(p->bridge, &smema, &sioa, 0);
2029a747e4fSDavid du Colombier 
2039a747e4fSDavid du Colombier 				hole = pcimask(smema-mema);
2049a747e4fSDavid du Colombier 				if(hole < (1<<20))
2059a747e4fSDavid du Colombier 					hole = 1<<20;
2069a747e4fSDavid du Colombier 				p->mema.size = hole;
2079a747e4fSDavid du Colombier 
2089a747e4fSDavid du Colombier 				hole = pcimask(sioa-ioa);
2099a747e4fSDavid du Colombier 				if(hole < (1<<12))
2109a747e4fSDavid du Colombier 					hole = 1<<12;
2119a747e4fSDavid du Colombier 
2129a747e4fSDavid du Colombier 				p->ioa.size = hole;
2139a747e4fSDavid du Colombier 
2149a747e4fSDavid du Colombier 				itb->dev = p;
2159a747e4fSDavid du Colombier 				itb->bar = -1;
2169a747e4fSDavid du Colombier 				itb->siz = p->ioa.size;
2179a747e4fSDavid du Colombier 				itb++;
2189a747e4fSDavid du Colombier 
2199a747e4fSDavid du Colombier 				mtb->dev = p;
2209a747e4fSDavid du Colombier 				mtb->bar = -1;
2219a747e4fSDavid du Colombier 				mtb->siz = p->mema.size;
2229a747e4fSDavid du Colombier 				mtb++;
2239a747e4fSDavid du Colombier 			}
2249a747e4fSDavid du Colombier 			if((pcicfgr8(p, PciHDT)&0x7f) != 0)
2259a747e4fSDavid du Colombier 				continue;
2269a747e4fSDavid du Colombier 		}
2279a747e4fSDavid du Colombier 
2289a747e4fSDavid du Colombier 		for(i = 0; i <= 5; i++) {
2299a747e4fSDavid du Colombier 			rno = PciBAR0 + i*4;
2309a747e4fSDavid du Colombier 			v = pcicfgrw32(p->tbdf, rno, 0, 1);
2319a747e4fSDavid du Colombier 			size = pcibarsize(p, rno);
2329a747e4fSDavid du Colombier 			if(size == 0)
2339a747e4fSDavid du Colombier 				continue;
2349a747e4fSDavid du Colombier 
2359a747e4fSDavid du Colombier 			if(v & 1) {
2369a747e4fSDavid du Colombier 				itb->dev = p;
2379a747e4fSDavid du Colombier 				itb->bar = i;
2389a747e4fSDavid du Colombier 				itb->siz = size;
2399a747e4fSDavid du Colombier 				itb++;
2409a747e4fSDavid du Colombier 			}
2419a747e4fSDavid du Colombier 			else {
2429a747e4fSDavid du Colombier 				mtb->dev = p;
2439a747e4fSDavid du Colombier 				mtb->bar = i;
2449a747e4fSDavid du Colombier 				mtb->siz = size;
2459a747e4fSDavid du Colombier 				mtb++;
2469a747e4fSDavid du Colombier 			}
2479a747e4fSDavid du Colombier 
2489a747e4fSDavid du Colombier 			p->mem[i].size = size;
2499a747e4fSDavid du Colombier 		}
2509a747e4fSDavid du Colombier 	}
2519a747e4fSDavid du Colombier 
2529a747e4fSDavid du Colombier 	/*
2539a747e4fSDavid du Colombier 	 * Sort both tables IO smallest first, Memory largest
2549a747e4fSDavid du Colombier 	 */
2559a747e4fSDavid du Colombier 	qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
2569a747e4fSDavid du Colombier 	tptr = table+ntb;
2579a747e4fSDavid du Colombier 	qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
2589a747e4fSDavid du Colombier 
2599a747e4fSDavid du Colombier 	/*
2609a747e4fSDavid du Colombier 	 * Allocate IO address space on this bus
2619a747e4fSDavid du Colombier 	 */
2629a747e4fSDavid du Colombier 	for(tptr = table; tptr < itb; tptr++) {
2639a747e4fSDavid du Colombier 		hole = tptr->siz;
2649a747e4fSDavid du Colombier 		if(tptr->bar == -1)
2659a747e4fSDavid du Colombier 			hole = 1<<12;
2669a747e4fSDavid du Colombier 		ioa = (ioa+hole-1) & ~(hole-1);
2679a747e4fSDavid du Colombier 
2689a747e4fSDavid du Colombier 		p = tptr->dev;
2699a747e4fSDavid du Colombier 		if(tptr->bar == -1)
2709a747e4fSDavid du Colombier 			p->ioa.bar = ioa;
2719a747e4fSDavid du Colombier 		else {
2729a747e4fSDavid du Colombier 			p->pcr |= IOen;
2739a747e4fSDavid du Colombier 			p->mem[tptr->bar].bar = ioa|1;
2749a747e4fSDavid du Colombier 			if(wrreg)
2759a747e4fSDavid du Colombier 				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
2769a747e4fSDavid du Colombier 		}
2779a747e4fSDavid du Colombier 
2789a747e4fSDavid du Colombier 		ioa += tptr->siz;
2799a747e4fSDavid du Colombier 	}
2809a747e4fSDavid du Colombier 
2819a747e4fSDavid du Colombier 	/*
2829a747e4fSDavid du Colombier 	 * Allocate Memory address space on this bus
2839a747e4fSDavid du Colombier 	 */
2849a747e4fSDavid du Colombier 	for(tptr = table+ntb; tptr < mtb; tptr++) {
2859a747e4fSDavid du Colombier 		hole = tptr->siz;
2869a747e4fSDavid du Colombier 		if(tptr->bar == -1)
2879a747e4fSDavid du Colombier 			hole = 1<<20;
2889a747e4fSDavid du Colombier 		mema = (mema+hole-1) & ~(hole-1);
2899a747e4fSDavid du Colombier 
2909a747e4fSDavid du Colombier 		p = tptr->dev;
2919a747e4fSDavid du Colombier 		if(tptr->bar == -1)
2929a747e4fSDavid du Colombier 			p->mema.bar = mema;
2939a747e4fSDavid du Colombier 		else {
2949a747e4fSDavid du Colombier 			p->pcr |= MEMen;
2959a747e4fSDavid du Colombier 			p->mem[tptr->bar].bar = mema;
2969a747e4fSDavid du Colombier 			if(wrreg)
2979a747e4fSDavid du Colombier 				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
2989a747e4fSDavid du Colombier 		}
2999a747e4fSDavid du Colombier 		mema += tptr->siz;
3009a747e4fSDavid du Colombier 	}
3019a747e4fSDavid du Colombier 
3029a747e4fSDavid du Colombier 	*pmema = mema;
3039a747e4fSDavid du Colombier 	*pioa = ioa;
3049a747e4fSDavid du Colombier 	free(table);
3059a747e4fSDavid du Colombier 
3069a747e4fSDavid du Colombier 	if(wrreg == 0)
3079a747e4fSDavid du Colombier 		return;
3089a747e4fSDavid du Colombier 
3099a747e4fSDavid du Colombier 	/*
3109a747e4fSDavid du Colombier 	 * Finally set all the bridge addresses & registers
3119a747e4fSDavid du Colombier 	 */
3129a747e4fSDavid du Colombier 	for(p = root; p != nil; p = p->link) {
3139a747e4fSDavid du Colombier 		if(p->bridge == nil) {
3149a747e4fSDavid du Colombier 			pcicfgrw8(p->tbdf, PciLTR, 64, 0);
3159a747e4fSDavid du Colombier 
3169a747e4fSDavid du Colombier 			p->pcr |= MASen;
3179a747e4fSDavid du Colombier 			pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0);
3189a747e4fSDavid du Colombier 			continue;
3199a747e4fSDavid du Colombier 		}
3209a747e4fSDavid du Colombier 
3219a747e4fSDavid du Colombier 		base = p->ioa.bar;
3229a747e4fSDavid du Colombier 		limit = base+p->ioa.size-1;
323*d0f3faacSDavid du Colombier 		v = pcicfgrw32(p->tbdf, PciIBR, 0, 1);
3249a747e4fSDavid du Colombier 		v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
325*d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciIBR, v, 0);
3269a747e4fSDavid du Colombier 		v = (limit & 0xFFFF0000)|(base>>16);
327*d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciIUBR, v, 0);
3289a747e4fSDavid du Colombier 
3299a747e4fSDavid du Colombier 		base = p->mema.bar;
3309a747e4fSDavid du Colombier 		limit = base+p->mema.size-1;
3319a747e4fSDavid du Colombier 		v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
332*d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciMBR, v, 0);
3339a747e4fSDavid du Colombier 
3349a747e4fSDavid du Colombier 		/*
3359a747e4fSDavid du Colombier 		 * Disable memory prefetch
3369a747e4fSDavid du Colombier 		 */
337*d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0);
3389a747e4fSDavid du Colombier 		pcicfgrw8(p->tbdf, PciLTR, 64, 0);
3399a747e4fSDavid du Colombier 
3409a747e4fSDavid du Colombier 		/*
3419a747e4fSDavid du Colombier 		 * Enable the bridge
3429a747e4fSDavid du Colombier 		 */
3439a747e4fSDavid du Colombier 		v = 0xFFFF0000 | IOen | MEMen | MASen;
3449a747e4fSDavid du Colombier 		pcicfgrw32(p->tbdf, PciPCR, v, 0);
3459a747e4fSDavid du Colombier 
3469a747e4fSDavid du Colombier 		sioa = p->ioa.bar;
3479a747e4fSDavid du Colombier 		smema = p->mema.bar;
3489a747e4fSDavid du Colombier 		pcibusmap(p->bridge, &smema, &sioa, 1);
3499a747e4fSDavid du Colombier 	}
3509a747e4fSDavid du Colombier }
3519a747e4fSDavid du Colombier 
3529a747e4fSDavid du Colombier static int
pcilscan(int bno,Pcidev ** list)3539a747e4fSDavid du Colombier pcilscan(int bno, Pcidev** list)
3549a747e4fSDavid du Colombier {
3559a747e4fSDavid du Colombier 	Pcidev *p, *head, *tail;
3569a747e4fSDavid du Colombier 	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
3579a747e4fSDavid du Colombier 
3589a747e4fSDavid du Colombier 	maxubn = bno;
3599a747e4fSDavid du Colombier 	head = nil;
3609a747e4fSDavid du Colombier 	tail = nil;
3619a747e4fSDavid du Colombier 	for(dno = 0; dno <= pcimaxdno; dno++){
3629a747e4fSDavid du Colombier 		maxfno = 0;
3639a747e4fSDavid du Colombier 		for(fno = 0; fno <= maxfno; fno++){
3649a747e4fSDavid du Colombier 			/*
3659a747e4fSDavid du Colombier 			 * For this possible device, form the
3669a747e4fSDavid du Colombier 			 * bus+device+function triplet needed to address it
3679a747e4fSDavid du Colombier 			 * and try to read the vendor and device ID.
3689a747e4fSDavid du Colombier 			 * If successful, allocate a device struct and
3699a747e4fSDavid du Colombier 			 * start to fill it in with some useful information
3709a747e4fSDavid du Colombier 			 * from the device's configuration space.
3719a747e4fSDavid du Colombier 			 */
3729a747e4fSDavid du Colombier 			tbdf = MKBUS(BusPCI, bno, dno, fno);
3739a747e4fSDavid du Colombier 			l = pcicfgrw32(tbdf, PciVID, 0, 1);
3749a747e4fSDavid du Colombier 			if(l == 0xFFFFFFFF || l == 0)
3759a747e4fSDavid du Colombier 				continue;
3769a747e4fSDavid du Colombier 			p = malloc(sizeof(*p));
3779a747e4fSDavid du Colombier 			p->tbdf = tbdf;
3789a747e4fSDavid du Colombier 			p->vid = l;
3799a747e4fSDavid du Colombier 			p->did = l>>16;
3809a747e4fSDavid du Colombier 
3819a747e4fSDavid du Colombier 			if(pcilist != nil)
3829a747e4fSDavid du Colombier 				pcitail->list = p;
3839a747e4fSDavid du Colombier 			else
3849a747e4fSDavid du Colombier 				pcilist = p;
3859a747e4fSDavid du Colombier 			pcitail = p;
3869a747e4fSDavid du Colombier 
3879a747e4fSDavid du Colombier 			p->rid = pcicfgr8(p, PciRID);
3889a747e4fSDavid du Colombier 			p->ccrp = pcicfgr8(p, PciCCRp);
3899a747e4fSDavid du Colombier 			p->ccru = pcicfgr8(p, PciCCRu);
3909a747e4fSDavid du Colombier 			p->ccrb = pcicfgr8(p, PciCCRb);
3919a747e4fSDavid du Colombier 			p->pcr = pcicfgr32(p, PciPCR);
3929a747e4fSDavid du Colombier 
3939a747e4fSDavid du Colombier 			p->intl = pcicfgr8(p, PciINTL);
3949a747e4fSDavid du Colombier 
3959a747e4fSDavid du Colombier 			/*
3969a747e4fSDavid du Colombier 			 * If the device is a multi-function device adjust the
3979a747e4fSDavid du Colombier 			 * loop count so all possible functions are checked.
3989a747e4fSDavid du Colombier 			 */
3999a747e4fSDavid du Colombier 			hdt = pcicfgr8(p, PciHDT);
4009a747e4fSDavid du Colombier 			if(hdt & 0x80)
4019a747e4fSDavid du Colombier 				maxfno = MaxFNO;
4029a747e4fSDavid du Colombier 
4039a747e4fSDavid du Colombier 			/*
4049a747e4fSDavid du Colombier 			 * If appropriate, read the base address registers
4059a747e4fSDavid du Colombier 			 * and work out the sizes.
4069a747e4fSDavid du Colombier 			 */
4079a747e4fSDavid du Colombier 			switch(p->ccrb) {
4089a747e4fSDavid du Colombier 			case 0x01:		/* mass storage controller */
4099a747e4fSDavid du Colombier 			case 0x02:		/* network controller */
4109a747e4fSDavid du Colombier 			case 0x03:		/* display controller */
4119a747e4fSDavid du Colombier 			case 0x04:		/* multimedia device */
4129a747e4fSDavid du Colombier 			case 0x06:		/* bridge device */
4139a747e4fSDavid du Colombier 			case 0x07:		/* simple comm. controllers */
4149a747e4fSDavid du Colombier 			case 0x08:		/* base system peripherals */
4159a747e4fSDavid du Colombier 			case 0x09:		/* input devices */
4169a747e4fSDavid du Colombier 			case 0x0A:		/* docking stations */
4179a747e4fSDavid du Colombier 			case 0x0B:		/* processors */
4189a747e4fSDavid du Colombier 			case 0x0C:		/* serial bus controllers */
4199a747e4fSDavid du Colombier 				if((hdt & 0x7F) != 0)
4209a747e4fSDavid du Colombier 					break;
4219a747e4fSDavid du Colombier 				rno = PciBAR0 - 4;
4229a747e4fSDavid du Colombier 				for(i = 0; i < nelem(p->mem); i++) {
4239a747e4fSDavid du Colombier 					rno += 4;
4249a747e4fSDavid du Colombier 					p->mem[i].bar = pcicfgr32(p, rno);
4259a747e4fSDavid du Colombier 					p->mem[i].size = pcibarsize(p, rno);
4269a747e4fSDavid du Colombier 				}
4279a747e4fSDavid du Colombier 				break;
4289a747e4fSDavid du Colombier 
4299a747e4fSDavid du Colombier 			case 0x00:
4309a747e4fSDavid du Colombier 			case 0x05:		/* memory controller */
4319a747e4fSDavid du Colombier 			default:
4329a747e4fSDavid du Colombier 				break;
4339a747e4fSDavid du Colombier 			}
4349a747e4fSDavid du Colombier 
4359a747e4fSDavid du Colombier 			if(head != nil)
4369a747e4fSDavid du Colombier 				tail->link = p;
4379a747e4fSDavid du Colombier 			else
4389a747e4fSDavid du Colombier 				head = p;
4399a747e4fSDavid du Colombier 			tail = p;
4409a747e4fSDavid du Colombier 		}
4419a747e4fSDavid du Colombier 	}
4429a747e4fSDavid du Colombier 
4439a747e4fSDavid du Colombier 	*list = head;
4449a747e4fSDavid du Colombier 	for(p = head; p != nil; p = p->link){
4459a747e4fSDavid du Colombier 		/*
4469a747e4fSDavid du Colombier 		 * Find PCI-PCI bridges and recursively descend the tree.
4479a747e4fSDavid du Colombier 		 */
4489a747e4fSDavid du Colombier 		if(p->ccrb != 0x06 || p->ccru != 0x04)
4499a747e4fSDavid du Colombier 			continue;
4509a747e4fSDavid du Colombier 
4519a747e4fSDavid du Colombier 		/*
4529a747e4fSDavid du Colombier 		 * If the secondary or subordinate bus number is not
4539a747e4fSDavid du Colombier 		 * initialised try to do what the PCI BIOS should have
4549a747e4fSDavid du Colombier 		 * done and fill in the numbers as the tree is descended.
4559a747e4fSDavid du Colombier 		 * On the way down the subordinate bus number is set to
4569a747e4fSDavid du Colombier 		 * the maximum as it's not known how many buses are behind
4579a747e4fSDavid du Colombier 		 * this one; the final value is set on the way back up.
4589a747e4fSDavid du Colombier 		 */
4599a747e4fSDavid du Colombier 		sbn = pcicfgr8(p, PciSBN);
4609a747e4fSDavid du Colombier 		ubn = pcicfgr8(p, PciUBN);
4619a747e4fSDavid du Colombier 
4629a747e4fSDavid du Colombier 		if(sbn == 0 || ubn == 0) {
4639a747e4fSDavid du Colombier 			sbn = maxubn+1;
4649a747e4fSDavid du Colombier 			/*
4659a747e4fSDavid du Colombier 			 * Make sure memory, I/O and master enables are
4669a747e4fSDavid du Colombier 			 * off, set the primary, secondary and subordinate
4679a747e4fSDavid du Colombier 			 * bus numbers and clear the secondary status before
4689a747e4fSDavid du Colombier 			 * attempting to scan the secondary bus.
4699a747e4fSDavid du Colombier 			 *
4709a747e4fSDavid du Colombier 			 * Initialisation of the bridge should be done here.
4719a747e4fSDavid du Colombier 			 */
4729a747e4fSDavid du Colombier 			pcicfgw32(p, PciPCR, 0xFFFF0000);
4739a747e4fSDavid du Colombier 			l = (MaxUBN<<16)|(sbn<<8)|bno;
4749a747e4fSDavid du Colombier 			pcicfgw32(p, PciPBN, l);
4759a747e4fSDavid du Colombier 			pcicfgw16(p, PciSPSR, 0xFFFF);
4769a747e4fSDavid du Colombier 			maxubn = pcilscan(sbn, &p->bridge);
4779a747e4fSDavid du Colombier 			l = (maxubn<<16)|(sbn<<8)|bno;
4789a747e4fSDavid du Colombier 
4799a747e4fSDavid du Colombier 			pcicfgw32(p, PciPBN, l);
4809a747e4fSDavid du Colombier 		}
4819a747e4fSDavid du Colombier 		else {
4829a747e4fSDavid du Colombier 			maxubn = ubn;
4839a747e4fSDavid du Colombier 			pcilscan(sbn, &p->bridge);
4849a747e4fSDavid du Colombier 		}
4859a747e4fSDavid du Colombier 	}
4869a747e4fSDavid du Colombier 
4879a747e4fSDavid du Colombier 	return maxubn;
4889a747e4fSDavid du Colombier }
4899a747e4fSDavid du Colombier 
4909a747e4fSDavid du Colombier int
pciscan(int bno,Pcidev ** list)4919a747e4fSDavid du Colombier pciscan(int bno, Pcidev **list)
4929a747e4fSDavid du Colombier {
4939a747e4fSDavid du Colombier 	int ubn;
4949a747e4fSDavid du Colombier 
4959a747e4fSDavid du Colombier 	qlock(&pcicfginitlock);
4969a747e4fSDavid du Colombier 	ubn = pcilscan(bno, list);
4979a747e4fSDavid du Colombier 	qunlock(&pcicfginitlock);
4989a747e4fSDavid du Colombier 	return ubn;
4999a747e4fSDavid du Colombier }
5009a747e4fSDavid du Colombier 
5019a747e4fSDavid du Colombier static void
pcicfginit(void)5029a747e4fSDavid du Colombier pcicfginit(void)
5039a747e4fSDavid du Colombier {
5049a747e4fSDavid du Colombier 	char *p;
5059a747e4fSDavid du Colombier 	int bno;
5069a747e4fSDavid du Colombier 	Pcidev **list;
5079a747e4fSDavid du Colombier 	ulong mema, ioa;
5089a747e4fSDavid du Colombier 
5099a747e4fSDavid du Colombier 	qlock(&pcicfginitlock);
5109a747e4fSDavid du Colombier 	if(pcicfgmode != -1)
5119a747e4fSDavid du Colombier 		goto out;
5129a747e4fSDavid du Colombier 
5139a747e4fSDavid du Colombier 	/*
5149a747e4fSDavid du Colombier 	 * Try to determine which PCI configuration mode is implemented.
5159a747e4fSDavid du Colombier 	 * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
5169a747e4fSDavid du Colombier 	 * a DWORD at 0xCF8 and another at 0xCFC and will pass through
5179a747e4fSDavid du Colombier 	 * any non-DWORD accesses as normal I/O cycles. There shouldn't be
5189a747e4fSDavid du Colombier 	 * a device behind these addresses so if Mode2 accesses fail try
5199a747e4fSDavid du Colombier 	 * for Mode1 (which is preferred, Mode2 is deprecated).
5209a747e4fSDavid du Colombier 	 */
5219a747e4fSDavid du Colombier 	outb(PciCSE, 0);
5229a747e4fSDavid du Colombier 	if(inb(PciCSE) == 0){
5239a747e4fSDavid du Colombier 		pcicfgmode = 2;
5249a747e4fSDavid du Colombier 		pcimaxdno = 15;
5259a747e4fSDavid du Colombier 	}
5269a747e4fSDavid du Colombier 	else {
5279a747e4fSDavid du Colombier 		outl(PciADDR, 0);
5289a747e4fSDavid du Colombier 		if(inl(PciADDR) == 0){
5299a747e4fSDavid du Colombier 			pcicfgmode = 1;
5309a747e4fSDavid du Colombier 			pcimaxdno = 31;
5319a747e4fSDavid du Colombier 		}
5329a747e4fSDavid du Colombier 	}
5339a747e4fSDavid du Colombier 
5349a747e4fSDavid du Colombier 	if(pcicfgmode < 0)
5359a747e4fSDavid du Colombier 		goto out;
5369a747e4fSDavid du Colombier 
5379a747e4fSDavid du Colombier 	fmtinstall('T', tbdffmt);
5389a747e4fSDavid du Colombier 
5399a747e4fSDavid du Colombier 	if(p = getconf("*pcimaxbno"))
5409a747e4fSDavid du Colombier 		pcimaxbno = strtoul(p, 0, 0);
5419a747e4fSDavid du Colombier 	if(p = getconf("*pcimaxdno"))
5429a747e4fSDavid du Colombier 		pcimaxdno = strtoul(p, 0, 0);
5439a747e4fSDavid du Colombier 
5449a747e4fSDavid du Colombier 	list = &pciroot;
5459a747e4fSDavid du Colombier 	for(bno = 0; bno <= pcimaxbno; bno++) {
5469a747e4fSDavid du Colombier 		int sbno = bno;
5479a747e4fSDavid du Colombier 		bno = pcilscan(bno, list);
5489a747e4fSDavid du Colombier 
5499a747e4fSDavid du Colombier 		while(*list)
5509a747e4fSDavid du Colombier 			list = &(*list)->link;
5519a747e4fSDavid du Colombier 
5529a747e4fSDavid du Colombier 		if (sbno == 0) {
5539a747e4fSDavid du Colombier 			Pcidev *pci;
5549a747e4fSDavid du Colombier 
5559a747e4fSDavid du Colombier 			/*
5569a747e4fSDavid du Colombier 			  * If we have found a PCI-to-Cardbus bridge, make sure
5579a747e4fSDavid du Colombier 			  * it has no valid mappings anymore.
5589a747e4fSDavid du Colombier 			  */
5599a747e4fSDavid du Colombier 			pci = pciroot;
5609a747e4fSDavid du Colombier 			while (pci) {
5619a747e4fSDavid du Colombier 				if (pci->ccrb == 6 && pci->ccru == 7) {
5629a747e4fSDavid du Colombier 					ushort bcr;
5639a747e4fSDavid du Colombier 
5649a747e4fSDavid du Colombier 					/* reset the cardbus */
5659a747e4fSDavid du Colombier 					bcr = pcicfgr16(pci, PciBCR);
5669a747e4fSDavid du Colombier 					pcicfgw16(pci, PciBCR, 0x40 | bcr);
5679a747e4fSDavid du Colombier 					delay(50);
5689a747e4fSDavid du Colombier 				}
5699a747e4fSDavid du Colombier 				pci = pci->link;
5709a747e4fSDavid du Colombier 			}
5719a747e4fSDavid du Colombier 		}
5729a747e4fSDavid du Colombier 	}
5739a747e4fSDavid du Colombier 
5749a747e4fSDavid du Colombier 	if(pciroot == nil)
5759a747e4fSDavid du Colombier 		goto out;
5769a747e4fSDavid du Colombier 
5779a747e4fSDavid du Colombier 	/*
5789a747e4fSDavid du Colombier 	 * Work out how big the top bus is
5799a747e4fSDavid du Colombier 	 */
5809a747e4fSDavid du Colombier 	mema = 0;
5819a747e4fSDavid du Colombier 	ioa = 0;
5829a747e4fSDavid du Colombier 	pcibusmap(pciroot, &mema, &ioa, 0);
5839a747e4fSDavid du Colombier 
5849a747e4fSDavid du Colombier 	DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
5859a747e4fSDavid du Colombier 		mema, pcimask(mema), ioa);
5869a747e4fSDavid du Colombier 
5879a747e4fSDavid du Colombier 	/*
5889a747e4fSDavid du Colombier 	 * Align the windows and map it
5899a747e4fSDavid du Colombier 	 */
5909a747e4fSDavid du Colombier 	ioa = 0x1000;
5919a747e4fSDavid du Colombier 	mema = 0;
5929a747e4fSDavid du Colombier 
5939a747e4fSDavid du Colombier 	pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
5949a747e4fSDavid du Colombier 
5959a747e4fSDavid du Colombier 	pcibusmap(pciroot, &mema, &ioa, 1);
5969a747e4fSDavid du Colombier 	DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
5979a747e4fSDavid du Colombier 
5989a747e4fSDavid du Colombier out:
5999a747e4fSDavid du Colombier 	qunlock(&pcicfginitlock);
6009a747e4fSDavid du Colombier }
6019a747e4fSDavid du Colombier 
6029a747e4fSDavid du Colombier static int
pcicfgrw8(int tbdf,int rno,int data,int read)6039a747e4fSDavid du Colombier pcicfgrw8(int tbdf, int rno, int data, int read)
6049a747e4fSDavid du Colombier {
6059a747e4fSDavid du Colombier 	int o, type, x;
6069a747e4fSDavid du Colombier 
6079a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
6089a747e4fSDavid du Colombier 		pcicfginit();
6099a747e4fSDavid du Colombier 
6109a747e4fSDavid du Colombier 	if(BUSBNO(tbdf))
6119a747e4fSDavid du Colombier 		type = 0x01;
6129a747e4fSDavid du Colombier 	else
6139a747e4fSDavid du Colombier 		type = 0x00;
6149a747e4fSDavid du Colombier 	x = -1;
6159a747e4fSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
6169a747e4fSDavid du Colombier 		return x;
6179a747e4fSDavid du Colombier 
6189a747e4fSDavid du Colombier 	lock(&pcicfglock);
6199a747e4fSDavid du Colombier 	switch(pcicfgmode){
6209a747e4fSDavid du Colombier 
6219a747e4fSDavid du Colombier 	case 1:
6229a747e4fSDavid du Colombier 		o = rno & 0x03;
6239a747e4fSDavid du Colombier 		rno &= ~0x03;
6249a747e4fSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
6259a747e4fSDavid du Colombier 		if(read)
6269a747e4fSDavid du Colombier 			x = inb(PciDATA+o);
6279a747e4fSDavid du Colombier 		else
6289a747e4fSDavid du Colombier 			outb(PciDATA+o, data);
6299a747e4fSDavid du Colombier 		outl(PciADDR, 0);
6309a747e4fSDavid du Colombier 		break;
6319a747e4fSDavid du Colombier 
6329a747e4fSDavid du Colombier 	case 2:
6339a747e4fSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
6349a747e4fSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
6359a747e4fSDavid du Colombier 		if(read)
6369a747e4fSDavid du Colombier 			x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
6379a747e4fSDavid du Colombier 		else
6389a747e4fSDavid du Colombier 			outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
6399a747e4fSDavid du Colombier 		outb(PciCSE, 0);
6409a747e4fSDavid du Colombier 		break;
6419a747e4fSDavid du Colombier 	}
6429a747e4fSDavid du Colombier 	unlock(&pcicfglock);
6439a747e4fSDavid du Colombier 
6449a747e4fSDavid du Colombier 	return x;
6459a747e4fSDavid du Colombier }
6469a747e4fSDavid du Colombier 
6479a747e4fSDavid du Colombier int
pcicfgr8(Pcidev * pcidev,int rno)6489a747e4fSDavid du Colombier pcicfgr8(Pcidev* pcidev, int rno)
6499a747e4fSDavid du Colombier {
6509a747e4fSDavid du Colombier 	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
6519a747e4fSDavid du Colombier }
6529a747e4fSDavid du Colombier 
6539a747e4fSDavid du Colombier void
pcicfgw8(Pcidev * pcidev,int rno,int data)6549a747e4fSDavid du Colombier pcicfgw8(Pcidev* pcidev, int rno, int data)
6559a747e4fSDavid du Colombier {
6569a747e4fSDavid du Colombier 	pcicfgrw8(pcidev->tbdf, rno, data, 0);
6579a747e4fSDavid du Colombier }
6589a747e4fSDavid du Colombier 
6599a747e4fSDavid du Colombier static int
pcicfgrw16(int tbdf,int rno,int data,int read)6609a747e4fSDavid du Colombier pcicfgrw16(int tbdf, int rno, int data, int read)
6619a747e4fSDavid du Colombier {
6629a747e4fSDavid du Colombier 	int o, type, x;
6639a747e4fSDavid du Colombier 
6649a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
6659a747e4fSDavid du Colombier 		pcicfginit();
6669a747e4fSDavid du Colombier 
6679a747e4fSDavid du Colombier 	if(BUSBNO(tbdf))
6689a747e4fSDavid du Colombier 		type = 0x01;
6699a747e4fSDavid du Colombier 	else
6709a747e4fSDavid du Colombier 		type = 0x00;
6719a747e4fSDavid du Colombier 	x = -1;
6729a747e4fSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
6739a747e4fSDavid du Colombier 		return x;
6749a747e4fSDavid du Colombier 
6759a747e4fSDavid du Colombier 	lock(&pcicfglock);
6769a747e4fSDavid du Colombier 	switch(pcicfgmode){
6779a747e4fSDavid du Colombier 
6789a747e4fSDavid du Colombier 	case 1:
6799a747e4fSDavid du Colombier 		o = rno & 0x02;
6809a747e4fSDavid du Colombier 		rno &= ~0x03;
6819a747e4fSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
6829a747e4fSDavid du Colombier 		if(read)
6839a747e4fSDavid du Colombier 			x = ins(PciDATA+o);
6849a747e4fSDavid du Colombier 		else
6859a747e4fSDavid du Colombier 			outs(PciDATA+o, data);
6869a747e4fSDavid du Colombier 		outl(PciADDR, 0);
6879a747e4fSDavid du Colombier 		break;
6889a747e4fSDavid du Colombier 
6899a747e4fSDavid du Colombier 	case 2:
6909a747e4fSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
6919a747e4fSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
6929a747e4fSDavid du Colombier 		if(read)
6939a747e4fSDavid du Colombier 			x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
6949a747e4fSDavid du Colombier 		else
6959a747e4fSDavid du Colombier 			outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
6969a747e4fSDavid du Colombier 		outb(PciCSE, 0);
6979a747e4fSDavid du Colombier 		break;
6989a747e4fSDavid du Colombier 	}
6999a747e4fSDavid du Colombier 	unlock(&pcicfglock);
7009a747e4fSDavid du Colombier 
7019a747e4fSDavid du Colombier 	return x;
7029a747e4fSDavid du Colombier }
7039a747e4fSDavid du Colombier 
7049a747e4fSDavid du Colombier int
pcicfgr16(Pcidev * pcidev,int rno)7059a747e4fSDavid du Colombier pcicfgr16(Pcidev* pcidev, int rno)
7069a747e4fSDavid du Colombier {
7079a747e4fSDavid du Colombier 	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
7089a747e4fSDavid du Colombier }
7099a747e4fSDavid du Colombier 
7109a747e4fSDavid du Colombier void
pcicfgw16(Pcidev * pcidev,int rno,int data)7119a747e4fSDavid du Colombier pcicfgw16(Pcidev* pcidev, int rno, int data)
7129a747e4fSDavid du Colombier {
7139a747e4fSDavid du Colombier 	pcicfgrw16(pcidev->tbdf, rno, data, 0);
7149a747e4fSDavid du Colombier }
7159a747e4fSDavid du Colombier 
7169a747e4fSDavid du Colombier static int
pcicfgrw32(int tbdf,int rno,int data,int read)7179a747e4fSDavid du Colombier pcicfgrw32(int tbdf, int rno, int data, int read)
7189a747e4fSDavid du Colombier {
7199a747e4fSDavid du Colombier 	int type, x;
7209a747e4fSDavid du Colombier 
7219a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
7229a747e4fSDavid du Colombier 		pcicfginit();
7239a747e4fSDavid du Colombier 
7249a747e4fSDavid du Colombier 	if(BUSBNO(tbdf))
7259a747e4fSDavid du Colombier 		type = 0x01;
7269a747e4fSDavid du Colombier 	else
7279a747e4fSDavid du Colombier 		type = 0x00;
7289a747e4fSDavid du Colombier 	x = -1;
7299a747e4fSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
7309a747e4fSDavid du Colombier 		return x;
7319a747e4fSDavid du Colombier 
7329a747e4fSDavid du Colombier 	lock(&pcicfglock);
7339a747e4fSDavid du Colombier 	switch(pcicfgmode){
7349a747e4fSDavid du Colombier 
7359a747e4fSDavid du Colombier 	case 1:
7369a747e4fSDavid du Colombier 		rno &= ~0x03;
7379a747e4fSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
7389a747e4fSDavid du Colombier 		if(read)
7399a747e4fSDavid du Colombier 			x = inl(PciDATA);
7409a747e4fSDavid du Colombier 		else
7419a747e4fSDavid du Colombier 			outl(PciDATA, data);
7429a747e4fSDavid du Colombier 		outl(PciADDR, 0);
7439a747e4fSDavid du Colombier 		break;
7449a747e4fSDavid du Colombier 
7459a747e4fSDavid du Colombier 	case 2:
7469a747e4fSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
7479a747e4fSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
7489a747e4fSDavid du Colombier 		if(read)
7499a747e4fSDavid du Colombier 			x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
7509a747e4fSDavid du Colombier 		else
7519a747e4fSDavid du Colombier 			outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
7529a747e4fSDavid du Colombier 		outb(PciCSE, 0);
7539a747e4fSDavid du Colombier 		break;
7549a747e4fSDavid du Colombier 	}
7559a747e4fSDavid du Colombier 	unlock(&pcicfglock);
7569a747e4fSDavid du Colombier 
7579a747e4fSDavid du Colombier 	return x;
7589a747e4fSDavid du Colombier }
7599a747e4fSDavid du Colombier 
7609a747e4fSDavid du Colombier int
pcicfgr32(Pcidev * pcidev,int rno)7619a747e4fSDavid du Colombier pcicfgr32(Pcidev* pcidev, int rno)
7629a747e4fSDavid du Colombier {
7639a747e4fSDavid du Colombier 	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
7649a747e4fSDavid du Colombier }
7659a747e4fSDavid du Colombier 
7669a747e4fSDavid du Colombier void
pcicfgw32(Pcidev * pcidev,int rno,int data)7679a747e4fSDavid du Colombier pcicfgw32(Pcidev* pcidev, int rno, int data)
7689a747e4fSDavid du Colombier {
7699a747e4fSDavid du Colombier 	pcicfgrw32(pcidev->tbdf, rno, data, 0);
7709a747e4fSDavid du Colombier }
7719a747e4fSDavid du Colombier 
7729a747e4fSDavid du Colombier Pcidev*
pcimatch(Pcidev * prev,int vid,int did)7739a747e4fSDavid du Colombier pcimatch(Pcidev* prev, int vid, int did)
7749a747e4fSDavid du Colombier {
7759a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
7769a747e4fSDavid du Colombier 		pcicfginit();
7779a747e4fSDavid du Colombier 
7789a747e4fSDavid du Colombier 	if(prev == nil)
7799a747e4fSDavid du Colombier 		prev = pcilist;
7809a747e4fSDavid du Colombier 	else
7819a747e4fSDavid du Colombier 		prev = prev->list;
7829a747e4fSDavid du Colombier 
7839a747e4fSDavid du Colombier 	while(prev != nil){
7849a747e4fSDavid du Colombier 		if((vid == 0 || prev->vid == vid)
7859a747e4fSDavid du Colombier 		&& (did == 0 || prev->did == did))
7869a747e4fSDavid du Colombier 			break;
7879a747e4fSDavid du Colombier 		prev = prev->list;
7889a747e4fSDavid du Colombier 	}
7899a747e4fSDavid du Colombier 	return prev;
7909a747e4fSDavid du Colombier }
7919a747e4fSDavid du Colombier 
7929a747e4fSDavid du Colombier Pcidev*
pcimatchtbdf(int tbdf)7939a747e4fSDavid du Colombier pcimatchtbdf(int tbdf)
7949a747e4fSDavid du Colombier {
7959a747e4fSDavid du Colombier 	Pcidev *pcidev;
7969a747e4fSDavid du Colombier 
7979a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
7989a747e4fSDavid du Colombier 		pcicfginit();
7999a747e4fSDavid du Colombier 
8009a747e4fSDavid du Colombier 	for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
8019a747e4fSDavid du Colombier 		if(pcidev->tbdf == tbdf)
8029a747e4fSDavid du Colombier 			break;
8039a747e4fSDavid du Colombier 	}
8049a747e4fSDavid du Colombier 	return pcidev;
8059a747e4fSDavid du Colombier }
8069a747e4fSDavid du Colombier 
8079a747e4fSDavid du Colombier uchar
pciipin(Pcidev * pci,uchar pin)8089a747e4fSDavid du Colombier pciipin(Pcidev *pci, uchar pin)
8099a747e4fSDavid du Colombier {
8109a747e4fSDavid du Colombier 	if (pci == nil)
8119a747e4fSDavid du Colombier 		pci = pcilist;
8129a747e4fSDavid du Colombier 
8139a747e4fSDavid du Colombier 	while (pci) {
8149a747e4fSDavid du Colombier 		uchar intl;
8159a747e4fSDavid du Colombier 
8169a747e4fSDavid du Colombier 		if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
8179a747e4fSDavid du Colombier 			return pci->intl;
8189a747e4fSDavid du Colombier 
8199a747e4fSDavid du Colombier 		if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
8209a747e4fSDavid du Colombier 			return intl;
8219a747e4fSDavid du Colombier 
8229a747e4fSDavid du Colombier 		pci = pci->list;
8239a747e4fSDavid du Colombier 	}
8249a747e4fSDavid du Colombier 	return 0;
8259a747e4fSDavid du Colombier }
8269a747e4fSDavid du Colombier 
8279a747e4fSDavid du Colombier static void
pcilhinv(Pcidev * p)8289a747e4fSDavid du Colombier pcilhinv(Pcidev* p)
8299a747e4fSDavid du Colombier {
8309a747e4fSDavid du Colombier 	int i;
8319a747e4fSDavid du Colombier 	Pcidev *t;
8329a747e4fSDavid du Colombier 
8339a747e4fSDavid du Colombier 	if(p == nil) {
8349a747e4fSDavid du Colombier 		putstrn(PCICONS.output, PCICONS.ptr);
8359a747e4fSDavid du Colombier 		p = pciroot;
8369a747e4fSDavid du Colombier 		print("bus dev type vid  did intl memory\n");
8379a747e4fSDavid du Colombier 	}
8389a747e4fSDavid du Colombier 	for(t = p; t != nil; t = t->link) {
8399a747e4fSDavid du Colombier 		print("%d  %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d  ",
8409a747e4fSDavid du Colombier 			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
8419a747e4fSDavid du Colombier 			t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
8429a747e4fSDavid du Colombier 
8439a747e4fSDavid du Colombier 		for(i = 0; i < nelem(p->mem); i++) {
8449a747e4fSDavid du Colombier 			if(t->mem[i].size == 0)
8459a747e4fSDavid du Colombier 				continue;
8469a747e4fSDavid du Colombier 			print("%d:%.8lux %d ", i,
8479a747e4fSDavid du Colombier 				t->mem[i].bar, t->mem[i].size);
8489a747e4fSDavid du Colombier 		}
8499a747e4fSDavid du Colombier 		if(t->ioa.bar || t->ioa.size)
8509a747e4fSDavid du Colombier 			print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
8519a747e4fSDavid du Colombier 		if(t->mema.bar || t->mema.size)
8529a747e4fSDavid du Colombier 			print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
8539a747e4fSDavid du Colombier 		if(t->bridge)
8549a747e4fSDavid du Colombier 			print("->%d", BUSBNO(t->bridge->tbdf));
8559a747e4fSDavid du Colombier 		print("\n");
8569a747e4fSDavid du Colombier 	}
8579a747e4fSDavid du Colombier 	while(p != nil) {
8589a747e4fSDavid du Colombier 		if(p->bridge != nil)
8599a747e4fSDavid du Colombier 			pcilhinv(p->bridge);
8609a747e4fSDavid du Colombier 		p = p->link;
8619a747e4fSDavid du Colombier 	}
8629a747e4fSDavid du Colombier }
8639a747e4fSDavid du Colombier 
8649a747e4fSDavid du Colombier void
pcihinv(Pcidev * p)8659a747e4fSDavid du Colombier pcihinv(Pcidev* p)
8669a747e4fSDavid du Colombier {
8679a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
8689a747e4fSDavid du Colombier 		pcicfginit();
8699a747e4fSDavid du Colombier 	qlock(&pcicfginitlock);
8709a747e4fSDavid du Colombier 	pcilhinv(p);
8719a747e4fSDavid du Colombier 	qunlock(&pcicfginitlock);
8729a747e4fSDavid du Colombier }
8739a747e4fSDavid du Colombier 
8749a747e4fSDavid du Colombier void
pcireset(void)8759a747e4fSDavid du Colombier pcireset(void)
8769a747e4fSDavid du Colombier {
8779a747e4fSDavid du Colombier 	Pcidev *p;
8789a747e4fSDavid du Colombier 	int pcr;
8799a747e4fSDavid du Colombier 
8809a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
8819a747e4fSDavid du Colombier 		pcicfginit();
8829a747e4fSDavid du Colombier 
8839a747e4fSDavid du Colombier 	for(p = pcilist; p != nil; p = p->list){
8849a747e4fSDavid du Colombier 		pcr = pcicfgr16(p, PciPCR);
8859a747e4fSDavid du Colombier 		pcr &= ~0x0004;
8869a747e4fSDavid du Colombier 		pcicfgw16(p, PciPCR, pcr);
8879a747e4fSDavid du Colombier 	}
8889a747e4fSDavid du Colombier }
8899a747e4fSDavid du Colombier 
8909a747e4fSDavid du Colombier void
pcisetbme(Pcidev * p)8919a747e4fSDavid du Colombier pcisetbme(Pcidev* p)
8929a747e4fSDavid du Colombier {
8939a747e4fSDavid du Colombier 	int pcr;
8949a747e4fSDavid du Colombier 
8959a747e4fSDavid du Colombier 	pcr = pcicfgr16(p, PciPCR);
8969a747e4fSDavid du Colombier 	pcr |= MASen;
8979a747e4fSDavid du Colombier 	pcicfgw16(p, PciPCR, pcr);
8989a747e4fSDavid du Colombier }
8999a747e4fSDavid du Colombier 
9009a747e4fSDavid du Colombier void
pciclrbme(Pcidev * p)9019a747e4fSDavid du Colombier pciclrbme(Pcidev* p)
9029a747e4fSDavid du Colombier {
9039a747e4fSDavid du Colombier 	int pcr;
9049a747e4fSDavid du Colombier 
9059a747e4fSDavid du Colombier 	pcr = pcicfgr16(p, PciPCR);
9069a747e4fSDavid du Colombier 	pcr &= ~MASen;
9079a747e4fSDavid du Colombier 	pcicfgw16(p, PciPCR, pcr);
9089a747e4fSDavid du Colombier }
909