patch-2.4.10 linux/arch/mips/sgi/kernel/indy_timer.c
Next file: linux/arch/mips/sgi/kernel/promcon.c
Previous file: linux/arch/mips/sgi/kernel/indy_sc.c
Back to the patch index
Back to the overall index
- Lines: 267
- Date:
Wed Dec 31 16:00:00 1969
- Orig file:
v2.4.9/linux/arch/mips/sgi/kernel/indy_timer.c
- Orig date:
Mon Oct 16 12:58:51 2000
diff -u --recursive --new-file v2.4.9/linux/arch/mips/sgi/kernel/indy_timer.c linux/arch/mips/sgi/kernel/indy_timer.c
@@ -1,266 +0,0 @@
-/* $Id: indy_timer.c,v 1.17 2000/01/21 22:34:03 ralf Exp $
- *
- * indy_timer.c: Setting up the clock on the INDY 8254 controller.
- *
- * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
- * Copytight (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
- */
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/timex.h>
-#include <linux/kernel_stat.h>
-
-#include <asm/bootinfo.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/ptrace.h>
-#include <asm/system.h>
-#include <asm/sgialib.h>
-#include <asm/sgi/sgi.h>
-#include <asm/sgi/sgihpc.h>
-#include <asm/sgi/sgint23.h>
-
-
-/* Because of a bug in the i8254 timer we need to use the onchip r4k
- * counter as our system wide timer interrupt running at 100HZ.
- */
-static unsigned long r4k_offset; /* Amount to increment compare reg each time */
-static unsigned long r4k_cur; /* What counter should be at next timer irq */
-
-extern rwlock_t xtime_lock;
-
-static inline void ack_r4ktimer(unsigned long newval)
-{
- write_32bit_cp0_register(CP0_COMPARE, newval);
-}
-
-static int set_rtc_mmss(unsigned long nowtime)
-{
- struct indy_clock *clock = (struct indy_clock *)INDY_CLOCK_REGS;
- int retval = 0;
- int real_seconds, real_minutes, clock_minutes;
-
-#define FROB_FROM_CLOCK(x) (((x) & 0xf) | ((((x) & 0xf0) >> 4) * 10));
-#define FROB_TO_CLOCK(x) ((((((x) & 0xff) / 10)<<4) | (((x) & 0xff) % 10)) & 0xff)
-
- clock->cmd &= ~(0x80);
- clock_minutes = clock->min;
- clock->cmd |= (0x80);
-
- clock_minutes = FROB_FROM_CLOCK(clock_minutes);
- real_seconds = nowtime % 60;
- real_minutes = nowtime / 60;
-
- if(((abs(real_minutes - clock_minutes) + 15)/30) & 1)
- real_minutes += 30; /* correct for half hour time zone */
-
- real_minutes %= 60;
- if(abs(real_minutes - clock_minutes) < 30) {
- /* Force clock oscillator to be on. */
- clock->month &= ~(0x80);
-
- /* Write real_seconds and real_minutes into the Dallas. */
- clock->cmd &= ~(0x80);
- clock->sec = real_seconds;
- clock->min = real_minutes;
- clock->cmd |= (0x80);
- } else
- return -1;
-
-#undef FROB_FROM_CLOCK
-#undef FROB_TO_CLOCK
-
- return retval;
-}
-
-static long last_rtc_update;
-unsigned long missed_heart_beats;
-
-void indy_timer_interrupt(struct pt_regs *regs)
-{
- unsigned long count;
- int irq = 7;
-
- write_lock(&xtime_lock);
- /* Ack timer and compute new compare. */
- count = read_32bit_cp0_register(CP0_COUNT);
- /* This has races. */
- if ((count - r4k_cur) >= r4k_offset) {
- /* If this happens to often we'll need to compensate. */
- missed_heart_beats++;
- r4k_cur = count + r4k_offset;
- }
- else
- r4k_cur += r4k_offset;
- ack_r4ktimer(r4k_cur);
- kstat.irqs[0][irq]++;
- do_timer(regs);
-
- /* We update the Dallas time of day approx. every 11 minutes,
- * because of how the numbers work out we need to make
- * absolutely sure we do this update within 500ms before the
- * next second starts, thus the following code.
- */
- if ((time_status & STA_UNSYNC) == 0 &&
- xtime.tv_sec > last_rtc_update + 660 &&
- xtime.tv_usec >= 500000 - (tick >> 1) &&
- xtime.tv_usec <= 500000 + (tick >> 1)) {
- if (set_rtc_mmss(xtime.tv_sec) == 0)
- last_rtc_update = xtime.tv_sec;
- else
- /* do it again in 60 s */
- last_rtc_update = xtime.tv_sec - 600;
- }
- write_unlock(&xtime_lock);
-}
-
-static unsigned long dosample(volatile unsigned char *tcwp,
- volatile unsigned char *tc2p)
-{
- unsigned long ct0, ct1;
- unsigned char msb, lsb;
-
- /* Start the counter. */
- *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MRGEN);
- *tc2p = (SGINT_TCSAMP_COUNTER & 0xff);
- *tc2p = (SGINT_TCSAMP_COUNTER >> 8);
-
- /* Get initial counter invariant */
- ct0 = read_32bit_cp0_register(CP0_COUNT);
-
- /* Latch and spin until top byte of counter2 is zero */
- do {
- *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT);
- lsb = *tc2p;
- msb = *tc2p;
- ct1 = read_32bit_cp0_register(CP0_COUNT);
- } while(msb);
-
- /* Stop the counter. */
- *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST);
-
- /* Return the difference, this is how far the r4k counter increments
- * for every one HZ.
- */
- return ct1 - ct0;
-}
-
-static unsigned long __init get_indy_time(void)
-{
- struct indy_clock *clock = (struct indy_clock *)INDY_CLOCK_REGS;
- unsigned int year, mon, day, hour, min, sec;
-
- /* Freeze it. */
- clock->cmd &= ~(0x80);
-
- /* Read regs. */
- sec = clock->sec;
- min = clock->min;
- hour = (clock->hr & 0x3f);
- day = (clock->date & 0x3f);
- mon = (clock->month & 0x1f);
- year = clock->year;
-
- /* Unfreeze clock. */
- clock->cmd |= 0x80;
-
- /* Frob the bits. */
-#define FROB1(x) (((x) & 0xf) + ((((x) & 0xf0) >> 4) * 10));
-#define FROB2(x) (((x) & 0xf) + (((((x) & 0xf0) >> 4) & 0x3) * 10));
-
- /* XXX Should really check that secs register is the same
- * XXX as when we first read it and if not go back and
- * XXX read the regs above again.
- */
- sec = FROB1(sec); min = FROB1(min); day = FROB1(day);
- mon = FROB1(mon); year = FROB1(year);
- hour = FROB2(hour);
-
-#undef FROB1
-#undef FROB2
-
- /* Wheee... */
- if(year < 45)
- year += 30;
- if ((year += 1940) < 1970)
- year += 100;
-
- return mktime(year, mon, day, hour, min, sec);
-}
-
-#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
-
-void __init indy_timer_init(void)
-{
- struct sgi_ioc_timers *p;
- volatile unsigned char *tcwp, *tc2p;
-
- /* Figure out the r4k offset, the algorithm is very simple
- * and works in _all_ cases as long as the 8254 counter
- * register itself works ok (as an interrupt driving timer
- * it does not because of bug, this is why we are using
- * the onchip r4k counter/compare register to serve this
- * purpose, but for r4k_offset calculation it will work
- * ok for us). There are other very complicated ways
- * of performing this calculation but this one works just
- * fine so I am not going to futz around. ;-)
- */
- p = ioc_timers;
- tcwp = &p->tcword;
- tc2p = &p->tcnt2;
-
- printk("calculating r4koff... ");
- dosample(tcwp, tc2p); /* First sample. */
- dosample(tcwp, tc2p); /* Eat one. */
- r4k_offset = dosample(tcwp, tc2p); /* Second sample. */
-
- printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
-
- r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset);
- write_32bit_cp0_register(CP0_COMPARE, r4k_cur);
- set_cp0_status(ST0_IM, ALLINTS);
- sti();
-
- write_lock_irq(&xtime_lock);
- xtime.tv_sec = get_indy_time(); /* Read time from RTC. */
- xtime.tv_usec = 0;
- write_unlock_irq(&xtime_lock);
-}
-
-void indy_8254timer_irq(void)
-{
- int cpu = smp_processor_id();
- int irq = 4;
-
- irq_enter(cpu);
- kstat.irqs[0][irq]++;
- printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n");
- prom_getchar();
- prom_imode();
- irq_exit(cpu);
-}
-
-void do_gettimeofday(struct timeval *tv)
-{
- unsigned long flags;
-
- read_lock_irqsave(&xtime_lock, flags);
- *tv = xtime;
- read_unlock_irqrestore(&xtime_lock, flags);
-}
-
-void do_settimeofday(struct timeval *tv)
-{
- write_lock_irq(&xtime_lock);
- xtime = *tv;
- time_state = TIME_BAD;
- time_maxerror = MAXPHASE;
- time_esterror = MAXPHASE;
- write_unlock_irq(&xtime_lock);
-}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)