The power-saving SLEEP patch by Simon Elén.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3259 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index c1541cf..f272bea 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -1376,3 +1376,8 @@
 desc: Display of recorded file size
 eng: "Size:"
 new:
+
+id: LANG_CPU_SLEEP
+desc: in system_settings_menu()
+eng: "Power Saving"
+new:
diff --git a/apps/main.c b/apps/main.c
index 614a7fc..e33e2bd 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -103,12 +103,12 @@
     font_init();
     show_logo();
 
+    set_irq_level(0);
 #ifdef DEBUG
     debug_init();
 #else
     serial_setup();
 #endif
-    set_irq_level(0);
 
     i2c_init();
 
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 8fb31d8..5196735 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -180,8 +180,7 @@
 
     while(!done)
     {
-        yield();
-        button = button_get(false);
+        button = button_get_w_tmo(HZ / peak_meter_fps);
         switch(button)
         {
             case BUTTON_OFF:
diff --git a/apps/settings.c b/apps/settings.c
index bc78e82..c7ab9e4 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include "config.h"
 #include "kernel.h"
+#include "thread.h"
 #include "settings.h"
 #include "disk.h"
 #include "panic.h"
@@ -102,7 +103,7 @@
 0x23    0x37    <rec. left gain (bit 0-3)>
 0x24    0x38    <rec. right gain (bit 0-3)>
 0x25    0x39    <disk poweroff flag (bit 0), MP3 buffer margin (bit 1-3),
-                 Trickle charge flag (bit 4)>
+                 Trickle charge flag (bit 4), CPU sleep flag (bit 5)>
 0x26    0x40    <runtime low byte>
 0x27    0x41    <runtime high byte>
 0x28    0x42    <topruntime low byte>
@@ -350,7 +351,8 @@
     config_block[0x25] = (unsigned char)
         ((global_settings.disk_poweroff & 1) |
          ((global_settings.buffer_margin & 7) << 1) |
-         ((global_settings.trickle_charge & 1) << 4));
+         ((global_settings.trickle_charge & 1) << 4) |
+         ((global_settings.cpu_sleep & 1) << 5));
 
     {
         static long lasttime = 0;
@@ -513,6 +515,8 @@
                  global_settings.lang_file);
         lang_load(buf);
     }
+
+    cpu_sleep(global_settings.cpu_sleep);
 }
 
 /*
@@ -636,6 +640,7 @@
             global_settings.disk_poweroff = config_block[0x25] & 1;
             global_settings.buffer_margin = (config_block[0x25] >> 1) & 7;
             global_settings.trickle_charge = (config_block[0x25] >> 4) & 1;
+            global_settings.cpu_sleep = (config_block[0x25] >> 5) & 1;
         }
 
         if (config_block[0x27] != 0xff)
@@ -881,6 +886,7 @@
     global_settings.lang_file[0] = 0;
     global_settings.runtime = 0;
     global_settings.topruntime = 0;
+    global_settings.cpu_sleep = true;
 }
 
 
diff --git a/apps/settings.h b/apps/settings.h
index be41ef8..529797c 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -147,6 +147,8 @@
     int bidir_limit;   /* bidir scroll length limit */
     int scroll_delay;  /* delay (in 1/10s) before starting scroll */
     int scroll_step;   /* pixels to advance per update */
+
+    bool cpu_sleep;    /* Use sleep instruction when idle? */
 };
 
 /* prototypes */
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index baffb81..37b2b09 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -27,6 +27,7 @@
 #include "mpeg.h"
 #include "button.h"
 #include "kernel.h"
+#include "thread.h"
 #include "sprintf.h"
 #include "settings.h"
 #include "settings_menu.h"
@@ -539,6 +540,13 @@
 }
 #endif
 
