shithub: plan9front

Download patch

ref: a0404ff58245f16d2117542d2dffd1fc6199943d
parent: 35558431105e4c793673310bd5bfa7f8a99d89e3
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Mar 1 11:24:54 EST 2021

devpccard, pci: fix pccard support and handle pci expansion roms

let pci.c deal with the special cardbus controller bar0 and
expansion roms.

handle apic interrupt routing for devices behind a cardbus slot.

do not free the pcidev on card removal, as the drivers
most certanly are not prepared to handle this yet.
instead, we provide a pcidevfree() function that just unlinks
the device from pcilist and the parent bridge.

--- a/sys/src/9/bcm64/pcibcm.c
+++ b/sys/src/9/bcm64/pcibcm.c
@@ -210,7 +210,7 @@
 
 	fmtinstall('T', tbdffmt);
 
-	pciscan(0, &pciroot);
+	pciscan(0, &pciroot, nil);
 	if(pciroot == nil)
 		return;
 
--- a/sys/src/9/mtx/pcimtx.c
+++ b/sys/src/9/mtx/pcimtx.c
@@ -67,7 +67,7 @@
 	list = &pciroot;
 	for(bno = 0; bno <= pcimaxbno; bno++) {
 		int sbno = bno;
-		bno = pciscan(bno, list);
+		bno = pciscan(bno, list, nil);
 
 		while(*list)
 			list = &(*list)->link;
--- a/sys/src/9/pc/devpccard.c
+++ b/sys/src/9/pc/devpccard.c
@@ -101,7 +101,6 @@
 
 	PciPMC = 0xa4,
 
-	Nbars = 6,
 	Ncmd = 10,
 	CBIRQ = 9,
 
@@ -346,7 +345,7 @@
 			break;
 		default:
 			if(DEBUG)
-				print("#Y%ld: Invalid message %s in SlotEmpty state\n",
+				print("#Y%zd: Invalid message %s in SlotEmpty state\n",
 					cb - cbslots, messages[message]);
 			break;
 		}
@@ -365,7 +364,7 @@
 			break;
 		default:
 			if(DEBUG)
-				print("#Y%ld: Invalid message %s in SlotFull state\n",
+				print("#Y%zd: Invalid message %s in SlotFull state\n",
 					cb - cbslots, messages[message]);
 			break;
 		}
@@ -383,7 +382,7 @@
 			powerdown(cb);
 			break;
 		default:
-			print("#Y%ld: Invalid message %s in SlotPowered state\n",
+			print("#Y%zd: Invalid message %s in SlotPowered state\n",
 				cb - cbslots, messages[message]);
 			break;
 		}
@@ -399,7 +398,7 @@
 			break;
 		default:
 			if(DEBUG)
-				print("#Y%ld: Invalid message %s in SlotConfigured state\n",
+				print("#Y%zd: Invalid message %s in SlotConfigured state\n",
 					cb - cbslots, messages[message]);
 			break;
 		}
@@ -492,7 +491,7 @@
 		rdreg(cb, Rcsc);	/* Ack the interrupt */
 
 		if(DEBUG)
-			print("#Y%ld: interrupt: event %.8lX, state %.8lX, (%s)\n",
+			print("#Y%zd: interrupt: event %.8lX, state %.8lX, (%s)\n",
 				cb - cbslots, event, state, states[cb->state]);
 
 		if (event & SE_CCD) {
@@ -520,7 +519,6 @@
 	static int initialized;
 	Pcidev *pci;
 	int i;
-	uchar intl;
 	char *p;
 
 	if (initialized)
@@ -539,9 +537,7 @@
 
 	/* Find all CardBus controllers */
 	pci = nil;
-	intl = 0xff;
 	while ((pci = pcimatch(pci, 0, 0)) != nil) {
-		uvlong baddr;
 		Cardbus *cb;
 		uchar pin;
 
@@ -559,21 +555,21 @@
 		cb->pci = pci;
 		cb->variant = &variant[i];
 
-		if (pci->vid != TI_vid) {
-			/*
-			 * Gross hack, needs a fix.  Inherit the mappings from
-			 * 9load for the TIs (pb)
-			 */
-			pcicfgw32(pci, PciCBMBR0, 0xffffffff);
-			pcicfgw32(pci, PciCBMLR0, 0);
-			pcicfgw32(pci, PciCBMBR1, 0xffffffff);
-			pcicfgw32(pci, PciCBMLR1, 0);
-			pcicfgw32(pci, PciCBIBR0, 0xffffffff);
-			pcicfgw32(pci, PciCBILR0, 0);
-			pcicfgw32(pci, PciCBIBR1, 0xffffffff);
-			pcicfgw32(pci, PciCBILR1, 0);
-		}
+		if(pci->mem[0].size == 0 || (pci->mem[0].bar & 0xF) != 0)
+			continue;
+		cb->regs = (ulong *)vmap(pci->mem[0].bar & ~0xFULL, pci->mem[0].size);
+		if(cb->regs == nil)
+			continue;
 
+		pcicfgw32(pci, PciCBMBR0, 0xffffffff);
+		pcicfgw32(pci, PciCBMLR0, 0);
+		pcicfgw32(pci, PciCBMBR1, 0xffffffff);
+		pcicfgw32(pci, PciCBMLR1, 0);
+		pcicfgw32(pci, PciCBIBR0, 0xffffffff);
+		pcicfgw32(pci, PciCBILR0, 0);
+		pcicfgw32(pci, PciCBIBR1, 0xffffffff);
+		pcicfgw32(pci, PciCBILR1, 0);
+
 		/* Set up PCI bus numbers if needed. */
 		if (pcicfgr8(pci, PciSBN) == 0) {
 			static int busbase = 0x20;
@@ -590,7 +586,7 @@
 			pcicfgw8(pci, PciINTL, pci->intl);
 
 			if (pci->intl == 0xff || pci->intl == 0)
-				print("#Y%ld: No interrupt?\n", cb - cbslots);
+				print("#Y%zd: No interrupt?\n", cb - cbslots);
 		}
 
 		/* Don't you love standards! */
@@ -632,30 +628,15 @@
 			pcicfgw8(cb->pci, 0x94, 0xCA);
 			pcicfgw8(cb->pci, 0xD4, 0xCA);
 		}
-
-		baddr = pcicfgr32(cb->pci, PciBAR0);
-		if (baddr == 0) {
-			int size = (pci->did == Ricoh_478_did)? 0x10000: 0x1000;
-			baddr = upaalloc(-1ULL, size, size);
-			if(baddr == -1)
-				continue;
-			pcicfgw32(cb->pci, PciBAR0, (ulong)baddr);
-			cb->regs = (ulong *)vmap(baddr, size);
-		}
-		else
-			cb->regs = (ulong *)vmap(baddr, 4096);
-		if(cb->regs == nil)
-			continue;
 		cb->state = SlotEmpty;
 
-		if (intl != 0xff && intl != pci->intl)
-			intrenable(pci->intl, cbinterrupt, cb, pci->tbdf, "cardbus");
+		intrenable(pci->intl, cbinterrupt, cb, pci->tbdf, "cardbus");
 
 		/* Don't really know what to do with this... */
 		i82365probe(cb, LegacyAddr, LegacyAddr + 1);
 
-		print("#Y%ld: %s, %.8lluX intl %d\n", cb - cbslots,
-			 variant[i].name, baddr, pci->intl);
+		print("#Y%zd: %s, %.8lluX intl %d\n", cb - cbslots,
+			 variant[i].name, pci->mem[0].bar, pci->intl);
 
 		nslots++;
 	}
@@ -699,7 +680,7 @@
 	state = cb->regs[SocketState];
 	if (state & SS_PC16) {
 		if(DEBUG)
-			print("#Y%ld: Probed a PC16 card, powering up card\n",
+			print("#Y%zd: Probed a PC16 card, powering up card\n",
 				cb - cbslots);
 		cb->type = PC16;
 		memset(&cb->linfo, 0, sizeof(Pcminfo));
@@ -719,12 +700,12 @@
 		return 0;
 
 	if (state & SS_NOTCARD) {
-		print("#Y%ld: No card inserted\n", cb - cbslots);
+		print("#Y%zd: No card inserted\n", cb - cbslots);
 		return 0;
 	}
 
 	if ((state & SS_3V) == 0 && (state & SS_5V) == 0) {
-		print("#Y%ld: Unsupported voltage, powering down card!\n",
+		print("#Y%zd: Unsupported voltage, powering down card!\n",
 			cb - cbslots);
 		cb->regs[SocketControl] = 0;
 		return 0;
@@ -731,7 +712,7 @@
 	}
 
 	if(DEBUG)
-		print("#Y%ld: card %spowered at %d volt\n", cb - cbslots,
+		print("#Y%zd: card %spowered at %d volt\n", cb - cbslots,
 			(state & SS_POWER)? "": "not ",
 			(state & SS_3V)? 3: (state & SS_5V)? 5: -1);
 
@@ -777,10 +758,9 @@
 static void
 configure(Cardbus *cb)
 {
-	int i, r;
 	Pcidev *pci;
-	uvlong romlen, memlen, membase, rombase, bar;
-	ulong iobase, iolen, size;
+	ulong iobase, iolen;
+	uvlong membase, memlen;
 
 	if(DEBUG)
 		print("configuring slot %ld (%s)\n", cb - cbslots, states[cb->state]);
@@ -796,102 +776,68 @@
 	}
 
 	/* Scan the CardBus for new PCI devices */
-	pciscan(pcicfgr8(cb->pci, PciSBN), &cb->pci->bridge);
+	pciscan(pcicfgr8(cb->pci, PciSBN), &cb->pci->bridge, cb->pci);
 
-	/*
-	 * size the devices on the bus, reserve a minimum for devices arriving later,
-	 * allow for ROM space, allocate space, and set the cardbus mapping registers
-	 */
-	pcibussize(cb->pci->bridge, &memlen, &iolen);	/* TO DO: need initial alignments */
+	/* size the devices on the bus, reserve a minimum for devices arriving later */
+	pcibussize(cb->pci->bridge, &memlen, &iolen);
 
-	romlen = 0;
-	for(pci = cb->pci->bridge; pci != nil; pci = pci->list){
-		size = pcibarsize(pci, PciEBAR0);
-		if(size > 0){
-			pci->rom.bar = -1;
-			pci->rom.size = size;
-			romlen += size;
-		}
-	}
-
 	if(iolen < 512)
 		iolen = 512;
-	iobase = ioreserve(-1, iolen, 0, "cardbus");
-	if(iobase == -1)
-		return;
-
-	rombase = memlen;
-	memlen += romlen;
 	if(memlen < 1*1024*1024)
 		memlen = 1*1024*1024;
-	membase = upaalloc(-1ULL, memlen, 4*1024*1024);	/* TO DO: better alignment */
-	if(membase == -1)
+
+	print("#Y%zd: iolen=%lud, memlen=%llud\n", cb - cbslots, iolen, memlen);
+
+	if(cb->pci->parent == nil){
+		iobase = ioreserve(-1,
+			iolen, iolen, "cardbus");
+		if(iobase == -1){
+NoIO:
+			print("#Y%zd: can't allocate io space\n", cb - cbslots);
+			return;
+		}
+		membase = upaalloc(-1ULL,
+			memlen, 4*1024*1024);
+	} else {
+		iobase = ioreservewin(cb->pci->parent->ioa.bar, cb->pci->parent->ioa.size,
+			iolen, iolen, "cardbus");
+		if(iobase == -1)
+			goto NoIO;
+		membase = upaallocwin(cb->pci->parent->mema.bar, cb->pci->parent->mema.size,
+			memlen, 4*1024*1024);
+	}
+	if(membase == -1){
+		print("#Y%zd: can't allocate memory space\n", cb - cbslots);
 		return;
+	}
 
+	print("#Y%zd: iobase=%lux, membase=%llux\n", cb - cbslots, iobase, membase);
+
 	pcicfgw32(cb->pci, PciCBIBR0, iobase);
 	pcicfgw32(cb->pci, PciCBILR0, iobase + iolen-1);
-	pcicfgw32(cb->pci, PciCBIBR1, 0);
+	pcicfgw32(cb->pci, PciCBIBR1, 0xffffffff);
 	pcicfgw32(cb->pci, PciCBILR1, 0);
+	cb->pci->ioa.bar = iobase;
+	cb->pci->ioa.size = iolen;
 
 	pcicfgw32(cb->pci, PciCBMBR0, (ulong)membase);
 	pcicfgw32(cb->pci, PciCBMLR0, (ulong)membase + memlen-1);
-	pcicfgw32(cb->pci, PciCBMBR1, 0);
+	pcicfgw32(cb->pci, PciCBMBR1, 0xffffffff);
 	pcicfgw32(cb->pci, PciCBMLR1, 0);
+	cb->pci->mema.bar = membase;
+	cb->pci->mema.size = memlen;
 
-//	pcibussize(cb->pci->bridge, &membase, &iobase);	/* now assign them */
-	rombase += membase;
+	/* Route interrupts to INTA#/B#, Disable prefetch for MEM0/1 */
+	pcicfgw16(cb->pci, PciBCR, pcicfgr16(cb->pci, PciBCR) & ~(7 << 7));
 
-	for(pci = cb->pci->bridge; pci != nil; pci = pci->list){
-		r = pcicfgr16(pci, PciPCR);
-		r &= ~(PciPCR_IO|PciPCR_MEM);
-		pcicfgw16(pci, PciPCR, r);
+	/* Assign resources */
+	pcibusmap(cb->pci->bridge, &membase, &iobase, 1);
 
-		/*
-		 * Treat the found device as an ordinary PCI card.
-		 * It seems that the CIS is not always present in
-		 * CardBus cards.
-		 * XXX, need to support multifunction cards
-		 */
-		for(i = 0; i < Nbars; i++) {
-			if(pci->mem[i].size == 0)
-				continue;
-			bar = pci->mem[i].bar;
-			if(bar & 1)
-				bar += iobase;
-			else
-				bar += membase;
-			pci->mem[i].bar = bar;
-			pcicfgw32(pci, PciBAR0 + 4*i, bar);
-			if((bar & 1) == 0){
-				print("%T mem[%d] %8.8llux %d\n", pci->tbdf, i, bar, pci->mem[i].size);
-				if(bar & 0x80){	/* TO DO: enable prefetch */
-					;
-				}
-			}
-		}
-		if((size = pcibarsize(pci, PciEBAR0)) > 0) {	/* TO DO: can this be done by pci.c? */
-			pci->rom.bar = rombase;
-			pci->rom.size = size;
-			rombase += size;
-			pcicfgw32(pci, PciEBAR0, pci->rom.bar);
-		}
-
-		/* Set the basic PCI registers for the device */
-		pci->pcr = pcicfgr16(pci, PciPCR);
-		pci->pcr |= PciPCR_IO|PciPCR_MEM|PciPCR_Master;
-		pci->cls = 8;
-		pci->ltr = 64;
-		pcicfgw16(pci, PciPCR, pci->pcr);
-		pcicfgw8(pci, PciCLS, pci->cls);
-		pcicfgw8(pci, PciLTR, pci->ltr);
-
-		if (pcicfgr8(pci, PciINTP)) {
-			pci->intl = pcicfgr8(cb->pci, PciINTL);
+	/* Assign legacy IRQ's */
+	for(pci = cb->pci->bridge; pci != nil; pci = pci->link) {
+		if(pcicfgr8(pci, PciINTP) != 0) {
+			pci->intl = cb->pci->intl;
 			pcicfgw8(pci, PciINTL, pci->intl);
-
-			/* Route interrupts to INTA#/B# */
-			pcicfgw16(cb->pci, PciBCR,
-					  pcicfgr16(cb->pci, PciBCR) & ~(1 << 7));
 		}
 	}
 }
@@ -899,58 +845,51 @@
 static void
 unconfigure(Cardbus *cb)
 {
-	Pcidev *pci;
-	int i, ioindex, memindex, r;
-
 	if (cb->type == PC16) {
-		print("#Y%d: Don't know how to unconfigure a PC16 card\n",
-			 (int)(cb - cbslots));
-
+		print("#Y%zd: Don't know how to unconfigure a PC16 card\n", cb - cbslots);
 		memset(&cb->linfo, 0, sizeof(Pcminfo));
 		return;
 	}
 
-	pci = cb->pci->bridge;
-	if (pci == nil)
-		return;		/* Not configured */
-	cb->pci->bridge = nil;
+	pcicfgw32(cb->pci, PciCBIBR0, 0xffffffff);
+	pcicfgw32(cb->pci, PciCBILR0, 0);
+	pcicfgw32(cb->pci, PciCBIBR1, 0xffffffff);
+	pcicfgw32(cb->pci, PciCBILR1, 0);
 
-	memindex = ioindex = 0;
-	while (pci) {
-		Pcidev *_pci;
+	pcicfgw32(cb->pci, PciCBMBR0, 0xffffffff);
+	pcicfgw32(cb->pci, PciCBMLR0, 0);
+	pcicfgw32(cb->pci, PciCBMBR1, 0xffffffff);
+	pcicfgw32(cb->pci, PciCBMLR1, 0);
 
-		for (i = 0; i != Nbars; i++) {
-			if (pci->mem[i].size == 0)
-				continue;
-			if (pci->mem[i].bar & 1) {
+	/* free i/o space */
+	if(cb->pci->ioa.size){
+		Pcidev *pci;
+		int i;
+
+		for(pci = cb->pci->bridge; pci != nil; pci = pci->link) {
+			for(i = 0; i < nelem(pci->mem); i++) {
+				if((pci->mem[i].size == 0)
+				|| (pci->mem[i].bar & 1) == 0
+				|| (pci->mem[i].bar & ~3) == 0)
+					continue;
 				iofree(pci->mem[i].bar & ~3);
-				pcicfgw16(cb->pci, PciCBIBR0 + ioindex * 8,
-						 (ushort)-1);
-				pcicfgw16(cb->pci, PciCBILR0 + ioindex * 8, 0);
-				ioindex++;
-				continue;
 			}
-
-			upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size);
-			pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, (ulong)-1);
-			pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0);
-			r = pcicfgr16(cb->pci, PciBCR);
-			r &= ~(1 << (8 + memindex));
-			pcicfgw16(cb->pci, PciBCR, r);
-			memindex++;
 		}
+		iofree(cb->pci->ioa.bar);
+		cb->pci->ioa.bar = 0;
+		cb->pci->ioa.size = 0;
+	}
 
-		if (pci->rom.bar && memindex < 2) {
-			upafree(pci->rom.bar & ~0xF, pci->rom.size);
-			pcicfgw32(cb->pci, PciCBMBR0 + memindex * 8, (ulong)-1);
-			pcicfgw32(cb->pci, PciCBMLR0 + memindex * 8, 0);
-			memindex++;
-		}
-
-		_pci = pci->list;
-		free(_pci);
-		pci = _pci;
+	/* free memory space */
+	if(cb->pci->mema.size){
+		upafree(cb->pci->mema.bar, cb->pci->mema.size);
+		cb->pci->mema.bar = 0;
+		cb->pci->mema.size = 0;
 	}
+
+	/* remove the devices */
+	while(cb->pci->bridge != nil)
+		pcidevfree(cb->pci->bridge);
 }
 
 static void
@@ -1174,7 +1113,7 @@
 	pi->irq = isa->irq;
 	unlock(cb);
 
-	print("#Y%ld: %s irq %d, port %lluX\n", cb - cbslots, pi->verstr, isa->irq, isa->port);
+	print("#Y%zd: %s irq %d, port %lluX\n", cb - cbslots, pi->verstr, isa->irq, isa->port);
 	return (int)(cb - cbslots);
 }
 
@@ -1321,10 +1260,10 @@
 				Pcidev *pci = cb->pci->bridge;
 				int i;
 
-				while (pci) {
+				while (pci != nil) {
 					p = seprint(p, e, "%.4uX %.4uX; irq %d\n",
 							  pci->vid, pci->did, pci->intl);
-					for (i = 0; i != Nbars; i++)
+					for (i = 0; i < nelem(pci->mem); i++)
 						if (pci->mem[i].size)
 							p = seprint(p, e,
 									  "\tmem[%d] %.8ullX (%.8uX)\n",
@@ -1333,7 +1272,7 @@
 					if (pci->rom.size)
 						p = seprint(p, e, "\tROM %.8ullX (%.8uX)\n",
 								  pci->rom.bar, pci->rom.size);
-					pci = pci->list;
+					pci = pci->link;
 				}
 			}
 			break;
--- a/sys/src/9/pc/mp.c
+++ b/sys/src/9/pc/mp.c
@@ -351,13 +351,19 @@
 
 	if(bus == nil){
 		/*
-		 * if the PCI device is behind a PCI-PCI bridge thats not described
-		 * by the MP or ACPI tables then walk up the bus translating interrupt
-		 * pin to parent bus.
+		 * if the PCI device is behind a bridge thats not described
+		 * by the MP or ACPI tables then walk up the bus translating
+		 * interrupt pin to parent bus.
 		 */
 		if(pci != nil && pci->parent != nil && pin > 0){
-			pin = ((dno+(pin-1))%4)+1;
 			pci = pci->parent;
+			if(pci->ccrb == 6 && pci->ccru == 7){
+				/* Cardbus bridge, use controllers interrupt pin */
+				pin = pcicfgr8(pci, PciINTP);
+			} else {
+				/* PCI-PCI bridge */
+				pin = ((dno+(pin-1))%4)+1;
+			}
 			bno = BUSBNO(pci->tbdf);
 			dno = BUSDNO(pci->tbdf);
 			goto Findbus;
--- a/sys/src/9/pc/pcipc.c
+++ b/sys/src/9/pc/pcipc.c
@@ -557,6 +557,10 @@
 				upaalloc(pa, p->mem[i].size, 0);
 			}
 		}
+		if(p->rom.size && (p->rom.bar & 1) != 0){
+			pa = p->rom.bar & ~0x7FFULL;
+			upaalloc(pa, p->rom.size, 0);
+		}
 	}
 
 	/*
@@ -688,7 +692,7 @@
 	list = &pciroot;
 	for(bno = 0; bno <= pcimaxbno; bno++) {
 		int sbno = bno;
-		bno = pciscan(bno, list);
+		bno = pciscan(bno, list, nil);
 
 		while(*list)
 			list = &(*list)->link;
--- a/sys/src/9/port/pci.c
+++ b/sys/src/9/port/pci.c
@@ -18,8 +18,7 @@
 int pcimaxdno;
 
 static Lock pcicfglock;
-static Pcidev* pcilist;
-static Pcidev* pcitail;
+static Pcidev *pcilist, **pcitail;
 
 static char* bustypes[] = {
 	"CBUSI",
@@ -68,6 +67,46 @@
 	}
 }
 
+static Pcidev*
+pcidevalloc(void)
+{
+	Pcidev *p;
+
+	p = xalloc(sizeof(*p));
+	if(p == nil)
+		panic("pci: no memory for Pcidev");
+	return p;
+}
+
+void
+pcidevfree(Pcidev *p)
+{
+	Pcidev **l;
+
+	if(p == nil)
+		return;
+
+	while(p->bridge != nil)
+		pcidevfree(p->bridge);
+
+	if(p->parent != nil){
+		for(l = &p->parent->bridge; *l != nil; l = &(*l)->link) {
+			if(*l == p) {
+				*l = p->link;
+				break;
+			}
+		}
+	}
+	for(l = &pcilist; *l != nil; l = &(*l)->list) {
+		if(*l == p) {
+			if((*l = p->list) == nil)
+				pcitail = l;
+			break;
+		}
+	}
+	/* leaked */
+}
+
 int
 pcicfgr8(Pcidev* p, int rno)
 {
@@ -135,12 +174,15 @@
 	pcicfgrw32(p->tbdf, rno, v, 0);
 	iunlock(&pcicfglock);
 
-	if(v & 1){
+	if(rno == PciEBAR0 || rno == PciEBAR1){
+		size &= ~0x7FF;
+	} else if(v & 1){
 		size = (short)size;
 		size &= ~3;
 	} else {
 		size &= ~0xF;
 	}
+
 	return -size;
 }
 
@@ -217,7 +259,7 @@
 		ntb++;
 
 	ntb *= (PciCIS-PciBAR0)/4;
-	table = malloc(2*ntb*sizeof(Pcisiz));
+	table = malloc((2*ntb+1)*sizeof(Pcisiz));
 	if(table == nil)
 		panic("pcibusmap: can't allocate memory");
 	itb = table;
@@ -228,6 +270,22 @@
 	 */
 	for(p = root; p != nil; p = p->link) {
 		if(p->ccrb == 0x06) {
+			/* carbus bridge? */
+			if(p->ccru == 0x07){
+				if(pcicfgr32(p, PciBAR0) & 1)
+					continue;
+				size = pcibarsize(p, PciBAR0);
+				if(size == 0)
+					continue;
+				mtb->dev = p;
+				mtb->bar = 0;
+				mtb->siz = size;
+				mtb->typ = 0;
+				mtb++;
+				continue;
+			}
+
+			/* pci bridge? */
 			if(p->ccru != 0x04 || p->bridge == nil)
 				continue;
 
@@ -252,9 +310,27 @@
 			mtb->siz = hole;
 			mtb->typ = 0;
 			mtb++;
+
+			size = pcibarsize(p, PciEBAR1);
+			if(size != 0){
+				mtb->dev = p;
+				mtb->bar = -3;
+				mtb->siz = size;
+				mtb->typ = 0;
+				mtb++;
+			}
 			continue;
 		}
 
+		size = pcibarsize(p, PciEBAR0);
+		if(size != 0){
+			mtb->dev = p;
+			mtb->bar = -2;
+			mtb->siz = size;
+			mtb->typ = 0;
+			mtb++;
+		}
+
 		for(i = 0; i < nelem(p->mem); i++) {
 			rno = PciBAR0 + i*4;
 			v = pcicfgr32(p, rno);
@@ -321,6 +397,14 @@
 			if(tptr->bar == -1) {
 				p->mema.bar = mema;
 				p->mema.size = tptr->siz;
+			} else if(tptr->bar == -2) {
+				p->rom.bar = mema|1;
+				p->rom.size = tptr->siz;
+				pcisetbar(p, PciEBAR0, p->rom.bar);
+			} else if(tptr->bar == -3) {
+				p->rom.bar = mema|1;
+				p->rom.size = tptr->siz;
+				pcisetbar(p, PciEBAR1, p->rom.bar);
 			} else {
 				p->mem[tptr->bar].size = tptr->siz;
 				p->mem[tptr->bar].bar = mema|tptr->typ;
@@ -409,10 +493,10 @@
 	}
 }
 
-static int
-pcilscan(int bno, Pcidev** list, Pcidev *parent)
+int
+pciscan(int bno, Pcidev** list, Pcidev *parent)
 {
-	Pcidev *p, *head, *tail;
+	Pcidev *p, *head, **tail;
 	int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
 
 	maxubn = bno;
@@ -437,19 +521,11 @@
 
 			if(l == 0xFFFFFFFF || l == 0)
 				continue;
-			p = malloc(sizeof(*p));
-			if(p == nil)
-				panic("pcilscan: can't allocate memory");
+			p = pcidevalloc();
 			p->tbdf = tbdf;
 			p->vid = l;
 			p->did = l>>16;
 
-			if(pcilist != nil)
-				pcitail->list = p;
-			else
-				pcilist = p;
-			pcitail = p;
-
 			p->pcr = pcicfgr16(p, PciPCR);
 			p->rid = pcicfgr8(p, PciRID);
 			p->ccrp = pcicfgr8(p, PciCCRp);
@@ -502,10 +578,27 @@
 					}
 					rno += 4;
 				}
+
+				p->rom.bar = (ulong)pcicfgr32(p, PciEBAR0);
+				p->rom.size = pcibarsize(p, PciEBAR0);
 				break;
 
-			case 0x05:		/* memory controller */
 			case 0x06:		/* bridge device */
+				/* cardbus bridge? */
+				if(p->ccru == 0x07){
+					p->mem[0].bar = (ulong)pcicfgr32(p, PciBAR0);
+					p->mem[0].size = pcibarsize(p, PciBAR0);
+					break;
+				}
+
+				/* pci bridge? */
+				if(p->ccru != 0x04)
+					break;
+
+				p->rom.bar = (ulong)pcicfgr32(p, PciEBAR1);
+				p->rom.size = pcibarsize(p, PciEBAR1);
+				break;
+			case 0x05:		/* memory controller */
 			default:
 				break;
 			}
@@ -512,10 +605,16 @@
 
 			p->parent = parent;
 			if(head != nil)
-				tail->link = p;
+				*tail = p;
 			else
 				head = p;
-			tail = p;
+			tail = &p->link;
+
+			if(pcilist != nil)
+				*pcitail = p;
+			else
+				pcilist = p;
+			pcitail = &p->list;
 		}
 	}
 
@@ -529,6 +628,7 @@
 			if(p->ccru == 0x04)
 				break;
 		default:
+			/* check and clear invalid membars for non bridges */
 			for(i = 0; i < nelem(p->mem); i++) {
 				if(p->mem[i].size == 0)
 					continue;
@@ -540,9 +640,24 @@
 					pcisetbar(p, PciBAR0 + i*4, p->mem[i].bar);
 				}
 			}
+			if(p->rom.size) {
+				if((p->rom.bar & 1) == 0
+				|| !pcivalidbar(p, p->rom.bar & ~0x7FFUL, p->rom.size)){
+					p->rom.bar = 0;
+					pcisetbar(p, PciEBAR0, p->rom.bar);
+				}
+			}
 			continue;
 		}
 
+		if(p->rom.size) {
+			if((p->rom.bar & 1) == 0
+			|| !pcivalidbar(p, p->rom.bar & ~0x7FFULL, p->rom.size)){
+				p->rom.bar = 0;
+				pcisetbar(p, PciEBAR1, p->rom.bar);
+			}
+		}
+
 		/*
 		 * If the secondary or subordinate bus number is not
 		 * initialised try to do what the PCI BIOS should have
@@ -581,7 +696,7 @@
 			pcisetwin(p, 0xFFF00000|0, 0);
 			pcisetwin(p, 0xFFF00000|8, 0);
 
-			maxubn = pcilscan(sbn, &p->bridge, p);
+			maxubn = pciscan(sbn, &p->bridge, p);
 			l = (maxubn<<16)|(sbn<<8)|bno;
 
 			pcicfgw32(p, PciPBN, l);
@@ -635,7 +750,7 @@
 
 			if(ubn > maxubn)
 				maxubn = ubn;
-			pcilscan(sbn, &p->bridge, p);
+			pciscan(sbn, &p->bridge, p);
 		}
 	}
 
@@ -642,12 +757,6 @@
 	return maxubn;
 }
 
-int
-pciscan(int bno, Pcidev **list)
-{
-	return pcilscan(bno, list, nil);
-}
-
 void
 pcibussize(Pcidev *root, uvlong *msize, ulong *iosize)
 {
@@ -720,6 +829,8 @@
 				continue;
 			print("%d:%.8llux %d ", i, t->mem[i].bar, t->mem[i].size);
 		}
+		if(t->rom.bar || t->rom.size)
+			print("rom:%.8llux %d ", t->rom.bar, t->rom.size);
 		if(t->ioa.bar || t->ioa.size)
 			print("ioa:%.8llux-%.8llux %d ", t->ioa.bar, t->ioa.bar+t->ioa.size, t->ioa.size);
 		if(t->mema.bar || t->mema.size)
--- a/sys/src/9/port/pci.h
+++ b/sys/src/9/port/pci.h
@@ -96,7 +96,7 @@
 	PciPULR		= 0x2C,		/* prefetchable limit upper 32 bits */
 	PciIUBR		= 0x30,		/* I/O base upper 16 bits */
 	PciIULR		= 0x32,		/* I/O limit upper 16 bits */
-	PciEBAR1	= 0x28,		/* expansion ROM base address */
+	PciEBAR1	= 0x38,		/* expansion ROM base address */
 	PciBCR		= 0x3E,		/* bridge control register */
 };
 
