diff options
author | Jesper Nilsson <jesper.nilsson@axis.com> | 2010-07-30 11:33:07 -0400 |
---|---|---|
committer | Jesper Nilsson <jesper.nilsson@axis.com> | 2010-08-04 06:58:55 -0400 |
commit | 60dbd6633178a8625ed71329da0167c6d50c559c (patch) | |
tree | bcbc1004dfe356ce7cf30f183725dbbc9dad3e4f /arch | |
parent | 26bfeea38a4a5daf52c8f01c986ca8680bf1f6a1 (diff) |
CRIS: GENERIC_TIME fixes
GENERIC_TIME was not functional for CRIS, giving random backward
time jumps.
For CRISv32 implement a new clocksource using the free running counter
and ditch the arch_gettimeoffset.
The random time jumps still existed, but turned out to be the write_seqlock
which was missing around our do_timer() call.
So switch over to GENERIC_TIME using the clocksource for CRISv32.
CRISv10 doesn't have the free running counter needed for the
clocksource trick, but we can still use GENERIC_TIME with
arch_gettimeoffset.
Unfortunately, there were problems in using the prescaler register
to timer0 for the gettimeoffset calculation, so it is now ignored,
making our resolution worse by the tune of 40usec (0.4%) worst case.
At the same time, clean up some formatting and use NSEC_PER_SEC
instead of 1000000000.
Signed-off-by: Jesper Nilsson <jesper.nilsson@axis.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/cris/Kconfig | 5 | ||||
-rw-r--r-- | arch/cris/arch-v10/kernel/time.c | 54 | ||||
-rw-r--r-- | arch/cris/arch-v32/kernel/time.c | 85 | ||||
-rw-r--r-- | arch/cris/kernel/time.c | 7 |
4 files changed, 45 insertions, 106 deletions
diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index e25bf4440b51..4827c72b9634 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig | |||
@@ -27,7 +27,7 @@ config GENERIC_CMOS_UPDATE | |||
27 | def_bool y | 27 | def_bool y |
28 | 28 | ||
29 | config ARCH_USES_GETTIMEOFFSET | 29 | config ARCH_USES_GETTIMEOFFSET |
30 | def_bool y | 30 | def_bool n |
31 | 31 | ||
32 | config GENERIC_IOMAP | 32 | config GENERIC_IOMAP |
33 | bool | 33 | bool |
@@ -131,16 +131,19 @@ choice | |||
131 | 131 | ||
132 | config ETRAX100LX | 132 | config ETRAX100LX |
133 | bool "ETRAX-100LX-v1" | 133 | bool "ETRAX-100LX-v1" |
134 | select ARCH_USES_GETTIMEOFFSET | ||
134 | help | 135 | help |
135 | Support version 1 of the ETRAX 100LX. | 136 | Support version 1 of the ETRAX 100LX. |
136 | 137 | ||
137 | config ETRAX100LX_V2 | 138 | config ETRAX100LX_V2 |
138 | bool "ETRAX-100LX-v2" | 139 | bool "ETRAX-100LX-v2" |
140 | select ARCH_USES_GETTIMEOFFSET | ||
139 | help | 141 | help |
140 | Support version 2 of the ETRAX 100LX. | 142 | Support version 2 of the ETRAX 100LX. |
141 | 143 | ||
142 | config SVINTO_SIM | 144 | config SVINTO_SIM |
143 | bool "ETRAX-100LX-for-xsim-simulator" | 145 | bool "ETRAX-100LX-for-xsim-simulator" |
146 | select ARCH_USES_GETTIMEOFFSET | ||
144 | help | 147 | help |
145 | Support the xsim ETRAX Simulator. | 148 | Support the xsim ETRAX Simulator. |
146 | 149 | ||
diff --git a/arch/cris/arch-v10/kernel/time.c b/arch/cris/arch-v10/kernel/time.c index 30adae594aef..00eb36f8debf 100644 --- a/arch/cris/arch-v10/kernel/time.c +++ b/arch/cris/arch-v10/kernel/time.c | |||
@@ -61,66 +61,16 @@ unsigned long get_ns_in_jiffie(void) | |||
61 | 61 | ||
62 | unsigned long do_slow_gettimeoffset(void) | 62 | unsigned long do_slow_gettimeoffset(void) |
63 | { | 63 | { |
64 | unsigned long count, t1; | 64 | unsigned long count; |
65 | unsigned long usec_count = 0; | ||
66 | unsigned short presc_count; | ||
67 | |||
68 | static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */ | ||
69 | static unsigned long jiffies_p = 0; | ||
70 | |||
71 | /* | ||
72 | * cache volatile jiffies temporarily; we have IRQs turned off. | ||
73 | */ | ||
74 | unsigned long jiffies_t; | ||
75 | 65 | ||
76 | /* The timer interrupt comes from Etrax timer 0. In order to get | 66 | /* The timer interrupt comes from Etrax timer 0. In order to get |
77 | * better precision, we check the current value. It might have | 67 | * better precision, we check the current value. It might have |
78 | * underflowed already though. | 68 | * underflowed already though. |
79 | */ | 69 | */ |
80 | |||
81 | #ifndef CONFIG_SVINTO_SIM | ||
82 | /* Not available in the xsim simulator. */ | ||
83 | count = *R_TIMER0_DATA; | 70 | count = *R_TIMER0_DATA; |
84 | presc_count = *R_TIM_PRESC_STATUS; | ||
85 | /* presc_count might be wrapped */ | ||
86 | t1 = *R_TIMER0_DATA; | ||
87 | if (count != t1){ | ||
88 | /* it wrapped, read prescaler again... */ | ||
89 | presc_count = *R_TIM_PRESC_STATUS; | ||
90 | count = t1; | ||
91 | } | ||
92 | #else | ||
93 | count = 0; | ||
94 | presc_count = 0; | ||
95 | #endif | ||
96 | |||
97 | jiffies_t = jiffies; | ||
98 | 71 | ||
99 | /* | ||
100 | * avoiding timer inconsistencies (they are rare, but they happen)... | ||
101 | * there are one problem that must be avoided here: | ||
102 | * 1. the timer counter underflows | ||
103 | */ | ||
104 | if( jiffies_t == jiffies_p ) { | ||
105 | if( count > count_p ) { | ||
106 | /* Timer wrapped, use new count and prescale | ||
107 | * increase the time corresponding to one jiffie | ||
108 | */ | ||
109 | usec_count = 1000000/HZ; | ||
110 | } | ||
111 | } else | ||
112 | jiffies_p = jiffies_t; | ||
113 | count_p = count; | ||
114 | if (presc_count >= PRESCALE_VALUE/2 ){ | ||
115 | presc_count = PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2; | ||
116 | } else { | ||
117 | presc_count = PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2; | ||
118 | } | ||
119 | /* Convert timer value to usec */ | 72 | /* Convert timer value to usec */ |
120 | usec_count += ( (TIMER0_DIV - count) * (1000000/HZ)/TIMER0_DIV ) + | 73 | return (TIMER0_DIV - count) * ((NSEC_PER_SEC/1000)/HZ)/TIMER0_DIV; |
121 | (( (presc_count) * (1000000000/PRESCALE_FREQ))/1000); | ||
122 | |||
123 | return usec_count; | ||
124 | } | 74 | } |
125 | 75 | ||
126 | /* Excerpt from the Etrax100 HSDD about the built-in watchdog: | 76 | /* Excerpt from the Etrax100 HSDD about the built-in watchdog: |
diff --git a/arch/cris/arch-v32/kernel/time.c b/arch/cris/arch-v32/kernel/time.c index 1ee0e1010228..a545211e999d 100644 --- a/arch/cris/arch-v32/kernel/time.c +++ b/arch/cris/arch-v32/kernel/time.c | |||
@@ -1,13 +1,13 @@ | |||
1 | /* | 1 | /* |
2 | * linux/arch/cris/arch-v32/kernel/time.c | 2 | * linux/arch/cris/arch-v32/kernel/time.c |
3 | * | 3 | * |
4 | * Copyright (C) 2003-2007 Axis Communications AB | 4 | * Copyright (C) 2003-2010 Axis Communications AB |
5 | * | 5 | * |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #include <linux/timex.h> | 8 | #include <linux/timex.h> |
9 | #include <linux/time.h> | 9 | #include <linux/time.h> |
10 | #include <linux/jiffies.h> | 10 | #include <linux/clocksource.h> |
11 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
12 | #include <linux/swap.h> | 12 | #include <linux/swap.h> |
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
@@ -36,6 +36,30 @@ | |||
36 | /* Number of 763 counts before watchdog bites */ | 36 | /* Number of 763 counts before watchdog bites */ |
37 | #define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) | 37 | #define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) |
38 | 38 | ||
39 | /* Register the continuos readonly timer available in FS and ARTPEC-3. */ | ||
40 | static cycle_t read_cont_rotime(struct clocksource *cs) | ||
41 | { | ||
42 | return (u32)REG_RD(timer, regi_timer0, r_time); | ||
43 | } | ||
44 | |||
45 | static struct clocksource cont_rotime = { | ||
46 | .name = "crisv32_rotime", | ||
47 | .rating = 300, | ||
48 | .read = read_cont_rotime, | ||
49 | .mask = CLOCKSOURCE_MASK(32), | ||
50 | .shift = 10, | ||
51 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
52 | }; | ||
53 | |||
54 | static int __init etrax_init_cont_rotime(void) | ||
55 | { | ||
56 | cont_rotime.mult = clocksource_khz2mult(100000, cont_rotime.shift); | ||
57 | clocksource_register(&cont_rotime); | ||
58 | return 0; | ||
59 | } | ||
60 | arch_initcall(etrax_init_cont_rotime); | ||
61 | |||
62 | |||
39 | unsigned long timer_regs[NR_CPUS] = | 63 | unsigned long timer_regs[NR_CPUS] = |
40 | { | 64 | { |
41 | regi_timer0, | 65 | regi_timer0, |
@@ -67,43 +91,6 @@ unsigned long get_ns_in_jiffie(void) | |||
67 | return ns; | 91 | return ns; |
68 | } | 92 | } |
69 | 93 | ||
70 | unsigned long do_slow_gettimeoffset(void) | ||
71 | { | ||
72 | unsigned long count; | ||
73 | unsigned long usec_count = 0; | ||
74 | |||
75 | /* For the first call after boot */ | ||
76 | static unsigned long count_p = TIMER0_DIV; | ||
77 | static unsigned long jiffies_p = 0; | ||
78 | |||
79 | /* Cache volatile jiffies temporarily; we have IRQs turned off. */ | ||
80 | unsigned long jiffies_t; | ||
81 | |||
82 | /* The timer interrupt comes from Etrax timer 0. In order to get | ||
83 | * better precision, we check the current value. It might have | ||
84 | * underflowed already though. */ | ||
85 | count = REG_RD(timer, regi_timer0, r_tmr0_data); | ||
86 | jiffies_t = jiffies; | ||
87 | |||
88 | /* Avoiding timer inconsistencies (they are rare, but they happen) | ||
89 | * There is one problem that must be avoided here: | ||
90 | * 1. the timer counter underflows | ||
91 | */ | ||
92 | if( jiffies_t == jiffies_p ) { | ||
93 | if( count > count_p ) { | ||
94 | /* Timer wrapped, use new count and prescale. | ||
95 | * Increase the time corresponding to one jiffy. | ||
96 | */ | ||
97 | usec_count = 1000000/HZ; | ||
98 | } | ||
99 | } else | ||
100 | jiffies_p = jiffies_t; | ||
101 | count_p = count; | ||
102 | /* Convert timer value to usec */ | ||
103 | /* 100 MHz timer, divide by 100 to get usec */ | ||
104 | usec_count += (TIMER0_DIV - count) / 100; | ||
105 | return usec_count; | ||
106 | } | ||
107 | 94 | ||
108 | /* From timer MDS describing the hardware watchdog: | 95 | /* From timer MDS describing the hardware watchdog: |
109 | * 4.3.1 Watchdog Operation | 96 | * 4.3.1 Watchdog Operation |
@@ -126,8 +113,7 @@ static short int watchdog_key = 42; /* arbitrary 7 bit number */ | |||
126 | * is used though, so set this really low. */ | 113 | * is used though, so set this really low. */ |
127 | #define WATCHDOG_MIN_FREE_PAGES 8 | 114 | #define WATCHDOG_MIN_FREE_PAGES 8 |
128 | 115 | ||
129 | void | 116 | void reset_watchdog(void) |
130 | reset_watchdog(void) | ||
131 | { | 117 | { |
132 | #if defined(CONFIG_ETRAX_WATCHDOG) | 118 | #if defined(CONFIG_ETRAX_WATCHDOG) |
133 | reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; | 119 | reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; |
@@ -147,8 +133,7 @@ reset_watchdog(void) | |||
147 | 133 | ||
148 | /* stop the watchdog - we still need the correct key */ | 134 | /* stop the watchdog - we still need the correct key */ |
149 | 135 | ||
150 | void | 136 | void stop_watchdog(void) |
151 | stop_watchdog(void) | ||
152 | { | 137 | { |
153 | #if defined(CONFIG_ETRAX_WATCHDOG) | 138 | #if defined(CONFIG_ETRAX_WATCHDOG) |
154 | reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; | 139 | reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; |
@@ -162,8 +147,7 @@ stop_watchdog(void) | |||
162 | 147 | ||
163 | extern void show_registers(struct pt_regs *regs); | 148 | extern void show_registers(struct pt_regs *regs); |
164 | 149 | ||
165 | void | 150 | void handle_watchdog_bite(struct pt_regs *regs) |
166 | handle_watchdog_bite(struct pt_regs* regs) | ||
167 | { | 151 | { |
168 | #if defined(CONFIG_ETRAX_WATCHDOG) | 152 | #if defined(CONFIG_ETRAX_WATCHDOG) |
169 | extern int cause_of_death; | 153 | extern int cause_of_death; |
@@ -203,8 +187,7 @@ handle_watchdog_bite(struct pt_regs* regs) | |||
203 | */ | 187 | */ |
204 | extern void cris_do_profile(struct pt_regs *regs); | 188 | extern void cris_do_profile(struct pt_regs *regs); |
205 | 189 | ||
206 | static inline irqreturn_t | 190 | static inline irqreturn_t timer_interrupt(int irq, void *dev_id) |
207 | timer_interrupt(int irq, void *dev_id) | ||
208 | { | 191 | { |
209 | struct pt_regs *regs = get_irq_regs(); | 192 | struct pt_regs *regs = get_irq_regs(); |
210 | int cpu = smp_processor_id(); | 193 | int cpu = smp_processor_id(); |
@@ -233,7 +216,9 @@ timer_interrupt(int irq, void *dev_id) | |||
233 | return IRQ_HANDLED; | 216 | return IRQ_HANDLED; |
234 | 217 | ||
235 | /* Call the real timer interrupt handler */ | 218 | /* Call the real timer interrupt handler */ |
219 | write_seqlock(&xtime_lock); | ||
236 | do_timer(1); | 220 | do_timer(1); |
221 | write_sequnlock(&xtime_lock); | ||
237 | return IRQ_HANDLED; | 222 | return IRQ_HANDLED; |
238 | } | 223 | } |
239 | 224 | ||
@@ -246,8 +231,7 @@ static struct irqaction irq_timer = { | |||
246 | .name = "timer" | 231 | .name = "timer" |
247 | }; | 232 | }; |
248 | 233 | ||
249 | void __init | 234 | void __init cris_timer_init(void) |
250 | cris_timer_init(void) | ||
251 | { | 235 | { |
252 | int cpu = smp_processor_id(); | 236 | int cpu = smp_processor_id(); |
253 | reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; | 237 | reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; |
@@ -273,8 +257,7 @@ cris_timer_init(void) | |||
273 | REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); | 257 | REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); |
274 | } | 258 | } |
275 | 259 | ||
276 | void __init | 260 | void __init time_init(void) |
277 | time_init(void) | ||
278 | { | 261 | { |
279 | reg_intr_vect_rw_mask intr_mask; | 262 | reg_intr_vect_rw_mask intr_mask; |
280 | 263 | ||
diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c index c72730d20ef6..b5096430ce1c 100644 --- a/arch/cris/kernel/time.c +++ b/arch/cris/kernel/time.c | |||
@@ -39,13 +39,16 @@ int have_rtc; /* used to remember if we have an RTC or not */; | |||
39 | extern unsigned long loops_per_jiffy; /* init/main.c */ | 39 | extern unsigned long loops_per_jiffy; /* init/main.c */ |
40 | unsigned long loops_per_usec; | 40 | unsigned long loops_per_usec; |
41 | 41 | ||
42 | |||
43 | #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET | ||
42 | extern unsigned long do_slow_gettimeoffset(void); | 44 | extern unsigned long do_slow_gettimeoffset(void); |
43 | static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; | 45 | static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; |
44 | 46 | ||
45 | u32 arch_gettimeoffset(void) | 47 | u32 arch_gettimeoffset(void) |
46 | { | 48 | { |
47 | return do_gettimeoffset() * 1000; | 49 | return do_gettimeoffset() * 1000; |
48 | } | 50 | } |
51 | #endif | ||
49 | 52 | ||
50 | /* | 53 | /* |
51 | * BUG: This routine does not handle hour overflow properly; it just | 54 | * BUG: This routine does not handle hour overflow properly; it just |
@@ -151,7 +154,7 @@ cris_do_profile(struct pt_regs* regs) | |||
151 | 154 | ||
152 | unsigned long long sched_clock(void) | 155 | unsigned long long sched_clock(void) |
153 | { | 156 | { |
154 | return (unsigned long long)jiffies * (1000000000 / HZ) + | 157 | return (unsigned long long)jiffies * (NSEC_PER_SEC / HZ) + |
155 | get_ns_in_jiffie(); | 158 | get_ns_in_jiffie(); |
156 | } | 159 | } |
157 | 160 | ||