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