]>
Commit | Line | Data |
---|---|---|
1394f032 | 1 | /* |
1b047d8c | 2 | * arch/blackfin/kernel/time.c |
1394f032 | 3 | * |
1b047d8c MF |
4 | * This file contains the Blackfin-specific time handling details. |
5 | * Most of the stuff is located in the machine specific files. | |
1394f032 | 6 | * |
1b047d8c MF |
7 | * Copyright 2004-2008 Analog Devices Inc. |
8 | * Licensed under the GPL-2 or later. | |
1394f032 BW |
9 | */ |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/profile.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/time.h> | |
15 | #include <linux/irq.h> | |
8f65873e | 16 | #include <linux/delay.h> |
1394f032 BW |
17 | |
18 | #include <asm/blackfin.h> | |
e6c91b64 | 19 | #include <asm/time.h> |
8f65873e | 20 | #include <asm/gptimers.h> |
1394f032 BW |
21 | |
22 | /* This is an NTP setting */ | |
23 | #define TICK_SIZE (tick_nsec / 1000) | |
24 | ||
1394f032 | 25 | static struct irqaction bfin_timer_irq = { |
1b047d8c | 26 | .name = "Blackfin Timer Tick", |
1394f032 BW |
27 | .flags = IRQF_DISABLED |
28 | }; | |
29 | ||
9b9bfded | 30 | #if defined(CONFIG_IPIPE) |
a1ee74ca | 31 | void __init setup_system_timer0(void) |
1b047d8c MF |
32 | { |
33 | /* Power down the core timer, just to play safe. */ | |
34 | bfin_write_TCNTL(0); | |
35 | ||
36 | disable_gptimers(TIMER0bit); | |
37 | set_gptimer_status(0, TIMER_STATUS_TRUN0); | |
38 | while (get_gptimer_status(0) & TIMER_STATUS_TRUN0) | |
39 | udelay(10); | |
40 | ||
41 | set_gptimer_config(0, 0x59); /* IRQ enable, periodic, PWM_OUT, SCLKed, OUT PAD disabled */ | |
42 | set_gptimer_period(TIMER0_id, get_sclk() / HZ); | |
43 | set_gptimer_pwidth(TIMER0_id, 1); | |
44 | SSYNC(); | |
45 | enable_gptimers(TIMER0bit); | |
46 | } | |
47 | #else | |
a1ee74ca | 48 | void __init setup_core_timer(void) |
1394f032 BW |
49 | { |
50 | u32 tcount; | |
51 | ||
52 | /* power up the timer, but don't enable it just yet */ | |
53 | bfin_write_TCNTL(1); | |
54 | CSYNC(); | |
55 | ||
1b047d8c MF |
56 | /* the TSCALE prescaler counter */ |
57 | bfin_write_TSCALE(TIME_SCALE - 1); | |
1394f032 BW |
58 | |
59 | tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1); | |
60 | bfin_write_TPERIOD(tcount); | |
61 | bfin_write_TCOUNT(tcount); | |
62 | ||
63 | /* now enable the timer */ | |
64 | CSYNC(); | |
65 | ||
66 | bfin_write_TCNTL(7); | |
8f65873e | 67 | } |
8f65873e | 68 | #endif |
1394f032 | 69 | |
a1ee74ca | 70 | static void __init |
8f65873e GY |
71 | time_sched_init(irqreturn_t(*timer_routine) (int, void *)) |
72 | { | |
9b9bfded | 73 | #if defined(CONFIG_IPIPE) |
8f65873e | 74 | setup_system_timer0(); |
1b047d8c | 75 | bfin_timer_irq.handler = timer_routine; |
8f65873e GY |
76 | setup_irq(IRQ_TIMER0, &bfin_timer_irq); |
77 | #else | |
1b047d8c MF |
78 | setup_core_timer(); |
79 | bfin_timer_irq.handler = timer_routine; | |
1394f032 | 80 | setup_irq(IRQ_CORETMR, &bfin_timer_irq); |
8f65873e | 81 | #endif |
1394f032 BW |
82 | } |
83 | ||
84 | /* | |
85 | * Should return useconds since last timer tick | |
86 | */ | |
1b047d8c | 87 | #ifndef CONFIG_GENERIC_TIME |
1394f032 BW |
88 | static unsigned long gettimeoffset(void) |
89 | { | |
90 | unsigned long offset; | |
91 | unsigned long clocks_per_jiffy; | |
92 | ||
9b9bfded | 93 | #if defined(CONFIG_IPIPE) |
1b047d8c MF |
94 | clocks_per_jiffy = bfin_read_TIMER0_PERIOD(); |
95 | offset = bfin_read_TIMER0_COUNTER() / \ | |
8f65873e GY |
96 | (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); |
97 | ||
98 | if ((get_gptimer_status(0) & TIMER_STATUS_TIMIL0) && offset < (100000 / HZ / 2)) | |
99 | offset += (USEC_PER_SEC / HZ); | |
100 | #else | |
1394f032 | 101 | clocks_per_jiffy = bfin_read_TPERIOD(); |
8f65873e | 102 | offset = (clocks_per_jiffy - bfin_read_TCOUNT()) / \ |
1b047d8c | 103 | (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); |
1394f032 BW |
104 | |
105 | /* Check if we just wrapped the counters and maybe missed a tick */ | |
106 | if ((bfin_read_ILAT() & (1 << IRQ_CORETMR)) | |
8f65873e | 107 | && (offset < (100000 / HZ / 2))) |
1394f032 | 108 | offset += (USEC_PER_SEC / HZ); |
8f65873e | 109 | #endif |
1394f032 BW |
110 | return offset; |
111 | } | |
1b047d8c | 112 | #endif |
1394f032 BW |
113 | |
114 | static inline int set_rtc_mmss(unsigned long nowtime) | |
115 | { | |
116 | return 0; | |
117 | } | |
118 | ||
119 | /* | |
120 | * timer_interrupt() needs to keep up the real-time clock, | |
121 | * as well as call the "do_timer()" routine every clocktick | |
122 | */ | |
123 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 | |
1b047d8c | 124 | __attribute__((l1_text)) |
1394f032 | 125 | #endif |
1394f032 BW |
126 | irqreturn_t timer_interrupt(int irq, void *dummy) |
127 | { | |
128 | /* last time the cmos clock got updated */ | |
1f83b8f1 | 129 | static long last_rtc_update; |
1394f032 BW |
130 | |
131 | write_seqlock(&xtime_lock); | |
9b9bfded GY |
132 | do_timer(1); |
133 | ||
9bd50df6 | 134 | /* |
9b9bfded GY |
135 | * If we have an externally synchronized Linux clock, then update |
136 | * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be | |
137 | * called as close as possible to 500 ms before the new second starts. | |
9bd50df6 | 138 | */ |
9b9bfded GY |
139 | if (ntp_synced() && |
140 | xtime.tv_sec > last_rtc_update + 660 && | |
141 | (xtime.tv_nsec / NSEC_PER_USEC) >= | |
142 | 500000 - ((unsigned)TICK_SIZE) / 2 | |
143 | && (xtime.tv_nsec / NSEC_PER_USEC) <= | |
144 | 500000 + ((unsigned)TICK_SIZE) / 2) { | |
145 | if (set_rtc_mmss(xtime.tv_sec) == 0) | |
146 | last_rtc_update = xtime.tv_sec; | |
147 | else | |
148 | /* Do it again in 60s. */ | |
149 | last_rtc_update = xtime.tv_sec - 600; | |
1394f032 BW |
150 | } |
151 | write_sequnlock(&xtime_lock); | |
aa02cd2d | 152 | |
6a01f230 YL |
153 | #ifdef CONFIG_IPIPE |
154 | update_root_process_times(get_irq_regs()); | |
155 | #else | |
aa02cd2d | 156 | update_process_times(user_mode(get_irq_regs())); |
6a01f230 | 157 | #endif |
8f65873e | 158 | profile_tick(CPU_PROFILING); |
aa02cd2d | 159 | |
1394f032 BW |
160 | return IRQ_HANDLED; |
161 | } | |
162 | ||
163 | void __init time_init(void) | |
164 | { | |
165 | time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */ | |
166 | ||
167 | #ifdef CONFIG_RTC_DRV_BFIN | |
168 | /* [#2663] hack to filter junk RTC values that would cause | |
169 | * userspace to have to deal with time values greater than | |
170 | * 2^31 seconds (which uClibc cannot cope with yet) | |
171 | */ | |
172 | if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) { | |
173 | printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n"); | |
174 | bfin_write_RTC_STAT(0); | |
175 | } | |
176 | #endif | |
177 | ||
178 | /* Initialize xtime. From now on, xtime is updated with timer interrupts */ | |
179 | xtime.tv_sec = secs_since_1970; | |
180 | xtime.tv_nsec = 0; | |
181 | ||
182 | wall_to_monotonic.tv_sec = -xtime.tv_sec; | |
183 | ||
184 | time_sched_init(timer_interrupt); | |
185 | } | |
186 | ||
187 | #ifndef CONFIG_GENERIC_TIME | |
188 | void do_gettimeofday(struct timeval *tv) | |
189 | { | |
190 | unsigned long flags; | |
191 | unsigned long seq; | |
192 | unsigned long usec, sec; | |
193 | ||
194 | do { | |
195 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | |
196 | usec = gettimeoffset(); | |
197 | sec = xtime.tv_sec; | |
198 | usec += (xtime.tv_nsec / NSEC_PER_USEC); | |
199 | } | |
200 | while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); | |
201 | ||
202 | while (usec >= USEC_PER_SEC) { | |
203 | usec -= USEC_PER_SEC; | |
204 | sec++; | |
205 | } | |
206 | ||
207 | tv->tv_sec = sec; | |
208 | tv->tv_usec = usec; | |
209 | } | |
210 | EXPORT_SYMBOL(do_gettimeofday); | |
211 | ||
212 | int do_settimeofday(struct timespec *tv) | |
213 | { | |
214 | time_t wtm_sec, sec = tv->tv_sec; | |
215 | long wtm_nsec, nsec = tv->tv_nsec; | |
216 | ||
217 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | |
218 | return -EINVAL; | |
219 | ||
220 | write_seqlock_irq(&xtime_lock); | |
221 | /* | |
222 | * This is revolting. We need to set the xtime.tv_usec | |
223 | * correctly. However, the value in this location is | |
224 | * is value at the last tick. | |
225 | * Discover what correction gettimeofday | |
226 | * would have done, and then undo it! | |
227 | */ | |
228 | nsec -= (gettimeoffset() * NSEC_PER_USEC); | |
229 | ||
230 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | |
231 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | |
232 | ||
233 | set_normalized_timespec(&xtime, sec, nsec); | |
234 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | |
235 | ||
236 | ntp_clear(); | |
237 | ||
238 | write_sequnlock_irq(&xtime_lock); | |
239 | clock_was_set(); | |
240 | ||
241 | return 0; | |
242 | } | |
243 | EXPORT_SYMBOL(do_settimeofday); | |
244 | #endif /* !CONFIG_GENERIC_TIME */ | |
245 | ||
246 | /* | |
247 | * Scheduler clock - returns current time in nanosec units. | |
248 | */ | |
249 | unsigned long long sched_clock(void) | |
250 | { | |
251 | return (unsigned long long)jiffies *(NSEC_PER_SEC / HZ); | |
252 | } |