diff options
Diffstat (limited to 'arch/powerpc/kernel/time.c')
| -rw-r--r-- | arch/powerpc/kernel/time.c | 97 | 
1 files changed, 62 insertions, 35 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 192b051df97..9fff9cdcc51 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -42,6 +42,7 @@  #include <linux/timex.h>  #include <linux/kernel_stat.h>  #include <linux/time.h> +#include <linux/clockchips.h>  #include <linux/init.h>  #include <linux/profile.h>  #include <linux/cpu.h> @@ -106,7 +107,7 @@ struct clock_event_device decrementer_clockevent = {  	.irq            = 0,  	.set_next_event = decrementer_set_next_event,  	.set_mode       = decrementer_set_mode, -	.features       = CLOCK_EVT_FEAT_ONESHOT, +	.features       = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP,  };  EXPORT_SYMBOL(decrementer_clockevent); @@ -213,8 +214,6 @@ static u64 scan_dispatch_log(u64 stop_tb)  	if (i == be64_to_cpu(vpa->dtl_idx))  		return 0;  	while (i < be64_to_cpu(vpa->dtl_idx)) { -		if (dtl_consumer) -			dtl_consumer(dtl, i);  		dtb = be64_to_cpu(dtl->timebase);  		tb_delta = be32_to_cpu(dtl->enqueue_to_dispatch_time) +  			be32_to_cpu(dtl->ready_to_enqueue_time); @@ -227,6 +226,8 @@ static u64 scan_dispatch_log(u64 stop_tb)  		}  		if (dtb > stop_tb)  			break; +		if (dtl_consumer) +			dtl_consumer(dtl, i);  		stolen += tb_delta;  		++i;  		++dtl; @@ -478,6 +479,47 @@ void arch_irq_work_raise(void)  #endif /* CONFIG_IRQ_WORK */ +void __timer_interrupt(void) +{ +	struct pt_regs *regs = get_irq_regs(); +	u64 *next_tb = &__get_cpu_var(decrementers_next_tb); +	struct clock_event_device *evt = &__get_cpu_var(decrementers); +	u64 now; + +	trace_timer_interrupt_entry(regs); + +	if (test_irq_work_pending()) { +		clear_irq_work_pending(); +		irq_work_run(); +	} + +	now = get_tb_or_rtc(); +	if (now >= *next_tb) { +		*next_tb = ~(u64)0; +		if (evt->event_handler) +			evt->event_handler(evt); +		__get_cpu_var(irq_stat).timer_irqs_event++; +	} else { +		now = *next_tb - now; +		if (now <= DECREMENTER_MAX) +			set_dec((int)now); +		/* We may have raced with new irq work */ +		if (test_irq_work_pending()) +			set_dec(1); +		__get_cpu_var(irq_stat).timer_irqs_others++; +	} + +#ifdef CONFIG_PPC64 +	/* collect purr register values often, for accurate calculations */ +	if (firmware_has_feature(FW_FEATURE_SPLPAR)) { +		struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array); +		cu->current_tb = mfspr(SPRN_PURR); +	} +#endif + +	trace_timer_interrupt_exit(regs); +} +  /*   * timer_interrupt - gets called when the decrementer overflows,   * with interrupts disabled. @@ -486,8 +528,6 @@ void timer_interrupt(struct pt_regs * regs)  {  	struct pt_regs *old_regs;  	u64 *next_tb = &__get_cpu_var(decrementers_next_tb); -	struct clock_event_device *evt = &__get_cpu_var(decrementers); -	u64 now;  	/* Ensure a positive value is written to the decrementer, or else  	 * some CPUs will continue to take decrementer exceptions. @@ -510,9 +550,8 @@ void timer_interrupt(struct pt_regs * regs)  	 */  	may_hard_irq_enable(); -	__get_cpu_var(irq_stat).timer_irqs++; -#if defined(CONFIG_PPC32) && defined(CONFIG_PMAC) +#if defined(CONFIG_PPC32) && defined(CONFIG_PPC_PMAC)  	if (atomic_read(&ppc_n_lost_interrupts) != 0)  		do_IRQ(regs);  #endif @@ -520,34 +559,7 @@ void timer_interrupt(struct pt_regs * regs)  	old_regs = set_irq_regs(regs);  	irq_enter(); -	trace_timer_interrupt_entry(regs); - -	if (test_irq_work_pending()) { -		clear_irq_work_pending(); -		irq_work_run(); -	} - -	now = get_tb_or_rtc(); -	if (now >= *next_tb) { -		*next_tb = ~(u64)0; -		if (evt->event_handler) -			evt->event_handler(evt); -	} else { -		now = *next_tb - now; -		if (now <= DECREMENTER_MAX) -			set_dec((int)now); -	} - -#ifdef CONFIG_PPC64 -	/* collect purr register values often, for accurate calculations */ -	if (firmware_has_feature(FW_FEATURE_SPLPAR)) { -		struct cpu_usage *cu = &__get_cpu_var(cpu_usage_array); -		cu->current_tb = mfspr(SPRN_PURR); -	} -#endif - -	trace_timer_interrupt_exit(regs); - +	__timer_interrupt();  	irq_exit();  	set_irq_regs(old_regs);  } @@ -803,6 +815,11 @@ static int decrementer_set_next_event(unsigned long evt,  {  	__get_cpu_var(decrementers_next_tb) = get_tb_or_rtc() + evt;  	set_dec(evt); + +	/* We may have raced with new irq work */ +	if (test_irq_work_pending()) +		set_dec(1); +  	return 0;  } @@ -813,6 +830,15 @@ static void decrementer_set_mode(enum clock_event_mode mode,  		decrementer_set_next_event(DECREMENTER_MAX, dev);  } +/* Interrupt handler for the timer broadcast IPI */ +void tick_broadcast_ipi_handler(void) +{ +	u64 *next_tb = &__get_cpu_var(decrementers_next_tb); + +	*next_tb = get_tb_or_rtc(); +	__timer_interrupt(); +} +  static void register_decrementer_clockevent(int cpu)  {  	struct clock_event_device *dec = &per_cpu(decrementers, cpu); @@ -916,6 +942,7 @@ void __init time_init(void)  	clocksource_init();  	init_decrementer_clockevent(); +	tick_setup_hrtimer_broadcast();  }  | 
