#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
};
+