patch-2.4.0-test3 linux/arch/mips/sgi/kernel/setup.c
Next file: linux/arch/mips/sgi/kernel/time.c
Previous file: linux/arch/mips/sgi/kernel/indyIRQ.S
Back to the patch index
Back to the overall index
- Lines: 130
- Date:
Sun Jul 9 22:18:15 2000
- Orig file:
v2.4.0-test2/linux/arch/mips/sgi/kernel/setup.c
- Orig date:
Tue May 23 15:31:33 2000
diff -u --recursive --new-file v2.4.0-test2/linux/arch/mips/sgi/kernel/setup.c linux/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 @@
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 @@
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 @@
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
@@ -229,5 +323,5 @@
#ifdef CONFIG_VIDEO_VINO
init_vino();
#endif
+
}
-__initcall(rs_init);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)