summaryrefslogtreecommitdiffstats
path: root/arch/mips/sgi/kernel
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-07-09 21:44:37 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-07-09 21:44:37 +0000
commitd9d8062e7b49943b2a2fb034f817a9fc217fd40f (patch)
tree39dec0bbe8bd707aa09199e84999e06b69e76593 /arch/mips/sgi/kernel
parentd69a8cf8c1b7a1aced66732997ff60cbf543de6d (diff)
Microsecond timers for Indy. From Keith Wesolowsky.
Diffstat (limited to 'arch/mips/sgi/kernel')
-rw-r--r--arch/mips/sgi/kernel/Makefile4
-rw-r--r--arch/mips/sgi/kernel/indyIRQ.S4
-rw-r--r--arch/mips/sgi/kernel/setup.c98
-rw-r--r--arch/mips/sgi/kernel/time.c24
4 files changed, 115 insertions, 15 deletions
diff --git a/arch/mips/sgi/kernel/Makefile b/arch/mips/sgi/kernel/Makefile
index 760e9fe51..6eb3f1775 100644
--- a/arch/mips/sgi/kernel/Makefile
+++ b/arch/mips/sgi/kernel/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.7 1999/05/07 18:00:16 ulfc Exp $
+#
# Makefile for the SGI specific kernel interface routines
# under Linux.
#
@@ -14,7 +14,7 @@
$(CC) $(CFLAGS) -c $< -o $*.o
OBJS = indy_mc.o indy_sc.o indy_hpc.o indy_int.o indy_rtc.o \
- system.o indy_timer.o indyIRQ.o reset.o setup.o time.o
+ system.o indyIRQ.o reset.o setup.o time.o
ifdef CONFIG_SGI_PROM_CONSOLE
OBJS += promcon.o
endif
diff --git a/arch/mips/sgi/kernel/indyIRQ.S b/arch/mips/sgi/kernel/indyIRQ.S
index fd8e2a1dd..14d20f044 100644
--- a/arch/mips/sgi/kernel/indyIRQ.S
+++ b/arch/mips/sgi/kernel/indyIRQ.S
@@ -1,4 +1,4 @@
-/* $Id: indyIRQ.S,v 1.3 1998/03/22 23:27:17 ralf Exp $
+/*
* indyIRQ.S: Interrupt exception dispatch code for FullHouse and
* Guiness.
*
@@ -67,7 +67,7 @@
/* Wheee, a timer interrupt. */
move a0, sp
- jal indy_timer_interrupt
+ jal indy_r4k_timer_interrupt
nop # delay slot
j ret_from_irq
diff --git a/arch/mips/sgi/kernel/setup.c b/arch/mips/sgi/kernel/setup.c
index feefa10b2..afd52c343 100644
--- a/arch/mips/sgi/kernel/setup.c
+++ b/arch/mips/sgi/kernel/setup.c
@@ -1,5 +1,4 @@
-/* $Id: setup.c,v 1.29 2000/01/27 01:05:23 ralf Exp $
- *
+/*
* setup.c: SGI specific setup, including init of the feature struct.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
@@ -37,6 +36,8 @@ static int remote_debug = 0;
extern void console_setup(char *);
#endif
+extern unsigned long r4k_interval; /* Cycle counter ticks per 1/HZ seconds */
+
extern struct rtc_ops indy_rtc_ops;
void indy_reboot_setup(void);
void sgi_volume_set(unsigned char);
@@ -136,6 +137,98 @@ int __init page_is_ram(unsigned long pagenr)
return 0;
}
+void (*board_time_init)(struct irqaction *irq);
+
+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 1/HZ seconds. We round off the the nearest 1 MHz of
+ * master clock (= 1000000 / 100 / 2 = 5000 count).
+ */
+ return ((ct1 - ct0) / 5000) * 5000;
+}
+
+#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
+
+void sgi_time_init (struct irqaction *irq) {
+ /* Here we need to calibrate the cycle counter to at least be close.
+ * We don't need to actually register the irq handler because that's
+ * all done in indyIRQ.S.
+ */
+ struct sgi_ioc_timers *p;
+ volatile unsigned char *tcwp, *tc2p;
+ unsigned long r4k_ticks[3];
+ unsigned long r4k_next;
+
+ /* 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("Calibrating system timer... ");
+ dosample(tcwp, tc2p); /* First sample. */
+ dosample(tcwp, tc2p); /* Eat one. */
+ r4k_ticks[0] = dosample(tcwp, tc2p); /* Second sample. */
+ dosample(tcwp, tc2p); /* Eat another. */
+ r4k_ticks[1] += dosample (tcwp, tc2p); /* Third sample. */
+
+ if (r4k_ticks[0] != r4k_ticks[1]) {
+ printk ("warning: timer counts differ, retrying...");
+ dosample (tcwp, tc2p);
+ r4k_ticks[2] = dosample (tcwp, tc2p);
+ if (r4k_ticks[2] == r4k_ticks[0]
+ || r4k_ticks[2] == r4k_ticks[1])
+ r4k_interval = r4k_ticks[2];
+ else {
+ printk ("disagreement, using average...");
+ r4k_interval = (r4k_ticks[0] + r4k_ticks[1]
+ + r4k_ticks[2]) / 3;
+ }
+ } else
+ r4k_interval = r4k_ticks[0];
+
+ printk("%d [%d.%02d MHz CPU]\n", (int) r4k_interval,
+ (int) (r4k_interval / 5000), (int) (r4k_interval % 5000) / 50);
+
+ /* Set ourselves up for future interrupts */
+ r4k_next = (read_32bit_cp0_register(CP0_COUNT) + r4k_interval);
+ write_32bit_cp0_register(CP0_COMPARE, r4k_next);
+ set_cp0_status(ST0_IM, ALLINTS);
+ sti ();
+}
+
void __init sgi_setup(void)
{
#ifdef CONFIG_SERIAL_CONSOLE
@@ -147,6 +240,7 @@ void __init sgi_setup(void)
irq_setup = sgi_irq_setup;
+ board_time_init = sgi_time_init;
/* Init the INDY HPC I/O controller. Need to call this before
* fucking with the memory controller because it needs to know the
diff --git a/arch/mips/sgi/kernel/time.c b/arch/mips/sgi/kernel/time.c
index 355f1c471..ea85b75db 100644
--- a/arch/mips/sgi/kernel/time.c
+++ b/arch/mips/sgi/kernel/time.c
@@ -1,15 +1,21 @@
-/* $Id: time.c,v 1.2 1998/04/05 11:24:00 ralf Exp $
- * time.c: Generic SGI time_init() code, this will dispatch to the
- * appropriate per-architecture time/counter init code.
+/*
+ * time.c: Generic SGI handler for (spurious) 8254 interrupts
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*/
-#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <asm/sgialib.h>
-extern void indy_timer_init(void);
-
-void __init time_init(void)
+void indy_8254timer_irq(void)
{
- /* XXX assume INDY for now XXX */
- indy_timer_init();
+ 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);
}