diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-07-09 21:44:37 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-07-09 21:44:37 +0000 |
commit | d9d8062e7b49943b2a2fb034f817a9fc217fd40f (patch) | |
tree | 39dec0bbe8bd707aa09199e84999e06b69e76593 /arch | |
parent | d69a8cf8c1b7a1aced66732997ff60cbf543de6d (diff) |
Microsecond timers for Indy. From Keith Wesolowsky.
Diffstat (limited to 'arch')
-rw-r--r-- | arch/mips/kernel/Makefile | 9 | ||||
-rw-r--r-- | arch/mips/kernel/time.c | 41 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/indyIRQ.S | 4 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/setup.c | 98 | ||||
-rw-r--r-- | arch/mips/sgi/kernel/time.c | 24 |
6 files changed, 153 insertions, 27 deletions
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 9ef5ecbb2..e213c0d38 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -1,4 +1,3 @@ -# $Id: Makefile,v 1.14 1999/10/09 00:00:58 ralf Exp $ # # Makefile for the Linux/MIPS kernel. # @@ -36,10 +35,10 @@ endif # # SGIs have very different interrupt/timer hardware. # -ifndef CONFIG_SGI_IP22 - ifndef CONFIG_DECSTATION - ifndef CONFIG_BAGET_MIPS - O_OBJS += time.o +ifndef CONFIG_DECSTATION + ifndef CONFIG_BAGET_MIPS + O_OBJS += time.o + ifndef CONFIG_SGI_IP22 OX_OBJS += irq.o endif endif diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index 6ef9a2f7b..8de389a50 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -14,6 +14,7 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/interrupt.h> +#include <linux/kernel_stat.h> #include <asm/bootinfo.h> #include <asm/mipsregs.h> @@ -25,6 +26,8 @@ #include <linux/timex.h> extern volatile unsigned long lost_ticks; +unsigned long r4k_interval = 0; +extern rwlock_t xtime_lock; /* * Change this if you have some constant time drift @@ -218,7 +221,7 @@ void do_gettimeofday(struct timeval *tv) { unsigned long flags; - save_and_cli(flags); + read_lock_irqsave (&xtime_lock, flags); *tv = xtime; tv->tv_usec += do_gettimeoffset(); @@ -229,7 +232,7 @@ void do_gettimeofday(struct timeval *tv) if (lost_ticks) tv->tv_usec += USECS_PER_JIFFY; - restore_flags(flags); + read_unlock_irqrestore (&xtime_lock, flags); if (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; @@ -239,7 +242,7 @@ void do_gettimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv) { - cli(); + write_lock_irq (&xtime_lock); /* This is revolting. We need to set the xtime.tv_usec * correctly. However, the value in this location is * is value at the last tick. @@ -258,7 +261,7 @@ void do_settimeofday(struct timeval *tv) time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); + write_unlock_irq (&xtime_lock); } /* @@ -378,22 +381,26 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * called as close as possible to 500 ms before the new second starts. */ + read_lock (&xtime_lock); if ((time_status & STA_UNSYNC) == 0 && xtime.tv_sec > last_rtc_update + 660 && xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && - xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) + xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } /* As we return to user mode fire off the other CPU schedulers.. this is basically because we don't yet share IRQ's around. This message is rigged to be safe on the 386 - basically it's a hack, so don't look closely for now.. */ /*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */ + read_unlock (&xtime_lock); } -static void r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static inline void +r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) { unsigned int count; @@ -405,6 +412,18 @@ static void r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) timerhi += (count < timerlo); /* Wrap around */ timerlo = count; +#ifdef CONFIG_SGI_IP22 + /* Since we don't get anything but r4k timer interrupts, we need to + * set this up so that we'll get one next time. Fortunately since we + * have timerhi/timerlo, we don't care so much if we miss one. So + * we need only ask for the next in r4k_interval counts. On other + * archs we have a real timer, so we don't want this. + */ + write_32bit_cp0_register (CP0_COMPARE, + (unsigned long) (count + r4k_interval)); + kstat.irqs[0][irq]++; +#endif + timer_interrupt(irq, dev_id, regs); if (!jiffies) @@ -418,6 +437,12 @@ static void r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) } } +void indy_r4k_timer_interrupt (struct pt_regs *regs) +{ + static const int INDY_R4K_TIMER_IRQ = 7; + r4k_timer_interrupt (INDY_R4K_TIMER_IRQ, NULL, regs); +} + /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. @@ -497,7 +522,7 @@ void (*board_time_init)(struct irqaction *irq); void __init time_init(void) { - unsigned int epoch, year, mon, day, hour, min, sec; + unsigned int epoch = 0, year, mon, day, hour, min, sec; int i; /* The Linux interpretation of the CMOS clock register contents: @@ -540,8 +565,10 @@ void __init time_init(void) } year += epoch; + write_lock_irq (&xtime_lock); xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_usec = 0; + write_unlock_irq (&xtime_lock); init_cycle_counter(); 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); } |