diff options
Diffstat (limited to 'arch/sh/kernel/time_32.c')
-rw-r--r-- | arch/sh/kernel/time_32.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/arch/sh/kernel/time_32.c b/arch/sh/kernel/time_32.c new file mode 100644 index 000000000000..2bc04bfee738 --- /dev/null +++ b/arch/sh/kernel/time_32.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * arch/sh/kernel/time.c | ||
3 | * | ||
4 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | ||
5 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | ||
6 | * Copyright (C) 2002 - 2007 Paul Mundt | ||
7 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | ||
8 | * | ||
9 | * Some code taken from i386 version. | ||
10 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | ||
11 | */ | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/profile.h> | ||
16 | #include <linux/timex.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/clockchips.h> | ||
19 | #include <asm/clock.h> | ||
20 | #include <asm/rtc.h> | ||
21 | #include <asm/timer.h> | ||
22 | #include <asm/kgdb.h> | ||
23 | |||
24 | struct sys_timer *sys_timer; | ||
25 | |||
26 | /* Move this somewhere more sensible.. */ | ||
27 | DEFINE_SPINLOCK(rtc_lock); | ||
28 | EXPORT_SYMBOL(rtc_lock); | ||
29 | |||
30 | /* Dummy RTC ops */ | ||
31 | static void null_rtc_get_time(struct timespec *tv) | ||
32 | { | ||
33 | tv->tv_sec = mktime(2000, 1, 1, 0, 0, 0); | ||
34 | tv->tv_nsec = 0; | ||
35 | } | ||
36 | |||
37 | static int null_rtc_set_time(const time_t secs) | ||
38 | { | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Null high precision timer functions for systems lacking one. | ||
44 | */ | ||
45 | static cycle_t null_hpt_read(void) | ||
46 | { | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time; | ||
51 | int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time; | ||
52 | |||
53 | #ifndef CONFIG_GENERIC_TIME | ||
54 | void do_gettimeofday(struct timeval *tv) | ||
55 | { | ||
56 | unsigned long flags; | ||
57 | unsigned long seq; | ||
58 | unsigned long usec, sec; | ||
59 | |||
60 | do { | ||
61 | /* | ||
62 | * Turn off IRQs when grabbing xtime_lock, so that | ||
63 | * the sys_timer get_offset code doesn't have to handle it. | ||
64 | */ | ||
65 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | ||
66 | usec = get_timer_offset(); | ||
67 | sec = xtime.tv_sec; | ||
68 | usec += xtime.tv_nsec / NSEC_PER_USEC; | ||
69 | } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); | ||
70 | |||
71 | while (usec >= 1000000) { | ||
72 | usec -= 1000000; | ||
73 | sec++; | ||
74 | } | ||
75 | |||
76 | tv->tv_sec = sec; | ||
77 | tv->tv_usec = usec; | ||
78 | } | ||
79 | EXPORT_SYMBOL(do_gettimeofday); | ||
80 | |||
81 | int do_settimeofday(struct timespec *tv) | ||
82 | { | ||
83 | time_t wtm_sec, sec = tv->tv_sec; | ||
84 | long wtm_nsec, nsec = tv->tv_nsec; | ||
85 | |||
86 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | ||
87 | return -EINVAL; | ||
88 | |||
89 | write_seqlock_irq(&xtime_lock); | ||
90 | /* | ||
91 | * This is revolting. We need to set "xtime" correctly. However, the | ||
92 | * value in this location is the value at the most recent update of | ||
93 | * wall time. Discover what correction gettimeofday() would have | ||
94 | * made, and then undo it! | ||
95 | */ | ||
96 | nsec -= get_timer_offset() * NSEC_PER_USEC; | ||
97 | |||
98 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | ||
99 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | ||
100 | |||
101 | set_normalized_timespec(&xtime, sec, nsec); | ||
102 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | ||
103 | |||
104 | ntp_clear(); | ||
105 | write_sequnlock_irq(&xtime_lock); | ||
106 | clock_was_set(); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | EXPORT_SYMBOL(do_settimeofday); | ||
111 | #endif /* !CONFIG_GENERIC_TIME */ | ||
112 | |||
113 | #ifndef CONFIG_GENERIC_CLOCKEVENTS | ||
114 | /* last time the RTC clock got updated */ | ||
115 | static long last_rtc_update; | ||
116 | |||
117 | /* | ||
118 | * handle_timer_tick() needs to keep up the real-time clock, | ||
119 | * as well as call the "do_timer()" routine every clocktick | ||
120 | */ | ||
121 | void handle_timer_tick(void) | ||
122 | { | ||
123 | do_timer(1); | ||
124 | #ifndef CONFIG_SMP | ||
125 | update_process_times(user_mode(get_irq_regs())); | ||
126 | #endif | ||
127 | if (current->pid) | ||
128 | profile_tick(CPU_PROFILING); | ||
129 | |||
130 | #ifdef CONFIG_HEARTBEAT | ||
131 | if (sh_mv.mv_heartbeat != NULL) | ||
132 | sh_mv.mv_heartbeat(); | ||
133 | #endif | ||
134 | |||
135 | /* | ||
136 | * If we have an externally synchronized Linux clock, then update | ||
137 | * RTC 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. | ||
139 | */ | ||
140 | if (ntp_synced() && | ||
141 | xtime.tv_sec > last_rtc_update + 660 && | ||
142 | (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && | ||
143 | (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { | ||
144 | if (rtc_sh_set_time(xtime.tv_sec) == 0) | ||
145 | last_rtc_update = xtime.tv_sec; | ||
146 | else | ||
147 | /* do it again in 60s */ | ||
148 | last_rtc_update = xtime.tv_sec - 600; | ||
149 | } | ||
150 | } | ||
151 | #endif /* !CONFIG_GENERIC_CLOCKEVENTS */ | ||
152 | |||
153 | #ifdef CONFIG_PM | ||
154 | int timer_suspend(struct sys_device *dev, pm_message_t state) | ||
155 | { | ||
156 | struct sys_timer *sys_timer = container_of(dev, struct sys_timer, dev); | ||
157 | |||
158 | sys_timer->ops->stop(); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | int timer_resume(struct sys_device *dev) | ||
164 | { | ||
165 | struct sys_timer *sys_timer = container_of(dev, struct sys_timer, dev); | ||
166 | |||
167 | sys_timer->ops->start(); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | #else | ||
172 | #define timer_suspend NULL | ||
173 | #define timer_resume NULL | ||
174 | #endif | ||
175 | |||
176 | static struct sysdev_class timer_sysclass = { | ||
177 | .name = "timer", | ||
178 | .suspend = timer_suspend, | ||
179 | .resume = timer_resume, | ||
180 | }; | ||
181 | |||
182 | static int __init timer_init_sysfs(void) | ||
183 | { | ||
184 | int ret = sysdev_class_register(&timer_sysclass); | ||
185 | if (ret != 0) | ||
186 | return ret; | ||
187 | |||
188 | sys_timer->dev.cls = &timer_sysclass; | ||
189 | return sysdev_register(&sys_timer->dev); | ||
190 | } | ||
191 | device_initcall(timer_init_sysfs); | ||
192 | |||
193 | void (*board_time_init)(void); | ||
194 | |||
195 | /* | ||
196 | * Shamelessly based on the MIPS and Sparc64 work. | ||
197 | */ | ||
198 | static unsigned long timer_ticks_per_nsec_quotient __read_mostly; | ||
199 | unsigned long sh_hpt_frequency = 0; | ||
200 | |||
201 | #define NSEC_PER_CYC_SHIFT 10 | ||
202 | |||
203 | struct clocksource clocksource_sh = { | ||
204 | .name = "SuperH", | ||
205 | .rating = 200, | ||
206 | .mask = CLOCKSOURCE_MASK(32), | ||
207 | .read = null_hpt_read, | ||
208 | .shift = 16, | ||
209 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
210 | }; | ||
211 | |||
212 | static void __init init_sh_clocksource(void) | ||
213 | { | ||
214 | if (!sh_hpt_frequency || clocksource_sh.read == null_hpt_read) | ||
215 | return; | ||
216 | |||
217 | clocksource_sh.mult = clocksource_hz2mult(sh_hpt_frequency, | ||
218 | clocksource_sh.shift); | ||
219 | |||
220 | timer_ticks_per_nsec_quotient = | ||
221 | clocksource_hz2mult(sh_hpt_frequency, NSEC_PER_CYC_SHIFT); | ||
222 | |||
223 | clocksource_register(&clocksource_sh); | ||
224 | } | ||
225 | |||
226 | #ifdef CONFIG_GENERIC_TIME | ||
227 | unsigned long long sched_clock(void) | ||
228 | { | ||
229 | unsigned long long ticks = clocksource_sh.read(); | ||
230 | return (ticks * timer_ticks_per_nsec_quotient) >> NSEC_PER_CYC_SHIFT; | ||
231 | } | ||
232 | #endif | ||
233 | |||
234 | void __init time_init(void) | ||
235 | { | ||
236 | if (board_time_init) | ||
237 | board_time_init(); | ||
238 | |||
239 | clk_init(); | ||
240 | |||
241 | rtc_sh_get_time(&xtime); | ||
242 | set_normalized_timespec(&wall_to_monotonic, | ||
243 | -xtime.tv_sec, -xtime.tv_nsec); | ||
244 | |||
245 | /* | ||
246 | * Find the timer to use as the system timer, it will be | ||
247 | * initialized for us. | ||
248 | */ | ||
249 | sys_timer = get_sys_timer(); | ||
250 | printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); | ||
251 | |||
252 | if (sys_timer->ops->read) | ||
253 | clocksource_sh.read = sys_timer->ops->read; | ||
254 | |||
255 | init_sh_clocksource(); | ||
256 | |||
257 | if (sh_hpt_frequency) | ||
258 | printk("Using %lu.%03lu MHz high precision timer.\n", | ||
259 | ((sh_hpt_frequency + 500) / 1000) / 1000, | ||
260 | ((sh_hpt_frequency + 500) / 1000) % 1000); | ||
261 | |||
262 | #if defined(CONFIG_SH_KGDB) | ||
263 | /* | ||
264 | * Set up kgdb as requested. We do it here because the serial | ||
265 | * init uses the timer vars we just set up for figuring baud. | ||
266 | */ | ||
267 | kgdb_init(); | ||
268 | #endif | ||
269 | } | ||