aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/hpet.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r--arch/x86/kernel/hpet.c148
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 */
150static unsigned long hpet_period; 150static unsigned long hpet_period;
151 151
152static void hpet_set_mode(enum clock_event_mode mode, 152static void hpet_legacy_set_mode(enum clock_event_mode mode,
153 struct clock_event_device *evt); 153 struct clock_event_device *evt);
154static int hpet_next_event(unsigned long delta, 154static 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,
160static struct clock_event_device hpet_clockevent = { 160static 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
181static void hpet_enable_int(void) 181static 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
190static void hpet_set_mode(enum clock_event_mode mode, 190static 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
222static 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
236static int hpet_next_event(unsigned long delta, 268static 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/* 308static int hpet_clocksource_register(void)
277 * Try to setup the HPET timer
278 */
279int __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 */
361int __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;