diff options
Diffstat (limited to 'arch/powerpc/kernel/time.c')
-rw-r--r-- | arch/powerpc/kernel/time.c | 90 |
1 files changed, 54 insertions, 36 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index b3dab20acf34..122a580f7322 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <linux/timex.h> | 42 | #include <linux/timex.h> |
43 | #include <linux/kernel_stat.h> | 43 | #include <linux/kernel_stat.h> |
44 | #include <linux/time.h> | 44 | #include <linux/time.h> |
45 | #include <linux/clockchips.h> | ||
45 | #include <linux/init.h> | 46 | #include <linux/init.h> |
46 | #include <linux/profile.h> | 47 | #include <linux/profile.h> |
47 | #include <linux/cpu.h> | 48 | #include <linux/cpu.h> |
@@ -106,7 +107,7 @@ struct clock_event_device decrementer_clockevent = { | |||
106 | .irq = 0, | 107 | .irq = 0, |
107 | .set_next_event = decrementer_set_next_event, | 108 | .set_next_event = decrementer_set_next_event, |
108 | .set_mode = decrementer_set_mode, | 109 | .set_mode = decrementer_set_mode, |
109 | .features = CLOCK_EVT_FEAT_ONESHOT, | 110 | .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP, |
110 | }; | 111 | }; |
111 | EXPORT_SYMBOL(decrementer_clockevent); | 112 | EXPORT_SYMBOL(decrementer_clockevent); |
112 | 113 | ||
@@ -478,6 +479,47 @@ void arch_irq_work_raise(void) | |||
478 | 479 | ||
479 | #endif /* CONFIG_IRQ_WORK */ | 480 | #endif /* CONFIG_IRQ_WORK */ |
480 | 481 | ||
482 | void __timer_interrupt(void) | ||
483 | { | ||
484 | struct pt_regs *regs = get_irq_regs(); | ||
485 | u64 *next_tb = &__get_cpu_var(decrementers_next_tb); | ||
486 | struct clock_event_device *evt = &__get_cpu_var(decrementers); | ||
487 | u64 now; | ||
488 | |||
489 | trace_timer_interrupt_entry(regs); | ||
490 | |||
491 | if (test_irq_work_pending()) { | ||
492 | clear_irq_work_pending(); | ||
493 | irq_work_run(); | ||
494 | } | ||
495 | |||
496 | now = get_tb_or_rtc(); | ||
497 | if (now >= *next_tb) { | ||
498 | *next_tb = ~(u64)0; | ||
499 | if (evt->event_handler) | ||
500 | evt->event_handler(evt); | ||
501 | __get_cpu_var(irq_stat).timer_irqs_event++; | ||
502 | } else { | ||
503 | now = *next_tb - now; | ||
504 | if (now <= DECREMENTER_MAX) | ||
505 | set_dec((int)now); | ||
506 | /* We may have raced with new irq work */ | ||
507 | if (test_irq_work_pending()) | ||
508 | set_dec(1); | ||
509 | __get_cpu_var(irq_stat).timer_irqs_others++; | ||
510 | } | ||
511 | |||
512 | #ifdef CONFIG_PPC64 | ||
513 | /* collect purr register values often, for accurate calculations */ | ||
514 | if (firmware_has_feature(FW_FEATURE_SPLPAR)) { | ||
515 | struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array); | ||
516 | cu->current_tb = mfspr(SPRN_PURR); | ||
517 | } | ||
518 | #endif | ||
519 | |||
520 | trace_timer_interrupt_exit(regs); | ||
521 | } | ||
522 | |||
481 | /* | 523 | /* |
482 | * timer_interrupt - gets called when the decrementer overflows, | 524 | * timer_interrupt - gets called when the decrementer overflows, |
483 | * with interrupts disabled. | 525 | * with interrupts disabled. |
@@ -486,8 +528,6 @@ void timer_interrupt(struct pt_regs * regs) | |||
486 | { | 528 | { |
487 | struct pt_regs *old_regs; | 529 | struct pt_regs *old_regs; |
488 | u64 *next_tb = &__get_cpu_var(decrementers_next_tb); | 530 | u64 *next_tb = &__get_cpu_var(decrementers_next_tb); |
489 | struct clock_event_device *evt = &__get_cpu_var(decrementers); | ||
490 | u64 now; | ||
491 | 531 | ||
492 | /* Ensure a positive value is written to the decrementer, or else | 532 | /* Ensure a positive value is written to the decrementer, or else |
493 | * some CPUs will continue to take decrementer exceptions. | 533 | * some CPUs will continue to take decrementer exceptions. |
@@ -519,39 +559,7 @@ void timer_interrupt(struct pt_regs * regs) | |||
519 | old_regs = set_irq_regs(regs); | 559 | old_regs = set_irq_regs(regs); |
520 | irq_enter(); | 560 | irq_enter(); |
521 | 561 | ||
522 | trace_timer_interrupt_entry(regs); | 562 | __timer_interrupt(); |
523 | |||
524 | if (test_irq_work_pending()) { | ||
525 | clear_irq_work_pending(); | ||
526 | irq_work_run(); | ||
527 | } | ||
528 | |||
529 | now = get_tb_or_rtc(); | ||
530 | if (now >= *next_tb) { | ||
531 | *next_tb = ~(u64)0; | ||
532 | if (evt->event_handler) | ||
533 | evt->event_handler(evt); | ||
534 | __get_cpu_var(irq_stat).timer_irqs_event++; | ||
535 | } else { | ||
536 | now = *next_tb - now; | ||
537 | if (now <= DECREMENTER_MAX) | ||
538 | set_dec((int)now); | ||
539 | /* We may have raced with new irq work */ | ||
540 | if (test_irq_work_pending()) | ||
541 | set_dec(1); | ||
542 | __get_cpu_var(irq_stat).timer_irqs_others++; | ||
543 | } | ||
544 | |||
545 | #ifdef CONFIG_PPC64 | ||
546 | /* collect purr register values often, for accurate calculations */ | ||
547 | if (firmware_has_feature(FW_FEATURE_SPLPAR)) { | ||
548 | struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array); | ||
549 | cu->current_tb = mfspr(SPRN_PURR); | ||
550 | } | ||
551 | #endif | ||
552 | |||
553 | trace_timer_interrupt_exit(regs); | ||
554 | |||
555 | irq_exit(); | 563 | irq_exit(); |
556 | set_irq_regs(old_regs); | 564 | set_irq_regs(old_regs); |
557 | } | 565 | } |
@@ -825,6 +833,15 @@ static void decrementer_set_mode(enum clock_event_mode mode, | |||
825 | decrementer_set_next_event(DECREMENTER_MAX, dev); | 833 | decrementer_set_next_event(DECREMENTER_MAX, dev); |
826 | } | 834 | } |
827 | 835 | ||
836 | /* Interrupt handler for the timer broadcast IPI */ | ||
837 | void tick_broadcast_ipi_handler(void) | ||
838 | { | ||
839 | u64 *next_tb = &__get_cpu_var(decrementers_next_tb); | ||
840 | |||
841 | *next_tb = get_tb_or_rtc(); | ||
842 | __timer_interrupt(); | ||
843 | } | ||
844 | |||
828 | static void register_decrementer_clockevent(int cpu) | 845 | static void register_decrementer_clockevent(int cpu) |
829 | { | 846 | { |
830 | struct clock_event_device *dec = &per_cpu(decrementers, cpu); | 847 | struct clock_event_device *dec = &per_cpu(decrementers, cpu); |
@@ -928,6 +945,7 @@ void __init time_init(void) | |||
928 | clocksource_init(); | 945 | clocksource_init(); |
929 | 946 | ||
930 | init_decrementer_clockevent(); | 947 | init_decrementer_clockevent(); |
948 | tick_setup_hrtimer_broadcast(); | ||
931 | } | 949 | } |
932 | 950 | ||
933 | 951 | ||