#include <libc.h>
#include <aml.h>
+int fd, iofd;
struct Ureg u;
-int fd, iofd, PM1A_CNT_BLK, PM1B_CNT_BLK, SLP_TYPa, SLP_TYPb;
+ulong PM1a_CNT_BLK, PM1b_CNT_BLK, SLP_TYPa, SLP_TYPb;
+ulong GPE0_BLK, GPE1_BLK, GPE0_BLK_LEN, GPE1_BLK_LEN;
+enum {
+ SLP_EN = 0x2000,
+ SLP_TM = 0x1c00,
+};
typedef struct Tbl Tbl;
struct Tbl {
uchar data[];
};
-void*
-amlalloc(int n){
- return mallocz(n, 1);
-}
-
-void
-amlfree(void *p){
- free(p);
-}
+enum {
+ Tblsz = 4+4+1+1+6+8+4+4+4,
+};
-static uint
+static ulong
get32(uchar *p){
return p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
}
loadacpi(void)
{
void *r, **rr;
+ ulong l;
Tbl *t;
int n;
- ulong l;
amlinit();
for(;;){
t = malloc(sizeof(*t));
- if((n = readn(fd, t, sizeof(*t))) <= 0)
+ if((n = readn(fd, t, Tblsz)) <= 0)
break;
- if(n != sizeof(*t))
+ if(n != Tblsz)
return -1;
- l = *(ulong*)(t->len);
- if(l < sizeof(*t))
+ l = get32(t->len);
+ if(l < Tblsz)
return -1;
- t = realloc(t, l);
- l -= sizeof(*t);
+ l -= Tblsz;
+ t = realloc(t, sizeof(*t) + l);
if(readn(fd, t->data, l) != l)
return -1;
- if(memcmp("DSDT", t->sig, 4) == 0)
+ if(memcmp("DSDT", t->sig, 4) == 0){
+ amlintmask = (~0ULL) >> (t->rev <= 1)*32;
amlload(t->data, l);
+ }
else if(memcmp("SSDT", t->sig, 4) == 0)
amlload(t->data, l);
else if(memcmp("FACP", t->sig, 4) == 0){
- PM1A_CNT_BLK = get32(((uchar*)t) + 64);
- PM1B_CNT_BLK = get32(((uchar*)t) + 68);
+ PM1a_CNT_BLK = get32(((uchar*)t) + 64);
+ PM1b_CNT_BLK = get32(((uchar*)t) + 68);
+ GPE0_BLK = get32(((uchar*)t) + 80);
+ GPE1_BLK = get32(((uchar*)t) + 84);
+ GPE0_BLK_LEN = *(((uchar*)t) + 92);
+ GPE1_BLK_LEN = *(((uchar*)t) + 93);
}
}
- if(PM1A_CNT_BLK == 0)
- return -1;
if(amleval(amlwalk(amlroot, "_S5"), "", &r) < 0)
return -1;
if(amltag(r) != 'p' || amllen(r) < 2)
return -1;
rr = amlval(r);
- if(amltag(rr[1]) != 'i')
- return -1;
- SLP_TYPa = (amlint(rr[1]) & 0xFF) << 10;
- SLP_TYPb = ((amlint(rr[1]) >> 8) & 0xFF) << 10;
+ SLP_TYPa = amlint(rr[0]);
+ SLP_TYPb = amlint(rr[1]);
return 0;
}
void
-outw(long addr, short val)
+outw(long addr, ushort val)
{
- char buf[2];
-
+ uchar buf[2];
+
+ if(addr == 0)
+ return;
buf[0] = val;
buf[1] = val >> 8;
pwrite(iofd, buf, 2, addr);
}
+void
+wirecpu0(void)
+{
+ char buf[128];
+ int ctl;
+
+ snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+ if((ctl = open(buf, OWRITE)) < 0){
+ snprint(buf, sizeof(buf), "#p/%d/ctl", getpid());
+ if((ctl = open(buf, OWRITE)) < 0)
+ return;
+ }
+ write(ctl, "wired 0", 7);
+ close(ctl);
+}
+
void
main()
{
+ int n;
+
+ wirecpu0();
+
if((fd = open("/dev/apm", ORDWR)) < 0)
if((fd = open("#P/apm", ORDWR)) < 0)
goto tryacpi;
goto fail;
if(loadacpi() < 0)
goto fail;
- outw(PM1A_CNT_BLK, SLP_TYPa | 0x2000);
- if(PM1B_CNT_BLK != 0)
- outw(PM1B_CNT_BLK, SLP_TYPb | 0x2000);
+
+ /* disable GPEs */
+ for(n = 0; GPE0_BLK > 0 && n < GPE0_BLK_LEN/2; n += 2){
+ outw(GPE0_BLK + GPE0_BLK_LEN/2 + n, 0); /* EN */
+ outw(GPE0_BLK + n, 0xffff); /* STS */
+ }
+ for(n = 0; GPE1_BLK > 0 && n < GPE1_BLK_LEN/2; n += 2){
+ outw(GPE1_BLK + GPE1_BLK_LEN/2 + n, 0); /* EN */
+ outw(GPE1_BLK + n, 0xffff); /* STS */
+ }
+
+ outw(PM1a_CNT_BLK, ((SLP_TYPa << 10) & SLP_TM) | SLP_EN);
+ outw(PM1b_CNT_BLK, ((SLP_TYPb << 10) & SLP_TM) | SLP_EN);
+ sleep(100);
+
+ /*
+ * The SetSystemSleeping() example from the ACPI spec
+ * writes the same value in both registers. But Linux/BSD
+ * write distinct values from the _Sx package (like the
+ * code above). The _S5 package on a HP DC5700 is
+ * Package(0x2){0x0, 0x7} and writing SLP_TYPa of 0 to
+ * PM1a_CNT_BLK seems to have no effect but 0x7 seems
+ * to work fine. So trying the following as a last effort.
+ */
+ SLP_TYPa |= SLP_TYPb;
+ outw(PM1a_CNT_BLK, ((SLP_TYPa << 10) & SLP_TM) | SLP_EN);
+ outw(PM1b_CNT_BLK, ((SLP_TYPa << 10) & SLP_TM) | SLP_EN);
+ sleep(100);
+
fail:
- ;
+ exits("scram");
}