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 */
671*28684b1dSDavid 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 */
67641dd6b47SDavid du Colombier { 0x1106, 0x3227, viaget, viaset }, /* Viatech VT8237 */
67741dd6b47SDavid du Colombier { 0x1045, 0xc700, optiget, optiset }, /* Opti 82C700 */
67841dd6b47SDavid du Colombier { 0x10b9, 0x1533, aliget, aliset }, /* Al M1533 */
67941dd6b47SDavid du Colombier { 0x1039, 0x0008, pIIxget, pIIxset }, /* SI 503 */
68041dd6b47SDavid du Colombier { 0x1039, 0x0496, pIIxget, pIIxset }, /* SI 496 */
68141dd6b47SDavid du Colombier { 0x1078, 0x0100, cyrixget, cyrixset }, /* Cyrix 5530 Legacy */
6825000fddeSDavid du Colombier
68341dd6b47SDavid du Colombier { 0x1022, 0x746B, nil, nil }, /* AMD 8111 */
68441dd6b47SDavid du Colombier { 0x10DE, 0x00D1, nil, nil }, /* NVIDIA nForce 3 */
68541ac1ab6SDavid du Colombier { 0x10DE, 0x00E0, nil, nil }, /* NVIDIA nForce 3 250 Series */
686b8a11165SDavid du Colombier { 0x10DE, 0x00E1, nil, nil }, /* NVIDIA nForce 3 250 Series */
68741dd6b47SDavid du Colombier { 0x1166, 0x0200, nil, nil }, /* ServerWorks ServerSet III LE */
68841dd6b47SDavid du Colombier { 0x1002, 0x4377, nil, nil }, /* ATI Radeon Xpress 200M */
68941dd6b47SDavid du Colombier { 0x1002, 0x4372, nil, nil }, /* ATI SB400 */
69059c21d95SDavid du Colombier };
6919a747e4fSDavid du Colombier
69259c21d95SDavid du Colombier typedef struct Slot Slot;
69359c21d95SDavid du Colombier struct Slot {
69441dd6b47SDavid du Colombier uchar bus; /* Pci bus number */
69541dd6b47SDavid du Colombier uchar dev; /* Pci device number */
69641dd6b47SDavid du Colombier uchar maps[12]; /* Avoid structs! Link and mask. */
69741dd6b47SDavid du Colombier uchar slot; /* Add-in/built-in slot */
69859c21d95SDavid du Colombier uchar reserved;
69959c21d95SDavid du Colombier };
70059c21d95SDavid du Colombier
70159c21d95SDavid du Colombier typedef struct Router Router;
70259c21d95SDavid du Colombier struct Router {
70341dd6b47SDavid du Colombier uchar signature[4]; /* Routing table signature */
70441dd6b47SDavid du Colombier uchar version[2]; /* Version number */
70541dd6b47SDavid du Colombier uchar size[2]; /* Total table size */
70641dd6b47SDavid du Colombier uchar bus; /* Interrupt router bus number */
70741dd6b47SDavid du Colombier uchar devfn; /* Router's devfunc */
70841dd6b47SDavid du Colombier uchar pciirqs[2]; /* Exclusive PCI irqs */
70941dd6b47SDavid du Colombier uchar compat[4]; /* Compatible PCI interrupt router */
71041dd6b47SDavid du Colombier uchar miniport[4]; /* Miniport data */
71159c21d95SDavid du Colombier uchar reserved[11];
71259c21d95SDavid du Colombier uchar checksum;
71359c21d95SDavid du Colombier };
7149a747e4fSDavid du Colombier
71541dd6b47SDavid du Colombier static ushort pciirqs; /* Exclusive PCI irqs */
71641dd6b47SDavid du Colombier static Bridge *southbridge; /* Which southbridge to use. */
7179a747e4fSDavid du Colombier
7189a747e4fSDavid du Colombier static void
pcirouting(void)7199a747e4fSDavid du Colombier pcirouting(void)
7209a747e4fSDavid du Colombier {
72159c21d95SDavid du Colombier Slot *e;
72259c21d95SDavid du Colombier Router *r;
7239a747e4fSDavid du Colombier int size, i, fn, tbdf;
7249a747e4fSDavid du Colombier Pcidev *sbpci, *pci;
7255000fddeSDavid du Colombier uchar *p, pin, irq, link, *map;
7269a747e4fSDavid du Colombier
72741dd6b47SDavid du Colombier /* Search for PCI interrupt routing table in BIOS */
7289a747e4fSDavid du Colombier for(p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16)
7299a747e4fSDavid du Colombier if(p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R')
7309a747e4fSDavid du Colombier break;
7319a747e4fSDavid du Colombier
7322210c76eSDavid du Colombier if(p >= (uchar *)KADDR(0xfffff)) {
7333d8d68ddSDavid du Colombier // print("no PCI intr routing table found\n");
7349a747e4fSDavid du Colombier return;
7352210c76eSDavid du Colombier }
7369a747e4fSDavid du Colombier
73759c21d95SDavid du Colombier r = (Router *)p;
7389a747e4fSDavid du Colombier
7392210c76eSDavid du Colombier if (0)
7402210c76eSDavid du Colombier print("PCI interrupt routing table version %d.%d at %#.6luX\n",
7412210c76eSDavid du Colombier r->version[0], r->version[1], (ulong)r & 0xfffff);
7429a747e4fSDavid du Colombier
74359c21d95SDavid du Colombier tbdf = (BusPCI << 24)|(r->bus << 16)|(r->devfn << 8);
7449a747e4fSDavid du Colombier sbpci = pcimatchtbdf(tbdf);
7459a747e4fSDavid du Colombier if(sbpci == nil) {
7469a747e4fSDavid du Colombier print("pcirouting: Cannot find south bridge %T\n", tbdf);
7479a747e4fSDavid du Colombier return;
7489a747e4fSDavid du Colombier }
7499a747e4fSDavid du Colombier
7509a747e4fSDavid du Colombier for(i = 0; i != nelem(southbridges); i++)
7519a747e4fSDavid du Colombier if(sbpci->vid == southbridges[i].vid && sbpci->did == southbridges[i].did)
7529a747e4fSDavid du Colombier break;
7539a747e4fSDavid du Colombier
7549a747e4fSDavid du Colombier if(i == nelem(southbridges)) {
7553ff48bf5SDavid du Colombier print("pcirouting: ignoring south bridge %T %.4uX/%.4uX\n", tbdf, sbpci->vid, sbpci->did);
7569a747e4fSDavid du Colombier return;
7579a747e4fSDavid du Colombier }
7589a747e4fSDavid du Colombier southbridge = &southbridges[i];
7595000fddeSDavid du Colombier if(southbridge->get == nil || southbridge->set == nil)
7605000fddeSDavid du Colombier return;
7619a747e4fSDavid du Colombier
76259c21d95SDavid du Colombier pciirqs = (r->pciirqs[1] << 8)|r->pciirqs[0];
7639a747e4fSDavid du Colombier
76459c21d95SDavid du Colombier size = (r->size[1] << 8)|r->size[0];
76559c21d95SDavid du Colombier for(e = (Slot *)&r[1]; (uchar *)e < p + size; e++) {
76641dd6b47SDavid du Colombier if (0) {
76741dd6b47SDavid du Colombier print("%.2uX/%.2uX %.2uX: ", e->bus, e->dev, e->slot);
76841dd6b47SDavid du Colombier for (i = 0; i != 4; i++) {
76941dd6b47SDavid du Colombier uchar *m = &e->maps[i * 3];
77041dd6b47SDavid du Colombier print("[%d] %.2uX %.4uX ",
77141dd6b47SDavid du Colombier i, m[0], (m[2] << 8)|m[1]);
77241dd6b47SDavid du Colombier }
77341dd6b47SDavid du Colombier print("\n");
77441dd6b47SDavid du Colombier }
7759a747e4fSDavid du Colombier for(fn = 0; fn != 8; fn++) {
77659c21d95SDavid du Colombier tbdf = (BusPCI << 24)|(e->bus << 16)|((e->dev | fn) << 8);
7779a747e4fSDavid du Colombier pci = pcimatchtbdf(tbdf);
7789a747e4fSDavid du Colombier if(pci == nil)
7799a747e4fSDavid du Colombier continue;
7809a747e4fSDavid du Colombier pin = pcicfgr8(pci, PciINTP);
7819a747e4fSDavid du Colombier if(pin == 0 || pin == 0xff)
7829a747e4fSDavid du Colombier continue;
7839a747e4fSDavid du Colombier
7845000fddeSDavid du Colombier map = &e->maps[(pin - 1) * 3];
7855000fddeSDavid du Colombier link = map[0];
7863ff48bf5SDavid du Colombier irq = southbridge->get(sbpci, link);
7879a747e4fSDavid du Colombier if(irq == 0 || irq == pci->intl)
7889a747e4fSDavid du Colombier continue;
7899a747e4fSDavid du Colombier if(pci->intl != 0 && pci->intl != 0xFF) {
7903ff48bf5SDavid du Colombier print("pcirouting: BIOS workaround: %T at pin %d link %d irq %d -> %d\n",
7913ff48bf5SDavid du Colombier tbdf, pin, link, irq, pci->intl);
7923ff48bf5SDavid du Colombier southbridge->set(sbpci, link, pci->intl);
7939a747e4fSDavid du Colombier continue;
7949a747e4fSDavid du Colombier }
7953ff48bf5SDavid du Colombier print("pcirouting: %T at pin %d link %d irq %d\n", tbdf, pin, link, irq);
7969a747e4fSDavid du Colombier pcicfgw8(pci, PciINTL, irq);
7979a747e4fSDavid du Colombier pci->intl = irq;
7989a747e4fSDavid du Colombier }
7999a747e4fSDavid du Colombier }
8009a747e4fSDavid du Colombier }
8019a747e4fSDavid du Colombier
8024de34a7eSDavid du Colombier static void pcireservemem(void);
8034de34a7eSDavid du Colombier
8044fafed5dSDavid du Colombier static int
pcicfgrw8bios(int tbdf,int rno,int data,int read)8054fafed5dSDavid du Colombier pcicfgrw8bios(int tbdf, int rno, int data, int read)
8064fafed5dSDavid du Colombier {
8074fafed5dSDavid du Colombier BIOS32ci ci;
8084fafed5dSDavid du Colombier
8094fafed5dSDavid du Colombier if(pcibiossi == nil)
8104fafed5dSDavid du Colombier return -1;
8114fafed5dSDavid du Colombier
8124fafed5dSDavid du Colombier memset(&ci, 0, sizeof(BIOS32ci));
8134fafed5dSDavid du Colombier ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
8144fafed5dSDavid du Colombier ci.edi = rno;
8154fafed5dSDavid du Colombier if(read){
8164fafed5dSDavid du Colombier ci.eax = 0xB108;
8174fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8184fafed5dSDavid du Colombier return ci.ecx & 0xFF;
8194fafed5dSDavid du Colombier }
8204fafed5dSDavid du Colombier else{
8214fafed5dSDavid du Colombier ci.eax = 0xB10B;
8224fafed5dSDavid du Colombier ci.ecx = data & 0xFF;
8234fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8244fafed5dSDavid du Colombier return 0;
8254fafed5dSDavid du Colombier }
8264fafed5dSDavid du Colombier
8274fafed5dSDavid du Colombier return -1;
8284fafed5dSDavid du Colombier }
8294fafed5dSDavid du Colombier
8304fafed5dSDavid du Colombier static int
pcicfgrw16bios(int tbdf,int rno,int data,int read)8314fafed5dSDavid du Colombier pcicfgrw16bios(int tbdf, int rno, int data, int read)
8324fafed5dSDavid du Colombier {
8334fafed5dSDavid du Colombier BIOS32ci ci;
8344fafed5dSDavid du Colombier
8354fafed5dSDavid du Colombier if(pcibiossi == nil)
8364fafed5dSDavid du Colombier return -1;
8374fafed5dSDavid du Colombier
8384fafed5dSDavid du Colombier memset(&ci, 0, sizeof(BIOS32ci));
8394fafed5dSDavid du Colombier ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
8404fafed5dSDavid du Colombier ci.edi = rno;
8414fafed5dSDavid du Colombier if(read){
8424fafed5dSDavid du Colombier ci.eax = 0xB109;
8434fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8444fafed5dSDavid du Colombier return ci.ecx & 0xFFFF;
8454fafed5dSDavid du Colombier }
8464fafed5dSDavid du Colombier else{
8474fafed5dSDavid du Colombier ci.eax = 0xB10C;
8484fafed5dSDavid du Colombier ci.ecx = data & 0xFFFF;
8494fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8504fafed5dSDavid du Colombier return 0;
8514fafed5dSDavid du Colombier }
8524fafed5dSDavid du Colombier
8534fafed5dSDavid du Colombier return -1;
8544fafed5dSDavid du Colombier }
8554fafed5dSDavid du Colombier
8564fafed5dSDavid du Colombier static int
pcicfgrw32bios(int tbdf,int rno,int data,int read)8574fafed5dSDavid du Colombier pcicfgrw32bios(int tbdf, int rno, int data, int read)
8584fafed5dSDavid du Colombier {
8594fafed5dSDavid du Colombier BIOS32ci ci;
8604fafed5dSDavid du Colombier
8614fafed5dSDavid du Colombier if(pcibiossi == nil)
8624fafed5dSDavid du Colombier return -1;
8634fafed5dSDavid du Colombier
8644fafed5dSDavid du Colombier memset(&ci, 0, sizeof(BIOS32ci));
8654fafed5dSDavid du Colombier ci.ebx = (BUSBNO(tbdf)<<8)|(BUSDNO(tbdf)<<3)|BUSFNO(tbdf);
8664fafed5dSDavid du Colombier ci.edi = rno;
8674fafed5dSDavid du Colombier if(read){
8684fafed5dSDavid du Colombier ci.eax = 0xB10A;
8694fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8704fafed5dSDavid du Colombier return ci.ecx;
8714fafed5dSDavid du Colombier }
8724fafed5dSDavid du Colombier else{
8734fafed5dSDavid du Colombier ci.eax = 0xB10D;
8744fafed5dSDavid du Colombier ci.ecx = data;
8754fafed5dSDavid du Colombier if(!bios32ci(pcibiossi, &ci)/* && !(ci.eax & 0xFF)*/)
8764fafed5dSDavid du Colombier return 0;
8774fafed5dSDavid du Colombier }
8784fafed5dSDavid du Colombier
8794fafed5dSDavid du Colombier return -1;
8804fafed5dSDavid du Colombier }
8814fafed5dSDavid du Colombier
8824fafed5dSDavid du Colombier static BIOS32si*
pcibiosinit(void)8834fafed5dSDavid du Colombier pcibiosinit(void)
8844fafed5dSDavid du Colombier {
8854fafed5dSDavid du Colombier BIOS32ci ci;
8864fafed5dSDavid du Colombier BIOS32si *si;
8874fafed5dSDavid du Colombier
8884fafed5dSDavid du Colombier if((si = bios32open("$PCI")) == nil)
8894fafed5dSDavid du Colombier return nil;
8904fafed5dSDavid du Colombier
8914fafed5dSDavid du Colombier memset(&ci, 0, sizeof(BIOS32ci));
8924fafed5dSDavid du Colombier ci.eax = 0xB101;
8934fafed5dSDavid du Colombier if(bios32ci(si, &ci) || ci.edx != ((' '<<24)|('I'<<16)|('C'<<8)|'P')){
8944fafed5dSDavid du Colombier free(si);
8954fafed5dSDavid du Colombier return nil;
8964fafed5dSDavid du Colombier }
8974fafed5dSDavid du Colombier if(ci.eax & 0x01)
8984fafed5dSDavid du Colombier pcimaxdno = 31;
8994fafed5dSDavid du Colombier else
9004fafed5dSDavid du Colombier pcimaxdno = 15;
9014fafed5dSDavid du Colombier pcimaxbno = ci.ecx & 0xff;
9024fafed5dSDavid du Colombier
9034fafed5dSDavid du Colombier return si;
9044fafed5dSDavid du Colombier }
9054fafed5dSDavid du Colombier
906c91329d7SDavid du Colombier void
pcibussize(Pcidev * root,ulong * msize,ulong * iosize)907c91329d7SDavid du Colombier pcibussize(Pcidev *root, ulong *msize, ulong *iosize)
908c91329d7SDavid du Colombier {
909c91329d7SDavid du Colombier *msize = 0;
910c91329d7SDavid du Colombier *iosize = 0;
911c91329d7SDavid du Colombier pcibusmap(root, msize, iosize, 0);
912c91329d7SDavid du Colombier }
913c91329d7SDavid du Colombier
9147dd7cddfSDavid du Colombier static void
pcicfginit(void)9157dd7cddfSDavid du Colombier pcicfginit(void)
9167dd7cddfSDavid du Colombier {
9177dd7cddfSDavid du Colombier char *p;
9187dd7cddfSDavid du Colombier Pcidev **list;
9197dd7cddfSDavid du Colombier ulong mema, ioa;
920becaf2abSDavid du Colombier int bno, n, pcibios;
9217dd7cddfSDavid du Colombier
9227dd7cddfSDavid du Colombier lock(&pcicfginitlock);
9237dd7cddfSDavid du Colombier if(pcicfgmode != -1)
9247dd7cddfSDavid du Colombier goto out;
9257dd7cddfSDavid du Colombier
926becaf2abSDavid du Colombier pcibios = 0;
92780ee5cbfSDavid du Colombier if(getconf("*nobios"))
92880ee5cbfSDavid du Colombier nobios = 1;
929becaf2abSDavid du Colombier else if(getconf("*pcibios"))
930becaf2abSDavid du Colombier pcibios = 1;
9313ff48bf5SDavid du Colombier if(getconf("*nopcirouting"))
9323ff48bf5SDavid du Colombier nopcirouting = 1;
9333ff48bf5SDavid du Colombier
9347dd7cddfSDavid du Colombier /*
9357dd7cddfSDavid du Colombier * Try to determine which PCI configuration mode is implemented.
9367dd7cddfSDavid du Colombier * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
9377dd7cddfSDavid du Colombier * a DWORD at 0xCF8 and another at 0xCFC and will pass through
9387dd7cddfSDavid du Colombier * any non-DWORD accesses as normal I/O cycles. There shouldn't be
9393ff48bf5SDavid du Colombier * a device behind these addresses so if Mode1 accesses fail try
9403ff48bf5SDavid du Colombier * for Mode2 (Mode2 is deprecated).
9417dd7cddfSDavid du Colombier */
942becaf2abSDavid du Colombier if(!pcibios){
943becaf2abSDavid du Colombier /*
944becaf2abSDavid du Colombier * Bits [30:24] of PciADDR must be 0,
945becaf2abSDavid du Colombier * according to the spec.
946becaf2abSDavid du Colombier */
947becaf2abSDavid du Colombier n = inl(PciADDR);
9486e712d44SDavid du Colombier if(!(n & 0x7F000000)){
949becaf2abSDavid du Colombier outl(PciADDR, 0x80000000);
950becaf2abSDavid du Colombier outb(PciADDR+3, 0);
951becaf2abSDavid du Colombier if(inl(PciADDR) & 0x80000000){
9527dd7cddfSDavid du Colombier pcicfgmode = 1;
9537dd7cddfSDavid du Colombier pcimaxdno = 31;
9547dd7cddfSDavid du Colombier }
955becaf2abSDavid du Colombier }
956becaf2abSDavid du Colombier outl(PciADDR, n);
957becaf2abSDavid du Colombier
958becaf2abSDavid du Colombier if(pcicfgmode < 0){
959becaf2abSDavid du Colombier /*
960becaf2abSDavid du Colombier * The 'key' part of PciCSE should be 0.
961becaf2abSDavid du Colombier */
962becaf2abSDavid du Colombier n = inb(PciCSE);
963becaf2abSDavid du Colombier if(!(n & 0xF0)){
964becaf2abSDavid du Colombier outb(PciCSE, 0x0E);
965becaf2abSDavid du Colombier if(inb(PciCSE) == 0x0E){
9663ff48bf5SDavid du Colombier pcicfgmode = 2;
9673ff48bf5SDavid du Colombier pcimaxdno = 15;
9683ff48bf5SDavid du Colombier }
9697dd7cddfSDavid du Colombier }
970becaf2abSDavid du Colombier outb(PciCSE, n);
971becaf2abSDavid du Colombier }
972becaf2abSDavid du Colombier }
9737dd7cddfSDavid du Colombier
9744fafed5dSDavid du Colombier if(pcicfgmode < 0 || pcibios) {
9754fafed5dSDavid du Colombier if((pcibiossi = pcibiosinit()) == nil)
9767dd7cddfSDavid du Colombier goto out;
9774fafed5dSDavid du Colombier pcicfgrw8 = pcicfgrw8bios;
9784fafed5dSDavid du Colombier pcicfgrw16 = pcicfgrw16bios;
9794fafed5dSDavid du Colombier pcicfgrw32 = pcicfgrw32bios;
9804fafed5dSDavid du Colombier pcicfgmode = 3;
9814fafed5dSDavid du Colombier }
9827dd7cddfSDavid du Colombier
9839a747e4fSDavid du Colombier fmtinstall('T', tbdffmt);
9847dd7cddfSDavid du Colombier
985becaf2abSDavid du Colombier if(p = getconf("*pcimaxbno")){
986becaf2abSDavid du Colombier n = strtoul(p, 0, 0);
987becaf2abSDavid du Colombier if(n < pcimaxbno)
988becaf2abSDavid du Colombier pcimaxbno = n;
989becaf2abSDavid du Colombier }
990becaf2abSDavid du Colombier if(p = getconf("*pcimaxdno")){
991becaf2abSDavid du Colombier n = strtoul(p, 0, 0);
992becaf2abSDavid du Colombier if(n < pcimaxdno)
993becaf2abSDavid du Colombier pcimaxdno = n;
994becaf2abSDavid du Colombier }
9957dd7cddfSDavid du Colombier
9967dd7cddfSDavid du Colombier list = &pciroot;
997223a736eSDavid du Colombier for(bno = 0; bno <= pcimaxbno; bno++) {
9989a747e4fSDavid du Colombier int sbno = bno;
9999a747e4fSDavid du Colombier bno = pcilscan(bno, list);
10009a747e4fSDavid du Colombier
10017dd7cddfSDavid du Colombier while(*list)
10027dd7cddfSDavid du Colombier list = &(*list)->link;
10039a747e4fSDavid du Colombier
10049a747e4fSDavid du Colombier if (sbno == 0) {
10059a747e4fSDavid du Colombier Pcidev *pci;
10069a747e4fSDavid du Colombier
10079a747e4fSDavid du Colombier /*
10089a747e4fSDavid du Colombier * If we have found a PCI-to-Cardbus bridge, make sure
10099a747e4fSDavid du Colombier * it has no valid mappings anymore.
10109a747e4fSDavid du Colombier */
1011c91329d7SDavid du Colombier for(pci = pciroot; pci != nil; pci = pci->link){
10129a747e4fSDavid du Colombier if (pci->ccrb == 6 && pci->ccru == 7) {
10139a747e4fSDavid du Colombier ushort bcr;
10149a747e4fSDavid du Colombier
10159a747e4fSDavid du Colombier /* reset the cardbus */
10169a747e4fSDavid du Colombier bcr = pcicfgr16(pci, PciBCR);
10179a747e4fSDavid du Colombier pcicfgw16(pci, PciBCR, 0x40 | bcr);
10189a747e4fSDavid du Colombier delay(50);
10199a747e4fSDavid du Colombier }
10209a747e4fSDavid du Colombier }
10219a747e4fSDavid du Colombier }
10227dd7cddfSDavid du Colombier }
10237dd7cddfSDavid du Colombier
10247dd7cddfSDavid du Colombier if(pciroot == nil)
10257dd7cddfSDavid du Colombier goto out;
10267dd7cddfSDavid du Colombier
102780ee5cbfSDavid du Colombier if(nobios) {
10287dd7cddfSDavid du Colombier /*
10297dd7cddfSDavid du Colombier * Work out how big the top bus is
10307dd7cddfSDavid du Colombier */
1031c91329d7SDavid du Colombier pcibussize(pciroot, &mema, &ioa);
10327dd7cddfSDavid du Colombier
10337dd7cddfSDavid du Colombier /*
10347dd7cddfSDavid du Colombier * Align the windows and map it
10357dd7cddfSDavid du Colombier */
10367dd7cddfSDavid du Colombier ioa = 0x1000;
10377dd7cddfSDavid du Colombier mema = 0x90000000;
10387dd7cddfSDavid du Colombier
10397dd7cddfSDavid du Colombier pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
10407dd7cddfSDavid du Colombier
10417dd7cddfSDavid du Colombier pcibusmap(pciroot, &mema, &ioa, 1);
10427dd7cddfSDavid du Colombier DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
10437dd7cddfSDavid du Colombier
10447dd7cddfSDavid du Colombier unlock(&pcicfginitlock);
10457dd7cddfSDavid du Colombier return;
10467dd7cddfSDavid du Colombier }
10479a747e4fSDavid du Colombier
10483ff48bf5SDavid du Colombier if (!nopcirouting)
10499a747e4fSDavid du Colombier pcirouting();
10509a747e4fSDavid du Colombier
10517dd7cddfSDavid du Colombier out:
10524de34a7eSDavid du Colombier pcireservemem();
10537dd7cddfSDavid du Colombier unlock(&pcicfginitlock);
10549a747e4fSDavid du Colombier
10553ff48bf5SDavid du Colombier if(getconf("*pcihinv"))
10563ff48bf5SDavid du Colombier pcihinv(nil);
10577dd7cddfSDavid du Colombier }
10587dd7cddfSDavid du Colombier
10594de34a7eSDavid du Colombier static void
pcireservemem(void)10604de34a7eSDavid du Colombier pcireservemem(void)
10614de34a7eSDavid du Colombier {
10624de34a7eSDavid du Colombier int i;
10634de34a7eSDavid du Colombier Pcidev *p;
10644de34a7eSDavid du Colombier
10654de34a7eSDavid du Colombier /*
10664de34a7eSDavid du Colombier * mark all the physical address space claimed by pci devices
10674de34a7eSDavid du Colombier * as in use, so that upaalloc doesn't give it out.
10684de34a7eSDavid du Colombier */
10694de34a7eSDavid du Colombier for(p=pciroot; p; p=p->list)
10704de34a7eSDavid du Colombier for(i=0; i<nelem(p->mem); i++)
10714de34a7eSDavid du Colombier if(p->mem[i].bar && (p->mem[i].bar&1) == 0)
10724de34a7eSDavid du Colombier upareserve(p->mem[i].bar&~0x0F, p->mem[i].size);
10734de34a7eSDavid du Colombier }
10744de34a7eSDavid du Colombier
10757dd7cddfSDavid du Colombier static int
pcicfgrw8raw(int tbdf,int rno,int data,int read)10764fafed5dSDavid du Colombier pcicfgrw8raw(int tbdf, int rno, int data, int read)
10777dd7cddfSDavid du Colombier {
10787dd7cddfSDavid du Colombier int o, type, x;
10797dd7cddfSDavid du Colombier
10807dd7cddfSDavid du Colombier if(pcicfgmode == -1)
10817dd7cddfSDavid du Colombier pcicfginit();
10827dd7cddfSDavid du Colombier
10837dd7cddfSDavid du Colombier if(BUSBNO(tbdf))
10847dd7cddfSDavid du Colombier type = 0x01;
10857dd7cddfSDavid du Colombier else
10867dd7cddfSDavid du Colombier type = 0x00;
10877dd7cddfSDavid du Colombier x = -1;
10887dd7cddfSDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
10897dd7cddfSDavid du Colombier return x;
10907dd7cddfSDavid du Colombier
10917dd7cddfSDavid du Colombier lock(&pcicfglock);
10927dd7cddfSDavid du Colombier switch(pcicfgmode){
10937dd7cddfSDavid du Colombier
10947dd7cddfSDavid du Colombier case 1:
10957dd7cddfSDavid du Colombier o = rno & 0x03;
10967dd7cddfSDavid du Colombier rno &= ~0x03;
10977dd7cddfSDavid du Colombier outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
10987dd7cddfSDavid du Colombier if(read)
10997dd7cddfSDavid du Colombier x = inb(PciDATA+o);
11007dd7cddfSDavid du Colombier else
11017dd7cddfSDavid du Colombier outb(PciDATA+o, data);
11027dd7cddfSDavid du Colombier outl(PciADDR, 0);
11037dd7cddfSDavid du Colombier break;
11047dd7cddfSDavid du Colombier
11057dd7cddfSDavid du Colombier case 2:
11067dd7cddfSDavid du Colombier outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
11077dd7cddfSDavid du Colombier outb(PciFORWARD, BUSBNO(tbdf));
11087dd7cddfSDavid du Colombier if(read)
11097dd7cddfSDavid du Colombier x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
11107dd7cddfSDavid du Colombier else
11117dd7cddfSDavid du Colombier outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
11127dd7cddfSDavid du Colombier outb(PciCSE, 0);
11137dd7cddfSDavid du Colombier break;
11147dd7cddfSDavid du Colombier }
11157dd7cddfSDavid du Colombier unlock(&pcicfglock);
11167dd7cddfSDavid du Colombier
11177dd7cddfSDavid du Colombier return x;
11187dd7cddfSDavid du Colombier }
11197dd7cddfSDavid du Colombier
11207dd7cddfSDavid du Colombier int
pcicfgr8(Pcidev * pcidev,int rno)11217dd7cddfSDavid du Colombier pcicfgr8(Pcidev* pcidev, int rno)
11227dd7cddfSDavid du Colombier {
11237dd7cddfSDavid du Colombier return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
11247dd7cddfSDavid du Colombier }
11257dd7cddfSDavid du Colombier
11267dd7cddfSDavid du Colombier void
pcicfgw8(Pcidev * pcidev,int rno,int data)11277dd7cddfSDavid du Colombier pcicfgw8(Pcidev* pcidev, int rno, int data)
11287dd7cddfSDavid du Colombier {
11297dd7cddfSDavid du Colombier pcicfgrw8(pcidev->tbdf, rno, data, 0);
11307dd7cddfSDavid du Colombier }
11317dd7cddfSDavid du Colombier
11327dd7cddfSDavid du Colombier static int
pcicfgrw16raw(int tbdf,int rno,int data,int read)11334fafed5dSDavid du Colombier pcicfgrw16raw(int tbdf, int rno, int data, int read)
11347dd7cddfSDavid du Colombier {
11357dd7cddfSDavid du Colombier int o, type, x;
11367dd7cddfSDavid du Colombier
11377dd7cddfSDavid du Colombier if(pcicfgmode == -1)
11387dd7cddfSDavid du Colombier pcicfginit();
11397dd7cddfSDavid du Colombier
11407dd7cddfSDavid du Colombier if(BUSBNO(tbdf))
11417dd7cddfSDavid du Colombier type = 0x01;
11427dd7cddfSDavid du Colombier else
11437dd7cddfSDavid du Colombier type = 0x00;
11447dd7cddfSDavid du Colombier x = -1;
11457dd7cddfSDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
11467dd7cddfSDavid du Colombier return x;
11477dd7cddfSDavid du Colombier
11487dd7cddfSDavid du Colombier lock(&pcicfglock);
11497dd7cddfSDavid du Colombier switch(pcicfgmode){
11507dd7cddfSDavid du Colombier
11517dd7cddfSDavid du Colombier case 1:
11527dd7cddfSDavid du Colombier o = rno & 0x02;
11537dd7cddfSDavid du Colombier rno &= ~0x03;
11547dd7cddfSDavid du Colombier outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
11557dd7cddfSDavid du Colombier if(read)
11567dd7cddfSDavid du Colombier x = ins(PciDATA+o);
11577dd7cddfSDavid du Colombier else
11587dd7cddfSDavid du Colombier outs(PciDATA+o, data);
11597dd7cddfSDavid du Colombier outl(PciADDR, 0);
11607dd7cddfSDavid du Colombier break;
11617dd7cddfSDavid du Colombier
11627dd7cddfSDavid du Colombier case 2:
11637dd7cddfSDavid du Colombier outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
11647dd7cddfSDavid du Colombier outb(PciFORWARD, BUSBNO(tbdf));
11657dd7cddfSDavid du Colombier if(read)
11667dd7cddfSDavid du Colombier x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
11677dd7cddfSDavid du Colombier else
11687dd7cddfSDavid du Colombier outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
11697dd7cddfSDavid du Colombier outb(PciCSE, 0);
11707dd7cddfSDavid du Colombier break;
11717dd7cddfSDavid du Colombier }
11727dd7cddfSDavid du Colombier unlock(&pcicfglock);
11737dd7cddfSDavid du Colombier
11747dd7cddfSDavid du Colombier return x;
11757dd7cddfSDavid du Colombier }
11767dd7cddfSDavid du Colombier
11777dd7cddfSDavid du Colombier int
pcicfgr16(Pcidev * pcidev,int rno)11787dd7cddfSDavid du Colombier pcicfgr16(Pcidev* pcidev, int rno)
11797dd7cddfSDavid du Colombier {
11807dd7cddfSDavid du Colombier return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
11817dd7cddfSDavid du Colombier }
11827dd7cddfSDavid du Colombier
11837dd7cddfSDavid du Colombier void
pcicfgw16(Pcidev * pcidev,int rno,int data)11847dd7cddfSDavid du Colombier pcicfgw16(Pcidev* pcidev, int rno, int data)
11857dd7cddfSDavid du Colombier {
11867dd7cddfSDavid du Colombier pcicfgrw16(pcidev->tbdf, rno, data, 0);
11877dd7cddfSDavid du Colombier }
11887dd7cddfSDavid du Colombier
11897dd7cddfSDavid du Colombier static int
pcicfgrw32raw(int tbdf,int rno,int data,int read)11904fafed5dSDavid du Colombier pcicfgrw32raw(int tbdf, int rno, int data, int read)
11917dd7cddfSDavid du Colombier {
11927dd7cddfSDavid du Colombier int type, x;
11937dd7cddfSDavid du Colombier
11947dd7cddfSDavid du Colombier if(pcicfgmode == -1)
11957dd7cddfSDavid du Colombier pcicfginit();
11967dd7cddfSDavid du Colombier
11977dd7cddfSDavid du Colombier if(BUSBNO(tbdf))
11987dd7cddfSDavid du Colombier type = 0x01;
11997dd7cddfSDavid du Colombier else
12007dd7cddfSDavid du Colombier type = 0x00;
12017dd7cddfSDavid du Colombier x = -1;
12027dd7cddfSDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
12037dd7cddfSDavid du Colombier return x;
12047dd7cddfSDavid du Colombier
12057dd7cddfSDavid du Colombier lock(&pcicfglock);
12067dd7cddfSDavid du Colombier switch(pcicfgmode){
12077dd7cddfSDavid du Colombier
12087dd7cddfSDavid du Colombier case 1:
12097dd7cddfSDavid du Colombier rno &= ~0x03;
12107dd7cddfSDavid du Colombier outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
12117dd7cddfSDavid du Colombier if(read)
12127dd7cddfSDavid du Colombier x = inl(PciDATA);
12137dd7cddfSDavid du Colombier else
12147dd7cddfSDavid du Colombier outl(PciDATA, data);
12157dd7cddfSDavid du Colombier outl(PciADDR, 0);
12167dd7cddfSDavid du Colombier break;
12177dd7cddfSDavid du Colombier
12187dd7cddfSDavid du Colombier case 2:
12197dd7cddfSDavid du Colombier outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
12207dd7cddfSDavid du Colombier outb(PciFORWARD, BUSBNO(tbdf));
12217dd7cddfSDavid du Colombier if(read)
12227dd7cddfSDavid du Colombier x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
12237dd7cddfSDavid du Colombier else
12247dd7cddfSDavid du Colombier outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
12257dd7cddfSDavid du Colombier outb(PciCSE, 0);
12267dd7cddfSDavid du Colombier break;
12277dd7cddfSDavid du Colombier }
12287dd7cddfSDavid du Colombier unlock(&pcicfglock);
12297dd7cddfSDavid du Colombier
12307dd7cddfSDavid du Colombier return x;
12317dd7cddfSDavid du Colombier }
12327dd7cddfSDavid du Colombier
12337dd7cddfSDavid du Colombier int
pcicfgr32(Pcidev * pcidev,int rno)12347dd7cddfSDavid du Colombier pcicfgr32(Pcidev* pcidev, int rno)
12357dd7cddfSDavid du Colombier {
12367dd7cddfSDavid du Colombier return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
12377dd7cddfSDavid du Colombier }
12387dd7cddfSDavid du Colombier
12397dd7cddfSDavid du Colombier void
pcicfgw32(Pcidev * pcidev,int rno,int data)12407dd7cddfSDavid du Colombier pcicfgw32(Pcidev* pcidev, int rno, int data)
12417dd7cddfSDavid du Colombier {
12427dd7cddfSDavid du Colombier pcicfgrw32(pcidev->tbdf, rno, data, 0);
12437dd7cddfSDavid du Colombier }
12447dd7cddfSDavid du Colombier
12457dd7cddfSDavid du Colombier Pcidev*
pcimatch(Pcidev * prev,int vid,int did)12467dd7cddfSDavid du Colombier pcimatch(Pcidev* prev, int vid, int did)
12477dd7cddfSDavid du Colombier {
12487dd7cddfSDavid du Colombier if(pcicfgmode == -1)
12497dd7cddfSDavid du Colombier pcicfginit();
12507dd7cddfSDavid du Colombier
12517dd7cddfSDavid du Colombier if(prev == nil)
12527dd7cddfSDavid du Colombier prev = pcilist;
12537dd7cddfSDavid du Colombier else
12547dd7cddfSDavid du Colombier prev = prev->list;
12557dd7cddfSDavid du Colombier
12567dd7cddfSDavid du Colombier while(prev != nil){
12577dd7cddfSDavid du Colombier if((vid == 0 || prev->vid == vid)
12587dd7cddfSDavid du Colombier && (did == 0 || prev->did == did))
12597dd7cddfSDavid du Colombier break;
12607dd7cddfSDavid du Colombier prev = prev->list;
12617dd7cddfSDavid du Colombier }
12627dd7cddfSDavid du Colombier return prev;
12637dd7cddfSDavid du Colombier }
12647dd7cddfSDavid du Colombier
12657dd7cddfSDavid du Colombier Pcidev*
pcimatchtbdf(int tbdf)12667dd7cddfSDavid du Colombier pcimatchtbdf(int tbdf)
12677dd7cddfSDavid du Colombier {
12687dd7cddfSDavid du Colombier Pcidev *pcidev;
12697dd7cddfSDavid du Colombier
12707dd7cddfSDavid du Colombier if(pcicfgmode == -1)
12717dd7cddfSDavid du Colombier pcicfginit();
12727dd7cddfSDavid du Colombier
12737dd7cddfSDavid du Colombier for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
12747dd7cddfSDavid du Colombier if(pcidev->tbdf == tbdf)
12757dd7cddfSDavid du Colombier break;
12767dd7cddfSDavid du Colombier }
12777dd7cddfSDavid du Colombier return pcidev;
12787dd7cddfSDavid du Colombier }
12797dd7cddfSDavid du Colombier
12809a747e4fSDavid du Colombier uchar
pciipin(Pcidev * pci,uchar pin)12819a747e4fSDavid du Colombier pciipin(Pcidev *pci, uchar pin)
12829a747e4fSDavid du Colombier {
12839a747e4fSDavid du Colombier if (pci == nil)
12849a747e4fSDavid du Colombier pci = pcilist;
12859a747e4fSDavid du Colombier
12869a747e4fSDavid du Colombier while (pci) {
12879a747e4fSDavid du Colombier uchar intl;
12889a747e4fSDavid du Colombier
12899a747e4fSDavid du Colombier if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
12909a747e4fSDavid du Colombier return pci->intl;
12919a747e4fSDavid du Colombier
12929a747e4fSDavid du Colombier if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
12939a747e4fSDavid du Colombier return intl;
12949a747e4fSDavid du Colombier
12959a747e4fSDavid du Colombier pci = pci->list;
12969a747e4fSDavid du Colombier }
12979a747e4fSDavid du Colombier return 0;
12989a747e4fSDavid du Colombier }
12999a747e4fSDavid du Colombier
13009a747e4fSDavid du Colombier static void
pcilhinv(Pcidev * p)13019a747e4fSDavid du Colombier pcilhinv(Pcidev* p)
13027dd7cddfSDavid du Colombier {
13037dd7cddfSDavid du Colombier int i;
13047dd7cddfSDavid du Colombier Pcidev *t;
13057dd7cddfSDavid du Colombier
13067dd7cddfSDavid du Colombier if(p == nil) {
13077dd7cddfSDavid du Colombier putstrn(PCICONS.output, PCICONS.ptr);
13087dd7cddfSDavid du Colombier p = pciroot;
13097dd7cddfSDavid du Colombier print("bus dev type vid did intl memory\n");
13107dd7cddfSDavid du Colombier }
13117dd7cddfSDavid du Colombier for(t = p; t != nil; t = t->link) {
13127dd7cddfSDavid du Colombier print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
13137dd7cddfSDavid du Colombier BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
13147dd7cddfSDavid du Colombier t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
13157dd7cddfSDavid du Colombier
13167dd7cddfSDavid du Colombier for(i = 0; i < nelem(p->mem); i++) {
13177dd7cddfSDavid du Colombier if(t->mem[i].size == 0)
13187dd7cddfSDavid du Colombier continue;
13197dd7cddfSDavid du Colombier print("%d:%.8lux %d ", i,
13207dd7cddfSDavid du Colombier t->mem[i].bar, t->mem[i].size);
13217dd7cddfSDavid du Colombier }
13227dd7cddfSDavid du Colombier if(t->ioa.bar || t->ioa.size)
13237dd7cddfSDavid du Colombier print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
13247dd7cddfSDavid du Colombier if(t->mema.bar || t->mema.size)
13257dd7cddfSDavid du Colombier print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
13267dd7cddfSDavid du Colombier if(t->bridge)
13277dd7cddfSDavid du Colombier print("->%d", BUSBNO(t->bridge->tbdf));
13287dd7cddfSDavid du Colombier print("\n");
13297dd7cddfSDavid du Colombier }
13307dd7cddfSDavid du Colombier while(p != nil) {
13317dd7cddfSDavid du Colombier if(p->bridge != nil)
13329a747e4fSDavid du Colombier pcilhinv(p->bridge);
13337dd7cddfSDavid du Colombier p = p->link;
13347dd7cddfSDavid du Colombier }
13357dd7cddfSDavid du Colombier }
13367dd7cddfSDavid du Colombier
13377dd7cddfSDavid du Colombier void
pcihinv(Pcidev * p)13389a747e4fSDavid du Colombier pcihinv(Pcidev* p)
13399a747e4fSDavid du Colombier {
13409a747e4fSDavid du Colombier if(pcicfgmode == -1)
13419a747e4fSDavid du Colombier pcicfginit();
13429a747e4fSDavid du Colombier lock(&pcicfginitlock);
13439a747e4fSDavid du Colombier pcilhinv(p);
13449a747e4fSDavid du Colombier unlock(&pcicfginitlock);
13459a747e4fSDavid du Colombier }
13469a747e4fSDavid du Colombier
13479a747e4fSDavid du Colombier void
pcireset(void)13487dd7cddfSDavid du Colombier pcireset(void)
13497dd7cddfSDavid du Colombier {
13507dd7cddfSDavid du Colombier Pcidev *p;
13517dd7cddfSDavid du Colombier
13527dd7cddfSDavid du Colombier if(pcicfgmode == -1)
13537dd7cddfSDavid du Colombier pcicfginit();
13547dd7cddfSDavid du Colombier
13557dd7cddfSDavid du Colombier for(p = pcilist; p != nil; p = p->list) {
13569a747e4fSDavid du Colombier /* don't mess with the bridges */
13579a747e4fSDavid du Colombier if(p->ccrb == 0x06)
13589a747e4fSDavid du Colombier continue;
13599a747e4fSDavid du Colombier pciclrbme(p);
13607dd7cddfSDavid du Colombier }
13617dd7cddfSDavid du Colombier }
13627dd7cddfSDavid du Colombier
13637dd7cddfSDavid du Colombier void
pcisetioe(Pcidev * p)1364220e960cSDavid du Colombier pcisetioe(Pcidev* p)
1365220e960cSDavid du Colombier {
1366e569ccb5SDavid du Colombier p->pcr |= IOen;
1367e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1368220e960cSDavid du Colombier }
1369220e960cSDavid du Colombier
1370220e960cSDavid du Colombier void
pciclrioe(Pcidev * p)1371220e960cSDavid du Colombier pciclrioe(Pcidev* p)
1372220e960cSDavid du Colombier {
1373e569ccb5SDavid du Colombier p->pcr &= ~IOen;
1374e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1375220e960cSDavid du Colombier }
1376220e960cSDavid du Colombier
1377220e960cSDavid du Colombier void
pcisetbme(Pcidev * p)13787dd7cddfSDavid du Colombier pcisetbme(Pcidev* p)
13797dd7cddfSDavid du Colombier {
1380e569ccb5SDavid du Colombier p->pcr |= MASen;
1381e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
13827dd7cddfSDavid du Colombier }
13839a747e4fSDavid du Colombier
13849a747e4fSDavid du Colombier void
pciclrbme(Pcidev * p)13859a747e4fSDavid du Colombier pciclrbme(Pcidev* p)
13869a747e4fSDavid du Colombier {
1387e569ccb5SDavid du Colombier p->pcr &= ~MASen;
1388e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1389e569ccb5SDavid du Colombier }
13909a747e4fSDavid du Colombier
1391d8f3f65eSDavid du Colombier void
pcisetmwi(Pcidev * p)1392d8f3f65eSDavid du Colombier pcisetmwi(Pcidev* p)
1393d8f3f65eSDavid du Colombier {
1394d8f3f65eSDavid du Colombier p->pcr |= MemWrInv;
1395d8f3f65eSDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1396d8f3f65eSDavid du Colombier }
1397d8f3f65eSDavid du Colombier
1398d8f3f65eSDavid du Colombier void
pciclrmwi(Pcidev * p)1399d8f3f65eSDavid du Colombier pciclrmwi(Pcidev* p)
1400d8f3f65eSDavid du Colombier {
1401d8f3f65eSDavid du Colombier p->pcr &= ~MemWrInv;
1402d8f3f65eSDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
1403d8f3f65eSDavid du Colombier }
1404d8f3f65eSDavid du Colombier
1405e569ccb5SDavid du Colombier static int
pcigetpmrb(Pcidev * p)1406e569ccb5SDavid du Colombier pcigetpmrb(Pcidev* p)
1407e569ccb5SDavid du Colombier {
1408e569ccb5SDavid du Colombier int ptr;
1409e569ccb5SDavid du Colombier
1410e569ccb5SDavid du Colombier if(p->pmrb != 0)
1411e569ccb5SDavid du Colombier return p->pmrb;
1412e569ccb5SDavid du Colombier p->pmrb = -1;
1413e569ccb5SDavid du Colombier
1414e569ccb5SDavid du Colombier /*
1415e569ccb5SDavid du Colombier * If there are no extended capabilities implemented,
1416e569ccb5SDavid du Colombier * (bit 4 in the status register) assume there's no standard
1417e569ccb5SDavid du Colombier * power management method.
1418e569ccb5SDavid du Colombier * Find the capabilities pointer based on PCI header type.
1419e569ccb5SDavid du Colombier */
14205979f962SDavid du Colombier if(!(pcicfgr16(p, PciPSR) & 0x0010))
1421e569ccb5SDavid du Colombier return -1;
1422e569ccb5SDavid du Colombier switch(pcicfgr8(p, PciHDT)){
1423e569ccb5SDavid du Colombier default:
1424e569ccb5SDavid du Colombier return -1;
1425e569ccb5SDavid du Colombier case 0: /* all other */
1426e569ccb5SDavid du Colombier case 1: /* PCI to PCI bridge */
1427e569ccb5SDavid du Colombier ptr = 0x34;
1428e569ccb5SDavid du Colombier break;
1429e569ccb5SDavid du Colombier case 2: /* CardBus bridge */
1430e569ccb5SDavid du Colombier ptr = 0x14;
1431e569ccb5SDavid du Colombier break;
1432e569ccb5SDavid du Colombier }
1433e569ccb5SDavid du Colombier ptr = pcicfgr32(p, ptr);
1434e569ccb5SDavid du Colombier
1435e569ccb5SDavid du Colombier while(ptr != 0){
1436e569ccb5SDavid du Colombier /*
1437e569ccb5SDavid du Colombier * Check for validity.
1438e569ccb5SDavid du Colombier * Can't be in standard header and must be double
1439e569ccb5SDavid du Colombier * word aligned.
1440e569ccb5SDavid du Colombier */
1441e569ccb5SDavid du Colombier if(ptr < 0x40 || (ptr & ~0xFC))
1442e569ccb5SDavid du Colombier return -1;
1443e569ccb5SDavid du Colombier if(pcicfgr8(p, ptr) == 0x01){
1444e569ccb5SDavid du Colombier p->pmrb = ptr;
1445e569ccb5SDavid du Colombier return ptr;
1446e569ccb5SDavid du Colombier }
1447e569ccb5SDavid du Colombier
1448e569ccb5SDavid du Colombier ptr = pcicfgr8(p, ptr+1);
1449e569ccb5SDavid du Colombier }
1450e569ccb5SDavid du Colombier
1451e569ccb5SDavid du Colombier return -1;
1452e569ccb5SDavid du Colombier }
1453e569ccb5SDavid du Colombier
1454e569ccb5SDavid du Colombier int
pcigetpms(Pcidev * p)1455e569ccb5SDavid du Colombier pcigetpms(Pcidev* p)
1456e569ccb5SDavid du Colombier {
1457e569ccb5SDavid du Colombier int pmcsr, ptr;
1458e569ccb5SDavid du Colombier
1459e569ccb5SDavid du Colombier if((ptr = pcigetpmrb(p)) == -1)
1460e569ccb5SDavid du Colombier return -1;
1461e569ccb5SDavid du Colombier
1462e569ccb5SDavid du Colombier /*
1463e569ccb5SDavid du Colombier * Power Management Register Block:
1464e569ccb5SDavid du Colombier * offset 0: Capability ID
1465e569ccb5SDavid du Colombier * 1: next item pointer
1466e569ccb5SDavid du Colombier * 2: capabilities
1467e569ccb5SDavid du Colombier * 4: control/status
1468e569ccb5SDavid du Colombier * 6: bridge support extensions
1469e569ccb5SDavid du Colombier * 7: data
1470e569ccb5SDavid du Colombier */
1471e569ccb5SDavid du Colombier pmcsr = pcicfgr16(p, ptr+4);
1472e569ccb5SDavid du Colombier
1473e569ccb5SDavid du Colombier return pmcsr & 0x0003;
1474e569ccb5SDavid du Colombier }
1475e569ccb5SDavid du Colombier
1476e569ccb5SDavid du Colombier int
pcisetpms(Pcidev * p,int state)1477e569ccb5SDavid du Colombier pcisetpms(Pcidev* p, int state)
1478e569ccb5SDavid du Colombier {
1479e569ccb5SDavid du Colombier int ostate, pmc, pmcsr, ptr;
1480e569ccb5SDavid du Colombier
1481e569ccb5SDavid du Colombier if((ptr = pcigetpmrb(p)) == -1)
1482e569ccb5SDavid du Colombier return -1;
1483e569ccb5SDavid du Colombier
1484e569ccb5SDavid du Colombier pmc = pcicfgr16(p, ptr+2);
1485e569ccb5SDavid du Colombier pmcsr = pcicfgr16(p, ptr+4);
1486e569ccb5SDavid du Colombier ostate = pmcsr & 0x0003;
1487e569ccb5SDavid du Colombier pmcsr &= ~0x0003;
1488e569ccb5SDavid du Colombier
1489e569ccb5SDavid du Colombier switch(state){
1490e569ccb5SDavid du Colombier default:
1491e569ccb5SDavid du Colombier return -1;
1492e569ccb5SDavid du Colombier case 0:
1493e569ccb5SDavid du Colombier break;
1494e569ccb5SDavid du Colombier case 1:
1495e569ccb5SDavid du Colombier if(!(pmc & 0x0200))
1496e569ccb5SDavid du Colombier return -1;
1497e569ccb5SDavid du Colombier break;
1498e569ccb5SDavid du Colombier case 2:
1499e569ccb5SDavid du Colombier if(!(pmc & 0x0400))
1500e569ccb5SDavid du Colombier return -1;
1501e569ccb5SDavid du Colombier break;
1502e569ccb5SDavid du Colombier case 3:
1503e569ccb5SDavid du Colombier break;
1504e569ccb5SDavid du Colombier }
1505e569ccb5SDavid du Colombier pmcsr |= state;
1506e569ccb5SDavid du Colombier pcicfgw16(p, ptr+4, pmcsr);
1507e569ccb5SDavid du Colombier
1508e569ccb5SDavid du Colombier return ostate;
15099a747e4fSDavid du Colombier }
1510