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

Next file: linux-2.4.25/arch/mips/au1000/csb250/Makefile
Previous file: linux-2.4.25/arch/mips/au1000/common/sleeper.S
Back to the patch index
Back to the overall index

diff -urN linux-2.4.24/arch/mips/au1000/common/time.c linux-2.4.25/arch/mips/au1000/common/time.c
@@ -25,6 +25,11 @@
  *
  * Setting up the clock on the MIPS boards.
  *
+ * Update.  Always configure the kernel with CONFIG_NEW_TIME_C.  This
+ * will use the user interface gettimeofday() functions from the
+ * arch/mips/kernel/time.c, and we provide the clock interrupt processing
+ * and the timer offset compute functions.  If CONFIG_PM is selected,
+ * we also ensure the 32KHz timer is available.   -- Dan
  */
 
 #include <linux/types.h>
@@ -44,6 +49,10 @@
 #include <linux/mc146818rtc.h>
 #include <linux/timex.h>
 
+#if !defined(CONFIG_NEW_TIME_C)
+#error "Alchemy processors need CONFIG_NEW_TIME_C defined"
+#endif
+
 extern void startup_match20_interrupt(void);
 extern void do_softirq(void);
 extern volatile unsigned long wall_jiffies;
@@ -52,7 +61,8 @@
 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;
+int	no_au1xxx_32khz;
+void	(*au1k_wait_ptr)(void);
 
 /* Cycle counter value at the previous timer interrupt.. */
 static unsigned int timerhi = 0, timerlo = 0;
@@ -85,12 +95,6 @@
 	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;
 
@@ -160,115 +164,148 @@
 		do_timer(regs); /* increment jiffies by one */
 	}
 }
-#endif
 
-/*
- * Figure out the r4k offset, the amount to increment the compare
- * register for each time tick.
- * Use the Programmable Counter 1 to do this.
+/* When we wakeup from sleep, we have to "catch up" on all of the
+ * timer ticks we have missed.
  */
-unsigned long cal_r4koff(void)
+void
+wakeup_counter0_adjust(void)
 {
-	unsigned long count;
-	unsigned long cpu_speed;
-	unsigned long start, end;
-	unsigned long counter;
-	int trim_divide = 16;
-	unsigned long flags;
+	unsigned long pc0;
+	int time_elapsed;
 
-	spin_lock_irqsave(&time_lock, flags);
+	pc0 = au_readl(SYS_TOYREAD);
+	if (pc0 < last_match20) {
+		/* counter overflowed */
+		time_elapsed = (0xffffffff - last_match20) + pc0;
+	}
+	else {
+		time_elapsed = pc0 - last_match20;
+	}
 
-	counter = au_readl(SYS_COUNTER_CNTRL);
-	au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL);
+	while (time_elapsed > 0) {
+		time_elapsed -= MATCH20_INC;
+		last_match20 += MATCH20_INC;
+	}
+
+	last_pc0 = pc0;
+	au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
+	au_sync();
 
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
-	au_writel(trim_divide-1, SYS_RTCTRIM); /* RTC now ticks at 32.768/16 kHz */
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
-
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
-	au_writel (0, SYS_TOYWRITE);
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
-
-	start = au_readl(SYS_RTCREAD);
-	start += 2;
-	/* wait for the beginning of a new tick */
-	while (au_readl(SYS_RTCREAD) < start);
-
-	/* Start r4k counter. */
-	write_c0_count(0);
-	end = start + (32768 / trim_divide)/2; /* wait 0.5 seconds */
+}
 
-	while (end > au_readl(SYS_RTCREAD));
+/* This is just for debugging to set the timer for a sleep delay.
+*/
+void
+wakeup_counter0_set(int ticks)
+{
+	unsigned long pc0;
 
-	count = read_c0_count();
-	cpu_speed = count * 2;
-	mips_counter_frequency = count;
-	set_au1x00_uart_baud_base(((cpu_speed) / 4) / 16);
-	spin_unlock_irqrestore(&time_lock, flags);
-	return (cpu_speed / HZ);
+	pc0 = au_readl(SYS_TOYREAD);
+	last_pc0 = pc0;
+	au_writel(last_match20 + (MATCH20_INC * ticks), SYS_TOYMATCH2);
+	au_sync();
 }
+#endif
 
+/* I haven't found anyone that doesn't use a 12 MHz source clock,
+ * but just in case.....
+ */
+#ifdef CONFIG_AU1000_SRC_CLK
+#define AU1000_SRC_CLK	CONFIG_AU1000_SRC_CLK
+#else
+#define AU1000_SRC_CLK	12000000
+#endif
 