+static bool cpu_sleep_set(void)
+{
+    bool result = set_bool(str(LANG_CPU_SLEEP), &global_settings.cpu_sleep);
+    cpu_sleep(global_settings.cpu_sleep);
+    return result;
+}
+
 static bool buffer_margin(void)
 {
     return set_int(str(LANG_MP3BUFFER_MARGIN), "s",
@@ -731,6 +739,7 @@
 #ifdef HAVE_ATA_POWER_OFF
         { str(LANG_POWEROFF),    poweroff        },
 #endif
+        { str(LANG_CPU_SLEEP),   cpu_sleep_set   },
 #ifndef SIMULATOR
         { str(LANG_BATTERY_CAPACITY), battery_capacity },
 #endif
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index 79193d9..e65200e 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -111,7 +111,8 @@
 {
     int timeout = current_tick + HZ*10;
     while (TIME_BEFORE(current_tick, timeout) && (ATA_ALT_STATUS & STATUS_BSY))
-        yield();
+        sleep_thread();
+    wake_up_thread();
 
     if (TIME_BEFORE(current_tick, timeout))
         return 1;
@@ -131,7 +132,8 @@
     
     while (TIME_BEFORE(current_tick, timeout) &&
            !(ATA_ALT_STATUS & STATUS_RDY))
-        yield();
+        sleep_thread();
+    wake_up_thread();
 
     if (TIME_BEFORE(current_tick, timeout))
         return STATUS_RDY;
diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c
index 2c33440..69d041f 100644
--- a/firmware/drivers/button.c
+++ b/firmware/drivers/button.c
@@ -163,19 +163,8 @@
 int button_get_w_tmo(int ticks)
 {
     struct event ev;
-    unsigned int timeout = current_tick + ticks;
-
-    while (TIME_BEFORE( current_tick, timeout ))
-    {
-        if(!queue_empty(&button_queue))
-        {
-            queue_wait(&button_queue, &ev);
-            return ev.id;
-        }
-        yield();
-    }
-
-    return BUTTON_NONE;
+    queue_wait_w_tmo(&button_queue, &ev, ticks);
+    return (ev.id != SYS_TIMEOUT)? ev.id: BUTTON_NONE;
 }
 
 #ifdef HAVE_RECORDER_KEYPAD
diff --git a/firmware/drivers/i2c.c b/firmware/drivers/i2c.c
index 97d7a51..f0b5907 100644
--- a/firmware/drivers/i2c.c
+++ b/firmware/drivers/i2c.c
@@ -19,6 +19,7 @@
 #include "lcd.h"
 #include "sh7034.h"
 #include "kernel.h"
+#include "thread.h"
 #include "debug.h"
 
 #define PB13 0x2000
@@ -108,7 +109,8 @@
     
     SCL_INPUT;   /* Set the clock to input */
     while(!SCL)  /* and wait for the MAS to release it */
-        yield();
+        sleep_thread();
+    wake_up_thread();
 
     DELAY;
     SCL_OUTPUT;
@@ -130,7 +132,8 @@
     SDA_INPUT;   /* And set to input */
     SCL_INPUT;   /* Set the clock to input */
     while(!SCL)  /* and wait for the MAS to release it */
-        yield();
+        sleep_thread();
+    wake_up_thread();
     
     if (SDA)
         /* ack failed */
diff --git a/firmware/export/thread.h b/firmware/export/thread.h
index 7940ddf..7be58d0 100644
--- a/firmware/export/thread.h
+++ b/firmware/export/thread.h
@@ -19,12 +19,17 @@
 #ifndef THREAD_H
 #define THREAD_H
 
+#include <stdbool.h>
+
 #define MAXTHREADS	16
 #define DEFAULT_STACK_SIZE 0x400 /* Bytes */
 
 int create_thread(void* function, void* stack, int stack_size, char *name);
 void switch_thread(void);
+void sleep_thread(void);
+void wake_up_thread(void);
 void init_threads(void);
 int thread_stack_usage(int threadnum);
+void cpu_sleep(bool enabled);
 
 #endif
diff --git a/firmware/kernel.c b/firmware/kernel.c
index 4d2f519..3255ba0 100644
--- a/firmware/kernel.c
+++ b/firmware/kernel.c
@@ -59,13 +59,15 @@
     int timeout = current_tick + ticks + 1;
 
     while (TIME_BEFORE( current_tick, timeout )) {
-        yield();
+        sleep_thread();
     }
+    wake_up_thread();
 }
 
 void yield(void)
 {
     switch_thread();
+    wake_up_thread();
 }
 
 /****************************************************************************
@@ -96,8 +98,9 @@
 {
     while(q->read == q->write)
     {
-        switch_thread();
+        sleep_thread();
     }
+    wake_up_thread();
 
     *ev = q->events[(q->read++) & QUEUE_LENGTH_MASK];
 }
@@ -108,8 +111,9 @@
 
     while(q->read == q->write && TIME_BEFORE( current_tick, timeout ))
     {
-        switch_thread();
+        sleep_thread();
     }
+    wake_up_thread();
 
     if(q->read != q->write)
     {
@@ -201,6 +205,7 @@
     }
 
     current_tick++;
+    wake_up_thread();
 
     TSR0 &= ~0x01;
 }
@@ -257,7 +262,8 @@
 {
     /* Wait until the lock is open... */
     while(m->locked)
-        yield();
+        sleep_thread();
+    wake_up_thread();
 
     /* ...and lock it */
     m->locked = true;
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
index 0518dd2..6fe60c9 100644
--- a/firmware/mpeg.c
+++ b/firmware/mpeg.c
@@ -845,6 +845,7 @@
             {
                 saving = true;
                 queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
+                wake_up_thread();
             }
         }
     }
