aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-realview/localtimer.c94
-rw-r--r--include/asm-arm/hardware/arm_twd.h7
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
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/*
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