17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier * PCI support code.
3e569ccb5SDavid du Colombier * Needs a massive rewrite.
47dd7cddfSDavid du Colombier */
57dd7cddfSDavid du Colombier #include "u.h"
67dd7cddfSDavid du Colombier #include "../port/lib.h"
77dd7cddfSDavid du Colombier #include "mem.h"
87dd7cddfSDavid du Colombier #include "dat.h"
97dd7cddfSDavid du Colombier #include "fns.h"
107dd7cddfSDavid du Colombier #include "io.h"
117dd7cddfSDavid du Colombier
127dd7cddfSDavid du Colombier #define DBG if(0) pcilog
137dd7cddfSDavid du Colombier
147dd7cddfSDavid du Colombier struct
157dd7cddfSDavid du Colombier {
16cc162d66SDavid du Colombier char output[PCICONSSIZE];
177dd7cddfSDavid du Colombier int ptr;
187dd7cddfSDavid du Colombier }PCICONS;
197dd7cddfSDavid du Colombier
202210c76eSDavid du Colombier int pcivga;
212210c76eSDavid du Colombier
227dd7cddfSDavid du Colombier int
pcilog(char * fmt,...)237dd7cddfSDavid du Colombier pcilog(char *fmt, ...)
247dd7cddfSDavid du Colombier {
257dd7cddfSDavid du Colombier int n;
267dd7cddfSDavid du Colombier va_list arg;
277dd7cddfSDavid du Colombier char buf[PRINTSIZE];
287dd7cddfSDavid du Colombier
297dd7cddfSDavid du Colombier va_start(arg, fmt);
309a747e4fSDavid du Colombier n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
317dd7cddfSDavid du Colombier va_end(arg);
327dd7cddfSDavid du Colombier
337dd7cddfSDavid du Colombier memmove(PCICONS.output+PCICONS.ptr, buf, n);
347dd7cddfSDavid du Colombier PCICONS.ptr += n;
357dd7cddfSDavid du Colombier return n;
367dd7cddfSDavid du Colombier }
377dd7cddfSDavid du Colombier
387dd7cddfSDavid du Colombier enum
397dd7cddfSDavid du Colombier { /* configuration mechanism #1 */
407dd7cddfSDavid du Colombier PciADDR = 0xCF8, /* CONFIG_ADDRESS */
417dd7cddfSDavid du Colombier PciDATA = 0xCFC, /* CONFIG_DATA */
427dd7cddfSDavid du Colombier
437dd7cddfSDavid du Colombier /* configuration mechanism #2 */
447dd7cddfSDavid du Colombier PciCSE = 0xCF8, /* configuration space enable */
457dd7cddfSDavid du Colombier PciFORWARD = 0xCFA, /* which bus */
467dd7cddfSDavid du Colombier
477dd7cddfSDavid du Colombier MaxFNO = 7,
487dd7cddfSDavid du Colombier MaxUBN = 255,
497dd7cddfSDavid du Colombier };
507dd7cddfSDavid du Colombier
517dd7cddfSDavid du Colombier enum
527dd7cddfSDavid du Colombier { /* command register */
537dd7cddfSDavid du Colombier IOen = (1<<0),
547dd7cddfSDavid du Colombier MEMen = (1<<1),
557dd7cddfSDavid du Colombier MASen = (1<<2),
567dd7cddfSDavid du Colombier MemWrInv = (1<<4),
577dd7cddfSDavid du Colombier PErrEn = (1<<6),
587dd7cddfSDavid du Colombier SErrEn = (1<<8),
597dd7cddfSDavid du Colombier };
607dd7cddfSDavid du Colombier
617dd7cddfSDavid du Colombier static Lock pcicfglock;
627dd7cddfSDavid du Colombier static Lock pcicfginitlock;
637dd7cddfSDavid du Colombier static int pcicfgmode = -1;
6459cc4ca5SDavid du Colombier static int pcimaxbno = 7;
657dd7cddfSDavid du Colombier static int pcimaxdno;
667dd7cddfSDavid du Colombier static Pcidev* pciroot;
677dd7cddfSDavid du Colombier static Pcidev* pcilist;
687dd7cddfSDavid du Colombier static Pcidev* pcitail;
693ff48bf5SDavid du Colombier static int nobios, nopcirouting;
704fafed5dSDavid du Colombier static BIOS32si* pcibiossi;
717dd7cddfSDavid du Colombier
724fafed5dSDavid du Colombier static int pcicfgrw8raw(int, int, int, int);
734fafed5dSDavid du Colombier static int pcicfgrw16raw(int, int, int, int);
744fafed5dSDavid du Colombier static int pcicfgrw32raw(int, int, int, int);
754fafed5dSDavid du Colombier
764fafed5dSDavid du Colombier static int (*pcicfgrw8)(int, int, int, int) = pcicfgrw8raw;
774fafed5dSDavid du Colombier static int (*pcicfgrw16)(int, int, int, int) = pcicfgrw16raw;
784fafed5dSDavid du Colombier static int (*pcicfgrw32)(int, int, int, int) = pcicfgrw32raw;
797dd7cddfSDavid du Colombier
807dd7cddfSDavid du Colombier static char* bustypes[] = {
817dd7cddfSDavid du Colombier "CBUSI",
827dd7cddfSDavid du Colombier "CBUSII",
837dd7cddfSDavid du Colombier "EISA",
847dd7cddfSDavid du Colombier "FUTURE",
857dd7cddfSDavid du Colombier "INTERN",
867dd7cddfSDavid du Colombier "ISA",
877dd7cddfSDavid du Colombier "MBI",
887dd7cddfSDavid du Colombier "MBII",
897dd7cddfSDavid du Colombier "MCA",
907dd7cddfSDavid du Colombier "MPI",
917dd7cddfSDavid du Colombier "MPSA",
927dd7cddfSDavid du Colombier "NUBUS",
937dd7cddfSDavid du Colombier "PCI",
947dd7cddfSDavid du Colombier "PCMCIA",
957dd7cddfSDavid du Colombier "TC",
967dd7cddfSDavid du Colombier "VL",
977dd7cddfSDavid du Colombier "VME",
987dd7cddfSDavid du Colombier "XPRESS",
997dd7cddfSDavid du Colombier };
1007dd7cddfSDavid du Colombier
1017dd7cddfSDavid du Colombier static int
tbdffmt(Fmt * fmt)1029a747e4fSDavid du Colombier tbdffmt(Fmt* fmt)
1037dd7cddfSDavid du Colombier {
1047dd7cddfSDavid du Colombier char *p;
10525fc6993SDavid du Colombier int l, r;
10625fc6993SDavid du Colombier uint type, tbdf;
1077dd7cddfSDavid du Colombier
1089a747e4fSDavid du Colombier if((p = malloc(READSTR)) == nil)
1099a747e4fSDavid du Colombier return fmtstrcpy(fmt, "(tbdfconv)");
1107dd7cddfSDavid du Colombier
1119a747e4fSDavid du Colombier switch(fmt->r){
1127dd7cddfSDavid du Colombier case 'T':
1139a747e4fSDavid du Colombier tbdf = va_arg(fmt->args, int);
11425fc6993SDavid du Colombier if(tbdf == BUSUNKNOWN)
11525fc6993SDavid du Colombier snprint(p, READSTR, "unknown");
11625fc6993SDavid du Colombier else{
1177dd7cddfSDavid du Colombier type = BUSTYPE(tbdf);
1187dd7cddfSDavid du Colombier if(type < nelem(bustypes))
1197dd7cddfSDavid du Colombier l = snprint(p, READSTR, bustypes[type]);
1207dd7cddfSDavid du Colombier else
1217dd7cddfSDavid du Colombier l = snprint(p, READSTR, "%d", type);
1227dd7cddfSDavid du Colombier snprint(p+l, READSTR-l, ".%d.%d.%d",
1237dd7cddfSDavid du Colombier BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
12425fc6993SDavid du Colombier }
1257dd7cddfSDavid du Colombier break;
1267dd7cddfSDavid du Colombier
1277dd7cddfSDavid du Colombier default:
1287dd7cddfSDavid du Colombier snprint(p, READSTR, "(tbdfconv)");
1297dd7cddfSDavid du Colombier break;
1307dd7cddfSDavid du Colombier }
1319a747e4fSDavid du Colombier r = fmtstrcpy(fmt, p);
1327dd7cddfSDavid du Colombier free(p);
1337dd7cddfSDavid du Colombier
1349a747e4fSDavid du Colombier return r;
1357dd7cddfSDavid du Colombier }
1367dd7cddfSDavid du Colombier
1377dd7cddfSDavid du Colombier ulong
pcibarsize(Pcidev * p,int rno)1387dd7cddfSDavid du Colombier pcibarsize(Pcidev *p, int rno)
1397dd7cddfSDavid du Colombier {
1407dd7cddfSDavid du Colombier ulong v, size;
1417dd7cddfSDavid du Colombier
1427dd7cddfSDavid du Colombier v = pcicfgrw32(p->tbdf, rno, 0, 1);
1437dd7cddfSDavid du Colombier pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
1447dd7cddfSDavid du Colombier size = pcicfgrw32(p->tbdf, rno, 0, 1);
1457dd7cddfSDavid du Colombier if(v & 1)
1467dd7cddfSDavid du Colombier size |= 0xFFFF0000;
1477dd7cddfSDavid du Colombier pcicfgrw32(p->tbdf, rno, v, 0);
1487dd7cddfSDavid du Colombier
1497dd7cddfSDavid du Colombier return -(size & ~0x0F);
1507dd7cddfSDavid du Colombier }
1517dd7cddfSDavid du Colombier
1527dd7cddfSDavid du Colombier static int
pcisizcmp(void * a,void * b)1537dd7cddfSDavid du Colombier pcisizcmp(void *a, void *b)
1547dd7cddfSDavid du Colombier {
1557dd7cddfSDavid du Colombier Pcisiz *aa, *bb;
1567dd7cddfSDavid du Colombier
1577dd7cddfSDavid du Colombier aa = a;
1587dd7cddfSDavid du Colombier bb = b;
1597dd7cddfSDavid du Colombier return aa->siz - bb->siz;
1607dd7cddfSDavid du Colombier }
1617dd7cddfSDavid du Colombier
1627dd7cddfSDavid du Colombier static ulong
pcimask(ulong v)1637dd7cddfSDavid du Colombier pcimask(ulong v)
1647dd7cddfSDavid du Colombier {
1657dd7cddfSDavid du Colombier ulong m;
1667dd7cddfSDavid du Colombier
1677dd7cddfSDavid du Colombier m = BI2BY*sizeof(v);
1687dd7cddfSDavid du Colombier for(m = 1<<(m-1); m != 0; m >>= 1) {
1697dd7cddfSDavid du Colombier if(m & v)
1707dd7cddfSDavid du Colombier break;
1717dd7cddfSDavid du Colombier }
1727dd7cddfSDavid du Colombier
1737dd7cddfSDavid du Colombier m--;
1747dd7cddfSDavid du Colombier if((v & m) == 0)
1757dd7cddfSDavid du Colombier return v;
1767dd7cddfSDavid du Colombier
1777dd7cddfSDavid du Colombier v |= m;
1787dd7cddfSDavid du Colombier return v+1;
1797dd7cddfSDavid du Colombier }
1807dd7cddfSDavid du Colombier
1817dd7cddfSDavid du Colombier static void
pcibusmap(Pcidev * root,ulong * pmema,ulong * pioa,int wrreg)1827dd7cddfSDavid du Colombier pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
1837dd7cddfSDavid du Colombier {
1847dd7cddfSDavid du Colombier Pcidev *p;
1857dd7cddfSDavid du Colombier int ntb, i, size, rno, hole;
1867dd7cddfSDavid du Colombier ulong v, mema, ioa, sioa, smema, base, limit;
1877dd7cddfSDavid du Colombier Pcisiz *table, *tptr, *mtb, *itb;
1887dd7cddfSDavid du Colombier
1894fafed5dSDavid du Colombier if(!nobios)
1904fafed5dSDavid du Colombier return;
1914fafed5dSDavid du Colombier
1927dd7cddfSDavid du Colombier ioa = *pioa;
1937dd7cddfSDavid du Colombier mema = *pmema;
1947dd7cddfSDavid du Colombier
1957dd7cddfSDavid du Colombier DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n",
1967dd7cddfSDavid du Colombier wrreg, root->tbdf, mema, ioa);
1977dd7cddfSDavid du Colombier
1987dd7cddfSDavid du Colombier ntb = 0;
1997dd7cddfSDavid du Colombier for(p = root; p != nil; p = p->link)
2007dd7cddfSDavid du Colombier ntb++;
2017dd7cddfSDavid du Colombier
2027dd7cddfSDavid du Colombier ntb *= (PciCIS-PciBAR0)/4;
2037dd7cddfSDavid du Colombier table = malloc(2*ntb*sizeof(Pcisiz));
204aa72973aSDavid du Colombier if(table == nil)
20540ff3dfeSDavid du Colombier panic("pcibusmap: no memory");
2067dd7cddfSDavid du Colombier itb = table;
2077dd7cddfSDavid du Colombier mtb = table+ntb;
2087dd7cddfSDavid du Colombier
2097dd7cddfSDavid du Colombier /*
2107dd7cddfSDavid du Colombier * Build a table of sizes
2117dd7cddfSDavid du Colombier */
2127dd7cddfSDavid du Colombier for(p = root; p != nil; p = p->link) {
2137dd7cddfSDavid du Colombier if(p->ccrb == 0x06) {
2147dd7cddfSDavid du Colombier if(p->ccru != 0x04 || p->bridge == nil) {
2157dd7cddfSDavid du Colombier // DBG("pci: ignored bridge %T\n", p->tbdf);
2167dd7cddfSDavid du Colombier continue;
2177dd7cddfSDavid du Colombier }
2187dd7cddfSDavid du Colombier
2197dd7cddfSDavid du Colombier sioa = ioa;
2207dd7cddfSDavid du Colombier smema = mema;
2217dd7cddfSDavid du Colombier pcibusmap(p->bridge, &smema, &sioa, 0);
2227dd7cddfSDavid du Colombier
2237dd7cddfSDavid du Colombier hole = pcimask(smema-mema);
2247dd7cddfSDavid du Colombier if(hole < (1<<20))
2257dd7cddfSDavid du Colombier hole = 1<<20;
2267dd7cddfSDavid du Colombier p->mema.size = hole;
2277dd7cddfSDavid du Colombier
2287dd7cddfSDavid du Colombier hole = pcimask(sioa-ioa);
2297dd7cddfSDavid du Colombier if(hole < (1<<12))
2307dd7cddfSDavid du Colombier hole = 1<<12;
2317dd7cddfSDavid du Colombier
2327dd7cddfSDavid du Colombier p->ioa.size = hole;
2337dd7cddfSDavid du Colombier
2347dd7cddfSDavid du Colombier itb->dev = p;
2357dd7cddfSDavid du Colombier itb->bar = -1;
2367dd7cddfSDavid du Colombier itb->siz = p->ioa.size;
2377dd7cddfSDavid du Colombier itb++;
2387dd7cddfSDavid du Colombier
2397dd7cddfSDavid du Colombier mtb->dev = p;
2407dd7cddfSDavid du Colombier mtb->bar = -1;
2417dd7cddfSDavid du Colombier mtb->siz = p->mema.size;
2427dd7cddfSDavid du Colombier mtb++;
2437dd7cddfSDavid du Colombier continue;
2447dd7cddfSDavid du Colombier }
2457dd7cddfSDavid du Colombier
2467dd7cddfSDavid du Colombier for(i = 0; i <= 5; i++) {
2477dd7cddfSDavid du Colombier rno = PciBAR0 + i*4;
2487dd7cddfSDavid du Colombier v = pcicfgrw32(p->tbdf, rno, 0, 1);
2497dd7cddfSDavid du Colombier size = pcibarsize(p, rno);
2507dd7cddfSDavid du Colombier if(size == 0)
2517dd7cddfSDavid du Colombier continue;
2527dd7cddfSDavid du Colombier
2537dd7cddfSDavid du Colombier if(v & 1) {
2547dd7cddfSDavid du Colombier itb->dev = p;
2557dd7cddfSDavid du Colombier itb->bar = i;
2567dd7cddfSDavid du Colombier itb->siz = size;
2577dd7cddfSDavid du Colombier itb++;
2587dd7cddfSDavid du Colombier }
2597dd7cddfSDavid du Colombier else {
2607dd7cddfSDavid du Colombier mtb->dev = p;
2617dd7cddfSDavid du Colombier mtb->bar = i;
2627dd7cddfSDavid du Colombier mtb->siz = size;
2637dd7cddfSDavid du Colombier mtb++;
2647dd7cddfSDavid du Colombier }
2657dd7cddfSDavid du Colombier
2667dd7cddfSDavid du Colombier p->mem[i].size = size;
2677dd7cddfSDavid du Colombier }
2687dd7cddfSDavid du Colombier }
2697dd7cddfSDavid du Colombier
2707dd7cddfSDavid du Colombier /*
2717dd7cddfSDavid du Colombier * Sort both tables IO smallest first, Memory largest
2727dd7cddfSDavid du Colombier */
2737dd7cddfSDavid du Colombier qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
2747dd7cddfSDavid du Colombier tptr = table+ntb;
2757dd7cddfSDavid du Colombier qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
2767dd7cddfSDavid du Colombier
2777dd7cddfSDavid du Colombier /*
2787dd7cddfSDavid du Colombier * Allocate IO address space on this bus
2797dd7cddfSDavid du Colombier */
2807dd7cddfSDavid du Colombier for(tptr = table; tptr < itb; tptr++) {
2817dd7cddfSDavid du Colombier hole = tptr->siz;
2827dd7cddfSDavid du Colombier if(tptr->bar == -1)
2837dd7cddfSDavid du Colombier hole = 1<<12;
2847dd7cddfSDavid du Colombier ioa = (ioa+hole-1) & ~(hole-1);
2857dd7cddfSDavid du Colombier
2867dd7cddfSDavid du Colombier p = tptr->dev;
2877dd7cddfSDavid du Colombier if(tptr->bar == -1)
2887dd7cddfSDavid du Colombier p->ioa.bar = ioa;
2897dd7cddfSDavid du Colombier else {
2907dd7cddfSDavid du Colombier p->pcr |= IOen;
2917dd7cddfSDavid du Colombier p->mem[tptr->bar].bar = ioa|1;
2927dd7cddfSDavid du Colombier if(wrreg)
2937dd7cddfSDavid du Colombier pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
2947dd7cddfSDavid du Colombier }
2957dd7cddfSDavid du Colombier
2967dd7cddfSDavid du Colombier ioa += tptr->siz;
2977dd7cddfSDavid du Colombier }
2987dd7cddfSDavid du Colombier
2997dd7cddfSDavid du Colombier /*
3007dd7cddfSDavid du Colombier * Allocate Memory address space on this bus
3017dd7cddfSDavid du Colombier */
3027dd7cddfSDavid du Colombier for(tptr = table+ntb; tptr < mtb; tptr++) {
3037dd7cddfSDavid du Colombier hole = tptr->siz;
3047dd7cddfSDavid du Colombier if(tptr->bar == -1)
3057dd7cddfSDavid du Colombier hole = 1<<20;
3067dd7cddfSDavid du Colombier mema = (mema+hole-1) & ~(hole-1);
3077dd7cddfSDavid du Colombier
3087dd7cddfSDavid du Colombier p = tptr->dev;
3097dd7cddfSDavid du Colombier if(tptr->bar == -1)
3107dd7cddfSDavid du Colombier p->mema.bar = mema;
3117dd7cddfSDavid du Colombier else {
3127dd7cddfSDavid du Colombier p->pcr |= MEMen;
3137dd7cddfSDavid du Colombier p->mem[tptr->bar].bar = mema;
3147dd7cddfSDavid du Colombier if(wrreg)
3157dd7cddfSDavid du Colombier pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
3167dd7cddfSDavid du Colombier }
3177dd7cddfSDavid du Colombier mema += tptr->siz;
3187dd7cddfSDavid du Colombier }
3197dd7cddfSDavid du Colombier
3207dd7cddfSDavid du Colombier *pmema = mema;
3217dd7cddfSDavid du Colombier *pioa = ioa;
3227dd7cddfSDavid du Colombier free(table);
3237dd7cddfSDavid du Colombier
3247dd7cddfSDavid du Colombier if(wrreg == 0)
3257dd7cddfSDavid du Colombier return;
3267dd7cddfSDavid du Colombier
3277dd7cddfSDavid du Colombier /*
3287dd7cddfSDavid du Colombier * Finally set all the bridge addresses & registers
3297dd7cddfSDavid du Colombier */
3307dd7cddfSDavid du Colombier for(p = root; p != nil; p = p->link) {
3317dd7cddfSDavid du Colombier if(p->bridge == nil) {
3327dd7cddfSDavid du Colombier pcicfgrw8(p->tbdf, PciLTR, 64, 0);
3337dd7cddfSDavid du Colombier
3347dd7cddfSDavid du Colombier p->pcr |= MASen;
335e569ccb5SDavid du Colombier pcicfgrw16(p->tbdf, PciPCR, p->pcr, 0);
3367dd7cddfSDavid du Colombier continue;
3377dd7cddfSDavid du Colombier }
3387dd7cddfSDavid du Colombier
3397dd7cddfSDavid du Colombier base = p->ioa.bar;
3407dd7cddfSDavid du Colombier limit = base+p->ioa.size-1;
341d0f3faacSDavid du Colombier v = pcicfgrw32(p->tbdf, PciIBR, 0, 1);
3427dd7cddfSDavid du Colombier v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
343d0f3faacSDavid du Colombier pcicfgrw32(p->tbdf, PciIBR, v, 0);
3447dd7cddfSDavid du Colombier v = (limit & 0xFFFF0000)|(base>>16);
345d0f3faacSDavid du Colombier pcicfgrw32(p->tbdf, PciIUBR, v, 0);
3467dd7cddfSDavid du Colombier
3477dd7cddfSDavid du Colombier base = p->mema.bar;
3487dd7cddfSDavid du Colombier limit = base+p->mema.size-1;
3497dd7cddfSDavid du Colombier v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
350d0f3faacSDavid du Colombier pcicfgrw32(p->tbdf, PciMBR, v, 0);
3517dd7cddfSDavid du Colombier
3527dd7cddfSDavid du Colombier /*
3537dd7cddfSDavid du Colombier * Disable memory prefetch
3547dd7cddfSDavid du Colombier */
355d0f3faacSDavid du Colombier pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0);
3567dd7cddfSDavid du Colombier pcicfgrw8(p->tbdf, PciLTR, 64, 0);
3577dd7cddfSDavid du Colombier
3587dd7cddfSDavid du Colombier /*
3597dd7cddfSDavid du Colombier * Enable the bridge
3607dd7cddfSDavid du Colombier */
361e569ccb5SDavid du Colombier p->pcr |= IOen|MEMen|MASen;
362e569ccb5SDavid du Colombier pcicfgrw32(p->tbdf, PciPCR, 0xFFFF0000|p->pcr , 0);
3637dd7cddfSDavid du Colombier
3647dd7cddfSDavid du Colombier sioa = p->ioa.bar;
3657dd7cddfSDavid du Colombier smema = p->mema.bar;
3667dd7cddfSDavid du Colombier pcibusmap(p->bridge, &smema, &sioa, 1);
3677dd7cddfSDavid du Colombier }
3687dd7cddfSDavid du Colombier }
3697dd7cddfSDavid du Colombier
3702210c76eSDavid du Colombier /* side effect: if a video controller is seen, set pcivga non-zero */
3717dd7cddfSDavid du Colombier static int
pcilscan(int bno,Pcidev ** list)3729a747e4fSDavid du Colombier pcilscan(int bno, Pcidev** list)
3737dd7cddfSDavid du Colombier {
3747dd7cddfSDavid du Colombier Pcidev *p, *head, *tail;
3757dd7cddfSDavid du Colombier int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
3767dd7cddfSDavid du Colombier
3777dd7cddfSDavid du Colombier maxubn = bno;
3787dd7cddfSDavid du Colombier head = nil;
3797dd7cddfSDavid du Colombier tail = nil;
3807dd7cddfSDavid du Colombier for(dno = 0; dno <= pcimaxdno; dno++){
3817dd7cddfSDavid du Colombier maxfno = 0;
3827dd7cddfSDavid du Colombier for(fno = 0; fno <= maxfno; fno++){
3837dd7cddfSDavid du Colombier /*
3847dd7cddfSDavid du Colombier * For this possible device, form the
3857dd7cddfSDavid du Colombier * bus+device+function triplet needed to address it
3867dd7cddfSDavid du Colombier * and try to read the vendor and device ID.
3877dd7cddfSDavid du Colombier * If successful, allocate a device struct and
3887dd7cddfSDavid du Colombier * start to fill it in with some useful information
3897dd7cddfSDavid du Colombier * from the device's configuration space.
3907dd7cddfSDavid du Colombier */
3917dd7cddfSDavid du Colombier tbdf = MKBUS(BusPCI, bno, dno, fno);
3927dd7cddfSDavid du Colombier l = pcicfgrw32(tbdf, PciVID, 0, 1);
3937dd7cddfSDavid du Colombier if(l == 0xFFFFFFFF || l == 0)
3947dd7cddfSDavid du Colombier continue;
3957dd7cddfSDavid du Colombier p = malloc(sizeof(*p));
396aa72973aSDavid du Colombier if(p == nil)
39740ff3dfeSDavid du Colombier panic("pcilscan: no memory");
3987dd7cddfSDavid du Colombier p->tbdf = tbdf;
3997dd7cddfSDavid du Colombier p->vid = l;
4007dd7cddfSDavid du Colombier p->did = l>>16;
4017dd7cddfSDavid du Colombier
4027dd7cddfSDavid du Colombier if(pcilist != nil)
4037dd7cddfSDavid du Colombier pcitail->list = p;
4047dd7cddfSDavid du Colombier else
4057dd7cddfSDavid du Colombier pcilist = p;
4067dd7cddfSDavid du Colombier pcitail = p;
4077dd7cddfSDavid du Colombier
408e569ccb5SDavid du Colombier p->pcr = pcicfgr16(p, PciPCR);
4097dd7cddfSDavid du Colombier p->rid = pcicfgr8(p, PciRID);
4107dd7cddfSDavid du Colombier p->ccrp = pcicfgr8(p, PciCCRp);
4117dd7cddfSDavid du Colombier p->ccru = pcicfgr8(p, PciCCRu);
4127dd7cddfSDavid du Colombier p->ccrb = pcicfgr8(p, PciCCRb);
413e569ccb5SDavid du Colombier p->cls = pcicfgr8(p, PciCLS);
414e569ccb5SDavid du Colombier p->ltr = pcicfgr8(p, PciLTR);
4157dd7cddfSDavid du Colombier
4167dd7cddfSDavid du Colombier p->intl = pcicfgr8(p, PciINTL);
4177dd7cddfSDavid du Colombier
4187dd7cddfSDavid du Colombier /*
4197dd7cddfSDavid du Colombier * If the device is a multi-function device adjust the
4207dd7cddfSDavid du Colombier * loop count so all possible functions are checked.
4217dd7cddfSDavid du Colombier */
4227dd7cddfSDavid du Colombier hdt = pcicfgr8(p, PciHDT);
4237dd7cddfSDavid du Colombier if(hdt & 0x80)
4247dd7cddfSDavid du Colombier maxfno = MaxFNO;
4257dd7cddfSDavid du Colombier
4267dd7cddfSDavid du Colombier /*
4277dd7cddfSDavid du Colombier * If appropriate, read the base address registers
4287dd7cddfSDavid du Colombier * and work out the sizes.
4297dd7cddfSDavid du Colombier */
4307dd7cddfSDavid du Colombier switch(p->ccrb) {
4312210c76eSDavid du Colombier case 0x03: /* display controller */
4322210c76eSDavid du Colombier pcivga = 1;
4332210c76eSDavid du Colombier /* fall through */
4347dd7cddfSDavid du Colombier case 0x01: /* mass storage controller */
4357dd7cddfSDavid du Colombier case 0x02: /* network controller */
4367dd7cddfSDavid du Colombier case 0x04: /* multimedia device */
4377dd7cddfSDavid du Colombier case 0x07: /* simple comm. controllers */
4387dd7cddfSDavid du Colombier case 0x08: /* base system peripherals */
4397dd7cddfSDavid du Colombier case 0x09: /* input devices */
4407dd7cddfSDavid du Colombier case 0x0A: /* docking stations */
4417dd7cddfSDavid du Colombier case 0x0B: /* processors */
4427dd7cddfSDavid du Colombier case 0x0C: /* serial bus controllers */
4437dd7cddfSDavid du Colombier if((hdt & 0x7F) != 0)
4447dd7cddfSDavid du Colombier break;
4457dd7cddfSDavid du Colombier rno = PciBAR0 - 4;
4467dd7cddfSDavid du Colombier for(i = 0; i < nelem(p->mem); i++) {
4477dd7cddfSDavid du Colombier rno += 4;
4487dd7cddfSDavid du Colombier p->mem[i].bar = pcicfgr32(p, rno);
4497dd7cddfSDavid du Colombier p->mem[i].size = pcibarsize(p, rno);
4507dd7cddfSDavid du Colombier }
4517dd7cddfSDavid du Colombier break;
4527dd7cddfSDavid du Colombier
4537dd7cddfSDavid du Colombier case 0x00:
4547dd7cddfSDavid du Colombier case 0x05: /* memory controller */
4557dd7cddfSDavid du Colombier case 0x06: /* bridge device */
4567dd7cddfSDavid du Colombier default:
4577dd7cddfSDavid du Colombier break;
4587dd7cddfSDavid du Colombier }
4597dd7cddfSDavid du Colombier
4607dd7cddfSDavid du Colombier if(head != nil)
4617dd7cddfSDavid du Colombier tail->link = p;
4627dd7cddfSDavid du Colombier else
4637dd7cddfSDavid du Colombier head = p;
4647dd7cddfSDavid du Colombier tail = p;
4657dd7cddfSDavid du Colombier }
4667dd7cddfSDavid du Colombier }
4677dd7cddfSDavid du Colombier
4687dd7cddfSDavid du Colombier *list = head;
4697dd7cddfSDavid du Colombier for(p = head; p != nil; p = p->link){
4707dd7cddfSDavid du Colombier /*
4717dd7cddfSDavid du Colombier * Find PCI-PCI bridges and recursively descend the tree.
4727dd7cddfSDavid du Colombier */
4737dd7cddfSDavid du Colombier if(p->ccrb != 0x06 || p->ccru != 0x04)
4747dd7cddfSDavid du Colombier continue;
4757dd7cddfSDavid du Colombier
4767dd7cddfSDavid du Colombier /*
4777dd7cddfSDavid du Colombier * If the secondary or subordinate bus number is not
4787dd7cddfSDavid du Colombier * initialised try to do what the PCI BIOS should have
4797dd7cddfSDavid du Colombier * done and fill in the numbers as the tree is descended.
4807dd7cddfSDavid du Colombier * On the way down the subordinate bus number is set to
4817dd7cddfSDavid du Colombier * the maximum as it's not known how many buses are behind
4827dd7cddfSDavid du Colombier * this one; the final value is set on the way back up.
4837dd7cddfSDavid du Colombier */
4847dd7cddfSDavid du Colombier sbn = pcicfgr8(p, PciSBN);
4857dd7cddfSDavid du Colombier ubn = pcicfgr8(p, PciUBN);
4867dd7cddfSDavid du Colombier
48780ee5cbfSDavid du Colombier if(sbn == 0 || ubn == 0 || nobios) {
4887dd7cddfSDavid du Colombier sbn = maxubn+1;
4897dd7cddfSDavid du Colombier /*
4907dd7cddfSDavid du Colombier * Make sure memory, I/O and master enables are
4917dd7cddfSDavid du Colombier * off, set the primary, secondary and subordinate
4927dd7cddfSDavid du Colombier * bus numbers and clear the secondary status before
4937dd7cddfSDavid du Colombier * attempting to scan the secondary bus.
4947dd7cddfSDavid du Colombier *
4957dd7cddfSDavid du Colombier * Initialisation of the bridge should be done here.
4967dd7cddfSDavid du Colombier */
4977dd7cddfSDavid du Colombier pcicfgw32(p, PciPCR, 0xFFFF0000);
4987dd7cddfSDavid du Colombier l = (MaxUBN<<16)|(sbn<<8)|bno;
4997dd7cddfSDavid du Colombier pcicfgw32(p, PciPBN, l);
5007dd7cddfSDavid du Colombier pcicfgw16(p, PciSPSR, 0xFFFF);
5019a747e4fSDavid du Colombier maxubn = pcilscan(sbn, &p->bridge);
5027dd7cddfSDavid du Colombier l = (maxubn<<16)|(sbn<<8)|bno;
5037dd7cddfSDavid du Colombier
5047dd7cddfSDavid du Colombier pcicfgw32(p, PciPBN, l);
5057dd7cddfSDavid du Colombier }
5067dd7cddfSDavid du Colombier else {
507e569ccb5SDavid du Colombier if(ubn > maxubn)
5087dd7cddfSDavid du Colombier maxubn = ubn;
5099a747e4fSDavid du Colombier pcilscan(sbn, &p->bridge);
5107dd7cddfSDavid du Colombier }
5117dd7cddfSDavid du Colombier }
5127dd7cddfSDavid du Colombier
5137dd7cddfSDavid du Colombier return maxubn;
5147dd7cddfSDavid du Colombier }
5157dd7cddfSDavid du Colombier
5169a747e4fSDavid du Colombier int
pciscan(int bno,Pcidev ** list)5179a747e4fSDavid du Colombier pciscan(int bno, Pcidev **list)
5189a747e4fSDavid du Colombier {
5199a747e4fSDavid du Colombier int ubn;
5209a747e4fSDavid du Colombier
5219a747e4fSDavid du Colombier lock(&pcicfginitlock);
5229a747e4fSDavid du Colombier ubn = pcilscan(bno, list);
5239a747e4fSDavid du Colombier unlock(&pcicfginitlock);
5249a747e4fSDavid du Colombier return ubn;
5259a747e4fSDavid du Colombier }
5269a747e4fSDavid du Colombier
5279a747e4fSDavid du Colombier static uchar
pIIxget(Pcidev * router,uchar link)52859c21d95SDavid du Colombier pIIxget(Pcidev *router, uchar link)
5299a747e4fSDavid du Colombier {
5309a747e4fSDavid du Colombier uchar pirq;
5319a747e4fSDavid du Colombier
5329a747e4fSDavid du Colombier /* link should be 0x60, 0x61, 0x62, 0x63 */
5339a747e4fSDavid du Colombier pirq = pcicfgr8(router, link);
5349a747e4fSDavid du Colombier return (pirq < 16)? pirq: 0;
5359a747e4fSDavid du Colombier }
5369a747e4fSDavid du Colombier
5379a747e4fSDavid du Colombier static void
pIIxset(Pcidev * router,uchar link,uchar irq)53859c21d95SDavid du Colombier pIIxset(Pcidev *router, uchar link, uchar irq)
5399a747e4fSDavid du Colombier {
5409a747e4fSDavid du Colombier pcicfgw8(router, link, irq);
5419a747e4fSDavid du Colombier }
5429a747e4fSDavid du Colombier
5439a747e4fSDavid du Colombier static uchar
viaget(Pcidev * router,uchar link)54459c21d95SDavid du Colombier viaget(Pcidev *router, uchar link)
5459a747e4fSDavid du Colombier {
5469a747e4fSDavid du Colombier uchar pirq;
5479a747e4fSDavid du Colombier
5489a747e4fSDavid du Colombier /* link should be 1, 2, 3, 5 */
5499a747e4fSDavid du Colombier pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0;
5509a747e4fSDavid du Colombier
5519a747e4fSDavid du Colombier return (link & 1)? (pirq >> 4): (pirq & 15);
5529a747e4fSDavid du Colombier }
5539a747e4fSDavid du Colombier
5549a747e4fSDavid du Colombier static void
viaset(Pcidev * router,uchar link,uchar irq)55559c21d95SDavid du Colombier viaset(Pcidev *router, uchar link, uchar irq)
5569a747e4fSDavid du Colombier {
5579a747e4fSDavid du Colombier uchar pirq;
5589a747e4fSDavid du Colombier
5599a747e4fSDavid du Colombier pirq = pcicfgr8(router, 0x55 + (link >> 1));
5609a747e4fSDavid du Colombier pirq &= (link & 1)? 0x0f: 0xf0;
5619a747e4fSDavid du Colombier pirq |= (link & 1)? (irq << 4): (irq & 15);
5629a747e4fSDavid du Colombier pcicfgw8(router, 0x55 + (link>>1), pirq);
5639a747e4fSDavid du Colombier }
5649a747e4fSDavid du Colombier
5659a747e4fSDavid du Colombier static uchar
optiget(Pcidev * router,uchar link)56659c21d95SDavid du Colombier optiget(Pcidev *router, uchar link)
5679a747e4fSDavid du Colombier {
5689a747e4fSDavid du Colombier uchar pirq = 0;
5699a747e4fSDavid du Colombier
5709a747e4fSDavid du Colombier /* link should be 0x02, 0x12, 0x22, 0x32 */
5719a747e4fSDavid du Colombier if ((link & 0xcf) == 0x02)
5729a747e4fSDavid du Colombier pirq = pcicfgr8(router, 0xb8 + (link >> 5));
5739a747e4fSDavid du Colombier return (link & 0x10)? (pirq >> 4): (pirq & 15);
5749a747e4fSDavid du Colombier }
5759a747e4fSDavid du Colombier
5769a747e4fSDavid du Colombier static void
optiset(Pcidev * router,uchar link,uchar irq)57759c21d95SDavid du Colombier optiset(Pcidev *router, uchar link, uchar irq)
5789a747e4fSDavid du Colombier {
5799a747e4fSDavid du Colombier uchar pirq;
5809a747e4fSDavid du Colombier
5819a747e4fSDavid du Colombier pirq = pcicfgr8(router, 0xb8 + (link >> 5));
5829a747e4fSDavid du Colombier pirq &= (link & 0x10)? 0x0f : 0xf0;
5839a747e4fSDavid du Colombier pirq |= (link & 0x10)? (irq << 4): (irq & 15);
5849a747e4fSDavid du Colombier pcicfgw8(router, 0xb8 + (link >> 5), pirq);
5859a747e4fSDavid du Colombier }
5869a747e4fSDavid du Colombier
5879a747e4fSDavid du Colombier static uchar
aliget(Pcidev * router,uchar link)58859c21d95SDavid du Colombier aliget(Pcidev *router, uchar link)
5899a747e4fSDavid du Colombier {
5909a747e4fSDavid du Colombier /* No, you're not dreaming */
5919a747e4fSDavid du Colombier static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
5929a747e4fSDavid du Colombier uchar pirq;
5939a747e4fSDavid du Colombier
5949a747e4fSDavid du Colombier /* link should be 0x01..0x08 */
5959a747e4fSDavid du Colombier pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
5969a747e4fSDavid du Colombier return (link & 1)? map[pirq&15]: map[pirq>>4];
5979a747e4fSDavid du Colombier }
5989a747e4fSDavid du Colombier
5999a747e4fSDavid du Colombier static void
aliset(Pcidev * router,uchar link,uchar irq)60059c21d95SDavid du Colombier aliset(Pcidev *router, uchar link, uchar irq)
6019a747e4fSDavid du Colombier {
60259c21d95SDavid du Colombier /* Inverse of map in aliget */
6039a747e4fSDavid du Colombier static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
6049a747e4fSDavid du Colombier uchar pirq;
6059a747e4fSDavid du Colombier
6069a747e4fSDavid du Colombier pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
6079a747e4fSDavid du Colombier pirq &= (link & 1)? 0x0f: 0xf0;
6089a747e4fSDavid du Colombier pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15);
6099a747e4fSDavid du Colombier pcicfgw8(router, 0x48 + ((link-1)>>1), pirq);
6109a747e4fSDavid du Colombier }
6119a747e4fSDavid du Colombier
6129a747e4fSDavid du Colombier static uchar
cyrixget(Pcidev * router,uchar link)61359c21d95SDavid du Colombier cyrixget(Pcidev *router, uchar link)
6149a747e4fSDavid du Colombier {
6159a747e4fSDavid du Colombier uchar pirq;
6169a747e4fSDavid du Colombier
6179a747e4fSDavid du Colombier /* link should be 1, 2, 3, 4 */
6189a747e4fSDavid du Colombier pirq = pcicfgr8(router, 0x5c + ((link-1)>>1));
6199a747e4fSDavid du Colombier return ((link & 1)? pirq >> 4: pirq & 15);
6209a747e4fSDavid du Colombier }
6219a747e4fSDavid du Colombier
6229a747e4fSDavid du Colombier static void
cyrixset(Pcidev * router,uchar link,uchar irq)62359c21d95SDavid du Colombier cyrixset(Pcidev *router, uchar link, uchar irq)
6249a747e4fSDavid du Colombier {
6259a747e4fSDavid du Colombier uchar pirq;
6269a747e4fSDavid du Colombier
6279a747e4fSDavid du Colombier pirq = pcicfgr8(router, 0x5c + (link>>1));
6289a747e4fSDavid du Colombier pirq &= (link & 1)? 0x0f: 0xf0;
6299a747e4fSDavid du Colombier pirq |= (link & 1)? (irq << 4): (irq & 15);
6309a747e4fSDavid du Colombier pcicfgw8(router, 0x5c + (link>>1), pirq);
6319a747e4fSDavid du Colombier }
6329a747e4fSDavid du Colombier
63359c21d95SDavid du Colombier typedef struct Bridge Bridge;
63459c21d95SDavid du Colombier struct Bridge
6359a747e4fSDavid du Colombier {
6369a747e4fSDavid du Colombier ushort vid;
6379a747e4fSDavid du Colombier ushort did;
6389a747e4fSDavid du Colombier uchar (*get)(Pcidev *, uchar);
6399a747e4fSDavid du Colombier void (*set)(Pcidev *, uchar, uchar);
6409a747e4fSDavid du Colombier };
6419a747e4fSDavid du Colombier
64259c21d95SDavid du Colombier static Bridge southbridges[] = {
64341dd6b47SDavid du Colombier { 0x8086, 0x122e, pIIxget, pIIxset }, /* Intel 82371FB */
64441dd6b47SDavid du Colombier { 0x8086, 0x1234, pIIxget, pIIxset }, /* Intel 82371MX */
64541dd6b47SDavid du Colombier { 0x8086, 0x7000, pIIxget, pIIxset }, /* Intel 82371SB */
64641dd6b47SDavid du Colombier { 0x8086, 0x7110, pIIxget, pIIxset }, /* Intel 82371AB */
64741dd6b47SDavid du Colombier { 0x8086, 0x7198, pIIxget, pIIxset }, /* Intel 82443MX (fn 1) */
64841dd6b47SDavid du Colombier { 0x8086, 0x2410, pIIxget, pIIxset }, /* Intel 82801AA */
64941dd6b47SDavid du Colombier { 0x8086, 0x2420, pIIxget, pIIxset }, /* Intel 82801AB */
65041dd6b47SDavid du Colombier { 0x8086, 0x2440, pIIxget, pIIxset }, /* Intel 82801BA */
651d8cd2beaSDavid du Colombier { 0x8086, 0x2448, pIIxget, pIIxset }, /* Intel 82801BAM/CAM/DBM */
65241dd6b47SDavid du Colombier { 0x8086, 0x244c, pIIxget, pIIxset }, /* Intel 82801BAM */
653d8cd2beaSDavid du Colombier { 0x8086, 0x244e, pIIxget, pIIxset }, /* Intel 82801 */
65441dd6b47SDavid du Colombier { 0x8086, 0x2480, pIIxget, pIIxset }, /* Intel 82801CA */
65541dd6b47SDavid du Colombier { 0x8086, 0x248c, pIIxget, pIIxset }, /* Intel 82801CAM */
65641dd6b47SDavid du Colombier { 0x8086, 0x24c0, pIIxget, pIIxset }, /* Intel 82801DBL */
65741dd6b47SDavid du Colombier { 0x8086, 0x24cc, pIIxget, pIIxset }, /* Intel 82801DBM */
65841dd6b47SDavid du Colombier { 0x8086, 0x24d0, pIIxget, pIIxset }, /* Intel 82801EB */
6591c9e5a6cSDavid du Colombier { 0x8086, 0x25a1, pIIxget, pIIxset }, /* Intel 6300ESB */
66041dd6b47SDavid du Colombier { 0x8086, 0x2640, pIIxget, pIIxset }, /* Intel 82801FB */
661add6b5c5SDavid du Colombier { 0x8086, 0x2641, pIIxget, pIIxset }, /* Intel 82801FBM */
66241dd6b47SDavid du Colombier { 0x8086, 0x27b8, pIIxget, pIIxset }, /* Intel 82801GB */
66341dd6b47SDavid du Colombier { 0x8086, 0x27b9, pIIxget, pIIxset }, /* Intel 82801GBM */
664194f7e8cSDavid du Colombier { 0x8086, 0x27bd, pIIxget, pIIxset }, /* Intel 82801GB/GR */
665d8cd2beaSDavid du Colombier { 0x8086, 0x3a16, pIIxget, pIIxset }, /* Intel 82801JIR */
666d8cd2beaSDavid du Colombier { 0x8086, 0x3a40, pIIxget, pIIxset }, /* Intel 82801JI */
667d8cd2beaSDavid du Colombier { 0x8086, 0x3a42, pIIxget, pIIxset }, /* Intel 82801JI */
668d8cd2beaSDavid du Colombier { 0x8086, 0x3a48, pIIxget, pIIxset }, /* Intel 82801JI */
669aef37788SDavid du Colombier { 0x8086, 0x2916, pIIxget, pIIxset }, /* Intel 82801? */
6706f8e93f6SDavid du Colombier { 0x8086, 0x1c02, pIIxget, pIIxset }, /* Intel 6 Series/C200 */
67128684b1dSDavid du Colombier { 0x8086, 0x1c44, pIIxget, pIIxset }, /* Intel 6 Series/Z68 Express */
6728c242bd4SDavid du Colombier { 0x8086, 0x1e53, pIIxget, pIIxset }, /* Intel 7 Series/C216 */
67341dd6b47SDavid du Colombier { 0x1106, 0x0586, viaget, viaset }, /* Viatech 82C586 */
67441dd6b47SDavid du Colombier { 0x1106, 0x0596, viaget, viaset }, /* Viatech 82C596 */
67541dd6b47SDavid du Colombier { 0x1106, 0x0686, viaget, viaset }, /* Viatech 82C686 */
676a0db15f5SDavid du Colombier { 0x1106, 0x3177, viaget, viaset }, /* Viatech VT8235 */
67741dd6b47SDavid du Colombier { 0x1106, 0x3227, viaget, viaset }, /* Viatech VT8237 */
678a0db15f5SDavid du Colombier { 0x1106, 0x3287, viaget, viaset }, /* Viatech VT8251 */
67941dd6b47SDavid du Colombier { 0x1045, 0xc700, optiget, optiset }, /* Opti 82C700 */
68041dd6b47SDavid du Colombier { 0x10b9, 0x1533, aliget, aliset }, /* Al M1533 */
68141dd6b47SDavid du Colombier { 0x1039, 0x0008, pIIxget, pIIxset }, /* SI 503 */
68241dd6b47SDavid du Colombier { 0x1039, 0x0496, pIIxget, pIIxset }, /* SI 496 */
68341dd6b47SDavid du Colombier { 0x1078, 0x0100, cyrixget, cyrixset }, /* Cyrix 5530 Legacy */
6845000fddeSDavid du Colombier
68541dd6b47SDavid du Colombier { 0x1022, 0x746B, nil, nil }, /* AMD 8111 */
68641dd6b47SDavid du Colombier { 0x10DE, 0x00D1, nil, nil }, /* NVIDIA nForce 3 */
68741ac1ab6SDavid du Colombier { 0x10DE, 0x00E0, nil, nil }, /* NVIDIA nForce 3 250 Series */
688b8a11165SDavid du Colombier { 0x10DE, 0x00E1, nil, nil }, /* NVIDIA nForce 3 250 Series */
68941dd6b47SDavid du Colombier { 0x1166, 0x0200, nil, nil }, /* ServerWorks ServerSet III LE */
69041dd6b47SDavid du Colombier { 0x1002, 0x4377, nil, nil }, /* ATI Radeon Xpress 200M */
69141dd6b47SDavid du Colombier { 0x1002, 0x4372, nil, nil }, /* ATI SB400 */
69259c21d95SDavid du Colombier };
6939a747e4fSDavid du Colombier
69459c21d95SDavid du Colombier typedef struct Slot Slot;
69559c21d95SDavid du Colombier struct Slot {
69641dd6b47SDavid du Colombier uchar bus; /* Pci bus number */
69741dd6b47SDavid du Colombier uchar dev; /* Pci device number */
69841dd6b47SDavid du Colombier uchar maps[12]; /* Avoid structs! Link and mask. */
69941dd6b47SDavid du Colombier uchar slot; /* Add-in/built-in slot */
70059c21d95SDavid du Colombier uchar reserved;
70159c21d95SDavid du Colombier };
70259c21d95SDavid du Colombier
70359c21d95SDavid du Colombier typedef struct Router Router;
70459c21d95SDavid du Colombier struct Router {
70541dd6b47SDavid du Colombier uchar signature[4]; /* Routing table signature */
70641dd6b47SDavid du Colombier uchar version[2]; /* Version number */
70741dd6b47SDavid du Colombier uchar size[2]; /* Total table size */
70841dd6b47SDavid du Colombier uchar bus; /* Interrupt router bus number */
70941dd6b47SDavid du Colombier uchar devfn; /* Router's devfunc */
71041dd6b47SDavid du Colombier uchar pciirqs[2]; /* Exclusive PCI irqs */
71141dd6b47SDavid du Colombier uchar compat[4]; /* Compatible PCI interrupt router */
71241dd6b47SDavid du Colombier uchar miniport[4]; /* Miniport data */
71359c21d95SDavid du Colombier uchar reserved[11];
71459c21d95SDavid du Colombier uchar checksum;
71559c21d95SDavid du Colombier };
7169a747e4fSDavid du Colombier
71741dd6b47SDavid du Colombier static ushort pciirqs; /* Exclusive PCI irqs */
71841dd6b47SDavid du Colombier static Bridge *southbridge; /* Which southbridge to use. */
7199a747e4fSDavid du Colombier
7209a747e4fSDavid du Colombier static void
pcirouting(void)7219a747e4fSDavid du Colombier pcirouting(void)
7229a747e4fSDavid du Colombier {
72359c21d95SDavid du Colombier Slot *e;
72459c21d95SDavid du Colombier Router *r;
7259a747e4fSDavid du Colombier int size, i, fn, tbdf;
7269a747e4fSDavid du Colombier Pcidev *sbpci, *pci;
7275000fddeSDavid du Colombier uchar *p, pin, irq, link, *map;
7289a747e4fSDavid du Colombier
72941dd6b47SDavid du Colombier /* Search for PCI interrupt routing table in BIOS */
7309a747e4fSDavid du Colombier for(p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16)
7319a747e4fSDavid du Colombier if(p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R')
7329a747e4fSDavid du Colombier break;
7339a747e4fSDavid du Colombier
7342210c76eSDavid du Colombier if(p >= (uchar *)KADDR(0xfffff)) {
7353d8d68ddSDavid du Colombier // print("no PCI intr routing table found\n");
7369a747e4fSDavid du Colombier return;
7372210c76eSDavid du Colombier }
7389a747e4fSDavid du Colombier
73959c21d95SDavid du Colombier r = (Router *)p;
7409a747e4fSDavid du Colombier
7412210c76eSDavid du Colombier if (0)
7422210c76eSDavid du Colombier print("PCI interrupt routing table version %d.%d at %#.6luX\n",
7432210c76eSDavid du Colombier r->version[0], r->version[1], (ulong)r & 0xfffff);
7449a747e4fSDavid du Colombier
74559c21d95SDavid du Colombier tbdf = (BusPCI << 24)|(r->bus << 16)|(r->devfn << 8);
7469a747e4fSDavid du Colombier sbpci = pcimatchtbdf(tbdf);
7479a747e4fSDavid du Colombier if(sbpci == nil) {
7489a747e4fSDavid du Colombier print("pcirouting: Cannot find south bridge %T\n", tbdf);
7499a747e4fSDavid du Colombier return;
7509a747e4fSDavid du Colombier }
7519a747e4fSDavid du Colombier
7529a747e4fSDavid du Colombier for(i = 0; i != nelem(southbridges); i++)
7539a747e4fSDavid du Colombier if(sbpci->vid == southbridges[i].vid && sbpci->did == southbridges[i].did)
7549a747e4fSDavid du Colombier break;
7559a747e4fSDavid du Colombier
7569a747e4fSDavid du Colombier if(i == nelem(southbridges)) {
7573ff48bf5SDavid du Colombier print("pcirouting: ignoring south bridge %T %.4uX/%.4uX\n", tbdf, sbpci->vid, sbpci->did);
7589a747e4fSDavid du Colombier return;
7599a747e4fSDavid du Colombier }
7609a747e4fSDavid du Colombier southbridge = &southbridges[i];
7615000fddeSDavid du Colombier if(southbridge->get == nil || southbridge->set == nil)
7625000fddeSDavid du Colombier return;
7639a747e4fSDavid du Colombier
76459c21d95SDavid du Colombier pciirqs = (r->pciirqs[1] << 8)|r->pciirqs[0];
7659a747e4fSDavid du Colombier
76659c21d95SDavid du Colombier size = (r->size[1] << 8)|r->size[0];
76759c21d95SDavid du Colombier for(e = (Slot *)&r[1]; (uchar *)e < p + size; e++) {
76841dd6b47SDavid du Colombier if (0) {
76941dd6b47SDavid du Colombier print("%.2uX/%.2uX %.2uX: ", e->bus, e->dev, e->slot);
77041dd6b47SDavid du Colombier for (i = 0; i != 4; i++) {
77141dd6b47SDavid du Colombier uchar *m = &e->maps[i * 3];
77241dd6b47SDavid du Colombier print("[%d] %.2uX %.4uX ",
77341dd6b47SDavid du Colombier i, m[0], (m[2] << 8)|m[1]);
77441dd6b47SDavid du Colombier }
77541dd6b47SDavid du Colombier print("\n");
77641dd6b47SDavid du Colombier }
7779a747e4fSDavid du Colombier for(fn = 0; fn != 8; fn++) {
77859c21d95SDavid du Colombier tbdf = (BusPCI << 24)|(e->bus << 16)|((e->dev | fn) << 8);
7799a747e4fSDavid du Colombier pci = pcimatchtbdf(tbdf);
7809a747e4fSDavid du Colombier if(pci == nil)
7819a747e4fSDavid du Colombier continue;
7829a747e4fSDavid du Colombier pin = pcicfgr8(pci, PciINTP);
7839a747e4fSDavid du Colombier if(pin == 0 || pin == 0xff)
7849a747e4fSDavid du Colombier continue;
7859a747e4fSDavid du Colombier
7865000fddeSDavid du Colombier map = &e->maps[(pin - 1) * 3];
7875000fddeSDavid du Colombier link = map[0];
7883ff48bf5SDavid du Colombier irq = southbridge->get(sbpci, link);
7899a747e4fSDavid du Colombier if(irq == 0 || irq == pci->intl)
7909a747e4fSDavid du Colombier continue;
7919a747e4fSDavid du Colombier if(pci->intl != 0 && pci->intl != 0xFF) {
7923ff48bf5SDavid du Colombier print("pcirouting: BIOS workaround: %T at pin %d link %d irq %d -> %d\n",
7933ff48bf5SDavid du Colombier tbdf, pin, link, irq, pci->intl);
7943ff48bf5SDavid du Colombier southbridge->set(sbpci, link, pci->intl);
7959a747e4fSDavid du Colombier continue;
7969a747e4fSDavid du Colombier }
7973ff48bf5SDavid du Colombier print("pcirouting: %T at pin %d link %d irq %d\n", tbdf, pin, link, irq);
7989a747e4fSDavid du Colombier pcicfgw8(pci, PciINTL, irq);
7999a747e4fSDavid du Colombier pci->intl = irq;
8009a747e4fSDavid du Colombier }
8019a747e4fSDavid du Colombier }
8029a747e4fSDavid du Colombier }
8039a747e4fSDavid du Colombier
8044de34a7eSDavid du Colombier static void pcireservemem(void);
8054de34a7eSDavid du Colombier
8064fafed5dSDavid du Colombier static int
pcicfgrw8bios(int tbdf,int rno,int data,int read)8074fafed5dSDavid du Colombier pcicfgrw8bios(int tbdf, int rno, int data, int read)
8084fafed5dSDavid du Colombier {
8094fafed5dSDavid du Colombier BIOS32ci ci;
8104fafed5dSDavid du Colombier
8114fafed5dSDavid du Colombier if(pcibiossi == nil)
8124fafed5dSDavid du Colombier return -1;
8134fafed5dSDavid du Colombier
8144fafed5dSDavid du Colombier memset(&ci, 0, sizeof(BIOS32ci));
8154fafed5dSDavid du Colombier ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
8164fafed5dSDavid du Colombier ci.edi = rno;
8174fafed5dSDavid du Colombier if(read){
8184fafed5dSDavid du Colombier ci.eax = 0xB108;
8194fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8204fafed5dSDavid du Colombier return ci.ecx & 0xFF;
8214fafed5dSDavid du Colombier }
8224fafed5dSDavid du Colombier else{
8234fafed5dSDavid du Colombier ci.eax = 0xB10B;
8244fafed5dSDavid du Colombier ci.ecx = data & 0xFF;
8254fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8264fafed5dSDavid du Colombier return 0;
8274fafed5dSDavid du Colombier }
8284fafed5dSDavid du Colombier
8294fafed5dSDavid du Colombier return -1;
8304fafed5dSDavid du Colombier }
8314fafed5dSDavid du Colombier
8324fafed5dSDavid du Colombier static int
pcicfgrw16bios(int tbdf,int rno,int data,int read)8334fafed5dSDavid du Colombier pcicfgrw16bios(int tbdf, int rno, int data, int read)
8344fafed5dSDavid du Colombier {
8354fafed5dSDavid du Colombier BIOS32ci ci;
8364fafed5dSDavid du Colombier
8374fafed5dSDavid du Colombier if(pcibiossi == nil)
8384fafed5dSDavid du Colombier return -1;
8394fafed5dSDavid du Colombier
8404fafed5dSDavid du Colombier memset(&ci, 0, sizeof(BIOS32ci));
8414fafed5dSDavid du Colombier ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
8424fafed5dSDavid du Colombier ci.edi = rno;
8434fafed5dSDavid du Colombier if(read){
8444fafed5dSDavid du Colombier ci.eax = 0xB109;
8454fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8464fafed5dSDavid du Colombier return ci.ecx & 0xFFFF;
8474fafed5dSDavid du Colombier }
8484fafed5dSDavid du Colombier else{
8494fafed5dSDavid du Colombier ci.eax = 0xB10C;
8504fafed5dSDavid du Colombier ci.ecx = data & 0xFFFF;
8514fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8524fafed5dSDavid du Colombier return 0;
8534fafed5dSDavid du Colombier }
8544fafed5dSDavid du Colombier
8554fafed5dSDavid du Colombier return -1;
8564fafed5dSDavid du Colombier }
8574fafed5dSDavid du Colombier
8584fafed5dSDavid du Colombier static int
pcicfgrw32bios(int tbdf,int rno,int data,int read)8594fafed5dSDavid du Colombier pcicfgrw32bios(int tbdf, int rno, int data, int read)
8604fafed5dSDavid du Colombier {
8614fafed5dSDavid du Colombier BIOS32ci ci;
8624fafed5dSDavid du Colombier
8634fafed5dSDavid du Colombier if(pcibiossi == nil)
8644fafed5dSDavid du Colombier return -1;
8654fafed5dSDavid du Colombier
8664fafed5dSDavid du Colombier memset(&ci, 0, sizeof(BIOS32ci));
8674fafed5dSDavid du Colombier ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
8684fafed5dSDavid du Colombier ci.edi = rno;
8694fafed5dSDavid du Colombier if(read){
8704fafed5dSDavid du Colombier ci.eax = 0xB10A;
8714fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8724fafed5dSDavid du Colombier return ci.ecx;
8734fafed5dSDavid du Colombier }
8744fafed5dSDavid du Colombier else{
8754fafed5dSDavid du Colombier ci.eax = 0xB10D;
8764fafed5dSDavid du Colombier ci.ecx = data;
8774fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8784fafed5dSDavid du Colombier return 0;
8794fafed5dSDavid du Colombier }
8804fafed5dSDavid du Colombier
8814fafed5dSDavid du Colombier return -1;
8824fafed5dSDavid du Colombier }
8834fafed5dSDavid du Colombier
8844fafed5dSDavid du Colombier static BIOS32si*
pcibiosinit(void)8854fafed5dSDavid du Colombier pcibiosinit(void)
8864fafed5dSDavid du Colombier {
8874fafed5dSDavid du Colombier BIOS32ci ci;
8884fafed5dSDavid du Colombier BIOS32si *si;
8894fafed5dSDavid du Colombier
8904fafed5dSDavid du Colombier if((si = bios32open("$PCI")) == nil)
8914fafed5dSDavid du Colombier return nil;
8924fafed5dSDavid du Colombier
8934fafed5dSDavid du Colombier memset(&ci, 0, sizeof(BIOS32ci));
8944fafed5dSDavid du Colombier ci.eax = 0xB101;
8954fafed5dSDavid du Colombier if(bios32ci(si, &ci) || ci.edx != ((' '<<24)|('I'<<16)|('C'<<8)|'P')){
8964fafed5dSDavid du Colombier free(si);
8974fafed5dSDavid du Colombier return nil;
8984fafed5dSDavid du Colombier }
8994fafed5dSDavid du Colombier if(ci.eax & 0x01)
9004fafed5dSDavid du Colombier pcimaxdno = 31;
9014fafed5dSDavid du Colombier else
9024fafed5dSDavid du Colombier pcimaxdno = 15;
9034fafed5dSDavid du Colombier pcimaxbno = ci.ecx & 0xff;
9044fafed5dSDavid du Colombier
9054fafed5dSDavid du Colombier return si;
9064fafed5dSDavid du Colombier }
9074fafed5dSDavid du Colombier
908c91329d7SDavid du Colombier void
pcibussize(Pcidev * root,ulong * msize,ulong * iosize)909c91329d7SDavid du Colombier pcibussize(Pcidev *root, ulong *msize, ulong *iosize)
910c91329d7SDavid du Colombier {
911c91329d7SDavid du Colombier *msize = 0;
912c91329d7SDavid du Colombier *iosize = 0;
913c91329d7SDavid du Colombier pcibusmap(root, msize, iosize, 0);
914c91329d7SDavid du Colombier }
915c91329d7SDavid du Colombier
9167dd7cddfSDavid du Colombier static void
pcicfginit(void)9177dd7cddfSDavid du Colombier pcicfginit(void)
9187dd7cddfSDavid du Colombier {
9197dd7cddfSDavid du Colombier char *p;
9207dd7cddfSDavid du Colombier Pcidev **list;
9217dd7cddfSDavid du Colombier ulong mema, ioa;
922becaf2abSDavid du Colombier int bno, n, pcibios;
9237dd7cddfSDavid du Colombier
9247dd7cddfSDavid du Colombier lock(&pcicfginitlock);
9257dd7cddfSDavid du Colombier if(pcicfgmode != -1)
9267dd7cddfSDavid du Colombier goto out;
9277dd7cddfSDavid du Colombier
928becaf2abSDavid du Colombier pcibios = 0;
92980ee5cbfSDavid du Colombier if(getconf("*nobios"))
93080ee5cbfSDavid du Colombier nobios = 1;
931becaf2abSDavid du Colombier else if(getconf("*pcibios"))
932becaf2abSDavid du Colombier pcibios = 1;
9333ff48bf5SDavid du Colombier if(getconf("*nopcirouting"))
9343ff48bf5SDavid du Colombier nopcirouting = 1;
9353ff48bf5SDavid du Colombier
9367dd7cddfSDavid du Colombier /*
9377dd7cddfSDavid du Colombier * Try to determine which PCI configuration mode is implemented.
9387dd7cddfSDavid du Colombier * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
9397dd7cddfSDavid du Colombier * a DWORD at 0xCF8 and another at 0xCFC and will pass through
9407dd7cddfSDavid du Colombier * any non-DWORD accesses as normal I/O cycles. There shouldn't be
9413ff48bf5SDavid du Colombier * a device behind these addresses so if Mode1 accesses fail try
9423ff48bf5SDavid du Colombier * for Mode2 (Mode2 is deprecated).
9437dd7cddfSDavid du Colombier */
944becaf2abSDavid du Colombier if(!pcibios){
945becaf2abSDavid du Colombier /*
946becaf2abSDavid du Colombier * Bits [30:24] of PciADDR must be 0,
947becaf2abSDavid du Colombier * according to the spec.
948becaf2abSDavid du Colombier */
949becaf2abSDavid du Colombier n = inl(PciADDR);
9506e712d44SDavid du Colombier if(!(n & 0x7F000000)){
951becaf2abSDavid du Colombier outl(PciADDR, 0x80000000);
952becaf2abSDavid du Colombier outb(PciADDR+3, 0);
953becaf2abSDavid du Colombier if(inl(PciADDR) & 0x80000000){
9547dd7cddfSDavid du Colombier pcicfgmode = 1;
9557dd7cddfSDavid du Colombier pcimaxdno = 31;
9567dd7cddfSDavid du Colombier }
957becaf2abSDavid du Colombier }
958becaf2abSDavid du Colombier outl(PciADDR, n);
959becaf2abSDavid du Colombier
960becaf2abSDavid du Colombier if(pcicfgmode < 0){
961becaf2abSDavid du Colombier /*
962becaf2abSDavid du Colombier * The 'key' part of PciCSE should be 0.
963becaf2abSDavid du Colombier */
964becaf2abSDavid du Colombier n = inb(PciCSE);
965becaf2abSDavid du Colombier if(!(n & 0xF0)){
966becaf2abSDavid du Colombier outb(PciCSE, 0x0E);
967becaf2abSDavid du Colombier if(inb(PciCSE) == 0x0E){
9683ff48bf5SDavid du Colombier pcicfgmode = 2;
9693ff48bf5SDavid du Colombier pcimaxdno = 15;
9703ff48bf5SDavid du Colombier }
9717dd7cddfSDavid du Colombier }
972becaf2abSDavid du Colombier outb(PciCSE, n);
973becaf2abSDavid du Colombier }
974becaf2abSDavid du Colombier }
9757dd7cddfSDavid du Colombier
9764fafed5dSDavid du Colombier if(pcicfgmode < 0 || pcibios) {
9774fafed5dSDavid du Colombier if((pcibiossi = pcibiosinit()) == nil)
9787dd7cddfSDavid du Colombier goto out;
9794fafed5dSDavid du Colombier pcicfgrw8 = pcicfgrw8bios;
9804fafed5dSDavid du Colombier pcicfgrw16 = pcicfgrw16bios;
9814fafed5dSDavid du Colombier pcicfgrw32 = pcicfgrw32bios;
9824fafed5dSDavid du Colombier pcicfgmode = 3;
9834fafed5dSDavid du Colombier }
9847dd7cddfSDavid du Colombier
9859a747e4fSDavid du Colombier fmtinstall('T', tbdffmt);
9867dd7cddfSDavid du Colombier
987becaf2abSDavid du Colombier if(p = getconf("*pcimaxbno")){
988becaf2abSDavid du Colombier n = strtoul(p, 0, 0);
989becaf2abSDavid du Colombier if(n < pcimaxbno)
990becaf2abSDavid du Colombier pcimaxbno = n;
991becaf2abSDavid du Colombier }
992becaf2abSDavid du Colombier if(p = getconf("*pcimaxdno")){
993becaf2abSDavid du Colombier n = strtoul(p, 0, 0);
994becaf2abSDavid du Colombier if(n < pcimaxdno)
995becaf2abSDavid du Colombier pcimaxdno = n;
996becaf2abSDavid du Colombier }
9977dd7cddfSDavid du Colombier
9987dd7cddfSDavid du Colombier list = &pciroot;
999223a736eSDavid du Colombier for(bno = 0; bno <= pcimaxbno; bno++) {
10009a747e4fSDavid du Colombier int sbno = bno;
10019a747e4fSDavid du Colombier bno = pcilscan(bno, list);
10029a747e4fSDavid du Colombier
10037dd7cddfSDavid du Colombier while(*list)
10047dd7cddfSDavid du Colombier list = &(*list)->link;
10059a747e4fSDavid du Colombier
10069a747e4fSDavid du Colombier if (sbno == 0) {
10079a747e4fSDavid du Colombier Pcidev *pci;
10089a747e4fSDavid du Colombier
10099a747e4fSDavid du Colombier /*
10109a747e4fSDavid du Colombier * If we have found a PCI-to-Cardbus bridge, make sure
10119a747e4fSDavid du Colombier * it has no valid mappings anymore.
10129a747e4fSDavid du Colombier */
1013c91329d7SDavid du Colombier for(pci = pciroot; pci != nil; pci = pci->link){
10149a747e4fSDavid du Colombier if (pci->ccrb == 6 && pci->ccru == 7) {
10159a747e4fSDavid du Colombier ushort bcr;
10169a747e4fSDavid du Colombier
10179a747e4fSDavid du Colombier /* reset the cardbus */
10189a747e4fSDavid du Colombier bcr = pcicfgr16(pci, PciBCR);
10199a747e4fSDavid du Colombier pcicfgw16(pci, PciBCR, 0x40 | bcr);
10209a747e4fSDavid du Colombier delay(50);
10219a747e4fSDavid du Colombier }
10229a747e4fSDavid du Colombier }
10239a747e4fSDavid du Colombier }
10247dd7cddfSDavid du Colombier }
10257dd7cddfSDavid du Colombier
10267dd7cddfSDavid du Colombier if(pciroot == nil)
10277dd7cddfSDavid du Colombier goto out;
10287dd7cddfSDavid du Colombier
102980ee5cbfSDavid du Colombier if(nobios) {
10307dd7cddfSDavid du Colombier /*
10317dd7cddfSDavid du Colombier * Work out how big the top bus is
10327dd7cddfSDavid du Colombier */
1033c91329d7SDavid du Colombier pcibussize(pciroot, &mema, &ioa);
10347dd7cddfSDavid du Colombier
10357dd7cddfSDavid du Colombier /*
10367dd7cddfSDavid du Colombier * Align the windows and map it
10377dd7cddfSDavid du Colombier */
10387dd7cddfSDavid du Colombier ioa = 0x1000;
10397dd7cddfSDavid du Colombier mema = 0x90000000;
10407dd7cddfSDavid du Colombier
10417dd7cddfSDavid du Colombier pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
10427dd7cddfSDavid du Colombier
10437dd7cddfSDavid du Colombier pcibusmap(pciroot, &mema, &ioa, 1);
10447dd7cddfSDavid du Colombier DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
10457dd7cddfSDavid du Colombier
10467dd7cddfSDavid du Colombier unlock(&pcicfginitlock);
10477dd7cddfSDavid du Colombier return;
10487dd7cddfSDavid du Colombier }
10499a747e4fSDavid du Colombier
10503ff48bf5SDavid du Colombier if (!nopcirouting)
10519a747e4fSDavid du Colombier pcirouting();
10529a747e4fSDavid du Colombier
10537dd7cddfSDavid du Colombier out:
10544de34a7eSDavid du Colombier pcireservemem();
10557dd7cddfSDavid du Colombier unlock(&pcicfginitlock);
10569a747e4fSDavid du Colombier
10573ff48bf5SDavid du Colombier if(getconf("*pcihinv"))
10583ff48bf5SDavid du Colombier pcihinv(nil);
10597dd7cddfSDavid du Colombier }
10607dd7cddfSDavid du Colombier
10614de34a7eSDavid du Colombier static void
pcireservemem(void)10624de34a7eSDavid du Colombier pcireservemem(void)
10634de34a7eSDavid du Colombier {
10644de34a7eSDavid du Colombier int i;
10654de34a7eSDavid du Colombier Pcidev *p;
10664de34a7eSDavid du Colombier
10674de34a7eSDavid du Colombier /*
10684de34a7eSDavid du Colombier * mark all the physical address space claimed by pci devices
10694de34a7eSDavid du Colombier * as in use, so that upaalloc doesn't give it out.
10704de34a7eSDavid du Colombier */
10714de34a7eSDavid du Colombier for(p=pciroot; p; p=p->list)
10724de34a7eSDavid du Colombier for(i=0; i<nelem(p->mem); i++)
10734de34a7eSDavid du Colombier if(p->mem[i].bar && (p->mem[i].bar&1) == 0)
10744de34a7eSDavid du Colombier upareserve(p->mem[i].bar&~0x0F, p->mem[i].size);
10754de34a7eSDavid du Colombier }
10764de34a7eSDavid du Colombier
10777dd7cddfSDavid du Colombier static int
pcicfgrw8raw(int tbdf,int rno,int data,int read)10784fafed5dSDavid du Colombier pcicfgrw8raw(int tbdf, int rno, int data, int read)
10797dd7cddfSDavid du Colombier {
10807dd7cddfSDavid du Colombier int o, type, x;
10817dd7cddfSDavid du Colombier
10827dd7cddfSDavid du Colombier if(pcicfgmode == -1)
10837dd7cddfSDavid du Colombier pcicfginit();
10847dd7cddfSDavid du Colombier
10857dd7cddfSDavid du Colombier if(BUSBNO(tbdf))
10867dd7cddfSDavid du Colombier type = 0x01;
10877dd7cddfSDavid du Colombier else
10887dd7cddfSDavid du Colombier type = 0x00;
10897dd7cddfSDavid du Colombier x = -1;
10907dd7cddfSDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
10917dd7cddfSDavid du Colombier return x;
10927dd7cddfSDavid du Colombier
10937dd7cddfSDavid du Colombier lock(&pcicfglock);
10947dd7cddfSDavid du Colombier switch(pcicfgmode){
10957dd7cddfSDavid du Colombier
10967dd7cddfSDavid du Colombier case 1:
10977dd7cddfSDavid du Colombier o = rno & 0x03;
10987dd7cddfSDavid du Colombier rno &= ~0x03;
10997dd7cddfSDavid du Colombier outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
11007dd7cddfSDavid du Colombier if(read)
11017dd7cddfSDavid du Colombier x = inb(PciDATA+o);
11027dd7cddfSDavid du Colombier else
11037dd7cddfSDavid du Colombier outb(PciDATA+o, data);
11047dd7cddfSDavid du Colombier outl(PciADDR, 0);
11057dd7cddfSDavid du Colombier break;
11067dd7cddfSDavid du Colombier
11077dd7cddfSDavid du Colombier case 2:
11087dd7cddfSDavid du Colombier outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
11097dd7cddfSDavid du Colombier outb(PciFORWARD, BUSBNO(tbdf));
11107dd7cddfSDavid du Colombier if(read)
11117dd7cddfSDavid du Colombier x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
11127dd7cddfSDavid du Colombier else
11137dd7cddfSDavid du Colombier outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
11147dd7cddfSDavid du Colombier outb(PciCSE, 0);
11157dd7cddfSDavid du Colombier break;
11167dd7cddfSDavid du Colombier }
11177dd7cddfSDavid du Colombier unlock(&pcicfglock);
11187dd7cddfSDavid du Colombier
11197dd7cddfSDavid du Colombier return x;
11207dd7cddfSDavid du Colombier }
11217dd7cddfSDavid du Colombier
11227dd7cddfSDavid du Colombier int
pcicfgr8(Pcidev * pcidev,int rno)11237dd7cddfSDavid du Colombier pcicfgr8(Pcidev* pcidev, int rno)
11247dd7cddfSDavid du Colombier {
11257dd7cddfSDavid du Colombier return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
11267dd7cddfSDavid du Colombier }
11277dd7cddfSDavid du Colombier
11287dd7cddfSDavid du Colombier void
pcicfgw8(Pcidev * pcidev,int rno,int data)11297dd7cddfSDavid du Colombier pcicfgw8(Pcidev* pcidev, int rno, int data)
11307dd7cddfSDavid du Colombier {
11317dd7cddfSDavid du Colombier pcicfgrw8(pcidev->tbdf, rno, data, 0);
11327dd7cddfSDavid du Colombier }
11337dd7cddfSDavid du Colombier
11347dd7cddfSDavid du Colombier static int
pcicfgrw16raw(int tbdf,int rno,int data,int read)11354fafed5dSDavid du Colombier pcicfgrw16raw(int tbdf, int rno, int data, int read)
11367dd7cddfSDavid du Colombier {
11377dd7cddfSDavid du Colombier int o, type, x;
11387dd7cddfSDavid du Colombier
11397dd7cddfSDavid du Colombier if(pcicfgmode == -1)
11407dd7cddfSDavid du Colombier pcicfginit();
11417dd7cddfSDavid du Colombier
11427dd7cddfSDavid du Colombier if(BUSBNO(tbdf))
11437dd7cddfSDavid du Colombier type = 0x01;
11447dd7cddfSDavid du Colombier else
11457dd7cddfSDavid du Colombier type = 0x00;
11467dd7cddfSDavid du Colombier x = -1;
11477dd7cddfSDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
11487dd7cddfSDavid du Colombier return x;
11497dd7cddfSDavid du Colombier
11507dd7cddfSDavid du Colombier lock(&pcicfglock);
11517dd7cddfSDavid du Colombier switch(pcicfgmode){
11527dd7cddfSDavid du Colombier
11537dd7cddfSDavid du Colombier case 1:
11547dd7cddfSDavid du Colombier o = rno & 0x02;
11557dd7cddfSDavid du Colombier rno &= ~0x03;
11567dd7cddfSDavid du Colombier outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
11577dd7cddfSDavid du Colombier if(read)
11587dd7cddfSDavid du Colombier x = ins(PciDATA+o);
11597dd7cddfSDavid du Colombier else
11607dd7cddfSDavid du Colombier outs(PciDATA+o, data);
11617dd7cddfSDavid du Colombier outl(PciADDR, 0);
11627dd7cddfSDavid du Colombier break;
11637dd7cddfSDavid du Colombier
11647dd7cddfSDavid du Colombier case 2:
11657dd7cddfSDavid du Colombier outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
11667dd7cddfSDavid du Colombier outb(PciFORWARD, BUSBNO(tbdf));
11677dd7cddfSDavid du Colombier if(read)
11687dd7cddfSDavid du Colombier x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
11697dd7cddfSDavid du Colombier else
11707dd7cddfSDavid du Colombier outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
11717dd7cddfSDavid du Colombier outb(PciCSE, 0);
11727dd7cddfSDavid du Colombier break;
11737dd7cddfSDavid du Colombier }
11747dd7cddfSDavid du Colombier unlock(&pcicfglock);
11757dd7cddfSDavid du Colombier
11767dd7cddfSDavid du Colombier return x;
11777dd7cddfSDavid du Colombier }
11787dd7cddfSDavid du Colombier
11797dd7cddfSDavid du Colombier int
pcicfgr16(Pcidev * pcidev,int rno)11807dd7cddfSDavid du Colombier pcicfgr16(Pcidev* pcidev, int rno)
11817dd7cddfSDavid du Colombier {
11827dd7cddfSDavid du Colombier return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
11837dd7cddfSDavid du Colombier }
11847dd7cddfSDavid du Colombier
11857dd7cddfSDavid du Colombier void
pcicfgw16(Pcidev * pcidev,int rno,int data)11867dd7cddfSDavid du Colombier pcicfgw16(Pcidev* pcidev, int rno, int data)
11877dd7cddfSDavid du Colombier {
11887dd7cddfSDavid du Colombier pcicfgrw16(pcidev->tbdf, rno, data, 0);
11897dd7cddfSDavid du Colombier }
11907dd7cddfSDavid du Colombier
11917dd7cddfSDavid du Colombier static int
pcicfgrw32raw(int tbdf,int rno,int data,int read)11924fafed5dSDavid du Colombier pcicfgrw32raw(int tbdf, int rno, int data, int read)
11937dd7cddfSDavid du Colombier {
11947dd7cddfSDavid du Colombier int type, x;
11957dd7cddfSDavid du Colombier
11967dd7cddfSDavid du Colombier if(pcicfgmode == -1)
11977dd7cddfSDavid du Colombier pcicfginit();
11987dd7cddfSDavid du Colombier
11997dd7cddfSDavid du Colombier if(BUSBNO(tbdf))
12007dd7cddfSDavid du Colombier type = 0x01;
12017dd7cddfSDavid du Colombier else
12027dd7cddfSDavid du Colombier type = 0x00;
12037dd7cddfSDavid du Colombier x = -1;
12047dd7cddfSDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
12057dd7cddfSDavid du Colombier return x;
12067dd7cddfSDavid du Colombier
12077dd7cddfSDavid du Colombier lock(&pcicfglock);
12087dd7cddfSDavid du Colombier switch(pcicfgmode){
12097dd7cddfSDavid du Colombier
12107dd7cddfSDavid du Colombier case 1:
12117dd7cddfSDavid du Colombier rno &= ~0x03;
12127dd7cddfSDavid du Colombier outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
12137dd7cddfSDavid du Colombier if(read)
12147dd7cddfSDavid du Colombier x = inl(PciDATA);
12157dd7cddfSDavid du Colombier else
12167dd7cddfSDavid du Colombier outl(PciDATA, data);
12177dd7cddfSDavid du Colombier outl(PciADDR, 0);
12187dd7cddfSDavid du Colombier break;
12197dd7cddfSDavid du Colombier
12207dd7cddfSDavid du Colombier case 2:
12217dd7cddfSDavid du Colombier outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
12227dd7cddfSDavid du Colombier outb(PciFORWARD, BUSBNO(tbdf));
12237dd7cddfSDavid du Colombier if(read)
12247dd7cddfSDavid du Colombier x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
12257dd7cddfSDavid du Colombier else
12267dd7cddfSDavid du Colombier outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
12277dd7cddfSDavid du Colombier outb(PciCSE, 0);
12287dd7cddfSDavid du Colombier break;
12297dd7cddfSDavid du Colombier }
12307dd7cddfSDavid du Colombier unlock(&pcicfglock);
12317dd7cddfSDavid du Colombier
12327dd7cddfSDavid du Colombier return x;
12337dd7cddfSDavid du Colombier }
12347dd7cddfSDavid du Colombier
12357dd7cddfSDavid du Colombier int
pcicfgr32(Pcidev * pcidev,int rno)12367dd7cddfSDavid du Colombier pcicfgr32(Pcidev* pcidev, int rno)
12377dd7cddfSDavid du Colombier {
12387dd7cddfSDavid du Colombier return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
12397dd7cddfSDavid du Colombier }
12407dd7cddfSDavid du Colombier
12417dd7cddfSDavid du Colombier void
pcicfgw32(Pcidev * pcidev,int rno,int data)12427dd7cddfSDavid du Colombier pcicfgw32(Pcidev* pcidev, int rno, int data)
12437dd7cddfSDavid du Colombier {
12447dd7cddfSDavid du Colombier pcicfgrw32(pcidev->tbdf, rno, data, 0);
12457dd7cddfSDavid du Colombier }
12467dd7cddfSDavid du Colombier
12477dd7cddfSDavid du Colombier Pcidev*
pcimatch(Pcidev * prev,int vid,int did)12487dd7cddfSDavid du Colombier pcimatch(Pcidev* prev, int vid, int did)
12497dd7cddfSDavid du Colombier {
12507dd7cddfSDavid du Colombier if(pcicfgmode == -1)
12517dd7cddfSDavid du Colombier pcicfginit();
12527dd7cddfSDavid du Colombier
12537dd7cddfSDavid du Colombier if(prev == nil)
12547dd7cddfSDavid du Colombier prev = pcilist;
12557dd7cddfSDavid du Colombier else
12567dd7cddfSDavid du Colombier prev = prev->list;
12577dd7cddfSDavid du Colombier
12587dd7cddfSDavid du Colombier while(prev != nil){
12597dd7cddfSDavid du Colombier if((vid == 0 || prev->vid == vid)
12607dd7cddfSDavid du Colombier && (did == 0 || prev->did == did))
12617dd7cddfSDavid du Colombier break;
12627dd7cddfSDavid du Colombier prev = prev->list;
12637dd7cddfSDavid du Colombier }
12647dd7cddfSDavid du Colombier return prev;
12657dd7cddfSDavid du Colombier }
12667dd7cddfSDavid du Colombier
12677dd7cddfSDavid du Colombier Pcidev*
pcimatchtbdf(int tbdf)12687dd7cddfSDavid du Colombier pcimatchtbdf(int tbdf)
12697dd7cddfSDavid du Colombier {
12707dd7cddfSDavid du Colombier Pcidev *pcidev;
12717dd7cddfSDavid du Colombier
12727dd7cddfSDavid du Colombier if(pcicfgmode == -1)
12737dd7cddfSDavid du Colombier pcicfginit();
12747dd7cddfSDavid du Colombier
12757dd7cddfSDavid du Colombier for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
12767dd7cddfSDavid du Colombier if(pcidev->tbdf == tbdf)
12777dd7cddfSDavid du Colombier break;
12787dd7cddfSDavid du Colombier }
12797dd7cddfSDavid du Colombier return pcidev;
12807dd7cddfSDavid du Colombier }
12817dd7cddfSDavid du Colombier
12829a747e4fSDavid du Colombier uchar
pciipin(Pcidev * pci,uchar pin)12839a747e4fSDavid du Colombier pciipin(Pcidev *pci, uchar pin)
12849a747e4fSDavid du Colombier {
12859a747e4fSDavid du Colombier if (pci == nil)
12869a747e4fSDavid du Colombier pci = pcilist;
12879a747e4fSDavid du Colombier
12889a747e4fSDavid du Colombier while (pci) {
12899a747e4fSDavid du Colombier uchar intl;
12909a747e4fSDavid du Colombier
12919a747e4fSDavid du Colombier if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
12929a747e4fSDavid du Colombier return pci->intl;
12939a747e4fSDavid du Colombier
12949a747e4fSDavid du Colombier if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
12959a747e4fSDavid du Colombier return intl;
12969a747e4fSDavid du Colombier
12979a747e4fSDavid du Colombier pci = pci->list;
12989a747e4fSDavid du Colombier }
12999a747e4fSDavid du Colombier return 0;
13009a747e4fSDavid du Colombier }
13019a747e4fSDavid du Colombier
13029a747e4fSDavid du Colombier static void
pcilhinv(Pcidev * p)13039a747e4fSDavid du Colombier pcilhinv(Pcidev* p)
13047dd7cddfSDavid du Colombier {
13057dd7cddfSDavid du Colombier int i;
13067dd7cddfSDavid du Colombier Pcidev *t;
13077dd7cddfSDavid du Colombier
13087dd7cddfSDavid du Colombier if(p == nil) {
13097dd7cddfSDavid du Colombier putstrn(PCICONS.output, PCICONS.ptr);
13107dd7cddfSDavid du Colombier p = pciroot;
13117dd7cddfSDavid du Colombier print("bus dev type vid did intl memory\n");
13127dd7cddfSDavid du Colombier }
13137dd7cddfSDavid du Colombier for(t = p; t != nil; t = t->link) {
13147dd7cddfSDavid du Colombier print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
13157dd7cddfSDavid du Colombier BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
13167dd7cddfSDavid du Colombier t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
13177dd7cddfSDavid du Colombier
13187dd7cddfSDavid du Colombier for(i = 0; i < nelem(p->mem); i++) {
13197dd7cddfSDavid du Colombier if(t->mem[i].size == 0)
13207dd7cddfSDavid du Colombier continue;
13217dd7cddfSDavid du Colombier print("%d:%.8lux %d ", i,
13227dd7cddfSDavid du Colombier t->mem[i].bar, t->mem[i].size);
13237dd7cddfSDavid du Colombier }
13247dd7cddfSDavid du Colombier if(t->ioa.bar || t->ioa.size)
13257dd7cddfSDavid du Colombier print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
13267dd7cddfSDavid du Colombier if(t->mema.bar || t->mema.size)
13277dd7cddfSDavid du Colombier print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
13287dd7cddfSDavid du Colombier if(t->bridge)
13297dd7cddfSDavid du Colombier print("->%d", BUSBNO(t->bridge->tbdf));
13307dd7cddfSDavid du Colombier print("\n");
13317dd7cddfSDavid du Colombier }
13327dd7cddfSDavid du Colombier while(p != nil) {
13337dd7cddfSDavid du Colombier if(p->bridge != nil)
13349a747e4fSDavid du Colombier pcilhinv(p->bridge);
13357dd7cddfSDavid du Colombier p = p->link;
13367dd7cddfSDavid du Colombier }
13377dd7cddfSDavid du Colombier }
13387dd7cddfSDavid du Colombier
13397dd7cddfSDavid du Colombier void
pcihinv(Pcidev * p)13409a747e4fSDavid du Colombier pcihinv(Pcidev* p)
13419a747e4fSDavid du Colombier {
13429a747e4fSDavid du Colombier if(pcicfgmode == -1)
13439a747e4fSDavid du Colombier pcicfginit();
13449a747e4fSDavid du Colombier lock(&pcicfginitlock);
13459a747e4fSDavid du Colombier pcilhinv(p);
13469a747e4fSDavid du Colombier unlock(&pcicfginitlock);
13479a747e4fSDavid du Colombier }
13489a747e4fSDavid du Colombier
13499a747e4fSDavid du Colombier void
pcireset(void)13507dd7cddfSDavid du Colombier pcireset(void)
13517dd7cddfSDavid du Colombier {
13527dd7cddfSDavid du Colombier Pcidev *p;
13537dd7cddfSDavid du Colombier
13547dd7cddfSDavid du Colombier if(pcicfgmode == -1)
13557dd7cddfSDavid du Colombier pcicfginit();
13567dd7cddfSDavid du Colombier
13577dd7cddfSDavid du Colombier for(p = pcilist; p != nil; p = p->list) {
13589a747e4fSDavid du Colombier /* don't mess with the bridges */
13599a747e4fSDavid du Colombier if(p->ccrb == 0x06)
13609a747e4fSDavid du Colombier continue;
13619a747e4fSDavid du Colombier pciclrbme(p);
13627dd7cddfSDavid du Colombier }
13637dd7cddfSDavid du Colombier }
13647dd7cddfSDavid du Colombier
13657dd7cddfSDavid du Colombier void
pcisetioe(Pcidev * p)1366220e960cSDavid du Colombier pcisetioe(Pcidev* p)
1367220e960cSDavid du Colombier {
1368e569ccb5SDavid du Colombier p->pcr |= IOen;
1369e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1370220e960cSDavid du Colombier }
1371220e960cSDavid du Colombier
1372220e960cSDavid du Colombier void
pciclrioe(Pcidev * p)1373220e960cSDavid du Colombier pciclrioe(Pcidev* p)
1374220e960cSDavid du Colombier {
1375e569ccb5SDavid du Colombier p->pcr &= ~IOen;
1376e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1377220e960cSDavid du Colombier }
1378220e960cSDavid du Colombier
1379220e960cSDavid du Colombier void
pcisetbme(Pcidev * p)13807dd7cddfSDavid du Colombier pcisetbme(Pcidev* p)
13817dd7cddfSDavid du Colombier {
1382e569ccb5SDavid du Colombier p->pcr |= MASen;
1383e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
13847dd7cddfSDavid du Colombier }
13859a747e4fSDavid du Colombier
13869a747e4fSDavid du Colombier void
pciclrbme(Pcidev * p)13879a747e4fSDavid du Colombier pciclrbme(Pcidev* p)
13889a747e4fSDavid du Colombier {
1389e569ccb5SDavid du Colombier p->pcr &= ~MASen;
1390e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1391e569ccb5SDavid du Colombier }
13929a747e4fSDavid du Colombier
1393d8f3f65eSDavid du Colombier void
pcisetmwi(Pcidev * p)1394d8f3f65eSDavid du Colombier pcisetmwi(Pcidev* p)
1395d8f3f65eSDavid du Colombier {
1396d8f3f65eSDavid du Colombier p->pcr |= MemWrInv;
1397d8f3f65eSDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1398d8f3f65eSDavid du Colombier }
1399d8f3f65eSDavid du Colombier
1400d8f3f65eSDavid du Colombier void
pciclrmwi(Pcidev * p)1401d8f3f65eSDavid du Colombier pciclrmwi(Pcidev* p)
1402d8f3f65eSDavid du Colombier {
1403d8f3f65eSDavid du Colombier p->pcr &= ~MemWrInv;
1404d8f3f65eSDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1405d8f3f65eSDavid du Colombier }
1406d8f3f65eSDavid du Colombier
1407ee52d9b2SDavid du Colombier int
pcienumcaps(Pcidev * p,int (* fmatch)(Pcidev *,int,int,int),int arg)1408*0336b12dSDavid du Colombier pcienumcaps(Pcidev* p, int (*fmatch)(Pcidev*, int, int, int), int arg)
1409e569ccb5SDavid du Colombier {
1410*0336b12dSDavid du Colombier int i, r, cap, off;
1411e569ccb5SDavid du Colombier
1412ee52d9b2SDavid du Colombier /* status register bit 4 has capabilities */
1413ee52d9b2SDavid du Colombier if((pcicfgr16(p, PciPSR) & 1<<4) == 0)
1414e569ccb5SDavid du Colombier return -1;
1415ee52d9b2SDavid du Colombier switch(pcicfgr8(p, PciHDT) & 0x7f){
1416e569ccb5SDavid du Colombier default:
1417e569ccb5SDavid du Colombier return -1;
1418e569ccb5SDavid du Colombier case 0: /* all other */
1419e569ccb5SDavid du Colombier case 1: /* PCI to PCI bridge */
1420ee52d9b2SDavid du Colombier off = 0x34;
1421e569ccb5SDavid du Colombier break;
1422e569ccb5SDavid du Colombier case 2: /* CardBus bridge */
1423ee52d9b2SDavid du Colombier off = 0x14;
1424e569ccb5SDavid du Colombier break;
1425e569ccb5SDavid du Colombier }
1426ee52d9b2SDavid du Colombier for(i = 48; i--;){
1427ee52d9b2SDavid du Colombier off = pcicfgr8(p, off);
1428ee52d9b2SDavid du Colombier if(off < 0x40 || (off & 3))
1429ee52d9b2SDavid du Colombier break;
1430ee52d9b2SDavid du Colombier off &= ~3;
1431*0336b12dSDavid du Colombier cap = pcicfgr8(p, off);
1432*0336b12dSDavid du Colombier if(cap == 0xff)
1433ee52d9b2SDavid du Colombier break;
1434*0336b12dSDavid du Colombier r = (*fmatch)(p, cap, off, arg);
1435*0336b12dSDavid du Colombier if(r < 0)
1436*0336b12dSDavid du Colombier break;
1437*0336b12dSDavid du Colombier if(r == 0)
1438ee52d9b2SDavid du Colombier return off;
1439ee52d9b2SDavid du Colombier off++;
1440e569ccb5SDavid du Colombier }
1441e569ccb5SDavid du Colombier
1442e569ccb5SDavid du Colombier return -1;
1443e569ccb5SDavid du Colombier }
1444e569ccb5SDavid du Colombier
1445*0336b12dSDavid du Colombier static int
matchcap(Pcidev *,int cap,int,int arg)1446*0336b12dSDavid du Colombier matchcap(Pcidev*, int cap, int, int arg)
1447*0336b12dSDavid du Colombier {
1448*0336b12dSDavid du Colombier return cap != arg;
1449*0336b12dSDavid du Colombier }
1450*0336b12dSDavid du Colombier
1451*0336b12dSDavid du Colombier static int
matchhtcap(Pcidev * p,int cap,int off,int arg)1452*0336b12dSDavid du Colombier matchhtcap(Pcidev* p, int cap, int off, int arg)
1453*0336b12dSDavid du Colombier {
1454*0336b12dSDavid du Colombier int mask;
1455*0336b12dSDavid du Colombier
1456*0336b12dSDavid du Colombier if(cap != PciCapHTC)
1457*0336b12dSDavid du Colombier return 1;
1458*0336b12dSDavid du Colombier if(arg == 0x00 || arg == 0x20)
1459*0336b12dSDavid du Colombier mask = 0xE0;
1460*0336b12dSDavid du Colombier else
1461*0336b12dSDavid du Colombier mask = 0xF8;
1462*0336b12dSDavid du Colombier cap = pcicfgr8(p, off+3);
1463*0336b12dSDavid du Colombier return (cap & mask) != arg;
1464*0336b12dSDavid du Colombier }
1465*0336b12dSDavid du Colombier
1466*0336b12dSDavid du Colombier int
pcicap(Pcidev * p,int cap)1467*0336b12dSDavid du Colombier pcicap(Pcidev* p, int cap)
1468*0336b12dSDavid du Colombier {
1469*0336b12dSDavid du Colombier return pcienumcaps(p, matchcap, cap);
1470*0336b12dSDavid du Colombier }
1471*0336b12dSDavid du Colombier
1472*0336b12dSDavid du Colombier int
pcihtcap(Pcidev * p,int cap)1473*0336b12dSDavid du Colombier pcihtcap(Pcidev* p, int cap)
1474*0336b12dSDavid du Colombier {
1475*0336b12dSDavid du Colombier return pcienumcaps(p, matchhtcap, cap);
1476*0336b12dSDavid du Colombier }
1477*0336b12dSDavid du Colombier
1478e569ccb5SDavid du Colombier int
pcigetpms(Pcidev * p)1479e569ccb5SDavid du Colombier pcigetpms(Pcidev* p)
1480e569ccb5SDavid du Colombier {
1481e569ccb5SDavid du Colombier int pmcsr, ptr;
1482e569ccb5SDavid du Colombier
1483ee52d9b2SDavid du Colombier if((ptr = pcicap(p, PciCapPMG)) == -1)
1484e569ccb5SDavid du Colombier return -1;
1485e569ccb5SDavid du Colombier
1486e569ccb5SDavid du Colombier /*
1487e569ccb5SDavid du Colombier * Power Management Register Block:
1488e569ccb5SDavid du Colombier * offset 0: Capability ID
1489e569ccb5SDavid du Colombier * 1: next item pointer
1490e569ccb5SDavid du Colombier * 2: capabilities
1491e569ccb5SDavid du Colombier * 4: control/status
1492e569ccb5SDavid du Colombier * 6: bridge support extensions
1493e569ccb5SDavid du Colombier * 7: data
1494e569ccb5SDavid du Colombier */
1495e569ccb5SDavid du Colombier pmcsr = pcicfgr16(p, ptr+4);
1496e569ccb5SDavid du Colombier
1497e569ccb5SDavid du Colombier return pmcsr & 0x0003;
1498e569ccb5SDavid du Colombier }
1499e569ccb5SDavid du Colombier
1500e569ccb5SDavid du Colombier int
pcisetpms(Pcidev * p,int state)1501e569ccb5SDavid du Colombier pcisetpms(Pcidev* p, int state)
1502e569ccb5SDavid du Colombier {
1503e569ccb5SDavid du Colombier int ostate, pmc, pmcsr, ptr;
1504e569ccb5SDavid du Colombier
1505ee52d9b2SDavid du Colombier if((ptr = pcicap(p, PciCapPMG)) == -1)
1506e569ccb5SDavid du Colombier return -1;
1507e569ccb5SDavid du Colombier
1508e569ccb5SDavid du Colombier pmc = pcicfgr16(p, ptr+2);
1509e569ccb5SDavid du Colombier pmcsr = pcicfgr16(p, ptr+4);
1510e569ccb5SDavid du Colombier ostate = pmcsr & 0x0003;
1511e569ccb5SDavid du Colombier pmcsr &= ~0x0003;
1512e569ccb5SDavid du Colombier
1513e569ccb5SDavid du Colombier switch(state){
1514e569ccb5SDavid du Colombier default:
1515e569ccb5SDavid du Colombier return -1;
1516e569ccb5SDavid du Colombier case 0:
1517e569ccb5SDavid du Colombier break;
1518e569ccb5SDavid du Colombier case 1:
1519e569ccb5SDavid du Colombier if(!(pmc & 0x0200))
1520e569ccb5SDavid du Colombier return -1;
1521e569ccb5SDavid du Colombier break;
1522e569ccb5SDavid du Colombier case 2:
1523e569ccb5SDavid du Colombier if(!(pmc & 0x0400))
1524e569ccb5SDavid du Colombier return -1;
1525e569ccb5SDavid du Colombier break;
1526e569ccb5SDavid du Colombier case 3:
1527e569ccb5SDavid du Colombier break;
1528e569ccb5SDavid du Colombier }
1529e569ccb5SDavid du Colombier pmcsr |= state;
1530e569ccb5SDavid du Colombier pcicfgw16(p, ptr+4, pmcsr);
1531e569ccb5SDavid du Colombier
1532e569ccb5SDavid du Colombier return ostate;
15339a747e4fSDavid du Colombier }
1534