| plan 9 kernel history: overview | file list | diff list |
2001/1003/pc/pci.c (diff list | history)
| 2001/1003/sys/src/9/pc/pci.c:1,1197 – 2001/1215/sys/src/9/pc/pci.c:1,1199 (short | long | prev | next) | ||
| 1995/0725 | /* | |
| 1997/0327 | * PCI support code. | |
| 1995/0725 | */ | |
| 1995/0517 | #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" | |
| 1999/0622 | #define DBG if(0) pcilog struct { char output[16384]; int ptr; }PCICONS; int pcilog(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; va_start(arg, fmt); n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); memmove(PCICONS.output+PCICONS.ptr, buf, n); PCICONS.ptr += n; return n; } | |
| 1999/0301 | enum { /* configuration mechanism #1 */ | |
| 1997/0327 | PciADDR = 0xCF8, /* CONFIG_ADDRESS */ PciDATA = 0xCFC, /* CONFIG_DATA */ | |
| 1995/0517 | ||
| 1997/0327 | /* configuration mechanism #2 */ PciCSE = 0xCF8, /* configuration space enable */ PciFORWARD = 0xCFA, /* which bus */ MaxFNO = 7, MaxUBN = 255, }; | |
| 1999/0622 | enum { /* command register */ | |
| 1999/0301 | IOen = (1<<0), MEMen = (1<<1), MASen = (1<<2), MemWrInv = (1<<4), PErrEn = (1<<6), SErrEn = (1<<8), }; | |
| 1997/0327 | static Lock pcicfglock; | |
| 2001/0622 | static QLock pcicfginitlock; | |
| 1995/0725 | static int pcicfgmode = -1; | |
| 2000/0805 | static int pcimaxbno = 7; | |
| 1997/0327 | static int pcimaxdno; static Pcidev* pciroot; | |
| 1997/1011 | static Pcidev* pcilist; | |
| 1997/1101 | static Pcidev* pcitail; | |
| 2000/1108 | static int nobios; | |
| 1995/0725 | ||
| 1997/0327 | static int pcicfgrw32(int, int, int, int); | |
| 1999/0301 | static int pcicfgrw8(int, int, int, int); | |
| 1997/0327 | ||
| 1999/0622 | static char* bustypes[] = { "CBUSI", "CBUSII", "EISA", "FUTURE", "INTERN", "ISA", "MBI", "MBII", "MCA", "MPI", "MPSA", "NUBUS", "PCI", "PCMCIA", "TC", "VL", "VME", "XPRESS", }; #pragma varargck type "T" int static int tbdfconv(va_list* arg, Fconv* f) { char *p; int l, type, tbdf; p = malloc(READSTR); if(p == nil){ strconv("(tbdfconv)", f); return sizeof(int); } switch(f->chr){ case 'T': tbdf = va_arg(*arg, int); type = BUSTYPE(tbdf); if(type < nelem(bustypes)) l = snprint(p, READSTR, bustypes[type]); else l = snprint(p, READSTR, "%d", type); snprint(p+l, READSTR-l, ".%d.%d.%d", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); break; default: snprint(p, READSTR, "(tbdfconv)"); break; } strconv(p, f); free(p); return sizeof(int); } | |
| 1999/0301 | ulong pcibarsize(Pcidev *p, int rno) { ulong v, size; v = pcicfgrw32(p->tbdf, rno, 0, 1); pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); size = pcicfgrw32(p->tbdf, rno, 0, 1); if(v & 1) size |= 0xFFFF0000; pcicfgrw32(p->tbdf, rno, v, 0); return -(size & ~0x0F); } | |
| 1999/0622 | static int | |
| 2000/0506 | pcisizcmp(void *a, void *b) | |
| 1999/0622 | { | |
| 2000/0506 | Pcisiz *aa, *bb; | |
| 1999/0622 | ||
| 2000/0506 | aa = a; bb = b; return aa->siz - bb->siz; | |
| 1999/0622 | } static ulong pcimask(ulong v) { ulong m; m = BI2BY*sizeof(v); for(m = 1<<(m-1); m != 0; m >>= 1) { if(m & v) break; } m--; if((v & m) == 0) return v; v |= m; return v+1; } | |
| 1999/0301 | static void | |
| 1999/0622 | pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg) | |
| 1999/0301 | { | |
| 1999/0622 | Pcidev *p; int ntb, i, size, rno, hole; ulong v, mema, ioa, sioa, smema, base, limit; Pcisiz *table, *tptr, *mtb, *itb; extern void qsort(void*, long, long, int (*)(void*, void*)); | |
| 1999/0301 | ||
| 2000/1108 | if(!nobios) | |
| 1999/0301 | return; ioa = *pioa; | |
| 1999/0622 | mema = *pmema; | |
| 1999/0301 | ||
| 2000/0506 | DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n", | |
| 1999/0622 | wrreg, root->tbdf, mema, ioa); | |
| 1999/0301 | ||
| 1999/0622 | ntb = 0; for(p = root; p != nil; p = p->link) ntb++; ntb *= (PciCIS-PciBAR0)/4; table = malloc(2*ntb*sizeof(Pcisiz)); itb = table; mtb = table+ntb; | |
| 1999/0301 | /* | |
| 1999/0622 | * Build a table of sizes | |
| 1999/0301 | */ | |
| 1999/0622 | for(p = root; p != nil; p = p->link) { | |
| 1999/0301 | if(p->ccrb == 0x06) { | |
| 2000/0517 | if(p->ccru != 0x04 || p->bridge == nil) { | |
| 1999/0622 | // DBG("pci: ignored bridge %T\n", p->tbdf); | |
| 1999/0301 | continue; } | |
| 1999/0622 | sioa = ioa; smema = mema; pcibusmap(p->bridge, &smema, &sioa, 0); | |
| 1999/0301 | ||
| 1999/0622 | hole = pcimask(smema-mema); if(hole < (1<<20)) hole = 1<<20; p->mema.size = hole; | |
| 1999/0301 | ||
| 1999/0622 | hole = pcimask(sioa-ioa); if(hole < (1<<12)) hole = 1<<12; | |
| 1999/0301 | ||
| 1999/0622 | p->ioa.size = hole; | |
| 1999/0301 | ||
| 1999/0622 | itb->dev = p; itb->bar = -1; itb->siz = p->ioa.size; itb++; | |
| 1999/0301 | ||
| 1999/0622 | mtb->dev = p; mtb->bar = -1; mtb->siz = p->mema.size; mtb++; | |
| 1999/0301 | continue; } | |
| 1999/0622 | for(i = 0; i <= 5; i++) { rno = PciBAR0 + i*4; v = pcicfgrw32(p->tbdf, rno, 0, 1); size = pcibarsize(p, rno); | |
| 1999/0301 | if(size == 0) continue; | |
| 1999/0622 | if(v & 1) { itb->dev = p; itb->bar = i; itb->siz = size; itb++; | |
| 1999/0301 | } | |
| 1999/0622 | else { mtb->dev = p; mtb->bar = i; mtb->siz = size; mtb++; | |
| 1999/0301 | } | |
| 1999/0622 | p->mem[i].size = size; | |
| 1999/0301 | } | |
| 1999/0622 | } | |
| 1999/0301 | ||
| 1999/0622 | /* * Sort both tables IO smallest first, Memory largest */ qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp); tptr = table+ntb; qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp); | |
| 1999/0301 | ||
| 1999/0622 | /* * Allocate IO address space on this bus */ for(tptr = table; tptr < itb; tptr++) { hole = tptr->siz; if(tptr->bar == -1) hole = 1<<12; ioa = (ioa+hole-1) & ~(hole-1); p = tptr->dev; if(tptr->bar == -1) p->ioa.bar = ioa; else { p->pcr |= IOen; p->mem[tptr->bar].bar = ioa|1; if(wrreg) pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0); | |
| 1999/0301 | } | |
| 1999/0622 | ioa += tptr->siz; | |
| 1999/0301 | } | |
| 1999/0622 | /* * Allocate Memory address space on this bus */ for(tptr = table+ntb; tptr < mtb; tptr++) { hole = tptr->siz; if(tptr->bar == -1) hole = 1<<20; mema = (mema+hole-1) & ~(hole-1); | |
| 1999/0301 | ||
| 1999/0622 | p = tptr->dev; if(tptr->bar == -1) p->mema.bar = mema; else { p->pcr |= MEMen; p->mem[tptr->bar].bar = mema; if(wrreg) pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0); } mema += tptr->siz; } | |
| 1999/0301 | *pmema = mema; *pioa = ioa; | |
| 1999/0622 | free(table); if(wrreg == 0) return; /* * Finally set all the bridge addresses & registers */ for(p = root; p != nil; p = p->link) { if(p->bridge == nil) { pcicfgrw8(p->tbdf, PciLTR, 64, 0); p->pcr |= MASen; pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0); continue; } base = p->ioa.bar; limit = base+p->ioa.size-1; v = pcicfgrw32(p->tbdf, PciBAR3, 0, 1); v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8); pcicfgrw32(p->tbdf, PciBAR3, v, 0); v = (limit & 0xFFFF0000)|(base>>16); pcicfgrw32(p->tbdf, 0x30, v, 0); base = p->mema.bar; limit = base+p->mema.size-1; v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16); pcicfgrw32(p->tbdf, PciBAR4, v, 0); /* * Disable memory prefetch */ pcicfgrw32(p->tbdf, PciBAR5, 0x0000FFFF, 0); pcicfgrw8(p->tbdf, PciLTR, 64, 0); /* * Enable the bridge */ v = 0xFFFF0000 | IOen | MEMen | MASen; pcicfgrw32(p->tbdf, PciPCR, v, 0); sioa = p->ioa.bar; smema = p->mema.bar; pcibusmap(p->bridge, &smema, &sioa, 1); } | |
| 1999/0301 | } | |
| 1997/0327 | static int | |
| 2001/0622 | pcilscan(int bno, Pcidev** list) | |
| 1995/0725 | { | |
| 1997/0327 | Pcidev *p, *head, *tail; | |
| 1998/0312 | int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; | |
| 1997/0327 | maxubn = bno; | |
| 1997/1011 | head = nil; tail = nil; | |
| 1997/0412 | for(dno = 0; dno <= pcimaxdno; dno++){ | |
| 1997/0327 | maxfno = 0; for(fno = 0; fno <= maxfno; fno++){ /* | |
| 1999/0301 | * For this possible device, form the * bus+device+function triplet needed to address it * and try to read the vendor and device ID. * If successful, allocate a device struct and * start to fill it in with some useful information * from the device's configuration space. | |
| 1997/0327 | */ tbdf = MKBUS(BusPCI, bno, dno, fno); l = pcicfgrw32(tbdf, PciVID, 0, 1); if(l == 0xFFFFFFFF || l == 0) continue; p = malloc(sizeof(*p)); p->tbdf = tbdf; p->vid = l; p->did = l>>16; | |
| 1997/1101 | if(pcilist != nil) pcitail->list = p; else pcilist = p; pcitail = p; | |
| 1997/1011 | ||
| 2000/0506 | p->rid = pcicfgr8(p, PciRID); | |
| 1999/0301 | p->ccrp = pcicfgr8(p, PciCCRp); p->ccru = pcicfgr8(p, PciCCRu); p->ccrb = pcicfgr8(p, PciCCRb); | |
| 1999/0622 | p->pcr = pcicfgr32(p, PciPCR); | |
| 1997/0327 | ||
| 2000/0506 | p->intl = pcicfgr8(p, PciINTL); | |
| 1998/0312 | /* * If the device is a multi-function device adjust the * loop count so all possible functions are checked. */ hdt = pcicfgr8(p, PciHDT); if(hdt & 0x80) maxfno = MaxFNO; /* * If appropriate, read the base address registers * and work out the sizes. */ | |
| 1999/0301 | switch(p->ccrb) { | |
| 1998/0312 | case 0x01: /* mass storage controller */ case 0x02: /* network controller */ case 0x03: /* display controller */ case 0x04: /* multimedia device */ | |
| 1999/0301 | case 0x07: /* simple comm. controllers */ | |
| 1998/0312 | case 0x08: /* base system peripherals */ case 0x09: /* input devices */ case 0x0A: /* docking stations */ case 0x0B: /* processors */ case 0x0C: /* serial bus controllers */ if((hdt & 0x7F) != 0) break; rno = PciBAR0 - 4; | |
| 1999/0301 | for(i = 0; i < nelem(p->mem); i++) { | |
| 1998/0312 | rno += 4; p->mem[i].bar = pcicfgr32(p, rno); | |
| 1999/0301 | p->mem[i].size = pcibarsize(p, rno); | |
| 1998/0312 | } break; case 0x00: case 0x05: /* memory controller */ case 0x06: /* bridge device */ default: break; | |
| 1997/0327 | } | |
| 1997/1011 | if(head != nil) tail->link = p; else head = p; tail = p; | |
| 1997/0327 | } } | |
| 1997/1011 | *list = head; for(p = head; p != nil; p = p->link){ | |
| 1997/0327 | /* | |
| 1998/0312 | * Find PCI-PCI bridges and recursively descend the tree. | |
| 1997/0327 | */ | |
| 2000/0517 | if(p->ccrb != 0x06 || p->ccru != 0x04) | |
| 1997/1011 | continue; | |
| 1995/0725 | ||
| 1997/0327 | /* | |
| 1999/0301 | * If the secondary or subordinate bus number is not * initialised try to do what the PCI BIOS should have * done and fill in the numbers as the tree is descended. * On the way down the subordinate bus number is set to * the maximum as it's not known how many buses are behind * this one; the final value is set on the way back up. | |
| 1997/0327 | */ | |
| 1998/0312 | sbn = pcicfgr8(p, PciSBN); ubn = pcicfgr8(p, PciUBN); | |
| 1999/0301 | ||
| 2000/1108 | if(sbn == 0 || ubn == 0 || nobios) { | |
| 1997/0327 | sbn = maxubn+1; /* | |
| 1999/0301 | * Make sure memory, I/O and master enables are * off, set the primary, secondary and subordinate * bus numbers and clear the secondary status before * attempting to scan the secondary bus. | |
| 1997/0327 | * * Initialisation of the bridge should be done here. */ | |
| 1998/0312 | pcicfgw32(p, PciPCR, 0xFFFF0000); | |
| 1997/0327 | l = (MaxUBN<<16)|(sbn<<8)|bno; | |
| 1998/0312 | pcicfgw32(p, PciPBN, l); pcicfgw16(p, PciSPSR, 0xFFFF); | |
| 2001/0622 | maxubn = pcilscan(sbn, &p->bridge); | |
| 1997/0327 | l = (maxubn<<16)|(sbn<<8)|bno; | |
| 1999/0301 | ||
| 1998/0312 | pcicfgw32(p, PciPBN, l); | |
| 1997/0327 | } | |
| 1999/0301 | else { | |
| 1997/0327 | maxubn = ubn; | |
| 2001/0622 | pcilscan(sbn, &p->bridge); | |
| 1997/0327 | } | |
| 1995/0725 | } | |
| 1997/0327 | return maxubn; | |
| 1995/0725 | } | |
| 2001/0622 | int pciscan(int bno, Pcidev **list) { int ubn; qlock(&pcicfginitlock); ubn = pcilscan(bno, list); qunlock(&pcicfginitlock); return ubn; } | |
| 2001/1003 | static uchar pIIx_link(Pcidev *router, uchar link) { uchar pirq; /* link should be 0x60, 0x61, 0x62, 0x63 */ pirq = pcicfgr8(router, link); return (pirq < 16)? pirq: 0; } static void pIIx_init(Pcidev *router, uchar link, uchar irq) { pcicfgw8(router, link, irq); } static uchar via_link(Pcidev *router, uchar link) { uchar pirq; /* link should be 1, 2, 3, 5 */ pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0; return (link & 1)? (pirq >> 4): (pirq & 15); } static void via_init(Pcidev *router, uchar link, uchar irq) { uchar pirq; pirq = pcicfgr8(router, 0x55 + (link >> 1)); pirq &= (link & 1)? 0x0f: 0xf0; pirq |= (link & 1)? (irq << 4): (irq & 15); pcicfgw8(router, 0x55 + (link>>1), pirq); } static uchar opti_link(Pcidev *router, uchar link) { uchar pirq = 0; /* link should be 0x02, 0x12, 0x22, 0x32 */ if ((link & 0xcf) == 0x02) pirq = pcicfgr8(router, 0xb8 + (link >> 5)); return (link & 0x10)? (pirq >> 4): (pirq & 15); } static void opti_init(Pcidev *router, uchar link, uchar irq) { uchar pirq; pirq = pcicfgr8(router, 0xb8 + (link >> 5)); pirq &= (link & 0x10)? 0x0f : 0xf0; pirq |= (link & 0x10)? (irq << 4): (irq & 15); pcicfgw8(router, 0xb8 + (link >> 5), pirq); } static uchar ali_link(Pcidev *router, uchar link) { /* No, you're not dreaming */ static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; uchar pirq; /* link should be 0x01..0x08 */ pirq = pcicfgr8(router, 0x48 + ((link-1)>>1)); return (link & 1)? map[pirq&15]: map[pirq>>4]; } static void ali_init(Pcidev *router, uchar link, uchar irq) { /* Inverse of map in ali_link */ static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; uchar pirq; pirq = pcicfgr8(router, 0x48 + ((link-1)>>1)); pirq &= (link & 1)? 0x0f: 0xf0; pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15); pcicfgw8(router, 0x48 + ((link-1)>>1), pirq); } static uchar cyrix_link(Pcidev *router, uchar link) { uchar pirq; /* link should be 1, 2, 3, 4 */ pirq = pcicfgr8(router, 0x5c + ((link-1)>>1)); return ((link & 1)? pirq >> 4: pirq & 15); } static void cyrix_init(Pcidev *router, uchar link, uchar irq) { uchar pirq; pirq = pcicfgr8(router, 0x5c + (link>>1)); pirq &= (link & 1)? 0x0f: 0xf0; pirq |= (link & 1)? (irq << 4): (irq & 15); pcicfgw8(router, 0x5c + (link>>1), pirq); } enum { Intel = 0x8086, Intel_82371FB_0 = 0x122e, Intel_82371SB_0 = 0x7000, Intel_82371AB_0 = 0x7110, Intel_82443MX_1 = 0x7198, Intel_82801AA_0 = 0x2410, Intel_82801AB_0 = 0x2420, Intel_82801BA_0 = 0x2440, Intel_82801BAM_0 = 0x244c, Viatech = 0x1106, Via_82C586_0 = 0x0586, Via_82C596 = 0x0596, Via_82C686 = 0x0686, Opti = 0x1045, Opti_82C700 = 0xc700, Al = 0x10b9, Al_M1533 = 0x1533, SI = 0x1039, SI_503 = 0x0008, SI_496 = 0x0496, Cyrix = 0x1078, Cyrix_5530_Legacy = 0x0100, }; typedef struct { ushort sb_vid, sb_did; uchar (*sb_translate)(Pcidev *, uchar); void (*sb_initialize)(Pcidev *, uchar, uchar); } bridge_t; static bridge_t southbridges[] = { { Intel, Intel_82371FB_0, pIIx_link, pIIx_init }, { Intel, Intel_82371SB_0, pIIx_link, pIIx_init }, { Intel, Intel_82371AB_0, pIIx_link, pIIx_init }, { Intel, Intel_82443MX_1, pIIx_link, pIIx_init }, { Intel, Intel_82801AA_0, pIIx_link, pIIx_init }, { Intel, Intel_82801AB_0, pIIx_link, pIIx_init }, { Intel, Intel_82801BA_0, pIIx_link, pIIx_init }, { Intel, Intel_82801BAM_0, pIIx_link, pIIx_init }, { Viatech, Via_82C586_0, via_link, via_init }, { Viatech, Via_82C596, via_link, via_init }, { Viatech, Via_82C686, via_link, via_init }, { Opti, Opti_82C700, opti_link, opti_init }, { Al, Al_M1533, ali_link, ali_init }, { SI, SI_503, pIIx_link, pIIx_init }, { SI, SI_496, pIIx_link, pIIx_init }, { Cyrix, Cyrix_5530_Legacy, cyrix_link, cyrix_init } }; typedef struct { uchar e_bus; // Pci bus number uchar e_dev; // Pci device number uchar e_maps[12]; // Avoid structs! Link and mask. uchar e_slot; // Add-in/built-in slot uchar e_reserved; } slot_t; typedef struct { uchar rt_signature[4]; // Routing table signature uchar rt_version[2]; // Version number uchar rt_size[2]; // Total table size uchar rt_bus; // Interrupt router bus number uchar rt_devfn; // Router's devfunc uchar rt_pciirqs[2]; // Exclusive PCI irqs uchar rt_compat[4]; // Compatible PCI interrupt router uchar rt_miniport[4]; // Miniport data uchar rt_reserved[11]; uchar rt_checksum; } router_t; static ushort pciirqs; // Exclusive PCI irqs static bridge_t *southbridge; // Which southbridge to use. | |
| 1997/0327 | static void | |
| 2001/1003 | pcirouting(void) { uchar *p, pin, irq; ulong tbdf, vdid; ushort vid, did; router_t *r; slot_t *e; int size, i, fn; Pcidev *sbpci, *pci; // Peek in the BIOS for (p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16) if (p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R') break; if (p >= (uchar *)KADDR(0xfffff)) return; r = (router_t *)p; // print("PCI interrupt routing table version %d.%d at %.6uX\n", // r->rt_version[0], r->rt_version[1], (ulong)r & 0xfffff); tbdf = (BusPCI << 24)|(r->rt_bus << 16)|(r->rt_devfn << 8); vdid = pcicfgrw32(tbdf, PciVID, 0, 1); vid = vdid; did = vdid >> 16; for (i = 0; i != nelem(southbridges); i++) if (vid == southbridges[i].sb_vid && did == southbridges[i].sb_did) break; if (i == nelem(southbridges)) { print("pcirouting: South bridge %.4uX, %.4uX not found\n", vid, did); return; } southbridge = &southbridges[i]; if ((sbpci = pcimatch(nil, vid, did)) == nil) { print("pcirouting: Cannot match south bridge %.4uX, %.4uX\n", vid, did); return; } pciirqs = (r->rt_pciirqs[1] << 8)|r->rt_pciirqs[0]; size = (r->rt_size[1] << 8)|r->rt_size[0]; for (e = (slot_t *)&r[1]; (uchar *)e < p + size; e++) { // print("%.2uX/%.2uX %.2uX: ", e->e_bus, e->e_dev, e->e_slot); // for (i = 0; i != 4; i++) { // uchar *m = &e->e_maps[i * 3]; // print("[%d] %.2uX %.4uX ", // i, m[0], (m[2] << 8)|m[1]); // } // print("\n"); for (fn = 0; fn != 8; fn++) { uchar *m; // Retrieve the did and vid through the devfn before // obtaining the Pcidev structure. tbdf = (BusPCI << 24)|(e->e_bus << 16)|((e->e_dev | fn) << 8); vdid = pcicfgrw32(tbdf, PciVID, 0, 1); if (vdid == 0xFFFFFFFF || vdid == 0) continue; vid = vdid; did = vdid >> 16; pci = nil; while ((pci = pcimatch(pci, vid, did)) != nil) { if (pci->intl != 0 && pci->intl != 0xFF) continue; pin = pcicfgr8(pci, PciINTP); if (pin == 0 || pin == 0xff) continue; m = &e->e_maps[(pin - 1) * 3]; irq = southbridge->sb_translate(sbpci, m[0]); if (irq) { print("pcirouting: %.4uX/%.4uX at pin %d irq %d\n", vid, did, pin, irq); pcicfgw8(pci, PciINTL, irq); pci->intl = irq; } } } } } static void | |
| 1997/0327 | pcicfginit(void) | |
| 1995/0517 | { | |
| 1998/0312 | char *p; int bno; Pcidev **list; | |
| 1999/0622 | ulong mema, ioa; | |
| 1998/0312 | ||
| 2001/0622 | qlock(&pcicfginitlock); | |
| 1999/0301 | if(pcicfgmode != -1) goto out; | |
| 2000/1108 | if (getconf("*nobios")) nobios = 1; | |
| 1999/0301 | /* * Try to determine which PCI configuration mode is implemented. * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses * a DWORD at 0xCF8 and another at 0xCFC and will pass through * any non-DWORD accesses as normal I/O cycles. There shouldn't be * a device behind these addresses so if Mode2 accesses fail try * for Mode1 (which is preferred, Mode2 is deprecated). */ outb(PciCSE, 0); if(inb(PciCSE) == 0){ pcicfgmode = 2; pcimaxdno = 15; } else { outl(PciADDR, 0); if(inl(PciADDR) == 0){ pcicfgmode = 1; pcimaxdno = 31; } } if(pcicfgmode < 0) goto out; | |
| 1999/0622 | fmtinstall('T', tbdfconv); | |
| 2000/0617 | if(p = getconf("*pcimaxbno")) pcimaxbno = strtoul(p, 0, 0); | |
| 1999/0301 | if(p = getconf("*pcimaxdno")) pcimaxdno = strtoul(p, 0, 0); list = &pciroot; | |
| 2000/0617 | for(bno = 0; bno <= pcimaxbno; bno++) { | |
| 2001/0905 | int sbno = bno; | |
| 2001/0622 | bno = pcilscan(bno, list); | |
| 2001/0905 | ||
| 1999/0301 | while(*list) list = &(*list)->link; | |
| 2001/0905 | if (sbno == 0) { Pcidev *pci; /* * If we have found a PCI-to-Cardbus bridge, make sure * it has no valid mappings anymore. */ pci = pciroot; while (pci) { if (pci->ccrb == 6 && pci->ccru == 7) { ushort bcr; /* reset the cardbus */ bcr = pcicfgr16(pci, PciBCR); pcicfgw16(pci, PciBCR, 0x40 | bcr); | |
| 2001/0925 | delay(50); | |
| 2001/0905 | } pci = pci->link; } } | |
| 1999/0301 | } | |
| 1999/0622 | if(pciroot == nil) goto out; | |
| 2000/1108 | if(nobios) { | |
| 1997/0327 | /* | |
| 1999/0301 | * Work out how big the top bus is | |
| 1997/0327 | */ | |
| 1999/0301 | mema = 0; ioa = 0; pcibusmap(pciroot, &mema, &ioa, 0); | |
| 1999/0622 | ||
| 2000/0506 | DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n", mema, pcimask(mema), ioa); | |
| 1997/0327 | ||
| 1999/0301 | /* * Align the windows and map it */ | |
| 1999/0622 | ioa = 0x1000; mema = 0x90000000; pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa); | |
| 1999/0301 | pcibusmap(pciroot, &mema, &ioa, 1); | |
| 2000/0506 | DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa); | |
| 1999/0301 | ||
| 2001/0622 | qunlock(&pcicfginitlock); | |
| 1999/0301 | return; | |
| 1997/0327 | } | |
| 2001/1003 | pcirouting(); | |
| 1999/0301 | out: | |
| 2001/0622 | qunlock(&pcicfginitlock); | |
| 1997/0327 | } | |
| 1995/0517 | ||
| 1997/0327 | static int pcicfgrw8(int tbdf, int rno, int data, int read) { int o, type, x; | |
| 1995/0725 | if(pcicfgmode == -1) | |
| 1997/0327 | pcicfginit(); | |
| 1995/0517 | ||
| 1997/0327 | if(BUSBNO(tbdf)) type = 0x01; else type = 0x00; x = -1; | |
| 1997/0412 | if(BUSDNO(tbdf) > pcimaxdno) | |
| 1997/0327 | return x; lock(&pcicfglock); | |
| 1995/0725 | switch(pcicfgmode){ case 1: | |
| 1997/0327 | o = rno & 0x03; rno &= ~0x03; outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); if(read) x = inb(PciDATA+o); else outb(PciDATA+o, data); outl(PciADDR, 0); | |
| 1995/0725 | break; case 2: | |
| 1997/0327 | outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); outb(PciFORWARD, BUSBNO(tbdf)); if(read) x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno); else outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); outb(PciCSE, 0); | |
| 1995/0725 | break; | |
| 1995/0517 | } unlock(&pcicfglock); | |
| 1997/0327 | return x; | |
| 1995/0517 | } | |
| 1997/0327 | int pcicfgr8(Pcidev* pcidev, int rno) { return pcicfgrw8(pcidev->tbdf, rno, 0, 1); } | |
| 1995/0721 | void | |
| 1997/0327 | pcicfgw8(Pcidev* pcidev, int rno, int data) | |
| 1995/0721 | { | |
| 1997/0327 | pcicfgrw8(pcidev->tbdf, rno, data, 0); } | |
| 1995/0721 | ||
| 1997/0327 | static int pcicfgrw16(int tbdf, int rno, int data, int read) { int o, type, x; | |
| 1995/0725 | if(pcicfgmode == -1) | |
| 1997/0327 | pcicfginit(); | |
| 1995/0721 | ||
| 1997/0327 | if(BUSBNO(tbdf)) type = 0x01; else type = 0x00; x = -1; | |
| 1997/0412 | if(BUSDNO(tbdf) > pcimaxdno) | |
| 1997/0327 | return x; lock(&pcicfglock); | |
| 1995/0725 | switch(pcicfgmode){ case 1: | |
| 1997/0327 | o = rno & 0x02; rno &= ~0x03; outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); if(read) x = ins(PciDATA+o); else outs(PciDATA+o, data); outl(PciADDR, 0); | |
| 1995/0725 | break; case 2: | |
| 1997/0327 | outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); outb(PciFORWARD, BUSBNO(tbdf)); if(read) x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno); else outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); outb(PciCSE, 0); break; | |
| 1995/1206 | } unlock(&pcicfglock); | |
| 1997/0327 | return x; | |
| 1995/1206 | } | |
| 1997/0327 | int pcicfgr16(Pcidev* pcidev, int rno) { return pcicfgrw16(pcidev->tbdf, rno, 0, 1); } | |
| 1995/1206 | void | |
| 1997/0327 | pcicfgw16(Pcidev* pcidev, int rno, int data) | |
| 1995/1206 | { | |
| 1997/0327 | pcicfgrw16(pcidev->tbdf, rno, data, 0); } | |
| 1995/1206 | ||
| 1997/0327 | static int pcicfgrw32(int tbdf, int rno, int data, int read) { int type, x; | |
| 1995/1206 | if(pcicfgmode == -1) | |
| 1997/0327 | pcicfginit(); | |
| 1995/1206 | ||
| 1997/0327 | if(BUSBNO(tbdf)) type = 0x01; else type = 0x00; x = -1; | |
| 1997/0412 | if(BUSDNO(tbdf) > pcimaxdno) | |
| 1997/0327 | return x; lock(&pcicfglock); | |
| 1995/1206 | switch(pcicfgmode){ | |
| 1997/0327 | case 1: rno &= ~0x03; outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); if(read) x = inl(PciDATA); else outl(PciDATA, data); outl(PciADDR, 0); | |
| 1996/0112 | break; | |
| 1995/1206 | case 2: | |
| 1997/0327 | outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); outb(PciFORWARD, BUSBNO(tbdf)); if(read) x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno); else outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); outb(PciCSE, 0); break; | |
| 1995/0721 | } unlock(&pcicfglock); | |
| 1997/0327 | return x; | |
| 1995/0721 | } | |
| 1995/0517 | int | |
| 1997/0327 | pcicfgr32(Pcidev* pcidev, int rno) | |
| 1995/0517 | { | |
| 1997/0327 | return pcicfgrw32(pcidev->tbdf, rno, 0, 1); } | |
| 1995/0517 | ||
| 1997/0327 | void pcicfgw32(Pcidev* pcidev, int rno, int data) { pcicfgrw32(pcidev->tbdf, rno, data, 0); } | |
| 1997/1011 | Pcidev* pcimatch(Pcidev* prev, int vid, int did) { | |
| 1995/0726 | if(pcicfgmode == -1) | |
| 1997/0327 | pcicfginit(); | |
| 1995/0726 | ||
| 1997/1011 | if(prev == nil) prev = pcilist; | |
| 1997/0327 | else | |
| 1997/1011 | prev = prev->list; | |
| 1997/0327 | ||
| 1998/0906 | while(prev != nil){ | |
| 1999/0301 | if((vid == 0 || prev->vid == vid) && (did == 0 || prev->did == did)) | |
| 1997/0327 | break; | |
| 1997/1011 | prev = prev->list; | |
| 1995/0721 | } | |
| 1997/1011 | return prev; | |
| 1998/0906 | } Pcidev* pcimatchtbdf(int tbdf) { Pcidev *pcidev; if(pcicfgmode == -1) pcicfginit(); | |
| 1999/0301 | for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) { | |
| 1998/0906 | if(pcidev->tbdf == tbdf) break; } return pcidev; | |
| 1997/0327 | } | |
| 2001/0928 | uchar pciipin(Pcidev *pci, uchar pin) { if (pci == nil) pci = pcilist; while (pci) { uchar intl; if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff) return pci->intl; if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0) return intl; pci = pci->list; } return 0; } | |
| 2001/0622 | static void pcilhinv(Pcidev* p) | |
| 1997/1011 | { int i; Pcidev *t; if(p == nil) { | |
| 2000/0506 | putstrn(PCICONS.output, PCICONS.ptr); | |
| 1997/1011 | p = pciroot; | |
| 1998/0322 | print("bus dev type vid did intl memory\n"); | |
| 1997/1011 | } for(t = p; t != nil; t = t->link) { | |
| 1999/0622 | print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ", | |
| 1997/1011 | BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), | |
| 1999/0301 | t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl); | |
| 1997/1011 | for(i = 0; i < nelem(p->mem); i++) { if(t->mem[i].size == 0) continue; print("%d:%.8lux %d ", i, t->mem[i].bar, t->mem[i].size); } | |
| 1999/0622 | if(t->ioa.bar || t->ioa.size) print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size); if(t->mema.bar || t->mema.size) print("mema:%.8lux %d ", t->mema.bar, t->mema.size); if(t->bridge) print("->%d", BUSBNO(t->bridge->tbdf)); | |
| 1997/1011 | print("\n"); } while(p != nil) { if(p->bridge != nil) | |
| 2001/0925 | pcilhinv(p->bridge); | |
| 1997/1011 | p = p->link; | |
| 2001/0622 | } } void pcihinv(Pcidev* p) { | |
| 2001/1215 | if(pcicfgmode == -1) pcicfginit(); | |
| 2001/0622 | qlock(&pcicfginitlock); pcilhinv(p); qunlock(&pcicfginitlock); | |
| 1998/0108 | } void pcireset(void) { Pcidev *p; int pcr; if(pcicfgmode == -1) pcicfginit(); for(p = pcilist; p != nil; p = p->list){ | |
| 1999/0314 | pcr = pcicfgr16(p, PciPCR); pcr &= ~0x0004; pcicfgw16(p, PciPCR, pcr); | |
| 1998/0108 | } | |
| 1999/0314 | } void pcisetbme(Pcidev* p) { int pcr; pcr = pcicfgr16(p, PciPCR); | |
| 1999/0622 | pcr |= MASen; | |
| 2001/0905 | pcicfgw16(p, PciPCR, pcr); } void pciclrbme(Pcidev* p) { int pcr; pcr = pcicfgr16(p, PciPCR); pcr &= ~MASen; | |
| 1999/0314 | pcicfgw16(p, PciPCR, pcr); | |
| 1995/0517 | } | |