]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/pc/archacpi.c
pc, pc64: implement acpi reset (for efi)
[plan9front.git] / sys / src / 9 / pc / archacpi.c
index 24b3245bca1dad780fc8499cbec935cadc90a572..a9482fc83a0d8fae0fe2fd77c80b80accf4bdce3 100644 (file)
@@ -51,21 +51,6 @@ static uintptr tblpa[64];
 static int ntblmap;
 static Tbl *tblmap[64];
 
-void*
-amlalloc(int n){
-       void *p;
-
-       if((p = malloc(n)) == nil)
-               panic("amlalloc: no memory");
-       memset(p, 0, n);
-       return p;
-}
-
-void
-amlfree(void *p){
-       free(p);
-}
-
 static ushort
 get16(uchar *p){
        return p[1]<<8 | p[0];
@@ -269,20 +254,17 @@ pcibusno(void *dot)
        char *id;
 
        id = nil;
-       if(x = amlwalk(dot, "^_HID")){
-               p = nil;
-               if(amleval(x, "", &p) == 0)
+       if((x = amlwalk(dot, "^_HID")) != nil)
+               if((p = amlval(x)) != nil)
                        id = eisaid(p);
-       }
        if((x = amlwalk(dot, "^_BBN")) == nil)
                if((x = amlwalk(dot, "^_ADR")) == nil)
                        return -1;
-       p = nil;
-       if(amleval(x, "", &p) < 0)
+       if((p = amlval(x)) == nil)
                return -1;
        adr = amlint(p);
        /* if root bridge, then we are done here */
-       if(id && (strcmp(id, "PNP0A03")==0 || strcmp(id, "PNP0A08")==0))
+       if(id != nil && (strcmp(id, "PNP0A03")==0 || strcmp(id, "PNP0A08")==0))
                return adr;
        x = amlwalk(dot, "^");
        if(x == nil || x == dot)
@@ -291,16 +273,150 @@ pcibusno(void *dot)
                return -1;
        tbdf = MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF);
        pdev = pcimatchtbdf(tbdf);
-       if(pdev == nil || pdev->bridge == nil)
+       if(pdev == nil)
                return -1;
+       if(pdev->bridge == nil)
+               return bno;
        return BUSBNO(pdev->bridge->tbdf);
 }
 
