Hi,
Since there's been a little bit of discussion about 13MHz operation
on the IRC channel here's a patch I've been playing with to switch
the system in and out of 13MHz operation when idle.
It works for me (saves about 200mW on a Compulab board) but I have done
no analysis of RAM timings etc. A similar patch to add 13Mhz as a
cpufreq stepping left my system stuck at 13MHz and I havn't worked out
why yet.
--- linux-2.6.git/arch/arm/mach-pxa/pxa27x.c.orig 2007-10-29 13:06:25.000000000 +0000
+++ linux-2.6.git/arch/arm/mach-pxa/pxa27x.c 2007-10-29 13:08:53.000000000 +0000
@@ -25,6 +25,9 @@
#include <asm/arch/pm.h>
#include <asm/arch/dma.h>
+#include <asm/arch/system.h>
+#include <asm/mach/time.h>
+
#include "generic.h"
#include "devices.h"
#include "clock.h"
@@ -32,6 +35,10 @@
/* Crystal clock: 13MHz */
#define BASE_CLK 13000000
+#define CCLKCFG_FCS 0x2
+#define CCCR_CPDIS 0x80000000 /* Disable CPU PLL */
+
+
/*
* Get the clock frequency as reflected by CCSR and the turbo flag.
* We assume these values have been applied via a fcs.
@@ -43,6 +50,13 @@
unsigned int l, L, m, M, n2, N, S;
int cccr_a, t, ht, b;
+ if ( CCCR & CCCR_CPDIS )
+ {
+ if (info)
+ printk( KERN_INFO "CPU PLL disabled (13MHz operation)\n");
+ return BASE_CLK / 1000;
+ }
+
ccsr = CCSR;
cccr_a = CCCR & (1 << 25);
@@ -312,6 +326,61 @@
}
#endif
+/*
+ * Try to save power by entering 13MHz operation during the idle loop
+ */
+void pxa27x_enter_13M(void)
+{
+ unsigned int cclkcfg;
+ unsigned int unused;
+
+ /* Disable CPU PLL */
+ CCCR |= CCCR_CPDIS;
+
+ /* Set the frequency change flag */
+ __asm__ __volatile__ ( "mrc p14, 0, %0, c6, c0, 0" : "=r" (cclkcfg) );
+ cclkcfg |= CCLKCFG_FCS;
+ __asm__ __volatile__ ( "mcr p14, 0, %1, c6, c0, 0" : "=&r" (unused)
+ : "r" (cclkcfg) );
+}
+
+void pxa27x_leave_13M(void)
+{
+ unsigned int cclkcfg;
+ unsigned int unused;
+
+ /* Enable CPU PLL */
+ CCCR &= ~CCCR_CPDIS;
+
+ /* Set the frequency change flag */
+ __asm__ __volatile__ ( "mrc p14, 0, %0, c6, c0, 0" : "=r" (cclkcfg) );
+ cclkcfg |= CCLKCFG_FCS;
+ __asm__ __volatile__ ( "mcr p14, 0, %1, c6, c0, 0" : "=&r" (unused)
+ : "r" (cclkcfg) );
+}
+
+static void pxa27x_idle(void)
+{
+ local_irq_disable();
+ if ( CCCR & CCCR_CPDIS )
+ {
+ if (!need_resched()) {
+ timer_dyn_reprogram();
+ arch_idle();
+ }
+ }
+ else
+ {
+ pxa27x_enter_13M();
+ if (!need_resched()) {
+ timer_dyn_reprogram();
+ arch_idle();
+ }
+ pxa27x_leave_13M();
+ }
+ local_irq_enable();
+}
+
/* PXA27x: Various gpios can issue wakeup events. This logic only
* handles the simple cases, not the WEMUX2 and WEMUX3 options
*/
@@ -455,6 +524,8 @@
#ifdef CONFIG_PM
pxa27x_init_pm();
#endif
+ pm_idle = pxa27x_idle;
+
ret = platform_add_devices(devices, ARRAY_SIZE(devices));
}
return ret;
--
Bob Dunlop
Guralp Systems Limited
http://www.guralp.com