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