+static int
+pciaddr(void *dot)
+{
+       int adr, bno;
+       void *x;
+
+       for(;;){
+               if((x = amlwalk(dot, "_ADR")) == nil){
+                       x = amlwalk(dot, "^");
+                       if(x == nil || x == dot)
+                               break;
+                       dot = x;
+                       continue;
+               }
+               if((bno = pcibusno(x)) < 0)
+                       break;
+               if((x = amlval(x)) == nil)
+                       break;
+               adr = amlint(x);
+               return MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF);
+       }
+       return -1;
+}
+
+static int
+getirqs(void *d, uchar pmask[32], int *pflags)
+{
+       int i, n, m;
+       uchar *p;
+
+       *pflags = 0;
+       memset(pmask, 0, 32);
+       if(amltag(d) != 'b')
+               return -1;
+       p = amlval(d);
+       if(amllen(d) >= 2 && (p[0] == 0x22 || p[0] == 0x23)){
+               pmask[0] = p[1];
+               pmask[1] = p[2];
+               if(amllen(d) >= 3 && p[0] == 0x23)
+                       *pflags = ((p[3] & (1<<0)) ? PcmpEDGE : PcmpLEVEL)
+                               | ((p[3] & (1<<3)) ? PcmpLOW : PcmpHIGH);
+               return 0;
+       }
+       if(amllen(d) >= 5 && p[0] == 0x89){
+               n = p[4];
+               if(amllen(d) < 5+n*4)
+                       return -1;
+               for(i=0; i<n; i++){
+                       m = get32(p+5 + i*4);
+                       if(m >= 0 && m < 256)
+                               pmask[m/8] |= 1<<(m%8);
+               }
+               *pflags = ((p[3] & (1<<1)) ? PcmpEDGE : PcmpLEVEL)
+                       | ((p[3] & (1<<2)) ? PcmpLOW : PcmpHIGH);
+               return 0;
+       }
+       return -1;
+}
+
+static uchar*
+setirq(void *d, uint irq)
+{
+       uchar *p;
+
+       if(amltag(d) != 'b')
+               return nil;
+       p = amlnew('b', amllen(d));
+       memmove(p, d, amllen(p));
+       if(p[0] == 0x22 || p[0] == 0x23){
+               irq = 1<<irq;
+               p[1] = irq;
+               p[2] = irq>>8;
+       }
+       if(p[0] == 0x89){
+               p[4] = 1;
+               p[5] = irq;
+               p[6] = irq>>8;
+               p[7] = irq>>16;
+               p[8] = irq>>24;
+       }
+       return p;
+}
+
+static int
+setuplink(void *link, int *pflags)
+{
+       uchar im, pm[32], cm[32], *c;
+       static int lastirq = 1;
+       int gsi, i;
+       void *r;
+
+       if(amltag(link) != 'N')
+               return -1;
+
+       r = nil;
+       if(amleval(amlwalk(link, "_PRS"), "", &r) < 0)
+               return -1;
+       if(getirqs(r, pm, pflags) < 0)
+               return -1;
+
+       r = nil;
+       if(amleval(amlwalk(link, "_CRS"), "", &r) < 0)
+               return -1;
+       if(getirqs(r, cm, pflags) < 0)
+               return -1;
+       
+       gsi = -1;
+       for(i=0; i<256; i++){
+               im = 1<<(i%8);
+               if(pm[i/8] & im){
+                       if(cm[i/8] & im)
+                               gsi = i;
+               }
+       }
+
+       if(gsi > 0 || getconf("*nopcirouting") != nil)
+               return gsi;
+
+       for(i=0; i<256; i++){
+               gsi = lastirq++ & 0xFF; /* round robin */
+               im = 1<<(gsi%8);
+               if(pm[gsi/8] & im){
+                       if((c = setirq(r, gsi)) == nil)
+                               break;
+                       if(amleval(amlwalk(link, "_SRS"), "b", c, nil) < 0)
+                               break;
+                       return gsi;
+               }
+       }
+       return -1;
+}
+
 static int
 enumprt(void *dot, void *)
 {
        void *p, **a, **b;
-       int bno, dno, pin;
+       int bno, dno, pin, gsi, flags;
        int n, i;
 
        bno = pcibusno(dot);
@@ -314,6 +430,7 @@ enumprt(void *dot, void *)
        if(amltag(p) != 'p')
                return 1;
 
+       amltake(p);
        n = amllen(p);
        a = amlval(p);
        for(i=0; i<n; i++){
@@ -321,15 +438,20 @@ enumprt(void *dot, void *)
                        continue;
                if(amllen(a[i]) != 4)
                        continue;
+               flags = 0;
                b = amlval(a[i]);
                dno = amlint(b[0])>>16;
                pin = amlint(b[1]);
-               if(amltag(b[2]) == 'N' || amlint(b[2])){
-                       print("enumprt: interrupt link not handled %V\n", b[2]);
-                       continue;
+               gsi = amlint(b[3]);
+               if(gsi==0){
+                       gsi = setuplink(b[2], &flags);
+                       if(gsi <= 0)
+                               continue;
                }
-               addirq(amlint(b[3]), BusPCI, bno, (dno<<2)|pin, 0);
+               addirq(gsi, BusPCI, bno, (dno<<2)|pin, flags);
        }
+       amldrop(p);
+
        return 1;
 }
 
@@ -339,7 +461,7 @@ acpiinit(void)
        Tbl *t;
        Apic *a;
        void *va;
-       uchar *p, *e;
+       uchar *s, *p, *e;
        ulong lapicbase;
        int machno, i, c;
 
@@ -375,16 +497,16 @@ acpiinit(void)
        return;
 
 Foundapic:
-       p = t->data;
-       e = p + tbldlen(t);
-       lapicbase = get32(p); p += 8;
+       s = t->data;
+       e = s + tbldlen(t);
+       lapicbase = get32(s); s += 8;
        va = vmap(lapicbase, 1024);
-       print("LAPIC: %.8lux %.8lux\n", lapicbase, (ulong)va);
+       print("LAPIC: %.8lux %#p\n", lapicbase, va);
        if(va == nil)
                panic("acpiinit: cannot map lapic %.8lux", lapicbase);
 
        machno = 0;
-       for(; p < e; p += c){
+       for(p = s; p < e; p += c){
                c = p[1];
                if(c < 2 || (p+c) > e)
                        break;
@@ -401,16 +523,21 @@ Foundapic:
                        a->lintr[0] = ApicIMASK;
                        a->lintr[1] = ApicIMASK;
                        a->flags = p[4] & PcmpEN;
-                       if(a->flags & PcmpEN){
-                               a->machno = machno++;
-
-                               /*
-                                * platform firmware should list the boot processor
-                                * as the first processor entry in the MADT
-                                */
-                               if(a->machno == 0)
-                                       a->flags |= PcmpBP;
+
+                       /* skip disabled processors */
+                       if((a->flags & PcmpEN) == 0 || mpapic[a->apicno] != nil){
+                               xfree(a);
+                               break;
                        }
+                       a->machno = machno++;
+
+                       /*
+                        * platform firmware should list the boot processor
+                        * as the first processor entry in the MADT
+                        */
+                       if(a->machno == 0)
+                               a->flags |= PcmpBP;
+
                        mpapic[a->apicno] = a;
                        break;
                case 0x01:      /* I/O APIC */
@@ -428,6 +555,18 @@ Foundapic:
                        mpioapic[a->apicno] = a;
                        ioapicinit(a, a->apicno);
                        break;
+               }
+       }
+
+       /*
+        * need 2nd pass as vbox puts interrupt overrides
+        * *before* the ioapic entries (!)
+        */
+       for(p = s; p < e; p += c){
+               c = p[1];
+               if(c < 2 || (p+c) > e)
+                       break;
+               switch(*p){
                case 0x02:      /* Interrupt Source Override */
                        addirq(get32(p+4), BusISA, 0, p[3], get16(p+8));
                        break;
@@ -506,11 +645,17 @@ readtbls(Chan*, void *v, long n, vlong o)
 static int
 identify(void)
 {
+       uintptr pa;
        char *cp;
 
        if((cp = getconf("*acpi")) == nil)
                return 1;
-       if((rsd = sigsearch("RSD PTR ")) == nil)
+       pa = (uintptr)strtoull(cp, nil, 16);
+       if(pa <= 1)
+               rsd = sigsearch("RSD PTR ");
+       else
+               rsd = vmap(pa, sizeof(Rsd));
+       if(rsd == nil)
                return 1;
        if(checksum(rsd, 20) && checksum(rsd, 36))
                return 1;
@@ -519,7 +664,227 @@ identify(void)
                return 1;
        if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
                return 1;
-       if(m->havetsc)
+       if(m->havetsc && getconf("*notsc") == nil)
                archacpi.fastclock = tscticks;
        return 0;
 }
+
+static int
+readpcicfg(Amlio *io, void *data, int n, int offset)
+{
+       ulong r, x;
+       Pcidev *p;
+       uchar *a;
+       int i;
+
+       a = data;
+       p = io->aux;
+       if(p == nil)
+               return -1;
+       offset += io->off;
+       if(offset > 256)
+               return 0;
+       if(n+offset > 256)
+               n = 256-offset;
+       r = offset;
+       if(!(r & 3) && n == 4){
+               x = pcicfgr32(p, r);
+               PBIT32(a, x);
+               return 4;
+       }
+       if(!(r & 1) && n == 2){
+               x = pcicfgr16(p, r);
+               PBIT16(a, x);
+               return 2;
+       }
+       for(i = 0; i <  n; i++){
+               x = pcicfgr8(p, r);
+               PBIT8(a, x);
+               a++;
+               r++;
+       }
+       return i;
+}
+
+static int
+writepcicfg(Amlio *io, void *data, int n, int offset)
+{
+       ulong r, x;
+       Pcidev *p;
+       uchar *a;
+       int i;
+
+       a = data;
+       p = io->aux;
+       if(p == nil)
+               return -1;
+       offset += io->off;
+       if(offset > 256)
+               return 0;
+       if(n+offset > 256)
+               n = 256-offset;
+       r = offset;
+       if(!(r & 3) && n == 4){
+               x = GBIT32(a);
+               pcicfgw32(p, r, x);
+               return 4;
+       }
+       if(!(r & 1) && n == 2){
+               x = GBIT16(a);
+               pcicfgw16(p, r, x);
+               return 2;
+       }
+       for(i = 0; i <  n; i++){
+               x = GBIT8(a);
+               pcicfgw8(p, r, x);
+               a++;
+               r++;
+       }
+       return i;
+}
+
+static int
+readioport(Amlio *io, void *data, int len, int port)
+{
+       uchar *a;
+
+       a = data;
+       port += io->off;
+       switch(len){
+       case 4:
+               PBIT32(a, inl(port));
+               return 4;
+       case 2:
+               PBIT16(a, ins(port));
+               return 2;
+       case 1:
+               PBIT8(a, inb(port));
+               return 1;
+       }
+       return -1;
+}
+
+static int
+writeioport(Amlio *io, void *data, int len, int port)
+{
+       uchar *a;
+
+       a = data;
+       port += io->off;
+       switch(len){
+       case 4:
+               outl(port, GBIT32(a));
+               return 4;
+       case 2:
+               outs(port, GBIT16(a));
+               return 2;
+       case 1:
+               outb(port, GBIT8(a));
+               return 1;
+       }
+       return -1;
+}
+
+int
+amlmapio(Amlio *io)
+{
+       int tbdf;
+       Pcidev *pdev;
+       char buf[64];
+
+       switch(io->space){
+       default:
+               print("amlmapio: address space %x not implemented\n", io->space);
+               break;
+       case MemSpace:
+               if((io->va = vmap(io->off, io->len)) == nil){
+                       print("amlmapio: vmap failed\n");
+                       break;
+               }
+               return 0;
+       case IoSpace:
+               snprint(buf, sizeof(buf), "%N", io->name);
+               if(ioalloc(io->off, io->len, 0, buf) < 0){
+                       print("amlmapio: ioalloc failed\n");
+                       break;
+               }
+               io->read = readioport;
+               io->write = writeioport;
+               return 0;
+       case PcicfgSpace:
+               if((tbdf = pciaddr(io->name)) < 0){
+                       print("amlmapio: no address\n");
+                       break;
+               }
+               if((pdev = pcimatchtbdf(tbdf)) == nil){
+                       print("amlmapio: no device %T\n", tbdf);
+                       break;
+               }
+               io->aux = pdev;
+               io->read = readpcicfg;
+               io->write = writepcicfg;
+               return 0;
+       }
+       print("amlmapio: mapping %N failed\n", io->name);
+       return -1;
+}
+
+void
+amlunmapio(Amlio *io)
+{
+       switch(io->space){
+       case MemSpace:
+               vunmap(io->va, io->len);
+               break;
+       case IoSpace:
+               iofree(io->off);
+               break;
+       }
+}
+
+void*
+amlalloc(int n){
+       void *p;
+
+       if((p = malloc(n)) == nil)
+               panic("amlalloc: no memory");
+       memset(p, 0, n);
+       return p;
+}
+
+void
+amlfree(void *p){
+       free(p);
+}
+
+void
+amldelay(int us)
+{
+       microdelay(us);
+}
+
+/*
+ * reset machine by writing acpi reset register.
+ */
+void
+acpireset(void)
+{
+       uchar *p;
+       Tbl *t;
+       int i;
+
+       for(i=0; i < ntblmap; i++){
+               t = tblmap[i];
+               if(memcmp(t->sig, "FACP", 4) != 0)
+                       continue;
+               if(get32(t->len) <= 128)
+                       break;
+               p = (uchar*)t;
+               if((get32(p + 112) & (1<<10)) == 0)
+                       break;
+               if(p[116+0] != IoSpace)
+                       break;
+               outb(get32(p+116+4), p[128]);
+               break;
+       }
+}