]> git.enpas.org Git - openwrt.git/blob - target/linux/coldfire/files-2.6.31/arch/m68k/coldfire/common/time.c
relayd: move the interface fixup to the right place
[openwrt.git] / target / linux / coldfire / files-2.6.31 / arch / m68k / coldfire / common / time.c
1 /*
2  *  linux/arch/m68k/coldfire/time.c
3  *
4  *  This file contains the coldfire specific time handling pieces.
5  *
6  *  Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
7  *  Kurt Mahan <kmahan@freescale.com>
8  *  Jason Jin Jason.Jin@freescale.com
9  *  Shrek Wu B16972@freescale.com
10  *
11  *  based on linux/arch/m68k/kernel/time.c
12  */
13 #include <linux/clk.h>
14 #include <linux/clk.h>
15 #include <linux/clocksource.h>
16 #include <linux/clockchips.h>
17 #include <linux/time.h>
18 #include <linux/timex.h>
19 #include <linux/errno.h>
20 #include <linux/module.h>
21 #include <linux/sysdev.h>
22 #include <linux/sched.h>
23 #include <linux/kernel.h>
24 #include <linux/param.h>
25 #include <linux/string.h>
26 #include <linux/mm.h>
27 #include <linux/rtc.h>
28
29 #include <asm/machdep.h>
30 #include <linux/io.h>
31 #include <asm/irq_regs.h>
32
33 #include <linux/profile.h>
34 #include <asm/mcfsim.h>
35 #ifdef CONFIG_GENERIC_CLOCKEVENTS
36 /*extern unsigned long long sys_dtim0_read(void);
37 extern void sys_dtim_init(void);*/
38 extern unsigned long long sys_dtim2_read(void);
39 extern void sys_dtim2_init(void);
40 static int cfv4_set_next_event(unsigned long evt,
41         struct clock_event_device *dev);
42 static void cfv4_set_mode(enum clock_event_mode mode,
43         struct clock_event_device *dev);
44 #if defined(CONFIG_M5445X)
45 #define FREQ    (MCF_BUSCLK / 16)
46 #else
47 #define FREQ    (MCF_BUSCLK)
48 #endif
49 /*
50  * realtime clock dummy code
51  */
52
53 static unsigned long null_rtc_get_time(void)
54 {
55         return mktime(2008, 1, 1, 0, 0, 0);
56 }
57
58 static int null_rtc_set_time(unsigned long sec)
59 {
60         return 0;
61 }
62
63 static unsigned long (*cf_rtc_get_time)(void) = null_rtc_get_time;
64 static int (*cf_rtc_set_time)(unsigned long) = null_rtc_set_time;
65 #endif /* CONFIG_GENERIC_CLOCKEVENTS */
66
67 /*
68  * old pre-GENERIC clock code
69  */
70
71 #ifndef CONFIG_GENERIC_CLOCKEVENTS
72 /*
73  * timer_interrupt() needs to keep up the real-time clock,
74  * as well as call the "do_timer()" routine every clocktick
75  */
76 static irqreturn_t timer_interrupt(int irq, void *dummy)
77 {
78 #ifdef CONFIG_COLDFIRE
79         /* kick hardware timer if necessary */
80         if (mach_tick)
81                 mach_tick();
82 #endif
83         do_timer(1);
84 #ifndef CONFIG_SMP
85         update_process_times(user_mode(get_irq_regs()));
86 #endif
87         profile_tick(CPU_PROFILING);
88
89 #ifdef CONFIG_HEARTBEAT
90         /* use power LED as a heartbeat instead -- much more useful
91            for debugging -- based on the version for PReP by Cort */
92         /* acts like an actual heart beat -- ie thump-thump-pause... */
93         if (mach_heartbeat) {
94                 unsigned cnt = 0, period = 0, dist = 0;
95
96                 if (cnt == 0 || cnt == dist)
97                         mach_heartbeat(1);
98                 else if (cnt == 7 || cnt == dist+7)
99                         mach_heartbeat(0);
100
101                 if (++cnt > period) {
102                         cnt = 0;
103                         /* The hyperbolic function below modifies
104                          * the heartbeat period length in dependency
105                          * of the current (5min) load. It goes through
106                          * the points f(0)=126, f(1)=86, f(5)=51,
107                          * f(inf)->30. */
108                         period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT)))
109                                         + 30;
110                         dist = period / 4;
111                 }
112         }
113 #endif /* CONFIG_HEARTBEAT */
114         return IRQ_HANDLED;
115 }
116
117 void __init time_init(void)
118 {
119         struct rtc_time time;
120
121         if (mach_hwclk) {
122                 mach_hwclk(0, &time);
123                 time.tm_year += 1900;
124                 if (time.tm_year < 1970)
125                         time.tm_year += 100;
126                 xtime.tv_sec = mktime(time.tm_year, time.tm_mon, time.tm_mday,
127                                       time.tm_hour, time.tm_min, time.tm_sec);
128                 xtime.tv_nsec = 0;
129         }
130         wall_to_monotonic.tv_sec = -xtime.tv_sec;
131
132         mach_sched_init(timer_interrupt);
133 }
134 #endif /* !CONFIG_GENERIC_CLOCKEVENTS */
135
136 #ifndef CONFIG_GENERIC_TIME
137 /*
138  * This version of gettimeofday has near microsecond resolution.
139  */
140 void do_gettimeofday(struct timeval *tv)
141 {
142         unsigned long flags;
143         unsigned long seq;
144         unsigned long usec, sec;
145         unsigned long max_ntp_tick = tick_usec - tickadj;
146
147         do {
148                 seq = read_seqbegin_irqsave(&xtime_lock, flags);
149
150                 usec = mach_gettimeoffset();
151
152                 /*
153                  * If time_adjust is negative then NTP is slowing the clock
154                  * so make sure not to go into next possible interval.
155                  * Better to lose some accuracy than have time go backwards..
156                  */
157                 if (unlikely(time_adjust < 0))
158                         usec = min(usec, max_ntp_tick);
159
160                 sec = xtime.tv_sec;
161                 usec += xtime.tv_nsec/1000;
162         } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
163
164
165         while (usec >= 1000000) {
166                 usec -= 1000000;
167                 sec++;
168         }
169
170         tv->tv_sec = sec;
171         tv->tv_usec = usec;
172 }
173 EXPORT_SYMBOL(do_gettimeofday);
174
175 int do_settimeofday(struct timespec *tv)
176 {
177         time_t wtm_sec, sec = tv->tv_sec;
178         long wtm_nsec, nsec = tv->tv_nsec;
179
180         if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
181                 return -EINVAL;
182
183         write_seqlock_irq(&xtime_lock);
184         /* This is revolting. We need to set the xtime.tv_nsec
185          * correctly. However, the value in this location is
186          * is value at the last tick.
187          * Discover what correction gettimeofday
188          * would have done, and then undo it!
189          */
190         nsec -= 1000 * mach_gettimeoffset();
191
192         wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
193         wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
194
195         set_normalized_timespec(&xtime, sec, nsec);
196         set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
197
198         ntp_clear();
199         write_sequnlock_irq(&xtime_lock);
200         clock_was_set();
201         return 0;
202 }
203 EXPORT_SYMBOL(do_settimeofday);
204 #endif /* !CONFIG_GENERIC_TIME */
205
206 #ifdef CONFIG_GENERIC_CLOCKEVENTS
207 /*
208  * Clock Evnt setup
209  */
210 static struct clock_event_device clockevent_cfv4 = {
211         .name           = "CFV4 timer2even",
212         .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
213         .rating         = 200,
214         .shift          = 20,
215         .set_mode       = cfv4_set_mode,
216         .set_next_event = cfv4_set_next_event,
217 };
218
219 static int cfv4_set_next_event(unsigned long evt,
220         struct clock_event_device *dev)
221 {
222         return 0;
223 }
224
225 static void cfv4_set_mode(enum clock_event_mode mode,
226         struct clock_event_device *dev)
227 {
228         if (mode != CLOCK_EVT_MODE_ONESHOT)
229                 cfv4_set_next_event((FREQ / HZ), dev);
230 }
231
232 static int __init cfv4_clockevent_init(void)
233 {
234         clockevent_cfv4.mult =
235                         div_sc(FREQ, NSEC_PER_SEC,
236                         clockevent_cfv4.shift);
237         clockevent_cfv4.max_delta_ns =
238                 clockevent_delta2ns((FREQ / HZ),
239                         &clockevent_cfv4);
240         clockevent_cfv4.min_delta_ns =
241                 clockevent_delta2ns(1, &clockevent_cfv4);
242
243         clockevent_cfv4.cpumask = &cpumask_of_cpu(0);
244
245         printk(KERN_INFO "timer: register clockevent\n");
246                 clockevents_register_device(&clockevent_cfv4);
247
248         return 0;
249 }
250
251 /*
252  * clocksource setup
253  */
254
255 struct clocksource clocksource_cfv4 = {
256         .name   = "ColdfireV4",
257         .rating = 250,
258         .mask   = CLOCKSOURCE_MASK(32),
259         .read   = sys_dtim2_read,
260         .shift  = 20,
261         .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
262 };
263
264 /*
265  * Initialize time subsystem.  Called from linux/init/main.c
266  */
267 void __init time_init(void)
268 {
269         int ret;
270
271         printk(KERN_INFO "Initializing time\n");
272 #if 0
273         /* initialize system clocks */
274         clk_init();
275 #endif
276         cfv4_clockevent_init();
277         /* initialize the system timer */
278         /*sys_dtim_init();*/
279         sys_dtim2_init();
280         /* setup initial system time */
281         xtime.tv_sec = cf_rtc_get_time();
282         xtime.tv_nsec = 0;
283         set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec,
284                                 -xtime.tv_nsec);
285
286         /* JKM */
287         clocksource_cfv4.mult = clocksource_hz2mult(FREQ,
288                         clocksource_cfv4.shift);
289
290         /* register our clocksource */
291         ret = clocksource_register(&clocksource_cfv4);
292         if (ret)
293                 printk(KERN_ERR "timer: unable to "
294                         "register clocksource - %d\n", ret);
295 }
296
297 /*
298  * sysfs pieces
299  */
300
301 static struct sysdev_class timer_class = {
302         .name   = "timer",
303 };
304
305 static struct sys_device timer_device = {
306         .id     = 0,
307         .cls    = &timer_class,
308 };
309
310 static int __init timer_init_sysfs(void)
311 {
312         int err = sysdev_class_register(&timer_class);
313         if (!err)
314                 err = sysdev_register(&timer_device);
315         return err;
316 }
317 device_initcall(timer_init_sysfs);
318 #endif /* CONFIG_GENERIC_CLOCKEVENTS */