patch-2.4.19 linux-2.4.19/arch/mips/au1000/common/time.c

Next file: linux-2.4.19/arch/mips/au1000/common/usbdev.c
Previous file: linux-2.4.19/arch/mips/au1000/common/serial.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/arch/mips/au1000/common/time.c linux-2.4.19/arch/mips/au1000/common/time.c
@@ -1,4 +1,5 @@
 /*
+ * 
  * Copyright (C) 2001 MontaVista Software, ppopov@mvista.com
  * Copied and modified Carsten Langgaard's time.c
  *
@@ -23,7 +24,10 @@
  * ########################################################################
  *
  * Setting up the clock on the MIPS boards.
+ *
  */
+
+#include <linux/types.h>
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/kernel_stat.h>
@@ -32,28 +36,38 @@
 
 #include <asm/mipsregs.h>
 #include <asm/ptrace.h>
+#include <asm/time.h>
+#include <asm/hardirq.h>
 #include <asm/div64.h>
 #include <asm/au1000.h>
 
 #include <linux/mc146818rtc.h>
 #include <linux/timex.h>
 
+extern void startup_match20_interrupt(void);
+
 extern volatile unsigned long wall_jiffies;
 unsigned long missed_heart_beats = 0;
-unsigned long uart_baud_base;
 
 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;
+unsigned int mips_counter_frequency = 0;
+
+/* Cycle counter value at the previous timer interrupt.. */
+static unsigned int timerhi = 0, timerlo = 0;
 
-#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
+#ifdef CONFIG_PM
+#define MATCH20_INC 328
+extern void startup_match20_interrupt(void);
+static unsigned long last_pc0, last_match20;
+#endif
 
 static inline void ack_r4ktimer(unsigned long newval)
 {
 	write_32bit_cp0_register(CP0_COMPARE, newval);
 }
 
-
 /*
  * There are a lot of conceptually broken versions of the MIPS timer interrupt
  * handler floating around.  This one is rather different, but the algorithm
@@ -62,12 +76,27 @@
 unsigned long wtimer;
 void mips_timer_interrupt(struct pt_regs *regs)
 {
-	int irq = 7;
+	int irq = 63;
+	unsigned long count;
+	int cpu = smp_processor_id();
+
+	irq_enter(cpu, irq);
+	kstat.irqs[cpu][irq]++;
+
+#ifdef CONFIG_PM
+	printk(KERN_ERR "Unexpected CP0 interrupt\n");
+	regs->cp0_status &= ~IE_IRQ5; /* disable CP0 interrupt */
+	return;
+#endif
 
 	if (r4k_offset == 0)
 		goto null;
 
 	do {
+		count = read_32bit_cp0_register(CP0_COUNT);
+		timerhi += (count < timerlo);   /* Wrap around */
+		timerlo = count;
+
 		kstat.irqs[0][irq]++;
 		do_timer(regs);
 		r4k_cur += r4k_offset;
@@ -76,12 +105,61 @@
 	} while (((unsigned long)read_32bit_cp0_register(CP0_COUNT)
 	         - r4k_cur) < 0x7fffffff);
 
+	irq_exit(cpu, irq);
+
+	if (softirq_pending(cpu))
+		do_softirq();
 	return;
 
 null:
 	ack_r4ktimer(0);
 }
 
+#ifdef CONFIG_PM
+void counter0_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long pc0;
+	int time_elapsed;
+	static int jiffie_drift = 0;
+
+	kstat.irqs[0][irq]++;
+	if (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) {
+		/* should never happen! */
+		printk(KERN_WARNING "counter 0 w status eror\n");
+		return;
+	}
+
+	pc0 = inl(SYS_TOYREAD);
+	if (pc0 < last_match20) {
+		/* counter overflowed */
+		time_elapsed = (0xffffffff - last_match20) + pc0;
+	}
+	else {
+		time_elapsed = pc0 - last_match20;
+	}
+
+	while (time_elapsed > 0) {
+		do_timer(regs);
+		time_elapsed -= MATCH20_INC;
+		last_match20 += MATCH20_INC;
+		jiffie_drift++;
+	}
+
+	last_pc0 = pc0;
+	outl(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
+	au_sync();
+
+	/* our counter ticks at 10.009765625 ms/tick, we we're running
+	 * almost 10uS too slow per tick.
+	 */
+	 
+	if (jiffie_drift >= 999) {
+		jiffie_drift -= 999;
+		do_timer(regs); /* increment jiffies by one */
+	}
+}
+#endif
+
 /* 
  * Figure out the r4k offset, the amount to increment the compare
  * register for each time tick. 
@@ -90,49 +168,48 @@
 unsigned long cal_r4koff(void)
 {
 	unsigned long count;
-	unsigned long cpu_pll;
 	unsigned long cpu_speed;
 	unsigned long start, end;
 	unsigned long counter;
-	int i;
 	int trim_divide = 16;
+	unsigned long flags;
 
-	counter = inl(PC_COUNTER_CNTRL);
-	outl(counter | PC_CNTRL_EN1, PC_COUNTER_CNTRL);
+	save_and_cli(flags);
 
-	while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_T1S);
-	outl(trim_divide-1, PC1_TRIM);    /* RTC now ticks at 32.768/16 kHz */
-	while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_T1S);
-
-	while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_C1S);
-	outl (0, PC1_COUNTER_WRITE);
-	while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_C1S);
+	counter = inl(SYS_COUNTER_CNTRL);
+	outl(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL);
 
