[PATCH 4/9] Experimental S3C2410A cpufreq driver (timer)

Cesar Eduardo Barros cesarb at cesarb.net
Wed Feb 13 02:24:32 CET 2008


This is the cpufreq notifier for the S3C2410 timer code.

Signed-off-by: Cesar Eduardo Barros <cesarb at cesarb.net>
---
 arch/arm/plat-s3c24xx/time.c |  130 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 130 insertions(+), 0 deletions(-)

diff --git a/arch/arm/plat-s3c24xx/time.c b/arch/arm/plat-s3c24xx/time.c
index 2ec1daa..2d8e575 100644
--- a/arch/arm/plat-s3c24xx/time.c
+++ b/arch/arm/plat-s3c24xx/time.c
@@ -25,6 +25,7 @@
 #include <linux/irq.h>
 #include <linux/err.h>
 #include <linux/clk.h>
+#include <linux/cpufreq.h>
 
 #include <asm/system.h>
 #include <asm/leds.h>
@@ -148,6 +149,132 @@ static struct irqaction s3c2410_timer_irq = {
 	machine_is_anubis()	|| \
 	machine_is_osiris() )
 
+#ifdef CONFIG_CPU_FREQ
+
+static unsigned long old_pclk;
+
+static inline void s3c2410_timer_cpufreq_setup(unsigned long pclk)
+{
+	old_pclk = pclk;
+}
+
+static int s3c2410_timer_cpufreq_notifier(struct notifier_block *nb,
+		unsigned long val, void *data __maybe_unused)
+{
+	struct clk *clk;
+	unsigned long pclk, tcnt, tval;
+	unsigned long tcon;
+	unsigned long flags;
+	bool waszero;
+
+	BUG_ON(!old_pclk);
+
+	if (!(val == CPUFREQ_POSTCHANGE
+			|| val == CPUFREQ_SUSPENDCHANGE
+			|| val == CPUFREQ_RESUMECHANGE))
+		return 0;
+
+	clk = clk_get(NULL, "timers");
+	BUG_ON(!clk);
+	pclk = clk_get_rate(clk);
+	clk_put(clk);
+
+	/* So nobody else plays with S3C2410_TCON. */
+	local_irq_save(flags);
+	tcon = __raw_readl(S3C2410_TCON);
+
+	/* Pause the timer so it will not wrap under us. */
+	tcon &= ~S3C2410_TCON_T4START;
+	__raw_writel(tcon, S3C2410_TCON);
+
+	/* Adjust the remaining ticks. */
+	tval = __raw_readl(S3C2410_TCNTO(4));
+	waszero = tval == 0;
+	tval = cpufreq_scale(tval, old_pclk, pclk);
+	/* Do not miss the interrupt on the 1->0 transition. */
+	if (!tval && !waszero)
+		tval = 1;
+
+	if (unlikely(tval > 0xffff)) {
+		/* Shouldn't happen. */
+		printk(KERN_CRIT
+			"s3c2410_timer: timer current counter overflow\n");
+		tval = 0xffff;
+	}
+
+	tcon |= S3C2410_TCON_T4RELOAD | S3C2410_TCON_T4MANUALUPD;
+	__raw_writel(tval, S3C2410_TCNTB(4));
+	__raw_writel(tcon, S3C2410_TCON);
+
+	/* Adjust the reload value. */
+	tcnt = (pclk / 6) / HZ;
+	tcnt--;
+
+	if (unlikely(tcnt > 0xffff)) {
+		/* Shouldn't happen. */
+		printk(KERN_CRIT
+			"s3c2410_timer: timer reload counter overflow\n");
+		tcnt = 0xffff;
+	}
+	timer_startval = tcnt;
+
+	tcon &= ~S3C2410_TCON_T4MANUALUPD;
+	__raw_writel(tcon, S3C2410_TCON);
+	__raw_writel(tcnt, S3C2410_TCNTB(4));
+	__raw_writel(tcnt, S3C2410_TCMPB(4));
+
+	/* Restart the timer. */
+	tcon |= S3C2410_TCON_T4START;
+	__raw_writel(tcon, S3C2410_TCON);
+
+	/* Save the current pclk for later use. */
+	old_pclk = pclk;
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static struct notifier_block s3c2410_timer_cpufreq_notifier_block = {
+	.notifier_call	= s3c2410_timer_cpufreq_notifier,
+	.priority	= 10,	/* slight boost so it runs before the rest */
+};
+
+static bool s3c2410_timer_enabled __initdata = false;
+
+static inline void __init s3c2410_timer_cpufreq_notifier_setup(void)
+{
+	if (use_tclk1_12())
+		return;
+
+	s3c2410_timer_enabled = true;
+}
+
+/* Must be called late in initialization (later than pure_initcall). */
+static int __init s3c2410_timer_cpufreq_notifier_initcall(void)
+{
+	if (!s3c2410_timer_enabled)
+		return 0;
+
+	return cpufreq_register_notifier(&s3c2410_timer_cpufreq_notifier_block,
+			CPUFREQ_TRANSITION_NOTIFIER);
+}
+device_initcall(s3c2410_timer_cpufreq_notifier_initcall);
+
+#else
+
+static inline void s3c2410_timer_cpufreq_setup(
+		unsigned long pclk __maybe_unused)
+{
+}
+
+static inline int __init s3c2410_timer_cpufreq_notifier_setup()
+{
+	return 0;
+}
+
+#endif
+
 /*
  * Set up timer interrupt, and return the current time in seconds.
  *
@@ -212,6 +339,8 @@ static void s3c2410_timer_setup (void)
 		tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT;
 
 		tcnt = (pclk / 6) / HZ;
+
+		s3c2410_timer_cpufreq_setup(pclk);
 	}
 
 	/* timers reload after counting zero, so reduce the count by 1 */
@@ -252,6 +381,7 @@ static void s3c2410_timer_setup (void)
 static void __init s3c2410_timer_init (void)
 {
 	s3c2410_timer_setup();
+	s3c2410_timer_cpufreq_notifier_setup();
 	setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
 }
 
-- 
1.5.4





More information about the openmoko-kernel mailing list