xref: /plan9/sys/src/9/pc/pci.c (revision 4de34a7edde43207e841ec91ecd12e6cf5f5ebe7)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * PCI support code.
3e569ccb5SDavid du Colombier  * Needs a massive rewrite.
47dd7cddfSDavid du Colombier  */
57dd7cddfSDavid du Colombier #include "u.h"
67dd7cddfSDavid du Colombier #include "../port/lib.h"
77dd7cddfSDavid du Colombier #include "mem.h"
87dd7cddfSDavid du Colombier #include "dat.h"
97dd7cddfSDavid du Colombier #include "fns.h"
107dd7cddfSDavid du Colombier #include "io.h"
117dd7cddfSDavid du Colombier #include "../port/error.h"
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier #define DBG	if(0) pcilog
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier struct
167dd7cddfSDavid du Colombier {
177dd7cddfSDavid du Colombier 	char	output[16384];
187dd7cddfSDavid du Colombier 	int	ptr;
197dd7cddfSDavid du Colombier }PCICONS;
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier int
227dd7cddfSDavid du Colombier pcilog(char *fmt, ...)
237dd7cddfSDavid du Colombier {
247dd7cddfSDavid du Colombier 	int n;
257dd7cddfSDavid du Colombier 	va_list arg;
267dd7cddfSDavid du Colombier 	char buf[PRINTSIZE];
277dd7cddfSDavid du Colombier 
287dd7cddfSDavid du Colombier 	va_start(arg, fmt);
299a747e4fSDavid du Colombier 	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
307dd7cddfSDavid du Colombier 	va_end(arg);
317dd7cddfSDavid du Colombier 
327dd7cddfSDavid du Colombier 	memmove(PCICONS.output+PCICONS.ptr, buf, n);
337dd7cddfSDavid du Colombier 	PCICONS.ptr += n;
347dd7cddfSDavid du Colombier 	return n;
357dd7cddfSDavid du Colombier }
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier enum
387dd7cddfSDavid du Colombier {					/* configuration mechanism #1 */
397dd7cddfSDavid du Colombier 	PciADDR		= 0xCF8,	/* CONFIG_ADDRESS */
407dd7cddfSDavid du Colombier 	PciDATA		= 0xCFC,	/* CONFIG_DATA */
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier 					/* configuration mechanism #2 */
437dd7cddfSDavid du Colombier 	PciCSE		= 0xCF8,	/* configuration space enable */
447dd7cddfSDavid du Colombier 	PciFORWARD	= 0xCFA,	/* which bus */
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier 	MaxFNO		= 7,
477dd7cddfSDavid du Colombier 	MaxUBN		= 255,
487dd7cddfSDavid du Colombier };
497dd7cddfSDavid du Colombier 
507dd7cddfSDavid du Colombier enum
517dd7cddfSDavid du Colombier {					/* command register */
527dd7cddfSDavid du Colombier 	IOen		= (1<<0),
537dd7cddfSDavid du Colombier 	MEMen		= (1<<1),
547dd7cddfSDavid du Colombier 	MASen		= (1<<2),
557dd7cddfSDavid du Colombier 	MemWrInv	= (1<<4),
567dd7cddfSDavid du Colombier 	PErrEn		= (1<<6),
577dd7cddfSDavid du Colombier 	SErrEn		= (1<<8),
587dd7cddfSDavid du Colombier };
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier static Lock pcicfglock;
617dd7cddfSDavid du Colombier static Lock pcicfginitlock;
627dd7cddfSDavid du Colombier static int pcicfgmode = -1;
6359cc4ca5SDavid du Colombier static int pcimaxbno = 7;
647dd7cddfSDavid du Colombier static int pcimaxdno;
657dd7cddfSDavid du Colombier static Pcidev* pciroot;
667dd7cddfSDavid du Colombier static Pcidev* pcilist;
677dd7cddfSDavid du Colombier static Pcidev* pcitail;
683ff48bf5SDavid du Colombier static int nobios, nopcirouting;
697dd7cddfSDavid du Colombier 
707dd7cddfSDavid du Colombier static int pcicfgrw32(int, int, int, int);
71e569ccb5SDavid du Colombier static int pcicfgrw16(int, int, int, int);
727dd7cddfSDavid du Colombier static int pcicfgrw8(int, int, int, int);
737dd7cddfSDavid du Colombier 
747dd7cddfSDavid du Colombier static char* bustypes[] = {
757dd7cddfSDavid du Colombier 	"CBUSI",
767dd7cddfSDavid du Colombier 	"CBUSII",
777dd7cddfSDavid du Colombier 	"EISA",
787dd7cddfSDavid du Colombier 	"FUTURE",
797dd7cddfSDavid du Colombier 	"INTERN",
807dd7cddfSDavid du Colombier 	"ISA",
817dd7cddfSDavid du Colombier 	"MBI",
827dd7cddfSDavid du Colombier 	"MBII",
837dd7cddfSDavid du Colombier 	"MCA",
847dd7cddfSDavid du Colombier 	"MPI",
857dd7cddfSDavid du Colombier 	"MPSA",
867dd7cddfSDavid du Colombier 	"NUBUS",
877dd7cddfSDavid du Colombier 	"PCI",
887dd7cddfSDavid du Colombier 	"PCMCIA",
897dd7cddfSDavid du Colombier 	"TC",
907dd7cddfSDavid du Colombier 	"VL",
917dd7cddfSDavid du Colombier 	"VME",
927dd7cddfSDavid du Colombier 	"XPRESS",
937dd7cddfSDavid du Colombier };
947dd7cddfSDavid du Colombier 
957dd7cddfSDavid du Colombier #pragma	varargck	type	"T"	int
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier static int
989a747e4fSDavid du Colombier tbdffmt(Fmt* fmt)
997dd7cddfSDavid du Colombier {
1007dd7cddfSDavid du Colombier 	char *p;
1019a747e4fSDavid du Colombier 	int l, r, type, tbdf;
1027dd7cddfSDavid du Colombier 
1039a747e4fSDavid du Colombier 	if((p = malloc(READSTR)) == nil)
1049a747e4fSDavid du Colombier 		return fmtstrcpy(fmt, "(tbdfconv)");
1057dd7cddfSDavid du Colombier 
1069a747e4fSDavid du Colombier 	switch(fmt->r){
1077dd7cddfSDavid du Colombier 	case 'T':
1089a747e4fSDavid du Colombier 		tbdf = va_arg(fmt->args, int);
1097dd7cddfSDavid du Colombier 		type = BUSTYPE(tbdf);
1107dd7cddfSDavid du Colombier 		if(type < nelem(bustypes))
1117dd7cddfSDavid du Colombier 			l = snprint(p, READSTR, bustypes[type]);
1127dd7cddfSDavid du Colombier 		else
1137dd7cddfSDavid du Colombier 			l = snprint(p, READSTR, "%d", type);
1147dd7cddfSDavid du Colombier 		snprint(p+l, READSTR-l, ".%d.%d.%d",
1157dd7cddfSDavid du Colombier 			BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
1167dd7cddfSDavid du Colombier 		break;
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier 	default:
1197dd7cddfSDavid du Colombier 		snprint(p, READSTR, "(tbdfconv)");
1207dd7cddfSDavid du Colombier 		break;
1217dd7cddfSDavid du Colombier 	}
1229a747e4fSDavid du Colombier 	r = fmtstrcpy(fmt, p);
1237dd7cddfSDavid du Colombier 	free(p);
1247dd7cddfSDavid du Colombier 
1259a747e4fSDavid du Colombier 	return r;
1267dd7cddfSDavid du Colombier }
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier ulong
1297dd7cddfSDavid du Colombier pcibarsize(Pcidev *p, int rno)
1307dd7cddfSDavid du Colombier {
1317dd7cddfSDavid du Colombier 	ulong v, size;
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier 	v = pcicfgrw32(p->tbdf, rno, 0, 1);
1347dd7cddfSDavid du Colombier 	pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
1357dd7cddfSDavid du Colombier 	size = pcicfgrw32(p->tbdf, rno, 0, 1);
1367dd7cddfSDavid du Colombier 	if(v & 1)
1377dd7cddfSDavid du Colombier 		size |= 0xFFFF0000;
1387dd7cddfSDavid du Colombier 	pcicfgrw32(p->tbdf, rno, v, 0);
1397dd7cddfSDavid du Colombier 
1407dd7cddfSDavid du Colombier 	return -(size & ~0x0F);
1417dd7cddfSDavid du Colombier }
1427dd7cddfSDavid du Colombier 
1437dd7cddfSDavid du Colombier static int
1447dd7cddfSDavid du Colombier pcisizcmp(void *a, void *b)
1457dd7cddfSDavid du Colombier {
1467dd7cddfSDavid du Colombier 	Pcisiz *aa, *bb;
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier 	aa = a;
1497dd7cddfSDavid du Colombier 	bb = b;
1507dd7cddfSDavid du Colombier 	return aa->siz - bb->siz;
1517dd7cddfSDavid du Colombier }
1527dd7cddfSDavid du Colombier 
1537dd7cddfSDavid du Colombier static ulong
1547dd7cddfSDavid du Colombier pcimask(ulong v)
1557dd7cddfSDavid du Colombier {
1567dd7cddfSDavid du Colombier 	ulong m;
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 	m = BI2BY*sizeof(v);
1597dd7cddfSDavid du Colombier 	for(m = 1<<(m-1); m != 0; m >>= 1) {
1607dd7cddfSDavid du Colombier 		if(m & v)
1617dd7cddfSDavid du Colombier 			break;
1627dd7cddfSDavid du Colombier 	}
1637dd7cddfSDavid du Colombier 
1647dd7cddfSDavid du Colombier 	m--;
1657dd7cddfSDavid du Colombier 	if((v & m) == 0)
1667dd7cddfSDavid du Colombier 		return v;
1677dd7cddfSDavid du Colombier 
1687dd7cddfSDavid du Colombier 	v |= m;
1697dd7cddfSDavid du Colombier 	return v+1;
1707dd7cddfSDavid du Colombier }
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier static void
1737dd7cddfSDavid du Colombier pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
1747dd7cddfSDavid du Colombier {
1757dd7cddfSDavid du Colombier 	Pcidev *p;
1767dd7cddfSDavid du Colombier 	int ntb, i, size, rno, hole;
1777dd7cddfSDavid du Colombier 	ulong v, mema, ioa, sioa, smema, base, limit;
1787dd7cddfSDavid du Colombier 	Pcisiz *table, *tptr, *mtb, *itb;
1797dd7cddfSDavid du Colombier 	extern void qsort(void*, long, long, int (*)(void*, void*));
1807dd7cddfSDavid du Colombier 
18180ee5cbfSDavid du Colombier 	if(!nobios)
1827dd7cddfSDavid du Colombier 		return;
1837dd7cddfSDavid du Colombier 
1847dd7cddfSDavid du Colombier 	ioa = *pioa;
1857dd7cddfSDavid du Colombier 	mema = *pmema;
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier 	DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n",
1887dd7cddfSDavid du Colombier 		wrreg, root->tbdf, mema, ioa);
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier 	ntb = 0;
1917dd7cddfSDavid du Colombier 	for(p = root; p != nil; p = p->link)
1927dd7cddfSDavid du Colombier 		ntb++;
1937dd7cddfSDavid du Colombier 
1947dd7cddfSDavid du Colombier 	ntb *= (PciCIS-PciBAR0)/4;
1957dd7cddfSDavid du Colombier 	table = malloc(2*ntb*sizeof(Pcisiz));
1967dd7cddfSDavid du Colombier 	itb = table;
1977dd7cddfSDavid du Colombier 	mtb = table+ntb;
1987dd7cddfSDavid du Colombier 
1997dd7cddfSDavid du Colombier 	/*
2007dd7cddfSDavid du Colombier 	 * Build a table of sizes
2017dd7cddfSDavid du Colombier 	 */
2027dd7cddfSDavid du Colombier 	for(p = root; p != nil; p = p->link) {
2037dd7cddfSDavid du Colombier 		if(p->ccrb == 0x06) {
2047dd7cddfSDavid du Colombier 			if(p->ccru != 0x04 || p->bridge == nil) {
2057dd7cddfSDavid du Colombier //				DBG("pci: ignored bridge %T\n", p->tbdf);
2067dd7cddfSDavid du Colombier 				continue;
2077dd7cddfSDavid du Colombier 			}
2087dd7cddfSDavid du Colombier 
2097dd7cddfSDavid du Colombier 			sioa = ioa;
2107dd7cddfSDavid du Colombier 			smema = mema;
2117dd7cddfSDavid du Colombier 			pcibusmap(p->bridge, &smema, &sioa, 0);
2127dd7cddfSDavid du Colombier 
2137dd7cddfSDavid du Colombier 			hole = pcimask(smema-mema);
2147dd7cddfSDavid du Colombier 			if(hole < (1<<20))
2157dd7cddfSDavid du Colombier 				hole = 1<<20;
2167dd7cddfSDavid du Colombier 			p->mema.size = hole;
2177dd7cddfSDavid du Colombier 
2187dd7cddfSDavid du Colombier 			hole = pcimask(sioa-ioa);
2197dd7cddfSDavid du Colombier 			if(hole < (1<<12))
2207dd7cddfSDavid du Colombier 				hole = 1<<12;
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier 			p->ioa.size = hole;
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier 			itb->dev = p;
2257dd7cddfSDavid du Colombier 			itb->bar = -1;
2267dd7cddfSDavid du Colombier 			itb->siz = p->ioa.size;
2277dd7cddfSDavid du Colombier 			itb++;
2287dd7cddfSDavid du Colombier 
2297dd7cddfSDavid du Colombier 			mtb->dev = p;
2307dd7cddfSDavid du Colombier 			mtb->bar = -1;
2317dd7cddfSDavid du Colombier 			mtb->siz = p->mema.size;
2327dd7cddfSDavid du Colombier 			mtb++;
2337dd7cddfSDavid du Colombier 			continue;
2347dd7cddfSDavid du Colombier 		}
2357dd7cddfSDavid du Colombier 
2367dd7cddfSDavid du Colombier 		for(i = 0; i <= 5; i++) {
2377dd7cddfSDavid du Colombier 			rno = PciBAR0 + i*4;
2387dd7cddfSDavid du Colombier 			v = pcicfgrw32(p->tbdf, rno, 0, 1);
2397dd7cddfSDavid du Colombier 			size = pcibarsize(p, rno);
2407dd7cddfSDavid du Colombier 			if(size == 0)
2417dd7cddfSDavid du Colombier 				continue;
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 			if(v & 1) {
2447dd7cddfSDavid du Colombier 				itb->dev = p;
2457dd7cddfSDavid du Colombier 				itb->bar = i;
2467dd7cddfSDavid du Colombier 				itb->siz = size;
2477dd7cddfSDavid du Colombier 				itb++;
2487dd7cddfSDavid du Colombier 			}
2497dd7cddfSDavid du Colombier 			else {
2507dd7cddfSDavid du Colombier 				mtb->dev = p;
2517dd7cddfSDavid du Colombier 				mtb->bar = i;
2527dd7cddfSDavid du Colombier 				mtb->siz = size;
2537dd7cddfSDavid du Colombier 				mtb++;
2547dd7cddfSDavid du Colombier 			}
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier 			p->mem[i].size = size;
2577dd7cddfSDavid du Colombier 		}
2587dd7cddfSDavid du Colombier 	}
2597dd7cddfSDavid du Colombier 
2607dd7cddfSDavid du Colombier 	/*
2617dd7cddfSDavid du Colombier 	 * Sort both tables IO smallest first, Memory largest
2627dd7cddfSDavid du Colombier 	 */
2637dd7cddfSDavid du Colombier 	qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
2647dd7cddfSDavid du Colombier 	tptr = table+ntb;
2657dd7cddfSDavid du Colombier 	qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier 	/*
2687dd7cddfSDavid du Colombier 	 * Allocate IO address space on this bus
2697dd7cddfSDavid du Colombier 	 */
2707dd7cddfSDavid du Colombier 	for(tptr = table; tptr < itb; tptr++) {
2717dd7cddfSDavid du Colombier 		hole = tptr->siz;
2727dd7cddfSDavid du Colombier 		if(tptr->bar == -1)
2737dd7cddfSDavid du Colombier 			hole = 1<<12;
2747dd7cddfSDavid du Colombier 		ioa = (ioa+hole-1) & ~(hole-1);
2757dd7cddfSDavid du Colombier 
2767dd7cddfSDavid du Colombier 		p = tptr->dev;
2777dd7cddfSDavid du Colombier 		if(tptr->bar == -1)
2787dd7cddfSDavid du Colombier 			p->ioa.bar = ioa;
2797dd7cddfSDavid du Colombier 		else {
2807dd7cddfSDavid du Colombier 			p->pcr |= IOen;
2817dd7cddfSDavid du Colombier 			p->mem[tptr->bar].bar = ioa|1;
2827dd7cddfSDavid du Colombier 			if(wrreg)
2837dd7cddfSDavid du Colombier 				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
2847dd7cddfSDavid du Colombier 		}
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier 		ioa += tptr->siz;
2877dd7cddfSDavid du Colombier 	}
2887dd7cddfSDavid du Colombier 
2897dd7cddfSDavid du Colombier 	/*
2907dd7cddfSDavid du Colombier 	 * Allocate Memory address space on this bus
2917dd7cddfSDavid du Colombier 	 */
2927dd7cddfSDavid du Colombier 	for(tptr = table+ntb; tptr < mtb; tptr++) {
2937dd7cddfSDavid du Colombier 		hole = tptr->siz;
2947dd7cddfSDavid du Colombier 		if(tptr->bar == -1)
2957dd7cddfSDavid du Colombier 			hole = 1<<20;
2967dd7cddfSDavid du Colombier 		mema = (mema+hole-1) & ~(hole-1);
2977dd7cddfSDavid du Colombier 
2987dd7cddfSDavid du Colombier 		p = tptr->dev;
2997dd7cddfSDavid du Colombier 		if(tptr->bar == -1)
3007dd7cddfSDavid du Colombier 			p->mema.bar = mema;
3017dd7cddfSDavid du Colombier 		else {
3027dd7cddfSDavid du Colombier 			p->pcr |= MEMen;
3037dd7cddfSDavid du Colombier 			p->mem[tptr->bar].bar = mema;
3047dd7cddfSDavid du Colombier 			if(wrreg)
3057dd7cddfSDavid du Colombier 				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
3067dd7cddfSDavid du Colombier 		}
3077dd7cddfSDavid du Colombier 		mema += tptr->siz;
3087dd7cddfSDavid du Colombier 	}
3097dd7cddfSDavid du Colombier 
3107dd7cddfSDavid du Colombier 	*pmema = mema;
3117dd7cddfSDavid du Colombier 	*pioa = ioa;
3127dd7cddfSDavid du Colombier 	free(table);
3137dd7cddfSDavid du Colombier 
3147dd7cddfSDavid du Colombier 	if(wrreg == 0)
3157dd7cddfSDavid du Colombier 		return;
3167dd7cddfSDavid du Colombier 
3177dd7cddfSDavid du Colombier 	/*
3187dd7cddfSDavid du Colombier 	 * Finally set all the bridge addresses & registers
3197dd7cddfSDavid du Colombier 	 */
3207dd7cddfSDavid du Colombier 	for(p = root; p != nil; p = p->link) {
3217dd7cddfSDavid du Colombier 		if(p->bridge == nil) {
3227dd7cddfSDavid du Colombier 			pcicfgrw8(p->tbdf, PciLTR, 64, 0);
3237dd7cddfSDavid du Colombier 
3247dd7cddfSDavid du Colombier 			p->pcr |= MASen;
325e569ccb5SDavid du Colombier 			pcicfgrw16(p->tbdf, PciPCR, p->pcr, 0);
3267dd7cddfSDavid du Colombier 			continue;
3277dd7cddfSDavid du Colombier 		}
3287dd7cddfSDavid du Colombier 
3297dd7cddfSDavid du Colombier 		base = p->ioa.bar;
3307dd7cddfSDavid du Colombier 		limit = base+p->ioa.size-1;
331d0f3faacSDavid du Colombier 		v = pcicfgrw32(p->tbdf, PciIBR, 0, 1);
3327dd7cddfSDavid du Colombier 		v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
333d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciIBR, v, 0);
3347dd7cddfSDavid du Colombier 		v = (limit & 0xFFFF0000)|(base>>16);
335d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciIUBR, v, 0);
3367dd7cddfSDavid du Colombier 
3377dd7cddfSDavid du Colombier 		base = p->mema.bar;
3387dd7cddfSDavid du Colombier 		limit = base+p->mema.size-1;
3397dd7cddfSDavid du Colombier 		v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
340d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciMBR, v, 0);
3417dd7cddfSDavid du Colombier 
3427dd7cddfSDavid du Colombier 		/*
3437dd7cddfSDavid du Colombier 		 * Disable memory prefetch
3447dd7cddfSDavid du Colombier 		 */
345d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0);
3467dd7cddfSDavid du Colombier 		pcicfgrw8(p->tbdf, PciLTR, 64, 0);
3477dd7cddfSDavid du Colombier 
3487dd7cddfSDavid du Colombier 		/*
3497dd7cddfSDavid du Colombier 		 * Enable the bridge
3507dd7cddfSDavid du Colombier 		 */
351e569ccb5SDavid du Colombier 		p->pcr |= IOen|MEMen|MASen;
352e569ccb5SDavid du Colombier 		pcicfgrw32(p->tbdf, PciPCR, 0xFFFF0000|p->pcr , 0);
3537dd7cddfSDavid du Colombier 
3547dd7cddfSDavid du Colombier 		sioa = p->ioa.bar;
3557dd7cddfSDavid du Colombier 		smema = p->mema.bar;
3567dd7cddfSDavid du Colombier 		pcibusmap(p->bridge, &smema, &sioa, 1);
3577dd7cddfSDavid du Colombier 	}
3587dd7cddfSDavid du Colombier }
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier static int
3619a747e4fSDavid du Colombier pcilscan(int bno, Pcidev** list)
3627dd7cddfSDavid du Colombier {
3637dd7cddfSDavid du Colombier 	Pcidev *p, *head, *tail;
3647dd7cddfSDavid du Colombier 	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
3657dd7cddfSDavid du Colombier 
3667dd7cddfSDavid du Colombier 	maxubn = bno;
3677dd7cddfSDavid du Colombier 	head = nil;
3687dd7cddfSDavid du Colombier 	tail = nil;
3697dd7cddfSDavid du Colombier 	for(dno = 0; dno <= pcimaxdno; dno++){
3707dd7cddfSDavid du Colombier 		maxfno = 0;
3717dd7cddfSDavid du Colombier 		for(fno = 0; fno <= maxfno; fno++){
3727dd7cddfSDavid du Colombier 			/*
3737dd7cddfSDavid du Colombier 			 * For this possible device, form the
3747dd7cddfSDavid du Colombier 			 * bus+device+function triplet needed to address it
3757dd7cddfSDavid du Colombier 			 * and try to read the vendor and device ID.
3767dd7cddfSDavid du Colombier 			 * If successful, allocate a device struct and
3777dd7cddfSDavid du Colombier 			 * start to fill it in with some useful information
3787dd7cddfSDavid du Colombier 			 * from the device's configuration space.
3797dd7cddfSDavid du Colombier 			 */
3807dd7cddfSDavid du Colombier 			tbdf = MKBUS(BusPCI, bno, dno, fno);
3817dd7cddfSDavid du Colombier 			l = pcicfgrw32(tbdf, PciVID, 0, 1);
3827dd7cddfSDavid du Colombier 			if(l == 0xFFFFFFFF || l == 0)
3837dd7cddfSDavid du Colombier 				continue;
3847dd7cddfSDavid du Colombier 			p = malloc(sizeof(*p));
3857dd7cddfSDavid du Colombier 			p->tbdf = tbdf;
3867dd7cddfSDavid du Colombier 			p->vid = l;
3877dd7cddfSDavid du Colombier 			p->did = l>>16;
3887dd7cddfSDavid du Colombier 
3897dd7cddfSDavid du Colombier 			if(pcilist != nil)
3907dd7cddfSDavid du Colombier 				pcitail->list = p;
3917dd7cddfSDavid du Colombier 			else
3927dd7cddfSDavid du Colombier 				pcilist = p;
3937dd7cddfSDavid du Colombier 			pcitail = p;
3947dd7cddfSDavid du Colombier 
395e569ccb5SDavid du Colombier 			p->pcr = pcicfgr16(p, PciPCR);
3967dd7cddfSDavid du Colombier 			p->rid = pcicfgr8(p, PciRID);
3977dd7cddfSDavid du Colombier 			p->ccrp = pcicfgr8(p, PciCCRp);
3987dd7cddfSDavid du Colombier 			p->ccru = pcicfgr8(p, PciCCRu);
3997dd7cddfSDavid du Colombier 			p->ccrb = pcicfgr8(p, PciCCRb);
400e569ccb5SDavid du Colombier 			p->cls = pcicfgr8(p, PciCLS);
401e569ccb5SDavid du Colombier 			p->ltr = pcicfgr8(p, PciLTR);
4027dd7cddfSDavid du Colombier 
4037dd7cddfSDavid du Colombier 			p->intl = pcicfgr8(p, PciINTL);
4047dd7cddfSDavid du Colombier 
4057dd7cddfSDavid du Colombier 			/*
4067dd7cddfSDavid du Colombier 			 * If the device is a multi-function device adjust the
4077dd7cddfSDavid du Colombier 			 * loop count so all possible functions are checked.
4087dd7cddfSDavid du Colombier 			 */
4097dd7cddfSDavid du Colombier 			hdt = pcicfgr8(p, PciHDT);
4107dd7cddfSDavid du Colombier 			if(hdt & 0x80)
4117dd7cddfSDavid du Colombier 				maxfno = MaxFNO;
4127dd7cddfSDavid du Colombier 
4137dd7cddfSDavid du Colombier 			/*
4147dd7cddfSDavid du Colombier 			 * If appropriate, read the base address registers
4157dd7cddfSDavid du Colombier 			 * and work out the sizes.
4167dd7cddfSDavid du Colombier 			 */
4177dd7cddfSDavid du Colombier 			switch(p->ccrb) {
4187dd7cddfSDavid du Colombier 			case 0x01:		/* mass storage controller */
4197dd7cddfSDavid du Colombier 			case 0x02:		/* network controller */
4207dd7cddfSDavid du Colombier 			case 0x03:		/* display controller */
4217dd7cddfSDavid du Colombier 			case 0x04:		/* multimedia device */
4227dd7cddfSDavid du Colombier 			case 0x07:		/* simple comm. controllers */
4237dd7cddfSDavid du Colombier 			case 0x08:		/* base system peripherals */
4247dd7cddfSDavid du Colombier 			case 0x09:		/* input devices */
4257dd7cddfSDavid du Colombier 			case 0x0A:		/* docking stations */
4267dd7cddfSDavid du Colombier 			case 0x0B:		/* processors */
4277dd7cddfSDavid du Colombier 			case 0x0C:		/* serial bus controllers */
4287dd7cddfSDavid du Colombier 				if((hdt & 0x7F) != 0)
4297dd7cddfSDavid du Colombier 					break;
4307dd7cddfSDavid du Colombier 				rno = PciBAR0 - 4;
4317dd7cddfSDavid du Colombier 				for(i = 0; i < nelem(p->mem); i++) {
4327dd7cddfSDavid du Colombier 					rno += 4;
4337dd7cddfSDavid du Colombier 					p->mem[i].bar = pcicfgr32(p, rno);
4347dd7cddfSDavid du Colombier 					p->mem[i].size = pcibarsize(p, rno);
4357dd7cddfSDavid du Colombier 				}
4367dd7cddfSDavid du Colombier 				break;
4377dd7cddfSDavid du Colombier 
4387dd7cddfSDavid du Colombier 			case 0x00:
4397dd7cddfSDavid du Colombier 			case 0x05:		/* memory controller */
4407dd7cddfSDavid du Colombier 			case 0x06:		/* bridge device */
4417dd7cddfSDavid du Colombier 			default:
4427dd7cddfSDavid du Colombier 				break;
4437dd7cddfSDavid du Colombier 			}
4447dd7cddfSDavid du Colombier 
4457dd7cddfSDavid du Colombier 			if(head != nil)
4467dd7cddfSDavid du Colombier 				tail->link = p;
4477dd7cddfSDavid du Colombier 			else
4487dd7cddfSDavid du Colombier 				head = p;
4497dd7cddfSDavid du Colombier 			tail = p;
4507dd7cddfSDavid du Colombier 		}
4517dd7cddfSDavid du Colombier 	}
4527dd7cddfSDavid du Colombier 
4537dd7cddfSDavid du Colombier 	*list = head;
4547dd7cddfSDavid du Colombier 	for(p = head; p != nil; p = p->link){
4557dd7cddfSDavid du Colombier 		/*
4567dd7cddfSDavid du Colombier 		 * Find PCI-PCI bridges and recursively descend the tree.
4577dd7cddfSDavid du Colombier 		 */
4587dd7cddfSDavid du Colombier 		if(p->ccrb != 0x06 || p->ccru != 0x04)
4597dd7cddfSDavid du Colombier 			continue;
4607dd7cddfSDavid du Colombier 
4617dd7cddfSDavid du Colombier 		/*
4627dd7cddfSDavid du Colombier 		 * If the secondary or subordinate bus number is not
4637dd7cddfSDavid du Colombier 		 * initialised try to do what the PCI BIOS should have
4647dd7cddfSDavid du Colombier 		 * done and fill in the numbers as the tree is descended.
4657dd7cddfSDavid du Colombier 		 * On the way down the subordinate bus number is set to
4667dd7cddfSDavid du Colombier 		 * the maximum as it's not known how many buses are behind
4677dd7cddfSDavid du Colombier 		 * this one; the final value is set on the way back up.
4687dd7cddfSDavid du Colombier 		 */
4697dd7cddfSDavid du Colombier 		sbn = pcicfgr8(p, PciSBN);
4707dd7cddfSDavid du Colombier 		ubn = pcicfgr8(p, PciUBN);
4717dd7cddfSDavid du Colombier 
47280ee5cbfSDavid du Colombier 		if(sbn == 0 || ubn == 0 || nobios) {
4737dd7cddfSDavid du Colombier 			sbn = maxubn+1;
4747dd7cddfSDavid du Colombier 			/*
4757dd7cddfSDavid du Colombier 			 * Make sure memory, I/O and master enables are
4767dd7cddfSDavid du Colombier 			 * off, set the primary, secondary and subordinate
4777dd7cddfSDavid du Colombier 			 * bus numbers and clear the secondary status before
4787dd7cddfSDavid du Colombier 			 * attempting to scan the secondary bus.
4797dd7cddfSDavid du Colombier 			 *
4807dd7cddfSDavid du Colombier 			 * Initialisation of the bridge should be done here.
4817dd7cddfSDavid du Colombier 			 */
4827dd7cddfSDavid du Colombier 			pcicfgw32(p, PciPCR, 0xFFFF0000);
4837dd7cddfSDavid du Colombier 			l = (MaxUBN<<16)|(sbn<<8)|bno;
4847dd7cddfSDavid du Colombier 			pcicfgw32(p, PciPBN, l);
4857dd7cddfSDavid du Colombier 			pcicfgw16(p, PciSPSR, 0xFFFF);
4869a747e4fSDavid du Colombier 			maxubn = pcilscan(sbn, &p->bridge);
4877dd7cddfSDavid du Colombier 			l = (maxubn<<16)|(sbn<<8)|bno;
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 			pcicfgw32(p, PciPBN, l);
4907dd7cddfSDavid du Colombier 		}
4917dd7cddfSDavid du Colombier 		else {
492e569ccb5SDavid du Colombier 			if(ubn > maxubn)
4937dd7cddfSDavid du Colombier 				maxubn = ubn;
4949a747e4fSDavid du Colombier 			pcilscan(sbn, &p->bridge);
4957dd7cddfSDavid du Colombier 		}
4967dd7cddfSDavid du Colombier 	}
4977dd7cddfSDavid du Colombier 
4987dd7cddfSDavid du Colombier 	return maxubn;
4997dd7cddfSDavid du Colombier }
5007dd7cddfSDavid du Colombier 
5019a747e4fSDavid du Colombier int
5029a747e4fSDavid du Colombier pciscan(int bno, Pcidev **list)
5039a747e4fSDavid du Colombier {
5049a747e4fSDavid du Colombier 	int ubn;
5059a747e4fSDavid du Colombier 
5069a747e4fSDavid du Colombier 	lock(&pcicfginitlock);
5079a747e4fSDavid du Colombier 	ubn = pcilscan(bno, list);
5089a747e4fSDavid du Colombier 	unlock(&pcicfginitlock);
5099a747e4fSDavid du Colombier 	return ubn;
5109a747e4fSDavid du Colombier }
5119a747e4fSDavid du Colombier 
5129a747e4fSDavid du Colombier static uchar
51359c21d95SDavid du Colombier pIIxget(Pcidev *router, uchar link)
5149a747e4fSDavid du Colombier {
5159a747e4fSDavid du Colombier 	uchar pirq;
5169a747e4fSDavid du Colombier 
5179a747e4fSDavid du Colombier 	/* link should be 0x60, 0x61, 0x62, 0x63 */
5189a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, link);
5199a747e4fSDavid du Colombier 	return (pirq < 16)? pirq: 0;
5209a747e4fSDavid du Colombier }
5219a747e4fSDavid du Colombier 
5229a747e4fSDavid du Colombier static void
52359c21d95SDavid du Colombier pIIxset(Pcidev *router, uchar link, uchar irq)
5249a747e4fSDavid du Colombier {
5259a747e4fSDavid du Colombier 	pcicfgw8(router, link, irq);
5269a747e4fSDavid du Colombier }
5279a747e4fSDavid du Colombier 
5289a747e4fSDavid du Colombier static uchar
52959c21d95SDavid du Colombier viaget(Pcidev *router, uchar link)
5309a747e4fSDavid du Colombier {
5319a747e4fSDavid du Colombier 	uchar pirq;
5329a747e4fSDavid du Colombier 
5339a747e4fSDavid du Colombier 	/* link should be 1, 2, 3, 5 */
5349a747e4fSDavid du Colombier 	pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0;
5359a747e4fSDavid du Colombier 
5369a747e4fSDavid du Colombier 	return (link & 1)? (pirq >> 4): (pirq & 15);
5379a747e4fSDavid du Colombier }
5389a747e4fSDavid du Colombier 
5399a747e4fSDavid du Colombier static void
54059c21d95SDavid du Colombier viaset(Pcidev *router, uchar link, uchar irq)
5419a747e4fSDavid du Colombier {
5429a747e4fSDavid du Colombier 	uchar pirq;
5439a747e4fSDavid du Colombier 
5449a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x55 + (link >> 1));
5459a747e4fSDavid du Colombier 	pirq &= (link & 1)? 0x0f: 0xf0;
5469a747e4fSDavid du Colombier 	pirq |= (link & 1)? (irq << 4): (irq & 15);
5479a747e4fSDavid du Colombier 	pcicfgw8(router, 0x55 + (link>>1), pirq);
5489a747e4fSDavid du Colombier }
5499a747e4fSDavid du Colombier 
5509a747e4fSDavid du Colombier static uchar
55159c21d95SDavid du Colombier optiget(Pcidev *router, uchar link)
5529a747e4fSDavid du Colombier {
5539a747e4fSDavid du Colombier 	uchar pirq = 0;
5549a747e4fSDavid du Colombier 
5559a747e4fSDavid du Colombier 	/* link should be 0x02, 0x12, 0x22, 0x32 */
5569a747e4fSDavid du Colombier 	if ((link & 0xcf) == 0x02)
5579a747e4fSDavid du Colombier 		pirq = pcicfgr8(router, 0xb8 + (link >> 5));
5589a747e4fSDavid du Colombier 	return (link & 0x10)? (pirq >> 4): (pirq & 15);
5599a747e4fSDavid du Colombier }
5609a747e4fSDavid du Colombier 
5619a747e4fSDavid du Colombier static void
56259c21d95SDavid du Colombier optiset(Pcidev *router, uchar link, uchar irq)
5639a747e4fSDavid du Colombier {
5649a747e4fSDavid du Colombier 	uchar pirq;
5659a747e4fSDavid du Colombier 
5669a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0xb8 + (link >> 5));
5679a747e4fSDavid du Colombier     	pirq &= (link & 0x10)? 0x0f : 0xf0;
5689a747e4fSDavid du Colombier     	pirq |= (link & 0x10)? (irq << 4): (irq & 15);
5699a747e4fSDavid du Colombier 	pcicfgw8(router, 0xb8 + (link >> 5), pirq);
5709a747e4fSDavid du Colombier }
5719a747e4fSDavid du Colombier 
5729a747e4fSDavid du Colombier static uchar
57359c21d95SDavid du Colombier aliget(Pcidev *router, uchar link)
5749a747e4fSDavid du Colombier {
5759a747e4fSDavid du Colombier 	/* No, you're not dreaming */
5769a747e4fSDavid du Colombier 	static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
5779a747e4fSDavid du Colombier 	uchar pirq;
5789a747e4fSDavid du Colombier 
5799a747e4fSDavid du Colombier 	/* link should be 0x01..0x08 */
5809a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
5819a747e4fSDavid du Colombier 	return (link & 1)? map[pirq&15]: map[pirq>>4];
5829a747e4fSDavid du Colombier }
5839a747e4fSDavid du Colombier 
5849a747e4fSDavid du Colombier static void
58559c21d95SDavid du Colombier aliset(Pcidev *router, uchar link, uchar irq)
5869a747e4fSDavid du Colombier {
58759c21d95SDavid du Colombier 	/* Inverse of map in aliget */
5889a747e4fSDavid du Colombier 	static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
5899a747e4fSDavid du Colombier 	uchar pirq;
5909a747e4fSDavid du Colombier 
5919a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
5929a747e4fSDavid du Colombier 	pirq &= (link & 1)? 0x0f: 0xf0;
5939a747e4fSDavid du Colombier 	pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15);
5949a747e4fSDavid du Colombier 	pcicfgw8(router, 0x48 + ((link-1)>>1), pirq);
5959a747e4fSDavid du Colombier }
5969a747e4fSDavid du Colombier 
5979a747e4fSDavid du Colombier static uchar
59859c21d95SDavid du Colombier cyrixget(Pcidev *router, uchar link)
5999a747e4fSDavid du Colombier {
6009a747e4fSDavid du Colombier 	uchar pirq;
6019a747e4fSDavid du Colombier 
6029a747e4fSDavid du Colombier 	/* link should be 1, 2, 3, 4 */
6039a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x5c + ((link-1)>>1));
6049a747e4fSDavid du Colombier 	return ((link & 1)? pirq >> 4: pirq & 15);
6059a747e4fSDavid du Colombier }
6069a747e4fSDavid du Colombier 
6079a747e4fSDavid du Colombier static void
60859c21d95SDavid du Colombier cyrixset(Pcidev *router, uchar link, uchar irq)
6099a747e4fSDavid du Colombier {
6109a747e4fSDavid du Colombier 	uchar pirq;
6119a747e4fSDavid du Colombier 
6129a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x5c + (link>>1));
6139a747e4fSDavid du Colombier 	pirq &= (link & 1)? 0x0f: 0xf0;
6149a747e4fSDavid du Colombier 	pirq |= (link & 1)? (irq << 4): (irq & 15);
6159a747e4fSDavid du Colombier 	pcicfgw8(router, 0x5c + (link>>1), pirq);
6169a747e4fSDavid du Colombier }
6179a747e4fSDavid du Colombier 
61859c21d95SDavid du Colombier typedef struct Bridge Bridge;
61959c21d95SDavid du Colombier struct Bridge
6209a747e4fSDavid du Colombier {
6219a747e4fSDavid du Colombier 	ushort	vid;
6229a747e4fSDavid du Colombier 	ushort	did;
6239a747e4fSDavid du Colombier 	uchar	(*get)(Pcidev *, uchar);
6249a747e4fSDavid du Colombier 	void	(*set)(Pcidev *, uchar, uchar);
6259a747e4fSDavid du Colombier };
6269a747e4fSDavid du Colombier 
62759c21d95SDavid du Colombier static Bridge southbridges[] = {
62859c21d95SDavid du Colombier 	{ 0x8086, 0x122e, pIIxget, pIIxset },	// Intel 82371FB
62959c21d95SDavid du Colombier 	{ 0x8086, 0x1234, pIIxget, pIIxset },	// Intel 82371MX
63059c21d95SDavid du Colombier 	{ 0x8086, 0x7000, pIIxget, pIIxset },	// Intel 82371SB
63159c21d95SDavid du Colombier 	{ 0x8086, 0x7110, pIIxget, pIIxset },	// Intel 82371AB
63259c21d95SDavid du Colombier 	{ 0x8086, 0x7198, pIIxget, pIIxset },	// Intel 82443MX (fn 1)
63359c21d95SDavid du Colombier 	{ 0x8086, 0x2410, pIIxget, pIIxset },	// Intel 82801AA
63459c21d95SDavid du Colombier 	{ 0x8086, 0x2420, pIIxget, pIIxset },	// Intel 82801AB
63559c21d95SDavid du Colombier 	{ 0x8086, 0x2440, pIIxget, pIIxset },	// Intel 82801BA
63659c21d95SDavid du Colombier 	{ 0x8086, 0x244c, pIIxget, pIIxset },	// Intel 82801BAM
6377c881178SDavid du Colombier 	{ 0x8086, 0x248c, pIIxget, pIIxset },	// Intel 82801CAM
6382839d78eSDavid du Colombier 	{ 0x8086, 0x24cc, pIIxget, pIIxset },	// Intel 82801DBM
6397c881178SDavid du Colombier 	{ 0x8086, 0x24d0, pIIxget, pIIxset },	// Intel 82801EB
640d8f3f65eSDavid du Colombier 	{ 0x8086, 0x2640, pIIxget, pIIxset },	// Intel 82801FB
64159c21d95SDavid du Colombier 	{ 0x1106, 0x0586, viaget, viaset },	// Viatech 82C586
64259c21d95SDavid du Colombier 	{ 0x1106, 0x0596, viaget, viaset },	// Viatech 82C596
64359c21d95SDavid du Colombier 	{ 0x1106, 0x0686, viaget, viaset },	// Viatech 82C686
6445000fddeSDavid du Colombier 	{ 0x1106, 0x3227, viaget, viaset },	// Viatech VT8237
64559c21d95SDavid du Colombier 	{ 0x1045, 0xc700, optiget, optiset },	// Opti 82C700
64659c21d95SDavid du Colombier 	{ 0x10b9, 0x1533, aliget, aliset },	// Al M1533
64759c21d95SDavid du Colombier 	{ 0x1039, 0x0008, pIIxget, pIIxset },	// SI 503
64859c21d95SDavid du Colombier 	{ 0x1039, 0x0496, pIIxget, pIIxset },	// SI 496
6495000fddeSDavid du Colombier 	{ 0x1078, 0x0100, cyrixget, cyrixset },	// Cyrix 5530 Legacy
6505000fddeSDavid du Colombier 
6515000fddeSDavid du Colombier 	{ 0x1022, 0x746B, nil, nil },		// AMD 8111
6525000fddeSDavid du Colombier 	{ 0x10DE, 0x00D1, nil, nil },		// NVIDIA nForce 3
6532839d78eSDavid du Colombier 	{ 0x1166, 0x0200, nil, nil },		// ServerWorks ServerSet III LE
65459c21d95SDavid du Colombier };
6559a747e4fSDavid du Colombier 
65659c21d95SDavid du Colombier typedef struct Slot Slot;
65759c21d95SDavid du Colombier struct Slot {
65859c21d95SDavid du Colombier 	uchar	bus;			// Pci bus number
65959c21d95SDavid du Colombier 	uchar	dev;			// Pci device number
66059c21d95SDavid du Colombier 	uchar	maps[12];		// Avoid structs!  Link and mask.
66159c21d95SDavid du Colombier 	uchar	slot;			// Add-in/built-in slot
66259c21d95SDavid du Colombier 	uchar	reserved;
66359c21d95SDavid du Colombier };
66459c21d95SDavid du Colombier 
66559c21d95SDavid du Colombier typedef struct Router Router;
66659c21d95SDavid du Colombier struct Router {
66759c21d95SDavid du Colombier 	uchar	signature[4];		// Routing table signature
66859c21d95SDavid du Colombier 	uchar	version[2];		// Version number
66959c21d95SDavid du Colombier 	uchar	size[2];		// Total table size
67059c21d95SDavid du Colombier 	uchar	bus;			// Interrupt router bus number
67159c21d95SDavid du Colombier 	uchar	devfn;			// Router's devfunc
67259c21d95SDavid du Colombier 	uchar	pciirqs[2];		// Exclusive PCI irqs
67359c21d95SDavid du Colombier 	uchar	compat[4];		// Compatible PCI interrupt router
67459c21d95SDavid du Colombier 	uchar	miniport[4];		// Miniport data
67559c21d95SDavid du Colombier 	uchar	reserved[11];
67659c21d95SDavid du Colombier 	uchar	checksum;
67759c21d95SDavid du Colombier };
6789a747e4fSDavid du Colombier 
6799a747e4fSDavid du Colombier static ushort pciirqs;			// Exclusive PCI irqs
68059c21d95SDavid du Colombier static Bridge *southbridge;		// Which southbridge to use.
6819a747e4fSDavid du Colombier 
6829a747e4fSDavid du Colombier static void
6839a747e4fSDavid du Colombier pcirouting(void)
6849a747e4fSDavid du Colombier {
68559c21d95SDavid du Colombier 	Slot *e;
68659c21d95SDavid du Colombier 	Router *r;
6879a747e4fSDavid du Colombier 	int size, i, fn, tbdf;
6889a747e4fSDavid du Colombier 	Pcidev *sbpci, *pci;
6895000fddeSDavid du Colombier 	uchar *p, pin, irq, link, *map;
6909a747e4fSDavid du Colombier 
6919a747e4fSDavid du Colombier 	// Search for PCI interrupt routing table in BIOS
6929a747e4fSDavid du Colombier 	for(p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16)
6939a747e4fSDavid du Colombier 		if(p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R')
6949a747e4fSDavid du Colombier 			break;
6959a747e4fSDavid du Colombier 
6969a747e4fSDavid du Colombier 	if(p >= (uchar *)KADDR(0xfffff))
6979a747e4fSDavid du Colombier 		return;
6989a747e4fSDavid du Colombier 
69959c21d95SDavid du Colombier 	r = (Router *)p;
7009a747e4fSDavid du Colombier 
7019a747e4fSDavid du Colombier 	// print("PCI interrupt routing table version %d.%d at %.6uX\n",
70259c21d95SDavid du Colombier 	// 	r->version[0], r->version[1], (ulong)r & 0xfffff);
7039a747e4fSDavid du Colombier 
70459c21d95SDavid du Colombier 	tbdf = (BusPCI << 24)|(r->bus << 16)|(r->devfn << 8);
7059a747e4fSDavid du Colombier 	sbpci = pcimatchtbdf(tbdf);
7069a747e4fSDavid du Colombier 	if(sbpci == nil) {
7079a747e4fSDavid du Colombier 		print("pcirouting: Cannot find south bridge %T\n", tbdf);
7089a747e4fSDavid du Colombier 		return;
7099a747e4fSDavid du Colombier 	}
7109a747e4fSDavid du Colombier 
7119a747e4fSDavid du Colombier 	for(i = 0; i != nelem(southbridges); i++)
7129a747e4fSDavid du Colombier 		if(sbpci->vid == southbridges[i].vid && sbpci->did == southbridges[i].did)
7139a747e4fSDavid du Colombier 			break;
7149a747e4fSDavid du Colombier 
7159a747e4fSDavid du Colombier 	if(i == nelem(southbridges)) {
7163ff48bf5SDavid du Colombier 		print("pcirouting: ignoring south bridge %T %.4uX/%.4uX\n", tbdf, sbpci->vid, sbpci->did);
7179a747e4fSDavid du Colombier 		return;
7189a747e4fSDavid du Colombier 	}
7199a747e4fSDavid du Colombier 	southbridge = &southbridges[i];
7205000fddeSDavid du Colombier 	if(southbridge->get == nil || southbridge->set == nil)
7215000fddeSDavid du Colombier 		return;
7229a747e4fSDavid du Colombier 
72359c21d95SDavid du Colombier 	pciirqs = (r->pciirqs[1] << 8)|r->pciirqs[0];
7249a747e4fSDavid du Colombier 
72559c21d95SDavid du Colombier 	size = (r->size[1] << 8)|r->size[0];
72659c21d95SDavid du Colombier 	for(e = (Slot *)&r[1]; (uchar *)e < p + size; e++) {
72759c21d95SDavid du Colombier 		// print("%.2uX/%.2uX %.2uX: ", e->bus, e->dev, e->slot);
7289a747e4fSDavid du Colombier 		// for (i = 0; i != 4; i++) {
72959c21d95SDavid du Colombier 		// 	uchar *m = &e->maps[i * 3];
7309a747e4fSDavid du Colombier 		// 	print("[%d] %.2uX %.4uX ",
7319a747e4fSDavid du Colombier 		// 		i, m[0], (m[2] << 8)|m[1]);
7329a747e4fSDavid du Colombier 		// }
7339a747e4fSDavid du Colombier 		// print("\n");
7349a747e4fSDavid du Colombier 
7359a747e4fSDavid du Colombier 		for(fn = 0; fn != 8; fn++) {
73659c21d95SDavid du Colombier 			tbdf = (BusPCI << 24)|(e->bus << 16)|((e->dev | fn) << 8);
7379a747e4fSDavid du Colombier 			pci = pcimatchtbdf(tbdf);
7389a747e4fSDavid du Colombier 			if(pci == nil)
7399a747e4fSDavid du Colombier 				continue;
7409a747e4fSDavid du Colombier 			pin = pcicfgr8(pci, PciINTP);
7419a747e4fSDavid du Colombier 			if(pin == 0 || pin == 0xff)
7429a747e4fSDavid du Colombier 				continue;
7439a747e4fSDavid du Colombier 
7445000fddeSDavid du Colombier 			map = &e->maps[(pin - 1) * 3];
7455000fddeSDavid du Colombier 			link = map[0];
7463ff48bf5SDavid du Colombier 			irq = southbridge->get(sbpci, link);
7479a747e4fSDavid du Colombier 			if(irq == 0 || irq == pci->intl)
7489a747e4fSDavid du Colombier 				continue;
7499a747e4fSDavid du Colombier 			if(pci->intl != 0 && pci->intl != 0xFF) {
7503ff48bf5SDavid du Colombier 				print("pcirouting: BIOS workaround: %T at pin %d link %d irq %d -> %d\n",
7513ff48bf5SDavid du Colombier 					  tbdf, pin, link, irq, pci->intl);
7523ff48bf5SDavid du Colombier 				southbridge->set(sbpci, link, pci->intl);
7539a747e4fSDavid du Colombier 				continue;
7549a747e4fSDavid du Colombier 			}
7553ff48bf5SDavid du Colombier 			print("pcirouting: %T at pin %d link %d irq %d\n", tbdf, pin, link, irq);
7569a747e4fSDavid du Colombier 			pcicfgw8(pci, PciINTL, irq);
7579a747e4fSDavid du Colombier 			pci->intl = irq;
7589a747e4fSDavid du Colombier 		}
7599a747e4fSDavid du Colombier 	}
7609a747e4fSDavid du Colombier }
7619a747e4fSDavid du Colombier 
762*4de34a7eSDavid du Colombier static void pcireservemem(void);
763*4de34a7eSDavid du Colombier 
7647dd7cddfSDavid du Colombier static void
7657dd7cddfSDavid du Colombier pcicfginit(void)
7667dd7cddfSDavid du Colombier {
7677dd7cddfSDavid du Colombier 	char *p;
7687dd7cddfSDavid du Colombier 	Pcidev **list;
7697dd7cddfSDavid du Colombier 	ulong mema, ioa;
770becaf2abSDavid du Colombier 	int bno, n, pcibios;
7717dd7cddfSDavid du Colombier 
7727dd7cddfSDavid du Colombier 	lock(&pcicfginitlock);
7737dd7cddfSDavid du Colombier 	if(pcicfgmode != -1)
7747dd7cddfSDavid du Colombier 		goto out;
7757dd7cddfSDavid du Colombier 
776becaf2abSDavid du Colombier 	pcibios = 0;
77780ee5cbfSDavid du Colombier 	if(getconf("*nobios"))
77880ee5cbfSDavid du Colombier 		nobios = 1;
779becaf2abSDavid du Colombier 	else if(getconf("*pcibios"))
780becaf2abSDavid du Colombier 		pcibios = 1;
7813ff48bf5SDavid du Colombier 	if(getconf("*nopcirouting"))
7823ff48bf5SDavid du Colombier 		nopcirouting = 1;
7833ff48bf5SDavid du Colombier 
7847dd7cddfSDavid du Colombier 	/*
7857dd7cddfSDavid du Colombier 	 * Try to determine which PCI configuration mode is implemented.
7867dd7cddfSDavid du Colombier 	 * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
7877dd7cddfSDavid du Colombier 	 * a DWORD at 0xCF8 and another at 0xCFC and will pass through
7887dd7cddfSDavid du Colombier 	 * any non-DWORD accesses as normal I/O cycles. There shouldn't be
7893ff48bf5SDavid du Colombier 	 * a device behind these addresses so if Mode1 accesses fail try
7903ff48bf5SDavid du Colombier 	 * for Mode2 (Mode2 is deprecated).
7917dd7cddfSDavid du Colombier 	 */
792becaf2abSDavid du Colombier 	if(!pcibios){
793becaf2abSDavid du Colombier 		/*
794becaf2abSDavid du Colombier 		 * Bits [30:24] of PciADDR must be 0,
795becaf2abSDavid du Colombier 		 * according to the spec.
796becaf2abSDavid du Colombier 		 */
797becaf2abSDavid du Colombier 		n = inl(PciADDR);
798becaf2abSDavid du Colombier 		if(!(n & 0x7FF00000)){
799becaf2abSDavid du Colombier 			outl(PciADDR, 0x80000000);
800becaf2abSDavid du Colombier 			outb(PciADDR+3, 0);
801becaf2abSDavid du Colombier 			if(inl(PciADDR) & 0x80000000){
8027dd7cddfSDavid du Colombier 				pcicfgmode = 1;
8037dd7cddfSDavid du Colombier 				pcimaxdno = 31;
8047dd7cddfSDavid du Colombier 			}
805becaf2abSDavid du Colombier 		}
806becaf2abSDavid du Colombier 		outl(PciADDR, n);
807becaf2abSDavid du Colombier 
808becaf2abSDavid du Colombier 		if(pcicfgmode < 0){
809becaf2abSDavid du Colombier 			/*
810becaf2abSDavid du Colombier 			 * The 'key' part of PciCSE should be 0.
811becaf2abSDavid du Colombier 			 */
812becaf2abSDavid du Colombier 			n = inb(PciCSE);
813becaf2abSDavid du Colombier 			if(!(n & 0xF0)){
814becaf2abSDavid du Colombier 				outb(PciCSE, 0x0E);
815becaf2abSDavid du Colombier 				if(inb(PciCSE) == 0x0E){
8163ff48bf5SDavid du Colombier 					pcicfgmode = 2;
8173ff48bf5SDavid du Colombier 					pcimaxdno = 15;
8183ff48bf5SDavid du Colombier 				}
8197dd7cddfSDavid du Colombier 			}
820becaf2abSDavid du Colombier 			outb(PciCSE, n);
821becaf2abSDavid du Colombier 		}
822becaf2abSDavid du Colombier 	}
8237dd7cddfSDavid du Colombier 
8247dd7cddfSDavid du Colombier 	if(pcicfgmode < 0)
8257dd7cddfSDavid du Colombier 		goto out;
8267dd7cddfSDavid du Colombier 
8279a747e4fSDavid du Colombier 	fmtinstall('T', tbdffmt);
8287dd7cddfSDavid du Colombier 
829becaf2abSDavid du Colombier 	if(p = getconf("*pcimaxbno")){
830becaf2abSDavid du Colombier 		n = strtoul(p, 0, 0);
831becaf2abSDavid du Colombier 		if(n < pcimaxbno)
832becaf2abSDavid du Colombier 			pcimaxbno = n;
833becaf2abSDavid du Colombier 	}
834becaf2abSDavid du Colombier 	if(p = getconf("*pcimaxdno")){
835becaf2abSDavid du Colombier 		n = strtoul(p, 0, 0);
836becaf2abSDavid du Colombier 		if(n < pcimaxdno)
837becaf2abSDavid du Colombier 			pcimaxdno = n;
838becaf2abSDavid du Colombier 	}
8397dd7cddfSDavid du Colombier 
8407dd7cddfSDavid du Colombier 	list = &pciroot;
841223a736eSDavid du Colombier 	for(bno = 0; bno <= pcimaxbno; bno++) {
8429a747e4fSDavid du Colombier 		int sbno = bno;
8439a747e4fSDavid du Colombier 		bno = pcilscan(bno, list);
8449a747e4fSDavid du Colombier 
8457dd7cddfSDavid du Colombier 		while(*list)
8467dd7cddfSDavid du Colombier 			list = &(*list)->link;
8479a747e4fSDavid du Colombier 
8489a747e4fSDavid du Colombier 		if (sbno == 0) {
8499a747e4fSDavid du Colombier 			Pcidev *pci;
8509a747e4fSDavid du Colombier 
8519a747e4fSDavid du Colombier 			/*
8529a747e4fSDavid du Colombier 			  * If we have found a PCI-to-Cardbus bridge, make sure
8539a747e4fSDavid du Colombier 			  * it has no valid mappings anymore.
8549a747e4fSDavid du Colombier 			  */
8559a747e4fSDavid du Colombier 			pci = pciroot;
8569a747e4fSDavid du Colombier 			while (pci) {
8579a747e4fSDavid du Colombier 				if (pci->ccrb == 6 && pci->ccru == 7) {
8589a747e4fSDavid du Colombier 					ushort bcr;
8599a747e4fSDavid du Colombier 
8609a747e4fSDavid du Colombier 					/* reset the cardbus */
8619a747e4fSDavid du Colombier 					bcr = pcicfgr16(pci, PciBCR);
8629a747e4fSDavid du Colombier 					pcicfgw16(pci, PciBCR, 0x40 | bcr);
8639a747e4fSDavid du Colombier 					delay(50);
8649a747e4fSDavid du Colombier 				}
8659a747e4fSDavid du Colombier 				pci = pci->link;
8669a747e4fSDavid du Colombier 			}
8679a747e4fSDavid du Colombier 		}
8687dd7cddfSDavid du Colombier 	}
8697dd7cddfSDavid du Colombier 
8707dd7cddfSDavid du Colombier 	if(pciroot == nil)
8717dd7cddfSDavid du Colombier 		goto out;
8727dd7cddfSDavid du Colombier 
87380ee5cbfSDavid du Colombier 	if(nobios) {
8747dd7cddfSDavid du Colombier 		/*
8757dd7cddfSDavid du Colombier 		 * Work out how big the top bus is
8767dd7cddfSDavid du Colombier 		 */
8777dd7cddfSDavid du Colombier 		mema = 0;
8787dd7cddfSDavid du Colombier 		ioa = 0;
8797dd7cddfSDavid du Colombier 		pcibusmap(pciroot, &mema, &ioa, 0);
8807dd7cddfSDavid du Colombier 
8817dd7cddfSDavid du Colombier 		DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
8827dd7cddfSDavid du Colombier 			mema, pcimask(mema), ioa);
8837dd7cddfSDavid du Colombier 
8847dd7cddfSDavid du Colombier 		/*
8857dd7cddfSDavid du Colombier 		 * Align the windows and map it
8867dd7cddfSDavid du Colombier 		 */
8877dd7cddfSDavid du Colombier 		ioa = 0x1000;
8887dd7cddfSDavid du Colombier 		mema = 0x90000000;
8897dd7cddfSDavid du Colombier 
8907dd7cddfSDavid du Colombier 		pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
8917dd7cddfSDavid du Colombier 
8927dd7cddfSDavid du Colombier 		pcibusmap(pciroot, &mema, &ioa, 1);
8937dd7cddfSDavid du Colombier 		DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
8947dd7cddfSDavid du Colombier 
8957dd7cddfSDavid du Colombier 		unlock(&pcicfginitlock);
8967dd7cddfSDavid du Colombier 		return;
8977dd7cddfSDavid du Colombier 	}
8989a747e4fSDavid du Colombier 
8993ff48bf5SDavid du Colombier 	if (!nopcirouting)
9009a747e4fSDavid du Colombier 		pcirouting();
9019a747e4fSDavid du Colombier 
9027dd7cddfSDavid du Colombier out:
903*4de34a7eSDavid du Colombier 	pcireservemem();
9047dd7cddfSDavid du Colombier 	unlock(&pcicfginitlock);
9059a747e4fSDavid du Colombier 
9063ff48bf5SDavid du Colombier 	if(getconf("*pcihinv"))
9073ff48bf5SDavid du Colombier 		pcihinv(nil);
9087dd7cddfSDavid du Colombier }
9097dd7cddfSDavid du Colombier 
910*4de34a7eSDavid du Colombier static void
911*4de34a7eSDavid du Colombier pcireservemem(void)
912*4de34a7eSDavid du Colombier {
913*4de34a7eSDavid du Colombier 	int i;
914*4de34a7eSDavid du Colombier 	Pcidev *p;
915*4de34a7eSDavid du Colombier 
916*4de34a7eSDavid du Colombier 	/*
917*4de34a7eSDavid du Colombier 	 * mark all the physical address space claimed by pci devices
918*4de34a7eSDavid du Colombier 	 * as in use, so that upaalloc doesn't give it out.
919*4de34a7eSDavid du Colombier 	 */
920*4de34a7eSDavid du Colombier 	for(p=pciroot; p; p=p->list)
921*4de34a7eSDavid du Colombier 		for(i=0; i<nelem(p->mem); i++)
922*4de34a7eSDavid du Colombier 			if(p->mem[i].bar && (p->mem[i].bar&1) == 0)
923*4de34a7eSDavid du Colombier 				upareserve(p->mem[i].bar&~0x0F, p->mem[i].size);
924*4de34a7eSDavid du Colombier }
925*4de34a7eSDavid du Colombier 
9267dd7cddfSDavid du Colombier static int
9277dd7cddfSDavid du Colombier pcicfgrw8(int tbdf, int rno, int data, int read)
9287dd7cddfSDavid du Colombier {
9297dd7cddfSDavid du Colombier 	int o, type, x;
9307dd7cddfSDavid du Colombier 
9317dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
9327dd7cddfSDavid du Colombier 		pcicfginit();
9337dd7cddfSDavid du Colombier 
9347dd7cddfSDavid du Colombier 	if(BUSBNO(tbdf))
9357dd7cddfSDavid du Colombier 		type = 0x01;
9367dd7cddfSDavid du Colombier 	else
9377dd7cddfSDavid du Colombier 		type = 0x00;
9387dd7cddfSDavid du Colombier 	x = -1;
9397dd7cddfSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
9407dd7cddfSDavid du Colombier 		return x;
9417dd7cddfSDavid du Colombier 
9427dd7cddfSDavid du Colombier 	lock(&pcicfglock);
9437dd7cddfSDavid du Colombier 	switch(pcicfgmode){
9447dd7cddfSDavid du Colombier 
9457dd7cddfSDavid du Colombier 	case 1:
9467dd7cddfSDavid du Colombier 		o = rno & 0x03;
9477dd7cddfSDavid du Colombier 		rno &= ~0x03;
9487dd7cddfSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
9497dd7cddfSDavid du Colombier 		if(read)
9507dd7cddfSDavid du Colombier 			x = inb(PciDATA+o);
9517dd7cddfSDavid du Colombier 		else
9527dd7cddfSDavid du Colombier 			outb(PciDATA+o, data);
9537dd7cddfSDavid du Colombier 		outl(PciADDR, 0);
9547dd7cddfSDavid du Colombier 		break;
9557dd7cddfSDavid du Colombier 
9567dd7cddfSDavid du Colombier 	case 2:
9577dd7cddfSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
9587dd7cddfSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
9597dd7cddfSDavid du Colombier 		if(read)
9607dd7cddfSDavid du Colombier 			x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
9617dd7cddfSDavid du Colombier 		else
9627dd7cddfSDavid du Colombier 			outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
9637dd7cddfSDavid du Colombier 		outb(PciCSE, 0);
9647dd7cddfSDavid du Colombier 		break;
9657dd7cddfSDavid du Colombier 	}
9667dd7cddfSDavid du Colombier 	unlock(&pcicfglock);
9677dd7cddfSDavid du Colombier 
9687dd7cddfSDavid du Colombier 	return x;
9697dd7cddfSDavid du Colombier }
9707dd7cddfSDavid du Colombier 
9717dd7cddfSDavid du Colombier int
9727dd7cddfSDavid du Colombier pcicfgr8(Pcidev* pcidev, int rno)
9737dd7cddfSDavid du Colombier {
9747dd7cddfSDavid du Colombier 	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
9757dd7cddfSDavid du Colombier }
9767dd7cddfSDavid du Colombier 
9777dd7cddfSDavid du Colombier void
9787dd7cddfSDavid du Colombier pcicfgw8(Pcidev* pcidev, int rno, int data)
9797dd7cddfSDavid du Colombier {
9807dd7cddfSDavid du Colombier 	pcicfgrw8(pcidev->tbdf, rno, data, 0);
9817dd7cddfSDavid du Colombier }
9827dd7cddfSDavid du Colombier 
9837dd7cddfSDavid du Colombier static int
9847dd7cddfSDavid du Colombier pcicfgrw16(int tbdf, int rno, int data, int read)
9857dd7cddfSDavid du Colombier {
9867dd7cddfSDavid du Colombier 	int o, type, x;
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
9897dd7cddfSDavid du Colombier 		pcicfginit();
9907dd7cddfSDavid du Colombier 
9917dd7cddfSDavid du Colombier 	if(BUSBNO(tbdf))
9927dd7cddfSDavid du Colombier 		type = 0x01;
9937dd7cddfSDavid du Colombier 	else
9947dd7cddfSDavid du Colombier 		type = 0x00;
9957dd7cddfSDavid du Colombier 	x = -1;
9967dd7cddfSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
9977dd7cddfSDavid du Colombier 		return x;
9987dd7cddfSDavid du Colombier 
9997dd7cddfSDavid du Colombier 	lock(&pcicfglock);
10007dd7cddfSDavid du Colombier 	switch(pcicfgmode){
10017dd7cddfSDavid du Colombier 
10027dd7cddfSDavid du Colombier 	case 1:
10037dd7cddfSDavid du Colombier 		o = rno & 0x02;
10047dd7cddfSDavid du Colombier 		rno &= ~0x03;
10057dd7cddfSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
10067dd7cddfSDavid du Colombier 		if(read)
10077dd7cddfSDavid du Colombier 			x = ins(PciDATA+o);
10087dd7cddfSDavid du Colombier 		else
10097dd7cddfSDavid du Colombier 			outs(PciDATA+o, data);
10107dd7cddfSDavid du Colombier 		outl(PciADDR, 0);
10117dd7cddfSDavid du Colombier 		break;
10127dd7cddfSDavid du Colombier 
10137dd7cddfSDavid du Colombier 	case 2:
10147dd7cddfSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
10157dd7cddfSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
10167dd7cddfSDavid du Colombier 		if(read)
10177dd7cddfSDavid du Colombier 			x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
10187dd7cddfSDavid du Colombier 		else
10197dd7cddfSDavid du Colombier 			outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
10207dd7cddfSDavid du Colombier 		outb(PciCSE, 0);
10217dd7cddfSDavid du Colombier 		break;
10227dd7cddfSDavid du Colombier 	}
10237dd7cddfSDavid du Colombier 	unlock(&pcicfglock);
10247dd7cddfSDavid du Colombier 
10257dd7cddfSDavid du Colombier 	return x;
10267dd7cddfSDavid du Colombier }
10277dd7cddfSDavid du Colombier 
10287dd7cddfSDavid du Colombier int
10297dd7cddfSDavid du Colombier pcicfgr16(Pcidev* pcidev, int rno)
10307dd7cddfSDavid du Colombier {
10317dd7cddfSDavid du Colombier 	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
10327dd7cddfSDavid du Colombier }
10337dd7cddfSDavid du Colombier 
10347dd7cddfSDavid du Colombier void
10357dd7cddfSDavid du Colombier pcicfgw16(Pcidev* pcidev, int rno, int data)
10367dd7cddfSDavid du Colombier {
10377dd7cddfSDavid du Colombier 	pcicfgrw16(pcidev->tbdf, rno, data, 0);
10387dd7cddfSDavid du Colombier }
10397dd7cddfSDavid du Colombier 
10407dd7cddfSDavid du Colombier static int
10417dd7cddfSDavid du Colombier pcicfgrw32(int tbdf, int rno, int data, int read)
10427dd7cddfSDavid du Colombier {
10437dd7cddfSDavid du Colombier 	int type, x;
10447dd7cddfSDavid du Colombier 
10457dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
10467dd7cddfSDavid du Colombier 		pcicfginit();
10477dd7cddfSDavid du Colombier 
10487dd7cddfSDavid du Colombier 	if(BUSBNO(tbdf))
10497dd7cddfSDavid du Colombier 		type = 0x01;
10507dd7cddfSDavid du Colombier 	else
10517dd7cddfSDavid du Colombier 		type = 0x00;
10527dd7cddfSDavid du Colombier 	x = -1;
10537dd7cddfSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
10547dd7cddfSDavid du Colombier 		return x;
10557dd7cddfSDavid du Colombier 
10567dd7cddfSDavid du Colombier 	lock(&pcicfglock);
10577dd7cddfSDavid du Colombier 	switch(pcicfgmode){
10587dd7cddfSDavid du Colombier 
10597dd7cddfSDavid du Colombier 	case 1:
10607dd7cddfSDavid du Colombier 		rno &= ~0x03;
10617dd7cddfSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
10627dd7cddfSDavid du Colombier 		if(read)
10637dd7cddfSDavid du Colombier 			x = inl(PciDATA);
10647dd7cddfSDavid du Colombier 		else
10657dd7cddfSDavid du Colombier 			outl(PciDATA, data);
10667dd7cddfSDavid du Colombier 		outl(PciADDR, 0);
10677dd7cddfSDavid du Colombier 		break;
10687dd7cddfSDavid du Colombier 
10697dd7cddfSDavid du Colombier 	case 2:
10707dd7cddfSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
10717dd7cddfSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
10727dd7cddfSDavid du Colombier 		if(read)
10737dd7cddfSDavid du Colombier 			x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
10747dd7cddfSDavid du Colombier 		else
10757dd7cddfSDavid du Colombier 			outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
10767dd7cddfSDavid du Colombier 		outb(PciCSE, 0);
10777dd7cddfSDavid du Colombier 		break;
10787dd7cddfSDavid du Colombier 	}
10797dd7cddfSDavid du Colombier 	unlock(&pcicfglock);
10807dd7cddfSDavid du Colombier 
10817dd7cddfSDavid du Colombier 	return x;
10827dd7cddfSDavid du Colombier }
10837dd7cddfSDavid du Colombier 
10847dd7cddfSDavid du Colombier int
10857dd7cddfSDavid du Colombier pcicfgr32(Pcidev* pcidev, int rno)
10867dd7cddfSDavid du Colombier {
10877dd7cddfSDavid du Colombier 	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
10887dd7cddfSDavid du Colombier }
10897dd7cddfSDavid du Colombier 
10907dd7cddfSDavid du Colombier void
10917dd7cddfSDavid du Colombier pcicfgw32(Pcidev* pcidev, int rno, int data)
10927dd7cddfSDavid du Colombier {
10937dd7cddfSDavid du Colombier 	pcicfgrw32(pcidev->tbdf, rno, data, 0);
10947dd7cddfSDavid du Colombier }
10957dd7cddfSDavid du Colombier 
10967dd7cddfSDavid du Colombier Pcidev*
10977dd7cddfSDavid du Colombier pcimatch(Pcidev* prev, int vid, int did)
10987dd7cddfSDavid du Colombier {
10997dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
11007dd7cddfSDavid du Colombier 		pcicfginit();
11017dd7cddfSDavid du Colombier 
11027dd7cddfSDavid du Colombier 	if(prev == nil)
11037dd7cddfSDavid du Colombier 		prev = pcilist;
11047dd7cddfSDavid du Colombier 	else
11057dd7cddfSDavid du Colombier 		prev = prev->list;
11067dd7cddfSDavid du Colombier 
11077dd7cddfSDavid du Colombier 	while(prev != nil){
11087dd7cddfSDavid du Colombier 		if((vid == 0 || prev->vid == vid)
11097dd7cddfSDavid du Colombier 		&& (did == 0 || prev->did == did))
11107dd7cddfSDavid du Colombier 			break;
11117dd7cddfSDavid du Colombier 		prev = prev->list;
11127dd7cddfSDavid du Colombier 	}
11137dd7cddfSDavid du Colombier 	return prev;
11147dd7cddfSDavid du Colombier }
11157dd7cddfSDavid du Colombier 
11167dd7cddfSDavid du Colombier Pcidev*
11177dd7cddfSDavid du Colombier pcimatchtbdf(int tbdf)
11187dd7cddfSDavid du Colombier {
11197dd7cddfSDavid du Colombier 	Pcidev *pcidev;
11207dd7cddfSDavid du Colombier 
11217dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
11227dd7cddfSDavid du Colombier 		pcicfginit();
11237dd7cddfSDavid du Colombier 
11247dd7cddfSDavid du Colombier 	for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
11257dd7cddfSDavid du Colombier 		if(pcidev->tbdf == tbdf)
11267dd7cddfSDavid du Colombier 			break;
11277dd7cddfSDavid du Colombier 	}
11287dd7cddfSDavid du Colombier 	return pcidev;
11297dd7cddfSDavid du Colombier }
11307dd7cddfSDavid du Colombier 
11319a747e4fSDavid du Colombier uchar
11329a747e4fSDavid du Colombier pciipin(Pcidev *pci, uchar pin)
11339a747e4fSDavid du Colombier {
11349a747e4fSDavid du Colombier 	if (pci == nil)
11359a747e4fSDavid du Colombier 		pci = pcilist;
11369a747e4fSDavid du Colombier 
11379a747e4fSDavid du Colombier 	while (pci) {
11389a747e4fSDavid du Colombier 		uchar intl;
11399a747e4fSDavid du Colombier 
11409a747e4fSDavid du Colombier 		if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
11419a747e4fSDavid du Colombier 			return pci->intl;
11429a747e4fSDavid du Colombier 
11439a747e4fSDavid du Colombier 		if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
11449a747e4fSDavid du Colombier 			return intl;
11459a747e4fSDavid du Colombier 
11469a747e4fSDavid du Colombier 		pci = pci->list;
11479a747e4fSDavid du Colombier 	}
11489a747e4fSDavid du Colombier 	return 0;
11499a747e4fSDavid du Colombier }
11509a747e4fSDavid du Colombier 
11519a747e4fSDavid du Colombier static void
11529a747e4fSDavid du Colombier pcilhinv(Pcidev* p)
11537dd7cddfSDavid du Colombier {
11547dd7cddfSDavid du Colombier 	int i;
11557dd7cddfSDavid du Colombier 	Pcidev *t;
11567dd7cddfSDavid du Colombier 
11577dd7cddfSDavid du Colombier 	if(p == nil) {
11587dd7cddfSDavid du Colombier 		putstrn(PCICONS.output, PCICONS.ptr);
11597dd7cddfSDavid du Colombier 		p = pciroot;
11607dd7cddfSDavid du Colombier 		print("bus dev type vid  did intl memory\n");
11617dd7cddfSDavid du Colombier 	}
11627dd7cddfSDavid du Colombier 	for(t = p; t != nil; t = t->link) {
11637dd7cddfSDavid du Colombier 		print("%d  %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d  ",
11647dd7cddfSDavid du Colombier 			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
11657dd7cddfSDavid du Colombier 			t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
11667dd7cddfSDavid du Colombier 
11677dd7cddfSDavid du Colombier 		for(i = 0; i < nelem(p->mem); i++) {
11687dd7cddfSDavid du Colombier 			if(t->mem[i].size == 0)
11697dd7cddfSDavid du Colombier 				continue;
11707dd7cddfSDavid du Colombier 			print("%d:%.8lux %d ", i,
11717dd7cddfSDavid du Colombier 				t->mem[i].bar, t->mem[i].size);
11727dd7cddfSDavid du Colombier 		}
11737dd7cddfSDavid du Colombier 		if(t->ioa.bar || t->ioa.size)
11747dd7cddfSDavid du Colombier 			print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
11757dd7cddfSDavid du Colombier 		if(t->mema.bar || t->mema.size)
11767dd7cddfSDavid du Colombier 			print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
11777dd7cddfSDavid du Colombier 		if(t->bridge)
11787dd7cddfSDavid du Colombier 			print("->%d", BUSBNO(t->bridge->tbdf));
11797dd7cddfSDavid du Colombier 		print("\n");
11807dd7cddfSDavid du Colombier 	}
11817dd7cddfSDavid du Colombier 	while(p != nil) {
11827dd7cddfSDavid du Colombier 		if(p->bridge != nil)
11839a747e4fSDavid du Colombier 			pcilhinv(p->bridge);
11847dd7cddfSDavid du Colombier 		p = p->link;
11857dd7cddfSDavid du Colombier 	}
11867dd7cddfSDavid du Colombier }
11877dd7cddfSDavid du Colombier 
11887dd7cddfSDavid du Colombier void
11899a747e4fSDavid du Colombier pcihinv(Pcidev* p)
11909a747e4fSDavid du Colombier {
11919a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
11929a747e4fSDavid du Colombier 		pcicfginit();
11939a747e4fSDavid du Colombier 	lock(&pcicfginitlock);
11949a747e4fSDavid du Colombier 	pcilhinv(p);
11959a747e4fSDavid du Colombier 	unlock(&pcicfginitlock);
11969a747e4fSDavid du Colombier }
11979a747e4fSDavid du Colombier 
11989a747e4fSDavid du Colombier void
11997dd7cddfSDavid du Colombier pcireset(void)
12007dd7cddfSDavid du Colombier {
12017dd7cddfSDavid du Colombier 	Pcidev *p;
12027dd7cddfSDavid du Colombier 
12037dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
12047dd7cddfSDavid du Colombier 		pcicfginit();
12057dd7cddfSDavid du Colombier 
12067dd7cddfSDavid du Colombier 	for(p = pcilist; p != nil; p = p->list) {
12079a747e4fSDavid du Colombier 		/* don't mess with the bridges */
12089a747e4fSDavid du Colombier 		if(p->ccrb == 0x06)
12099a747e4fSDavid du Colombier 			continue;
12109a747e4fSDavid du Colombier 		pciclrbme(p);
12117dd7cddfSDavid du Colombier 	}
12127dd7cddfSDavid du Colombier }
12137dd7cddfSDavid du Colombier 
12147dd7cddfSDavid du Colombier void
1215220e960cSDavid du Colombier pcisetioe(Pcidev* p)
1216220e960cSDavid du Colombier {
1217e569ccb5SDavid du Colombier 	p->pcr |= IOen;
1218e569ccb5SDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1219220e960cSDavid du Colombier }
1220220e960cSDavid du Colombier 
1221220e960cSDavid du Colombier void
1222220e960cSDavid du Colombier pciclrioe(Pcidev* p)
1223220e960cSDavid du Colombier {
1224e569ccb5SDavid du Colombier 	p->pcr &= ~IOen;
1225e569ccb5SDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1226220e960cSDavid du Colombier }
1227220e960cSDavid du Colombier 
1228220e960cSDavid du Colombier void
12297dd7cddfSDavid du Colombier pcisetbme(Pcidev* p)
12307dd7cddfSDavid du Colombier {
1231e569ccb5SDavid du Colombier 	p->pcr |= MASen;
1232e569ccb5SDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
12337dd7cddfSDavid du Colombier }
12349a747e4fSDavid du Colombier 
12359a747e4fSDavid du Colombier void
12369a747e4fSDavid du Colombier pciclrbme(Pcidev* p)
12379a747e4fSDavid du Colombier {
1238e569ccb5SDavid du Colombier 	p->pcr &= ~MASen;
1239e569ccb5SDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1240e569ccb5SDavid du Colombier }
12419a747e4fSDavid du Colombier 
1242d8f3f65eSDavid du Colombier void
1243d8f3f65eSDavid du Colombier pcisetmwi(Pcidev* p)
1244d8f3f65eSDavid du Colombier {
1245d8f3f65eSDavid du Colombier 	p->pcr |= MemWrInv;
1246d8f3f65eSDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1247d8f3f65eSDavid du Colombier }
1248d8f3f65eSDavid du Colombier 
1249d8f3f65eSDavid du Colombier void
1250d8f3f65eSDavid du Colombier pciclrmwi(Pcidev* p)
1251d8f3f65eSDavid du Colombier {
1252d8f3f65eSDavid du Colombier 	p->pcr &= ~MemWrInv;
1253d8f3f65eSDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1254d8f3f65eSDavid du Colombier }
1255d8f3f65eSDavid du Colombier 
1256e569ccb5SDavid du Colombier static int
1257e569ccb5SDavid du Colombier pcigetpmrb(Pcidev* p)
1258e569ccb5SDavid du Colombier {
1259e569ccb5SDavid du Colombier 	int ptr;
1260e569ccb5SDavid du Colombier 
1261e569ccb5SDavid du Colombier 	if(p->pmrb != 0)
1262e569ccb5SDavid du Colombier 		return p->pmrb;
1263e569ccb5SDavid du Colombier 	p->pmrb = -1;
1264e569ccb5SDavid du Colombier 
1265e569ccb5SDavid du Colombier 	/*
1266e569ccb5SDavid du Colombier 	 * If there are no extended capabilities implemented,
1267e569ccb5SDavid du Colombier 	 * (bit 4 in the status register) assume there's no standard
1268e569ccb5SDavid du Colombier 	 * power management method.
1269e569ccb5SDavid du Colombier 	 * Find the capabilities pointer based on PCI header type.
1270e569ccb5SDavid du Colombier 	 */
1271e569ccb5SDavid du Colombier 	if(!(p->pcr & 0x0010))
1272e569ccb5SDavid du Colombier 		return -1;
1273e569ccb5SDavid du Colombier 	switch(pcicfgr8(p, PciHDT)){
1274e569ccb5SDavid du Colombier 	default:
1275e569ccb5SDavid du Colombier 		return -1;
1276e569ccb5SDavid du Colombier 	case 0:					/* all other */
1277e569ccb5SDavid du Colombier 	case 1:					/* PCI to PCI bridge */
1278e569ccb5SDavid du Colombier 		ptr = 0x34;
1279e569ccb5SDavid du Colombier 		break;
1280e569ccb5SDavid du Colombier 	case 2:					/* CardBus bridge */
1281e569ccb5SDavid du Colombier 		ptr = 0x14;
1282e569ccb5SDavid du Colombier 		break;
1283e569ccb5SDavid du Colombier 	}
1284e569ccb5SDavid du Colombier 	ptr = pcicfgr32(p, ptr);
1285e569ccb5SDavid du Colombier 
1286e569ccb5SDavid du Colombier 	while(ptr != 0){
1287e569ccb5SDavid du Colombier 		/*
1288e569ccb5SDavid du Colombier 		 * Check for validity.
1289e569ccb5SDavid du Colombier 		 * Can't be in standard header and must be double
1290e569ccb5SDavid du Colombier 		 * word aligned.
1291e569ccb5SDavid du Colombier 		 */
1292e569ccb5SDavid du Colombier 		if(ptr < 0x40 || (ptr & ~0xFC))
1293e569ccb5SDavid du Colombier 			return -1;
1294e569ccb5SDavid du Colombier 		if(pcicfgr8(p, ptr) == 0x01){
1295e569ccb5SDavid du Colombier 			p->pmrb = ptr;
1296e569ccb5SDavid du Colombier 			return ptr;
1297e569ccb5SDavid du Colombier 		}
1298e569ccb5SDavid du Colombier 
1299e569ccb5SDavid du Colombier 		ptr = pcicfgr8(p, ptr+1);
1300e569ccb5SDavid du Colombier 	}
1301e569ccb5SDavid du Colombier 
1302e569ccb5SDavid du Colombier 	return -1;
1303e569ccb5SDavid du Colombier }
1304e569ccb5SDavid du Colombier 
1305e569ccb5SDavid du Colombier int
1306e569ccb5SDavid du Colombier pcigetpms(Pcidev* p)
1307e569ccb5SDavid du Colombier {
1308e569ccb5SDavid du Colombier 	int pmcsr, ptr;
1309e569ccb5SDavid du Colombier 
1310e569ccb5SDavid du Colombier 	if((ptr = pcigetpmrb(p)) == -1)
1311e569ccb5SDavid du Colombier 		return -1;
1312e569ccb5SDavid du Colombier 
1313e569ccb5SDavid du Colombier 	/*
1314e569ccb5SDavid du Colombier 	 * Power Management Register Block:
1315e569ccb5SDavid du Colombier 	 *  offset 0:	Capability ID
1316e569ccb5SDavid du Colombier 	 *	   1:	next item pointer
1317e569ccb5SDavid du Colombier 	 *	   2:	capabilities
1318e569ccb5SDavid du Colombier 	 *	   4:	control/status
1319e569ccb5SDavid du Colombier 	 *	   6:	bridge support extensions
1320e569ccb5SDavid du Colombier 	 *	   7:	data
1321e569ccb5SDavid du Colombier 	 */
1322e569ccb5SDavid du Colombier 	pmcsr = pcicfgr16(p, ptr+4);
1323e569ccb5SDavid du Colombier 
1324e569ccb5SDavid du Colombier 	return pmcsr & 0x0003;
1325e569ccb5SDavid du Colombier }
1326e569ccb5SDavid du Colombier 
1327e569ccb5SDavid du Colombier int
1328e569ccb5SDavid du Colombier pcisetpms(Pcidev* p, int state)
1329e569ccb5SDavid du Colombier {
1330e569ccb5SDavid du Colombier 	int ostate, pmc, pmcsr, ptr;
1331e569ccb5SDavid du Colombier 
1332e569ccb5SDavid du Colombier 	if((ptr = pcigetpmrb(p)) == -1)
1333e569ccb5SDavid du Colombier 		return -1;
1334e569ccb5SDavid du Colombier 
1335e569ccb5SDavid du Colombier 	pmc = pcicfgr16(p, ptr+2);
1336e569ccb5SDavid du Colombier 	pmcsr = pcicfgr16(p, ptr+4);
1337e569ccb5SDavid du Colombier 	ostate = pmcsr & 0x0003;
1338e569ccb5SDavid du Colombier 	pmcsr &= ~0x0003;
1339e569ccb5SDavid du Colombier 
1340e569ccb5SDavid du Colombier 	switch(state){
1341e569ccb5SDavid du Colombier 	default:
1342e569ccb5SDavid du Colombier 		return -1;
1343e569ccb5SDavid du Colombier 	case 0:
1344e569ccb5SDavid du Colombier 		break;
1345e569ccb5SDavid du Colombier 	case 1:
1346e569ccb5SDavid du Colombier 		if(!(pmc & 0x0200))
1347e569ccb5SDavid du Colombier 			return -1;
1348e569ccb5SDavid du Colombier 		break;
1349e569ccb5SDavid du Colombier 	case 2:
1350e569ccb5SDavid du Colombier 		if(!(pmc & 0x0400))
1351e569ccb5SDavid du Colombier 			return -1;
1352e569ccb5SDavid du Colombier 		break;
1353e569ccb5SDavid du Colombier 	case 3:
1354e569ccb5SDavid du Colombier 		break;
1355e569ccb5SDavid du Colombier 	}
1356e569ccb5SDavid du Colombier 	pmcsr |= state;
1357e569ccb5SDavid du Colombier 	pcicfgw16(p, ptr+4, pmcsr);
1358e569ccb5SDavid du Colombier 
1359e569ccb5SDavid du Colombier 	return ostate;
13609a747e4fSDavid du Colombier }
1361