From: rnhodek@inf2.informatik.uni-erlangen.de (Roman Hodek)
Date: Sun, 8 Jun 1997 15:20:01 +0200 (CEST)
To: linux-m68k@phil.uni-sb.de
Cc: Paul Gortmaker <gpg109@rsphy1.anu.edu.au>
Subject: L68K: /dev/rtc for Atari & misc other things
Sender: owner-linux-m68k@phil.uni-sb.de
Reply-To: linux-m68k@phil.uni-sb.de


I now adapted drivers/char/rtc.c for the Atari. Basically this was
easy, because the RTC chip in PCs and Ataris is the same.

I tried to use the configuration methods already provided (e.g.
RTC_IRQ, RTC_PORT macros), but had to extend those a little bit (new
macros RTC_IRQ_FLAGS and MACH_INIT). I hope this meets Linus' taste
:-) All in all, I tried to be minimal-invasive... :-)

Another change was necessary to how the real year is calculated from
the value in the RTC register. PCs store the year plain (i.e., 1997 is
stored as 97), and switch centuries for values below 70. There was
already an adaption in the source (for Alpha ARC consoles), that
basically use RTC values lowered by 20. But the Atari stored the year
based on an offset of 1970 or 1968 (depending on the TOS version...),
i.e. 1997 is stored as 27 or 29. That way, no century adjustment is
needed. I tried to sort out all the differences between these formats
and introduced some new year interpretation macros (RTC_YEAR_BASE,
RTC_MINYEAR, RTC_CENTURY_SWITCH). The calculations for the other
machines should be unchanged (except I've miscalculated something :-).

Another fixed "bug": When checking whether a user-supplied year is in
range, the driver assumed BCD representation (true on PCs), but which
is wrong for Atari (uses binary format by default). Now, you can also
still set the date in 2225 (if your Atari is still running then... :-)

Other little things fixed:

 - arch/m68k/atari/config.c: There was a return with value in void
   function atari_debug_init() ...

 - include/asm-m68k/io.h: I've slightly rewritten the read{b,w,l}
   macros, because gcc sometimes emitted *two* read statements for the
   memory location... (guess due to volatile and returning the value
   from a ({ }) contruct). Assing to a local variable and returning
   that fixed the problem.

   That problem hit me in the CMOS_READ macro, where in_p (expanding
   to readb) was enclosed in another ({ }), and was the reason that
   /dev/rtc didn't work at first...

BTW, hasn't there been another /dev/rtc patch some time ago? I can
remember something like that somehow, but haven't seen any 68k
specific stuff in current kernels...

Roman

------------------------------------------------------------------------------
diff -u --recursive --exclude-from=diff-excludes --new-file linux-2.1.42-orig/arch/m68k/atari/config.c linux-2.1.42/arch/m68k/atari/config.c
--- linux-2.1.42-orig/arch/m68k/atari/config.c	Mon May 26 18:14:24 1997
+++ linux-2.1.42/arch/m68k/atari/config.c	Sun Jun  8 13:06:27 1997
@@ -981,8 +981,8 @@
 {
 #ifdef CONFIG_KGDB
 	/* if the m68k_debug_device is used by the GDB stub, do nothing here */
-	if (kgdb_initialized)
-		return(NULL);
+    if (kgdb_initialized)
+	return;
 #endif
 
     if (!strcmp( m68k_debug_device, "ser" )) {
diff -u --recursive --exclude-from=diff-excludes --new-file linux-2.1.42-orig/arch/m68k/config.in linux-2.1.42/arch/m68k/config.in
--- linux-2.1.42-orig/arch/m68k/config.in	Mon Apr 14 13:29:27 1997
+++ linux-2.1.42/arch/m68k/config.in	Fri Jun  6 19:10:10 1997
@@ -254,6 +254,9 @@
 if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_ATARI" = "y" ]; then
   define_bool CONFIG_ABSTRACT_CONSOLE y
 fi
+if [ "$CONFIG_ATARI" = "y" ]; then
+  bool 'Enhanced Real Time Clock Support' CONFIG_RTC
+fi
 endmenu
 
 mainmenu_option next_comment
diff -u --recursive --exclude-from=diff-excludes --new-file linux-2.1.42-orig/drivers/char/rtc.c linux-2.1.42/drivers/char/rtc.c
--- linux-2.1.42-orig/drivers/char/rtc.c	Fri May 16 11:07:48 1997
+++ linux-2.1.42/drivers/char/rtc.c	Sun Jun  8 14:48:12 1997
@@ -32,8 +32,40 @@
 
 #define RTC_VERSION		"1.07"
 
+#include <linux/config.h>
+
+#ifdef CONFIG_ATARI
+/* Special parameters for RTC in Atari machines */
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#define RTC_IRQ 	IRQ_TT_MFP_RTC
+#define RTC_PORT(x)	(TT_RTC_BAS + 2*(x))
+#define RTC_ALWAYS_BCD	0	/* TOS uses binary mode, Linux should be able
+				 * to deal with both modes */
+#define RTC_IRQ_FLAGS	IRQ_TYPE_FAST
+
+#define CHECK_DRIVER_INIT() (MACH_IS_ATARI && ATARIHW_PRESENT(TT_CLK))
+static int leap_year_fix;
+#define MACH_INIT()						\
+    do {							\
+	/* select RTC int on H->L edge */			\
+	tt_mfp.active_edge &= ~0x40;				\
+	leap_year_fix = (is_medusa || is_hades ||		\
+			 *(short *)0xff000002 >= 0x306);	\
+    } while(0)
+#else
+/* RTC in a PC */
 #define RTC_IRQ 	8	/* Can't see this changing soon.	*/
 #define RTC_IO_EXTENT	0x10	/* Only really two ports, but...	*/
+#define RTC_IRQ_FLAGS	SA_INTERRUPT
+
+#define CHECK_DRIVER_INIT() 1
+#define MACH_INIT()						\
+    do {							\
+	/* Check region? Naaah! Just snarf it up. */		\
+	request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");	\
+    } while(0)
+#endif
 
 /*
  *	Note that *all* calls to CMOS_READ and CMOS_WRITE are done with
@@ -57,11 +89,30 @@
 #include <asm/system.h>
 #include <asm/poll.h>
 
+/* RTC year interpretation parameters:
+ *   RTC_YEAR_BASE: the year (- 1900) that a RTC value of 0 would
+ *     (virtually) correspond to. Value is added to RTC year values.
+ *   RTC_CENTURY_SWITCH: year line (-1900) below which a century switch (+ 100
+ *     years) should be a assumed
+ *   RTC_MINYEAR: minimum real year that can be stored in the RTC
+ */
+
+#ifdef CONFIG_ATARI
+/* On Atari, the year was stored with base 1970 in old TOS versions (before
+ * 3.06). Later, Atari recognized that this broke leap year recognition, and
+ * changed the base to 1968. Medusa and Hades always use the new version. */
+#define RTC_YEAR_BASE 		(leap_year_fix ? 68 : 70)
+#define RTC_CENTURY_SWITCH	-1	/* no century switch */
+#define RTC_MINYEAR		(RTC_YEAR_BASE + 1900)
+#elif defined(CONFIG_RTC_ARC)
 /* Adjust starting epoch if ARC console time is being used */
-#ifdef CONFIG_RTC_ARC
-#define ARCFUDGE 20 
+#define RTC_YEAR_BASE		-20
+#define RTC_CENTURY_SWITCH	49
+#define RTC_MINYEAR		1950
 #else
-#define ARCFUDGE 0
+#define RTC_YEAR_BASE		0
+#define RTC_CENTURY_SWITCH	69
+#define RTC_MINYEAR		1970
 #endif
 
 /*
@@ -334,6 +385,7 @@
 			unsigned char save_control, save_freq_select;
 			unsigned int yrs;
 			unsigned long flags;
+			int is_bcd;
 			
 			if (!suser())
 				return -EACCES;
@@ -344,14 +396,22 @@
 
 			copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time));
 
-			yrs = rtc_tm.tm_year + 1900 + ARCFUDGE;
+			yrs = rtc_tm.tm_year + 1900;
 			mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
 			day = rtc_tm.tm_mday;
 			hrs = rtc_tm.tm_hour;
 			min = rtc_tm.tm_min;
 			sec = rtc_tm.tm_sec;
 
-			if ((yrs < 1970) || (yrs > 2069))
+			save_flags(flags);
+			cli();
+			save_control = CMOS_READ(RTC_CONTROL);
+			restore_flags(flags);
+			is_bcd = (!(save_control & RTC_DM_BINARY) ||
+				  RTC_ALWAYS_BCD);
+
+			if ((yrs < RTC_MINYEAR) ||
+			    (yrs > RTC_MINYEAR + (is_bcd ? 99 : 255)))
 				return -EINVAL;
 
 			leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
@@ -365,15 +425,13 @@
 			if ((hrs >= 24) || (min >= 60) || (sec >= 60))
 				return -EINVAL;
 
-			if (yrs >= 2000)
-				yrs -= 2000;	/* RTC (0, 1, ... 69) */
-			else
-				yrs -= 1900;	/* RTC (70, 71, ... 99) */
-
+			yrs -= RTC_YEAR_BASE;
+			yrs -= (yrs >= 2000 && RTC_CENTURY_SWITCH >= 0)
+			       ? 2000 : 1900;
+			
 			save_flags(flags);
 			cli();
-			if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ||
-							RTC_ALWAYS_BCD)
+			if (is_bcd)
 			{
 				BIN_TO_BCD(sec);
 				BIN_TO_BCD(min);
@@ -383,7 +441,6 @@
 				BIN_TO_BCD(yrs);
 			}
 
-			save_control = CMOS_READ(RTC_CONTROL);
 			CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 			save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
 			CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
@@ -536,16 +593,19 @@
 {
 	unsigned long flags;
 
+	/* First test whether the driver should init at all */
+	if (!CHECK_DRIVER_INIT())
+	    return -ENODEV;
+
 	printk("Real Time Clock Driver v%s\n", RTC_VERSION);
-	if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
+	if(request_irq(RTC_IRQ, rtc_interrupt, RTC_IRQ_FLAGS, "rtc", NULL))
 	{
 		/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
 		printk("rtc: IRQ %d is not free.\n", RTC_IRQ);
 		return -EIO;
 	}
 	misc_register(&rtc_dev);
-	/* Check region? Naaah! Just snarf it up. */
-	request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
+	MACH_INIT();
 	init_timer(&rtc_irq_timer);
 	rtc_irq_timer.function = rtc_dropped_irq;
 	rtc_wait = NULL;
@@ -726,15 +786,15 @@
 		BCD_TO_BIN(rtc_tm->tm_year);
 	}
 
+	/* if RTC_BASE_YEAR == 0, the optimizer should do away with this */
+	rtc_tm->tm_year += RTC_YEAR_BASE;
+
 	/*
 	 * Account for differences between how the RTC uses the values
 	 * and how they are defined in a struct rtc_time;
 	 */
-	if (rtc_tm->tm_year <= 69)
+	if (rtc_tm->tm_year <= RTC_CENTURY_SWITCH)
 		rtc_tm->tm_year += 100;
-
-	/* if ARCFUDGE == 0, the optimizer should do away with this */
-	rtc_tm->tm_year -= ARCFUDGE;
 
 	rtc_tm->tm_mon--;
 }
diff -u --recursive --exclude-from=diff-excludes --new-file linux-2.1.42-orig/include/asm-m68k/io.h linux-2.1.42/include/asm-m68k/io.h
--- linux-2.1.42-orig/include/asm-m68k/io.h	Mon Nov 11 21:13:30 1996
+++ linux-2.1.42/include/asm-m68k/io.h	Sun Jun  8 13:03:17 1997
@@ -9,9 +9,15 @@
  * differently. On the m68k architecture, we just read/write the
  * memory location directly.
  */
-#define readb(addr) (*(volatile unsigned char *) (addr))
-#define readw(addr) (*(volatile unsigned short *) (addr))
-#define readl(addr) (*(volatile unsigned int *) (addr))
+/* ++roman: The assignments to temp. vars avoid that gcc sometimes generates
+ * two accesses to memory, which may be undesireable for some devices.
+ */
+#define readb(addr) \
+    ({ unsigned char __v = (*(volatile unsigned char *) (addr)); __v; })
+#define readw(addr) \
+    ({ unsigned short __v = (*(volatile unsigned short *) (addr)); __v; })
+#define readl(addr) \
+    ({ unsigned int __v = (*(volatile unsigned int *) (addr)); __v; })
 
 #define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b))
 #define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b))