-void __init time_init(void)
+/*
+ * We read the real processor speed from the PLL.  This is important
+ * because it is more accurate than computing it from the 32KHz
+ * counter, if it exists.  If we don't have an accurate processor
+ * speed, all of the peripherals that derive their clocks based on
+ * this advertised speed will introduce error and sometimes not work
+ * properly.  This function is futher convoluted to still allow configurations
+ * to do that in case they have really, really old silicon with a
+ * write-only PLL register, that we need the 32KHz when power management
+ * "wait" is enabled, and we need to detect if the 32KHz isn't present
+ * but requested......got it? :-)		-- Dan
+ */
+unsigned long cal_r4koff(void)
 {
-        unsigned int est_freq;
+	unsigned long count;
+	unsigned long cpu_speed;
+	unsigned long flags;
+	unsigned long counter;
 
-	printk("calculating r4koff... ");
-	r4k_offset = cal_r4koff();
-	printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
+	spin_lock_irqsave(&time_lock, flags);
 
-	//est_freq = 2*r4k_offset*HZ;
-	est_freq = r4k_offset*HZ;
-	est_freq += 5000;    /* round */
-	est_freq -= est_freq%10000;
-	printk("CPU frequency %d.%02d MHz\n", est_freq/1000000,
-	       (est_freq%1000000)*100/1000000);
- 	set_au1x00_speed(est_freq);
- 	set_au1x00_lcd_clock(); // program the LCD clock
-	r4k_cur = (read_c0_count() + r4k_offset);
+	/* Power management cares if we don't have a 32KHz counter.
+	*/
+	no_au1xxx_32khz = 0;
+	counter = au_readl(SYS_COUNTER_CNTRL);
+	if (counter & SYS_CNTRL_E0) {
+		int trim_divide = 16;
 
-	write_c0_compare(r4k_cur);
+		au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL);
 
-	/* no RTC on the pb1000 */
-	xtime.tv_sec = 0;
-	xtime.tv_usec = 0;
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
+		/* RTC now ticks at 32.768/16 kHz */
+		au_writel(trim_divide-1, SYS_RTCTRIM);
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
+
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
+		au_writel (0, SYS_TOYWRITE);
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
+
+#if defined(CONFIG_AU1000_USE32K)
+		{
+			unsigned long start, end;
+
+			start = au_readl(SYS_RTCREAD);
+			start += 2;
+			/* wait for the beginning of a new tick
+			*/
+			while (au_readl(SYS_RTCREAD) < start);
+
+			/* Start r4k counter.
+			*/
+			write_c0_count(0);
+
+			/* Wait 0.5 seconds.
+			*/
+			end = start + (32768 / trim_divide)/2;
 
-#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 (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
-	au_writel(0, SYS_TOYWRITE);
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
+			while (end > au_readl(SYS_RTCREAD));
 
-	au_writel(au_readl(SYS_WAKEMSK) | (1<<8), SYS_WAKEMSK);
-	au_writel(~0, SYS_WAKESRC);
-	au_sync();
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
-
-	/* setup match20 to interrupt once every 10ms */
-	last_pc0 = last_match20 = au_readl(SYS_TOYREAD);
-	au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
-	au_sync();
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
-	startup_match20_interrupt();
+			count = read_c0_count();
+			cpu_speed = count * 2;
+		}
+#else
+		cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * 
+			AU1000_SRC_CLK;
+		count = cpu_speed / 2;
 #endif
-
-	//set_c0_status(ALLINTS);
-	au_sync();
+	}
+	else {
+		/* The 32KHz oscillator isn't running, so assume there
+		 * isn't one and grab the processor speed from the PLL.
+		 * NOTE: some old silicon doesn't allow reading the PLL.
+		 */
+		cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK;
+		count = cpu_speed / 2;
+		no_au1xxx_32khz = 1;
+	}
+	mips_hpt_frequency = count;
+	// Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16)
+	set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16));
+	spin_unlock_irqrestore(&time_lock, flags);
+	return (cpu_speed / HZ);
 }
 
 /* This is for machines which generate the exact clock. */
 #define USECS_PER_JIFFY (1000000/HZ)
 #define USECS_PER_JIFFY_FRAC (0x100000000*1000000/HZ&0xffffffff)
 
-#ifndef CONFIG_PM
+
 static unsigned long
 div64_32(unsigned long v1, unsigned long v2, unsigned long v3)
 {
@@ -276,30 +313,9 @@
 	do_div64_32(r0, v1, v2, v3);
 	return r0;
 }
-#endif
 
-static unsigned long do_fast_gettimeoffset(void)
+static unsigned long do_fast_cp0_gettimeoffset(void)
 {
-#ifdef CONFIG_PM
-	unsigned long pc0;
-	unsigned long offset;
-
-	pc0 = au_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;
@@ -340,60 +356,118 @@
 		 "r" (quotient));
 
 	/*
- 	 * Due to possible jiffies inconsistencies, we need to check
+ 	 * Due to possible jiffies inconsistencies, we need to check 
 	 * the result so that we'll get a timer that is monotonic.
 	 */
 	if (res >= USECS_PER_JIFFY)
 		res = USECS_PER_JIFFY-1;
 
 	return res;
