ref: 04a8392f8c7c75f585d48360774c6f1faa950f0c
dir: /sys/src/9/bitsy/power.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "pool.h" /* Power management for the bitsy */ #define TODFREQ 1000000000LL /* saved state during power down. * it's only used up to 164/4. * it's only used by routines in l.s */ ulong power_state[200/4]; ulong resumeaddr[1]; Rendez powerr; ulong powerflag = 0; /* set to start power-off sequence */ extern void power_resume(void); extern int setpowerlabel(void); extern void _start(void); extern Uart sa1110uart[]; GPIOregs savedgpioregs; Intrregs savedintrregs; #define R(p) ((Uartregs*)((p)->regs)) uchar *savedtext; static void checkflash(void) { ulong *p; ulong s; s = 0; for (p = (ulong*)FLASHZERO; p < (ulong*)(FLASHZERO+8*1024*1024); p++) s += *p; iprint("flash checksum is 0x%lux\n", s); } static void checkktext(void) { ulong *p; ulong s; s = 0; for (p = (ulong*)_start; p < (ulong*)etext; p++){ if(*p == 0) iprint("%#p->0\n", p); if (((ulong)p & 0x1fff) == 0){ iprint("page 0x%lux checksum 0x%lux\n", (ulong)(p-1)&~0x1fff, s); s = 0; } s += *p; } iprint("page 0x%lux checksum 0x%lux\n", (ulong)(p-1)&~0x1fff, s); } static void checkpagetab(void) { extern ulong l1table; ulong *p; ulong s; s = 0; for (p = (ulong*)l1table; p < (ulong*)(l1table+16*1024); p++) s += *p; iprint("page table checksum is 0x%lux\n", s); } static void dumpitall(void) { extern ulong l1table; iprint("intr: icip %lux iclr %lux iccr %lux icmr %lux\n", intrregs->icip, intrregs->iclr, intrregs->iccr, intrregs->icmr ); iprint("gpio: lvl %lux dir %lux, re %lux, fe %lux sts %lux alt %lux\n", gpioregs->level, gpioregs->direction, gpioregs->rising, gpioregs->falling, gpioregs->edgestatus, gpioregs->altfunc); iprint("uart1: %lux %lux %lux \nuart3: %lux %lux %lux\n", R(&sa1110uart[0])->ctl[0], R(&sa1110uart[0])->status[0], R(&sa1110uart[0])->status[1], R(&sa1110uart[1])->ctl[0], R(&sa1110uart[1])->status[0], R(&sa1110uart[1])->status[1]); iprint("tmr: osmr %lux %lux %lux %lux oscr %lux ossr %lux oier %lux\n", timerregs->osmr[0], timerregs->osmr[1], timerregs->osmr[2], timerregs->osmr[3], timerregs->oscr, timerregs->ossr, timerregs->oier); iprint("dram: mdcnfg %lux mdrefr %lux cas %lux %lux %lux %lux %lux %lux\n", memconfregs->mdcnfg, memconfregs->mdrefr, memconfregs->mdcas00, memconfregs->mdcas01,memconfregs->mdcas02, memconfregs->mdcas20, memconfregs->mdcas21,memconfregs->mdcas22); iprint("dram: mdcnfg msc %lux %lux %lux mecr %lux\n", memconfregs->msc0, memconfregs->msc1,memconfregs->msc2, memconfregs->mecr); iprint("mmu: CpControl %lux CpTTB %lux CpDAC %lux l1table 0x%lux\n", getcontrol(), getttb(), getdac(), l1table); iprint("powerregs: pmcr %lux pssr %lux pcfr %lux ppcr %lux pwer %lux pspr %lux pgsr %lux posr %lux\n", powerregs->pmcr, powerregs->pssr, powerregs->pcfr, powerregs->ppcr, powerregs->pwer, powerregs->pspr, powerregs->pgsr, powerregs->posr); checkpagetab(); checkflash(); checkktext(); iprint("\n\n"); } static void intrcpy(Intrregs *to, Intrregs *from) { to->iclr = from->iclr; to->iccr = from->iccr; to->icmr = from->icmr; // interrupts enabled } static void gpiosave(GPIOregs *to, GPIOregs *from) { to->level = from->level; to->rising = from->rising; // gpio intrs enabled to->falling= from->falling; // gpio intrs enabled to->altfunc = from->altfunc; to->direction = from->direction; } static void gpiorestore(GPIOregs *to, GPIOregs *from) { to->direction = from->direction; to->altfunc = from->altfunc; to->set = from->level & 0x0fffffff; to->clear = ~from->level & 0x0fffffff; to->rising = from->rising; // gpio intrs enabled to->falling= from->falling; // gpio intrs enabled } void (*restart)(void) = nil; static int bitno(ulong x) { int i; for(i = 0; i < 8*sizeof(x); i++) if((1<<i) & x) break; return i; } int powerdown(void *) { return powerflag; } void deepsleep(void) { static int power_pl; ulong xsp, xlink; // ulong mecr; ulong clkd; vlong savedtod; extern void power_resume(void); power_pl = splhi(); xlink = getcallerpc(&xlink); /* Power down */ pcmciapower(0); irpower(0); audiopower(0); screenpower(0); µcpower(0); iprint("entering suspend mode, sp = %#p, pc = 0x%lux, psw = 0x%ux\n", &xsp, xlink, power_pl); // dumpitall(); delay(1000); uartpower(0); rs232power(0); clockpower(0); gpiosave(&savedgpioregs, gpioregs); intrcpy(&savedintrregs, intrregs); cacheflush(); delay(50); if(setpowerlabel()){ /* return here with mmu back on */ trapresume(); gpiorestore(gpioregs, &savedgpioregs); delay(50); intrcpy(intrregs, &savedintrregs); if(intrregs->icip & (1<<IRQgpio0)){ // don't want to sleep now. clear on/off irq. gpioregs->edgestatus = (1<<IRQgpio0); intrregs->icip = (1<<IRQgpio0); } clkd = clockpower(1); gpclkregs->r0 = 1<<0; todset(savedtod + clkd * TODFREQ, 0LL, 0); resetsuspendtimer(); rs232power(1); uartpower(1); delay(100); xlink = getcallerpc(&xlink); iprint("\nresuming execution, sp = %#p, pc = 0x%lux, psw = 0x%ux\n", &xsp, xlink, splhi()); // dumpitall(); delay(1000); // irpower(1); audiopower(1); µcpower(1); screenpower(1); pcmciapower(1); splx(power_pl); return; } cacheflush(); delay(100); savedtod = todget(nil); power_down(); /* no return */ } void powerkproc(void*) { ulong xlink, xlink1; while(waserror()) ; for(;;){ while(powerflag == 0) sleep(&powerr, powerdown, 0); xlink = getcallerpc(&xlink); // iprint("call deepsleep, pc = 0x%lux, sp = 0x%lux\n", xlink, &xlink); deepsleep(); xlink1 = getcallerpc(&xlink1); delay(2000); // iprint("deepsleep returned, pc = 0x%lux, sp = 0x%lux\n", xlink1, &xlink); powerflag = 0; } } void onoffintr(Ureg* , void*) { int i; /* Power down interrupt comes on power button release. * Act only after button has been released a full 100 ms */ if (powerflag) return; for (i = 0; i < 100; i++) { delay(1); if ((gpioregs->level & GPIO_PWR_ON_i) == 0) return; /* bounced */ } powerflag = 1; wakeup(&powerr); } static void blanktimer(void) { drawactive(0); } static ulong suspendtime = 120 * HZ; static int lastsuspend; void resetsuspendtimer(void) { suspendtime = 60 * HZ; } static void suspendtimer(void) { #ifdef notdef uvlong now; if (suspendtime > 0) suspendtime--; if (suspendtime == 0){ now = seconds(); if (now < lastsuspend + 10){ resetsuspendtimer(); return; } lastsuspend = seconds(); deepsleep(); lastsuspend = seconds(); return; } #endif /* notdef */ } void powerinit(void) { extern ulong power_magic; extern ulong power_code; extern ulong doze_code; ulong *p, *q, i; p = (ulong*)(((ulong)&power_magic + 0x1f) & ~0x1f); q = &power_code; for (i = 0; i < 8; i++) *p++ = *q++; p = (ulong*)(((ulong)doze + 0x3f) & ~0x1f); q = &doze_code; for (i = 0; i < 3; i++) *p++ = *q++; *resumeaddr = (ulong) power_resume; addclock0link(blanktimer, 1000/HZ); addclock0link(suspendtimer, 1000/HZ); intrenable(GPIOrising, bitno(GPIO_PWR_ON_i), onoffintr, nil, "on/off"); } void idlehands(void) { char *msgb = "idlehands called with splhi\n"; char *msga = "doze returns with splhi\n"; if(!islo()){ uartputs(msga, strlen(msga)); spllo(); } doze(); if(!islo()){ uartputs(msgb, strlen(msgb)); spllo(); } }