patch-2.3.23 linux/arch/sh/kernel/time.c

Next file: linux/arch/sh/kernel/traps.c
Previous file: linux/arch/sh/kernel/test-img.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.22/linux/arch/sh/kernel/time.c linux/arch/sh/kernel/time.c
@@ -1,12 +1,15 @@
-/*
+/* $Id: time.c,v 1.2 1999/10/11 13:12:02 gniibe Exp $
+ *
  *  linux/arch/sh/kernel/time.c
  *
- *  Copyright (C) 1999  Niibe Yutaka
+ *  Copyright (C) 1999  Tetsuya Okada & Niibe Yutaka
  *
  *  Some code taken from i386 version.
  *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
  */
 
+#include <linux/config.h>
+
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -28,6 +31,11 @@
 #include <linux/timex.h>
 #include <linux/irq.h>
 
+#define TMU_TOCR_INIT	0x00
+#define TMU0_TCR_INIT	0x0020
+#define TMU_TSTR_INIT	1
+
+#if defined(__sh3__)
 #define TMU_TOCR	0xfffffe90	/* Byte access */
 #define TMU_TSTR	0xfffffe92	/* Byte access */
 
@@ -35,13 +43,63 @@
 #define TMU0_TCNT	0xfffffe98	/* Long access */
 #define TMU0_TCR	0xfffffe9c	/* Word access */
 
-#define TMU_TOCR_INIT	0x00
-#define TMU0_TCR_INIT	0x0020
-#define TMU_TSTR_INIT	1
-
-#define CLOCK_MHZ	(60/4)
 #define INTERVAL	37500 /* (1000000*CLOCK_MHZ/HZ/2) ??? */
 
+/* SH-3 RTC */
+#define R64CNT  	0xfffffec0
+#define RSECCNT 	0xfffffec2
+#define RMINCNT 	0xfffffec4
+#define RHRCNT  	0xfffffec6
+#define RWKCNT  	0xfffffec8
+#define RDAYCNT 	0xfffffeca
+#define RMONCNT 	0xfffffecc
+#define RYRCNT  	0xfffffece
+#define RSECAR  	0xfffffed0
+#define RMINAR  	0xfffffed2
+#define RHRAR   	0xfffffed4
+#define RWKAR   	0xfffffed6
+#define RDAYAR  	0xfffffed8
+#define RMONAR  	0xfffffeda
+#define RCR1    	0xfffffedc
+#define RCR2    	0xfffffede
+
+#elif defined(__SH4__)
+#define TMU_TOCR	0xffd80000	/* Byte access */
+#define TMU_TSTR	0xffd80004	/* Byte access */
+
+#define TMU0_TCOR	0xffd80008	/* Long access */
+#define TMU0_TCNT	0xffd8000c	/* Long access */
+#define TMU0_TCR	0xffd80010	/* Word access */
+
+#define INTERVAL	83333
+
+/* SH-4 RTC */
+#define R64CNT  	0xffc80000
+#define RSECCNT 	0xffc80004
+#define RMINCNT 	0xffc80008
+#define RHRCNT  	0xffc8000c
+#define RWKCNT  	0xffc80010
+#define RDAYCNT 	0xffc80014
+#define RMONCNT 	0xffc80018
+#define RYRCNT  	0xffc8001c  /* 16bit */
+#define RSECAR  	0xffc80020
+#define RMINAR  	0xffc80024
+#define RHRAR   	0xffc80028
+#define RWKAR   	0xffc8002c
+#define RDAYAR  	0xffc80030
+#define RMONAR  	0xffc80034
+#define RCR1    	0xffc80038
+#define RCR2    	0xffc8003c
+#endif
+
+#ifndef BCD_TO_BIN
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#endif
+
+#ifndef BIN_TO_BCD
+#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
+#endif
+
 extern rwlock_t xtime_lock;
 #define TICK_SIZE tick
 
@@ -82,14 +140,48 @@
 	write_unlock_irq(&xtime_lock);
 }
 
-/*
- */
 static int set_rtc_time(unsigned long nowtime)
 {
-/* XXX  should be implemented XXXXXXXXXX */
-	int retval = -1;
+#ifdef CONFIG_SH_CPU_RTC
+	int retval = 0;
+	int real_seconds, real_minutes, cmos_minutes;
+
+	ctrl_outb(2, RCR2);  /* reset pre-scaler & stop RTC */
+
+	cmos_minutes = ctrl_inb(RMINCNT);
+	BCD_TO_BIN(cmos_minutes);
+
+	/*
+	 * since we're only adjusting minutes and seconds,
+	 * don't interfere with hour overflow. This avoids
+	 * messing with unknown time zones but requires your
+	 * RTC not to be off by more than 15 minutes
+	 */
+	real_seconds = nowtime % 60;
+	real_minutes = nowtime / 60;
+	if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
+		real_minutes += 30;	/* correct for half hour time zone */
+	real_minutes %= 60;
+
+	if (abs(real_minutes - cmos_minutes) < 30) {
+		BIN_TO_BCD(real_seconds);
+		BIN_TO_BCD(real_minutes);
+		ctrl_outb(real_seconds, RSECCNT);
+		ctrl_outb(real_minutes, RMINCNT);
+	} else {
+		printk(KERN_WARNING
+		       "set_rtc_time: can't update from %d to %d\n",
+		       cmos_minutes, real_minutes);
+		retval = -1;
+	}
+
+	ctrl_outb(2, RCR2);  /* start RTC */
 
 	return retval;
+#else
+	/* XXX should support other clock devices? */
+	return -1;
+#endif
 }
 
 /* last time the RTC clock got updated */
