Linux 2.4.0-test10pre4
[davej-history.git] / arch / mips64 / sgi-ip22 / ip22-timer.c
blob18f80682594a884f6ec468a56727bf10230e3ede
1 /* $Id: ip22-timer.c,v 1.6 2000/02/04 07:40:24 ralf Exp $
3 * indy_timer.c: Setting up the clock on the INDY 8254 controller.
5 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
6 * Copytight (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
7 */
8 #include <linux/errno.h>
9 #include <linux/init.h>
10 #include <linux/sched.h>
11 #include <linux/kernel.h>
12 #include <linux/param.h>
13 #include <linux/string.h>
14 #include <linux/mm.h>
15 #include <linux/interrupt.h>
16 #include <linux/timex.h>
17 #include <linux/kernel_stat.h>
19 #include <asm/bootinfo.h>
20 #include <asm/io.h>
21 #include <asm/irq.h>
22 #include <asm/ptrace.h>
23 #include <asm/system.h>
24 #include <asm/sgi/sgi.h>
25 #include <asm/sgi/sgihpc.h>
26 #include <asm/sgi/sgint23.h>
27 #include <asm/sgialib.h>
30 /* Because of a bug in the i8254 timer we need to use the onchip r4k
31 * counter as our system wide timer interrupt running at 100HZ.
33 static unsigned long r4k_offset; /* Amount to increment compare reg each time */
34 static unsigned long r4k_cur; /* What counter should be at next timer irq */
36 extern rwlock_t xtime_lock;
38 static inline void ack_r4ktimer(unsigned long newval)
40 write_32bit_cp0_register(CP0_COMPARE, newval);
43 static int set_rtc_mmss(unsigned long nowtime)
45 struct indy_clock *clock = (struct indy_clock *)INDY_CLOCK_REGS;
46 int retval = 0;
47 int real_seconds, real_minutes, clock_minutes;
49 #define FROB_FROM_CLOCK(x) (((x) & 0xf) | ((((x) & 0xf0) >> 4) * 10));
50 #define FROB_TO_CLOCK(x) ((((((x) & 0xff) / 10)<<4) | (((x) & 0xff) % 10)) & 0xff)
52 clock->cmd &= ~(0x80);
53 clock_minutes = clock->min;
54 clock->cmd |= (0x80);
56 clock_minutes = FROB_FROM_CLOCK(clock_minutes);
57 real_seconds = nowtime % 60;
58 real_minutes = nowtime / 60;
60 if(((abs(real_minutes - clock_minutes) + 15)/30) & 1)
61 real_minutes += 30; /* correct for half hour time zone */
63 real_minutes %= 60;
64 if(abs(real_minutes - clock_minutes) < 30) {
65 /* Force clock oscillator to be on. */
66 clock->month &= ~(0x80);
68 /* Write real_seconds and real_minutes into the Dallas. */
69 clock->cmd &= ~(0x80);
70 clock->sec = real_seconds;
71 clock->min = real_minutes;
72 clock->cmd |= (0x80);
73 } else
74 return -1;
76 #undef FROB_FROM_CLOCK
77 #undef FROB_TO_CLOCK
79 return retval;
82 static long last_rtc_update;
83 unsigned long missed_heart_beats;
85 void indy_timer_interrupt(struct pt_regs *regs)
87 unsigned long count;
88 int irq = 7;
90 write_lock(&xtime_lock);
91 /* Ack timer and compute new compare. */
92 count = read_32bit_cp0_register(CP0_COUNT);
93 /* This has races. */
94 if ((count - r4k_cur) >= r4k_offset) {
95 /* If this happens to often we'll need to compensate. */
96 missed_heart_beats++;
97 r4k_cur = count + r4k_offset;
99 else
100 r4k_cur += r4k_offset;
101 ack_r4ktimer(r4k_cur);
102 kstat.irqs[0][irq]++;
103 do_timer(regs);
105 /* We update the Dallas time of day approx. every 11 minutes,
106 * because of how the numbers work out we need to make
107 * absolutely sure we do this update within 500ms before the
108 * next second starts, thus the following code.
110 if ((time_status & STA_UNSYNC) == 0 &&
111 xtime.tv_sec > last_rtc_update + 660 &&
112 xtime.tv_usec >= 500000 - (tick >> 1) &&
113 xtime.tv_usec <= 500000 + (tick >> 1)) {
114 if (set_rtc_mmss(xtime.tv_sec) == 0)
115 last_rtc_update = xtime.tv_sec;
116 else
117 /* do it again in 60s */
118 last_rtc_update = xtime.tv_sec - 600;
120 write_unlock(&xtime_lock);
123 static unsigned long dosample(volatile unsigned char *tcwp,
124 volatile unsigned char *tc2p)
126 unsigned long ct0, ct1;
127 unsigned char msb, lsb;
129 /* Start the counter. */
130 *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MRGEN);
131 *tc2p = (SGINT_TCSAMP_COUNTER & 0xff);
132 *tc2p = (SGINT_TCSAMP_COUNTER >> 8);
134 /* Get initial counter invariant */
135 ct0 = read_32bit_cp0_register(CP0_COUNT);
137 /* Latch and spin until top byte of counter2 is zero */
138 do {
139 *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT);
140 lsb = *tc2p;
141 msb = *tc2p;
142 ct1 = read_32bit_cp0_register(CP0_COUNT);
143 } while(msb);
145 /* Stop the counter. */
146 *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST);
148 /* Return the difference, this is how far the r4k counter increments
149 * for every one HZ.
151 return ct1 - ct0;
154 static unsigned long __init get_indy_time(void)
156 struct indy_clock *clock = (struct indy_clock *)INDY_CLOCK_REGS;
157 unsigned int year, mon, day, hour, min, sec;
159 /* Freeze it. */
160 clock->cmd &= ~(0x80);
162 /* Read regs. */
163 sec = clock->sec;
164 min = clock->min;
165 hour = (clock->hr & 0x3f);
166 day = (clock->date & 0x3f);
167 mon = (clock->month & 0x1f);
168 year = clock->year;
170 /* Unfreeze clock. */
171 clock->cmd |= 0x80;
173 /* Frob the bits. */
174 #define FROB1(x) (((x) & 0xf) + ((((x) & 0xf0) >> 4) * 10));
175 #define FROB2(x) (((x) & 0xf) + (((((x) & 0xf0) >> 4) & 0x3) * 10));
177 /* XXX Should really check that secs register is the same
178 * XXX as when we first read it and if not go back and
179 * XXX read the regs above again.
181 sec = FROB1(sec); min = FROB1(min); day = FROB1(day);
182 mon = FROB1(mon); year = FROB1(year);
183 hour = FROB2(hour);
185 #undef FROB1
186 #undef FROB2
188 /* Wheee... */
189 if(year < 45)
190 year += 30;
191 if ((year += 1940) < 1970)
192 year += 100;
194 return mktime(year, mon, day, hour, min, sec);
197 #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
199 void __init indy_timer_init(void)
201 struct sgi_ioc_timers *p;
202 volatile unsigned char *tcwp, *tc2p;
204 /* Figure out the r4k offset, the algorithm is very simple and works
205 * in _all_ cases as long as the 8254 counter register itself works ok
206 * (as an interrupt driving timer it does not because of bug, this is
207 * why we are using the onchip r4k counter/compare register to serve
208 * this purpose, but for r4k_offset calculation it will work ok for us).
209 * There are other very complicated ways of performing this calculation
210 * but this one works just fine so I am not going to futz around. ;-)
212 p = ioc_timers;
213 tcwp = &p->tcword;
214 tc2p = &p->tcnt2;
216 printk("calculating r4koff... ");
217 dosample(tcwp, tc2p); /* First sample. */
218 dosample(tcwp, tc2p); /* Eat one. */
219 r4k_offset = dosample(tcwp, tc2p); /* Second sample. */
221 printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
223 r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset);
224 write_32bit_cp0_register(CP0_COMPARE, r4k_cur);
225 set_cp0_status(ST0_IM, ALLINTS);
226 sti();
228 write_lock_irq(&xtime_lock);
229 xtime.tv_sec = get_indy_time(); /* Read time from RTC. */
230 xtime.tv_usec = 0;
231 write_unlock_irq(&xtime_lock);
234 void indy_8254timer_irq(void)
236 int cpu = smp_processor_id();
237 int irq = 4;
239 irq_enter(cpu, irq);
240 kstat.irqs[0][irq]++;
241 printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n");
242 prom_getchar();
243 ArcEnterInteractiveMode();
244 irq_exit(cpu, irq);
247 void do_gettimeofday(struct timeval *tv)
249 unsigned long flags;
251 read_lock_irqsave(&xtime_lock, flags);
252 *tv = xtime;
253 read_unlock_irqrestore(&xtime_lock, flags);
256 void do_settimeofday(struct timeval *tv)
258 write_lock_irq(&xtime_lock);
259 xtime = *tv;
260 time_state = TIME_BAD;
261 time_maxerror = MAXPHASE;
262 time_esterror = MAXPHASE;
263 write_unlock_irq(&xtime_lock);