@@ -173,31 +173,34 @@
 	uchar	cls;
 	uchar	ltr;
 
+	uchar	intl;			/* interrupt line */
+
 	struct {
 		uvlong	bar;		/* base address */
 		int	size;
 	} mem[6];
 
-	struct {
+	struct {			/* expansion rom bar */
 		uvlong	bar;	
 		int	size;
 	} rom;
-	uchar	intl;			/* interrupt line */
 
-	Pcidev*	list;
-	Pcidev*	link;			/* next device on this bno */
-
-	Pcidev*	parent;			/* up a bus */
-	Pcidev*	bridge;			/* down a bus */
-	struct {
+	struct {			/* 32-bit io and memory windows */
 		uvlong	bar;
 		int	size;
 	} ioa, mema;
-	struct {
+
+	struct {			/* 64-bit prefechable memory window */
 		uvlong	bar;
 		uvlong	size;
 	} prefa;
 
+	Pcidev*	list;
+	Pcidev*	link;			/* next device on this bno */
+
+	Pcidev*	parent;			/* up a bus */
+	Pcidev*	bridge;			/* down a bus */
+
 	int	pmrb;			/* power management register block */
 	int	msi;			/* MSI capability register block */
 };
@@ -221,6 +224,8 @@
 
 extern int pcimaxdno;
 
+extern void pcidevfree(Pcidev* pcidev);
+
 extern int pcicfgr32(Pcidev* pcidev, int rno);
 extern void pcicfgw32(Pcidev* pcidev, int rno, int data);
 extern int pcicfgr16(Pcidev* pcidev, int rno);
@@ -228,7 +233,7 @@
 extern int pcicfgr8(Pcidev* pcidev, int rno);
 extern void pcicfgw8(Pcidev* pcidev, int rno, int data);
 
-extern int pciscan(int bno, Pcidev **list);
+extern int pciscan(int bno, Pcidev **list, Pcidev *parent);
 extern void pcibusmap(Pcidev *root, uvlong *pmema, ulong *pioa, int wrreg);
 extern void pcibussize(Pcidev *root, uvlong *msize, ulong *iosize);
 
--- a/sys/src/9/teg2/pciteg.c
+++ b/sys/src/9/teg2/pciteg.c
@@ -181,7 +181,7 @@
 	list = &pciroot;
 	/* was bno = 0; trimslice needs to start at 1 */
 	for(bno = 1; bno <= pcimaxbno; bno++) {
-		bno = pciscan(bno, list);
+		bno = pciscan(bno, list, nil);
 		while(*list)
 			list = &(*list)->link;
 	}