diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2008-02-04 11:32:57 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-02-04 12:52:20 -0500 |
commit | 93c2904d5081468128e66792a85439df314de773 (patch) | |
tree | 867887ca3a31efcd007015a5170d1ca2050632d0 | |
parent | a8655e83fc44ec2b92cbea9f3ff3cc0da05a991c (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>
-rw-r--r-- | arch/arm/mach-realview/localtimer.c | 94 | ||||
-rw-r--r-- | include/asm-arm/hardware/arm_twd.h | 7 |
2 files changed, 71 insertions, 30 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 | ||
44 | static unsigned long mpcore_timer_rate; | 44 | static unsigned long mpcore_timer_rate; |
45 | 45 | ||
46 | static 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 | |||
71 | static 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 | ||
64 | void __cpuinit local_timer_setup(unsigned int cpu) | 101 | static 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. | 148 | void __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 | /* |
diff --git a/include/asm-arm/hardware/arm_twd.h b/include/asm-arm/hardware/arm_twd.h index 131d5b40e072..e521b70713c8 100644 --- a/include/asm-arm/hardware/arm_twd.h +++ b/include/asm-arm/hardware/arm_twd.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __ASM_HARDWARE_TWD_H | 1 | #ifndef __ASM_HARDWARE_TWD_H |
2 | #define __ASM_HARDWARE_TWD_H | 2 | #define __ASM_HARDWARE_TWD_H |
3 | 3 | ||
4 | #define TWD_TIMER_LOAD 0x00 | 4 | #define TWD_TIMER_LOAD 0x00 |
5 | #define TWD_TIMER_COUNTER 0x04 | 5 | #define TWD_TIMER_COUNTER 0x04 |
6 | #define TWD_TIMER_CONTROL 0x08 | 6 | #define TWD_TIMER_CONTROL 0x08 |
7 | #define TWD_TIMER_INTSTAT 0x0C | 7 | #define TWD_TIMER_INTSTAT 0x0C |
@@ -13,4 +13,9 @@ | |||
13 | #define TWD_WDOG_RESETSTAT 0x30 | 13 | #define TWD_WDOG_RESETSTAT 0x30 |
14 | #define TWD_WDOG_DISABLE 0x34 | 14 | #define TWD_WDOG_DISABLE 0x34 |
15 | 15 | ||
16 | #define TWD_TIMER_CONTROL_ENABLE (1 << 0) | ||
17 | #define TWD_TIMER_CONTROL_ONESHOT (0 << 1) | ||
18 | #define TWD_TIMER_CONTROL_PERIODIC (1 << 1) | ||
19 | #define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2) | ||
20 | |||
16 | #endif | 21 | #endif |