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