-#endif
 }
 
-void do_gettimeofday(struct timeval *tv)
+#ifdef CONFIG_PM
+static unsigned long do_fast_pm_gettimeoffset(void)
 {
-	unsigned long flags;
-
-	read_lock_irqsave (&xtime_lock, flags);
-	*tv = xtime;
-	tv->tv_usec += do_fast_gettimeoffset();
-
-	/*
-	 * xtime is atomically updated in timer_bh. jiffies - wall_jiffies
-	 * is nonzero if the timer bottom half hasnt executed yet.
-	 */
-	if (jiffies - wall_jiffies)
-		tv->tv_usec += USECS_PER_JIFFY;
-
-	read_unlock_irqrestore (&xtime_lock, flags);
+	unsigned long pc0;
+	unsigned long offset;
 
-	if (tv->tv_usec >= 1000000) {
-		tv->tv_usec -= 1000000;
-		tv->tv_sec++;
+	pc0 = au_readl(SYS_TOYREAD);
+	au_sync();
+	offset = pc0 - last_pc0;
+	if (offset > 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);
 	}
+	offset = (unsigned long)((offset * 305) / 10);
+	return offset;
 }
+#endif
 
-void do_settimeofday(struct timeval *tv)
+void __init au1xxx_timer_setup(void)
 {
-	write_lock_irq (&xtime_lock);
+        unsigned int est_freq;
+	extern unsigned long (*do_gettimeoffset)(void);
+	extern void au1k_wait(void);
+
+	printk("calculating r4koff... ");
+	r4k_offset = cal_r4koff();
+	printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
+
+	//est_freq = 2*r4k_offset*HZ;	
+	est_freq = r4k_offset*HZ;	
+	est_freq += 5000;    /* round */
+	est_freq -= est_freq%10000;
+	printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, 
+	       (est_freq%1000000)*100/1000000);
+ 	set_au1x00_speed(est_freq);
+ 	set_au1x00_lcd_clock(); // program the LCD clock
+
+	r4k_cur = (read_c0_count() + r4k_offset);
+	write_c0_compare(r4k_cur);
 
-	/* This is revolting. We need to set the xtime.tv_usec correctly.
-	 * However, the value in this location is value at the last tick.
-	 * Discover what correction gettimeofday would have done, and then
-	 * undo it!
+	/* no RTC on the pb1000 */
+	xtime.tv_sec = 0;
+	xtime.tv_usec = 0;
+
+#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.
+	 *
+	 * Check to ensure we really have a 32KHz oscillator before
+	 * we do this.
 	 */
-	tv->tv_usec -= do_fast_gettimeoffset();
+	if (no_au1xxx_32khz) {
+		unsigned int c0_status;
+
+		printk("WARNING: no 32KHz clock found.\n");
+		do_gettimeoffset = do_fast_cp0_gettimeoffset;
 
-	if (tv->tv_usec < 0) {
-		tv->tv_usec += 1000000;
-		tv->tv_sec--;
+		/* Ensure we get CPO_COUNTER interrupts.
+		*/
+		c0_status = read_c0_status();
+		c0_status |= IE_IRQ5;
+		write_c0_status(c0_status);
+	}
+	else {
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
+		au_writel(0, SYS_TOYWRITE);
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
+
+		au_writel(au_readl(SYS_WAKEMSK) | (1<<8), SYS_WAKEMSK);
+		au_writel(~0, SYS_WAKESRC);
+		au_sync();
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
+
+		/* setup match20 to interrupt once every 10ms */
+		last_pc0 = last_match20 = au_readl(SYS_TOYREAD);
+		au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
+		au_sync();
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
+		startup_match20_interrupt();
+
+		do_gettimeoffset = do_fast_pm_gettimeoffset;
+
+		/* We can use the real 'wait' instruction.
+		*/
+		au1k_wait_ptr = au1k_wait;
 	}
 
-	xtime = *tv;
-	time_adjust = 0;		/* stop active adjtime() */
-	time_status |= STA_UNSYNC;
-	time_maxerror = NTP_PHASE_LIMIT;
-	time_esterror = NTP_PHASE_LIMIT;
+#else
+	/* We have to do this here instead of in timer_init because
+	 * the generic code in arch/mips/kernel/time.c will write
+	 * over our function pointer.
+	 */
+	do_gettimeoffset = do_fast_cp0_gettimeoffset;
+#endif
+}
 
-	write_unlock_irq (&xtime_lock);
+void __init au1xxx_time_init(void)
+{
 }

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