diff options
author | Jeff Ohlstein <johlstei@codeaurora.org> | 2010-12-02 15:05:12 -0500 |
---|---|---|
committer | David Brown <davidb@codeaurora.org> | 2011-01-07 18:54:44 -0500 |
commit | 94790ec25fdd51dc4126cc176f2e104f80f87fcb (patch) | |
tree | 6df4ba7d9e71459817b3a4d39b9094ac63698f6c /arch/arm/mach-msm/timer.c | |
parent | 7b181446c68768e2f3231a0885095ee41261dcde (diff) |
msm: timer: SMP timer support for msm
The msm provides timer hardware that is private to each core. Each
timer has separate counter and match registers, so we create separate
clock_event_devices for each core. For the global clocksource, use
cpu 0's counter.
Signed-off-by: Jeff Ohlstein <johlstei@codeaurora.org>
Signed-off-by: David Brown <davidb@codeaurora.org>
Diffstat (limited to 'arch/arm/mach-msm/timer.c')
-rw-r--r-- | arch/arm/mach-msm/timer.c | 125 |
1 files changed, 102 insertions, 23 deletions
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 595be7fea31a..c105d28b53e3 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c | |||
@@ -47,6 +47,19 @@ enum { | |||
47 | 47 | ||
48 | #define GPT_HZ 32768 | 48 | #define GPT_HZ 32768 |
49 | 49 | ||
50 | enum timer_location { | ||
51 | LOCAL_TIMER = 0, | ||
52 | GLOBAL_TIMER = 1, | ||
53 | }; | ||
54 | |||
55 | #ifdef MSM_TMR0_BASE | ||
56 | #define MSM_TMR_GLOBAL (MSM_TMR0_BASE - MSM_TMR_BASE) | ||
57 | #else | ||
58 | #define MSM_TMR_GLOBAL 0 | ||
59 | #endif | ||
60 | |||
61 | #define MSM_GLOBAL_TIMER MSM_CLOCK_DGT | ||
62 | |||
50 | #if defined(CONFIG_ARCH_QSD8X50) | 63 | #if defined(CONFIG_ARCH_QSD8X50) |
51 | #define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */ | 64 | #define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */ |
52 | #define MSM_DGT_SHIFT (0) | 65 | #define MSM_DGT_SHIFT (0) |
@@ -65,49 +78,67 @@ struct msm_clock { | |||
65 | void __iomem *regbase; | 78 | void __iomem *regbase; |
66 | uint32_t freq; | 79 | uint32_t freq; |
67 | uint32_t shift; | 80 | uint32_t shift; |
81 | void __iomem *global_counter; | ||
82 | void __iomem *local_counter; | ||
83 | }; | ||
84 | |||
85 | enum { | ||
86 | MSM_CLOCK_GPT, | ||
87 | MSM_CLOCK_DGT, | ||
88 | NR_TIMERS, | ||
68 | }; | 89 | }; |
69 | 90 | ||
91 | |||
92 | static struct msm_clock msm_clocks[]; | ||
93 | static struct clock_event_device *local_clock_event; | ||
94 | |||
70 | static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) | 95 | static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) |
71 | { | 96 | { |
72 | struct clock_event_device *evt = dev_id; | 97 | struct clock_event_device *evt = dev_id; |
98 | if (smp_processor_id() != 0) | ||
99 | evt = local_clock_event; | ||
100 | if (evt->event_handler == NULL) | ||
101 | return IRQ_HANDLED; | ||
73 | evt->event_handler(evt); | 102 | evt->event_handler(evt); |
74 | return IRQ_HANDLED; | 103 | return IRQ_HANDLED; |
75 | } | 104 | } |
76 | 105 | ||
77 | static cycle_t msm_gpt_read(struct clocksource *cs) | 106 | static cycle_t msm_read_timer_count(struct clocksource *cs) |
78 | { | 107 | { |
79 | return readl(MSM_GPT_BASE + TIMER_COUNT_VAL); | 108 | struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource); |
109 | |||
110 | return readl(clk->global_counter); | ||
80 | } | 111 | } |
81 | 112 | ||
82 | static cycle_t msm_dgt_read(struct clocksource *cs) | 113 | static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt) |
83 | { | 114 | { |
84 | return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT; | 115 | #ifdef CONFIG_SMP |
116 | int i; | ||
117 | for (i = 0; i < NR_TIMERS; i++) | ||
118 | if (evt == &(msm_clocks[i].clockevent)) | ||
119 | return &msm_clocks[i]; | ||
120 | return &msm_clocks[MSM_GLOBAL_TIMER]; | ||
121 | #else | ||
122 | return container_of(evt, struct msm_clock, clockevent); | ||
123 | #endif | ||
85 | } | 124 | } |
86 | 125 | ||
87 | static int msm_timer_set_next_event(unsigned long cycles, | 126 | static int msm_timer_set_next_event(unsigned long cycles, |
88 | struct clock_event_device *evt) | 127 | struct clock_event_device *evt) |
89 | { | 128 | { |
90 | struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); | 129 | struct msm_clock *clock = clockevent_to_clock(evt); |
91 | uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL); | 130 | uint32_t now = readl(clock->local_counter); |
92 | uint32_t alarm = now + (cycles << clock->shift); | 131 | uint32_t alarm = now + (cycles << clock->shift); |
93 | int late; | ||
94 | 132 | ||
95 | writel(alarm, clock->regbase + TIMER_MATCH_VAL); | 133 | writel(alarm, clock->regbase + TIMER_MATCH_VAL); |
96 | now = readl(clock->regbase + TIMER_COUNT_VAL); | ||
97 | late = now - alarm; | ||
98 | if (late >= (-2 << clock->shift) && late < DGT_HZ*5) { | ||
99 | printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, " | ||
100 | "alarm already expired, now %x, alarm %x, late %d\n", | ||
101 | cycles, clock->clockevent.name, now, alarm, late); | ||
102 | return -ETIME; | ||
103 | } | ||
104 | return 0; | 134 | return 0; |
105 | } | 135 | } |
106 | 136 | ||
107 | static void msm_timer_set_mode(enum clock_event_mode mode, | 137 | static void msm_timer_set_mode(enum clock_event_mode mode, |
108 | struct clock_event_device *evt) | 138 | struct clock_event_device *evt) |
109 | { | 139 | { |
110 | struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); | 140 | struct msm_clock *clock = clockevent_to_clock(evt); |
141 | |||
111 | switch (mode) { | 142 | switch (mode) { |
112 | case CLOCK_EVT_MODE_RESUME: | 143 | case CLOCK_EVT_MODE_RESUME: |
113 | case CLOCK_EVT_MODE_PERIODIC: | 144 | case CLOCK_EVT_MODE_PERIODIC: |
@@ -123,7 +154,7 @@ static void msm_timer_set_mode(enum clock_event_mode mode, | |||
123 | } | 154 | } |
124 | 155 | ||
125 | static struct msm_clock msm_clocks[] = { | 156 | static struct msm_clock msm_clocks[] = { |
126 | { | 157 | [MSM_CLOCK_GPT] = { |
127 | .clockevent = { | 158 | .clockevent = { |
128 | .name = "gp_timer", | 159 | .name = "gp_timer", |
129 | .features = CLOCK_EVT_FEAT_ONESHOT, | 160 | .features = CLOCK_EVT_FEAT_ONESHOT, |
@@ -135,7 +166,7 @@ static struct msm_clock msm_clocks[] = { | |||
135 | .clocksource = { | 166 | .clocksource = { |
136 | .name = "gp_timer", | 167 | .name = "gp_timer", |
137 | .rating = 200, | 168 | .rating = 200, |
138 | .read = msm_gpt_read, | 169 | .read = msm_read_timer_count, |
139 | .mask = CLOCKSOURCE_MASK(32), | 170 | .mask = CLOCKSOURCE_MASK(32), |
140 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 171 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
141 | }, | 172 | }, |
@@ -147,9 +178,12 @@ static struct msm_clock msm_clocks[] = { | |||
147 | .irq = INT_GP_TIMER_EXP | 178 | .irq = INT_GP_TIMER_EXP |
148 | }, | 179 | }, |
149 | .regbase = MSM_GPT_BASE, | 180 | .regbase = MSM_GPT_BASE, |
150 | .freq = GPT_HZ | 181 | .freq = GPT_HZ, |
182 | .local_counter = MSM_GPT_BASE + TIMER_COUNT_VAL, | ||
183 | .global_counter = MSM_GPT_BASE + TIMER_COUNT_VAL + | ||
184 | MSM_TMR_GLOBAL, | ||
151 | }, | 185 | }, |
152 | { | 186 | [MSM_CLOCK_DGT] = { |
153 | .clockevent = { | 187 | .clockevent = { |
154 | .name = "dg_timer", | 188 | .name = "dg_timer", |
155 | .features = CLOCK_EVT_FEAT_ONESHOT, | 189 | .features = CLOCK_EVT_FEAT_ONESHOT, |
@@ -161,7 +195,7 @@ static struct msm_clock msm_clocks[] = { | |||
161 | .clocksource = { | 195 | .clocksource = { |
162 | .name = "dg_timer", | 196 | .name = "dg_timer", |
163 | .rating = 300, | 197 | .rating = 300, |
164 | .read = msm_dgt_read, | 198 | .read = msm_read_timer_count, |
165 | .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), | 199 | .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), |
166 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 200 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
167 | }, | 201 | }, |
@@ -174,7 +208,10 @@ static struct msm_clock msm_clocks[] = { | |||
174 | }, | 208 | }, |
175 | .regbase = MSM_DGT_BASE, | 209 | .regbase = MSM_DGT_BASE, |
176 | .freq = DGT_HZ >> MSM_DGT_SHIFT, | 210 | .freq = DGT_HZ >> MSM_DGT_SHIFT, |
177 | .shift = MSM_DGT_SHIFT | 211 | .shift = MSM_DGT_SHIFT, |
212 | .local_counter = MSM_DGT_BASE + TIMER_COUNT_VAL, | ||
213 | .global_counter = MSM_DGT_BASE + TIMER_COUNT_VAL + | ||
214 | MSM_TMR_GLOBAL, | ||
178 | } | 215 | } |
179 | }; | 216 | }; |
180 | 217 | ||
@@ -183,7 +220,7 @@ static void __init msm_timer_init(void) | |||
183 | int i; | 220 | int i; |
184 | int res; | 221 | int res; |
185 | 222 | ||
186 | #ifdef CONFIG_ARCH_MSM8X60 | 223 | #ifdef CONFIG_ARCH_MSM_SCORPIONMP |
187 | writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); | 224 | writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); |
188 | #endif | 225 | #endif |
189 | 226 | ||
@@ -217,6 +254,48 @@ static void __init msm_timer_init(void) | |||
217 | } | 254 | } |
218 | } | 255 | } |
219 | 256 | ||
257 | #ifdef CONFIG_SMP | ||
258 | void __cpuinit local_timer_setup(struct clock_event_device *evt) | ||
259 | { | ||
260 | struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER]; | ||
261 | |||
262 | /* Use existing clock_event for cpu 0 */ | ||
263 | if (!smp_processor_id()) | ||
264 | return; | ||
265 | |||
266 | writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); | ||
267 | |||
268 | if (!local_clock_event) { | ||
269 | writel(0, clock->regbase + TIMER_ENABLE); | ||
270 | writel(0, clock->regbase + TIMER_CLEAR); | ||
271 | writel(~0, clock->regbase + TIMER_MATCH_VAL); | ||
272 | } | ||
273 | evt->irq = clock->irq.irq; | ||
274 | evt->name = "local_timer"; | ||
275 | evt->features = CLOCK_EVT_FEAT_ONESHOT; | ||
276 | evt->rating = clock->clockevent.rating; | ||
277 | evt->set_mode = msm_timer_set_mode; | ||
278 | evt->set_next_event = msm_timer_set_next_event; | ||
279 | evt->shift = clock->clockevent.shift; | ||
280 | evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift); | ||
281 | evt->max_delta_ns = | ||
282 | clockevent_delta2ns(0xf0000000 >> clock->shift, evt); | ||
283 | evt->min_delta_ns = clockevent_delta2ns(4, evt); | ||
284 | |||
285 | local_clock_event = evt; | ||
286 | |||
287 | gic_enable_ppi(clock->irq.irq); | ||
288 | |||
289 | clockevents_register_device(evt); | ||
290 | } | ||
291 | |||
292 | inline int local_timer_ack(void) | ||
293 | { | ||
294 | return 1; | ||
295 | } | ||
296 | |||
297 | #endif | ||
298 | |||
220 | struct sys_timer msm_timer = { | 299 | struct sys_timer msm_timer = { |
221 | .init = msm_timer_init | 300 | .init = msm_timer_init |
222 | }; | 301 | }; |