diff options
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r-- | arch/x86/kernel/hpet.c | 148 |
1 files changed, 83 insertions, 65 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 078dbc6d80ec..ce3bcc4334d5 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
@@ -149,9 +149,9 @@ static void hpet_reserve_platform_timers(unsigned long id) { } | |||
149 | */ | 149 | */ |
150 | static unsigned long hpet_period; | 150 | static unsigned long hpet_period; |
151 | 151 | ||
152 | static void hpet_set_mode(enum clock_event_mode mode, | 152 | static void hpet_legacy_set_mode(enum clock_event_mode mode, |
153 | struct clock_event_device *evt); | 153 | struct clock_event_device *evt); |
154 | static int hpet_next_event(unsigned long delta, | 154 | static int hpet_legacy_next_event(unsigned long delta, |
155 | struct clock_event_device *evt); | 155 | struct clock_event_device *evt); |
156 | 156 | ||
157 | /* | 157 | /* |
@@ -160,8 +160,8 @@ static int hpet_next_event(unsigned long delta, | |||
160 | static struct clock_event_device hpet_clockevent = { | 160 | static struct clock_event_device hpet_clockevent = { |
161 | .name = "hpet", | 161 | .name = "hpet", |
162 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 162 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
163 | .set_mode = hpet_set_mode, | 163 | .set_mode = hpet_legacy_set_mode, |
164 | .set_next_event = hpet_next_event, | 164 | .set_next_event = hpet_legacy_next_event, |
165 | .shift = 32, | 165 | .shift = 32, |
166 | .irq = 0, | 166 | .irq = 0, |
167 | }; | 167 | }; |
@@ -178,7 +178,7 @@ static void hpet_start_counter(void) | |||
178 | hpet_writel(cfg, HPET_CFG); | 178 | hpet_writel(cfg, HPET_CFG); |
179 | } | 179 | } |
180 | 180 | ||
181 | static void hpet_enable_int(void) | 181 | static void hpet_enable_legacy_int(void) |
182 | { | 182 | { |
183 | unsigned long cfg = hpet_readl(HPET_CFG); | 183 | unsigned long cfg = hpet_readl(HPET_CFG); |
184 | 184 | ||
@@ -187,7 +187,39 @@ static void hpet_enable_int(void) | |||
187 | hpet_legacy_int_enabled = 1; | 187 | hpet_legacy_int_enabled = 1; |
188 | } | 188 | } |
189 | 189 | ||
190 | static void hpet_set_mode(enum clock_event_mode mode, | 190 | static void hpet_legacy_clockevent_register(void) |
191 | { | ||
192 | uint64_t hpet_freq; | ||
193 | |||
194 | /* Start HPET legacy interrupts */ | ||
195 | hpet_enable_legacy_int(); | ||
196 | |||
197 | /* | ||
198 | * The period is a femto seconds value. We need to calculate the | ||
199 | * scaled math multiplication factor for nanosecond to hpet tick | ||
200 | * conversion. | ||
201 | */ | ||
202 | hpet_freq = 1000000000000000ULL; | ||
203 | do_div(hpet_freq, hpet_period); | ||
204 | hpet_clockevent.mult = div_sc((unsigned long) hpet_freq, | ||
205 | NSEC_PER_SEC, 32); | ||
206 | /* Calculate the min / max delta */ | ||
207 | hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF, | ||
208 | &hpet_clockevent); | ||
209 | hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30, | ||
210 | &hpet_clockevent); | ||
211 | |||
212 | /* | ||
213 | * Start hpet with the boot cpu mask and make it | ||
214 | * global after the IO_APIC has been initialized. | ||
215 | */ | ||
216 | hpet_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); | ||
217 | clockevents_register_device(&hpet_clockevent); | ||
218 | global_clock_event = &hpet_clockevent; | ||
219 | printk(KERN_DEBUG "hpet clockevent registered\n"); | ||
220 | } | ||
221 | |||
222 | static void hpet_legacy_set_mode(enum clock_event_mode mode, | ||
191 | struct clock_event_device *evt) | 223 | struct clock_event_device *evt) |
192 | { | 224 | { |
193 | unsigned long cfg, cmp, now; | 225 | unsigned long cfg, cmp, now; |
@@ -228,12 +260,12 @@ static void hpet_set_mode(enum clock_event_mode mode, | |||
228 | break; | 260 | break; |
229 | 261 | ||
230 | case CLOCK_EVT_MODE_RESUME: | 262 | case CLOCK_EVT_MODE_RESUME: |
231 | hpet_enable_int(); | 263 | hpet_enable_legacy_int(); |
232 | break; | 264 | break; |
233 | } | 265 | } |
234 | } | 266 | } |
235 | 267 | ||
236 | static int hpet_next_event(unsigned long delta, | 268 | static int hpet_legacy_next_event(unsigned long delta, |
237 | struct clock_event_device *evt) | 269 | struct clock_event_device *evt) |
238 | { | 270 | { |
239 | unsigned long cnt; | 271 | unsigned long cnt; |
@@ -273,58 +305,11 @@ static struct clocksource clocksource_hpet = { | |||
273 | #endif | 305 | #endif |
274 | }; | 306 | }; |
275 | 307 | ||
276 | /* | 308 | static int hpet_clocksource_register(void) |
277 | * Try to setup the HPET timer | ||
278 | */ | ||
279 | int __init hpet_enable(void) | ||
280 | { | 309 | { |
281 | unsigned long id; | ||
282 | uint64_t hpet_freq; | ||
283 | u64 tmp, start, now; | 310 | u64 tmp, start, now; |
284 | cycle_t t1; | 311 | cycle_t t1; |
285 | 312 | ||
286 | if (!is_hpet_capable()) | ||
287 | return 0; | ||
288 | |||
289 | hpet_set_mapping(); | ||
290 | |||
291 | /* | ||
292 | * Read the period and check for a sane value: | ||
293 | */ | ||
294 | hpet_period = hpet_readl(HPET_PERIOD); | ||
295 | if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD) | ||
296 | goto out_nohpet; | ||
297 | |||
298 | /* | ||
299 | * The period is a femto seconds value. We need to calculate the | ||
300 | * scaled math multiplication factor for nanosecond to hpet tick | ||
301 | * conversion. | ||
302 | */ | ||
303 | hpet_freq = 1000000000000000ULL; | ||
304 | do_div(hpet_freq, hpet_period); | ||
305 | hpet_clockevent.mult = div_sc((unsigned long) hpet_freq, | ||
306 | NSEC_PER_SEC, 32); | ||
307 | /* Calculate the min / max delta */ | ||
308 | hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF, | ||
309 | &hpet_clockevent); | ||
310 | hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30, | ||
311 | &hpet_clockevent); | ||
312 | |||
313 | /* | ||
314 | * Read the HPET ID register to retrieve the IRQ routing | ||
315 | * information and the number of channels | ||
316 | */ | ||
317 | id = hpet_readl(HPET_ID); | ||
318 | |||
319 | #ifdef CONFIG_HPET_EMULATE_RTC | ||
320 | /* | ||
321 | * The legacy routing mode needs at least two channels, tick timer | ||
322 | * and the rtc emulation channel. | ||
323 | */ | ||
324 | if (!(id & HPET_ID_NUMBER)) | ||
325 | goto out_nohpet; | ||
326 | #endif | ||
327 | |||
328 | /* Start the counter */ | 313 | /* Start the counter */ |
329 | hpet_start_counter(); | 314 | hpet_start_counter(); |
330 | 315 | ||
@@ -346,7 +331,7 @@ int __init hpet_enable(void) | |||
346 | if (t1 == read_hpet()) { | 331 | if (t1 == read_hpet()) { |
347 | printk(KERN_WARNING | 332 | printk(KERN_WARNING |
348 | "HPET counter not counting. HPET disabled\n"); | 333 | "HPET counter not counting. HPET disabled\n"); |
349 | goto out_nohpet; | 334 | return -ENODEV; |
350 | } | 335 | } |
351 | 336 | ||
352 | /* Initialize and register HPET clocksource | 337 | /* Initialize and register HPET clocksource |
@@ -367,15 +352,48 @@ int __init hpet_enable(void) | |||
367 | 352 | ||
368 | clocksource_register(&clocksource_hpet); | 353 | clocksource_register(&clocksource_hpet); |
369 | 354 | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * Try to setup the HPET timer | ||
360 | */ | ||
361 | int __init hpet_enable(void) | ||
362 | { | ||
363 | unsigned long id; | ||
364 | |||
365 | if (!is_hpet_capable()) | ||
366 | return 0; | ||
367 | |||
368 | hpet_set_mapping(); | ||
369 | |||
370 | /* | ||
371 | * Read the period and check for a sane value: | ||
372 | */ | ||
373 | hpet_period = hpet_readl(HPET_PERIOD); | ||
374 | if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD) | ||
375 | goto out_nohpet; | ||
376 | |||
377 | /* | ||
378 | * Read the HPET ID register to retrieve the IRQ routing | ||
379 | * information and the number of channels | ||
380 | */ | ||
381 | id = hpet_readl(HPET_ID); | ||
382 | |||
383 | #ifdef CONFIG_HPET_EMULATE_RTC | ||
384 | /* | ||
385 | * The legacy routing mode needs at least two channels, tick timer | ||
386 | * and the rtc emulation channel. | ||
387 | */ | ||
388 | if (!(id & HPET_ID_NUMBER)) | ||
389 | goto out_nohpet; | ||
390 | #endif | ||
391 | |||
392 | if (hpet_clocksource_register()) | ||
393 | goto out_nohpet; | ||
394 | |||
370 | if (id & HPET_ID_LEGSUP) { | 395 | if (id & HPET_ID_LEGSUP) { |
371 | hpet_enable_int(); | 396 | hpet_legacy_clockevent_register(); |
372 | /* | ||
373 | * Start hpet with the boot cpu mask and make it | ||
374 | * global after the IO_APIC has been initialized. | ||
375 | */ | ||
376 | hpet_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); | ||
377 | clockevents_register_device(&hpet_clockevent); | ||
378 | global_clock_event = &hpet_clockevent; | ||
379 | return 1; | 397 | return 1; |
380 | } | 398 | } |
381 | return 0; | 399 | return 0; |