aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin/kernel/time.c
diff options
context:
space:
mode:
authorBryan Wu <bryan.wu@analog.com>2007-05-06 17:50:22 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-07 15:12:58 -0400
commit1394f03221790a988afc3e4b3cb79f2e477246a9 (patch)
tree2c1963c9a4f2d84a5e021307fde240c5d567cf70 /arch/blackfin/kernel/time.c
parent73243284463a761e04d69d22c7516b2be7de096c (diff)
blackfin architecture
This adds support for the Analog Devices Blackfin processor architecture, and currently supports the BF533, BF532, BF531, BF537, BF536, BF534, and BF561 (Dual Core) devices, with a variety of development platforms including those avaliable from Analog Devices (BF533-EZKit, BF533-STAMP, BF537-STAMP, BF561-EZKIT), and Bluetechnix! Tinyboards. The Blackfin architecture was jointly developed by Intel and Analog Devices Inc. (ADI) as the Micro Signal Architecture (MSA) core and introduced it in December of 2000. Since then ADI has put this core into its Blackfin processor family of devices. The Blackfin core has the advantages of a clean, orthogonal,RISC-like microprocessor instruction set. It combines a dual-MAC (Multiply/Accumulate), state-of-the-art signal processing engine and single-instruction, multiple-data (SIMD) multimedia capabilities into a single instruction-set architecture. The Blackfin architecture, including the instruction set, is described by the ADSP-BF53x/BF56x Blackfin Processor Programming Reference http://blackfin.uclinux.org/gf/download/frsrelease/29/2549/Blackfin_PRM.pdf The Blackfin processor is already supported by major releases of gcc, and there are binary and source rpms/tarballs for many architectures at: http://blackfin.uclinux.org/gf/project/toolchain/frs There is complete documentation, including "getting started" guides available at: http://docs.blackfin.uclinux.org/ which provides links to the sources and patches you will need in order to set up a cross-compiling environment for bfin-linux-uclibc This patch, as well as the other patches (toolchain, distribution, uClibc) are actively supported by Analog Devices Inc, at: http://blackfin.uclinux.org/ We have tested this on LTP, and our test plan (including pass/fails) can be found at: http://docs.blackfin.uclinux.org/doku.php?id=testing_the_linux_kernel [m.kozlowski@tuxland.pl: balance parenthesis in blackfin header files] Signed-off-by: Bryan Wu <bryan.wu@analog.com> Signed-off-by: Mariusz Kozlowski <m.kozlowski@tuxland.pl> Signed-off-by: Aubrey Li <aubrey.li@analog.com> Signed-off-by: Jie Zhang <jie.zhang@analog.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/blackfin/kernel/time.c')
-rw-r--r--arch/blackfin/kernel/time.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c
new file mode 100644
index 000000000000..f578176b6d92
--- /dev/null
+++ b/arch/blackfin/kernel/time.c
@@ -0,0 +1,326 @@
1/*
2 * File: arch/blackfin/kernel/time.c
3 * Based on: none - original work
4 * Author:
5 *
6 * Created:
7 * Description: This file contains the bfin-specific time handling details.
8 * Most of the stuff is located in the machine specific files.
9 *
10 * Modified:
11 * Copyright 2004-2006 Analog Devices Inc.
12 *
13 * Bugs: Enter bugs at http://blackfin.uclinux.org/
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see the file COPYING, or write
27 * to the Free Software Foundation, Inc.,
28 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 */
30
31#include <linux/module.h>
32#include <linux/profile.h>
33#include <linux/interrupt.h>
34#include <linux/time.h>
35#include <linux/irq.h>
36
37#include <asm/blackfin.h>
38
39/* This is an NTP setting */
40#define TICK_SIZE (tick_nsec / 1000)
41
42static void time_sched_init(irqreturn_t(*timer_routine)
43 (int, void *));
44static unsigned long gettimeoffset(void);
45static inline void do_leds(void);
46
47#if (defined(CONFIG_BFIN_ALIVE_LED) || defined(CONFIG_BFIN_IDLE_LED))
48void __init init_leds(void)
49{
50 unsigned int tmp = 0;
51
52#if defined(CONFIG_BFIN_ALIVE_LED)
53 /* config pins as output. */
54 tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_DPORT();
55 SSYNC();
56 bfin_write_CONFIG_BFIN_ALIVE_LED_DPORT(tmp | CONFIG_BFIN_ALIVE_LED_PIN);
57 SSYNC();
58
59 /* First set led be off */
60 tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_PORT();
61 SSYNC();
62 bfin_write_CONFIG_BFIN_ALIVE_LED_PORT(tmp | CONFIG_BFIN_ALIVE_LED_PIN); /* light off */
63 SSYNC();
64#endif
65
66#if defined(CONFIG_BFIN_IDLE_LED)
67 /* config pins as output. */
68 tmp = bfin_read_CONFIG_BFIN_IDLE_LED_DPORT();
69 SSYNC();
70 bfin_write_CONFIG_BFIN_IDLE_LED_DPORT(tmp | CONFIG_BFIN_IDLE_LED_PIN);
71 SSYNC();
72
73 /* First set led be off */
74 tmp = bfin_read_CONFIG_BFIN_IDLE_LED_PORT();
75 SSYNC();
76 bfin_write_CONFIG_BFIN_IDLE_LED_PORT(tmp | CONFIG_BFIN_IDLE_LED_PIN); /* light off */
77 SSYNC();
78#endif
79}
80#else
81void __init init_leds(void)
82{
83}
84#endif
85
86#if defined(CONFIG_BFIN_ALIVE_LED)
87static inline void do_leds(void)
88{
89 static unsigned int count = 50;
90 static int flag = 0;
91 unsigned short tmp = 0;
92
93 if (--count == 0) {
94 count = 50;
95 flag = ~flag;
96 }
97 tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_PORT();
98 SSYNC();
99
100 if (flag)
101 tmp &= ~CONFIG_BFIN_ALIVE_LED_PIN; /* light on */
102 else
103 tmp |= CONFIG_BFIN_ALIVE_LED_PIN; /* light off */
104
105 bfin_write_CONFIG_BFIN_ALIVE_LED_PORT(tmp);
106 SSYNC();
107
108}
109#else
110static inline void do_leds(void)
111{
112}
113#endif
114
115static struct irqaction bfin_timer_irq = {
116 .name = "BFIN Timer Tick",
117 .flags = IRQF_DISABLED
118};
119
120/*
121 * The way that the Blackfin core timer works is:
122 * - CCLK is divided by a programmable 8-bit pre-scaler (TSCALE)
123 * - Every time TSCALE ticks, a 32bit is counted down (TCOUNT)
124 *
125 * If you take the fastest clock (1ns, or 1GHz to make the math work easier)
126 * 10ms is 10,000,000 clock ticks, which fits easy into a 32-bit counter
127 * (32 bit counter is 4,294,967,296ns or 4.2 seconds) so, we don't need
128 * to use TSCALE, and program it to zero (which is pass CCLK through).
129 * If you feel like using it, try to keep HZ * TIMESCALE to some
130 * value that divides easy (like power of 2).
131 */
132
133#define TIME_SCALE 1
134
135static void
136time_sched_init(irqreturn_t(*timer_routine) (int, void *))
137{
138 u32 tcount;
139
140 /* power up the timer, but don't enable it just yet */
141 bfin_write_TCNTL(1);
142 CSYNC();
143
144 /*
145 * the TSCALE prescaler counter.
146 */
147 bfin_write_TSCALE((TIME_SCALE - 1));
148
149 tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1);
150 bfin_write_TPERIOD(tcount);
151 bfin_write_TCOUNT(tcount);
152
153 /* now enable the timer */
154 CSYNC();
155
156 bfin_write_TCNTL(7);
157
158 bfin_timer_irq.handler = (irq_handler_t)timer_routine;
159 /* call setup_irq instead of request_irq because request_irq calls
160 * kmalloc which has not been initialized yet
161 */
162 setup_irq(IRQ_CORETMR, &bfin_timer_irq);
163}
164
165/*
166 * Should return useconds since last timer tick
167 */
168static unsigned long gettimeoffset(void)
169{
170 unsigned long offset;
171 unsigned long clocks_per_jiffy;
172
173 clocks_per_jiffy = bfin_read_TPERIOD();
174 offset =
175 (clocks_per_jiffy -
176 bfin_read_TCOUNT()) / (((clocks_per_jiffy + 1) * HZ) /
177 USEC_PER_SEC);
178
179 /* Check if we just wrapped the counters and maybe missed a tick */
180 if ((bfin_read_ILAT() & (1 << IRQ_CORETMR))
181 && (offset < (100000 / HZ / 2)))
182 offset += (USEC_PER_SEC / HZ);
183
184 return offset;
185}
186
187static inline int set_rtc_mmss(unsigned long nowtime)
188{
189 return 0;
190}
191
192/*
193 * timer_interrupt() needs to keep up the real-time clock,
194 * as well as call the "do_timer()" routine every clocktick
195 */
196#ifdef CONFIG_CORE_TIMER_IRQ_L1
197irqreturn_t timer_interrupt(int irq, void *dummy)__attribute__((l1_text));
198#endif
199
200irqreturn_t timer_interrupt(int irq, void *dummy)
201{
202 /* last time the cmos clock got updated */
203 static long last_rtc_update = 0;
204
205 write_seqlock(&xtime_lock);
206
207 do_timer(1);
208 do_leds();
209
210#ifndef CONFIG_SMP
211 update_process_times(user_mode(get_irq_regs()));
212#endif
213 profile_tick(CPU_PROFILING);
214
215 /*
216 * If we have an externally synchronized Linux clock, then update
217 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
218 * called as close as possible to 500 ms before the new second starts.
219 */
220
221 if (ntp_synced() &&
222 xtime.tv_sec > last_rtc_update + 660 &&
223 (xtime.tv_nsec / NSEC_PER_USEC) >=
224 500000 - ((unsigned)TICK_SIZE) / 2
225 && (xtime.tv_nsec / NSEC_PER_USEC) <=
226 500000 + ((unsigned)TICK_SIZE) / 2) {
227 if (set_rtc_mmss(xtime.tv_sec) == 0)
228 last_rtc_update = xtime.tv_sec;
229 else
230 /* Do it again in 60s. */
231 last_rtc_update = xtime.tv_sec - 600;
232 }
233 write_sequnlock(&xtime_lock);
234 return IRQ_HANDLED;
235}
236
237void __init time_init(void)
238{
239 time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */
240
241#ifdef CONFIG_RTC_DRV_BFIN
242 /* [#2663] hack to filter junk RTC values that would cause
243 * userspace to have to deal with time values greater than
244 * 2^31 seconds (which uClibc cannot cope with yet)
245 */
246 if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) {
247 printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n");
248 bfin_write_RTC_STAT(0);
249 }
250#endif
251
252 /* Initialize xtime. From now on, xtime is updated with timer interrupts */
253 xtime.tv_sec = secs_since_1970;
254 xtime.tv_nsec = 0;
255
256 wall_to_monotonic.tv_sec = -xtime.tv_sec;
257
258 time_sched_init(timer_interrupt);
259}
260
261#ifndef CONFIG_GENERIC_TIME
262void do_gettimeofday(struct timeval *tv)
263{
264 unsigned long flags;
265 unsigned long seq;
266 unsigned long usec, sec;
267
268 do {
269 seq = read_seqbegin_irqsave(&xtime_lock, flags);
270 usec = gettimeoffset();
271 sec = xtime.tv_sec;
272 usec += (xtime.tv_nsec / NSEC_PER_USEC);
273 }
274 while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
275
276 while (usec >= USEC_PER_SEC) {
277 usec -= USEC_PER_SEC;
278 sec++;
279 }
280
281 tv->tv_sec = sec;
282 tv->tv_usec = usec;
283}
284EXPORT_SYMBOL(do_gettimeofday);
285
286int do_settimeofday(struct timespec *tv)
287{
288 time_t wtm_sec, sec = tv->tv_sec;
289 long wtm_nsec, nsec = tv->tv_nsec;
290
291 if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
292 return -EINVAL;
293
294 write_seqlock_irq(&xtime_lock);
295 /*
296 * This is revolting. We need to set the xtime.tv_usec
297 * correctly. However, the value in this location is
298 * is value at the last tick.
299 * Discover what correction gettimeofday
300 * would have done, and then undo it!
301 */
302 nsec -= (gettimeoffset() * NSEC_PER_USEC);
303
304 wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
305 wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
306
307 set_normalized_timespec(&xtime, sec, nsec);
308 set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
309
310 ntp_clear();
311
312 write_sequnlock_irq(&xtime_lock);
313 clock_was_set();
314
315 return 0;
316}
317EXPORT_SYMBOL(do_settimeofday);
318#endif /* !CONFIG_GENERIC_TIME */
319
320/*
321 * Scheduler clock - returns current time in nanosec units.
322 */
323unsigned long long sched_clock(void)
324{
325 return (unsigned long long)jiffies *(NSEC_PER_SEC / HZ);
326}