-	start = inl(PC1_COUNTER_READ);
+	while (inl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
+	outl(trim_divide-1, SYS_RTCTRIM); /* RTC now ticks at 32.768/16 kHz */
+	while (inl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
+
+	while (inl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
+	outl (0, SYS_TOYWRITE);
+	while (inl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
+
+	start = inl(SYS_RTCREAD);
 	start += 2;
 	/* wait for the beginning of a new tick */
-	while (inl(PC1_COUNTER_READ) < start);
+	while (inl(SYS_RTCREAD) < start);
 
 	/* Start r4k counter. */
 	write_32bit_cp0_register(CP0_COUNT, 0);
 	end = start + (32768 / trim_divide)/2; /* wait 0.5 seconds */
 
-	while (end > inl(PC1_COUNTER_READ));
+	while (end > inl(SYS_RTCREAD));
 
 	count = read_32bit_cp0_register(CP0_COUNT);
 	cpu_speed = count * 2;
-	uart_baud_base = (((cpu_speed) / 4) / 16);
+	mips_counter_frequency = count;
+	set_au1000_uart_baud_base(((cpu_speed) / 4) / 16);
+	restore_flags(flags);
 	return (cpu_speed / HZ);
 }
 
-static unsigned long __init get_mips_time(void)
-{
-	return inl(PC0_COUNTER_READ);
-}
 
 void __init time_init(void)
 {
-        unsigned int est_freq, flags;
+        unsigned int est_freq;
 
 	printk("calculating r4koff... ");
 	r4k_offset = cal_r4koff();
@@ -144,25 +221,51 @@
 	est_freq -= est_freq%10000;
 	printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, 
 	       (est_freq%1000000)*100/1000000);
+	set_au1000_speed(est_freq);
+	set_au1000_lcd_clock(); // program the LCD clock
 	r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset);
 
 	write_32bit_cp0_register(CP0_COMPARE, r4k_cur);
-	set_cp0_status(ALLINTS);
 
-	/* Read time from the RTC chipset. */
-	write_lock_irqsave (&xtime_lock, flags);
-	xtime.tv_sec = get_mips_time();
+	/* no RTC on the pb1000 */
+	xtime.tv_sec = 0;
 	xtime.tv_usec = 0;
-	write_unlock_irqrestore(&xtime_lock, flags);
+
+#ifdef CONFIG_PM
+	/*
+	 * setup counter 0, since it keeps ticking after a
+	 * 'wait' instruction has been executed. The CP0 timer and
+	 * counter 1 do NOT continue running after 'wait'
+	 *
+	 * It's too early to call request_irq() here, so we handle
+	 * counter 0 interrupt as a special irq and it doesn't show
+	 * up under /proc/interrupts.
+	 */
+	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
+	writel(0, SYS_TOYWRITE);
+	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
+
+	writel(readl(SYS_WAKEMSK) | (1<<8), SYS_WAKEMSK);
+	writel(~0, SYS_WAKESRC);
+	au_sync();
+	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
+
+	/* setup match20 to interrupt once every 10ms */
+	last_pc0 = last_match20 = readl(SYS_TOYREAD);
+	writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
+	au_sync();
+	while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
+	startup_match20_interrupt();
+#endif
+
+	//set_cp0_status(ALLINTS);
+	au_sync();
 }
 
 /* This is for machines which generate the exact clock. */
 #define USECS_PER_JIFFY (1000000/HZ)
-#define USECS_PER_JIFFY_FRAC ((1000000ULL << 32) / HZ & 0xffffffff)
-
-/* Cycle counter value at the previous timer interrupt.. */
+#define USECS_PER_JIFFY_FRAC (0x100000000*1000000/HZ&0xffffffff)
 
-static unsigned int timerhi = 0, timerlo = 0;
 
 static unsigned long
 div64_32(unsigned long v1, unsigned long v2, unsigned long v3)
@@ -173,11 +276,28 @@
 }
 
 
-/*
- * FIXME: Does playing with the RP bit in c0_status interfere with this code?
- */
 static unsigned long do_fast_gettimeoffset(void)
 {
+#ifdef CONFIG_PM
+	unsigned long pc0;
+	unsigned long offset;
+
+	pc0 = readl(SYS_TOYREAD);
+	if (pc0 < last_pc0) {
+		offset = 0xffffffff - last_pc0 + pc0;
+		printk("offset over: %x\n", (unsigned)offset);
+	}
+	else {
+		offset = (unsigned long)(((pc0 - last_pc0) * 305) / 10);
+	}
+	if ((pc0-last_pc0) > 2*MATCH20_INC) {
+		printk("huge offset %x, last_pc0 %x last_match20 %x pc0 %x\n", 
+				(unsigned)offset, (unsigned)last_pc0, 
+				(unsigned)last_match20, (unsigned)pc0);
+	}
+	au_sync();
+	return offset;
+#else
 	u32 count;
 	unsigned long res, tmp;
 	unsigned long r0;
@@ -225,6 +345,7 @@
 		res = USECS_PER_JIFFY-1;
 
 	return res;
+#endif
 }
 
 void do_gettimeofday(struct timeval *tv)
@@ -274,13 +395,3 @@
 
 	write_unlock_irq (&xtime_lock);
 }
-
-/*
- * The UART baud base is not known at compile time ... if
- * we want to be able to use the same code on different
- * speed CPUs.
- */
-unsigned long get_au1000_uart_baud()
-{
-	return uart_baud_base;
-}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)