diff --git a/apps/features.txt b/apps/features.txt
index 3c0b0a3856..39c8422fbd 100644
--- a/apps/features.txt
+++ b/apps/features.txt
@@ -288,3 +288,7 @@ play_frequency
#if defined(HAVE_BOOTDATA)
boot_data
#endif
+
+#if defined(CONFIG_POWER_SAVING)
+sys_powersaving
+#endif
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index d4e0697667..fa1b5f2c27 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -13647,3 +13647,54 @@
*: "Disable Touch"
+
+ id: LANG_POWER_SAVING_MENU
+ desc: system clock and voltage settings
+ user: core
+
+
+ *: none
+ sys_powersaving: "Power Saving"
+
+
+ *: none
+ sys_powersaving: "Power Saving"
+
+
+
+ id: LANG_CPU
+ desc: system clock and voltage settings
+ user: core
+
+
+ *: none
+ sys_powersaving: "CPU"
+
+
+ *: none
+ sys_powersaving: "CPU"
+
+
+
+ id: LANG_I2C
+ desc: system clock and voltage settings
+ user: core
+
+
+ *: none
+ sys_powersaving: "I2C"
+
+
+ *: none
+ sys_powersaving: "I2C"
+
+
diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c
index 5c99cb4cd4..f23f01d4dc 100644
--- a/apps/menus/settings_menu.c
+++ b/apps/menus/settings_menu.c
@@ -295,6 +295,36 @@ MAKE_MENU(disk_menu, ID2P(LANG_DISK_MENU), 0, Icon_NOICON,
);
#endif
+#ifdef CONFIG_POWER_SAVING
+#if (CONFIG_POWER_SAVING & POWERSV_CPU)
+MENUITEM_SETTING(cpu_powersave, &global_settings.cpu_powersave, NULL);
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISK)
+MENUITEM_SETTING(disk_powersave, &global_settings.disk_powersave, NULL);
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_I2C)
+MENUITEM_SETTING(i2c_powersave, &global_settings.i2c_powersave, NULL);
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISP)
+MENUITEM_SETTING(disp_powersave, &global_settings.disp_powersave, NULL);
+#endif
+
+MAKE_MENU(power_save_menu, ID2P(LANG_POWER_SAVING_MENU), 0, Icon_NOICON,
+#if (CONFIG_POWER_SAVING & POWERSV_CPU)
+ &cpu_powersave,
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISK)
+ &disk_powersave,
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_I2C)
+ &i2c_powersave,
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISP)
+ &disp_powersave,
+#endif
+ );
+#endif /* ifdef CONFIG_POWER_SAVING */
+
/* Limits menu */
MENUITEM_SETTING(max_files_in_dir, &global_settings.max_files_in_dir, NULL);
MENUITEM_SETTING(max_files_in_playlist, &global_settings.max_files_in_playlist, NULL);
@@ -412,6 +442,11 @@ MAKE_MENU(system_menu, ID2P(LANG_SYSTEM),
&disk_menu,
#endif
&limits_menu,
+
+#if defined(CONFIG_POWER_SAVING)
+ &power_save_menu,
+#endif
+
#ifdef HAVE_QUICKSCREEN
&shortcuts_replaces_quickscreen,
#endif
diff --git a/apps/settings.h b/apps/settings.h
index 71233d904a..411675153a 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -860,6 +860,25 @@ struct user_settings
int governor;
int usb_mode;
#endif
+
+#ifdef CONFIG_POWER_SAVING
+#if (CONFIG_POWER_SAVING & POWERSV_CPU)
+ bool cpu_powersave;
+#endif
+
+#if (CONFIG_POWER_SAVING & POWERSV_DISK)
+ bool disk_powersave;
+#endif
+
+#if (CONFIG_POWER_SAVING & POWERSV_I2C)
+ bool i2c_powersave;
+#endif
+
+#if (CONFIG_POWER_SAVING & POWERSV_DISP)
+ bool disp_powersave;
+#endif
+#endif /*defined(CONFIG_POWER_SAVING)*/
+
};
/** global variables **/
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 57763d345a..fe280b0ff6 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -2246,7 +2246,43 @@ const struct settings_list settings[] = {
ID2P(LANG_IBASSO_USB_MODE_CHARGE),
ID2P(LANG_IBASSO_USB_MODE_ADB)),
#endif
-};
+
+#ifdef CONFIG_POWER_SAVING
+#if (CONFIG_POWER_SAVING & POWERSV_CPU)
+ OFFON_SETTING(0,
+ cpu_powersave,
+ LANG_CPU,
+ false,
+ "cpu powersave",
+ cpu_set_powersave),
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISK)
+ OFFON_SETTING(0,
+ disk_powersave,
+ LANG_DISK_MENU,
+ false,
+ "disk powersave",
+ disk_set_powersave),
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISP)
+ OFFON_SETTING(0,
+ disp_powersave,
+ LANG_DISPLAY,
+ false,
+ "disp powersave",
+ disp_set_powersave),
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_I2C)
+ OFFON_SETTING(0,
+ i2c_powersave,
+ LANG_I2C,
+ false,
+ "i2c powersave",
+ i2c_set_powersave),
+#endif
+#endif /*defined(CONFIG_POWER_SAVING)*/
+
+};/*struct settings_list settings*/
const int nb_settings = sizeof(settings)/sizeof(*settings);
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 14664257bf..e362000c18 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -168,6 +168,12 @@
#define SONY_NWZA860_PAD 64 /* The NWZ-A860 is too different (touchscreen) */
#define AGPTEK_ROCKER_PAD 65
+/* CONFIG_POWER_SAVE combinable bit flags*/
+#define POWERSV_CPU 0x1
+#define POWERSV_I2C 0x2
+#define POWERSV_DISK 0x4
+#define POWERSV_DISP 0x8
+
/* CONFIG_REMOTE_KEYPAD */
#define H100_REMOTE 1
#define H300_REMOTE 2
diff --git a/firmware/export/config/sansac200v2.h b/firmware/export/config/sansac200v2.h
index a51f9e3b00..98fbb570fb 100644
--- a/firmware/export/config/sansac200v2.h
+++ b/firmware/export/config/sansac200v2.h
@@ -193,6 +193,14 @@
/* Define this if you have adjustable CPU frequency */
#define HAVE_ADJUSTABLE_CPU_FREQ
+/*define this to enable CPU voltage scaling on AMS devices*/
+#define HAVE_ADJUSTABLE_CPU_VOLTAGE
+
+#ifndef BOOTLOADER
+/*define this with flags for power saving options device supports*/
+#define CONFIG_POWER_SAVING (POWERSV_CPU | POWERSV_I2C | POWERSV_DISK)
+#endif
+
#define BOOTFILE_EXT "sansa"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/sansaclip.h b/firmware/export/config/sansaclip.h
index cdee43d44a..f0300c2ac1 100644
--- a/firmware/export/config/sansaclip.h
+++ b/firmware/export/config/sansaclip.h
@@ -186,6 +186,14 @@
/* Define this if you have adjustable CPU frequency */
#define HAVE_ADJUSTABLE_CPU_FREQ
+/*define this to enable CPU voltage scaling on AMS devices*/
+#define HAVE_ADJUSTABLE_CPU_VOLTAGE
+
+#ifndef BOOTLOADER
+/*define this with flags for power saving options device supports*/
+#define CONFIG_POWER_SAVING (POWERSV_CPU | POWERSV_I2C | POWERSV_DISK)
+#endif
+
#define BOOTFILE_EXT "sansa"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h
index addf7d86c0..9a307420f4 100644
--- a/firmware/export/config/sansaclipplus.h
+++ b/firmware/export/config/sansaclipplus.h
@@ -206,6 +206,11 @@
/*define this to enable CPU voltage scaling on AMS devices*/
#define HAVE_ADJUSTABLE_CPU_VOLTAGE
+#ifndef BOOTLOADER
+/*define this with flags for power saving options device supports*/
+#define CONFIG_POWER_SAVING (POWERSV_CPU | POWERSV_I2C | POWERSV_DISK | POWERSV_DISP)
+#endif
+
#define BOOTFILE_EXT "sansa"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/sansaclipv2.h b/firmware/export/config/sansaclipv2.h
index cf3f90e363..81a0d4703f 100644
--- a/firmware/export/config/sansaclipv2.h
+++ b/firmware/export/config/sansaclipv2.h
@@ -200,6 +200,11 @@
/*define this to enable CPU voltage scaling on AMS devices*/
#define HAVE_ADJUSTABLE_CPU_VOLTAGE
+#ifndef BOOTLOADER
+/*define this with flags for power saving options device supports*/
+#define CONFIG_POWER_SAVING (POWERSV_CPU | POWERSV_I2C | POWERSV_DISK)
+#endif
+
#define BOOTFILE_EXT "sansa"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/sansaclipzip.h b/firmware/export/config/sansaclipzip.h
index fc9f558eef..8464603c34 100644
--- a/firmware/export/config/sansaclipzip.h
+++ b/firmware/export/config/sansaclipzip.h
@@ -205,6 +205,11 @@
/*define this to enable CPU voltage scaling on AMS devices*/
#define HAVE_ADJUSTABLE_CPU_VOLTAGE
+#ifndef BOOTLOADER
+/*define this with flags for power saving options device supports*/
+#define CONFIG_POWER_SAVING (POWERSV_CPU | POWERSV_I2C | POWERSV_DISK | POWERSV_DISP)
+#endif
+
#define BOOTFILE_EXT "sansa"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/sansae200v2.h b/firmware/export/config/sansae200v2.h
index 96b51f4e12..50e1843189 100644
--- a/firmware/export/config/sansae200v2.h
+++ b/firmware/export/config/sansae200v2.h
@@ -213,6 +213,14 @@
/* Define this if you have adjustable CPU frequency */
#define HAVE_ADJUSTABLE_CPU_FREQ
+/*define this to enable CPU voltage scaling on AMS devices*/
+#define HAVE_ADJUSTABLE_CPU_VOLTAGE
+
+#ifndef BOOTLOADER
+/*define this with flags for power saving options device supports*/
+#define CONFIG_POWER_SAVING (POWERSV_CPU | POWERSV_I2C | POWERSV_DISK)
+#endif
+
#define BOOTFILE_EXT "sansa"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/sansafuze.h b/firmware/export/config/sansafuze.h
index fae3463d75..dedf8c44da 100644
--- a/firmware/export/config/sansafuze.h
+++ b/firmware/export/config/sansafuze.h
@@ -219,6 +219,14 @@
/* Define this if you have adjustable CPU frequency */
#define HAVE_ADJUSTABLE_CPU_FREQ
+/*define this to enable CPU voltage scaling on AMS devices*/
+#define HAVE_ADJUSTABLE_CPU_VOLTAGE
+
+#ifndef BOOTLOADER
+/*define this with flags for power saving options device supports*/
+#define CONFIG_POWER_SAVING (POWERSV_CPU | POWERSV_I2C | POWERSV_DISK)
+#endif
+
#define BOOTFILE_EXT "sansa"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/sansafuzev2.h b/firmware/export/config/sansafuzev2.h
index b85e0747a3..3190930b56 100644
--- a/firmware/export/config/sansafuzev2.h
+++ b/firmware/export/config/sansafuzev2.h
@@ -224,6 +224,11 @@
/*define this to enable CPU voltage scaling on AMS devices*/
#define HAVE_ADJUSTABLE_CPU_VOLTAGE
+#ifndef BOOTLOADER
+/*define this with flags for power saving options device supports*/
+#define CONFIG_POWER_SAVING (POWERSV_CPU | POWERSV_I2C | POWERSV_DISK)
+#endif
+
#define BOOTFILE_EXT "sansa"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/target/arm/as3525/ascodec-as3525.c b/firmware/target/arm/as3525/ascodec-as3525.c
index 14c3ee7a36..d23859e420 100644
--- a/firmware/target/arm/as3525/ascodec-as3525.c
+++ b/firmware/target/arm/as3525/ascodec-as3525.c
@@ -623,11 +623,25 @@ void i2c_init(void)
/* required function but called too late for our needs */
}
+static void i2c_set_prescaler(unsigned int prescaler)
+{
+ int oldlevel = disable_interrupt_save(IRQ_FIQ_STATUS);
+ /* must be on to write regs */
+ bool i2c_enabled = bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE) &
+ CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE;
+
+ I2C2_CPSR0 = prescaler & 0xFF; /* 8 lsb */
+ I2C2_CPSR1 = (prescaler >> 8) & 0x3; /* 2 msb */
+
+ if (!i2c_enabled) /* put it back how we found it */
+ bitclr32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
+
+ restore_irq(oldlevel);
+}
+
/* initialises the internal i2c bus and prepares for transfers to the codec */
void ascodec_init(void)
{
- int prescaler;
-
ll_init(&req_list);
mutex_init(&as_mtx);
ascodec_async_init(&as_audio_req, ascodec_int_audio_cb, 0);
@@ -637,9 +651,7 @@ void ascodec_init(void)
bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE);
/* prescaler for i2c clock */
- prescaler = AS3525_I2C_PRESCALER;
- I2C2_CPSR0 = prescaler & 0xFF; /* 8 lsb */
- I2C2_CPSR1 = (prescaler >> 8) & 0x3; /* 2 msb */
+ i2c_set_prescaler(AS3525_I2C_PRESCALER);
/* set i2c slave address of codec part */
I2C2_SLAD0 = AS3514_I2C_ADDR << 1;
@@ -690,3 +702,12 @@ void ams_i2c_get_debug_cpsr(unsigned int *i2c_cpsr)
restore_irq(oldlevel);
}
+
+#if defined(CONFIG_POWER_SAVING) && (CONFIG_POWER_SAVING & POWERSV_I2C)
+/* declared in system-as3525.c */
+void ams_i2c_set_low_speed(bool slow)
+{
+ i2c_set_prescaler(slow ? AS3525_I2C_PRESCALER_MAX : AS3525_I2C_PRESCALER);
+}
+#endif
+
diff --git a/firmware/target/arm/as3525/clock-target.h b/firmware/target/arm/as3525/clock-target.h
index 7f6b17eff4..c45529dfda 100644
--- a/firmware/target/arm/as3525/clock-target.h
+++ b/firmware/target/arm/as3525/clock-target.h
@@ -158,22 +158,32 @@
#endif /* CONFIG_CPU */
/* PCLK as Source */
- #define AS3525_DBOP_DIV (CLK_DIV(AS3525_PCLK_FREQ, AS3525_DBOP_FREQ) - 1) /*div=1/(n+1)*/
- #define AS3525_I2C_PRESCALER CLK_DIV(AS3525_PCLK_FREQ, AS3525_I2C_FREQ)
- #define AS3525_I2C_FREQ 400000
- #define AS3525_SD_IDENT_DIV ((CLK_DIV(AS3525_PCLK_FREQ, AS3525_SD_IDENT_FREQ) / 2) - 1)
- #define AS3525_SD_IDENT_FREQ 400000 /* must be between 100 & 400 kHz */
- #define AS3525_SSP_PRESCALER ((CLK_DIV(AS3525_PCLK_FREQ, AS3525_SSP_FREQ) + 1) & ~1) /* must be an even number */
- #define AS3525_SSP_FREQ 12000000
+ #define AS3525_DBOP_DIV (CLK_DIV(AS3525_PCLK_FREQ, AS3525_DBOP_FREQ) - 1) /*div=1/(n+1)*/
+ #define AS3525_I2C_PRESCALER CLK_DIV(AS3525_PCLK_FREQ, AS3525_I2C_FREQ)
+ #define AS3525_I2C_PRESCALER_MAX 0xFF | 0x300 /* Max value for prescaler */
+ #define AS3525_I2C_FREQ 400000
+ #define AS3525_SD_IDENT_DIV ((CLK_DIV(AS3525_PCLK_FREQ, AS3525_SD_IDENT_FREQ) / 2) - 1)
+ #define AS3525_SD_IDENT_FREQ 400000 /* must be between 100 & 400 kHz */
+ #define AS3525_SSP_PRESCALER ((CLK_DIV(AS3525_PCLK_FREQ, AS3525_SSP_FREQ) + 1) & ~1) /* must be an even number */
+#if LCD_DEPTH > 1
+ #define AS3525_SSP_PRESCALER_MAX ((CLK_DIV(AS3525_PCLK_FREQ, AS3525_SSP_FREQ_MIN) + 1) & ~1)/* must be an even number */
+ #define AS3525_SSP_FREQ_MIN 2000000 /* 2 MHz gives a decent refresh rate on clipzip*/
+#else
+ #define AS3525_SSP_PRESCALER_MAX 0xFE & ~1 /*Max value for divider - must be an even number */
+ #define AS3525_SSP_FREQ_MIN AS3525_SSP_FREQ /* No set minimum we just use max divider */
+#endif
+ #define AS3525_SSP_FREQ 12000000
#define AS3525_IDE_SEL AS3525_CLK_PLLA /* Input Source */
#define AS3525_IDE_DIV (CLK_DIV(AS3525_PLLA_FREQ, AS3525_IDE_FREQ) - 1)/*div=1/(n+1)*/
+#define AS3525_IDE_DIV_MAX 0xF /* Max value for divider */
#if CONFIG_CPU == AS3525v2
#define AS3525_MS_FREQ 120000000
#define AS3525_MS_DIV (CLK_DIV(AS3525_PLLA_FREQ, AS3525_MS_FREQ) -1)
#define AS3525_SDSLOT_FREQ 24000000
#define AS3525_SDSLOT_DIV (CLK_DIV(AS3525_PLLA_FREQ, AS3525_SDSLOT_FREQ) -1)
+#define AS3525_SDSLOT_DIV_MAX 0xF /* Max value for divider */
#define AS3525_IDE_FREQ 80000000
#else
#define AS3525_IDE_FREQ 50000000 /* The OF uses 66MHz maximal freq */
@@ -211,6 +221,10 @@
#error SSP frequency is too low : clock divider will not fit !
#endif
+#if (((CLK_DIV(AS3525_PCLK_FREQ, AS3525_SSP_FREQ_MIN)) + 1 ) & ~1) >= (1<<8) /* 8 bits */
+#error SSP_MIN frequency is too low : clock divider will not fit !
+#endif
+
/* AS3525_SD_IDENT_FREQ */
#if ((CLK_DIV(AS3525_PCLK_FREQ, AS3525_SD_IDENT_FREQ) / 2) - 1) >= (1<<8) /* 8 bits */
#error SD IDENTIFICATION frequency is too low : clock divider will not fit !
diff --git a/firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c b/firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c
index a50a9e5c80..7c0cfb5345 100644
--- a/firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c
+++ b/firmware/target/arm/as3525/sansa-clipplus/lcd-clip-plus.c
@@ -26,11 +26,25 @@
#include "system.h"
#include "cpu.h"
+static void ssp_set_prescaler(unsigned int prescaler)
+{
+ int oldlevel = disable_interrupt_save(IRQ_FIQ_STATUS);
+ /* must be on to write regs */
+ bool ssp_enabled = bitset32(&CGU_PERI, CGU_SSP_CLOCK_ENABLE) &
+ CGU_SSP_CLOCK_ENABLE;
+ SSP_CPSR = prescaler;
+
+ if (!ssp_enabled) /* put it back how we found it */
+ bitclr32(&CGU_PERI, CGU_SSP_CLOCK_ENABLE);
+
+ restore_irq(oldlevel);
+}
+
int lcd_hw_init(void)
{
bitset32(&CGU_PERI, CGU_SSP_CLOCK_ENABLE);
- SSP_CPSR = AS3525_SSP_PRESCALER; /* OF = 0x10 */
+ ssp_set_prescaler(AS3525_SSP_PRESCALER); /* OF = 0x10 */
SSP_CR0 = (1<<7) | (1<<6) | 7; /* Motorola SPI frame format, 8 bits */
SSP_CR1 = (1<<3) | (1<<1); /* SSP Operation enabled */
SSP_IMSC = 0; /* No interrupts */
@@ -115,3 +129,10 @@ void lcd_enable_power(bool onoff)
#endif
}
+#if defined(CONFIG_POWER_SAVING) && (CONFIG_POWER_SAVING & POWERSV_DISP)
+/* declared in system-as3525.c */
+void ams_ssp_set_low_speed(bool slow)
+{
+ ssp_set_prescaler(slow ? AS3525_SSP_PRESCALER_MAX : AS3525_SSP_PRESCALER);
+}
+#endif
diff --git a/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c b/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
index e17bfc421b..8a3df517fb 100644
--- a/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
+++ b/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
@@ -35,12 +35,26 @@ static int lcd_type;
static bool lcd_enabled;
#endif
+static void ssp_set_prescaler(unsigned int prescaler)
+{
+ int oldlevel = disable_interrupt_save(IRQ_FIQ_STATUS);
+ /* must be on to write regs */
+ bool ssp_enabled = bitset32(&CGU_PERI, CGU_SSP_CLOCK_ENABLE) &
+ CGU_SSP_CLOCK_ENABLE;
+ SSP_CPSR = prescaler;
+
+ if (!ssp_enabled) /* put it back how we found it */
+ bitclr32(&CGU_PERI, CGU_SSP_CLOCK_ENABLE);
+
+ restore_irq(oldlevel);
+}
+
/* initialises the host lcd hardware, returns the lcd type */
static int lcd_hw_init(void)
{
/* configure SSP */
bitset32(&CGU_PERI, CGU_SSP_CLOCK_ENABLE);
- SSP_CPSR = 4; /* TODO: use AS3525_SSP_PRESCALER, OF uses 8 */
+ ssp_set_prescaler(AS3525_SSP_PRESCALER); /* OF = 0x8 */
SSP_CR0 = (0 << 8) | /* SCR, serial clock rate divider = 1 */
(1 << 7) | /* SPH, phase = 1 */
(1 << 6) | /* SPO, polarity = 1 */
@@ -437,3 +451,11 @@ void lcd_update(void)
{
lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
}
+
+#if defined(CONFIG_POWER_SAVING) && (CONFIG_POWER_SAVING & POWERSV_DISP)
+/* declared in system-as3525.c */
+void ams_ssp_set_low_speed(bool slow)
+{
+ ssp_set_prescaler(slow ? AS3525_SSP_PRESCALER_MAX : AS3525_SSP_PRESCALER);
+}
+#endif
diff --git a/firmware/target/arm/as3525/sd-as3525.c b/firmware/target/arm/as3525/sd-as3525.c
index 494a76a782..9e4a86acbe 100644
--- a/firmware/target/arm/as3525/sd-as3525.c
+++ b/firmware/target/arm/as3525/sd-as3525.c
@@ -89,6 +89,8 @@
| MCI_CMD_CRC_FAIL)
#define MCI_FIFO(i) ((unsigned long *) (pl180_base[i]+0x80))
+
+#define IDE_INTERFACE_CLK (1<<6) /* non AHB interface */
/* volumes */
#define INTERNAL_AS3525 0 /* embedded SD card */
#define SD_SLOT_AS3525 1 /* SD slot if present */
@@ -109,7 +111,8 @@ static void init_pl180_controller(const int drive);
static tCardInfo card_info[NUM_DRIVES];
-/* maximum timeouts recommanded in the SD Specification v2.00 */
+/* maximum timeouts recommended in the SD Specification v2.00 */
+/* MCI_DATA_TIMER register data timeout in card bus clock periods */
#define SD_MAX_READ_TIMEOUT ((AS3525_PCLK_FREQ) / 1000 * 100) /* 100 ms */
#define SD_MAX_WRITE_TIMEOUT ((AS3525_PCLK_FREQ) / 1000 * 250) /* 250 ms */
@@ -143,7 +146,17 @@ static unsigned char *uncached_buffer = AS3525_UNCACHED_ADDR(&aligned_buffer[0])
static inline void mci_delay(void) { udelay(1000) ; }
-static void enable_controller(bool on)
+static inline bool card_detect_target(void)
+{
+#if defined(HAVE_MULTIDRIVE)
+ return !(GPIOA_PIN(2));
+#else
+ return false;
+#endif
+}
+
+#if defined(HAVE_MULTIDRIVE) || defined(HAVE_HOTSWAP)
+static void enable_controller_mci(bool on)
{
#if defined(HAVE_BUTTON_LIGHT) && defined(HAVE_MULTIDRIVE)
@@ -197,17 +210,33 @@ static void enable_controller(bool on)
#endif
}
}
+#endif /* defined(HAVE_MULTIDRIVE) || defined(HAVE_HOTSWAP) */
-static inline bool card_detect_target(void)
+/* AMS v1 have two different drive interfaces MCI_SD(XPD) and GGU_IDE */
+static void enable_controller(bool on, const int drive)
{
-#if defined(HAVE_MULTIDRIVE)
- return !(GPIOA_PIN(2));
-#else
- return false;
+
+ if (drive == INTERNAL_AS3525)
+ {
+#ifndef BOOTLOADER
+ if (on)
+ {
+ bitset32(&CGU_PERI, CGU_NAF_CLOCK_ENABLE);
+ CGU_IDE |= IDE_INTERFACE_CLK; /* interface enable */
+ }
+ else
+ {
+ CGU_IDE &= ~(IDE_INTERFACE_CLK); /* interface disable */
+ bitclr32(&CGU_PERI, CGU_NAF_CLOCK_ENABLE);
+ }
+#endif
+ }
+#if defined(HAVE_MULTIDRIVE) || defined(HAVE_HOTSWAP)
+ else
+ enable_controller_mci(on);
#endif
}
-
#ifdef HAVE_HOTSWAP
static int sd1_oneshot_callback(struct timeout *tmo)
{
@@ -326,6 +355,7 @@ static bool send_cmd(const int drive, const int cmd, const int arg,
return false;
}
+/* MCI_CLOCK = MCLK / 2x(ClkDiv[bits 7:0]+1) */
#define MCI_FULLSPEED (MCI_CLOCK_ENABLE | MCI_CLOCK_BYPASS) /* MCLK */
#define MCI_HALFSPEED (MCI_CLOCK_ENABLE) /* MCLK/2 */
#define MCI_QUARTERSPEED (MCI_CLOCK_ENABLE | 1) /* MCLK/4 */
@@ -345,7 +375,7 @@ static int sd_init_card(const int drive)
/* 100 - 400kHz clock required for Identification Mode */
/* Start of Card Identification Mode ************************************/
- /* CMD0 Go Idle */
+ /* CMD0 Go Idle -- all card functions switch back to default */
if(!send_cmd(drive, SD_GO_IDLE_STATE, 0, MCI_NO_RESP, NULL))
return -1;
mci_delay();
@@ -393,10 +423,10 @@ static int sd_init_card(const int drive)
if(sd_wait_for_tran_state(drive))
return -6;
- /* CMD6 */
+ /* CMD6 0xf indicates no influence, [3:0],0x1 - HS Access*/
if(!send_cmd(drive, SD_SWITCH_FUNC, 0x80fffff1, MCI_NO_RESP, NULL))
return -7;
- sleep(HZ/10);
+ sleep(HZ/10);/* need to wait at least 8 clock periods */
/* go back to STBY state so we can read csd */
/* CMD7 w/rca=0: Deselect card to put it in STBY state */
@@ -517,7 +547,7 @@ static void init_pl180_controller(const int drive)
int sd_init(void)
{
int ret;
- CGU_IDE = (1<<6) /* enable non AHB interface*/
+ CGU_IDE = IDE_INTERFACE_CLK /* enable interface */
| (AS3525_IDE_DIV << 2)
| AS3525_CLK_PLLA; /* clock source = PLLA */
@@ -540,7 +570,9 @@ int sd_init(void)
/* init mutex */
mutex_init(&sd_mtx);
- enable_controller(false);
+ for (int i = 0; i < NUM_DRIVES ; i++)
+ enable_controller(false, i);
+
return 0;
}
@@ -656,7 +688,7 @@ static int sd_transfer_sectors(IF_MD(int drive,) unsigned long start,
unsigned long response;
bool aligned = !((uintptr_t)buf & (CACHEALIGN_SIZE - 1));
- enable_controller(true);
+ enable_controller(true, drive);
led(true);
if (card_info[drive].initialized <= 0)
@@ -692,27 +724,21 @@ static int sd_transfer_sectors(IF_MD(int drive,) unsigned long start,
else
discard_dcache_range(buf, count * SECTOR_SIZE);
}
-
- while(count)
+ const int cmd = write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK;
+ while(count > 0)
{
/* 128 * 512 = 2^16, and doesn't fit in the 16 bits of DATA_LENGTH
* register, so we have to transfer maximum 127 sectors at a time. */
unsigned int transfer = (count >= 128) ? 127 : count; /* sectors */
void *dma_buf;
- const int cmd =
- write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK;
+
unsigned long bank_start = start;
- unsigned long status;
/* Only switch banks for internal storage */
if(drive == INTERNAL_AS3525)
{
- unsigned int bank = 0;
- while(bank_start >= BLOCKS_PER_BANK)
- {
- bank_start -= BLOCKS_PER_BANK;
- bank++;
- }
+ unsigned int bank = bank_start / BLOCKS_PER_BANK;
+ bank_start -= bank * BLOCKS_PER_BANK;
/* Switch bank if needed */
if(card_info[INTERNAL_AS3525].current_bank != bank)
@@ -770,10 +796,7 @@ static int sd_transfer_sectors(IF_MD(int drive,) unsigned long start,
/*Small delay for writes prevents data crc failures at lower freqs*/
#ifdef HAVE_MULTIDRIVE
if((drive == SD_SLOT_AS3525) && !hs_card)
- {
- int write_delay = 125;
- while(write_delay--);
- }
+ udelay(4);
#endif
}
else
@@ -804,7 +827,7 @@ static int sd_transfer_sectors(IF_MD(int drive,) unsigned long start,
last_disk_activity = current_tick;
- if(!send_cmd(drive, SD_STOP_TRANSMISSION, 0, MCI_RESP, &status))
+ if(!send_cmd(drive, SD_STOP_TRANSMISSION, 0, MCI_RESP, &response))
{
ret = -4*20;
goto sd_transfer_error;
@@ -831,7 +854,7 @@ sd_transfer_error:
sd_transfer_error_nodma:
led(false);
- enable_controller(false);
+ enable_controller(false, drive);
if (ret) /* error */
card_info[drive].initialized = 0;
@@ -922,12 +945,18 @@ void ams_sd_get_debug_info(struct ams_sd_debug_info *info)
#define MCI_SD *((volatile unsigned long *)(SD_MCI_BASE + 0x04))
mutex_lock(&sd_mtx);
- enable_controller(true); /* must be on to read regs */
+
+ for (int i = 0; i < NUM_DRIVES ; i++)
+ enable_controller(true, i); /* must be on to read regs */
+
info->mci_nand = MCI_NAND;
#ifdef HAVE_MULTIDRIVE
info->mci_sd = MCI_SD;
#endif
- enable_controller(false);
+
+ for (int i = 0; i < NUM_DRIVES ; i++)
+ enable_controller(false, i);
+
mutex_unlock(&sd_mtx);
}
@@ -948,10 +977,10 @@ int sd_event(long id, intptr_t data)
if (id == SYS_HOTSWAP_INSERTED)
{
- enable_controller(true);
+ enable_controller(true, data);
init_pl180_controller(data);
rc = sd_init_card(data);
- enable_controller(false);
+ enable_controller(false, data);
}
mutex_unlock(&sd_mtx);
@@ -968,3 +997,42 @@ int sd_event(long id, intptr_t data)
return rc;
}
+
+#if defined(CONFIG_POWER_SAVING) && (CONFIG_POWER_SAVING & POWERSV_DISK)
+/* declared in system-as3525.c */
+void ams_sd_set_low_speed(bool slow)
+{
+ /* block access while speed is changed */
+ mutex_lock(&sd_mtx);
+ enable_controller(true, INTERNAL_AS3525);
+
+ /* After a data write, data cannot be written to MCI_CLOCK
+ for 3 MCLK periods + 2 PCLK periods. ~10us worst case
+ */
+ udelay(100);
+ if (slow)
+ {
+ /* only affects internal drive clock speed*/
+ CGU_IDE = (CGU_IDE & ~(0xF << 2)) | (AS3525_IDE_DIV_MAX << 2);
+ /* power save is enabled for the sd card(s) */
+ for (int i = 0; i < NUM_DRIVES ; i++)
+ {
+ if (i != INTERNAL_AS3525 && (MCI_CLOCK(i) & MCI_CLOCK_POWERSAVE) == 0)
+ MCI_CLOCK(i) |= MCI_CLOCK_POWERSAVE;
+ }
+ }
+ else
+ {
+ /* Full Speed */
+ CGU_IDE = (CGU_IDE & ~(0xF << 2)) | (AS3525_IDE_DIV << 2);
+ for (int i = 0; i < NUM_DRIVES ; i++)
+ {
+ if (i != INTERNAL_AS3525 && (MCI_CLOCK(i) & MCI_CLOCK_POWERSAVE) != 0)
+ MCI_CLOCK(i) = (MCI_CLOCK(i) & ~MCI_CLOCK_POWERSAVE);
+ }
+ }
+ enable_controller(false, INTERNAL_AS3525);
+ mutex_unlock(&sd_mtx);
+}
+#endif
+
diff --git a/firmware/target/arm/as3525/sd-as3525v2.c b/firmware/target/arm/as3525/sd-as3525v2.c
index b512cc2ea4..d27df5289c 100644
--- a/firmware/target/arm/as3525/sd-as3525v2.c
+++ b/firmware/target/arm/as3525/sd-as3525v2.c
@@ -488,7 +488,7 @@ static int sd_init_card(const int drive)
card_info[drive].initialized = 0;
card_info[drive].rca = 0;
- /* assume 24 MHz clock / 60 = 400 kHz */
+ /* assume 24 MHz clock / (2x)60 = 200 kHz */
MCI_CLKDIV = (MCI_CLKDIV & ~(0xFF)) | 0x3C; /* CLK_DIV_0 : bits 7:0 */
/* 100 - 400kHz clock required for Identification Mode */
@@ -957,3 +957,27 @@ int sd_event(long id, intptr_t data)
return rc;
}
+
+#if defined(CONFIG_POWER_SAVING) && (CONFIG_POWER_SAVING & POWERSV_DISK)
+/* declared in system-as3525.c */
+void ams_sd_set_low_speed(bool slow)
+{
+ /* block access while speed is changed */
+ mutex_lock(&sd_mtx);
+ enable_controller(true);
+ if (slow)
+ {
+ CGU_SDSLOT = (CGU_SDSLOT & ~(0xF << 2)) | (AS3525_SDSLOT_DIV_MAX << 2);
+ /* power save is enabled for the sd card(s) ASSUMES CRD0 is int drive! */
+ MCI_CLKENA |= (CCLK_LP_CRD1 | CCLK_LP_CRD2 | CCLK_LP_CRD3);
+ }
+ else
+ {
+ /* Full Speed */
+ CGU_SDSLOT = (CGU_SDSLOT & ~(0xF << 2)) | (AS3525_SDSLOT_DIV << 2);
+ MCI_CLKENA = (MCI_CLKENA & ~(CCLK_LP_CRD1 | CCLK_LP_CRD2 | CCLK_LP_CRD3));
+ }
+ enable_controller(false);
+ mutex_unlock(&sd_mtx);
+}
+#endif
diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c
index d630ef38ec..c11c90f9f3 100644
--- a/firmware/target/arm/as3525/system-as3525.c
+++ b/firmware/target/arm/as3525/system-as3525.c
@@ -52,6 +52,29 @@ struct mutex cpufreq_mtx;
#define default_interrupt(name) \
extern __attribute__((weak,alias("UIRQ"))) void name (void)
+#ifdef CONFIG_POWER_SAVING
+/* Powersave functions either manipulate the system directly
+ or pass enabled flag on to these specific functions
+ dis/enabling powersaving for the selected subsystem
+*/
+#if (CONFIG_POWER_SAVING & POWERSV_CPU)
+/*cpu_set_powersave*/
+#include "settings.h"
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISP)
+/*disp_set_powersave*/
+void ams_ssp_set_low_speed(bool slow); /*lcd-clip-plus.c & lcd-clipzip.c*/
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISK)
+/*disk_set_powersave*/
+void ams_sd_set_low_speed(bool slow); /* sd-as3525.c & sd-as3525v2.c */
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_I2C)
+/*i2c_set_powersave*/
+void ams_i2c_set_low_speed(bool slow); /* ascodec-as3525.c*/
+#endif
+#endif /*CONFIG_POWER_SAVING*/
+
#if CONFIG_USBOTG != USBOTG_DESIGNWARE
static void UIRQ (void) __attribute__((interrupt ("IRQ")));
#endif
@@ -422,6 +445,39 @@ void udelay(unsigned usecs)
);
}
+#ifdef CONFIG_POWER_SAVING
+#if (CONFIG_POWER_SAVING & POWERSV_CPU)
+void cpu_set_powersave(bool enabled)
+{
+ /*global_settings.cpu_powersave*/
+ /*handled in: set_cpu_frequency()*/
+ (void) enabled;
+}
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISK)
+void disk_set_powersave(bool enabled)
+{
+ /*global_settings.disk_powersave*/
+ ams_sd_set_low_speed(enabled);
+}
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISP)
+void disp_set_powersave(bool enabled)
+{
+ /*global_settings.disp_powersave*/
+ ams_ssp_set_low_speed(enabled);
+}
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_I2C)
+void i2c_set_powersave(bool enabled)
+{
+ /*global_settings.i2c_powersave*/
+ ams_i2c_set_low_speed(enabled);
+}
+#endif
+#endif /*defined(CONFIG_POWER_SAVING)*/
+
+
#ifndef BOOTLOADER
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
bool set_cpu_frequency__lock(void)
@@ -481,7 +537,12 @@ void set_cpu_frequency(long frequency)
CGU_PROC = ((0xf << 4) | (0x3 << 2) | AS3525_CLK_MAIN);
#ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE
- /* Decreasing frequency so reduce voltage after change */
+ /* Decreasing frequency so reduce voltage after change */
+#if defined(CONFIG_POWER_SAVING) && (CONFIG_POWER_SAVING & POWERSV_CPU)
+ if (!global_settings.cpu_powersave)
+ ascodec_write(AS3514_CVDD_DCDC3, (AS314_CP_DCDC3_SETTING | CVDD_1_15));
+ else
+#endif
ascodec_write(AS3514_CVDD_DCDC3, (AS314_CP_DCDC3_SETTING | CVDD_1_10));
#endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */
@@ -519,6 +580,13 @@ void set_cpu_frequency(long frequency)
/* Set CVDD1 power supply */
#ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE
+#if defined(CONFIG_POWER_SAVING) && (CONFIG_POWER_SAVING & POWERSV_CPU)
+ if (!global_settings.cpu_powersave)
+ {
+ ascodec_write_pmu(0x17, 1, 0x80 | 26);
+ return;
+ }
+#endif
#if defined(SANSA_CLIPZIP)
ascodec_write_pmu(0x17, 1, 0x80 | 20);
#elif defined(SANSA_CLIPPLUS)
diff --git a/firmware/target/arm/as3525/system-target.h b/firmware/target/arm/as3525/system-target.h
index 5cdc573a1b..9c5b5a9cd6 100644
--- a/firmware/target/arm/as3525/system-target.h
+++ b/firmware/target/arm/as3525/system-target.h
@@ -73,6 +73,26 @@ static inline void mdelay(unsigned msecs)
void usb_insert_int(void);
void usb_remove_int(void);
+
+#ifdef CONFIG_POWER_SAVING
+/* Powersave functions either manipulate the system directly
+ or pass enabled flag on to the devices specific function
+ dis/enabling powersaving for the selected subsystem
+*/
+#if (CONFIG_POWER_SAVING & POWERSV_CPU)
+void cpu_set_powersave(bool enabled);
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISP)
+void disp_set_powersave(bool enabled);
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_DISK)
+void disk_set_powersave(bool enabled);
+#endif
+#if (CONFIG_POWER_SAVING & POWERSV_I2C)
+void i2c_set_powersave(bool enabled);
+#endif
+#endif /*CONFIG_POWER_SAVING*/
+
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
#define CPU_BOOST_LOCK_DEFINED
diff --git a/manual/configure_rockbox/system_options.tex b/manual/configure_rockbox/system_options.tex
index ff2967cec7..b279308e71 100755
--- a/manual/configure_rockbox/system_options.tex
+++ b/manual/configure_rockbox/system_options.tex
@@ -297,6 +297,30 @@ therefore result in better runtime.
\end{description}
} %\nopt{HAS_BUTTON_HOLD}
+\opt{CONFIG_POWER_SAVING}{
+ \subsection{Power Saving}
+ These options allow users to increase runtime by lowering performance
+ of select subsystems. Certain options may \emph{not} be applicable to your
+ \dap{} or may cause undesired operation to your particular use case.
+
+ \emph{WARNING!} While every effort has been made to ensure the safety of these
+ options, due to manufacturing variance some options may cause unwanted side
+ effects, cause the \dap{} to crash or (while unlikely) even \emph{destroy}
+ your \dap{}. \emph{PROCEED WITH CAUTION}
+
+ \begin{description}
+
+ \item[CPU]
+ Allows lower voltages or cpu speed when enabled.
+ \item[Disk]
+ Allows slower disk operations when enabled.
+ \item[I2C]
+ Allows slower clocking of I2C device bus when enabled.
+ \item[Display]
+ Allows slower screen refresh rates when enabled.
+
+ \end{description}
+} %\opt{CONFIG_POWER_SAVING}
\opt{usb_hid}{
\subsection{\label{ref:USB_HID}USB HID}