aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorCatalin Marinas <catalin.marinas@arm.com>2008-02-04 11:32:57 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2008-02-04 12:52:20 -0500
commit93c2904d5081468128e66792a85439df314de773 (patch)
tree867887ca3a31efcd007015a5170d1ca2050632d0 /arch/arm
parenta8655e83fc44ec2b92cbea9f3ff3cc0da05a991c (diff)
[ARM] 4815/1: RealView: Add clockevents suport for the local timers
This patch registers the local timers on ARM11MPCore as clock event devices. The clock device can be set up as periodic or oneshot. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-realview/localtimer.c94
1 files changed, 65 insertions, 29 deletions
diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c
index 529eb6979e61..4f87b4d09c7d 100644
--- a/arch/arm/mach-realview/localtimer.c
+++ b/arch/arm/mach-realview/localtimer.c
@@ -16,8 +16,8 @@
16#include <linux/jiffies.h> 16#include <linux/jiffies.h>
17#include <linux/percpu.h> 17#include <linux/percpu.h>
18#include <linux/clockchips.h> 18#include <linux/clockchips.h>
19#include <linux/irq.h>
19 20
20#include <asm/mach/time.h>
21#include <asm/hardware/arm_twd.h> 21#include <asm/hardware/arm_twd.h>
22#include <asm/hardware/gic.h> 22#include <asm/hardware/gic.h>
23#include <asm/hardware.h> 23#include <asm/hardware.h>
@@ -43,6 +43,43 @@ void local_timer_interrupt(void)
43 43
44static unsigned long mpcore_timer_rate; 44static unsigned long mpcore_timer_rate;
45 45
46static void local_timer_set_mode(enum clock_event_mode mode,
47 struct clock_event_device *clk)
48{
49 void __iomem *base = TWD_BASE(smp_processor_id());
50 unsigned long ctrl;
51
52 switch(mode) {
53 case CLOCK_EVT_MODE_PERIODIC:
54 /* timer load already set up */
55 ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
56 | TWD_TIMER_CONTROL_PERIODIC;
57 break;
58 case CLOCK_EVT_MODE_ONESHOT:
59 /* period set, and timer enabled in 'next_event' hook */
60 ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
61 break;
62 case CLOCK_EVT_MODE_UNUSED:
63 case CLOCK_EVT_MODE_SHUTDOWN:
64 default:
65 ctrl = 0;
66 }
67
68 __raw_writel(ctrl, base + TWD_TIMER_CONTROL);
69}
70
71static int local_timer_set_next_event(unsigned long evt,
72 struct clock_event_device *unused)
73{
74 void __iomem *base = TWD_BASE(smp_processor_id());
75 unsigned long ctrl = __raw_readl(base + TWD_TIMER_CONTROL);
76
77 __raw_writel(evt, base + TWD_TIMER_COUNTER);
78 __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, base + TWD_TIMER_CONTROL);
79
80 return 0;
81}
82
46/* 83/*
47 * local_timer_ack: checks for a local timer interrupt. 84 * local_timer_ack: checks for a local timer interrupt.
48 * 85 *
@@ -61,12 +98,11 @@ int local_timer_ack(void)
61 return 0; 98 return 0;
62} 99}
63 100
64void __cpuinit local_timer_setup(unsigned int cpu) 101static void __cpuinit twd_calibrate_rate(unsigned int cpu)
65{ 102{
66 void __iomem *base = TWD_BASE(cpu); 103 void __iomem *base = TWD_BASE(cpu);
67 unsigned int load, offset; 104 unsigned long load, count;
68 u64 waitjiffies; 105 u64 waitjiffies;
69 unsigned int count;
70 106
71 /* 107 /*
72 * If this is the first time round, we need to work out how fast 108 * If this is the first time round, we need to work out how fast
@@ -104,36 +140,36 @@ void __cpuinit local_timer_setup(unsigned int cpu)
104 load = mpcore_timer_rate / HZ; 140 load = mpcore_timer_rate / HZ;
105 141
106 __raw_writel(load, base + TWD_TIMER_LOAD); 142 __raw_writel(load, base + TWD_TIMER_LOAD);
107 __raw_writel(0x7, base + TWD_TIMER_CONTROL); 143}
108
109 /*
110 * Now maneuver our local tick into the right part of the jiffy.
111 * Start by working out where within the tick our local timer
112 * interrupt should go.
113 */
114 offset = ((mpcore_timer_rate / HZ) / (NR_CPUS + 1)) * (cpu + 1);
115 144
116 /* 145/*
117 * gettimeoffset() will return a number of us since the last tick. 146 * Setup the local clock events for a CPU.
118 * Convert this number of us to a local timer tick count. 147 */
119 * Be careful of integer overflow whilst keeping maximum precision. 148void __cpuinit local_timer_setup(unsigned int cpu)
120 * 149{
121 * with HZ=100 and 1MHz (fpga) ~ 1GHz processor: 150 struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
122 * load = 1 ~ 10,000 151 unsigned long flags;
123 * mpcore_timer_rate/10000 = 100 ~ 100,000
124 *
125 * so the multiply value will be less than 10^9 always.
126 */
127 load = (system_timer->offset() * (mpcore_timer_rate / 10000)) / 100;
128 152
129 /* Add on our offset to get the load value */ 153 twd_calibrate_rate(cpu);
130 load = (load + offset) % (mpcore_timer_rate / HZ);
131 154
132 __raw_writel(load, base + TWD_TIMER_COUNTER); 155 clk->name = "local_timer";
156 clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
157 clk->rating = 350;
158 clk->set_mode = local_timer_set_mode;
159 clk->set_next_event = local_timer_set_next_event;
160 clk->irq = IRQ_LOCALTIMER;
161 clk->cpumask = cpumask_of_cpu(cpu);
162 clk->shift = 20;
163 clk->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, clk->shift);
164 clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk);
165 clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
133 166
134 /* Make sure our local interrupt controller has this enabled */ 167 /* Make sure our local interrupt controller has this enabled */
135 __raw_writel(1 << IRQ_LOCALTIMER, 168 local_irq_save(flags);
136 __io_address(REALVIEW_GIC_DIST_BASE) + GIC_DIST_ENABLE_SET); 169 get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
170 local_irq_restore(flags);
171
172 clockevents_register_device(clk);
137} 173}
138 174
139/* 175/*