diff options
author | Richard Henderson <rth@twiddle.net> | 2013-07-14 13:57:34 -0400 |
---|---|---|
committer | Matt Turner <mattst88@gmail.com> | 2013-11-16 19:33:19 -0500 |
commit | a1659d6d128a7e0c2985bce7c957b66af1f71181 (patch) | |
tree | 9f53e3fc589023cde0a573a23855535225848146 /arch | |
parent | db2d3260617ae8c9076ef12e6de06bd5b3d82cd3 (diff) |
alpha: Switch to GENERIC_CLOCKEVENTS
This allows us to get rid of some hacky code for SMP. Get rid of
some cycle counter hackery that's now handled by generic code via
clocksource + clock_event_device objects.
Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/alpha/Kconfig | 1 | ||||
-rw-r--r-- | arch/alpha/kernel/irq_alpha.c | 14 | ||||
-rw-r--r-- | arch/alpha/kernel/proto.h | 2 | ||||
-rw-r--r-- | arch/alpha/kernel/smp.c | 33 | ||||
-rw-r--r-- | arch/alpha/kernel/time.c | 112 |
5 files changed, 53 insertions, 109 deletions
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 5d863d171b94..d39dc9b95a2c 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig | |||
@@ -16,6 +16,7 @@ config ALPHA | |||
16 | select ARCH_WANT_IPC_PARSE_VERSION | 16 | select ARCH_WANT_IPC_PARSE_VERSION |
17 | select ARCH_HAVE_NMI_SAFE_CMPXCHG | 17 | select ARCH_HAVE_NMI_SAFE_CMPXCHG |
18 | select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE | 18 | select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE |
19 | select GENERIC_CLOCKEVENTS | ||
19 | select GENERIC_SMP_IDLE_THREAD | 20 | select GENERIC_SMP_IDLE_THREAD |
20 | select GENERIC_STRNCPY_FROM_USER | 21 | select GENERIC_STRNCPY_FROM_USER |
21 | select GENERIC_STRNLEN_USER | 22 | select GENERIC_STRNLEN_USER |
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c index 28e4429596f3..6990ddc0fbaf 100644 --- a/arch/alpha/kernel/irq_alpha.c +++ b/arch/alpha/kernel/irq_alpha.c | |||
@@ -66,21 +66,7 @@ do_entInt(unsigned long type, unsigned long vector, | |||
66 | break; | 66 | break; |
67 | case 1: | 67 | case 1: |
68 | old_regs = set_irq_regs(regs); | 68 | old_regs = set_irq_regs(regs); |
69 | #ifdef CONFIG_SMP | ||
70 | { | ||
71 | long cpu; | ||
72 | |||
73 | smp_percpu_timer_interrupt(regs); | ||
74 | cpu = smp_processor_id(); | ||
75 | if (cpu != boot_cpuid) { | ||
76 | kstat_incr_irqs_this_cpu(RTC_IRQ, irq_to_desc(RTC_IRQ)); | ||
77 | } else { | ||
78 | handle_irq(RTC_IRQ); | ||
79 | } | ||
80 | } | ||
81 | #else | ||
82 | handle_irq(RTC_IRQ); | 69 | handle_irq(RTC_IRQ); |
83 | #endif | ||
84 | set_irq_regs(old_regs); | 70 | set_irq_regs(old_regs); |
85 | return; | 71 | return; |
86 | case 2: | 72 | case 2: |
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 3b250fa5f2c1..bc806893afe0 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h | |||
@@ -135,13 +135,13 @@ extern void unregister_srm_console(void); | |||
135 | /* smp.c */ | 135 | /* smp.c */ |
136 | extern void setup_smp(void); | 136 | extern void setup_smp(void); |
137 | extern void handle_ipi(struct pt_regs *); | 137 | extern void handle_ipi(struct pt_regs *); |
138 | extern void smp_percpu_timer_interrupt(struct pt_regs *); | ||
139 | 138 | ||
140 | /* bios32.c */ | 139 | /* bios32.c */ |
141 | /* extern void reset_for_srm(void); */ | 140 | /* extern void reset_for_srm(void); */ |
142 | 141 | ||
143 | /* time.c */ | 142 | /* time.c */ |
144 | extern irqreturn_t timer_interrupt(int irq, void *dev); | 143 | extern irqreturn_t timer_interrupt(int irq, void *dev); |
144 | extern void init_clockevent(void); | ||
145 | extern void common_init_rtc(void); | 145 | extern void common_init_rtc(void); |
146 | extern unsigned long est_cycle_freq; | 146 | extern unsigned long est_cycle_freq; |
147 | 147 | ||
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 9dbbcb3b9146..99ac36d5de4e 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c | |||
@@ -138,9 +138,11 @@ smp_callin(void) | |||
138 | 138 | ||
139 | /* Get our local ticker going. */ | 139 | /* Get our local ticker going. */ |
140 | smp_setup_percpu_timer(cpuid); | 140 | smp_setup_percpu_timer(cpuid); |
141 | init_clockevent(); | ||
141 | 142 | ||
142 | /* Call platform-specific callin, if specified */ | 143 | /* Call platform-specific callin, if specified */ |
143 | if (alpha_mv.smp_callin) alpha_mv.smp_callin(); | 144 | if (alpha_mv.smp_callin) |
145 | alpha_mv.smp_callin(); | ||
144 | 146 | ||
145 | /* All kernel threads share the same mm context. */ | 147 | /* All kernel threads share the same mm context. */ |
146 | atomic_inc(&init_mm.mm_count); | 148 | atomic_inc(&init_mm.mm_count); |
@@ -498,35 +500,6 @@ smp_cpus_done(unsigned int max_cpus) | |||
498 | ((bogosum + 2500) / (5000/HZ)) % 100); | 500 | ((bogosum + 2500) / (5000/HZ)) % 100); |
499 | } | 501 | } |
500 | 502 | ||
501 | |||
502 | void | ||
503 | smp_percpu_timer_interrupt(struct pt_regs *regs) | ||
504 | { | ||
505 | struct pt_regs *old_regs; | ||
506 | int cpu = smp_processor_id(); | ||
507 | unsigned long user = user_mode(regs); | ||
508 | struct cpuinfo_alpha *data = &cpu_data[cpu]; | ||
509 | |||
510 | old_regs = set_irq_regs(regs); | ||
511 | |||
512 | /* Record kernel PC. */ | ||
513 | profile_tick(CPU_PROFILING); | ||
514 | |||
515 | if (!--data->prof_counter) { | ||
516 | /* We need to make like a normal interrupt -- otherwise | ||
517 | timer interrupts ignore the global interrupt lock, | ||
518 | which would be a Bad Thing. */ | ||
519 | irq_enter(); | ||
520 | |||
521 | update_process_times(user); | ||
522 | |||
523 | data->prof_counter = data->prof_multiplier; | ||
524 | |||
525 | irq_exit(); | ||
526 | } | ||
527 | set_irq_regs(old_regs); | ||
528 | } | ||
529 | |||
530 | int | 503 | int |
531 | setup_profiling_timer(unsigned int multiplier) | 504 | setup_profiling_timer(unsigned int multiplier) |
532 | { | 505 | { |
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index 0d72e2df4b0e..08ff3f502a76 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include <linux/time.h> | 42 | #include <linux/time.h> |
43 | #include <linux/timex.h> | 43 | #include <linux/timex.h> |
44 | #include <linux/clocksource.h> | 44 | #include <linux/clocksource.h> |
45 | #include <linux/clockchips.h> | ||
45 | 46 | ||
46 | #include "proto.h" | 47 | #include "proto.h" |
47 | #include "irq_impl.h" | 48 | #include "irq_impl.h" |
@@ -49,25 +50,6 @@ | |||
49 | DEFINE_SPINLOCK(rtc_lock); | 50 | DEFINE_SPINLOCK(rtc_lock); |
50 | EXPORT_SYMBOL(rtc_lock); | 51 | EXPORT_SYMBOL(rtc_lock); |
51 | 52 | ||
52 | #define TICK_SIZE (tick_nsec / 1000) | ||
53 | |||
54 | /* | ||
55 | * Shift amount by which scaled_ticks_per_cycle is scaled. Shifting | ||
56 | * by 48 gives us 16 bits for HZ while keeping the accuracy good even | ||
57 | * for large CPU clock rates. | ||
58 | */ | ||
59 | #define FIX_SHIFT 48 | ||
60 | |||
61 | /* lump static variables together for more efficient access: */ | ||
62 | static struct { | ||
63 | /* cycle counter last time it got invoked */ | ||
64 | __u32 last_time; | ||
65 | /* ticks/cycle * 2^48 */ | ||
66 | unsigned long scaled_ticks_per_cycle; | ||
67 | /* partial unused tick */ | ||
68 | unsigned long partial_tick; | ||
69 | } state; | ||
70 | |||
71 | unsigned long est_cycle_freq; | 53 | unsigned long est_cycle_freq; |
72 | 54 | ||
73 | #ifdef CONFIG_IRQ_WORK | 55 | #ifdef CONFIG_IRQ_WORK |
@@ -96,49 +78,64 @@ static inline __u32 rpcc(void) | |||
96 | return __builtin_alpha_rpcc(); | 78 | return __builtin_alpha_rpcc(); |
97 | } | 79 | } |
98 | 80 | ||
81 | |||
82 | |||
99 | /* | 83 | /* |
100 | * timer_interrupt() needs to keep up the real-time clock, | 84 | * The RTC as a clock_event_device primitive. |
101 | * as well as call the "xtime_update()" routine every clocktick | ||
102 | */ | 85 | */ |
103 | irqreturn_t timer_interrupt(int irq, void *dev) | ||
104 | { | ||
105 | unsigned long delta; | ||
106 | __u32 now; | ||
107 | long nticks; | ||
108 | 86 | ||
109 | #ifndef CONFIG_SMP | 87 | static DEFINE_PER_CPU(struct clock_event_device, cpu_ce); |
110 | /* Not SMP, do kernel PC profiling here. */ | ||
111 | profile_tick(CPU_PROFILING); | ||
112 | #endif | ||
113 | 88 | ||
114 | /* | 89 | irqreturn_t |
115 | * Calculate how many ticks have passed since the last update, | 90 | timer_interrupt(int irq, void *dev) |
116 | * including any previous partial leftover. Save any resulting | 91 | { |
117 | * fraction for the next pass. | 92 | int cpu = smp_processor_id(); |
118 | */ | 93 | struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); |
119 | now = rpcc(); | ||
120 | delta = now - state.last_time; | ||
121 | state.last_time = now; | ||
122 | delta = delta * state.scaled_ticks_per_cycle + state.partial_tick; | ||
123 | state.partial_tick = delta & ((1UL << FIX_SHIFT) - 1); | ||
124 | nticks = delta >> FIX_SHIFT; | ||
125 | 94 | ||
126 | if (nticks) | 95 | /* Don't run the hook for UNUSED or SHUTDOWN. */ |
127 | xtime_update(nticks); | 96 | if (likely(ce->mode == CLOCK_EVT_MODE_PERIODIC)) |
97 | ce->event_handler(ce); | ||
128 | 98 | ||
129 | if (test_irq_work_pending()) { | 99 | if (test_irq_work_pending()) { |
130 | clear_irq_work_pending(); | 100 | clear_irq_work_pending(); |
131 | irq_work_run(); | 101 | irq_work_run(); |
132 | } | 102 | } |
133 | 103 | ||
134 | #ifndef CONFIG_SMP | ||
135 | while (nticks--) | ||
136 | update_process_times(user_mode(get_irq_regs())); | ||
137 | #endif | ||
138 | |||
139 | return IRQ_HANDLED; | 104 | return IRQ_HANDLED; |
140 | } | 105 | } |
141 | 106 | ||
107 | static void | ||
108 | rtc_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce) | ||
109 | { | ||
110 | /* The mode member of CE is updated in generic code. | ||
111 | Since we only support periodic events, nothing to do. */ | ||
112 | } | ||
113 | |||
114 | static int | ||
115 | rtc_ce_set_next_event(unsigned long evt, struct clock_event_device *ce) | ||
116 | { | ||
117 | /* This hook is for oneshot mode, which we don't support. */ | ||
118 | return -EINVAL; | ||
119 | } | ||
120 | |||
121 | void __init | ||
122 | init_clockevent(void) | ||
123 | { | ||
124 | int cpu = smp_processor_id(); | ||
125 | struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); | ||
126 | |||
127 | *ce = (struct clock_event_device){ | ||
128 | .name = "rtc", | ||
129 | .features = CLOCK_EVT_FEAT_PERIODIC, | ||
130 | .rating = 100, | ||
131 | .cpumask = cpumask_of(cpu), | ||
132 | .set_mode = rtc_ce_set_mode, | ||
133 | .set_next_event = rtc_ce_set_next_event, | ||
134 | }; | ||
135 | |||
136 | clockevents_config_and_register(ce, CONFIG_HZ, 0, 0); | ||
137 | } | ||
138 | |||
142 | void __init | 139 | void __init |
143 | common_init_rtc(void) | 140 | common_init_rtc(void) |
144 | { | 141 | { |
@@ -372,22 +369,9 @@ time_init(void) | |||
372 | clocksource_register_hz(&clocksource_rpcc, cycle_freq); | 369 | clocksource_register_hz(&clocksource_rpcc, cycle_freq); |
373 | #endif | 370 | #endif |
374 | 371 | ||
375 | /* From John Bowman <bowman@math.ualberta.ca>: allow the values | ||
376 | to settle, as the Update-In-Progress bit going low isn't good | ||
377 | enough on some hardware. 2ms is our guess; we haven't found | ||
378 | bogomips yet, but this is close on a 500Mhz box. */ | ||
379 | __delay(1000000); | ||
380 | |||
381 | if (HZ > (1<<16)) { | ||
382 | extern void __you_loose (void); | ||
383 | __you_loose(); | ||
384 | } | ||
385 | |||
386 | state.last_time = cc1; | ||
387 | state.scaled_ticks_per_cycle | ||
388 | = ((unsigned long) HZ << FIX_SHIFT) / cycle_freq; | ||
389 | state.partial_tick = 0L; | ||
390 | |||
391 | /* Startup the timer source. */ | 372 | /* Startup the timer source. */ |
392 | alpha_mv.init_rtc(); | 373 | alpha_mv.init_rtc(); |
374 | |||
375 | /* Start up the clock event device. */ | ||
376 | init_clockevent(); | ||
393 | } | 377 | } |