/* Zipit sleep control. * (c) 2005 Chris Studholme * GPL */ #include #include #include #include #include #include #include "hardware.h" volatile unsigned char* pbdr = (unsigned char*)(CLPS7111_VIRT_BASE+PBDR); volatile unsigned char* pbddr = (unsigned char*)(CLPS7111_VIRT_BASE+PBDDR); volatile unsigned char* pddr = (unsigned char*)(CLPS7111_VIRT_BASE+PDDR); volatile unsigned char* ledflsh = (unsigned char*)(CLPS7111_VIRT_BASE+LEDFLSH); volatile unsigned int* rtc = (unsigned int*)(CLPS7111_VIRT_BASE+RTCDR); volatile unsigned int* rtcmr = (unsigned int*)(CLPS7111_VIRT_BASE+RTCMR); volatile unsigned int* stdby = (unsigned int*)(CLPS7111_VIRT_BASE+STDBY); volatile unsigned int* intmr1 = (unsigned int*)(CLPS7111_VIRT_BASE+INTMR1); volatile unsigned int* intmr2 = (unsigned int*)(CLPS7111_VIRT_BASE+INTMR2); volatile unsigned int* intmr3 = (unsigned int*)(CLPS7111_VIRT_BASE+INTMR3); volatile unsigned int* rtceoi = (unsigned int*)(CLPS7111_VIRT_BASE+RTCEOI); volatile unsigned int* kbdeoi = (unsigned int*)(CLPS7111_VIRT_BASE+KBDEOI); volatile unsigned int* syscon1 = (unsigned int*)(CLPS7111_VIRT_BASE+SYSCON1); volatile unsigned int* syscon2 = (unsigned int*)(CLPS7111_VIRT_BASE+SYSCON2); volatile unsigned int* syscon3 = (unsigned int*)(CLPS7111_VIRT_BASE+SYSCON3); /* Put processor to sleep. * * If sync is non-zero, busy waits are added before and after sleep to sync * with RTC and ensure kernel clock can be accurately updated. * * If timeout>0, auto wakeup in timeout seconds. * * If wol is non-zero, incoming network packets will cause wakeup. * * The following functions are performed: * - green LED is forced off * - orange LED blinks briefly once every 4 seconds * - LCD is shut down * - timer interrupt is disabled * - keyboard wakeup is enabled: * a. if lid is closed, any key including lid will wake * b. if lid is open, any key except Enter/BS/;/L/lid will wake * - actual sleep * * On wakeup all hardware is restored to previous state and kernel clock * is updated to account for slept time. If sync is zero, the kernel can * gain or lose up to one second per sleep. */ static void do_sleep(int sync, int timeout, int wol) { // turn off green LED unsigned char pbddr_save = *pbddr; *pbddr |= 2; unsigned char pbdr_save = *pbdr; *pbdr &= ~2; // setup orange/yellow LED to flash unsigned char pddr_save = *pddr; *ledflsh = 64 + 4 + 3; // save syscon and intmr registers unsigned int syscon1_save = *syscon1; unsigned int syscon2_save = *syscon2; unsigned int intmr1_save = *intmr1; unsigned int intmr2_save = *intmr2; // disable LCD *pddr &= ~(1<<5); *syscon1 &= ~(1<<12); // turn off DAI??? Is this audio? What do I do? // wait for rtc edge if (sync) { unsigned int now = *rtc; while (now==*rtc) ; } unsigned int start = *rtc; // disable timer interrupt *intmr1 &= ~(1<<9); // disable net interrupt if !wol if (!wol) *intmr1 &= ~(1<<5); // disable external interrupt 1 // keyboard interrupt if (*pbdr&8) { // lid open; we'll miss wakeup on Enter/BS/;/L/lid *syscon2 = syscon2_save|2; // only use first 6 port A bits *syscon1 &= ~15; // scan entire keyboard *kbdeoi = 0xff; // clear pending interrupt *intmr2 |= 1; // enable keyboard interrupt } else { // lid closed; wake on any keyboard activity *syscon2 = syscon2_save|8; } if (timeout>0) { if (sync && timeout>1) --timeout; // we busy wait one of these seconds after wakeup *rtcmr = start+timeout; *rtceoi = 0xff; *intmr1 |= (1<<10); } // ******** actually sleep ******** *stdby = 0xAA; // ******** sleep done ******** // wait for rtc edge if (sync) { unsigned int now = *rtc; while (now==*rtc) ; } unsigned int end = *rtc; // update kernel clock struct timeval tv; gettimeofday(&tv,0); tv.tv_sec += end-start; settimeofday(&tv,0); // restore interrupts *syscon2 = syscon2_save; *intmr2 = intmr2_save; *intmr1 = intmr1_save; // turn on DAI // probably something I should do? // restore LCD *syscon1 = syscon1_save; // pddr is restored below // restore orange/yellow LED *ledflsh = 0; *pddr = pddr_save; // restore green LED *pbdr = pbdr_save; *pbddr = pbddr_save; } int main(int argc, char **argv) { int timeout=0; int wol=0; int sync=1; if (argc>=2) timeout = atoi(argv[1]); if (argc>=3) wol = atoi(argv[2]); do_sleep(sync,timeout,wol); return 0; }