@@ -948,6 +949,7 @@
     }
 
     CHCR3 &= ~0x0002; /* Clear DMA interrupt */
+    wake_up_thread();
 }
 
 #ifdef HAVE_MAS3587F
@@ -1791,9 +1793,10 @@
         }
         else
         {
+            /* This doesn't look neccessary...
             yield();
             if(!queue_empty(&mpeg_queue))
-            {
+            {*/
                 queue_wait(&mpeg_queue, &ev);
                 switch(ev.id)
                 {
@@ -1897,7 +1900,7 @@
                         init_playback_done = true;
                         break;
                 }
-            }
+            /*}*/
         }
 #endif
     }
@@ -1974,7 +1977,8 @@
     queue_post(&mpeg_queue, MPEG_INIT_RECORDING, NULL);
 
     while(!init_recording_done)
-        yield();
+        sleep_thread();
+    wake_up_thread();
 }
 
 void mpeg_init_playback(void)
@@ -1983,7 +1987,8 @@
     queue_post(&mpeg_queue, MPEG_INIT_PLAYBACK, NULL);
 
     while(!init_playback_done)
-        yield();
+        sleep_thread();
+    wake_up_thread();
 }
 
 static void init_recording(void)
diff --git a/firmware/test/fat/test.sh b/firmware/test/fat/test.sh
index 6a26e7f..44ff6bb 100644
--- a/firmware/test/fat/test.sh
+++ b/firmware/test/fat/test.sh
@@ -75,7 +75,6 @@
     try mkfile /cpa.rock 0
     check
     try chkfile /cpa.rock
-    try chkfile /apa.rock
     try chkfile /bpa.rock
 
     LOOP=50
diff --git a/firmware/thread.c b/firmware/thread.c
index 2651a4f..ea518ca 100644
--- a/firmware/thread.c
+++ b/firmware/thread.c
@@ -16,8 +16,11 @@
  * KIND, either express or implied.
  *
  ****************************************************************************/
+#include <stdbool.h>
 #include "thread.h"
 #include "panic.h"
+#include "kernel.h"
+#include "sh7034.h"
 
 struct regs
 {
@@ -30,6 +33,8 @@
 };
 
 int num_threads;
+bool cpu_sleep_enabled;
+static volatile int num_sleepers;
 static int current_thread;
 static struct regs thread_contexts[MAXTHREADS] __attribute__ ((section(".idata")));
 char *thread_name[MAXTHREADS];
@@ -95,6 +100,18 @@
     int next;
     unsigned int *stackptr;
 
+#ifdef SIMULATOR
+    /* Do nothing */
+#else
+
+    while (cpu_sleep_enabled && num_sleepers == num_threads)
+    {
+        /* Enter sleep mode, woken up on interrupt */
+        SBYCR &= 0x7F;
+        asm volatile ("sleep");
+    }
+    
+#endif
     next = current = current_thread;
     if (++next >= num_threads)
         next = 0;
@@ -108,6 +125,22 @@
        panicf("Stkov %s", thread_name[next]);
 }
 
+void cpu_sleep(bool enabled)
+{
+    cpu_sleep_enabled = enabled;
+}
+
+void sleep_thread(void)
+{
+    ++num_sleepers;
+    switch_thread();
+}
+
+void wake_up_thread(void)
+{
+    num_sleepers = 0;
+}
+
 /*--------------------------------------------------------------------------- 
  * Create thread.
  * Return 0 if context area could be allocated, else -1.
@@ -144,6 +177,7 @@
         regs->sr = 0;
         regs->pr = function;
     }
+    wake_up_thread();
     return 0;
 }
 
@@ -154,6 +188,7 @@
     thread_name[0] = main_thread_name;
     thread_stack[0] = stackbegin;
     thread_stack_size[0] = (int)stackend - (int)stackbegin;
+    num_sleepers = 0;
 }
 
 int thread_stack_usage(int threadnum)
diff --git a/uisimulator/common/stubs.c b/uisimulator/common/stubs.c
index e25c3ec..cfef2b5 100644
--- a/uisimulator/common/stubs.c
+++ b/uisimulator/common/stubs.c
@@ -221,3 +221,8 @@
   (void)ny;
 }
 #endif
+
+void cpu_sleep(bool enabled)
+{
+    (void)enabled;
+}