@@ -131,14 +223,12 @@
  */
 static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	unsigned long __dummy;
+	unsigned long timer_status;
 
 	/* Clear UNF bit */
-	asm volatile("mov.w	%1,%0\n\t"
-		     "and	%2,%0\n\t"
-		     "mov.w	%0,%1"
-		     : "=&z" (__dummy)
-		     : "m" (__m(TMU0_TCR)), "r" (~0x100));
+	timer_status = ctrl_inw(TMU0_TCR);
+	timer_status &= ~0x100;
+	ctrl_outw(timer_status, TMU0_TCR);
 
 	/*
 	 * Here we are in the timer irq handler. We just have irqs locally
@@ -187,16 +277,67 @@
 
 static unsigned long get_rtc_time(void)
 {
-/* XXX not implemented yet */
+#ifdef CONFIG_SH_CPU_RTC
+	unsigned int sec, min, hr, wk, day, mon, yr, yr100;
+
+ again:
+	ctrl_outb(1, RCR1);  /* clear CF bit */
+	do {
+		sec = ctrl_inb(RSECCNT);
+		min = ctrl_inb(RMINCNT);
+		hr  = ctrl_inb(RHRCNT);
+		wk  = ctrl_inb(RWKCNT);
+		day = ctrl_inb(RDAYCNT);
+		mon = ctrl_inb(RMONCNT);
+#if defined(__SH4__)
+		yr  = ctrl_inw(RYRCNT);
+		yr100 = (yr >> 8);
+		yr &= 0xff;
+#else
+		yr  = ctrl_inb(RYRCNT);
+		yr100 = (yr == 0x99) ? 0x19 : 0x20;
+#endif
+	} while ((ctrl_inb(RCR1) & 0x80) != 0);
+
+	BCD_TO_BIN(yr100);
+	BCD_TO_BIN(yr);
+	BCD_TO_BIN(mon);
+	BCD_TO_BIN(day);
+	BCD_TO_BIN(hr);
+	BCD_TO_BIN(min);
+	BCD_TO_BIN(sec);
+
+	if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
+	    hr > 23 || min > 59 || sec > 59) {
+		printk(KERN_ERR
+		       "SH RTC: invalid value, resetting to 1 Jan 2000\n");
+		ctrl_outb(2, RCR2);  /* reset, stop */
+		ctrl_outb(0, RSECCNT);
+		ctrl_outb(0, RMINCNT);
+		ctrl_outb(0, RHRCNT);
+		ctrl_outb(6, RWKCNT);
+		ctrl_outb(1, RDAYCNT);
+		ctrl_outb(1, RMONCNT);
+#if defined(__SH4__)
+		ctrl_outw(0x2000, RYRCNT);
+#else
+		ctrl_outb(0, RYRCNT);
+#endif
+		ctrl_outb(1, RCR2);  /* start */
+		goto again;
+	}
+
+	return mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
+#else
+	/* XXX should support other clock devices? */
 	return 0;
+#endif
 }
 
 static struct irqaction irq0  = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
 
 void __init time_init(void)
 {
-	unsigned long __dummy;
-
 	xtime.tv_sec = get_rtc_time();
 	xtime.tv_usec = 0;
 
@@ -204,19 +345,12 @@
 	setup_irq(TIMER_IRQ, &irq0);
 
 	/* Start TMU0 */
-	asm volatile("mov	%1,%0\n\t"
-		     "mov.b	%0,%2		! external clock input\n\t"
-		     "mov	%3,%0\n\t"
-		     "mov.w	%0,%4		! enable timer0 interrupt\n\t"
-		     "mov.l	%5,%6\n\t"
-		     "mov.l	%5,%7\n\t"
-		     "mov	%8,%0\n\t"
-		     "mov.b	%0,%9"
-		     : "=&z" (__dummy)
-		     : "i" (TMU_TOCR_INIT), "m" (__m(TMU_TOCR)),
-		       "i" (TMU0_TCR_INIT), "m" (__m(TMU0_TCR)),
-		       "r" (INTERVAL), "m" (__m(TMU0_TCOR)), "m" (__m(TMU0_TCNT)),
-		       "i" (TMU_TSTR_INIT), "m" (__m(TMU_TSTR)));
+	ctrl_outb(TMU_TOCR_INIT,TMU_TOCR);
+	ctrl_outw(TMU0_TCR_INIT,TMU0_TCR);
+	ctrl_outl(INTERVAL,TMU0_TCOR);
+	ctrl_outl(INTERVAL,TMU0_TCNT);
+	ctrl_outb(TMU_TSTR_INIT,TMU_TSTR);
+
 #if 0
 	/* Start RTC */
 	asm volatile("");

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