TIMER: clock source use timer3, clock event use timer2
author黄涛 <huangtao@rock-chips.com>
Mon, 14 Jun 2010 07:22:53 +0000 (07:22 +0000)
committer黄涛 <huangtao@rock-chips.com>
Mon, 21 Jun 2010 05:35:27 +0000 (13:35 +0800)
arch/arm/mach-rk2818/timer.c

index 1a9ad50a2e6b7c4129c7acf0ff9f5b4bb3ee2f37..2caa74d4c90efd227655e620c9a5ee5e4c12c58e 100644 (file)
@@ -19,8 +19,8 @@
 #include <linux/irq.h>
 #include <linux/clk.h>
 #include <linux/clockchips.h>
-#include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/cpufreq.h>
 
 #include <asm/mach/time.h>
 #include <mach/rk2818_iomap.h>
 #define RK2818_TIMER1_BASE      RK2818_TIMER_BASE
 #define RK2818_TIMER2_BASE      RK2818_TIMER_BASE + 0x14
 #define RK2818_TIMER3_BASE      RK2818_TIMER_BASE + 0x28
-#define TIMER_LOAD_COUNT               0x0000
-#define TIMER_CUR_VALUE                        0x0004
-#define TIMER_CONTROL_REG              0x0008
-#define TIMER_EOI                      0x000C
-#define TIMER_INT_STATUS               0x0010
-
-#define TIMER_MATCH_VAL         0x0000
-#define TIMER_COUNT_VAL         0x0004
-#define TIMER_ENABLE            0x0008
-#define TIMER_ENABLE_CLR_ON_MATCH_EN    2
-#define TIMER_ENABLE_EN                 3
-#define TIMER_CLEAR             0x000C
-
-#define CSR_PROTECTION          0x0020
-#define CSR_PROTECTION_EN               1
-
-#define TIMER_HZ       24000000 
-#define timer_cycle    (TIMER_HZ+HZ/2)/HZ
-uint32_t       tcount;
-uint32_t       mycycles = 0;
-static int     pit_cnt=0;
-struct rk2818_clock {
-       struct clock_event_device   clockevent;
-       struct clocksource          clocksource;
-       struct irqaction            irq;
-       uint32_t                    regbase;
-       uint32_t                    freq;
-       uint32_t                    shift;
+
+#define TIMER_LOAD_COUNT       0x0000
+#define TIMER_CUR_VALUE                0x0004
+#define TIMER_CONTROL_REG      0x0008
+#define TIMER_EOI              0x000C
+#define TIMER_INT_STATUS       0x0010
+
+#define TIMER_DISABLE                  4
+#define TIMER_ENABLE                   3
+#define TIMER_ENABLE_FREE_RUNNING      1
+
+static struct clk *timer_clk;
+static volatile unsigned long timer_mult;
+
+static int rk2818_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt)
+{
+       writel(TIMER_DISABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+       writel(cycles * timer_mult, RK2818_TIMER2_BASE + TIMER_LOAD_COUNT);
+       writel(TIMER_ENABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+
+       return 0;
+}
+
+static void rk2818_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt)
+{
+       switch (mode) {
+       case CLOCK_EVT_MODE_RESUME:
+       case CLOCK_EVT_MODE_PERIODIC:
+       case CLOCK_EVT_MODE_ONESHOT:
+               break;
+       case CLOCK_EVT_MODE_UNUSED:
+       case CLOCK_EVT_MODE_SHUTDOWN:
+               writel(TIMER_DISABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+               break;
+       }
+}
+
+static struct clock_event_device clockenent_timer2 = {
+       .name           = "timer2",
+       .features       = CLOCK_EVT_FEAT_ONESHOT,
+       .shift          = 32,
+       .rating         = 200,
+       .set_next_event = rk2818_timer_set_next_event,
+       .set_mode       = rk2818_timer_set_mode,
 };
 
-static irqreturn_t rk2818_timer_interrupt(int irq, void *dev_id)
+static irqreturn_t rk2818_timer2_interrupt(int irq, void *dev_id)
 {
        struct clock_event_device *evt = dev_id;
-       struct rk2818_clock *clock = container_of(evt, struct rk2818_clock, clockevent);
-       readl(clock->regbase + TIMER_EOI);
-       pit_cnt +=mycycles;
+
+       readl(RK2818_TIMER2_BASE + TIMER_EOI);
+       writel(TIMER_DISABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+
        evt->event_handler(evt);
+
        return IRQ_HANDLED;
 }
 
-static cycle_t rk2818_timer_read(struct clocksource *cs)
+static struct irqaction rk2818_timer2_irq = {
+       .name           = "timer2",
+       .flags          = IRQF_DISABLED | IRQF_TIMER,
+       .handler        = rk2818_timer2_interrupt,
+       .irq            = IRQ_NR_TIMER2,
+       .dev_id         = &clockenent_timer2,
+};
+
+static int rk2818_timer_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data)
 {
+       if (val == CPUFREQ_POSTCHANGE) {
+               timer_mult = clk_get_rate(timer_clk) / 1000000;
+       }
 
-       unsigned int elapsed;
-       unsigned int t;
+       return 0;
+}
 
-       t = readl(RK2818_TIMER3_BASE + TIMER_LOAD_COUNT);
+static struct notifier_block rk2818_timer_cpufreq_notifier_block = {
+       .notifier_call  = rk2818_timer_cpufreq_notifier,
+       .priority       = 0x7ffffff,
+};
 
-       elapsed = __raw_readl(RK2818_TIMER3_BASE + TIMER_CUR_VALUE);
-       
-       elapsed = t - elapsed;
-       elapsed += pit_cnt;
-    return elapsed;
+static __init int rk2818_timer_init_cpufreq(void)
+{
+       cpufreq_register_notifier(&rk2818_timer_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
+       return 0;
 }
 
-static int rk2818_timer_set_next_event(unsigned long cycles,
-                                   struct clock_event_device *evt)
+arch_initcall_sync(rk2818_timer_init_cpufreq);
+
+static __init int rk2818_timer_init_clockevent(void)
 {
-       
-       struct rk2818_clock *clock = container_of(evt, struct rk2818_clock, clockevent);
+       struct clock_event_device *ce = &clockenent_timer2;
+
+       timer_clk = clk_get(NULL, "timer");
+       timer_mult = clk_get_rate(timer_clk) / 1000000;
+
+       writel(TIMER_DISABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+
+       setup_irq(rk2818_timer2_irq.irq, &rk2818_timer2_irq);
+
+       ce->mult = div_sc(1000000, NSEC_PER_SEC, ce->shift);
+       ce->max_delta_ns = clockevent_delta2ns(0xFFFFFFFFUL / 256, ce); // max pclk < 256MHz
+       ce->min_delta_ns = clockevent_delta2ns(1, ce);
+
+       ce->cpumask = cpumask_of(0);
+
+       clockevents_register_device(ce);
 
-    writel(4, clock->regbase + TIMER_CONTROL_REG);
-       mycycles = cycles;
-    writel(cycles, clock->regbase + TIMER_LOAD_COUNT);
-    writel(TIMER_ENABLE_EN, clock->regbase + TIMER_CONTROL_REG);
        return 0;
 }
 
-static void rk2818_timer_set_mode(enum clock_event_mode mode,
-                             struct clock_event_device *evt)
+static cycle_t rk2818_timer3_read(struct clocksource *cs)
 {
-       struct rk2818_clock *clock = container_of(evt, struct rk2818_clock, clockevent);
-       printk("%s::Enter--mode is %d\n",__FUNCTION__,mode);
-       switch (mode) {
-       case CLOCK_EVT_MODE_RESUME:
-       case CLOCK_EVT_MODE_PERIODIC:
-               break;
-       case CLOCK_EVT_MODE_ONESHOT:
-               readl(clock->regbase+TIMER_EOI);
-               writel((TIMER_HZ+ HZ/2) / HZ,clock->regbase+TIMER_LOAD_COUNT);
-               writel(TIMER_ENABLE_EN, clock->regbase + TIMER_CONTROL_REG);
-               break;
-       case CLOCK_EVT_MODE_UNUSED:
-       case CLOCK_EVT_MODE_SHUTDOWN:
-               writel(4, clock->regbase + TIMER_CONTROL_REG);
-               break;
-       }
+       return ~readl(RK2818_TIMER3_BASE + TIMER_CUR_VALUE);
 }
 
-static struct rk2818_clock rk2818_system_clocks[] = {
-       {
-               .clockevent = {
-                       .name           = "timer",
-                       .features       = CLOCK_EVT_FEAT_ONESHOT,
-                       .shift          = 32,
-                       .rating         = 200,
-                       .set_next_event = rk2818_timer_set_next_event,
-                       .set_mode       = rk2818_timer_set_mode,
-               },
-               .clocksource = {
-                       .name           = "timer",
-                       .rating         = 200,
-                       .read           = rk2818_timer_read,
-                       .mask           = CLOCKSOURCE_MASK(24),
-                       .shift          = 26,
-                       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
-               },
-               .irq = {
-                       .name    = "timer",
-                       .flags   = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING,
-                       .handler = rk2818_timer_interrupt,
-                       .dev_id  = &rk2818_system_clocks[0].clockevent,
-                       .irq     = IRQ_NR_TIMER3 
-               },
-               .regbase = RK2818_TIMER3_BASE,
-               .freq = TIMER_HZ
-       },
+static struct clocksource clocksource_timer3 = {
+       .name           = "timer3",
+       .rating         = 200,
+       .read           = rk2818_timer3_read,
+       .mask           = CLOCKSOURCE_MASK(32),
+       .shift          = 26,
+       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
+static void __init rk2818_timer_init_clocksource(void)
+{
+       static char err[] __initdata = KERN_ERR "%s: can't register clocksource!\n";
+       struct clocksource *cs = &clocksource_timer3;
+
+       writel(TIMER_DISABLE, RK2818_TIMER3_BASE + TIMER_CONTROL_REG);
+       writel(0xFFFFFFFF, RK2818_TIMER3_BASE + TIMER_LOAD_COUNT);
+       writel(TIMER_ENABLE_FREE_RUNNING, RK2818_TIMER3_BASE + TIMER_CONTROL_REG);
+
+       cs->mult = clocksource_hz2mult(24000000, cs->shift);
+       if (clocksource_register(cs))
+               printk(err, cs->name);
+}
+
 static void __init rk2818_timer_init(void)
 {
-       int i;
-       int res;
-               
-       for (i = 0; i < ARRAY_SIZE(rk2818_system_clocks); i++) {
-               struct rk2818_clock *clock = &rk2818_system_clocks[i];
-               struct clock_event_device *ce = &clock->clockevent;
-               struct clocksource *cs = &clock->clocksource;
-               
-               printk("%s::Enter %d\n",__FUNCTION__,i+1);              
-               writel((TIMER_HZ+ HZ/2) / HZ,clock->regbase+TIMER_LOAD_COUNT);
-
-               writel(0x04, clock->regbase + TIMER_CONTROL_REG);
-               
-               ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift);
-               /* allow at least 10 seconds to notice that the timer wrapped */
-               ce->max_delta_ns =
-                       clockevent_delta2ns(0xf0000000 >> clock->shift, ce);
-               /* 4 gets rounded down to 3 */
-               ce->min_delta_ns = clockevent_delta2ns(4, ce);
-               ce->cpumask = cpumask_of(0);
-
-               cs->mult = clocksource_hz2mult(clock->freq, cs->shift);
-               printk("mult is %x\n",cs->mult);
-               res = clocksource_register(cs);
-               if (res)
-                       printk(KERN_ERR "rk2818_timer_init: clocksource_register "
-                              "failed for %s\n", cs->name);
-               printk("%s::irq is %d\n",__FUNCTION__,clock->irq.irq);
-               res = setup_irq(clock->irq.irq, &clock->irq);
-               if (res)
-                       printk(KERN_ERR "rk2818_timer_init: setup_irq "
-                              "failed for %s\n", cs->name);
-
-               clockevents_register_device(ce);
-       }
+       rk2818_timer_init_clocksource();
+       rk2818_timer_init_clockevent();
 }
 
 struct sys_timer rk2818_timer = {
        .init = rk2818_timer_init
 };
+