xref: /inferno-os/os/boot/pc/pci.c (revision 8a8c2d742b51525f66c2210e3c8a251de10022ff)
174a4d8c2SCharles.Forsyth /*
274a4d8c2SCharles.Forsyth  * PCI support code.
374a4d8c2SCharles.Forsyth  * To do:
474a4d8c2SCharles.Forsyth  *	initialise bridge mappings if the PCI BIOS didn't.
574a4d8c2SCharles.Forsyth  */
674a4d8c2SCharles.Forsyth #include "u.h"
774a4d8c2SCharles.Forsyth #include "lib.h"
874a4d8c2SCharles.Forsyth #include "mem.h"
974a4d8c2SCharles.Forsyth #include "dat.h"
1074a4d8c2SCharles.Forsyth #include "fns.h"
1174a4d8c2SCharles.Forsyth #include "io.h"
1274a4d8c2SCharles.Forsyth #include "error.h"
1374a4d8c2SCharles.Forsyth 
1474a4d8c2SCharles.Forsyth enum {					/* configuration mechanism #1 */
1574a4d8c2SCharles.Forsyth 	PciADDR		= 0xCF8,	/* CONFIG_ADDRESS */
1674a4d8c2SCharles.Forsyth 	PciDATA		= 0xCFC,	/* CONFIG_DATA */
1774a4d8c2SCharles.Forsyth 
1874a4d8c2SCharles.Forsyth 					/* configuration mechanism #2 */
1974a4d8c2SCharles.Forsyth 	PciCSE		= 0xCF8,	/* configuration space enable */
2074a4d8c2SCharles.Forsyth 	PciFORWARD	= 0xCFA,	/* which bus */
2174a4d8c2SCharles.Forsyth 
2274a4d8c2SCharles.Forsyth 	MaxFNO		= 7,
2374a4d8c2SCharles.Forsyth 	MaxUBN		= 255,
2474a4d8c2SCharles.Forsyth };
2574a4d8c2SCharles.Forsyth 
26*8a8c2d74SCharles.Forsyth enum
27*8a8c2d74SCharles.Forsyth {					/* command register */
28*8a8c2d74SCharles.Forsyth 	IOen		= (1<<0),
29*8a8c2d74SCharles.Forsyth 	MEMen		= (1<<1),
30*8a8c2d74SCharles.Forsyth 	MASen		= (1<<2),
31*8a8c2d74SCharles.Forsyth 	MemWrInv	= (1<<4),
32*8a8c2d74SCharles.Forsyth 	PErrEn		= (1<<6),
33*8a8c2d74SCharles.Forsyth 	SErrEn		= (1<<8),
34*8a8c2d74SCharles.Forsyth };
35*8a8c2d74SCharles.Forsyth 
3674a4d8c2SCharles.Forsyth static Lock pcicfglock;
3774a4d8c2SCharles.Forsyth static Lock pcicfginitlock;
3874a4d8c2SCharles.Forsyth static int pcicfgmode = -1;
3974a4d8c2SCharles.Forsyth static int pcimaxbno = 7;
4074a4d8c2SCharles.Forsyth static int pcimaxdno;
4174a4d8c2SCharles.Forsyth static Pcidev* pciroot;
4274a4d8c2SCharles.Forsyth static Pcidev* pcilist;
4374a4d8c2SCharles.Forsyth static Pcidev* pcitail;
4474a4d8c2SCharles.Forsyth 
4574a4d8c2SCharles.Forsyth static int pcicfgrw32(int, int, int, int);
4674a4d8c2SCharles.Forsyth static int pcicfgrw8(int, int, int, int);
4774a4d8c2SCharles.Forsyth 
4874a4d8c2SCharles.Forsyth ulong
pcibarsize(Pcidev * p,int rno)4974a4d8c2SCharles.Forsyth pcibarsize(Pcidev *p, int rno)
5074a4d8c2SCharles.Forsyth {
5174a4d8c2SCharles.Forsyth 	ulong v, size;
5274a4d8c2SCharles.Forsyth 
5374a4d8c2SCharles.Forsyth 	v = pcicfgrw32(p->tbdf, rno, 0, 1);
5474a4d8c2SCharles.Forsyth 	pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
5574a4d8c2SCharles.Forsyth 	size = pcicfgrw32(p->tbdf, rno, 0, 1);
5674a4d8c2SCharles.Forsyth 	if(v & 1)
5774a4d8c2SCharles.Forsyth 		size |= 0xFFFF0000;
5874a4d8c2SCharles.Forsyth 	pcicfgrw32(p->tbdf, rno, v, 0);
5974a4d8c2SCharles.Forsyth 
6074a4d8c2SCharles.Forsyth 	return -(size & ~0x0F);
6174a4d8c2SCharles.Forsyth }
6274a4d8c2SCharles.Forsyth 
6374a4d8c2SCharles.Forsyth int
pciscan(int bno,Pcidev ** list)6474a4d8c2SCharles.Forsyth pciscan(int bno, Pcidev** list)
6574a4d8c2SCharles.Forsyth {
6674a4d8c2SCharles.Forsyth 	Pcidev *p, *head, *tail;
6774a4d8c2SCharles.Forsyth 	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
6874a4d8c2SCharles.Forsyth 
6974a4d8c2SCharles.Forsyth 	maxubn = bno;
7074a4d8c2SCharles.Forsyth 	head = nil;
7174a4d8c2SCharles.Forsyth 	tail = nil;
7274a4d8c2SCharles.Forsyth 	for(dno = 0; dno <= pcimaxdno; dno++){
7374a4d8c2SCharles.Forsyth 		maxfno = 0;
7474a4d8c2SCharles.Forsyth 		for(fno = 0; fno <= maxfno; fno++){
7574a4d8c2SCharles.Forsyth 			/*
7674a4d8c2SCharles.Forsyth 			 * For this possible device, form the
7774a4d8c2SCharles.Forsyth 			 * bus+device+function triplet needed to address it
7874a4d8c2SCharles.Forsyth 			 * and try to read the vendor and device ID.
7974a4d8c2SCharles.Forsyth 			 * If successful, allocate a device struct and
8074a4d8c2SCharles.Forsyth 			 * start to fill it in with some useful information
8174a4d8c2SCharles.Forsyth 			 * from the device's configuration space.
8274a4d8c2SCharles.Forsyth 			 */
8374a4d8c2SCharles.Forsyth 			tbdf = MKBUS(BusPCI, bno, dno, fno);
8474a4d8c2SCharles.Forsyth 			l = pcicfgrw32(tbdf, PciVID, 0, 1);
8574a4d8c2SCharles.Forsyth 			if(l == 0xFFFFFFFF || l == 0)
8674a4d8c2SCharles.Forsyth 				continue;
8774a4d8c2SCharles.Forsyth 			p = malloc(sizeof(*p));
8874a4d8c2SCharles.Forsyth 			p->tbdf = tbdf;
8974a4d8c2SCharles.Forsyth 			p->vid = l;
9074a4d8c2SCharles.Forsyth 			p->did = l>>16;
9174a4d8c2SCharles.Forsyth 
9274a4d8c2SCharles.Forsyth 			if(pcilist != nil)
9374a4d8c2SCharles.Forsyth 				pcitail->list = p;
9474a4d8c2SCharles.Forsyth 			else
9574a4d8c2SCharles.Forsyth 				pcilist = p;
9674a4d8c2SCharles.Forsyth 			pcitail = p;
9774a4d8c2SCharles.Forsyth 
9874a4d8c2SCharles.Forsyth 			p->rid = pcicfgr8(p, PciRID);
9974a4d8c2SCharles.Forsyth 			p->ccrp = pcicfgr8(p, PciCCRp);
10074a4d8c2SCharles.Forsyth 			p->ccru = pcicfgr8(p, PciCCRu);
10174a4d8c2SCharles.Forsyth 			p->ccrb = pcicfgr8(p, PciCCRb);
10274a4d8c2SCharles.Forsyth 			p->pcr = pcicfgr32(p, PciPCR);
10374a4d8c2SCharles.Forsyth 
10474a4d8c2SCharles.Forsyth 			p->intl = pcicfgr8(p, PciINTL);
10574a4d8c2SCharles.Forsyth 
10674a4d8c2SCharles.Forsyth 			/*
10774a4d8c2SCharles.Forsyth 			 * If the device is a multi-function device adjust the
10874a4d8c2SCharles.Forsyth 			 * loop count so all possible functions are checked.
10974a4d8c2SCharles.Forsyth 			 */
11074a4d8c2SCharles.Forsyth 			hdt = pcicfgr8(p, PciHDT);
11174a4d8c2SCharles.Forsyth 			if(hdt & 0x80)
11274a4d8c2SCharles.Forsyth 				maxfno = MaxFNO;
11374a4d8c2SCharles.Forsyth 
11474a4d8c2SCharles.Forsyth 			/*
11574a4d8c2SCharles.Forsyth 			 * If appropriate, read the base address registers
11674a4d8c2SCharles.Forsyth 			 * and work out the sizes.
11774a4d8c2SCharles.Forsyth 			 */
11874a4d8c2SCharles.Forsyth 			switch(p->ccrb){
11974a4d8c2SCharles.Forsyth 
12074a4d8c2SCharles.Forsyth 			case 0x01:		/* mass storage controller */
12174a4d8c2SCharles.Forsyth 			case 0x02:		/* network controller */
12274a4d8c2SCharles.Forsyth 			case 0x03:		/* display controller */
12374a4d8c2SCharles.Forsyth 			case 0x04:		/* multimedia device */
12474a4d8c2SCharles.Forsyth 			case 0x07:		/* simple comm. controllers */
12574a4d8c2SCharles.Forsyth 			case 0x08:		/* base system peripherals */
12674a4d8c2SCharles.Forsyth 			case 0x09:		/* input devices */
12774a4d8c2SCharles.Forsyth 			case 0x0A:		/* docking stations */
12874a4d8c2SCharles.Forsyth 			case 0x0B:		/* processors */
12974a4d8c2SCharles.Forsyth 			case 0x0C:		/* serial bus controllers */
13074a4d8c2SCharles.Forsyth 				if((hdt & 0x7F) != 0)
13174a4d8c2SCharles.Forsyth 					break;
13274a4d8c2SCharles.Forsyth 				rno = PciBAR0 - 4;
13374a4d8c2SCharles.Forsyth 				for(i = 0; i < nelem(p->mem); i++){
13474a4d8c2SCharles.Forsyth 					rno += 4;
13574a4d8c2SCharles.Forsyth 					p->mem[i].bar = pcicfgr32(p, rno);
13674a4d8c2SCharles.Forsyth 					p->mem[i].size = pcibarsize(p, rno);
13774a4d8c2SCharles.Forsyth 				}
13874a4d8c2SCharles.Forsyth 				break;
13974a4d8c2SCharles.Forsyth 
14074a4d8c2SCharles.Forsyth 			case 0x00:
14174a4d8c2SCharles.Forsyth 			case 0x05:		/* memory controller */
14274a4d8c2SCharles.Forsyth 			case 0x06:		/* bridge device */
14374a4d8c2SCharles.Forsyth 			default:
14474a4d8c2SCharles.Forsyth 				break;
14574a4d8c2SCharles.Forsyth 			}
14674a4d8c2SCharles.Forsyth 
14774a4d8c2SCharles.Forsyth 			if(head != nil)
14874a4d8c2SCharles.Forsyth 				tail->link = p;
14974a4d8c2SCharles.Forsyth 			else
15074a4d8c2SCharles.Forsyth 				head = p;
15174a4d8c2SCharles.Forsyth 			tail = p;
15274a4d8c2SCharles.Forsyth 		}
15374a4d8c2SCharles.Forsyth 	}
15474a4d8c2SCharles.Forsyth 
15574a4d8c2SCharles.Forsyth 	*list = head;
15674a4d8c2SCharles.Forsyth 	for(p = head; p != nil; p = p->link){
15774a4d8c2SCharles.Forsyth 		/*
158*8a8c2d74SCharles.Forsyth 		 * Find PCI-PCI and PCI-Cardbus bridges
159*8a8c2d74SCharles.Forsyth 		 * and recursively descend the tree.
16074a4d8c2SCharles.Forsyth 		 */
16174a4d8c2SCharles.Forsyth 		if(p->ccrb != 0x06 || p->ccru != 0x04)
16274a4d8c2SCharles.Forsyth 			continue;
16374a4d8c2SCharles.Forsyth 
16474a4d8c2SCharles.Forsyth 		/*
16574a4d8c2SCharles.Forsyth 		 * If the secondary or subordinate bus number is not
16674a4d8c2SCharles.Forsyth 		 * initialised try to do what the PCI BIOS should have
16774a4d8c2SCharles.Forsyth 		 * done and fill in the numbers as the tree is descended.
16874a4d8c2SCharles.Forsyth 		 * On the way down the subordinate bus number is set to
16974a4d8c2SCharles.Forsyth 		 * the maximum as it's not known how many buses are behind
17074a4d8c2SCharles.Forsyth 		 * this one; the final value is set on the way back up.
17174a4d8c2SCharles.Forsyth 		 */
17274a4d8c2SCharles.Forsyth 		ubn = pcicfgr8(p, PciUBN);
17374a4d8c2SCharles.Forsyth 		sbn = pcicfgr8(p, PciSBN);
17474a4d8c2SCharles.Forsyth 
17574a4d8c2SCharles.Forsyth 		if(sbn == 0 || ubn == 0){
17674a4d8c2SCharles.Forsyth 			sbn = maxubn+1;
17774a4d8c2SCharles.Forsyth 			/*
17874a4d8c2SCharles.Forsyth 			 * Make sure memory, I/O and master enables are
17974a4d8c2SCharles.Forsyth 			 * off, set the primary, secondary and subordinate
18074a4d8c2SCharles.Forsyth 			 * bus numbers and clear the secondary status before
18174a4d8c2SCharles.Forsyth 			 * attempting to scan the secondary bus.
18274a4d8c2SCharles.Forsyth 			 *
18374a4d8c2SCharles.Forsyth 			 * Initialisation of the bridge should be done here.
18474a4d8c2SCharles.Forsyth 			 */
18574a4d8c2SCharles.Forsyth 			pcicfgw32(p, PciPCR, 0xFFFF0000);
18674a4d8c2SCharles.Forsyth 			l = (MaxUBN<<16)|(sbn<<8)|bno;
18774a4d8c2SCharles.Forsyth 			pcicfgw32(p, PciPBN, l);
18874a4d8c2SCharles.Forsyth 			pcicfgw16(p, PciSPSR, 0xFFFF);
18974a4d8c2SCharles.Forsyth 			maxubn = pciscan(sbn, &p->bridge);
19074a4d8c2SCharles.Forsyth 			l = (maxubn<<16)|(sbn<<8)|bno;
19174a4d8c2SCharles.Forsyth 
19274a4d8c2SCharles.Forsyth 			pcicfgw32(p, PciPBN, l);
19374a4d8c2SCharles.Forsyth 		}
19474a4d8c2SCharles.Forsyth 		else{
195*8a8c2d74SCharles.Forsyth 			/*
196*8a8c2d74SCharles.Forsyth 			 * You can't go back.
197*8a8c2d74SCharles.Forsyth 			 * This shouldn't be possible, but the
198*8a8c2d74SCharles.Forsyth 			 * Iwill DK8-HTX seems to have subordinate
199*8a8c2d74SCharles.Forsyth 			 * bus numbers which get smaller on the
200*8a8c2d74SCharles.Forsyth 			 * way down. Need to look more closely at
201*8a8c2d74SCharles.Forsyth 			 * this.
202*8a8c2d74SCharles.Forsyth 			 */
203*8a8c2d74SCharles.Forsyth 			if(ubn > maxubn)
20474a4d8c2SCharles.Forsyth 				maxubn = ubn;
20574a4d8c2SCharles.Forsyth 			pciscan(sbn, &p->bridge);
20674a4d8c2SCharles.Forsyth 		}
20774a4d8c2SCharles.Forsyth 	}
20874a4d8c2SCharles.Forsyth 
20974a4d8c2SCharles.Forsyth 	return maxubn;
21074a4d8c2SCharles.Forsyth }
21174a4d8c2SCharles.Forsyth 
21274a4d8c2SCharles.Forsyth static uchar
null_link(Pcidev *,uchar)213*8a8c2d74SCharles.Forsyth null_link(Pcidev *, uchar )
214*8a8c2d74SCharles.Forsyth {
215*8a8c2d74SCharles.Forsyth 	return 0;
216*8a8c2d74SCharles.Forsyth }
217*8a8c2d74SCharles.Forsyth 
218*8a8c2d74SCharles.Forsyth static void
null_init(Pcidev *,uchar,uchar)219*8a8c2d74SCharles.Forsyth null_init(Pcidev *, uchar , uchar )
220*8a8c2d74SCharles.Forsyth {
221*8a8c2d74SCharles.Forsyth }
222*8a8c2d74SCharles.Forsyth 
223*8a8c2d74SCharles.Forsyth static uchar
pIIx_link(Pcidev * router,uchar link)22474a4d8c2SCharles.Forsyth pIIx_link(Pcidev *router, uchar link)
22574a4d8c2SCharles.Forsyth {
22674a4d8c2SCharles.Forsyth 	uchar pirq;
22774a4d8c2SCharles.Forsyth 
22874a4d8c2SCharles.Forsyth 	/* link should be 0x60, 0x61, 0x62, 0x63 */
22974a4d8c2SCharles.Forsyth 	pirq = pcicfgr8(router, link);
23074a4d8c2SCharles.Forsyth 	return (pirq < 16)? pirq: 0;
23174a4d8c2SCharles.Forsyth }
23274a4d8c2SCharles.Forsyth 
23374a4d8c2SCharles.Forsyth static void
pIIx_init(Pcidev * router,uchar link,uchar irq)23474a4d8c2SCharles.Forsyth pIIx_init(Pcidev *router, uchar link, uchar irq)
23574a4d8c2SCharles.Forsyth {
23674a4d8c2SCharles.Forsyth 	pcicfgw8(router, link, irq);
23774a4d8c2SCharles.Forsyth }
23874a4d8c2SCharles.Forsyth 
23974a4d8c2SCharles.Forsyth static uchar
via_link(Pcidev * router,uchar link)24074a4d8c2SCharles.Forsyth via_link(Pcidev *router, uchar link)
24174a4d8c2SCharles.Forsyth {
24274a4d8c2SCharles.Forsyth 	uchar pirq;
24374a4d8c2SCharles.Forsyth 
24474a4d8c2SCharles.Forsyth 	/* link should be 1, 2, 3, 5 */
24574a4d8c2SCharles.Forsyth 	pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0;
24674a4d8c2SCharles.Forsyth 
24774a4d8c2SCharles.Forsyth 	return (link & 1)? (pirq >> 4): (pirq & 15);
24874a4d8c2SCharles.Forsyth }
24974a4d8c2SCharles.Forsyth 
25074a4d8c2SCharles.Forsyth static void
via_init(Pcidev * router,uchar link,uchar irq)25174a4d8c2SCharles.Forsyth via_init(Pcidev *router, uchar link, uchar irq)
25274a4d8c2SCharles.Forsyth {
25374a4d8c2SCharles.Forsyth 	uchar pirq;
25474a4d8c2SCharles.Forsyth 
25574a4d8c2SCharles.Forsyth 	pirq = pcicfgr8(router, 0x55 + (link >> 1));
25674a4d8c2SCharles.Forsyth 	pirq &= (link & 1)? 0x0f: 0xf0;
25774a4d8c2SCharles.Forsyth 	pirq |= (link & 1)? (irq << 4): (irq & 15);
25874a4d8c2SCharles.Forsyth 	pcicfgw8(router, 0x55 + (link>>1), pirq);
25974a4d8c2SCharles.Forsyth }
26074a4d8c2SCharles.Forsyth 
26174a4d8c2SCharles.Forsyth static uchar
opti_link(Pcidev * router,uchar link)26274a4d8c2SCharles.Forsyth opti_link(Pcidev *router, uchar link)
26374a4d8c2SCharles.Forsyth {
26474a4d8c2SCharles.Forsyth 	uchar pirq = 0;
26574a4d8c2SCharles.Forsyth 
26674a4d8c2SCharles.Forsyth 	/* link should be 0x02, 0x12, 0x22, 0x32 */
26774a4d8c2SCharles.Forsyth 	if ((link & 0xcf) == 0x02)
26874a4d8c2SCharles.Forsyth 		pirq = pcicfgr8(router, 0xb8 + (link >> 5));
26974a4d8c2SCharles.Forsyth 	return (link & 0x10)? (pirq >> 4): (pirq & 15);
27074a4d8c2SCharles.Forsyth }
27174a4d8c2SCharles.Forsyth 
27274a4d8c2SCharles.Forsyth static void
opti_init(Pcidev * router,uchar link,uchar irq)27374a4d8c2SCharles.Forsyth opti_init(Pcidev *router, uchar link, uchar irq)
27474a4d8c2SCharles.Forsyth {
27574a4d8c2SCharles.Forsyth 	uchar pirq;
27674a4d8c2SCharles.Forsyth 
27774a4d8c2SCharles.Forsyth 	pirq = pcicfgr8(router, 0xb8 + (link >> 5));
27874a4d8c2SCharles.Forsyth     	pirq &= (link & 0x10)? 0x0f : 0xf0;
27974a4d8c2SCharles.Forsyth     	pirq |= (link & 0x10)? (irq << 4): (irq & 15);
28074a4d8c2SCharles.Forsyth 	pcicfgw8(router, 0xb8 + (link >> 5), pirq);
28174a4d8c2SCharles.Forsyth }
28274a4d8c2SCharles.Forsyth 
28374a4d8c2SCharles.Forsyth static uchar
ali_link(Pcidev * router,uchar link)28474a4d8c2SCharles.Forsyth ali_link(Pcidev *router, uchar link)
28574a4d8c2SCharles.Forsyth {
28674a4d8c2SCharles.Forsyth 	/* No, you're not dreaming */
28774a4d8c2SCharles.Forsyth 	static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
28874a4d8c2SCharles.Forsyth 	uchar pirq;
28974a4d8c2SCharles.Forsyth 
29074a4d8c2SCharles.Forsyth 	/* link should be 0x01..0x08 */
29174a4d8c2SCharles.Forsyth 	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
29274a4d8c2SCharles.Forsyth 	return (link & 1)? map[pirq&15]: map[pirq>>4];
29374a4d8c2SCharles.Forsyth }
29474a4d8c2SCharles.Forsyth 
29574a4d8c2SCharles.Forsyth static void
ali_init(Pcidev * router,uchar link,uchar irq)29674a4d8c2SCharles.Forsyth ali_init(Pcidev *router, uchar link, uchar irq)
29774a4d8c2SCharles.Forsyth {
29874a4d8c2SCharles.Forsyth 	/* Inverse of map in ali_link */
29974a4d8c2SCharles.Forsyth 	static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
30074a4d8c2SCharles.Forsyth 	uchar pirq;
30174a4d8c2SCharles.Forsyth 
30274a4d8c2SCharles.Forsyth 	pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
30374a4d8c2SCharles.Forsyth 	pirq &= (link & 1)? 0x0f: 0xf0;
30474a4d8c2SCharles.Forsyth 	pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15);
30574a4d8c2SCharles.Forsyth 	pcicfgw8(router, 0x48 + ((link-1)>>1), pirq);
30674a4d8c2SCharles.Forsyth }
30774a4d8c2SCharles.Forsyth 
30874a4d8c2SCharles.Forsyth static uchar
cyrix_link(Pcidev * router,uchar link)30974a4d8c2SCharles.Forsyth cyrix_link(Pcidev *router, uchar link)
31074a4d8c2SCharles.Forsyth {
31174a4d8c2SCharles.Forsyth 	uchar pirq;
31274a4d8c2SCharles.Forsyth 
31374a4d8c2SCharles.Forsyth 	/* link should be 1, 2, 3, 4 */
31474a4d8c2SCharles.Forsyth 	pirq = pcicfgr8(router, 0x5c + ((link-1)>>1));
31574a4d8c2SCharles.Forsyth 	return ((link & 1)? pirq >> 4: pirq & 15);
31674a4d8c2SCharles.Forsyth }
31774a4d8c2SCharles.Forsyth 
31874a4d8c2SCharles.Forsyth static void
cyrix_init(Pcidev * router,uchar link,uchar irq)31974a4d8c2SCharles.Forsyth cyrix_init(Pcidev *router, uchar link, uchar irq)
32074a4d8c2SCharles.Forsyth {
32174a4d8c2SCharles.Forsyth 	uchar pirq;
32274a4d8c2SCharles.Forsyth 
32374a4d8c2SCharles.Forsyth 	pirq = pcicfgr8(router, 0x5c + (link>>1));
32474a4d8c2SCharles.Forsyth 	pirq &= (link & 1)? 0x0f: 0xf0;
32574a4d8c2SCharles.Forsyth 	pirq |= (link & 1)? (irq << 4): (irq & 15);
32674a4d8c2SCharles.Forsyth 	pcicfgw8(router, 0x5c + (link>>1), pirq);
32774a4d8c2SCharles.Forsyth }
32874a4d8c2SCharles.Forsyth 
32974a4d8c2SCharles.Forsyth typedef struct {
33074a4d8c2SCharles.Forsyth 	ushort	sb_vid, sb_did;
33174a4d8c2SCharles.Forsyth 	uchar	(*sb_translate)(Pcidev *, uchar);
33274a4d8c2SCharles.Forsyth 	void	(*sb_initialize)(Pcidev *, uchar, uchar);
33374a4d8c2SCharles.Forsyth } bridge_t;
33474a4d8c2SCharles.Forsyth 
33574a4d8c2SCharles.Forsyth static bridge_t southbridges[] = {
336*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x122e, pIIx_link, pIIx_init },	// Intel 82371FB
337*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x1234, pIIx_link, pIIx_init },	// Intel 82371MX
338*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x7000, pIIx_link, pIIx_init },	// Intel 82371SB
339*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x7110, pIIx_link, pIIx_init },	// Intel 82371AB
340*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x7198, pIIx_link, pIIx_init },	// Intel 82443MX (fn 1)
341*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x2410, pIIx_link, pIIx_init },	// Intel 82801AA
342*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x2420, pIIx_link, pIIx_init },	// Intel 82801AB
343*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x2440, pIIx_link, pIIx_init },	// Intel 82801BA
344*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x244c, pIIx_link, pIIx_init },	// Intel 82801BAM
345*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x2480, pIIx_link, pIIx_init },	// Intel 82801CA
346*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x248c, pIIx_link, pIIx_init },	// Intel 82801CAM
347*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x24c0, pIIx_link, pIIx_init },	// Intel 82801DBL
348*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x24cc, pIIx_link, pIIx_init },	// Intel 82801DBM
349*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x24d0, pIIx_link, pIIx_init },	// Intel 82801EB
350*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x2640, pIIx_link, pIIx_init },	// Intel 82801FB
351*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x27b8, pIIx_link, pIIx_init },	// Intel 82801GB
352*8a8c2d74SCharles.Forsyth 	{ 0x8086, 0x27b9, pIIx_link, pIIx_init },	// Intel 82801GBM
353*8a8c2d74SCharles.Forsyth 	{ 0x1106, 0x0586, via_link, via_init },		// Viatech 82C586
354*8a8c2d74SCharles.Forsyth 	{ 0x1106, 0x0596, via_link, via_init },		// Viatech 82C596
355*8a8c2d74SCharles.Forsyth 	{ 0x1106, 0x0686, via_link, via_init },		// Viatech 82C686
356*8a8c2d74SCharles.Forsyth 	{ 0x1106, 0x3227, via_link, via_init },		// Viatech VT8237
357*8a8c2d74SCharles.Forsyth 	{ 0x1045, 0xc700, opti_link, opti_init },	// Opti 82C700
358*8a8c2d74SCharles.Forsyth 	{ 0x10b9, 0x1533, ali_link, ali_init },		// Al M1533
359*8a8c2d74SCharles.Forsyth 	{ 0x1039, 0x0008, pIIx_link, pIIx_init },	// SI 503
360*8a8c2d74SCharles.Forsyth 	{ 0x1039, 0x0496, pIIx_link, pIIx_init },	// SI 496
361*8a8c2d74SCharles.Forsyth 	{ 0x1078, 0x0100, cyrix_link, cyrix_init },	// Cyrix 5530 Legacy
362*8a8c2d74SCharles.Forsyth 
363*8a8c2d74SCharles.Forsyth 	{ 0x1002, 0x4377, nil, nil },		// ATI Radeon Xpress 200M
364*8a8c2d74SCharles.Forsyth 	{ 0x1002, 0x4372, nil, nil },		// ATI SB400
365*8a8c2d74SCharles.Forsyth 	{ 0x1022, 0x746B, nil, nil },		// AMD 8111
366*8a8c2d74SCharles.Forsyth 	{ 0x10DE, 0x00D1, nil, nil },		// NVIDIA nForce 3
367*8a8c2d74SCharles.Forsyth 	{ 0x10DE, 0x00E0, nil, nil },		// NVIDIA nForce 3 250 Series
368*8a8c2d74SCharles.Forsyth 	{ 0x1166, 0x0200, nil, nil },		// ServerWorks ServerSet III LE
36974a4d8c2SCharles.Forsyth };
37074a4d8c2SCharles.Forsyth 
37174a4d8c2SCharles.Forsyth typedef struct {
37274a4d8c2SCharles.Forsyth 	uchar	e_bus;			// Pci bus number
37374a4d8c2SCharles.Forsyth 	uchar	e_dev;			// Pci device number
37474a4d8c2SCharles.Forsyth 	uchar	e_maps[12];		// Avoid structs!  Link and mask.
37574a4d8c2SCharles.Forsyth 	uchar	e_slot;			// Add-in/built-in slot
37674a4d8c2SCharles.Forsyth 	uchar	e_reserved;
37774a4d8c2SCharles.Forsyth } slot_t;
37874a4d8c2SCharles.Forsyth 
37974a4d8c2SCharles.Forsyth typedef struct {
38074a4d8c2SCharles.Forsyth 	uchar	rt_signature[4];	// Routing table signature
38174a4d8c2SCharles.Forsyth 	uchar	rt_version[2];		// Version number
38274a4d8c2SCharles.Forsyth 	uchar	rt_size[2];			// Total table size
38374a4d8c2SCharles.Forsyth 	uchar	rt_bus;			// Interrupt router bus number
38474a4d8c2SCharles.Forsyth 	uchar	rt_devfn;			// Router's devfunc
38574a4d8c2SCharles.Forsyth 	uchar	rt_pciirqs[2];		// Exclusive PCI irqs
38674a4d8c2SCharles.Forsyth 	uchar	rt_compat[4];		// Compatible PCI interrupt router
38774a4d8c2SCharles.Forsyth 	uchar	rt_miniport[4];		// Miniport data
38874a4d8c2SCharles.Forsyth 	uchar	rt_reserved[11];
38974a4d8c2SCharles.Forsyth 	uchar	rt_checksum;
39074a4d8c2SCharles.Forsyth } router_t;
39174a4d8c2SCharles.Forsyth 
39274a4d8c2SCharles.Forsyth static ushort pciirqs;			// Exclusive PCI irqs
39374a4d8c2SCharles.Forsyth static bridge_t *southbridge;	// Which southbridge to use.
39474a4d8c2SCharles.Forsyth 
39574a4d8c2SCharles.Forsyth static void
pcirouting(void)39674a4d8c2SCharles.Forsyth pcirouting(void)
39774a4d8c2SCharles.Forsyth {
39874a4d8c2SCharles.Forsyth 	uchar *p, pin, irq;
39974a4d8c2SCharles.Forsyth 	ulong tbdf, vdid;
40074a4d8c2SCharles.Forsyth 	ushort vid, did;
40174a4d8c2SCharles.Forsyth 	router_t *r;
40274a4d8c2SCharles.Forsyth 	slot_t *e;
40374a4d8c2SCharles.Forsyth 	int size, i, fn;
40474a4d8c2SCharles.Forsyth 	Pcidev *sbpci, *pci;
40574a4d8c2SCharles.Forsyth 
40674a4d8c2SCharles.Forsyth 	// Peek in the BIOS
40774a4d8c2SCharles.Forsyth 	for (p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16)
40874a4d8c2SCharles.Forsyth 		if (p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R')
40974a4d8c2SCharles.Forsyth 			break;
41074a4d8c2SCharles.Forsyth 
41174a4d8c2SCharles.Forsyth 	if (p >= (uchar *)KADDR(0xfffff))
41274a4d8c2SCharles.Forsyth 		return;
41374a4d8c2SCharles.Forsyth 
41474a4d8c2SCharles.Forsyth 	r = (router_t *)p;
41574a4d8c2SCharles.Forsyth 
41674a4d8c2SCharles.Forsyth 	// print("PCI interrupt routing table version %d.%d at %.6uX\n",
41774a4d8c2SCharles.Forsyth 	// 	r->rt_version[0], r->rt_version[1], (ulong)r & 0xfffff);
41874a4d8c2SCharles.Forsyth 
41974a4d8c2SCharles.Forsyth 	tbdf = (BusPCI << 24)|(r->rt_bus << 16)|(r->rt_devfn << 8);
42074a4d8c2SCharles.Forsyth 	vdid = pcicfgrw32(tbdf, PciVID, 0, 1);
42174a4d8c2SCharles.Forsyth 	vid = vdid;
42274a4d8c2SCharles.Forsyth 	did = vdid >> 16;
42374a4d8c2SCharles.Forsyth 
42474a4d8c2SCharles.Forsyth 	for (i = 0; i != nelem(southbridges); i++)
42574a4d8c2SCharles.Forsyth 		if (vid == southbridges[i].sb_vid && did == southbridges[i].sb_did)
42674a4d8c2SCharles.Forsyth 			break;
42774a4d8c2SCharles.Forsyth 
42874a4d8c2SCharles.Forsyth 	if (i == nelem(southbridges)) {
42974a4d8c2SCharles.Forsyth 		print("pcirouting: South bridge %.4uX, %.4uX not found\n", vid, did);
43074a4d8c2SCharles.Forsyth 		return;
43174a4d8c2SCharles.Forsyth 	}
43274a4d8c2SCharles.Forsyth 	southbridge = &southbridges[i];
43374a4d8c2SCharles.Forsyth 
43474a4d8c2SCharles.Forsyth 	if ((sbpci = pcimatch(nil, vid, did)) == nil) {
43574a4d8c2SCharles.Forsyth 		print("pcirouting: Cannot match south bridge %.4uX, %.4uX\n",
43674a4d8c2SCharles.Forsyth 			  vid, did);
43774a4d8c2SCharles.Forsyth 		return;
43874a4d8c2SCharles.Forsyth 	}
43974a4d8c2SCharles.Forsyth 
44074a4d8c2SCharles.Forsyth 	pciirqs = (r->rt_pciirqs[1] << 8)|r->rt_pciirqs[0];
44174a4d8c2SCharles.Forsyth 
44274a4d8c2SCharles.Forsyth 	size = (r->rt_size[1] << 8)|r->rt_size[0];
44374a4d8c2SCharles.Forsyth 	for (e = (slot_t *)&r[1]; (uchar *)e < p + size; e++) {
44474a4d8c2SCharles.Forsyth 		// print("%.2uX/%.2uX %.2uX: ", e->e_bus, e->e_dev, e->e_slot);
44574a4d8c2SCharles.Forsyth 		// for (i = 0; i != 4; i++) {
44674a4d8c2SCharles.Forsyth 		// 	uchar *m = &e->e_maps[i * 3];
44774a4d8c2SCharles.Forsyth 		// 	print("[%d] %.2uX %.4uX ",
44874a4d8c2SCharles.Forsyth 		// 		i, m[0], (m[2] << 8)|m[1]);
44974a4d8c2SCharles.Forsyth 		// }
45074a4d8c2SCharles.Forsyth 		// print("\n");
45174a4d8c2SCharles.Forsyth 
45274a4d8c2SCharles.Forsyth 		for (fn = 0; fn != 8; fn++) {
45374a4d8c2SCharles.Forsyth 			uchar *m;
45474a4d8c2SCharles.Forsyth 
45574a4d8c2SCharles.Forsyth 			// Retrieve the did and vid through the devfn before
45674a4d8c2SCharles.Forsyth 			// obtaining the Pcidev structure.
45774a4d8c2SCharles.Forsyth 			tbdf = (BusPCI << 24)|(e->e_bus << 16)|((e->e_dev | fn) << 8);
45874a4d8c2SCharles.Forsyth 			vdid = pcicfgrw32(tbdf, PciVID, 0, 1);
45974a4d8c2SCharles.Forsyth 			if (vdid == 0xFFFFFFFF || vdid == 0)
46074a4d8c2SCharles.Forsyth 				continue;
46174a4d8c2SCharles.Forsyth 
46274a4d8c2SCharles.Forsyth 			vid = vdid;
46374a4d8c2SCharles.Forsyth 			did = vdid >> 16;
46474a4d8c2SCharles.Forsyth 
46574a4d8c2SCharles.Forsyth 			pci = nil;
46674a4d8c2SCharles.Forsyth 			while ((pci = pcimatch(pci, vid, did)) != nil) {
46774a4d8c2SCharles.Forsyth 
46874a4d8c2SCharles.Forsyth 				if (pci->intl != 0 && pci->intl != 0xFF)
46974a4d8c2SCharles.Forsyth 					continue;
47074a4d8c2SCharles.Forsyth 
47174a4d8c2SCharles.Forsyth 				pin = pcicfgr8(pci, PciINTP);
47274a4d8c2SCharles.Forsyth 				if (pin == 0 || pin == 0xff)
47374a4d8c2SCharles.Forsyth 					continue;
47474a4d8c2SCharles.Forsyth 
47574a4d8c2SCharles.Forsyth 				m = &e->e_maps[(pin - 1) * 3];
47674a4d8c2SCharles.Forsyth 				irq = southbridge->sb_translate(sbpci, m[0]);
47774a4d8c2SCharles.Forsyth 				if (irq) {
47874a4d8c2SCharles.Forsyth 					print("pcirouting: %.4uX/%.4uX at pin %d irq %d\n",
47974a4d8c2SCharles.Forsyth 						  vid, did, pin, irq);
48074a4d8c2SCharles.Forsyth 					pcicfgw8(pci, PciINTL, irq);
48174a4d8c2SCharles.Forsyth 					pci->intl = irq;
48274a4d8c2SCharles.Forsyth 				}
48374a4d8c2SCharles.Forsyth 			}
48474a4d8c2SCharles.Forsyth 		}
48574a4d8c2SCharles.Forsyth 	}
48674a4d8c2SCharles.Forsyth }
48774a4d8c2SCharles.Forsyth 
48874a4d8c2SCharles.Forsyth static void
pcicfginit(void)48974a4d8c2SCharles.Forsyth pcicfginit(void)
49074a4d8c2SCharles.Forsyth {
49174a4d8c2SCharles.Forsyth 	char *p;
49274a4d8c2SCharles.Forsyth 	int bno, n;
49374a4d8c2SCharles.Forsyth 	Pcidev **list;
49474a4d8c2SCharles.Forsyth 
49574a4d8c2SCharles.Forsyth 	lock(&pcicfginitlock);
49674a4d8c2SCharles.Forsyth 	if(pcicfgmode != -1)
49774a4d8c2SCharles.Forsyth 		goto out;
49874a4d8c2SCharles.Forsyth 
49974a4d8c2SCharles.Forsyth 	/*
50074a4d8c2SCharles.Forsyth 	 * Try to determine which PCI configuration mode is implemented.
50174a4d8c2SCharles.Forsyth 	 * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
50274a4d8c2SCharles.Forsyth 	 * a DWORD at 0xCF8 and another at 0xCFC and will pass through
50374a4d8c2SCharles.Forsyth 	 * any non-DWORD accesses as normal I/O cycles. There shouldn't be
50474a4d8c2SCharles.Forsyth 	 * a device behind these addresses so if Mode1 accesses fail try
50574a4d8c2SCharles.Forsyth 	 * for Mode2 (Mode2 is deprecated).
50674a4d8c2SCharles.Forsyth 	 */
50774a4d8c2SCharles.Forsyth 
50874a4d8c2SCharles.Forsyth 	/*
50974a4d8c2SCharles.Forsyth 	 * Bits [30:24] of PciADDR must be 0,
51074a4d8c2SCharles.Forsyth 	 * according to the spec.
51174a4d8c2SCharles.Forsyth 	 */
51274a4d8c2SCharles.Forsyth 	n = inl(PciADDR);
51374a4d8c2SCharles.Forsyth 	if(!(n & 0x7FF00000)){
51474a4d8c2SCharles.Forsyth 		outl(PciADDR, 0x80000000);
51574a4d8c2SCharles.Forsyth 		outb(PciADDR+3, 0);
51674a4d8c2SCharles.Forsyth 		if(inl(PciADDR) & 0x80000000){
51774a4d8c2SCharles.Forsyth 			pcicfgmode = 1;
51874a4d8c2SCharles.Forsyth 			pcimaxdno = 31;
51974a4d8c2SCharles.Forsyth 		}
52074a4d8c2SCharles.Forsyth 	}
52174a4d8c2SCharles.Forsyth 	outl(PciADDR, n);
52274a4d8c2SCharles.Forsyth 
52374a4d8c2SCharles.Forsyth 	if(pcicfgmode < 0){
52474a4d8c2SCharles.Forsyth 		/*
52574a4d8c2SCharles.Forsyth 		 * The 'key' part of PciCSE should be 0.
52674a4d8c2SCharles.Forsyth 		 */
52774a4d8c2SCharles.Forsyth 		n = inb(PciCSE);
52874a4d8c2SCharles.Forsyth 		if(!(n & 0xF0)){
52974a4d8c2SCharles.Forsyth 			outb(PciCSE, 0x0E);
53074a4d8c2SCharles.Forsyth 			if(inb(PciCSE) == 0x0E){
53174a4d8c2SCharles.Forsyth 				pcicfgmode = 2;
53274a4d8c2SCharles.Forsyth 				pcimaxdno = 15;
53374a4d8c2SCharles.Forsyth 			}
53474a4d8c2SCharles.Forsyth 		}
53574a4d8c2SCharles.Forsyth 		outb(PciCSE, n);
53674a4d8c2SCharles.Forsyth 	}
53774a4d8c2SCharles.Forsyth 
53874a4d8c2SCharles.Forsyth 	if(pcicfgmode < 0)
53974a4d8c2SCharles.Forsyth 		goto out;
54074a4d8c2SCharles.Forsyth 
54174a4d8c2SCharles.Forsyth 
54274a4d8c2SCharles.Forsyth 	if(p = getconf("*pcimaxbno"))
54374a4d8c2SCharles.Forsyth 		pcimaxbno = strtoul(p, 0, 0);
54474a4d8c2SCharles.Forsyth 	if(p = getconf("*pcimaxdno"))
54574a4d8c2SCharles.Forsyth 		pcimaxdno = strtoul(p, 0, 0);
54674a4d8c2SCharles.Forsyth 
54774a4d8c2SCharles.Forsyth 	list = &pciroot;
54874a4d8c2SCharles.Forsyth 	for(bno = 0; bno <= pcimaxbno; bno++) {
54974a4d8c2SCharles.Forsyth 		bno = pciscan(bno, list);
55074a4d8c2SCharles.Forsyth 		while(*list)
55174a4d8c2SCharles.Forsyth 			list = &(*list)->link;
55274a4d8c2SCharles.Forsyth 	}
55374a4d8c2SCharles.Forsyth 
55474a4d8c2SCharles.Forsyth 	pcirouting();
55574a4d8c2SCharles.Forsyth 
55674a4d8c2SCharles.Forsyth out:
55774a4d8c2SCharles.Forsyth 	unlock(&pcicfginitlock);
55874a4d8c2SCharles.Forsyth 
55974a4d8c2SCharles.Forsyth 	if(getconf("*pcihinv"))
56074a4d8c2SCharles.Forsyth 		pcihinv(nil);
56174a4d8c2SCharles.Forsyth }
56274a4d8c2SCharles.Forsyth 
56374a4d8c2SCharles.Forsyth 
56474a4d8c2SCharles.Forsyth static int
pcicfgrw8(int tbdf,int rno,int data,int read)56574a4d8c2SCharles.Forsyth pcicfgrw8(int tbdf, int rno, int data, int read)
56674a4d8c2SCharles.Forsyth {
56774a4d8c2SCharles.Forsyth 	int o, type, x;
56874a4d8c2SCharles.Forsyth 
56974a4d8c2SCharles.Forsyth 	if(pcicfgmode == -1)
57074a4d8c2SCharles.Forsyth 		pcicfginit();
57174a4d8c2SCharles.Forsyth 
57274a4d8c2SCharles.Forsyth 	if(BUSBNO(tbdf))
57374a4d8c2SCharles.Forsyth 		type = 0x01;
57474a4d8c2SCharles.Forsyth 	else
57574a4d8c2SCharles.Forsyth 		type = 0x00;
57674a4d8c2SCharles.Forsyth 	x = -1;
57774a4d8c2SCharles.Forsyth 	if(BUSDNO(tbdf) > pcimaxdno)
57874a4d8c2SCharles.Forsyth 		return x;
57974a4d8c2SCharles.Forsyth 
58074a4d8c2SCharles.Forsyth 	lock(&pcicfglock);
58174a4d8c2SCharles.Forsyth 	switch(pcicfgmode){
58274a4d8c2SCharles.Forsyth 
58374a4d8c2SCharles.Forsyth 	case 1:
58474a4d8c2SCharles.Forsyth 		o = rno & 0x03;
58574a4d8c2SCharles.Forsyth 		rno &= ~0x03;
58674a4d8c2SCharles.Forsyth 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
58774a4d8c2SCharles.Forsyth 		if(read)
58874a4d8c2SCharles.Forsyth 			x = inb(PciDATA+o);
58974a4d8c2SCharles.Forsyth 		else
59074a4d8c2SCharles.Forsyth 			outb(PciDATA+o, data);
59174a4d8c2SCharles.Forsyth 		outl(PciADDR, 0);
59274a4d8c2SCharles.Forsyth 		break;
59374a4d8c2SCharles.Forsyth 
59474a4d8c2SCharles.Forsyth 	case 2:
59574a4d8c2SCharles.Forsyth 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
59674a4d8c2SCharles.Forsyth 		outb(PciFORWARD, BUSBNO(tbdf));
59774a4d8c2SCharles.Forsyth 		if(read)
59874a4d8c2SCharles.Forsyth 			x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
59974a4d8c2SCharles.Forsyth 		else
60074a4d8c2SCharles.Forsyth 			outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
60174a4d8c2SCharles.Forsyth 		outb(PciCSE, 0);
60274a4d8c2SCharles.Forsyth 		break;
60374a4d8c2SCharles.Forsyth 	}
60474a4d8c2SCharles.Forsyth 	unlock(&pcicfglock);
60574a4d8c2SCharles.Forsyth 
60674a4d8c2SCharles.Forsyth 	return x;
60774a4d8c2SCharles.Forsyth }
60874a4d8c2SCharles.Forsyth 
60974a4d8c2SCharles.Forsyth int
pcicfgr8(Pcidev * pcidev,int rno)61074a4d8c2SCharles.Forsyth pcicfgr8(Pcidev* pcidev, int rno)
61174a4d8c2SCharles.Forsyth {
61274a4d8c2SCharles.Forsyth 	return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
61374a4d8c2SCharles.Forsyth }
61474a4d8c2SCharles.Forsyth 
61574a4d8c2SCharles.Forsyth void
pcicfgw8(Pcidev * pcidev,int rno,int data)61674a4d8c2SCharles.Forsyth pcicfgw8(Pcidev* pcidev, int rno, int data)
61774a4d8c2SCharles.Forsyth {
61874a4d8c2SCharles.Forsyth 	pcicfgrw8(pcidev->tbdf, rno, data, 0);
61974a4d8c2SCharles.Forsyth }
62074a4d8c2SCharles.Forsyth 
62174a4d8c2SCharles.Forsyth static int
pcicfgrw16(int tbdf,int rno,int data,int read)62274a4d8c2SCharles.Forsyth pcicfgrw16(int tbdf, int rno, int data, int read)
62374a4d8c2SCharles.Forsyth {
62474a4d8c2SCharles.Forsyth 	int o, type, x;
62574a4d8c2SCharles.Forsyth 
62674a4d8c2SCharles.Forsyth 	if(pcicfgmode == -1)
62774a4d8c2SCharles.Forsyth 		pcicfginit();
62874a4d8c2SCharles.Forsyth 
62974a4d8c2SCharles.Forsyth 	if(BUSBNO(tbdf))
63074a4d8c2SCharles.Forsyth 		type = 0x01;
63174a4d8c2SCharles.Forsyth 	else
63274a4d8c2SCharles.Forsyth 		type = 0x00;
63374a4d8c2SCharles.Forsyth 	x = -1;
63474a4d8c2SCharles.Forsyth 	if(BUSDNO(tbdf) > pcimaxdno)
63574a4d8c2SCharles.Forsyth 		return x;
63674a4d8c2SCharles.Forsyth 
63774a4d8c2SCharles.Forsyth 	lock(&pcicfglock);
63874a4d8c2SCharles.Forsyth 	switch(pcicfgmode){
63974a4d8c2SCharles.Forsyth 
64074a4d8c2SCharles.Forsyth 	case 1:
64174a4d8c2SCharles.Forsyth 		o = rno & 0x02;
64274a4d8c2SCharles.Forsyth 		rno &= ~0x03;
64374a4d8c2SCharles.Forsyth 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
64474a4d8c2SCharles.Forsyth 		if(read)
64574a4d8c2SCharles.Forsyth 			x = ins(PciDATA+o);
64674a4d8c2SCharles.Forsyth 		else
64774a4d8c2SCharles.Forsyth 			outs(PciDATA+o, data);
64874a4d8c2SCharles.Forsyth 		outl(PciADDR, 0);
64974a4d8c2SCharles.Forsyth 		break;
65074a4d8c2SCharles.Forsyth 
65174a4d8c2SCharles.Forsyth 	case 2:
65274a4d8c2SCharles.Forsyth 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
65374a4d8c2SCharles.Forsyth 		outb(PciFORWARD, BUSBNO(tbdf));
65474a4d8c2SCharles.Forsyth 		if(read)
65574a4d8c2SCharles.Forsyth 			x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
65674a4d8c2SCharles.Forsyth 		else
65774a4d8c2SCharles.Forsyth 			outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
65874a4d8c2SCharles.Forsyth 		outb(PciCSE, 0);
65974a4d8c2SCharles.Forsyth 		break;
66074a4d8c2SCharles.Forsyth 	}
66174a4d8c2SCharles.Forsyth 	unlock(&pcicfglock);
66274a4d8c2SCharles.Forsyth 
66374a4d8c2SCharles.Forsyth 	return x;
66474a4d8c2SCharles.Forsyth }
66574a4d8c2SCharles.Forsyth 
66674a4d8c2SCharles.Forsyth int
pcicfgr16(Pcidev * pcidev,int rno)66774a4d8c2SCharles.Forsyth pcicfgr16(Pcidev* pcidev, int rno)
66874a4d8c2SCharles.Forsyth {
66974a4d8c2SCharles.Forsyth 	return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
67074a4d8c2SCharles.Forsyth }
67174a4d8c2SCharles.Forsyth 
67274a4d8c2SCharles.Forsyth void
pcicfgw16(Pcidev * pcidev,int rno,int data)67374a4d8c2SCharles.Forsyth pcicfgw16(Pcidev* pcidev, int rno, int data)
67474a4d8c2SCharles.Forsyth {
67574a4d8c2SCharles.Forsyth 	pcicfgrw16(pcidev->tbdf, rno, data, 0);
67674a4d8c2SCharles.Forsyth }
67774a4d8c2SCharles.Forsyth 
67874a4d8c2SCharles.Forsyth static int
pcicfgrw32(int tbdf,int rno,int data,int read)67974a4d8c2SCharles.Forsyth pcicfgrw32(int tbdf, int rno, int data, int read)
68074a4d8c2SCharles.Forsyth {
68174a4d8c2SCharles.Forsyth 	int type, x;
68274a4d8c2SCharles.Forsyth 
68374a4d8c2SCharles.Forsyth 	if(pcicfgmode == -1)
68474a4d8c2SCharles.Forsyth 		pcicfginit();
68574a4d8c2SCharles.Forsyth 
68674a4d8c2SCharles.Forsyth 	if(BUSBNO(tbdf))
68774a4d8c2SCharles.Forsyth 		type = 0x01;
68874a4d8c2SCharles.Forsyth 	else
68974a4d8c2SCharles.Forsyth 		type = 0x00;
69074a4d8c2SCharles.Forsyth 	x = -1;
69174a4d8c2SCharles.Forsyth 	if(BUSDNO(tbdf) > pcimaxdno)
69274a4d8c2SCharles.Forsyth 		return x;
69374a4d8c2SCharles.Forsyth 
69474a4d8c2SCharles.Forsyth 	lock(&pcicfglock);
69574a4d8c2SCharles.Forsyth 	switch(pcicfgmode){
69674a4d8c2SCharles.Forsyth 
69774a4d8c2SCharles.Forsyth 	case 1:
69874a4d8c2SCharles.Forsyth 		rno &= ~0x03;
69974a4d8c2SCharles.Forsyth 		outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
70074a4d8c2SCharles.Forsyth 		if(read)
70174a4d8c2SCharles.Forsyth 			x = inl(PciDATA);
70274a4d8c2SCharles.Forsyth 		else
70374a4d8c2SCharles.Forsyth 			outl(PciDATA, data);
70474a4d8c2SCharles.Forsyth 		outl(PciADDR, 0);
70574a4d8c2SCharles.Forsyth 		break;
70674a4d8c2SCharles.Forsyth 
70774a4d8c2SCharles.Forsyth 	case 2:
70874a4d8c2SCharles.Forsyth 		outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
70974a4d8c2SCharles.Forsyth 		outb(PciFORWARD, BUSBNO(tbdf));
71074a4d8c2SCharles.Forsyth 		if(read)
71174a4d8c2SCharles.Forsyth 			x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
71274a4d8c2SCharles.Forsyth 		else
71374a4d8c2SCharles.Forsyth 			outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
71474a4d8c2SCharles.Forsyth 		outb(PciCSE, 0);
71574a4d8c2SCharles.Forsyth 		break;
71674a4d8c2SCharles.Forsyth 	}
71774a4d8c2SCharles.Forsyth 	unlock(&pcicfglock);
71874a4d8c2SCharles.Forsyth 
71974a4d8c2SCharles.Forsyth 	return x;
72074a4d8c2SCharles.Forsyth }
72174a4d8c2SCharles.Forsyth 
72274a4d8c2SCharles.Forsyth int
pcicfgr32(Pcidev * pcidev,int rno)72374a4d8c2SCharles.Forsyth pcicfgr32(Pcidev* pcidev, int rno)
72474a4d8c2SCharles.Forsyth {
72574a4d8c2SCharles.Forsyth 	return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
72674a4d8c2SCharles.Forsyth }
72774a4d8c2SCharles.Forsyth 
72874a4d8c2SCharles.Forsyth void
pcicfgw32(Pcidev * pcidev,int rno,int data)72974a4d8c2SCharles.Forsyth pcicfgw32(Pcidev* pcidev, int rno, int data)
73074a4d8c2SCharles.Forsyth {
73174a4d8c2SCharles.Forsyth 	pcicfgrw32(pcidev->tbdf, rno, data, 0);
73274a4d8c2SCharles.Forsyth }
73374a4d8c2SCharles.Forsyth 
73474a4d8c2SCharles.Forsyth Pcidev*
pcimatch(Pcidev * prev,int vid,int did)73574a4d8c2SCharles.Forsyth pcimatch(Pcidev* prev, int vid, int did)
73674a4d8c2SCharles.Forsyth {
73774a4d8c2SCharles.Forsyth 	if(pcicfgmode == -1)
73874a4d8c2SCharles.Forsyth 		pcicfginit();
73974a4d8c2SCharles.Forsyth 
74074a4d8c2SCharles.Forsyth 	if(prev == nil)
74174a4d8c2SCharles.Forsyth 		prev = pcilist;
74274a4d8c2SCharles.Forsyth 	else
74374a4d8c2SCharles.Forsyth 		prev = prev->list;
74474a4d8c2SCharles.Forsyth 
74574a4d8c2SCharles.Forsyth 	while(prev != nil) {
74674a4d8c2SCharles.Forsyth 		if((vid == 0 || prev->vid == vid)
74774a4d8c2SCharles.Forsyth 		&& (did == 0 || prev->did == did))
74874a4d8c2SCharles.Forsyth 			break;
74974a4d8c2SCharles.Forsyth 		prev = prev->list;
75074a4d8c2SCharles.Forsyth 	}
75174a4d8c2SCharles.Forsyth 	return prev;
75274a4d8c2SCharles.Forsyth }
75374a4d8c2SCharles.Forsyth 
75474a4d8c2SCharles.Forsyth uchar
pciipin(Pcidev * pci,uchar pin)75574a4d8c2SCharles.Forsyth pciipin(Pcidev *pci, uchar pin)
75674a4d8c2SCharles.Forsyth {
75774a4d8c2SCharles.Forsyth 	if (pci == nil)
75874a4d8c2SCharles.Forsyth 		pci = pcilist;
75974a4d8c2SCharles.Forsyth 
76074a4d8c2SCharles.Forsyth 	while (pci) {
76174a4d8c2SCharles.Forsyth 		uchar intl;
76274a4d8c2SCharles.Forsyth 
76374a4d8c2SCharles.Forsyth 		if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
76474a4d8c2SCharles.Forsyth 			return pci->intl;
76574a4d8c2SCharles.Forsyth 
76674a4d8c2SCharles.Forsyth 		if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
76774a4d8c2SCharles.Forsyth 			return intl;
76874a4d8c2SCharles.Forsyth 
76974a4d8c2SCharles.Forsyth 		pci = pci->list;
77074a4d8c2SCharles.Forsyth 	}
77174a4d8c2SCharles.Forsyth 	return 0;
77274a4d8c2SCharles.Forsyth }
77374a4d8c2SCharles.Forsyth 
77474a4d8c2SCharles.Forsyth static ushort
pciimask(Pcidev * pci)77574a4d8c2SCharles.Forsyth pciimask(Pcidev *pci)
77674a4d8c2SCharles.Forsyth {
77774a4d8c2SCharles.Forsyth 	ushort imask;
77874a4d8c2SCharles.Forsyth 
77974a4d8c2SCharles.Forsyth 	imask = 0;
78074a4d8c2SCharles.Forsyth 	while (pci) {
78174a4d8c2SCharles.Forsyth 		if (pcicfgr8(pci, PciINTP) && pci->intl < 16)
78274a4d8c2SCharles.Forsyth 			imask |= 1 << pci->intl;
78374a4d8c2SCharles.Forsyth 
78474a4d8c2SCharles.Forsyth 		if (pci->bridge)
78574a4d8c2SCharles.Forsyth 			imask |= pciimask(pci->bridge);
78674a4d8c2SCharles.Forsyth 
78774a4d8c2SCharles.Forsyth 		pci = pci->list;
78874a4d8c2SCharles.Forsyth 	}
78974a4d8c2SCharles.Forsyth 	return imask;
79074a4d8c2SCharles.Forsyth }
79174a4d8c2SCharles.Forsyth 
79274a4d8c2SCharles.Forsyth uchar
pciintl(Pcidev * pci)79374a4d8c2SCharles.Forsyth pciintl(Pcidev *pci)
79474a4d8c2SCharles.Forsyth {
79574a4d8c2SCharles.Forsyth 	ushort imask;
79674a4d8c2SCharles.Forsyth 	int i;
79774a4d8c2SCharles.Forsyth 
79874a4d8c2SCharles.Forsyth 	if (pci == nil)
79974a4d8c2SCharles.Forsyth 		pci = pcilist;
80074a4d8c2SCharles.Forsyth 
80174a4d8c2SCharles.Forsyth 	imask = pciimask(pci) | 1;
80274a4d8c2SCharles.Forsyth 	for (i = 0; i != 16; i++)
80374a4d8c2SCharles.Forsyth 		if ((imask & (1 << i)) == 0)
80474a4d8c2SCharles.Forsyth 			return i;
80574a4d8c2SCharles.Forsyth 	return 0;
80674a4d8c2SCharles.Forsyth }
80774a4d8c2SCharles.Forsyth 
80874a4d8c2SCharles.Forsyth void
pcihinv(Pcidev * p)80974a4d8c2SCharles.Forsyth pcihinv(Pcidev* p)
81074a4d8c2SCharles.Forsyth {
81174a4d8c2SCharles.Forsyth 	int i;
81274a4d8c2SCharles.Forsyth 	Pcidev *t;
81374a4d8c2SCharles.Forsyth 
81474a4d8c2SCharles.Forsyth 	if(pcicfgmode == -1)
81574a4d8c2SCharles.Forsyth 		pcicfginit();
81674a4d8c2SCharles.Forsyth 
81774a4d8c2SCharles.Forsyth 	if(p == nil) {
81874a4d8c2SCharles.Forsyth 		p = pciroot;
81974a4d8c2SCharles.Forsyth 		print("bus dev type vid  did intl memory\n");
82074a4d8c2SCharles.Forsyth 	}
82174a4d8c2SCharles.Forsyth 	for(t = p; t != nil; t = t->link) {
82274a4d8c2SCharles.Forsyth 		print("%d  %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d  ",
82374a4d8c2SCharles.Forsyth 			BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
82474a4d8c2SCharles.Forsyth 			t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
82574a4d8c2SCharles.Forsyth 
82674a4d8c2SCharles.Forsyth 		for(i = 0; i < nelem(p->mem); i++) {
82774a4d8c2SCharles.Forsyth 			if(t->mem[i].size == 0)
82874a4d8c2SCharles.Forsyth 				continue;
82974a4d8c2SCharles.Forsyth 			print("%d:%.8lux %d ", i,
83074a4d8c2SCharles.Forsyth 				t->mem[i].bar, t->mem[i].size);
83174a4d8c2SCharles.Forsyth 		}
83274a4d8c2SCharles.Forsyth 		print("\n");
83374a4d8c2SCharles.Forsyth 	}
83474a4d8c2SCharles.Forsyth 	while(p != nil) {
83574a4d8c2SCharles.Forsyth 		if(p->bridge != nil)
83674a4d8c2SCharles.Forsyth 			pcihinv(p->bridge);
83774a4d8c2SCharles.Forsyth 		p = p->link;
83874a4d8c2SCharles.Forsyth 	}
83974a4d8c2SCharles.Forsyth }
84074a4d8c2SCharles.Forsyth 
84174a4d8c2SCharles.Forsyth void
pcireset(void)84274a4d8c2SCharles.Forsyth pcireset(void)
84374a4d8c2SCharles.Forsyth {
84474a4d8c2SCharles.Forsyth 	Pcidev *p;
84574a4d8c2SCharles.Forsyth 	int pcr;
84674a4d8c2SCharles.Forsyth 
84774a4d8c2SCharles.Forsyth 	if(pcicfgmode == -1)
84874a4d8c2SCharles.Forsyth 		pcicfginit();
84974a4d8c2SCharles.Forsyth 
85074a4d8c2SCharles.Forsyth 	for(p = pcilist; p != nil; p = p->list){
85174a4d8c2SCharles.Forsyth 		pcr = pcicfgr16(p, PciPSR);
85274a4d8c2SCharles.Forsyth 		pcicfgw16(p, PciPSR, pcr & ~0x04);
85374a4d8c2SCharles.Forsyth 	}
85474a4d8c2SCharles.Forsyth }
85574a4d8c2SCharles.Forsyth 
85674a4d8c2SCharles.Forsyth void
pcisetioe(Pcidev * p)857*8a8c2d74SCharles.Forsyth pcisetioe(Pcidev* p)
85874a4d8c2SCharles.Forsyth {
859*8a8c2d74SCharles.Forsyth 	p->pcr |= IOen;
860*8a8c2d74SCharles.Forsyth 	pcicfgw16(p, PciPCR, p->pcr);
86174a4d8c2SCharles.Forsyth }
86274a4d8c2SCharles.Forsyth 
863*8a8c2d74SCharles.Forsyth void
pciclrioe(Pcidev * p)864*8a8c2d74SCharles.Forsyth pciclrioe(Pcidev* p)
865*8a8c2d74SCharles.Forsyth {
866*8a8c2d74SCharles.Forsyth 	p->pcr &= ~IOen;
867*8a8c2d74SCharles.Forsyth 	pcicfgw16(p, PciPCR, p->pcr);
868*8a8c2d74SCharles.Forsyth }
869*8a8c2d74SCharles.Forsyth 
870*8a8c2d74SCharles.Forsyth void
pcisetbme(Pcidev * p)871*8a8c2d74SCharles.Forsyth pcisetbme(Pcidev* p)
872*8a8c2d74SCharles.Forsyth {
873*8a8c2d74SCharles.Forsyth 	p->pcr |= MASen;
874*8a8c2d74SCharles.Forsyth 	pcicfgw16(p, PciPCR, p->pcr);
875*8a8c2d74SCharles.Forsyth }
876*8a8c2d74SCharles.Forsyth 
877*8a8c2d74SCharles.Forsyth void
pciclrbme(Pcidev * p)878*8a8c2d74SCharles.Forsyth pciclrbme(Pcidev* p)
879*8a8c2d74SCharles.Forsyth {
880*8a8c2d74SCharles.Forsyth 	p->pcr &= ~MASen;
881*8a8c2d74SCharles.Forsyth 	pcicfgw16(p, PciPCR, p->pcr);
882*8a8c2d74SCharles.Forsyth }
883*8a8c2d74SCharles.Forsyth 
884*8a8c2d74SCharles.Forsyth void
pcisetmwi(Pcidev * p)885*8a8c2d74SCharles.Forsyth pcisetmwi(Pcidev* p)
886*8a8c2d74SCharles.Forsyth {
887*8a8c2d74SCharles.Forsyth 	p->pcr |= MemWrInv;
888*8a8c2d74SCharles.Forsyth 	pcicfgw16(p, PciPCR, p->pcr);
889*8a8c2d74SCharles.Forsyth }
890*8a8c2d74SCharles.Forsyth 
891*8a8c2d74SCharles.Forsyth void
pciclrmwi(Pcidev * p)892*8a8c2d74SCharles.Forsyth pciclrmwi(Pcidev* p)
893*8a8c2d74SCharles.Forsyth {
894*8a8c2d74SCharles.Forsyth 	p->pcr &= ~MemWrInv;
895*8a8c2d74SCharles.Forsyth 	pcicfgw16(p, PciPCR, p->pcr);
896*8a8c2d74SCharles.Forsyth }
897*8a8c2d74SCharles.Forsyth 
898*8a8c2d74SCharles.Forsyth static int
pcigetpmrb(Pcidev * p)899*8a8c2d74SCharles.Forsyth pcigetpmrb(Pcidev* p)
900*8a8c2d74SCharles.Forsyth {
901*8a8c2d74SCharles.Forsyth 	int ptr;
902*8a8c2d74SCharles.Forsyth 
903*8a8c2d74SCharles.Forsyth 	if(p->pmrb != 0)
904*8a8c2d74SCharles.Forsyth 		return p->pmrb;
905*8a8c2d74SCharles.Forsyth 	p->pmrb = -1;
906*8a8c2d74SCharles.Forsyth 
907*8a8c2d74SCharles.Forsyth 	/*
908*8a8c2d74SCharles.Forsyth 	 * If there are no extended capabilities implemented,
909*8a8c2d74SCharles.Forsyth 	 * (bit 4 in the status register) assume there's no standard
910*8a8c2d74SCharles.Forsyth 	 * power management method.
911*8a8c2d74SCharles.Forsyth 	 * Find the capabilities pointer based on PCI header type.
912*8a8c2d74SCharles.Forsyth 	 */
913*8a8c2d74SCharles.Forsyth 	if(!(pcicfgr16(p, PciPSR) & 0x0010))
914*8a8c2d74SCharles.Forsyth 		return -1;
915*8a8c2d74SCharles.Forsyth 	switch(pcicfgr8(p, PciHDT)){
916*8a8c2d74SCharles.Forsyth 	default:
917*8a8c2d74SCharles.Forsyth 		return -1;
918*8a8c2d74SCharles.Forsyth 	case 0:					/* all other */
919*8a8c2d74SCharles.Forsyth 	case 1:					/* PCI to PCI bridge */
920*8a8c2d74SCharles.Forsyth 		ptr = 0x34;
921*8a8c2d74SCharles.Forsyth 		break;
922*8a8c2d74SCharles.Forsyth 	case 2:					/* CardBus bridge */
923*8a8c2d74SCharles.Forsyth 		ptr = 0x14;
924*8a8c2d74SCharles.Forsyth 		break;
925*8a8c2d74SCharles.Forsyth 	}
926*8a8c2d74SCharles.Forsyth 	ptr = pcicfgr32(p, ptr);
927*8a8c2d74SCharles.Forsyth 
928*8a8c2d74SCharles.Forsyth 	while(ptr != 0){
929*8a8c2d74SCharles.Forsyth 		/*
930*8a8c2d74SCharles.Forsyth 		 * Check for validity.
931*8a8c2d74SCharles.Forsyth 		 * Can't be in standard header and must be double
932*8a8c2d74SCharles.Forsyth 		 * word aligned.
933*8a8c2d74SCharles.Forsyth 		 */
934*8a8c2d74SCharles.Forsyth 		if(ptr < 0x40 || (ptr & ~0xFC))
935*8a8c2d74SCharles.Forsyth 			return -1;
936*8a8c2d74SCharles.Forsyth 		if(pcicfgr8(p, ptr) == 0x01){
937*8a8c2d74SCharles.Forsyth 			p->pmrb = ptr;
938*8a8c2d74SCharles.Forsyth 			return ptr;
939*8a8c2d74SCharles.Forsyth 		}
940*8a8c2d74SCharles.Forsyth 
941*8a8c2d74SCharles.Forsyth 		ptr = pcicfgr8(p, ptr+1);
942*8a8c2d74SCharles.Forsyth 	}
943*8a8c2d74SCharles.Forsyth 
944*8a8c2d74SCharles.Forsyth 	return -1;
945*8a8c2d74SCharles.Forsyth }
946*8a8c2d74SCharles.Forsyth 
947*8a8c2d74SCharles.Forsyth int
pcigetpms(Pcidev * p)948*8a8c2d74SCharles.Forsyth pcigetpms(Pcidev* p)
949*8a8c2d74SCharles.Forsyth {
950*8a8c2d74SCharles.Forsyth 	int pmcsr, ptr;
951*8a8c2d74SCharles.Forsyth 
952*8a8c2d74SCharles.Forsyth 	if((ptr = pcigetpmrb(p)) == -1)
953*8a8c2d74SCharles.Forsyth 		return -1;
954*8a8c2d74SCharles.Forsyth 
955*8a8c2d74SCharles.Forsyth 	/*
956*8a8c2d74SCharles.Forsyth 	 * Power Management Register Block:
957*8a8c2d74SCharles.Forsyth 	 *  offset 0:	Capability ID
958*8a8c2d74SCharles.Forsyth 	 *	   1:	next item pointer
959*8a8c2d74SCharles.Forsyth 	 *	   2:	capabilities
960*8a8c2d74SCharles.Forsyth 	 *	   4:	control/status
961*8a8c2d74SCharles.Forsyth 	 *	   6:	bridge support extensions
962*8a8c2d74SCharles.Forsyth 	 *	   7:	data
963*8a8c2d74SCharles.Forsyth 	 */
964*8a8c2d74SCharles.Forsyth 	pmcsr = pcicfgr16(p, ptr+4);
965*8a8c2d74SCharles.Forsyth 
966*8a8c2d74SCharles.Forsyth 	return pmcsr & 0x0003;
967*8a8c2d74SCharles.Forsyth }
968*8a8c2d74SCharles.Forsyth 
969*8a8c2d74SCharles.Forsyth int
pcisetpms(Pcidev * p,int state)970*8a8c2d74SCharles.Forsyth pcisetpms(Pcidev* p, int state)
971*8a8c2d74SCharles.Forsyth {
972*8a8c2d74SCharles.Forsyth 	int ostate, pmc, pmcsr, ptr;
973*8a8c2d74SCharles.Forsyth 
974*8a8c2d74SCharles.Forsyth 	if((ptr = pcigetpmrb(p)) == -1)
975*8a8c2d74SCharles.Forsyth 		return -1;
976*8a8c2d74SCharles.Forsyth 
977*8a8c2d74SCharles.Forsyth 	pmc = pcicfgr16(p, ptr+2);
978*8a8c2d74SCharles.Forsyth 	pmcsr = pcicfgr16(p, ptr+4);
979*8a8c2d74SCharles.Forsyth 	ostate = pmcsr & 0x0003;
980*8a8c2d74SCharles.Forsyth 	pmcsr &= ~0x0003;
981*8a8c2d74SCharles.Forsyth 
982*8a8c2d74SCharles.Forsyth 	switch(state){
983*8a8c2d74SCharles.Forsyth 	default:
984*8a8c2d74SCharles.Forsyth 		return -1;
985*8a8c2d74SCharles.Forsyth 	case 0:
986*8a8c2d74SCharles.Forsyth 		break;
987*8a8c2d74SCharles.Forsyth 	case 1:
988*8a8c2d74SCharles.Forsyth 		if(!(pmc & 0x0200))
989*8a8c2d74SCharles.Forsyth 			return -1;
990*8a8c2d74SCharles.Forsyth 		break;
991*8a8c2d74SCharles.Forsyth 	case 2:
992*8a8c2d74SCharles.Forsyth 		if(!(pmc & 0x0400))
993*8a8c2d74SCharles.Forsyth 			return -1;
994*8a8c2d74SCharles.Forsyth 		break;
995*8a8c2d74SCharles.Forsyth 	case 3:
996*8a8c2d74SCharles.Forsyth 		break;
997*8a8c2d74SCharles.Forsyth 	}
998*8a8c2d74SCharles.Forsyth 	pmcsr |= state;
999*8a8c2d74SCharles.Forsyth 	pcicfgw16(p, ptr+4, pmcsr);
1000*8a8c2d74SCharles.Forsyth 
1001*8a8c2d74SCharles.Forsyth 	return ostate;
1002*8a8c2d74SCharles.Forsyth }
1003