]>
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> |
d43c36dc | 17 | #include <linux/sched.h> |
1394f032 BW |
18 | |
19 | #include <asm/blackfin.h> | |
e6c91b64 | 20 | #include <asm/time.h> |
8f65873e | 21 | #include <asm/gptimers.h> |
1394f032 BW |
22 | |
23 | /* This is an NTP setting */ | |
24 | #define TICK_SIZE (tick_nsec / 1000) | |
25 | ||
1394f032 | 26 | static struct irqaction bfin_timer_irq = { |
1b047d8c | 27 | .name = "Blackfin Timer Tick", |
1394f032 BW |
28 | .flags = IRQF_DISABLED |
29 | }; | |
30 | ||
9b9bfded | 31 | #if defined(CONFIG_IPIPE) |
a1ee74ca | 32 | void __init setup_system_timer0(void) |
1b047d8c MF |
33 | { |
34 | /* Power down the core timer, just to play safe. */ | |
35 | bfin_write_TCNTL(0); | |
36 | ||
37 | disable_gptimers(TIMER0bit); | |
38 | set_gptimer_status(0, TIMER_STATUS_TRUN0); | |
39 | while (get_gptimer_status(0) & TIMER_STATUS_TRUN0) | |
40 | udelay(10); | |
41 | ||
42 | set_gptimer_config(0, 0x59); /* IRQ enable, periodic, PWM_OUT, SCLKed, OUT PAD disabled */ | |
43 | set_gptimer_period(TIMER0_id, get_sclk() / HZ); | |
44 | set_gptimer_pwidth(TIMER0_id, 1); | |
45 | SSYNC(); | |
46 | enable_gptimers(TIMER0bit); | |
47 | } | |
48 | #else | |
a1ee74ca | 49 | void __init setup_core_timer(void) |
1394f032 BW |
50 | { |
51 | u32 tcount; | |
52 | ||
53 | /* power up the timer, but don't enable it just yet */ | |
54 | bfin_write_TCNTL(1); | |
55 | CSYNC(); | |
56 | ||
1b047d8c MF |
57 | /* the TSCALE prescaler counter */ |
58 | bfin_write_TSCALE(TIME_SCALE - 1); | |
1394f032 BW |
59 | |
60 | tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1); | |
61 | bfin_write_TPERIOD(tcount); | |
62 | bfin_write_TCOUNT(tcount); | |
63 | ||
64 | /* now enable the timer */ | |
65 | CSYNC(); | |
66 | ||
67 | bfin_write_TCNTL(7); | |
8f65873e | 68 | } |
8f65873e | 69 | #endif |
1394f032 | 70 | |
a1ee74ca | 71 | static void __init |
8f65873e GY |
72 | time_sched_init(irqreturn_t(*timer_routine) (int, void *)) |
73 | { | |
9b9bfded | 74 | #if defined(CONFIG_IPIPE) |
8f65873e | 75 | setup_system_timer0(); |
1b047d8c | 76 | bfin_timer_irq.handler = timer_routine; |
8f65873e GY |
77 | setup_irq(IRQ_TIMER0, &bfin_timer_irq); |
78 | #else | |
1b047d8c MF |
79 | setup_core_timer(); |
80 | bfin_timer_irq.handler = timer_routine; | |
1394f032 | 81 | setup_irq(IRQ_CORETMR, &bfin_timer_irq); |
8f65873e | 82 | #endif |
1394f032 BW |
83 | } |
84 | ||
10f03f1a | 85 | #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET |
1394f032 BW |
86 | /* |
87 | * Should return useconds since last timer tick | |
88 | */ | |
10f03f1a | 89 | u32 arch_gettimeoffset(void) |
1394f032 BW |
90 | { |
91 | unsigned long offset; | |
92 | unsigned long clocks_per_jiffy; | |
93 | ||
9b9bfded | 94 | #if defined(CONFIG_IPIPE) |
1b047d8c MF |
95 | clocks_per_jiffy = bfin_read_TIMER0_PERIOD(); |
96 | offset = bfin_read_TIMER0_COUNTER() / \ | |
8f65873e GY |
97 | (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); |
98 | ||
99 | if ((get_gptimer_status(0) & TIMER_STATUS_TIMIL0) && offset < (100000 / HZ / 2)) | |
100 | offset += (USEC_PER_SEC / HZ); | |
101 | #else | |
1394f032 | 102 | clocks_per_jiffy = bfin_read_TPERIOD(); |
8f65873e | 103 | offset = (clocks_per_jiffy - bfin_read_TCOUNT()) / \ |
1b047d8c | 104 | (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); |
1394f032 BW |
105 | |
106 | /* Check if we just wrapped the counters and maybe missed a tick */ | |
107 | if ((bfin_read_ILAT() & (1 << IRQ_CORETMR)) | |
8f65873e | 108 | && (offset < (100000 / HZ / 2))) |
1394f032 | 109 | offset += (USEC_PER_SEC / HZ); |
8f65873e | 110 | #endif |
1394f032 BW |
111 | return offset; |
112 | } | |
1b047d8c | 113 | #endif |
1394f032 BW |
114 | |
115 | static inline int set_rtc_mmss(unsigned long nowtime) | |
116 | { | |
117 | return 0; | |
118 | } | |
119 | ||
120 | /* | |
121 | * timer_interrupt() needs to keep up the real-time clock, | |
122 | * as well as call the "do_timer()" routine every clocktick | |
123 | */ | |
124 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 | |
1b047d8c | 125 | __attribute__((l1_text)) |
1394f032 | 126 | #endif |
1394f032 BW |
127 | irqreturn_t timer_interrupt(int irq, void *dummy) |
128 | { | |
129 | /* last time the cmos clock got updated */ | |
1f83b8f1 | 130 | static long last_rtc_update; |
1394f032 BW |
131 | |
132 | write_seqlock(&xtime_lock); | |
9b9bfded GY |
133 | do_timer(1); |
134 | ||
9bd50df6 | 135 | /* |
9b9bfded GY |
136 | * If we have an externally synchronized Linux clock, then update |
137 | * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be | |
138 | * called as close as possible to 500 ms before the new second starts. | |
9bd50df6 | 139 | */ |
9b9bfded GY |
140 | if (ntp_synced() && |
141 | xtime.tv_sec > last_rtc_update + 660 && | |
142 | (xtime.tv_nsec / NSEC_PER_USEC) >= | |
143 | 500000 - ((unsigned)TICK_SIZE) / 2 | |
144 | && (xtime.tv_nsec / NSEC_PER_USEC) <= | |
145 | 500000 + ((unsigned)TICK_SIZE) / 2) { | |
146 | if (set_rtc_mmss(xtime.tv_sec) == 0) | |
147 | last_rtc_update = xtime.tv_sec; | |
148 | else | |
149 | /* Do it again in 60s. */ | |
150 | last_rtc_update = xtime.tv_sec - 600; | |
1394f032 BW |
151 | } |
152 | write_sequnlock(&xtime_lock); | |
aa02cd2d | 153 | |
6a01f230 YL |
154 | #ifdef CONFIG_IPIPE |
155 | update_root_process_times(get_irq_regs()); | |
156 | #else | |
aa02cd2d | 157 | update_process_times(user_mode(get_irq_regs())); |
6a01f230 | 158 | #endif |
8f65873e | 159 | profile_tick(CPU_PROFILING); |
aa02cd2d | 160 | |
1394f032 BW |
161 | return IRQ_HANDLED; |
162 | } | |
163 | ||
164 | void __init time_init(void) | |
165 | { | |
166 | time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */ | |
167 | ||
168 | #ifdef CONFIG_RTC_DRV_BFIN | |
169 | /* [#2663] hack to filter junk RTC values that would cause | |
170 | * userspace to have to deal with time values greater than | |
171 | * 2^31 seconds (which uClibc cannot cope with yet) | |
172 | */ | |
173 | if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) { | |
174 | printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n"); | |
175 | bfin_write_RTC_STAT(0); | |
176 | } | |
177 | #endif | |
178 | ||
179 | /* Initialize xtime. From now on, xtime is updated with timer interrupts */ | |
180 | xtime.tv_sec = secs_since_1970; | |
181 | xtime.tv_nsec = 0; | |
182 | ||
183 | wall_to_monotonic.tv_sec = -xtime.tv_sec; | |
184 | ||
185 | time_sched_init(timer_interrupt); | |
186 | } |