Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: time.c,v 1.60 2002/01/23 14:33:55 davem Exp $ |
2 | * linux/arch/sparc/kernel/time.c | |
3 | * | |
4 | * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | |
5 | * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) | |
6 | * | |
7 | * Chris Davis (cdavis@cois.on.ca) 03/27/1998 | |
8 | * Added support for the intersil on the sun4/4200 | |
9 | * | |
10 | * Gleb Raiko (rajko@mech.math.msu.su) 08/18/1998 | |
11 | * Support for MicroSPARC-IIep, PCI CPU. | |
12 | * | |
13 | * This file handles the Sparc specific time handling details. | |
14 | * | |
15 | * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 | |
16 | * "A Kernel Model for Precision Timekeeping" by Dave Mills | |
17 | */ | |
1da177e4 LT |
18 | #include <linux/errno.h> |
19 | #include <linux/module.h> | |
20 | #include <linux/sched.h> | |
21 | #include <linux/kernel.h> | |
22 | #include <linux/param.h> | |
23 | #include <linux/string.h> | |
24 | #include <linux/mm.h> | |
25 | #include <linux/interrupt.h> | |
26 | #include <linux/time.h> | |
27 | #include <linux/timex.h> | |
28 | #include <linux/init.h> | |
29 | #include <linux/pci.h> | |
30 | #include <linux/ioport.h> | |
31 | #include <linux/profile.h> | |
32 | ||
33 | #include <asm/oplib.h> | |
1da177e4 LT |
34 | #include <asm/timer.h> |
35 | #include <asm/mostek.h> | |
36 | #include <asm/system.h> | |
37 | #include <asm/irq.h> | |
38 | #include <asm/io.h> | |
39 | #include <asm/idprom.h> | |
40 | #include <asm/machines.h> | |
41 | #include <asm/sun4paddr.h> | |
42 | #include <asm/page.h> | |
43 | #include <asm/pcic.h> | |
ee5caf0e | 44 | #include <asm/of_device.h> |
0d84438d | 45 | #include <asm/irq_regs.h> |
1da177e4 | 46 | |
1da177e4 LT |
47 | DEFINE_SPINLOCK(rtc_lock); |
48 | enum sparc_clock_type sp_clock_typ; | |
49 | DEFINE_SPINLOCK(mostek_lock); | |
50 | void __iomem *mstk48t02_regs = NULL; | |
c316ef04 | 51 | static struct mostek48t08 __iomem *mstk48t08_regs = NULL; |
1da177e4 LT |
52 | static int set_rtc_mmss(unsigned long); |
53 | static int sbus_do_settimeofday(struct timespec *tv); | |
54 | ||
55 | #ifdef CONFIG_SUN4 | |
56 | struct intersil *intersil_clock; | |
57 | #define intersil_cmd(intersil_reg, intsil_cmd) intersil_reg->int_cmd_reg = \ | |
58 | (intsil_cmd) | |
59 | ||
60 | #define intersil_intr(intersil_reg, intsil_cmd) intersil_reg->int_intr_reg = \ | |
61 | (intsil_cmd) | |
62 | ||
63 | #define intersil_start(intersil_reg) intersil_cmd(intersil_reg, \ | |
64 | ( INTERSIL_START | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\ | |
65 | INTERSIL_INTR_ENABLE)) | |
66 | ||
67 | #define intersil_stop(intersil_reg) intersil_cmd(intersil_reg, \ | |
68 | ( INTERSIL_STOP | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\ | |
69 | INTERSIL_INTR_ENABLE)) | |
70 | ||
71 | #define intersil_read_intr(intersil_reg, towhere) towhere = \ | |
72 | intersil_reg->int_intr_reg | |
73 | ||
74 | #endif | |
75 | ||
76 | unsigned long profile_pc(struct pt_regs *regs) | |
77 | { | |
78 | extern char __copy_user_begin[], __copy_user_end[]; | |
79 | extern char __atomic_begin[], __atomic_end[]; | |
80 | extern char __bzero_begin[], __bzero_end[]; | |
81 | extern char __bitops_begin[], __bitops_end[]; | |
82 | ||
83 | unsigned long pc = regs->pc; | |
84 | ||
85 | if (in_lock_functions(pc) || | |
86 | (pc >= (unsigned long) __copy_user_begin && | |
87 | pc < (unsigned long) __copy_user_end) || | |
88 | (pc >= (unsigned long) __atomic_begin && | |
89 | pc < (unsigned long) __atomic_end) || | |
90 | (pc >= (unsigned long) __bzero_begin && | |
91 | pc < (unsigned long) __bzero_end) || | |
92 | (pc >= (unsigned long) __bitops_begin && | |
93 | pc < (unsigned long) __bitops_end)) | |
94 | pc = regs->u_regs[UREG_RETPC]; | |
95 | return pc; | |
96 | } | |
97 | ||
9550e59c MH |
98 | EXPORT_SYMBOL(profile_pc); |
99 | ||
1da177e4 LT |
100 | __volatile__ unsigned int *master_l10_counter; |
101 | __volatile__ unsigned int *master_l10_limit; | |
102 | ||
103 | /* | |
104 | * timer_interrupt() needs to keep up the real-time clock, | |
105 | * as well as call the "do_timer()" routine every clocktick | |
106 | */ | |
107 | ||
108 | #define TICK_SIZE (tick_nsec / 1000) | |
109 | ||
0d84438d | 110 | irqreturn_t timer_interrupt(int irq, void *dev_id) |
1da177e4 LT |
111 | { |
112 | /* last time the cmos clock got updated */ | |
113 | static long last_rtc_update; | |
114 | ||
115 | #ifndef CONFIG_SMP | |
0d84438d | 116 | profile_tick(CPU_PROFILING); |
1da177e4 LT |
117 | #endif |
118 | ||
119 | /* Protect counter clear so that do_gettimeoffset works */ | |
120 | write_seqlock(&xtime_lock); | |
121 | #ifdef CONFIG_SUN4 | |
122 | if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) || | |
123 | (idprom->id_machtype == (SM_SUN4 | SM_4_110))) { | |
124 | int temp; | |
125 | intersil_read_intr(intersil_clock, temp); | |
126 | /* re-enable the irq */ | |
127 | enable_pil_irq(10); | |
128 | } | |
129 | #endif | |
130 | clear_clock_irq(); | |
131 | ||
3171a030 | 132 | do_timer(1); |
1da177e4 | 133 | #ifndef CONFIG_SMP |
0d84438d | 134 | update_process_times(user_mode(get_irq_regs())); |
1da177e4 LT |
135 | #endif |
136 | ||
137 | ||
138 | /* Determine when to update the Mostek clock. */ | |
b149ee22 | 139 | if (ntp_synced() && |
1da177e4 LT |
140 | xtime.tv_sec > last_rtc_update + 660 && |
141 | (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && | |
142 | (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { | |
143 | if (set_rtc_mmss(xtime.tv_sec) == 0) | |
144 | last_rtc_update = xtime.tv_sec; | |
145 | else | |
146 | last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ | |
147 | } | |
148 | write_sequnlock(&xtime_lock); | |
149 | ||
150 | return IRQ_HANDLED; | |
151 | } | |
152 | ||
153 | /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ | |
154 | static void __init kick_start_clock(void) | |
155 | { | |
156 | struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; | |
157 | unsigned char sec; | |
158 | int i, count; | |
159 | ||
160 | prom_printf("CLOCK: Clock was stopped. Kick start "); | |
161 | ||
162 | spin_lock_irq(&mostek_lock); | |
163 | ||
164 | /* Turn on the kick start bit to start the oscillator. */ | |
165 | regs->creg |= MSTK_CREG_WRITE; | |
166 | regs->sec &= ~MSTK_STOP; | |
167 | regs->hour |= MSTK_KICK_START; | |
168 | regs->creg &= ~MSTK_CREG_WRITE; | |
169 | ||
170 | spin_unlock_irq(&mostek_lock); | |
171 | ||
172 | /* Delay to allow the clock oscillator to start. */ | |
173 | sec = MSTK_REG_SEC(regs); | |
174 | for (i = 0; i < 3; i++) { | |
175 | while (sec == MSTK_REG_SEC(regs)) | |
176 | for (count = 0; count < 100000; count++) | |
177 | /* nothing */ ; | |
178 | prom_printf("."); | |
179 | sec = regs->sec; | |
180 | } | |
181 | prom_printf("\n"); | |
182 | ||
183 | spin_lock_irq(&mostek_lock); | |
184 | ||
185 | /* Turn off kick start and set a "valid" time and date. */ | |
186 | regs->creg |= MSTK_CREG_WRITE; | |
187 | regs->hour &= ~MSTK_KICK_START; | |
188 | MSTK_SET_REG_SEC(regs,0); | |
189 | MSTK_SET_REG_MIN(regs,0); | |
190 | MSTK_SET_REG_HOUR(regs,0); | |
191 | MSTK_SET_REG_DOW(regs,5); | |
192 | MSTK_SET_REG_DOM(regs,1); | |
193 | MSTK_SET_REG_MONTH(regs,8); | |
194 | MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO); | |
195 | regs->creg &= ~MSTK_CREG_WRITE; | |
196 | ||
197 | spin_unlock_irq(&mostek_lock); | |
198 | ||
199 | /* Ensure the kick start bit is off. If it isn't, turn it off. */ | |
200 | while (regs->hour & MSTK_KICK_START) { | |
201 | prom_printf("CLOCK: Kick start still on!\n"); | |
202 | ||
203 | spin_lock_irq(&mostek_lock); | |
204 | regs->creg |= MSTK_CREG_WRITE; | |
205 | regs->hour &= ~MSTK_KICK_START; | |
206 | regs->creg &= ~MSTK_CREG_WRITE; | |
207 | spin_unlock_irq(&mostek_lock); | |
208 | } | |
209 | ||
210 | prom_printf("CLOCK: Kick start procedure successful.\n"); | |
211 | } | |
212 | ||
213 | /* Return nonzero if the clock chip battery is low. */ | |
214 | static __inline__ int has_low_battery(void) | |
215 | { | |
216 | struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; | |
217 | unsigned char data1, data2; | |
218 | ||
219 | spin_lock_irq(&mostek_lock); | |
220 | data1 = regs->eeprom[0]; /* Read some data. */ | |
221 | regs->eeprom[0] = ~data1; /* Write back the complement. */ | |
222 | data2 = regs->eeprom[0]; /* Read back the complement. */ | |
223 | regs->eeprom[0] = data1; /* Restore the original value. */ | |
224 | spin_unlock_irq(&mostek_lock); | |
225 | ||
226 | return (data1 == data2); /* Was the write blocked? */ | |
227 | } | |
228 | ||
96ba989d BB |
229 | static void __init mostek_set_system_time(void) |
230 | { | |
231 | unsigned int year, mon, day, hour, min, sec; | |
232 | struct mostek48t02 *mregs; | |
233 | ||
234 | mregs = (struct mostek48t02 *)mstk48t02_regs; | |
235 | if(!mregs) { | |
236 | prom_printf("Something wrong, clock regs not mapped yet.\n"); | |
237 | prom_halt(); | |
238 | } | |
239 | spin_lock_irq(&mostek_lock); | |
240 | mregs->creg |= MSTK_CREG_READ; | |
241 | sec = MSTK_REG_SEC(mregs); | |
242 | min = MSTK_REG_MIN(mregs); | |
243 | hour = MSTK_REG_HOUR(mregs); | |
244 | day = MSTK_REG_DOM(mregs); | |
245 | mon = MSTK_REG_MONTH(mregs); | |
246 | year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); | |
247 | xtime.tv_sec = mktime(year, mon, day, hour, min, sec); | |
248 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); | |
249 | set_normalized_timespec(&wall_to_monotonic, | |
250 | -xtime.tv_sec, -xtime.tv_nsec); | |
251 | mregs->creg &= ~MSTK_CREG_READ; | |
252 | spin_unlock_irq(&mostek_lock); | |
253 | } | |
254 | ||
1da177e4 LT |
255 | /* Probe for the real time clock chip on Sun4 */ |
256 | static __inline__ void sun4_clock_probe(void) | |
257 | { | |
258 | #ifdef CONFIG_SUN4 | |
259 | int temp; | |
260 | struct resource r; | |
261 | ||
262 | memset(&r, 0, sizeof(r)); | |
263 | if( idprom->id_machtype == (SM_SUN4 | SM_4_330) ) { | |
264 | sp_clock_typ = MSTK48T02; | |
265 | r.start = sun4_clock_physaddr; | |
266 | mstk48t02_regs = sbus_ioremap(&r, 0, | |
267 | sizeof(struct mostek48t02), NULL); | |
268 | mstk48t08_regs = NULL; /* To catch weirdness */ | |
269 | intersil_clock = NULL; /* just in case */ | |
270 | ||
271 | /* Kick start the clock if it is completely stopped. */ | |
272 | if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) | |
273 | kick_start_clock(); | |
274 | } else if( idprom->id_machtype == (SM_SUN4 | SM_4_260)) { | |
275 | /* intersil setup code */ | |
276 | printk("Clock: INTERSIL at %8x ",sun4_clock_physaddr); | |
277 | sp_clock_typ = INTERSIL; | |
278 | r.start = sun4_clock_physaddr; | |
279 | intersil_clock = (struct intersil *) | |
280 | sbus_ioremap(&r, 0, sizeof(*intersil_clock), "intersil"); | |
281 | mstk48t02_regs = 0; /* just be sure */ | |
282 | mstk48t08_regs = NULL; /* ditto */ | |
283 | /* initialise the clock */ | |
284 | ||
285 | intersil_intr(intersil_clock,INTERSIL_INT_100HZ); | |
286 | ||
287 | intersil_start(intersil_clock); | |
288 | ||
289 | intersil_read_intr(intersil_clock, temp); | |
290 | while (!(temp & 0x80)) | |
291 | intersil_read_intr(intersil_clock, temp); | |
292 | ||
293 | intersil_read_intr(intersil_clock, temp); | |
294 | while (!(temp & 0x80)) | |
295 | intersil_read_intr(intersil_clock, temp); | |
296 | ||
297 | intersil_stop(intersil_clock); | |
298 | ||
299 | } | |
300 | #endif | |
301 | } | |
302 | ||
96ba989d | 303 | #ifndef CONFIG_SUN4 |
ee5caf0e | 304 | static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) |
1da177e4 | 305 | { |
ee5caf0e DM |
306 | struct device_node *dp = op->node; |
307 | char *model = of_get_property(dp, "model", NULL); | |
1da177e4 | 308 | |
ee5caf0e DM |
309 | if (!model) |
310 | return -ENODEV; | |
1da177e4 | 311 | |
ee5caf0e | 312 | if (!strcmp(model, "mk48t02")) { |
1da177e4 | 313 | sp_clock_typ = MSTK48T02; |
ee5caf0e | 314 | |
1da177e4 | 315 | /* Map the clock register io area read-only */ |
ee5caf0e DM |
316 | mstk48t02_regs = of_ioremap(&op->resource[0], 0, |
317 | sizeof(struct mostek48t02), | |
318 | "mk48t02"); | |
1da177e4 | 319 | mstk48t08_regs = NULL; /* To catch weirdness */ |
ee5caf0e | 320 | } else if (!strcmp(model, "mk48t08")) { |
1da177e4 | 321 | sp_clock_typ = MSTK48T08; |
ee5caf0e DM |
322 | mstk48t08_regs = of_ioremap(&op->resource[0], 0, |
323 | sizeof(struct mostek48t08), | |
324 | "mk48t08"); | |
1da177e4 LT |
325 | |
326 | mstk48t02_regs = &mstk48t08_regs->regs; | |
ee5caf0e DM |
327 | } else |
328 | return -ENODEV; | |
1da177e4 LT |
329 | |
330 | /* Report a low battery voltage condition. */ | |
331 | if (has_low_battery()) | |
332 | printk(KERN_CRIT "NVRAM: Low battery voltage!\n"); | |
333 | ||
334 | /* Kick start the clock if it is completely stopped. */ | |
335 | if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) | |
336 | kick_start_clock(); | |
ee5caf0e | 337 | |
96ba989d BB |
338 | mostek_set_system_time(); |
339 | ||
ee5caf0e DM |
340 | return 0; |
341 | } | |
342 | ||
343 | static struct of_device_id clock_match[] = { | |
344 | { | |
345 | .name = "eeprom", | |
346 | }, | |
347 | {}, | |
348 | }; | |
349 | ||
350 | static struct of_platform_driver clock_driver = { | |
351 | .name = "clock", | |
352 | .match_table = clock_match, | |
353 | .probe = clock_probe, | |
354 | }; | |
355 | ||
356 | ||
357 | /* Probe for the mostek real time clock chip. */ | |
96ba989d | 358 | static int __init clock_init(void) |
ee5caf0e | 359 | { |
96ba989d | 360 | return of_register_driver(&clock_driver, &of_bus_type); |
1da177e4 LT |
361 | } |
362 | ||
96ba989d BB |
363 | /* Must be after subsys_initcall() so that busses are probed. Must |
364 | * be before device_initcall() because things like the RTC driver | |
365 | * need to see the clock registers. | |
366 | */ | |
367 | fs_initcall(clock_init); | |
368 | #endif /* !CONFIG_SUN4 */ | |
369 | ||
1da177e4 LT |
370 | void __init sbus_time_init(void) |
371 | { | |
1da177e4 LT |
372 | |
373 | BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM); | |
374 | btfixup(); | |
375 | ||
376 | if (ARCH_SUN4) | |
377 | sun4_clock_probe(); | |
1da177e4 LT |
378 | |
379 | sparc_init_timers(timer_interrupt); | |
380 | ||
381 | #ifdef CONFIG_SUN4 | |
382 | if(idprom->id_machtype == (SM_SUN4 | SM_4_330)) { | |
96ba989d | 383 | mostek_set_system_time(); |
1da177e4 LT |
384 | } else if(idprom->id_machtype == (SM_SUN4 | SM_4_260) ) { |
385 | /* initialise the intersil on sun4 */ | |
96ba989d BB |
386 | unsigned int year, mon, day, hour, min, sec; |
387 | int temp; | |
388 | struct intersil *iregs; | |
1da177e4 LT |
389 | |
390 | iregs=intersil_clock; | |
391 | if(!iregs) { | |
392 | prom_printf("Something wrong, clock regs not mapped yet.\n"); | |
393 | prom_halt(); | |
394 | } | |
395 | ||
396 | intersil_intr(intersil_clock,INTERSIL_INT_100HZ); | |
397 | disable_pil_irq(10); | |
398 | intersil_stop(iregs); | |
399 | intersil_read_intr(intersil_clock, temp); | |
400 | ||
401 | temp = iregs->clk.int_csec; | |
402 | ||
403 | sec = iregs->clk.int_sec; | |
404 | min = iregs->clk.int_min; | |
405 | hour = iregs->clk.int_hour; | |
406 | day = iregs->clk.int_day; | |
407 | mon = iregs->clk.int_month; | |
408 | year = MSTK_CVT_YEAR(iregs->clk.int_year); | |
409 | ||
410 | enable_pil_irq(10); | |
411 | intersil_start(iregs); | |
412 | ||
413 | xtime.tv_sec = mktime(year, mon, day, hour, min, sec); | |
414 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); | |
415 | set_normalized_timespec(&wall_to_monotonic, | |
416 | -xtime.tv_sec, -xtime.tv_nsec); | |
417 | printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec); | |
418 | } | |
419 | #endif | |
420 | ||
421 | /* Now that OBP ticker has been silenced, it is safe to enable IRQ. */ | |
422 | local_irq_enable(); | |
423 | } | |
424 | ||
425 | void __init time_init(void) | |
426 | { | |
427 | #ifdef CONFIG_PCI | |
428 | extern void pci_time_init(void); | |
429 | if (pcic_present()) { | |
430 | pci_time_init(); | |
431 | return; | |
432 | } | |
433 | #endif | |
434 | sbus_time_init(); | |
435 | } | |
436 | ||
3115624e | 437 | static inline unsigned long do_gettimeoffset(void) |
1da177e4 LT |
438 | { |
439 | return (*master_l10_counter >> 10) & 0x1fffff; | |
440 | } | |
441 | ||
442 | /* | |
443 | * Returns nanoseconds | |
444 | * XXX This is a suboptimal implementation. | |
445 | */ | |
446 | unsigned long long sched_clock(void) | |
447 | { | |
448 | return (unsigned long long)jiffies * (1000000000 / HZ); | |
449 | } | |
450 | ||
451 | /* Ok, my cute asm atomicity trick doesn't work anymore. | |
452 | * There are just too many variables that need to be protected | |
8ef38609 | 453 | * now (both members of xtime, et al.) |
1da177e4 LT |
454 | */ |
455 | void do_gettimeofday(struct timeval *tv) | |
456 | { | |
457 | unsigned long flags; | |
458 | unsigned long seq; | |
459 | unsigned long usec, sec; | |
460 | unsigned long max_ntp_tick = tick_usec - tickadj; | |
461 | ||
462 | do { | |
1da177e4 LT |
463 | seq = read_seqbegin_irqsave(&xtime_lock, flags); |
464 | usec = do_gettimeoffset(); | |
1da177e4 LT |
465 | |
466 | /* | |
467 | * If time_adjust is negative then NTP is slowing the clock | |
468 | * so make sure not to go into next possible interval. | |
469 | * Better to lose some accuracy than have time go backwards.. | |
470 | */ | |
8ef38609 | 471 | if (unlikely(time_adjust < 0)) |
1da177e4 LT |
472 | usec = min(usec, max_ntp_tick); |
473 | ||
1da177e4 LT |
474 | sec = xtime.tv_sec; |
475 | usec += (xtime.tv_nsec / 1000); | |
476 | } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); | |
477 | ||
478 | while (usec >= 1000000) { | |
479 | usec -= 1000000; | |
480 | sec++; | |
481 | } | |
482 | ||
483 | tv->tv_sec = sec; | |
484 | tv->tv_usec = usec; | |
485 | } | |
486 | ||
487 | EXPORT_SYMBOL(do_gettimeofday); | |
488 | ||
489 | int do_settimeofday(struct timespec *tv) | |
490 | { | |
491 | int ret; | |
492 | ||
493 | write_seqlock_irq(&xtime_lock); | |
494 | ret = bus_do_settimeofday(tv); | |
495 | write_sequnlock_irq(&xtime_lock); | |
496 | clock_was_set(); | |
497 | return ret; | |
498 | } | |
499 | ||
500 | EXPORT_SYMBOL(do_settimeofday); | |
501 | ||
502 | static int sbus_do_settimeofday(struct timespec *tv) | |
503 | { | |
504 | time_t wtm_sec, sec = tv->tv_sec; | |
505 | long wtm_nsec, nsec = tv->tv_nsec; | |
506 | ||
507 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | |
508 | return -EINVAL; | |
509 | ||
510 | /* | |
511 | * This is revolting. We need to set "xtime" correctly. However, the | |
512 | * value in this location is the value at the most recent update of | |
513 | * wall time. Discover what correction gettimeofday() would have | |
514 | * made, and then undo it! | |
515 | */ | |
8ef38609 | 516 | nsec -= 1000 * do_gettimeoffset(); |
1da177e4 LT |
517 | |
518 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | |
519 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | |
520 | ||
521 | set_normalized_timespec(&xtime, sec, nsec); | |
522 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | |
523 | ||
b149ee22 | 524 | ntp_clear(); |
1da177e4 LT |
525 | return 0; |
526 | } | |
527 | ||
528 | /* | |
529 | * BUG: This routine does not handle hour overflow properly; it just | |
530 | * sets the minutes. Usually you won't notice until after reboot! | |
531 | */ | |
532 | static int set_rtc_mmss(unsigned long nowtime) | |
533 | { | |
534 | int real_seconds, real_minutes, mostek_minutes; | |
535 | struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; | |
536 | unsigned long flags; | |
537 | #ifdef CONFIG_SUN4 | |
538 | struct intersil *iregs = intersil_clock; | |
539 | int temp; | |
540 | #endif | |
541 | ||
542 | /* Not having a register set can lead to trouble. */ | |
543 | if (!regs) { | |
544 | #ifdef CONFIG_SUN4 | |
545 | if(!iregs) | |
546 | return -1; | |
547 | else { | |
548 | temp = iregs->clk.int_csec; | |
549 | ||
550 | mostek_minutes = iregs->clk.int_min; | |
551 | ||
552 | real_seconds = nowtime % 60; | |
553 | real_minutes = nowtime / 60; | |
554 | if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) | |
555 | real_minutes += 30; /* correct for half hour time zone */ | |
556 | real_minutes %= 60; | |
557 | ||
558 | if (abs(real_minutes - mostek_minutes) < 30) { | |
559 | intersil_stop(iregs); | |
560 | iregs->clk.int_sec=real_seconds; | |
561 | iregs->clk.int_min=real_minutes; | |
562 | intersil_start(iregs); | |
563 | } else { | |
564 | printk(KERN_WARNING | |
565 | "set_rtc_mmss: can't update from %d to %d\n", | |
566 | mostek_minutes, real_minutes); | |
567 | return -1; | |
568 | } | |
569 | ||
570 | return 0; | |
571 | } | |
572 | #endif | |
573 | } | |
574 | ||
575 | spin_lock_irqsave(&mostek_lock, flags); | |
576 | /* Read the current RTC minutes. */ | |
577 | regs->creg |= MSTK_CREG_READ; | |
578 | mostek_minutes = MSTK_REG_MIN(regs); | |
579 | regs->creg &= ~MSTK_CREG_READ; | |
580 | ||
581 | /* | |
582 | * since we're only adjusting minutes and seconds, | |
583 | * don't interfere with hour overflow. This avoids | |
584 | * messing with unknown time zones but requires your | |
585 | * RTC not to be off by more than 15 minutes | |
586 | */ | |
587 | real_seconds = nowtime % 60; | |
588 | real_minutes = nowtime / 60; | |
589 | if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) | |
590 | real_minutes += 30; /* correct for half hour time zone */ | |
591 | real_minutes %= 60; | |
592 | ||
593 | if (abs(real_minutes - mostek_minutes) < 30) { | |
594 | regs->creg |= MSTK_CREG_WRITE; | |
595 | MSTK_SET_REG_SEC(regs,real_seconds); | |
596 | MSTK_SET_REG_MIN(regs,real_minutes); | |
597 | regs->creg &= ~MSTK_CREG_WRITE; | |
598 | spin_unlock_irqrestore(&mostek_lock, flags); | |
599 | return 0; | |
600 | } else { | |
601 | spin_unlock_irqrestore(&mostek_lock, flags); | |
602 | return -1; | |
603 | } | |
604 | } |