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