xref: /plan9/sys/src/9/pc/pci.c (revision 6e712d4454b3ec3ca788d40bdf261d15d756e39c)
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;
694fafed5dSDavid du Colombier static BIOS32si* pcibiossi;
707dd7cddfSDavid du Colombier 
714fafed5dSDavid du Colombier static int pcicfgrw8raw(int, int, int, int);
724fafed5dSDavid du Colombier static int pcicfgrw16raw(int, int, int, int);
734fafed5dSDavid du Colombier static int pcicfgrw32raw(int, int, int, int);
744fafed5dSDavid du Colombier 
754fafed5dSDavid du Colombier static int (*pcicfgrw8)(int, int, int, int) = pcicfgrw8raw;
764fafed5dSDavid du Colombier static int (*pcicfgrw16)(int, int, int, int) = pcicfgrw16raw;
774fafed5dSDavid du Colombier static int (*pcicfgrw32)(int, int, int, int) = pcicfgrw32raw;
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier static char* bustypes[] = {
807dd7cddfSDavid du Colombier 	"CBUSI",
817dd7cddfSDavid du Colombier 	"CBUSII",
827dd7cddfSDavid du Colombier 	"EISA",
837dd7cddfSDavid du Colombier 	"FUTURE",
847dd7cddfSDavid du Colombier 	"INTERN",
857dd7cddfSDavid du Colombier 	"ISA",
867dd7cddfSDavid du Colombier 	"MBI",
877dd7cddfSDavid du Colombier 	"MBII",
887dd7cddfSDavid du Colombier 	"MCA",
897dd7cddfSDavid du Colombier 	"MPI",
907dd7cddfSDavid du Colombier 	"MPSA",
917dd7cddfSDavid du Colombier 	"NUBUS",
927dd7cddfSDavid du Colombier 	"PCI",
937dd7cddfSDavid du Colombier 	"PCMCIA",
947dd7cddfSDavid du Colombier 	"TC",
957dd7cddfSDavid du Colombier 	"VL",
967dd7cddfSDavid du Colombier 	"VME",
977dd7cddfSDavid du Colombier 	"XPRESS",
987dd7cddfSDavid du Colombier };
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier #pragma	varargck	type	"T"	int
1017dd7cddfSDavid du Colombier 
1027dd7cddfSDavid du Colombier static int
1039a747e4fSDavid du Colombier tbdffmt(Fmt* fmt)
1047dd7cddfSDavid du Colombier {
1057dd7cddfSDavid du Colombier 	char *p;
1069a747e4fSDavid du Colombier 	int l, r, type, tbdf;
1077dd7cddfSDavid du Colombier 
1089a747e4fSDavid du Colombier 	if((p = malloc(READSTR)) == nil)
1099a747e4fSDavid du Colombier 		return fmtstrcpy(fmt, "(tbdfconv)");
1107dd7cddfSDavid du Colombier 
1119a747e4fSDavid du Colombier 	switch(fmt->r){
1127dd7cddfSDavid du Colombier 	case 'T':
1139a747e4fSDavid du Colombier 		tbdf = va_arg(fmt->args, int);
1147dd7cddfSDavid du Colombier 		type = BUSTYPE(tbdf);
1157dd7cddfSDavid du Colombier 		if(type < nelem(bustypes))
1167dd7cddfSDavid du Colombier 			l = snprint(p, READSTR, bustypes[type]);
1177dd7cddfSDavid du Colombier 		else
1187dd7cddfSDavid du Colombier 			l = snprint(p, READSTR, "%d", type);
1197dd7cddfSDavid du Colombier 		snprint(p+l, READSTR-l, ".%d.%d.%d",
1207dd7cddfSDavid du Colombier 			BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
1217dd7cddfSDavid du Colombier 		break;
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier 	default:
1247dd7cddfSDavid du Colombier 		snprint(p, READSTR, "(tbdfconv)");
1257dd7cddfSDavid du Colombier 		break;
1267dd7cddfSDavid du Colombier 	}
1279a747e4fSDavid du Colombier 	r = fmtstrcpy(fmt, p);
1287dd7cddfSDavid du Colombier 	free(p);
1297dd7cddfSDavid du Colombier 
1309a747e4fSDavid du Colombier 	return r;
1317dd7cddfSDavid du Colombier }
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier ulong
1347dd7cddfSDavid du Colombier pcibarsize(Pcidev *p, int rno)
1357dd7cddfSDavid du Colombier {
1367dd7cddfSDavid du Colombier 	ulong v, size;
1377dd7cddfSDavid du Colombier 
1387dd7cddfSDavid du Colombier 	v = pcicfgrw32(p->tbdf, rno, 0, 1);
1397dd7cddfSDavid du Colombier 	pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
1407dd7cddfSDavid du Colombier 	size = pcicfgrw32(p->tbdf, rno, 0, 1);
1417dd7cddfSDavid du Colombier 	if(v & 1)
1427dd7cddfSDavid du Colombier 		size |= 0xFFFF0000;
1437dd7cddfSDavid du Colombier 	pcicfgrw32(p->tbdf, rno, v, 0);
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier 	return -(size & ~0x0F);
1467dd7cddfSDavid du Colombier }
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier static int
1497dd7cddfSDavid du Colombier pcisizcmp(void *a, void *b)
1507dd7cddfSDavid du Colombier {
1517dd7cddfSDavid du Colombier 	Pcisiz *aa, *bb;
1527dd7cddfSDavid du Colombier 
1537dd7cddfSDavid du Colombier 	aa = a;
1547dd7cddfSDavid du Colombier 	bb = b;
1557dd7cddfSDavid du Colombier 	return aa->siz - bb->siz;
1567dd7cddfSDavid du Colombier }
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier static ulong
1597dd7cddfSDavid du Colombier pcimask(ulong v)
1607dd7cddfSDavid du Colombier {
1617dd7cddfSDavid du Colombier 	ulong m;
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 	m = BI2BY*sizeof(v);
1647dd7cddfSDavid du Colombier 	for(m = 1<<(m-1); m != 0; m >>= 1) {
1657dd7cddfSDavid du Colombier 		if(m & v)
1667dd7cddfSDavid du Colombier 			break;
1677dd7cddfSDavid du Colombier 	}
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier 	m--;
1707dd7cddfSDavid du Colombier 	if((v & m) == 0)
1717dd7cddfSDavid du Colombier 		return v;
1727dd7cddfSDavid du Colombier 
1737dd7cddfSDavid du Colombier 	v |= m;
1747dd7cddfSDavid du Colombier 	return v+1;
1757dd7cddfSDavid du Colombier }
1767dd7cddfSDavid du Colombier 
1777dd7cddfSDavid du Colombier static void
1787dd7cddfSDavid du Colombier pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
1797dd7cddfSDavid du Colombier {
1807dd7cddfSDavid du Colombier 	Pcidev *p;
1817dd7cddfSDavid du Colombier 	int ntb, i, size, rno, hole;
1827dd7cddfSDavid du Colombier 	ulong v, mema, ioa, sioa, smema, base, limit;
1837dd7cddfSDavid du Colombier 	Pcisiz *table, *tptr, *mtb, *itb;
1847dd7cddfSDavid du Colombier 
1854fafed5dSDavid du Colombier 	if(!nobios)
1864fafed5dSDavid du Colombier 		return;
1874fafed5dSDavid du Colombier 
1887dd7cddfSDavid du Colombier 	ioa = *pioa;
1897dd7cddfSDavid du Colombier 	mema = *pmema;
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier 	DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n",
1927dd7cddfSDavid du Colombier 		wrreg, root->tbdf, mema, ioa);
1937dd7cddfSDavid du Colombier 
1947dd7cddfSDavid du Colombier 	ntb = 0;
1957dd7cddfSDavid du Colombier 	for(p = root; p != nil; p = p->link)
1967dd7cddfSDavid du Colombier 		ntb++;
1977dd7cddfSDavid du Colombier 
1987dd7cddfSDavid du Colombier 	ntb *= (PciCIS-PciBAR0)/4;
1997dd7cddfSDavid du Colombier 	table = malloc(2*ntb*sizeof(Pcisiz));
2007dd7cddfSDavid du Colombier 	itb = table;
2017dd7cddfSDavid du Colombier 	mtb = table+ntb;
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier 	/*
2047dd7cddfSDavid du Colombier 	 * Build a table of sizes
2057dd7cddfSDavid du Colombier 	 */
2067dd7cddfSDavid du Colombier 	for(p = root; p != nil; p = p->link) {
2077dd7cddfSDavid du Colombier 		if(p->ccrb == 0x06) {
2087dd7cddfSDavid du Colombier 			if(p->ccru != 0x04 || p->bridge == nil) {
2097dd7cddfSDavid du Colombier //				DBG("pci: ignored bridge %T\n", p->tbdf);
2107dd7cddfSDavid du Colombier 				continue;
2117dd7cddfSDavid du Colombier 			}
2127dd7cddfSDavid du Colombier 
2137dd7cddfSDavid du Colombier 			sioa = ioa;
2147dd7cddfSDavid du Colombier 			smema = mema;
2157dd7cddfSDavid du Colombier 			pcibusmap(p->bridge, &smema, &sioa, 0);
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier 			hole = pcimask(smema-mema);
2187dd7cddfSDavid du Colombier 			if(hole < (1<<20))
2197dd7cddfSDavid du Colombier 				hole = 1<<20;
2207dd7cddfSDavid du Colombier 			p->mema.size = hole;
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier 			hole = pcimask(sioa-ioa);
2237dd7cddfSDavid du Colombier 			if(hole < (1<<12))
2247dd7cddfSDavid du Colombier 				hole = 1<<12;
2257dd7cddfSDavid du Colombier 
2267dd7cddfSDavid du Colombier 			p->ioa.size = hole;
2277dd7cddfSDavid du Colombier 
2287dd7cddfSDavid du Colombier 			itb->dev = p;
2297dd7cddfSDavid du Colombier 			itb->bar = -1;
2307dd7cddfSDavid du Colombier 			itb->siz = p->ioa.size;
2317dd7cddfSDavid du Colombier 			itb++;
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier 			mtb->dev = p;
2347dd7cddfSDavid du Colombier 			mtb->bar = -1;
2357dd7cddfSDavid du Colombier 			mtb->siz = p->mema.size;
2367dd7cddfSDavid du Colombier 			mtb++;
2377dd7cddfSDavid du Colombier 			continue;
2387dd7cddfSDavid du Colombier 		}
2397dd7cddfSDavid du Colombier 
2407dd7cddfSDavid du Colombier 		for(i = 0; i <= 5; i++) {
2417dd7cddfSDavid du Colombier 			rno = PciBAR0 + i*4;
2427dd7cddfSDavid du Colombier 			v = pcicfgrw32(p->tbdf, rno, 0, 1);
2437dd7cddfSDavid du Colombier 			size = pcibarsize(p, rno);
2447dd7cddfSDavid du Colombier 			if(size == 0)
2457dd7cddfSDavid du Colombier 				continue;
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier 			if(v & 1) {
2487dd7cddfSDavid du Colombier 				itb->dev = p;
2497dd7cddfSDavid du Colombier 				itb->bar = i;
2507dd7cddfSDavid du Colombier 				itb->siz = size;
2517dd7cddfSDavid du Colombier 				itb++;
2527dd7cddfSDavid du Colombier 			}
2537dd7cddfSDavid du Colombier 			else {
2547dd7cddfSDavid du Colombier 				mtb->dev = p;
2557dd7cddfSDavid du Colombier 				mtb->bar = i;
2567dd7cddfSDavid du Colombier 				mtb->siz = size;
2577dd7cddfSDavid du Colombier 				mtb++;
2587dd7cddfSDavid du Colombier 			}
2597dd7cddfSDavid du Colombier 
2607dd7cddfSDavid du Colombier 			p->mem[i].size = size;
2617dd7cddfSDavid du Colombier 		}
2627dd7cddfSDavid du Colombier 	}
2637dd7cddfSDavid du Colombier 
2647dd7cddfSDavid du Colombier 	/*
2657dd7cddfSDavid du Colombier 	 * Sort both tables IO smallest first, Memory largest
2667dd7cddfSDavid du Colombier 	 */
2677dd7cddfSDavid du Colombier 	qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
2687dd7cddfSDavid du Colombier 	tptr = table+ntb;
2697dd7cddfSDavid du Colombier 	qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 	/*
2727dd7cddfSDavid du Colombier 	 * Allocate IO address space on this bus
2737dd7cddfSDavid du Colombier 	 */
2747dd7cddfSDavid du Colombier 	for(tptr = table; tptr < itb; tptr++) {
2757dd7cddfSDavid du Colombier 		hole = tptr->siz;
2767dd7cddfSDavid du Colombier 		if(tptr->bar == -1)
2777dd7cddfSDavid du Colombier 			hole = 1<<12;
2787dd7cddfSDavid du Colombier 		ioa = (ioa+hole-1) & ~(hole-1);
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier 		p = tptr->dev;
2817dd7cddfSDavid du Colombier 		if(tptr->bar == -1)
2827dd7cddfSDavid du Colombier 			p->ioa.bar = ioa;
2837dd7cddfSDavid du Colombier 		else {
2847dd7cddfSDavid du Colombier 			p->pcr |= IOen;
2857dd7cddfSDavid du Colombier 			p->mem[tptr->bar].bar = ioa|1;
2867dd7cddfSDavid du Colombier 			if(wrreg)
2877dd7cddfSDavid du Colombier 				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
2887dd7cddfSDavid du Colombier 		}
2897dd7cddfSDavid du Colombier 
2907dd7cddfSDavid du Colombier 		ioa += tptr->siz;
2917dd7cddfSDavid du Colombier 	}
2927dd7cddfSDavid du Colombier 
2937dd7cddfSDavid du Colombier 	/*
2947dd7cddfSDavid du Colombier 	 * Allocate Memory address space on this bus
2957dd7cddfSDavid du Colombier 	 */
2967dd7cddfSDavid du Colombier 	for(tptr = table+ntb; tptr < mtb; tptr++) {
2977dd7cddfSDavid du Colombier 		hole = tptr->siz;
2987dd7cddfSDavid du Colombier 		if(tptr->bar == -1)
2997dd7cddfSDavid du Colombier 			hole = 1<<20;
3007dd7cddfSDavid du Colombier 		mema = (mema+hole-1) & ~(hole-1);
3017dd7cddfSDavid du Colombier 
3027dd7cddfSDavid du Colombier 		p = tptr->dev;
3037dd7cddfSDavid du Colombier 		if(tptr->bar == -1)
3047dd7cddfSDavid du Colombier 			p->mema.bar = mema;
3057dd7cddfSDavid du Colombier 		else {
3067dd7cddfSDavid du Colombier 			p->pcr |= MEMen;
3077dd7cddfSDavid du Colombier 			p->mem[tptr->bar].bar = mema;
3087dd7cddfSDavid du Colombier 			if(wrreg)
3097dd7cddfSDavid du Colombier 				pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
3107dd7cddfSDavid du Colombier 		}
3117dd7cddfSDavid du Colombier 		mema += tptr->siz;
3127dd7cddfSDavid du Colombier 	}
3137dd7cddfSDavid du Colombier 
3147dd7cddfSDavid du Colombier 	*pmema = mema;
3157dd7cddfSDavid du Colombier 	*pioa = ioa;
3167dd7cddfSDavid du Colombier 	free(table);
3177dd7cddfSDavid du Colombier 
3187dd7cddfSDavid du Colombier 	if(wrreg == 0)
3197dd7cddfSDavid du Colombier 		return;
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier 	/*
3227dd7cddfSDavid du Colombier 	 * Finally set all the bridge addresses & registers
3237dd7cddfSDavid du Colombier 	 */
3247dd7cddfSDavid du Colombier 	for(p = root; p != nil; p = p->link) {
3257dd7cddfSDavid du Colombier 		if(p->bridge == nil) {
3267dd7cddfSDavid du Colombier 			pcicfgrw8(p->tbdf, PciLTR, 64, 0);
3277dd7cddfSDavid du Colombier 
3287dd7cddfSDavid du Colombier 			p->pcr |= MASen;
329e569ccb5SDavid du Colombier 			pcicfgrw16(p->tbdf, PciPCR, p->pcr, 0);
3307dd7cddfSDavid du Colombier 			continue;
3317dd7cddfSDavid du Colombier 		}
3327dd7cddfSDavid du Colombier 
3337dd7cddfSDavid du Colombier 		base = p->ioa.bar;
3347dd7cddfSDavid du Colombier 		limit = base+p->ioa.size-1;
335d0f3faacSDavid du Colombier 		v = pcicfgrw32(p->tbdf, PciIBR, 0, 1);
3367dd7cddfSDavid du Colombier 		v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
337d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciIBR, v, 0);
3387dd7cddfSDavid du Colombier 		v = (limit & 0xFFFF0000)|(base>>16);
339d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciIUBR, v, 0);
3407dd7cddfSDavid du Colombier 
3417dd7cddfSDavid du Colombier 		base = p->mema.bar;
3427dd7cddfSDavid du Colombier 		limit = base+p->mema.size-1;
3437dd7cddfSDavid du Colombier 		v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
344d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciMBR, v, 0);
3457dd7cddfSDavid du Colombier 
3467dd7cddfSDavid du Colombier 		/*
3477dd7cddfSDavid du Colombier 		 * Disable memory prefetch
3487dd7cddfSDavid du Colombier 		 */
349d0f3faacSDavid du Colombier 		pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0);
3507dd7cddfSDavid du Colombier 		pcicfgrw8(p->tbdf, PciLTR, 64, 0);
3517dd7cddfSDavid du Colombier 
3527dd7cddfSDavid du Colombier 		/*
3537dd7cddfSDavid du Colombier 		 * Enable the bridge
3547dd7cddfSDavid du Colombier 		 */
355e569ccb5SDavid du Colombier 		p->pcr |= IOen|MEMen|MASen;
356e569ccb5SDavid du Colombier 		pcicfgrw32(p->tbdf, PciPCR, 0xFFFF0000|p->pcr , 0);
3577dd7cddfSDavid du Colombier 
3587dd7cddfSDavid du Colombier 		sioa = p->ioa.bar;
3597dd7cddfSDavid du Colombier 		smema = p->mema.bar;
3607dd7cddfSDavid du Colombier 		pcibusmap(p->bridge, &smema, &sioa, 1);
3617dd7cddfSDavid du Colombier 	}
3627dd7cddfSDavid du Colombier }
3637dd7cddfSDavid du Colombier 
3647dd7cddfSDavid du Colombier static int
3659a747e4fSDavid du Colombier pcilscan(int bno, Pcidev** list)
3667dd7cddfSDavid du Colombier {
3677dd7cddfSDavid du Colombier 	Pcidev *p, *head, *tail;
3687dd7cddfSDavid du Colombier 	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
3697dd7cddfSDavid du Colombier 
3707dd7cddfSDavid du Colombier 	maxubn = bno;
3717dd7cddfSDavid du Colombier 	head = nil;
3727dd7cddfSDavid du Colombier 	tail = nil;
3737dd7cddfSDavid du Colombier 	for(dno = 0; dno <= pcimaxdno; dno++){
3747dd7cddfSDavid du Colombier 		maxfno = 0;
3757dd7cddfSDavid du Colombier 		for(fno = 0; fno <= maxfno; fno++){
3767dd7cddfSDavid du Colombier 			/*
3777dd7cddfSDavid du Colombier 			 * For this possible device, form the
3787dd7cddfSDavid du Colombier 			 * bus+device+function triplet needed to address it
3797dd7cddfSDavid du Colombier 			 * and try to read the vendor and device ID.
3807dd7cddfSDavid du Colombier 			 * If successful, allocate a device struct and
3817dd7cddfSDavid du Colombier 			 * start to fill it in with some useful information
3827dd7cddfSDavid du Colombier 			 * from the device's configuration space.
3837dd7cddfSDavid du Colombier 			 */
3847dd7cddfSDavid du Colombier 			tbdf = MKBUS(BusPCI, bno, dno, fno);
3857dd7cddfSDavid du Colombier 			l = pcicfgrw32(tbdf, PciVID, 0, 1);
3867dd7cddfSDavid du Colombier 			if(l == 0xFFFFFFFF || l == 0)
3877dd7cddfSDavid du Colombier 				continue;
3887dd7cddfSDavid du Colombier 			p = malloc(sizeof(*p));
3897dd7cddfSDavid du Colombier 			p->tbdf = tbdf;
3907dd7cddfSDavid du Colombier 			p->vid = l;
3917dd7cddfSDavid du Colombier 			p->did = l>>16;
3927dd7cddfSDavid du Colombier 
3937dd7cddfSDavid du Colombier 			if(pcilist != nil)
3947dd7cddfSDavid du Colombier 				pcitail->list = p;
3957dd7cddfSDavid du Colombier 			else
3967dd7cddfSDavid du Colombier 				pcilist = p;
3977dd7cddfSDavid du Colombier 			pcitail = p;
3987dd7cddfSDavid du Colombier 
399e569ccb5SDavid du Colombier 			p->pcr = pcicfgr16(p, PciPCR);
4007dd7cddfSDavid du Colombier 			p->rid = pcicfgr8(p, PciRID);
4017dd7cddfSDavid du Colombier 			p->ccrp = pcicfgr8(p, PciCCRp);
4027dd7cddfSDavid du Colombier 			p->ccru = pcicfgr8(p, PciCCRu);
4037dd7cddfSDavid du Colombier 			p->ccrb = pcicfgr8(p, PciCCRb);
404e569ccb5SDavid du Colombier 			p->cls = pcicfgr8(p, PciCLS);
405e569ccb5SDavid du Colombier 			p->ltr = pcicfgr8(p, PciLTR);
4067dd7cddfSDavid du Colombier 
4077dd7cddfSDavid du Colombier 			p->intl = pcicfgr8(p, PciINTL);
4087dd7cddfSDavid du Colombier 
4097dd7cddfSDavid du Colombier 			/*
4107dd7cddfSDavid du Colombier 			 * If the device is a multi-function device adjust the
4117dd7cddfSDavid du Colombier 			 * loop count so all possible functions are checked.
4127dd7cddfSDavid du Colombier 			 */
4137dd7cddfSDavid du Colombier 			hdt = pcicfgr8(p, PciHDT);
4147dd7cddfSDavid du Colombier 			if(hdt & 0x80)
4157dd7cddfSDavid du Colombier 				maxfno = MaxFNO;
4167dd7cddfSDavid du Colombier 
4177dd7cddfSDavid du Colombier 			/*
4187dd7cddfSDavid du Colombier 			 * If appropriate, read the base address registers
4197dd7cddfSDavid du Colombier 			 * and work out the sizes.
4207dd7cddfSDavid du Colombier 			 */
4217dd7cddfSDavid du Colombier 			switch(p->ccrb) {
4227dd7cddfSDavid du Colombier 			case 0x01:		/* mass storage controller */
4237dd7cddfSDavid du Colombier 			case 0x02:		/* network controller */
4247dd7cddfSDavid du Colombier 			case 0x03:		/* display controller */
4257dd7cddfSDavid du Colombier 			case 0x04:		/* multimedia device */
4267dd7cddfSDavid du Colombier 			case 0x07:		/* simple comm. controllers */
4277dd7cddfSDavid du Colombier 			case 0x08:		/* base system peripherals */
4287dd7cddfSDavid du Colombier 			case 0x09:		/* input devices */
4297dd7cddfSDavid du Colombier 			case 0x0A:		/* docking stations */
4307dd7cddfSDavid du Colombier 			case 0x0B:		/* processors */
4317dd7cddfSDavid du Colombier 			case 0x0C:		/* serial bus controllers */
4327dd7cddfSDavid du Colombier 				if((hdt & 0x7F) != 0)
4337dd7cddfSDavid du Colombier 					break;
4347dd7cddfSDavid du Colombier 				rno = PciBAR0 - 4;
4357dd7cddfSDavid du Colombier 				for(i = 0; i < nelem(p->mem); i++) {
4367dd7cddfSDavid du Colombier 					rno += 4;
4377dd7cddfSDavid du Colombier 					p->mem[i].bar = pcicfgr32(p, rno);
4387dd7cddfSDavid du Colombier 					p->mem[i].size = pcibarsize(p, rno);
4397dd7cddfSDavid du Colombier 				}
4407dd7cddfSDavid du Colombier 				break;
4417dd7cddfSDavid du Colombier 
4427dd7cddfSDavid du Colombier 			case 0x00:
4437dd7cddfSDavid du Colombier 			case 0x05:		/* memory controller */
4447dd7cddfSDavid du Colombier 			case 0x06:		/* bridge device */
4457dd7cddfSDavid du Colombier 			default:
4467dd7cddfSDavid du Colombier 				break;
4477dd7cddfSDavid du Colombier 			}
4487dd7cddfSDavid du Colombier 
4497dd7cddfSDavid du Colombier 			if(head != nil)
4507dd7cddfSDavid du Colombier 				tail->link = p;
4517dd7cddfSDavid du Colombier 			else
4527dd7cddfSDavid du Colombier 				head = p;
4537dd7cddfSDavid du Colombier 			tail = p;
4547dd7cddfSDavid du Colombier 		}
4557dd7cddfSDavid du Colombier 	}
4567dd7cddfSDavid du Colombier 
4577dd7cddfSDavid du Colombier 	*list = head;
4587dd7cddfSDavid du Colombier 	for(p = head; p != nil; p = p->link){
4597dd7cddfSDavid du Colombier 		/*
4607dd7cddfSDavid du Colombier 		 * Find PCI-PCI bridges and recursively descend the tree.
4617dd7cddfSDavid du Colombier 		 */
4627dd7cddfSDavid du Colombier 		if(p->ccrb != 0x06 || p->ccru != 0x04)
4637dd7cddfSDavid du Colombier 			continue;
4647dd7cddfSDavid du Colombier 
4657dd7cddfSDavid du Colombier 		/*
4667dd7cddfSDavid du Colombier 		 * If the secondary or subordinate bus number is not
4677dd7cddfSDavid du Colombier 		 * initialised try to do what the PCI BIOS should have
4687dd7cddfSDavid du Colombier 		 * done and fill in the numbers as the tree is descended.
4697dd7cddfSDavid du Colombier 		 * On the way down the subordinate bus number is set to
4707dd7cddfSDavid du Colombier 		 * the maximum as it's not known how many buses are behind
4717dd7cddfSDavid du Colombier 		 * this one; the final value is set on the way back up.
4727dd7cddfSDavid du Colombier 		 */
4737dd7cddfSDavid du Colombier 		sbn = pcicfgr8(p, PciSBN);
4747dd7cddfSDavid du Colombier 		ubn = pcicfgr8(p, PciUBN);
4757dd7cddfSDavid du Colombier 
47680ee5cbfSDavid du Colombier 		if(sbn == 0 || ubn == 0 || nobios) {
4777dd7cddfSDavid du Colombier 			sbn = maxubn+1;
4787dd7cddfSDavid du Colombier 			/*
4797dd7cddfSDavid du Colombier 			 * Make sure memory, I/O and master enables are
4807dd7cddfSDavid du Colombier 			 * off, set the primary, secondary and subordinate
4817dd7cddfSDavid du Colombier 			 * bus numbers and clear the secondary status before
4827dd7cddfSDavid du Colombier 			 * attempting to scan the secondary bus.
4837dd7cddfSDavid du Colombier 			 *
4847dd7cddfSDavid du Colombier 			 * Initialisation of the bridge should be done here.
4857dd7cddfSDavid du Colombier 			 */
4867dd7cddfSDavid du Colombier 			pcicfgw32(p, PciPCR, 0xFFFF0000);
4877dd7cddfSDavid du Colombier 			l = (MaxUBN<<16)|(sbn<<8)|bno;
4887dd7cddfSDavid du Colombier 			pcicfgw32(p, PciPBN, l);
4897dd7cddfSDavid du Colombier 			pcicfgw16(p, PciSPSR, 0xFFFF);
4909a747e4fSDavid du Colombier 			maxubn = pcilscan(sbn, &p->bridge);
4917dd7cddfSDavid du Colombier 			l = (maxubn<<16)|(sbn<<8)|bno;
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier 			pcicfgw32(p, PciPBN, l);
4947dd7cddfSDavid du Colombier 		}
4957dd7cddfSDavid du Colombier 		else {
496e569ccb5SDavid du Colombier 			if(ubn > maxubn)
4977dd7cddfSDavid du Colombier 				maxubn = ubn;
4989a747e4fSDavid du Colombier 			pcilscan(sbn, &p->bridge);
4997dd7cddfSDavid du Colombier 		}
5007dd7cddfSDavid du Colombier 	}
5017dd7cddfSDavid du Colombier 
5027dd7cddfSDavid du Colombier 	return maxubn;
5037dd7cddfSDavid du Colombier }
5047dd7cddfSDavid du Colombier 
5059a747e4fSDavid du Colombier int
5069a747e4fSDavid du Colombier pciscan(int bno, Pcidev **list)
5079a747e4fSDavid du Colombier {
5089a747e4fSDavid du Colombier 	int ubn;
5099a747e4fSDavid du Colombier 
5109a747e4fSDavid du Colombier 	lock(&pcicfginitlock);
5119a747e4fSDavid du Colombier 	ubn = pcilscan(bno, list);
5129a747e4fSDavid du Colombier 	unlock(&pcicfginitlock);
5139a747e4fSDavid du Colombier 	return ubn;
5149a747e4fSDavid du Colombier }
5159a747e4fSDavid du Colombier 
5169a747e4fSDavid du Colombier static uchar
51759c21d95SDavid du Colombier pIIxget(Pcidev *router, uchar link)
5189a747e4fSDavid du Colombier {
5199a747e4fSDavid du Colombier 	uchar pirq;
5209a747e4fSDavid du Colombier 
5219a747e4fSDavid du Colombier 	/* link should be 0x60, 0x61, 0x62, 0x63 */
5229a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, link);
5239a747e4fSDavid du Colombier 	return (pirq < 16)? pirq: 0;
5249a747e4fSDavid du Colombier }
5259a747e4fSDavid du Colombier 
5269a747e4fSDavid du Colombier static void
52759c21d95SDavid du Colombier pIIxset(Pcidev *router, uchar link, uchar irq)
5289a747e4fSDavid du Colombier {
5299a747e4fSDavid du Colombier 	pcicfgw8(router, link, irq);
5309a747e4fSDavid du Colombier }
5319a747e4fSDavid du Colombier 
5329a747e4fSDavid du Colombier static uchar
53359c21d95SDavid du Colombier viaget(Pcidev *router, uchar link)
5349a747e4fSDavid du Colombier {
5359a747e4fSDavid du Colombier 	uchar pirq;
5369a747e4fSDavid du Colombier 
5379a747e4fSDavid du Colombier 	/* link should be 1, 2, 3, 5 */
5389a747e4fSDavid du Colombier 	pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0;
5399a747e4fSDavid du Colombier 
5409a747e4fSDavid du Colombier 	return (link & 1)? (pirq >> 4): (pirq & 15);
5419a747e4fSDavid du Colombier }
5429a747e4fSDavid du Colombier 
5439a747e4fSDavid du Colombier static void
54459c21d95SDavid du Colombier viaset(Pcidev *router, uchar link, uchar irq)
5459a747e4fSDavid du Colombier {
5469a747e4fSDavid du Colombier 	uchar pirq;
5479a747e4fSDavid du Colombier 
5489a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x55 + (link >> 1));
5499a747e4fSDavid du Colombier 	pirq &= (link & 1)? 0x0f: 0xf0;
5509a747e4fSDavid du Colombier 	pirq |= (link & 1)? (irq << 4): (irq & 15);
5519a747e4fSDavid du Colombier 	pcicfgw8(router, 0x55 + (link>>1), pirq);
5529a747e4fSDavid du Colombier }
5539a747e4fSDavid du Colombier 
5549a747e4fSDavid du Colombier static uchar
55559c21d95SDavid du Colombier optiget(Pcidev *router, uchar link)
5569a747e4fSDavid du Colombier {
5579a747e4fSDavid du Colombier 	uchar pirq = 0;
5589a747e4fSDavid du Colombier 
5599a747e4fSDavid du Colombier 	/* link should be 0x02, 0x12, 0x22, 0x32 */
5609a747e4fSDavid du Colombier 	if ((link & 0xcf) == 0x02)
5619a747e4fSDavid du Colombier 		pirq = pcicfgr8(router, 0xb8 + (link >> 5));
5629a747e4fSDavid du Colombier 	return (link & 0x10)? (pirq >> 4): (pirq & 15);
5639a747e4fSDavid du Colombier }
5649a747e4fSDavid du Colombier 
5659a747e4fSDavid du Colombier static void
56659c21d95SDavid du Colombier optiset(Pcidev *router, uchar link, uchar irq)
5679a747e4fSDavid du Colombier {
5689a747e4fSDavid du Colombier 	uchar pirq;
5699a747e4fSDavid du Colombier 
5709a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0xb8 + (link >> 5));
5719a747e4fSDavid du Colombier     	pirq &= (link & 0x10)? 0x0f : 0xf0;
5729a747e4fSDavid du Colombier     	pirq |= (link & 0x10)? (irq << 4): (irq & 15);
5739a747e4fSDavid du Colombier 	pcicfgw8(router, 0xb8 + (link >> 5), pirq);
5749a747e4fSDavid du Colombier }
5759a747e4fSDavid du Colombier 
5769a747e4fSDavid du Colombier static uchar
57759c21d95SDavid du Colombier aliget(Pcidev *router, uchar link)
5789a747e4fSDavid du Colombier {
5799a747e4fSDavid du Colombier 	/* No, you're not dreaming */
5809a747e4fSDavid du Colombier 	static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
5819a747e4fSDavid du Colombier 	uchar pirq;
5829a747e4fSDavid du Colombier 
5839a747e4fSDavid du Colombier 	/* link should be 0x01..0x08 */
5849a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
5859a747e4fSDavid du Colombier 	return (link & 1)? map[pirq&15]: map[pirq>>4];
5869a747e4fSDavid du Colombier }
5879a747e4fSDavid du Colombier 
5889a747e4fSDavid du Colombier static void
58959c21d95SDavid du Colombier aliset(Pcidev *router, uchar link, uchar irq)
5909a747e4fSDavid du Colombier {
59159c21d95SDavid du Colombier 	/* Inverse of map in aliget */
5929a747e4fSDavid du Colombier 	static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
5939a747e4fSDavid du Colombier 	uchar pirq;
5949a747e4fSDavid du Colombier 
5959a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
5969a747e4fSDavid du Colombier 	pirq &= (link & 1)? 0x0f: 0xf0;
5979a747e4fSDavid du Colombier 	pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15);
5989a747e4fSDavid du Colombier 	pcicfgw8(router, 0x48 + ((link-1)>>1), pirq);
5999a747e4fSDavid du Colombier }
6009a747e4fSDavid du Colombier 
6019a747e4fSDavid du Colombier static uchar
60259c21d95SDavid du Colombier cyrixget(Pcidev *router, uchar link)
6039a747e4fSDavid du Colombier {
6049a747e4fSDavid du Colombier 	uchar pirq;
6059a747e4fSDavid du Colombier 
6069a747e4fSDavid du Colombier 	/* link should be 1, 2, 3, 4 */
6079a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x5c + ((link-1)>>1));
6089a747e4fSDavid du Colombier 	return ((link & 1)? pirq >> 4: pirq & 15);
6099a747e4fSDavid du Colombier }
6109a747e4fSDavid du Colombier 
6119a747e4fSDavid du Colombier static void
61259c21d95SDavid du Colombier cyrixset(Pcidev *router, uchar link, uchar irq)
6139a747e4fSDavid du Colombier {
6149a747e4fSDavid du Colombier 	uchar pirq;
6159a747e4fSDavid du Colombier 
6169a747e4fSDavid du Colombier 	pirq = pcicfgr8(router, 0x5c + (link>>1));
6179a747e4fSDavid du Colombier 	pirq &= (link & 1)? 0x0f: 0xf0;
6189a747e4fSDavid du Colombier 	pirq |= (link & 1)? (irq << 4): (irq & 15);
6199a747e4fSDavid du Colombier 	pcicfgw8(router, 0x5c + (link>>1), pirq);
6209a747e4fSDavid du Colombier }
6219a747e4fSDavid du Colombier 
62259c21d95SDavid du Colombier typedef struct Bridge Bridge;
62359c21d95SDavid du Colombier struct Bridge
6249a747e4fSDavid du Colombier {
6259a747e4fSDavid du Colombier 	ushort	vid;
6269a747e4fSDavid du Colombier 	ushort	did;
6279a747e4fSDavid du Colombier 	uchar	(*get)(Pcidev *, uchar);
6289a747e4fSDavid du Colombier 	void	(*set)(Pcidev *, uchar, uchar);
6299a747e4fSDavid du Colombier };
6309a747e4fSDavid du Colombier 
63159c21d95SDavid du Colombier static Bridge southbridges[] = {
63241dd6b47SDavid du Colombier 	{ 0x8086, 0x122e, pIIxget, pIIxset },	/* Intel 82371FB */
63341dd6b47SDavid du Colombier 	{ 0x8086, 0x1234, pIIxget, pIIxset },	/* Intel 82371MX */
63441dd6b47SDavid du Colombier 	{ 0x8086, 0x7000, pIIxget, pIIxset },	/* Intel 82371SB */
63541dd6b47SDavid du Colombier 	{ 0x8086, 0x7110, pIIxget, pIIxset },	/* Intel 82371AB */
63641dd6b47SDavid du Colombier 	{ 0x8086, 0x7198, pIIxget, pIIxset },	/* Intel 82443MX (fn 1) */
63741dd6b47SDavid du Colombier 	{ 0x8086, 0x2410, pIIxget, pIIxset },	/* Intel 82801AA */
63841dd6b47SDavid du Colombier 	{ 0x8086, 0x2420, pIIxget, pIIxset },	/* Intel 82801AB */
63941dd6b47SDavid du Colombier 	{ 0x8086, 0x2440, pIIxget, pIIxset },	/* Intel 82801BA */
64041dd6b47SDavid du Colombier 	{ 0x8086, 0x244c, pIIxget, pIIxset },	/* Intel 82801BAM */
64141dd6b47SDavid du Colombier 	{ 0x8086, 0x2480, pIIxget, pIIxset },	/* Intel 82801CA */
64241dd6b47SDavid du Colombier 	{ 0x8086, 0x248c, pIIxget, pIIxset },	/* Intel 82801CAM */
64341dd6b47SDavid du Colombier 	{ 0x8086, 0x24c0, pIIxget, pIIxset },	/* Intel 82801DBL */
64441dd6b47SDavid du Colombier 	{ 0x8086, 0x24cc, pIIxget, pIIxset },	/* Intel 82801DBM */
64541dd6b47SDavid du Colombier 	{ 0x8086, 0x24d0, pIIxget, pIIxset },	/* Intel 82801EB */
64641dd6b47SDavid du Colombier 	{ 0x8086, 0x2640, pIIxget, pIIxset },	/* Intel 82801FB */
64741dd6b47SDavid du Colombier 	{ 0x8086, 0x27b8, pIIxget, pIIxset },	/* Intel 82801GB */
64841dd6b47SDavid du Colombier 	{ 0x8086, 0x27b9, pIIxget, pIIxset },	/* Intel 82801GBM */
64941dd6b47SDavid du Colombier 	{ 0x1106, 0x0586, viaget, viaset },	/* Viatech 82C586 */
65041dd6b47SDavid du Colombier 	{ 0x1106, 0x0596, viaget, viaset },	/* Viatech 82C596 */
65141dd6b47SDavid du Colombier 	{ 0x1106, 0x0686, viaget, viaset },	/* Viatech 82C686 */
65241dd6b47SDavid du Colombier 	{ 0x1106, 0x3227, viaget, viaset },	/* Viatech VT8237 */
65341dd6b47SDavid du Colombier 	{ 0x1045, 0xc700, optiget, optiset },	/* Opti 82C700 */
65441dd6b47SDavid du Colombier 	{ 0x10b9, 0x1533, aliget, aliset },	/* Al M1533 */
65541dd6b47SDavid du Colombier 	{ 0x1039, 0x0008, pIIxget, pIIxset },	/* SI 503 */
65641dd6b47SDavid du Colombier 	{ 0x1039, 0x0496, pIIxget, pIIxset },	/* SI 496 */
65741dd6b47SDavid du Colombier 	{ 0x1078, 0x0100, cyrixget, cyrixset },	/* Cyrix 5530 Legacy */
6585000fddeSDavid du Colombier 
65941dd6b47SDavid du Colombier 	{ 0x1022, 0x746B, nil, nil },		/* AMD 8111 */
66041dd6b47SDavid du Colombier 	{ 0x10DE, 0x00D1, nil, nil },		/* NVIDIA nForce 3 */
66141dd6b47SDavid du Colombier 	{ 0x1166, 0x0200, nil, nil },		/* ServerWorks ServerSet III LE */
66241dd6b47SDavid du Colombier 	{ 0x1002, 0x4377, nil, nil },		/* ATI Radeon Xpress 200M */
66341dd6b47SDavid du Colombier 	{ 0x1002, 0x4372, nil, nil },		/* ATI SB400 */
66459c21d95SDavid du Colombier };
6659a747e4fSDavid du Colombier 
66659c21d95SDavid du Colombier typedef struct Slot Slot;
66759c21d95SDavid du Colombier struct Slot {
66841dd6b47SDavid du Colombier 	uchar	bus;		/* Pci bus number */
66941dd6b47SDavid du Colombier 	uchar	dev;		/* Pci device number */
67041dd6b47SDavid du Colombier 	uchar	maps[12];	/* Avoid structs!  Link and mask. */
67141dd6b47SDavid du Colombier 	uchar	slot;		/* Add-in/built-in slot */
67259c21d95SDavid du Colombier 	uchar	reserved;
67359c21d95SDavid du Colombier };
67459c21d95SDavid du Colombier 
67559c21d95SDavid du Colombier typedef struct Router Router;
67659c21d95SDavid du Colombier struct Router {
67741dd6b47SDavid du Colombier 	uchar	signature[4];	/* Routing table signature */
67841dd6b47SDavid du Colombier 	uchar	version[2];	/* Version number */
67941dd6b47SDavid du Colombier 	uchar	size[2];	/* Total table size */
68041dd6b47SDavid du Colombier 	uchar	bus;		/* Interrupt router bus number */
68141dd6b47SDavid du Colombier 	uchar	devfn;		/* Router's devfunc */
68241dd6b47SDavid du Colombier 	uchar	pciirqs[2];	/* Exclusive PCI irqs */
68341dd6b47SDavid du Colombier 	uchar	compat[4];	/* Compatible PCI interrupt router */
68441dd6b47SDavid du Colombier 	uchar	miniport[4];	/* Miniport data */
68559c21d95SDavid du Colombier 	uchar	reserved[11];
68659c21d95SDavid du Colombier 	uchar	checksum;
68759c21d95SDavid du Colombier };
6889a747e4fSDavid du Colombier 
68941dd6b47SDavid du Colombier static ushort pciirqs;		/* Exclusive PCI irqs */
69041dd6b47SDavid du Colombier static Bridge *southbridge;	/* Which southbridge to use. */
6919a747e4fSDavid du Colombier 
6929a747e4fSDavid du Colombier static void
6939a747e4fSDavid du Colombier pcirouting(void)
6949a747e4fSDavid du Colombier {
69559c21d95SDavid du Colombier 	Slot *e;
69659c21d95SDavid du Colombier 	Router *r;
6979a747e4fSDavid du Colombier 	int size, i, fn, tbdf;
6989a747e4fSDavid du Colombier 	Pcidev *sbpci, *pci;
6995000fddeSDavid du Colombier 	uchar *p, pin, irq, link, *map;
7009a747e4fSDavid du Colombier 
70141dd6b47SDavid du Colombier 	/* Search for PCI interrupt routing table in BIOS */
7029a747e4fSDavid du Colombier 	for(p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16)
7039a747e4fSDavid du Colombier 		if(p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R')
7049a747e4fSDavid du Colombier 			break;
7059a747e4fSDavid du Colombier 
7069a747e4fSDavid du Colombier 	if(p >= (uchar *)KADDR(0xfffff))
7079a747e4fSDavid du Colombier 		return;
7089a747e4fSDavid du Colombier 
70959c21d95SDavid du Colombier 	r = (Router *)p;
7109a747e4fSDavid du Colombier 
7119a747e4fSDavid du Colombier 	// print("PCI interrupt routing table version %d.%d at %.6uX\n",
71259c21d95SDavid du Colombier 	//	r->version[0], r->version[1], (ulong)r & 0xfffff);
7139a747e4fSDavid du Colombier 
71459c21d95SDavid du Colombier 	tbdf = (BusPCI << 24)|(r->bus << 16)|(r->devfn << 8);
7159a747e4fSDavid du Colombier 	sbpci = pcimatchtbdf(tbdf);
7169a747e4fSDavid du Colombier 	if(sbpci == nil) {
7179a747e4fSDavid du Colombier 		print("pcirouting: Cannot find south bridge %T\n", tbdf);
7189a747e4fSDavid du Colombier 		return;
7199a747e4fSDavid du Colombier 	}
7209a747e4fSDavid du Colombier 
7219a747e4fSDavid du Colombier 	for(i = 0; i != nelem(southbridges); i++)
7229a747e4fSDavid du Colombier 		if(sbpci->vid == southbridges[i].vid && sbpci->did == southbridges[i].did)
7239a747e4fSDavid du Colombier 			break;
7249a747e4fSDavid du Colombier 
7259a747e4fSDavid du Colombier 	if(i == nelem(southbridges)) {
7263ff48bf5SDavid du Colombier 		print("pcirouting: ignoring south bridge %T %.4uX/%.4uX\n", tbdf, sbpci->vid, sbpci->did);
7279a747e4fSDavid du Colombier 		return;
7289a747e4fSDavid du Colombier 	}
7299a747e4fSDavid du Colombier 	southbridge = &southbridges[i];
7305000fddeSDavid du Colombier 	if(southbridge->get == nil || southbridge->set == nil)
7315000fddeSDavid du Colombier 		return;
7329a747e4fSDavid du Colombier 
73359c21d95SDavid du Colombier 	pciirqs = (r->pciirqs[1] << 8)|r->pciirqs[0];
7349a747e4fSDavid du Colombier 
73559c21d95SDavid du Colombier 	size = (r->size[1] << 8)|r->size[0];
73659c21d95SDavid du Colombier 	for(e = (Slot *)&r[1]; (uchar *)e < p + size; e++) {
73741dd6b47SDavid du Colombier 		if (0) {
73841dd6b47SDavid du Colombier 			print("%.2uX/%.2uX %.2uX: ", e->bus, e->dev, e->slot);
73941dd6b47SDavid du Colombier 			for (i = 0; i != 4; i++) {
74041dd6b47SDavid du Colombier 				uchar *m = &e->maps[i * 3];
74141dd6b47SDavid du Colombier 				print("[%d] %.2uX %.4uX ",
74241dd6b47SDavid du Colombier 					i, m[0], (m[2] << 8)|m[1]);
74341dd6b47SDavid du Colombier 			}
74441dd6b47SDavid du Colombier 			print("\n");
74541dd6b47SDavid du Colombier 		}
7469a747e4fSDavid du Colombier 		for(fn = 0; fn != 8; fn++) {
74759c21d95SDavid du Colombier 			tbdf = (BusPCI << 24)|(e->bus << 16)|((e->dev | fn) << 8);
7489a747e4fSDavid du Colombier 			pci = pcimatchtbdf(tbdf);
7499a747e4fSDavid du Colombier 			if(pci == nil)
7509a747e4fSDavid du Colombier 				continue;
7519a747e4fSDavid du Colombier 			pin = pcicfgr8(pci, PciINTP);
7529a747e4fSDavid du Colombier 			if(pin == 0 || pin == 0xff)
7539a747e4fSDavid du Colombier 				continue;
7549a747e4fSDavid du Colombier 
7555000fddeSDavid du Colombier 			map = &e->maps[(pin - 1) * 3];
7565000fddeSDavid du Colombier 			link = map[0];
7573ff48bf5SDavid du Colombier 			irq = southbridge->get(sbpci, link);
7589a747e4fSDavid du Colombier 			if(irq == 0 || irq == pci->intl)
7599a747e4fSDavid du Colombier 				continue;
7609a747e4fSDavid du Colombier 			if(pci->intl != 0 && pci->intl != 0xFF) {
7613ff48bf5SDavid du Colombier 				print("pcirouting: BIOS workaround: %T at pin %d link %d irq %d -> %d\n",
7623ff48bf5SDavid du Colombier 					  tbdf, pin, link, irq, pci->intl);
7633ff48bf5SDavid du Colombier 				southbridge->set(sbpci, link, pci->intl);
7649a747e4fSDavid du Colombier 				continue;
7659a747e4fSDavid du Colombier 			}
7663ff48bf5SDavid du Colombier 			print("pcirouting: %T at pin %d link %d irq %d\n", tbdf, pin, link, irq);
7679a747e4fSDavid du Colombier 			pcicfgw8(pci, PciINTL, irq);
7689a747e4fSDavid du Colombier 			pci->intl = irq;
7699a747e4fSDavid du Colombier 		}
7709a747e4fSDavid du Colombier 	}
7719a747e4fSDavid du Colombier }
7729a747e4fSDavid du Colombier 
7734de34a7eSDavid du Colombier static void pcireservemem(void);
7744de34a7eSDavid du Colombier 
7754fafed5dSDavid du Colombier static int
7764fafed5dSDavid du Colombier pcicfgrw8bios(int tbdf, int rno, int data, int read)
7774fafed5dSDavid du Colombier {
7784fafed5dSDavid du Colombier 	BIOS32ci ci;
7794fafed5dSDavid du Colombier 
7804fafed5dSDavid du Colombier 	if(pcibiossi == nil)
7814fafed5dSDavid du Colombier 		return -1;
7824fafed5dSDavid du Colombier 
7834fafed5dSDavid du Colombier 	memset(&ci, 0, sizeof(BIOS32ci));
7844fafed5dSDavid du Colombier 	ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
7854fafed5dSDavid du Colombier 	ci.edi = rno;
7864fafed5dSDavid du Colombier 	if(read){
7874fafed5dSDavid du Colombier 		ci.eax = 0xB108;
7884fafed5dSDavid du Colombier 		if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
7894fafed5dSDavid du Colombier 			return ci.ecx & 0xFF;
7904fafed5dSDavid du Colombier 	}
7914fafed5dSDavid du Colombier 	else{
7924fafed5dSDavid du Colombier 		ci.eax = 0xB10B;
7934fafed5dSDavid du Colombier 		ci.ecx = data & 0xFF;
7944fafed5dSDavid du Colombier 		if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
7954fafed5dSDavid du Colombier 			return 0;
7964fafed5dSDavid du Colombier 	}
7974fafed5dSDavid du Colombier 
7984fafed5dSDavid du Colombier 	return -1;
7994fafed5dSDavid du Colombier }
8004fafed5dSDavid du Colombier 
8014fafed5dSDavid du Colombier static int
8024fafed5dSDavid du Colombier pcicfgrw16bios(int tbdf, int rno, int data, int read)
8034fafed5dSDavid du Colombier {
8044fafed5dSDavid du Colombier 	BIOS32ci ci;
8054fafed5dSDavid du Colombier 
8064fafed5dSDavid du Colombier 	if(pcibiossi == nil)
8074fafed5dSDavid du Colombier 		return -1;
8084fafed5dSDavid du Colombier 
8094fafed5dSDavid du Colombier 	memset(&ci, 0, sizeof(BIOS32ci));
8104fafed5dSDavid du Colombier 	ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
8114fafed5dSDavid du Colombier 	ci.edi = rno;
8124fafed5dSDavid du Colombier 	if(read){
8134fafed5dSDavid du Colombier 		ci.eax = 0xB109;
8144fafed5dSDavid du Colombier 		if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8154fafed5dSDavid du Colombier 			return ci.ecx & 0xFFFF;
8164fafed5dSDavid du Colombier 	}
8174fafed5dSDavid du Colombier 	else{
8184fafed5dSDavid du Colombier 		ci.eax = 0xB10C;
8194fafed5dSDavid du Colombier 		ci.ecx = data & 0xFFFF;
8204fafed5dSDavid du Colombier 		if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8214fafed5dSDavid du Colombier 			return 0;
8224fafed5dSDavid du Colombier 	}
8234fafed5dSDavid du Colombier 
8244fafed5dSDavid du Colombier 	return -1;
8254fafed5dSDavid du Colombier }
8264fafed5dSDavid du Colombier 
8274fafed5dSDavid du Colombier static int
8284fafed5dSDavid du Colombier pcicfgrw32bios(int tbdf, int rno, int data, int read)
8294fafed5dSDavid du Colombier {
8304fafed5dSDavid du Colombier 	BIOS32ci ci;
8314fafed5dSDavid du Colombier 
8324fafed5dSDavid du Colombier 	if(pcibiossi == nil)
8334fafed5dSDavid du Colombier 		return -1;
8344fafed5dSDavid du Colombier 
8354fafed5dSDavid du Colombier 	memset(&ci, 0, sizeof(BIOS32ci));
8364fafed5dSDavid du Colombier 	ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
8374fafed5dSDavid du Colombier 	ci.edi = rno;
8384fafed5dSDavid du Colombier 	if(read){
8394fafed5dSDavid du Colombier 		ci.eax = 0xB10A;
8404fafed5dSDavid du Colombier 		if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8414fafed5dSDavid du Colombier 			return ci.ecx;
8424fafed5dSDavid du Colombier 	}
8434fafed5dSDavid du Colombier 	else{
8444fafed5dSDavid du Colombier 		ci.eax = 0xB10D;
8454fafed5dSDavid du Colombier 		ci.ecx = data;
8464fafed5dSDavid du Colombier 		if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8474fafed5dSDavid du Colombier 			return 0;
8484fafed5dSDavid du Colombier 	}
8494fafed5dSDavid du Colombier 
8504fafed5dSDavid du Colombier 	return -1;
8514fafed5dSDavid du Colombier }
8524fafed5dSDavid du Colombier 
8534fafed5dSDavid du Colombier static BIOS32si*
8544fafed5dSDavid du Colombier pcibiosinit(void)
8554fafed5dSDavid du Colombier {
8564fafed5dSDavid du Colombier 	BIOS32ci ci;
8574fafed5dSDavid du Colombier 	BIOS32si *si;
8584fafed5dSDavid du Colombier 
8594fafed5dSDavid du Colombier 	if((si = bios32open("$PCI")) == nil)
8604fafed5dSDavid du Colombier 		return nil;
8614fafed5dSDavid du Colombier 
8624fafed5dSDavid du Colombier 	memset(&ci, 0, sizeof(BIOS32ci));
8634fafed5dSDavid du Colombier 	ci.eax = 0xB101;
8644fafed5dSDavid du Colombier 	if(bios32ci(si, &ci) || ci.edx != ((' '<<24)|('I'<<16)|('C'<<8)|'P')){
8654fafed5dSDavid du Colombier 		free(si);
8664fafed5dSDavid du Colombier 		return nil;
8674fafed5dSDavid du Colombier 	}
8684fafed5dSDavid du Colombier 	if(ci.eax & 0x01)
8694fafed5dSDavid du Colombier 		pcimaxdno = 31;
8704fafed5dSDavid du Colombier 	else
8714fafed5dSDavid du Colombier 		pcimaxdno = 15;
8724fafed5dSDavid du Colombier 	pcimaxbno = ci.ecx & 0xff;
8734fafed5dSDavid du Colombier 
8744fafed5dSDavid du Colombier 	return si;
8754fafed5dSDavid du Colombier }
8764fafed5dSDavid du Colombier 
877c91329d7SDavid du Colombier void
878c91329d7SDavid du Colombier pcibussize(Pcidev *root, ulong *msize, ulong *iosize)
879c91329d7SDavid du Colombier {
880c91329d7SDavid du Colombier 	*msize = 0;
881c91329d7SDavid du Colombier 	*iosize = 0;
882c91329d7SDavid du Colombier 	pcibusmap(root, msize, iosize, 0);
883c91329d7SDavid du Colombier }
884c91329d7SDavid du Colombier 
8857dd7cddfSDavid du Colombier static void
8867dd7cddfSDavid du Colombier pcicfginit(void)
8877dd7cddfSDavid du Colombier {
8887dd7cddfSDavid du Colombier 	char *p;
8897dd7cddfSDavid du Colombier 	Pcidev **list;
8907dd7cddfSDavid du Colombier 	ulong mema, ioa;
891becaf2abSDavid du Colombier 	int bno, n, pcibios;
8927dd7cddfSDavid du Colombier 
8937dd7cddfSDavid du Colombier 	lock(&pcicfginitlock);
8947dd7cddfSDavid du Colombier 	if(pcicfgmode != -1)
8957dd7cddfSDavid du Colombier 		goto out;
8967dd7cddfSDavid du Colombier 
897becaf2abSDavid du Colombier 	pcibios = 0;
89880ee5cbfSDavid du Colombier 	if(getconf("*nobios"))
89980ee5cbfSDavid du Colombier 		nobios = 1;
900becaf2abSDavid du Colombier 	else if(getconf("*pcibios"))
901becaf2abSDavid du Colombier 		pcibios = 1;
9023ff48bf5SDavid du Colombier 	if(getconf("*nopcirouting"))
9033ff48bf5SDavid du Colombier 		nopcirouting = 1;
9043ff48bf5SDavid du Colombier 
9057dd7cddfSDavid du Colombier 	/*
9067dd7cddfSDavid du Colombier 	 * Try to determine which PCI configuration mode is implemented.
9077dd7cddfSDavid du Colombier 	 * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
9087dd7cddfSDavid du Colombier 	 * a DWORD at 0xCF8 and another at 0xCFC and will pass through
9097dd7cddfSDavid du Colombier 	 * any non-DWORD accesses as normal I/O cycles. There shouldn't be
9103ff48bf5SDavid du Colombier 	 * a device behind these addresses so if Mode1 accesses fail try
9113ff48bf5SDavid du Colombier 	 * for Mode2 (Mode2 is deprecated).
9127dd7cddfSDavid du Colombier 	 */
913becaf2abSDavid du Colombier 	if(!pcibios){
914becaf2abSDavid du Colombier 		/*
915becaf2abSDavid du Colombier 		 * Bits [30:24] of PciADDR must be 0,
916becaf2abSDavid du Colombier 		 * according to the spec.
917becaf2abSDavid du Colombier 		 */
918becaf2abSDavid du Colombier 		n = inl(PciADDR);
919*6e712d44SDavid du Colombier 		if(!(n & 0x7F000000)){
920becaf2abSDavid du Colombier 			outl(PciADDR, 0x80000000);
921becaf2abSDavid du Colombier 			outb(PciADDR+3, 0);
922becaf2abSDavid du Colombier 			if(inl(PciADDR) & 0x80000000){
9237dd7cddfSDavid du Colombier 				pcicfgmode = 1;
9247dd7cddfSDavid du Colombier 				pcimaxdno = 31;
9257dd7cddfSDavid du Colombier 			}
926becaf2abSDavid du Colombier 		}
927becaf2abSDavid du Colombier 		outl(PciADDR, n);
928becaf2abSDavid du Colombier 
929becaf2abSDavid du Colombier 		if(pcicfgmode < 0){
930becaf2abSDavid du Colombier 			/*
931becaf2abSDavid du Colombier 			 * The 'key' part of PciCSE should be 0.
932becaf2abSDavid du Colombier 			 */
933becaf2abSDavid du Colombier 			n = inb(PciCSE);
934becaf2abSDavid du Colombier 			if(!(n & 0xF0)){
935becaf2abSDavid du Colombier 				outb(PciCSE, 0x0E);
936becaf2abSDavid du Colombier 				if(inb(PciCSE) == 0x0E){
9373ff48bf5SDavid du Colombier 					pcicfgmode = 2;
9383ff48bf5SDavid du Colombier 					pcimaxdno = 15;
9393ff48bf5SDavid du Colombier 				}
9407dd7cddfSDavid du Colombier 			}
941becaf2abSDavid du Colombier 			outb(PciCSE, n);
942becaf2abSDavid du Colombier 		}
943becaf2abSDavid du Colombier 	}
9447dd7cddfSDavid du Colombier 
9454fafed5dSDavid du Colombier 	if(pcicfgmode < 0 || pcibios) {
9464fafed5dSDavid du Colombier 		if((pcibiossi = pcibiosinit()) == nil)
9477dd7cddfSDavid du Colombier 			goto out;
9484fafed5dSDavid du Colombier 		pcicfgrw8 = pcicfgrw8bios;
9494fafed5dSDavid du Colombier 		pcicfgrw16 = pcicfgrw16bios;
9504fafed5dSDavid du Colombier 		pcicfgrw32 = pcicfgrw32bios;
9514fafed5dSDavid du Colombier 		pcicfgmode = 3;
9524fafed5dSDavid du Colombier 	}
9537dd7cddfSDavid du Colombier 
9549a747e4fSDavid du Colombier 	fmtinstall('T', tbdffmt);
9557dd7cddfSDavid du Colombier 
956becaf2abSDavid du Colombier 	if(p = getconf("*pcimaxbno")){
957becaf2abSDavid du Colombier 		n = strtoul(p, 0, 0);
958becaf2abSDavid du Colombier 		if(n < pcimaxbno)
959becaf2abSDavid du Colombier 			pcimaxbno = n;
960becaf2abSDavid du Colombier 	}
961becaf2abSDavid du Colombier 	if(p = getconf("*pcimaxdno")){
962becaf2abSDavid du Colombier 		n = strtoul(p, 0, 0);
963becaf2abSDavid du Colombier 		if(n < pcimaxdno)
964becaf2abSDavid du Colombier 			pcimaxdno = n;
965becaf2abSDavid du Colombier 	}
9667dd7cddfSDavid du Colombier 
9677dd7cddfSDavid du Colombier 	list = &pciroot;
968223a736eSDavid du Colombier 	for(bno = 0; bno <= pcimaxbno; bno++) {
9699a747e4fSDavid du Colombier 		int sbno = bno;
9709a747e4fSDavid du Colombier 		bno = pcilscan(bno, list);
9719a747e4fSDavid du Colombier 
9727dd7cddfSDavid du Colombier 		while(*list)
9737dd7cddfSDavid du Colombier 			list = &(*list)->link;
9749a747e4fSDavid du Colombier 
9759a747e4fSDavid du Colombier 		if (sbno == 0) {
9769a747e4fSDavid du Colombier 			Pcidev *pci;
9779a747e4fSDavid du Colombier 
9789a747e4fSDavid du Colombier 			/*
9799a747e4fSDavid du Colombier 			  * If we have found a PCI-to-Cardbus bridge, make sure
9809a747e4fSDavid du Colombier 			  * it has no valid mappings anymore.
9819a747e4fSDavid du Colombier 			  */
982c91329d7SDavid du Colombier 			for(pci = pciroot; pci != nil; pci = pci->link){
9839a747e4fSDavid du Colombier 				if (pci->ccrb == 6 && pci->ccru == 7) {
9849a747e4fSDavid du Colombier 					ushort bcr;
9859a747e4fSDavid du Colombier 
9869a747e4fSDavid du Colombier 					/* reset the cardbus */
9879a747e4fSDavid du Colombier 					bcr = pcicfgr16(pci, PciBCR);
9889a747e4fSDavid du Colombier 					pcicfgw16(pci, PciBCR, 0x40 | bcr);
9899a747e4fSDavid du Colombier 					delay(50);
9909a747e4fSDavid du Colombier 				}
9919a747e4fSDavid du Colombier 			}
9929a747e4fSDavid du Colombier 		}
9937dd7cddfSDavid du Colombier 	}
9947dd7cddfSDavid du Colombier 
9957dd7cddfSDavid du Colombier 	if(pciroot == nil)
9967dd7cddfSDavid du Colombier 		goto out;
9977dd7cddfSDavid du Colombier 
99880ee5cbfSDavid du Colombier 	if(nobios) {
9997dd7cddfSDavid du Colombier 		/*
10007dd7cddfSDavid du Colombier 		 * Work out how big the top bus is
10017dd7cddfSDavid du Colombier 		 */
1002c91329d7SDavid du Colombier 		pcibussize(pciroot, &mema, &ioa);
10037dd7cddfSDavid du Colombier 
10047dd7cddfSDavid du Colombier 		/*
10057dd7cddfSDavid du Colombier 		 * Align the windows and map it
10067dd7cddfSDavid du Colombier 		 */
10077dd7cddfSDavid du Colombier 		ioa = 0x1000;
10087dd7cddfSDavid du Colombier 		mema = 0x90000000;
10097dd7cddfSDavid du Colombier 
10107dd7cddfSDavid du Colombier 		pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
10117dd7cddfSDavid du Colombier 
10127dd7cddfSDavid du Colombier 		pcibusmap(pciroot, &mema, &ioa, 1);
10137dd7cddfSDavid du Colombier 		DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
10147dd7cddfSDavid du Colombier 
10157dd7cddfSDavid du Colombier 		unlock(&pcicfginitlock);
10167dd7cddfSDavid du Colombier 		return;
10177dd7cddfSDavid du Colombier 	}
10189a747e4fSDavid du Colombier 
10193ff48bf5SDavid du Colombier 	if (!nopcirouting)
10209a747e4fSDavid du Colombier 		pcirouting();
10219a747e4fSDavid du Colombier 
10227dd7cddfSDavid du Colombier out:
10234de34a7eSDavid du Colombier 	pcireservemem();
10247dd7cddfSDavid du Colombier 	unlock(&pcicfginitlock);
10259a747e4fSDavid du Colombier 
10263ff48bf5SDavid du Colombier 	if(getconf("*pcihinv"))
10273ff48bf5SDavid du Colombier 		pcihinv(nil);
10287dd7cddfSDavid du Colombier }
10297dd7cddfSDavid du Colombier 
10304de34a7eSDavid du Colombier static void
10314de34a7eSDavid du Colombier pcireservemem(void)
10324de34a7eSDavid du Colombier {
10334de34a7eSDavid du Colombier 	int i;
10344de34a7eSDavid du Colombier 	Pcidev *p;
10354de34a7eSDavid du Colombier 
10364de34a7eSDavid du Colombier 	/*
10374de34a7eSDavid du Colombier 	 * mark all the physical address space claimed by pci devices
10384de34a7eSDavid du Colombier 	 * as in use, so that upaalloc doesn't give it out.
10394de34a7eSDavid du Colombier 	 */
10404de34a7eSDavid du Colombier 	for(p=pciroot; p; p=p->list)
10414de34a7eSDavid du Colombier 		for(i=0; i<nelem(p->mem); i++)
10424de34a7eSDavid du Colombier 			if(p->mem[i].bar && (p->mem[i].bar&1) == 0)
10434de34a7eSDavid du Colombier 				upareserve(p->mem[i].bar&~0x0F, p->mem[i].size);
10444de34a7eSDavid du Colombier }
10454de34a7eSDavid du Colombier 
10467dd7cddfSDavid du Colombier static int
10474fafed5dSDavid du Colombier pcicfgrw8raw(int tbdf, int rno, int data, int read)
10487dd7cddfSDavid du Colombier {
10497dd7cddfSDavid du Colombier 	int o, type, x;
10507dd7cddfSDavid du Colombier 
10517dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
10527dd7cddfSDavid du Colombier 		pcicfginit();
10537dd7cddfSDavid du Colombier 
10547dd7cddfSDavid du Colombier 	if(BUSBNO(tbdf))
10557dd7cddfSDavid du Colombier 		type = 0x01;
10567dd7cddfSDavid du Colombier 	else
10577dd7cddfSDavid du Colombier 		type = 0x00;
10587dd7cddfSDavid du Colombier 	x = -1;
10597dd7cddfSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
10607dd7cddfSDavid du Colombier 		return x;
10617dd7cddfSDavid du Colombier 
10627dd7cddfSDavid du Colombier 	lock(&pcicfglock);
10637dd7cddfSDavid du Colombier 	switch(pcicfgmode){
10647dd7cddfSDavid du Colombier 
10657dd7cddfSDavid du Colombier 	case 1:
10667dd7cddfSDavid du Colombier 		o = rno & 0x03;
10677dd7cddfSDavid du Colombier 		rno &= ~0x03;
10687dd7cddfSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
10697dd7cddfSDavid du Colombier 		if(read)
10707dd7cddfSDavid du Colombier 			x = inb(PciDATA+o);
10717dd7cddfSDavid du Colombier 		else
10727dd7cddfSDavid du Colombier 			outb(PciDATA+o, data);
10737dd7cddfSDavid du Colombier 		outl(PciADDR, 0);
10747dd7cddfSDavid du Colombier 		break;
10757dd7cddfSDavid du Colombier 
10767dd7cddfSDavid du Colombier 	case 2:
10777dd7cddfSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
10787dd7cddfSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
10797dd7cddfSDavid du Colombier 		if(read)
10807dd7cddfSDavid du Colombier 			x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
10817dd7cddfSDavid du Colombier 		else
10827dd7cddfSDavid du Colombier 			outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
10837dd7cddfSDavid du Colombier 		outb(PciCSE, 0);
10847dd7cddfSDavid du Colombier 		break;
10857dd7cddfSDavid du Colombier 	}
10867dd7cddfSDavid du Colombier 	unlock(&pcicfglock);
10877dd7cddfSDavid du Colombier 
10887dd7cddfSDavid du Colombier 	return x;
10897dd7cddfSDavid du Colombier }
10907dd7cddfSDavid du Colombier 
10917dd7cddfSDavid du Colombier int
10927dd7cddfSDavid du Colombier pcicfgr8(Pcidev* pcidev, int rno)
10937dd7cddfSDavid du Colombier {
10947dd7cddfSDavid du Colombier 	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
10957dd7cddfSDavid du Colombier }
10967dd7cddfSDavid du Colombier 
10977dd7cddfSDavid du Colombier void
10987dd7cddfSDavid du Colombier pcicfgw8(Pcidev* pcidev, int rno, int data)
10997dd7cddfSDavid du Colombier {
11007dd7cddfSDavid du Colombier 	pcicfgrw8(pcidev->tbdf, rno, data, 0);
11017dd7cddfSDavid du Colombier }
11027dd7cddfSDavid du Colombier 
11037dd7cddfSDavid du Colombier static int
11044fafed5dSDavid du Colombier pcicfgrw16raw(int tbdf, int rno, int data, int read)
11057dd7cddfSDavid du Colombier {
11067dd7cddfSDavid du Colombier 	int o, type, x;
11077dd7cddfSDavid du Colombier 
11087dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
11097dd7cddfSDavid du Colombier 		pcicfginit();
11107dd7cddfSDavid du Colombier 
11117dd7cddfSDavid du Colombier 	if(BUSBNO(tbdf))
11127dd7cddfSDavid du Colombier 		type = 0x01;
11137dd7cddfSDavid du Colombier 	else
11147dd7cddfSDavid du Colombier 		type = 0x00;
11157dd7cddfSDavid du Colombier 	x = -1;
11167dd7cddfSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
11177dd7cddfSDavid du Colombier 		return x;
11187dd7cddfSDavid du Colombier 
11197dd7cddfSDavid du Colombier 	lock(&pcicfglock);
11207dd7cddfSDavid du Colombier 	switch(pcicfgmode){
11217dd7cddfSDavid du Colombier 
11227dd7cddfSDavid du Colombier 	case 1:
11237dd7cddfSDavid du Colombier 		o = rno & 0x02;
11247dd7cddfSDavid du Colombier 		rno &= ~0x03;
11257dd7cddfSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
11267dd7cddfSDavid du Colombier 		if(read)
11277dd7cddfSDavid du Colombier 			x = ins(PciDATA+o);
11287dd7cddfSDavid du Colombier 		else
11297dd7cddfSDavid du Colombier 			outs(PciDATA+o, data);
11307dd7cddfSDavid du Colombier 		outl(PciADDR, 0);
11317dd7cddfSDavid du Colombier 		break;
11327dd7cddfSDavid du Colombier 
11337dd7cddfSDavid du Colombier 	case 2:
11347dd7cddfSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
11357dd7cddfSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
11367dd7cddfSDavid du Colombier 		if(read)
11377dd7cddfSDavid du Colombier 			x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
11387dd7cddfSDavid du Colombier 		else
11397dd7cddfSDavid du Colombier 			outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
11407dd7cddfSDavid du Colombier 		outb(PciCSE, 0);
11417dd7cddfSDavid du Colombier 		break;
11427dd7cddfSDavid du Colombier 	}
11437dd7cddfSDavid du Colombier 	unlock(&pcicfglock);
11447dd7cddfSDavid du Colombier 
11457dd7cddfSDavid du Colombier 	return x;
11467dd7cddfSDavid du Colombier }
11477dd7cddfSDavid du Colombier 
11487dd7cddfSDavid du Colombier int
11497dd7cddfSDavid du Colombier pcicfgr16(Pcidev* pcidev, int rno)
11507dd7cddfSDavid du Colombier {
11517dd7cddfSDavid du Colombier 	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
11527dd7cddfSDavid du Colombier }
11537dd7cddfSDavid du Colombier 
11547dd7cddfSDavid du Colombier void
11557dd7cddfSDavid du Colombier pcicfgw16(Pcidev* pcidev, int rno, int data)
11567dd7cddfSDavid du Colombier {
11577dd7cddfSDavid du Colombier 	pcicfgrw16(pcidev->tbdf, rno, data, 0);
11587dd7cddfSDavid du Colombier }
11597dd7cddfSDavid du Colombier 
11607dd7cddfSDavid du Colombier static int
11614fafed5dSDavid du Colombier pcicfgrw32raw(int tbdf, int rno, int data, int read)
11627dd7cddfSDavid du Colombier {
11637dd7cddfSDavid du Colombier 	int type, x;
11647dd7cddfSDavid du Colombier 
11657dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
11667dd7cddfSDavid du Colombier 		pcicfginit();
11677dd7cddfSDavid du Colombier 
11687dd7cddfSDavid du Colombier 	if(BUSBNO(tbdf))
11697dd7cddfSDavid du Colombier 		type = 0x01;
11707dd7cddfSDavid du Colombier 	else
11717dd7cddfSDavid du Colombier 		type = 0x00;
11727dd7cddfSDavid du Colombier 	x = -1;
11737dd7cddfSDavid du Colombier 	if(BUSDNO(tbdf) > pcimaxdno)
11747dd7cddfSDavid du Colombier 		return x;
11757dd7cddfSDavid du Colombier 
11767dd7cddfSDavid du Colombier 	lock(&pcicfglock);
11777dd7cddfSDavid du Colombier 	switch(pcicfgmode){
11787dd7cddfSDavid du Colombier 
11797dd7cddfSDavid du Colombier 	case 1:
11807dd7cddfSDavid du Colombier 		rno &= ~0x03;
11817dd7cddfSDavid du Colombier 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
11827dd7cddfSDavid du Colombier 		if(read)
11837dd7cddfSDavid du Colombier 			x = inl(PciDATA);
11847dd7cddfSDavid du Colombier 		else
11857dd7cddfSDavid du Colombier 			outl(PciDATA, data);
11867dd7cddfSDavid du Colombier 		outl(PciADDR, 0);
11877dd7cddfSDavid du Colombier 		break;
11887dd7cddfSDavid du Colombier 
11897dd7cddfSDavid du Colombier 	case 2:
11907dd7cddfSDavid du Colombier 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
11917dd7cddfSDavid du Colombier 		outb(PciFORWARD, BUSBNO(tbdf));
11927dd7cddfSDavid du Colombier 		if(read)
11937dd7cddfSDavid du Colombier 			x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
11947dd7cddfSDavid du Colombier 		else
11957dd7cddfSDavid du Colombier 			outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
11967dd7cddfSDavid du Colombier 		outb(PciCSE, 0);
11977dd7cddfSDavid du Colombier 		break;
11987dd7cddfSDavid du Colombier 	}
11997dd7cddfSDavid du Colombier 	unlock(&pcicfglock);
12007dd7cddfSDavid du Colombier 
12017dd7cddfSDavid du Colombier 	return x;
12027dd7cddfSDavid du Colombier }
12037dd7cddfSDavid du Colombier 
12047dd7cddfSDavid du Colombier int
12057dd7cddfSDavid du Colombier pcicfgr32(Pcidev* pcidev, int rno)
12067dd7cddfSDavid du Colombier {
12077dd7cddfSDavid du Colombier 	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
12087dd7cddfSDavid du Colombier }
12097dd7cddfSDavid du Colombier 
12107dd7cddfSDavid du Colombier void
12117dd7cddfSDavid du Colombier pcicfgw32(Pcidev* pcidev, int rno, int data)
12127dd7cddfSDavid du Colombier {
12137dd7cddfSDavid du Colombier 	pcicfgrw32(pcidev->tbdf, rno, data, 0);
12147dd7cddfSDavid du Colombier }
12157dd7cddfSDavid du Colombier 
12167dd7cddfSDavid du Colombier Pcidev*
12177dd7cddfSDavid du Colombier pcimatch(Pcidev* prev, int vid, int did)
12187dd7cddfSDavid du Colombier {
12197dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
12207dd7cddfSDavid du Colombier 		pcicfginit();
12217dd7cddfSDavid du Colombier 
12227dd7cddfSDavid du Colombier 	if(prev == nil)
12237dd7cddfSDavid du Colombier 		prev = pcilist;
12247dd7cddfSDavid du Colombier 	else
12257dd7cddfSDavid du Colombier 		prev = prev->list;
12267dd7cddfSDavid du Colombier 
12277dd7cddfSDavid du Colombier 	while(prev != nil){
12287dd7cddfSDavid du Colombier 		if((vid == 0 || prev->vid == vid)
12297dd7cddfSDavid du Colombier 		&& (did == 0 || prev->did == did))
12307dd7cddfSDavid du Colombier 			break;
12317dd7cddfSDavid du Colombier 		prev = prev->list;
12327dd7cddfSDavid du Colombier 	}
12337dd7cddfSDavid du Colombier 	return prev;
12347dd7cddfSDavid du Colombier }
12357dd7cddfSDavid du Colombier 
12367dd7cddfSDavid du Colombier Pcidev*
12377dd7cddfSDavid du Colombier pcimatchtbdf(int tbdf)
12387dd7cddfSDavid du Colombier {
12397dd7cddfSDavid du Colombier 	Pcidev *pcidev;
12407dd7cddfSDavid du Colombier 
12417dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
12427dd7cddfSDavid du Colombier 		pcicfginit();
12437dd7cddfSDavid du Colombier 
12447dd7cddfSDavid du Colombier 	for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
12457dd7cddfSDavid du Colombier 		if(pcidev->tbdf == tbdf)
12467dd7cddfSDavid du Colombier 			break;
12477dd7cddfSDavid du Colombier 	}
12487dd7cddfSDavid du Colombier 	return pcidev;
12497dd7cddfSDavid du Colombier }
12507dd7cddfSDavid du Colombier 
12519a747e4fSDavid du Colombier uchar
12529a747e4fSDavid du Colombier pciipin(Pcidev *pci, uchar pin)
12539a747e4fSDavid du Colombier {
12549a747e4fSDavid du Colombier 	if (pci == nil)
12559a747e4fSDavid du Colombier 		pci = pcilist;
12569a747e4fSDavid du Colombier 
12579a747e4fSDavid du Colombier 	while (pci) {
12589a747e4fSDavid du Colombier 		uchar intl;
12599a747e4fSDavid du Colombier 
12609a747e4fSDavid du Colombier 		if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
12619a747e4fSDavid du Colombier 			return pci->intl;
12629a747e4fSDavid du Colombier 
12639a747e4fSDavid du Colombier 		if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
12649a747e4fSDavid du Colombier 			return intl;
12659a747e4fSDavid du Colombier 
12669a747e4fSDavid du Colombier 		pci = pci->list;
12679a747e4fSDavid du Colombier 	}
12689a747e4fSDavid du Colombier 	return 0;
12699a747e4fSDavid du Colombier }
12709a747e4fSDavid du Colombier 
12719a747e4fSDavid du Colombier static void
12729a747e4fSDavid du Colombier pcilhinv(Pcidev* p)
12737dd7cddfSDavid du Colombier {
12747dd7cddfSDavid du Colombier 	int i;
12757dd7cddfSDavid du Colombier 	Pcidev *t;
12767dd7cddfSDavid du Colombier 
12777dd7cddfSDavid du Colombier 	if(p == nil) {
12787dd7cddfSDavid du Colombier 		putstrn(PCICONS.output, PCICONS.ptr);
12797dd7cddfSDavid du Colombier 		p = pciroot;
12807dd7cddfSDavid du Colombier 		print("bus dev type vid  did intl memory\n");
12817dd7cddfSDavid du Colombier 	}
12827dd7cddfSDavid du Colombier 	for(t = p; t != nil; t = t->link) {
12837dd7cddfSDavid du Colombier 		print("%d  %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d  ",
12847dd7cddfSDavid du Colombier 			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
12857dd7cddfSDavid du Colombier 			t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
12867dd7cddfSDavid du Colombier 
12877dd7cddfSDavid du Colombier 		for(i = 0; i < nelem(p->mem); i++) {
12887dd7cddfSDavid du Colombier 			if(t->mem[i].size == 0)
12897dd7cddfSDavid du Colombier 				continue;
12907dd7cddfSDavid du Colombier 			print("%d:%.8lux %d ", i,
12917dd7cddfSDavid du Colombier 				t->mem[i].bar, t->mem[i].size);
12927dd7cddfSDavid du Colombier 		}
12937dd7cddfSDavid du Colombier 		if(t->ioa.bar || t->ioa.size)
12947dd7cddfSDavid du Colombier 			print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
12957dd7cddfSDavid du Colombier 		if(t->mema.bar || t->mema.size)
12967dd7cddfSDavid du Colombier 			print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
12977dd7cddfSDavid du Colombier 		if(t->bridge)
12987dd7cddfSDavid du Colombier 			print("->%d", BUSBNO(t->bridge->tbdf));
12997dd7cddfSDavid du Colombier 		print("\n");
13007dd7cddfSDavid du Colombier 	}
13017dd7cddfSDavid du Colombier 	while(p != nil) {
13027dd7cddfSDavid du Colombier 		if(p->bridge != nil)
13039a747e4fSDavid du Colombier 			pcilhinv(p->bridge);
13047dd7cddfSDavid du Colombier 		p = p->link;
13057dd7cddfSDavid du Colombier 	}
13067dd7cddfSDavid du Colombier }
13077dd7cddfSDavid du Colombier 
13087dd7cddfSDavid du Colombier void
13099a747e4fSDavid du Colombier pcihinv(Pcidev* p)
13109a747e4fSDavid du Colombier {
13119a747e4fSDavid du Colombier 	if(pcicfgmode == -1)
13129a747e4fSDavid du Colombier 		pcicfginit();
13139a747e4fSDavid du Colombier 	lock(&pcicfginitlock);
13149a747e4fSDavid du Colombier 	pcilhinv(p);
13159a747e4fSDavid du Colombier 	unlock(&pcicfginitlock);
13169a747e4fSDavid du Colombier }
13179a747e4fSDavid du Colombier 
13189a747e4fSDavid du Colombier void
13197dd7cddfSDavid du Colombier pcireset(void)
13207dd7cddfSDavid du Colombier {
13217dd7cddfSDavid du Colombier 	Pcidev *p;
13227dd7cddfSDavid du Colombier 
13237dd7cddfSDavid du Colombier 	if(pcicfgmode == -1)
13247dd7cddfSDavid du Colombier 		pcicfginit();
13257dd7cddfSDavid du Colombier 
13267dd7cddfSDavid du Colombier 	for(p = pcilist; p != nil; p = p->list) {
13279a747e4fSDavid du Colombier 		/* don't mess with the bridges */
13289a747e4fSDavid du Colombier 		if(p->ccrb == 0x06)
13299a747e4fSDavid du Colombier 			continue;
13309a747e4fSDavid du Colombier 		pciclrbme(p);
13317dd7cddfSDavid du Colombier 	}
13327dd7cddfSDavid du Colombier }
13337dd7cddfSDavid du Colombier 
13347dd7cddfSDavid du Colombier void
1335220e960cSDavid du Colombier pcisetioe(Pcidev* p)
1336220e960cSDavid du Colombier {
1337e569ccb5SDavid du Colombier 	p->pcr |= IOen;
1338e569ccb5SDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1339220e960cSDavid du Colombier }
1340220e960cSDavid du Colombier 
1341220e960cSDavid du Colombier void
1342220e960cSDavid du Colombier pciclrioe(Pcidev* p)
1343220e960cSDavid du Colombier {
1344e569ccb5SDavid du Colombier 	p->pcr &= ~IOen;
1345e569ccb5SDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1346220e960cSDavid du Colombier }
1347220e960cSDavid du Colombier 
1348220e960cSDavid du Colombier void
13497dd7cddfSDavid du Colombier pcisetbme(Pcidev* p)
13507dd7cddfSDavid du Colombier {
1351e569ccb5SDavid du Colombier 	p->pcr |= MASen;
1352e569ccb5SDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
13537dd7cddfSDavid du Colombier }
13549a747e4fSDavid du Colombier 
13559a747e4fSDavid du Colombier void
13569a747e4fSDavid du Colombier pciclrbme(Pcidev* p)
13579a747e4fSDavid du Colombier {
1358e569ccb5SDavid du Colombier 	p->pcr &= ~MASen;
1359e569ccb5SDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1360e569ccb5SDavid du Colombier }
13619a747e4fSDavid du Colombier 
1362d8f3f65eSDavid du Colombier void
1363d8f3f65eSDavid du Colombier pcisetmwi(Pcidev* p)
1364d8f3f65eSDavid du Colombier {
1365d8f3f65eSDavid du Colombier 	p->pcr |= MemWrInv;
1366d8f3f65eSDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1367d8f3f65eSDavid du Colombier }
1368d8f3f65eSDavid du Colombier 
1369d8f3f65eSDavid du Colombier void
1370d8f3f65eSDavid du Colombier pciclrmwi(Pcidev* p)
1371d8f3f65eSDavid du Colombier {
1372d8f3f65eSDavid du Colombier 	p->pcr &= ~MemWrInv;
1373d8f3f65eSDavid du Colombier 	pcicfgw16(p, PciPCR, p->pcr);
1374d8f3f65eSDavid du Colombier }
1375d8f3f65eSDavid du Colombier 
1376e569ccb5SDavid du Colombier static int
1377e569ccb5SDavid du Colombier pcigetpmrb(Pcidev* p)
1378e569ccb5SDavid du Colombier {
1379e569ccb5SDavid du Colombier 	int ptr;
1380e569ccb5SDavid du Colombier 
1381e569ccb5SDavid du Colombier 	if(p->pmrb != 0)
1382e569ccb5SDavid du Colombier 		return p->pmrb;
1383e569ccb5SDavid du Colombier 	p->pmrb = -1;
1384e569ccb5SDavid du Colombier 
1385e569ccb5SDavid du Colombier 	/*
1386e569ccb5SDavid du Colombier 	 * If there are no extended capabilities implemented,
1387e569ccb5SDavid du Colombier 	 * (bit 4 in the status register) assume there's no standard
1388e569ccb5SDavid du Colombier 	 * power management method.
1389e569ccb5SDavid du Colombier 	 * Find the capabilities pointer based on PCI header type.
1390e569ccb5SDavid du Colombier 	 */
13915979f962SDavid du Colombier 	if(!(pcicfgr16(p, PciPSR) & 0x0010))
1392e569ccb5SDavid du Colombier 		return -1;
1393e569ccb5SDavid du Colombier 	switch(pcicfgr8(p, PciHDT)){
1394e569ccb5SDavid du Colombier 	default:
1395e569ccb5SDavid du Colombier 		return -1;
1396e569ccb5SDavid du Colombier 	case 0:					/* all other */
1397e569ccb5SDavid du Colombier 	case 1:					/* PCI to PCI bridge */
1398e569ccb5SDavid du Colombier 		ptr = 0x34;
1399e569ccb5SDavid du Colombier 		break;
1400e569ccb5SDavid du Colombier 	case 2:					/* CardBus bridge */
1401e569ccb5SDavid du Colombier 		ptr = 0x14;
1402e569ccb5SDavid du Colombier 		break;
1403e569ccb5SDavid du Colombier 	}
1404e569ccb5SDavid du Colombier 	ptr = pcicfgr32(p, ptr);
1405e569ccb5SDavid du Colombier 
1406e569ccb5SDavid du Colombier 	while(ptr != 0){
1407e569ccb5SDavid du Colombier 		/*
1408e569ccb5SDavid du Colombier 		 * Check for validity.
1409e569ccb5SDavid du Colombier 		 * Can't be in standard header and must be double
1410e569ccb5SDavid du Colombier 		 * word aligned.
1411e569ccb5SDavid du Colombier 		 */
1412e569ccb5SDavid du Colombier 		if(ptr < 0x40 || (ptr & ~0xFC))
1413e569ccb5SDavid du Colombier 			return -1;
1414e569ccb5SDavid du Colombier 		if(pcicfgr8(p, ptr) == 0x01){
1415e569ccb5SDavid du Colombier 			p->pmrb = ptr;
1416e569ccb5SDavid du Colombier 			return ptr;
1417e569ccb5SDavid du Colombier 		}
1418e569ccb5SDavid du Colombier 
1419e569ccb5SDavid du Colombier 		ptr = pcicfgr8(p, ptr+1);
1420e569ccb5SDavid du Colombier 	}
1421e569ccb5SDavid du Colombier 
1422e569ccb5SDavid du Colombier 	return -1;
1423e569ccb5SDavid du Colombier }
1424e569ccb5SDavid du Colombier 
1425e569ccb5SDavid du Colombier int
1426e569ccb5SDavid du Colombier pcigetpms(Pcidev* p)
1427e569ccb5SDavid du Colombier {
1428e569ccb5SDavid du Colombier 	int pmcsr, ptr;
1429e569ccb5SDavid du Colombier 
1430e569ccb5SDavid du Colombier 	if((ptr = pcigetpmrb(p)) == -1)
1431e569ccb5SDavid du Colombier 		return -1;
1432e569ccb5SDavid du Colombier 
1433e569ccb5SDavid du Colombier 	/*
1434e569ccb5SDavid du Colombier 	 * Power Management Register Block:
1435e569ccb5SDavid du Colombier 	 *  offset 0:	Capability ID
1436e569ccb5SDavid du Colombier 	 *	   1:	next item pointer
1437e569ccb5SDavid du Colombier 	 *	   2:	capabilities
1438e569ccb5SDavid du Colombier 	 *	   4:	control/status
1439e569ccb5SDavid du Colombier 	 *	   6:	bridge support extensions
1440e569ccb5SDavid du Colombier 	 *	   7:	data
1441e569ccb5SDavid du Colombier 	 */
1442e569ccb5SDavid du Colombier 	pmcsr = pcicfgr16(p, ptr+4);
1443e569ccb5SDavid du Colombier 
1444e569ccb5SDavid du Colombier 	return pmcsr & 0x0003;
1445e569ccb5SDavid du Colombier }
1446e569ccb5SDavid du Colombier 
1447e569ccb5SDavid du Colombier int
1448e569ccb5SDavid du Colombier pcisetpms(Pcidev* p, int state)
1449e569ccb5SDavid du Colombier {
1450e569ccb5SDavid du Colombier 	int ostate, pmc, pmcsr, ptr;
1451e569ccb5SDavid du Colombier 
1452e569ccb5SDavid du Colombier 	if((ptr = pcigetpmrb(p)) == -1)
1453e569ccb5SDavid du Colombier 		return -1;
1454e569ccb5SDavid du Colombier 
1455e569ccb5SDavid du Colombier 	pmc = pcicfgr16(p, ptr+2);
1456e569ccb5SDavid du Colombier 	pmcsr = pcicfgr16(p, ptr+4);
1457e569ccb5SDavid du Colombier 	ostate = pmcsr & 0x0003;
1458e569ccb5SDavid du Colombier 	pmcsr &= ~0x0003;
1459e569ccb5SDavid du Colombier 
1460e569ccb5SDavid du Colombier 	switch(state){
1461e569ccb5SDavid du Colombier 	default:
1462e569ccb5SDavid du Colombier 		return -1;
1463e569ccb5SDavid du Colombier 	case 0:
1464e569ccb5SDavid du Colombier 		break;
1465e569ccb5SDavid du Colombier 	case 1:
1466e569ccb5SDavid du Colombier 		if(!(pmc & 0x0200))
1467e569ccb5SDavid du Colombier 			return -1;
1468e569ccb5SDavid du Colombier 		break;
1469e569ccb5SDavid du Colombier 	case 2:
1470e569ccb5SDavid du Colombier 		if(!(pmc & 0x0400))
1471e569ccb5SDavid du Colombier 			return -1;
1472e569ccb5SDavid du Colombier 		break;
1473e569ccb5SDavid du Colombier 	case 3:
1474e569ccb5SDavid du Colombier 		break;
1475e569ccb5SDavid du Colombier 	}
1476e569ccb5SDavid du Colombier 	pmcsr |= state;
1477e569ccb5SDavid du Colombier 	pcicfgw16(p, ptr+4, pmcsr);
1478e569ccb5SDavid du Colombier 
1479e569ccb5SDavid du Colombier 	return ostate;
14809a747e4fSDavid du Colombier }
1481