Date: Mon, 10 Nov 1997 15:06:16 GMT
From: Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
To: linux-m68k@lists.linux-m68k.org
Cc: Paul Gortmaker <gpg109@rsphy1.anu.edu.au>, util-linux@math.uio.no
Subject: L68K: rtc driver update
Sender: owner-linux-m68k@phil.uni-sb.de


Below is an update for the /dev/rtc driver. When I've written the
code, I must have been a little absent-minded and I forgot that
machines other than the TT can have a different RTC interrupt number
or no such interrupt at all (Falcon). Finally James told me that
/dev/rtc doesn't work on his Falcon... :-)

Ok, changes are:

 - Definitions of RTC parameters (RTC_IRQ, RTC_ALWAYS_BCD, ...) moved
   from rtc.c to mc146818rtc.h, to avoid #ifdef's in a .c file. Hope
   Linus like this patch ;-))

 - New parameter RTC_HAS_IRQ; for Atari assumed yes if there is a
   TT-MFP.

   If the MFP doesn't have an interrupt, certain functions are
   disabled: reading /dev/rtc fails with EIO (such a read normally
   returns if an RTC interrupt comes in), and all ioctls that have to
   do with interrupts return EINVAL: RTC_AIE_{ON,OFF},
   RTC_PIE_{ON,OFF}, RTC_UIE_{ON,OFF}, and RTC_IRQP_{READ,SET}.

 - Unfortunately, hwclock (and probably clock, but haven't verified
   now) uses the update interrupt to synchronize to the RTC's update
   cycle before setting the clock. IMHO, this synchronization is
   really a job for the kernel, not a user program. And it is also
   easier and faster to implement in the kernel[1], so I did that.

 - Below is an additional patch for hwclock so that it doesn't
   complain anymore if the RTC_UIE_ON ioctl doesn't work. This isn't
   available if the RTC doesn't have an interrupt... Synchronizing to
   the tick isn't that crucial that it can't be omitted.

Roman

[1]: A user program has to wait for the falling edge of the UIP bit,
and then has 1 second to update the time. Besides that there's still
no guarantee that the process is choosen by the scheduler again within
that second, it has to wait up to 1s which is really unnecessary.

Date: Wed, 12 Nov 1997 13:01:36 GMT
From: Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
To: linux-m68k@lists.linux-m68k.org
In-Reply-To: <9711101505.AA07151@faui21.informatik.uni-erlangen.de> (message
	from Roman Hodek on Mon, 10 Nov 1997 15:06:16 GMT)
Subject: Re: L68K: rtc driver update
Sender: owner-linux-m68k@phil.uni-sb.de


Unfortunately the rtc patch I posted two days ago didn't apply cleanly
to 2.1.61 :-(( (I "extended" an older diff relative to 2.1.57, and
didn't notice that other unrelated things have changed to 2.1.61.)

Below is a fixed version.

Roman

------------------------------------------------------------------------------
--- linux-2.1.61/drivers/char/rtc.c~	Sun Oct 26 16:57:56 1997
+++ linux-2.1.61/drivers/char/rtc.c	Wed Nov 12 13:56:33 1997
@@ -30,42 +30,7 @@
  *
  */
 
-#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
+#define RTC_VERSION		"1.08"
 
 /*
  *	Note that *all* calls to CMOS_READ and CMOS_WRITE are done with
@@ -90,32 +55,6 @@
 #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 */
-#define RTC_YEAR_BASE		-20
-#define RTC_CENTURY_SWITCH	49
-#define RTC_MINYEAR		1950
-#else
-#define RTC_YEAR_BASE		0
-#define RTC_CENTURY_SWITCH	69
-#define RTC_MINYEAR		1970
-#endif
-
 /*
  *	We sponge a minor off of the misc major. No need slurping
  *	up another valuable major dev number for this. If you add
@@ -127,6 +66,8 @@
 
 static struct timer_list rtc_irq_timer;
 
+RTC_MACH_VARIABLES
+
 static long long rtc_llseek(struct file *file, loff_t offset, int origin);
 
 static ssize_t rtc_read(struct file *file, char *buf,
@@ -203,6 +144,9 @@
 	struct wait_queue wait = { current, NULL };
 	unsigned long data;
 	ssize_t retval;
+ 
+ 	if (!RTC_HAS_IRQ)
+ 		return -EIO;
 	
 	if (count < sizeof(unsigned long))
 		return -EINVAL;
@@ -243,16 +187,22 @@
 	switch (cmd) {
 		case RTC_AIE_OFF:	/* Mask alarm int. enab. bit	*/
 		{
+			if (!RTC_HAS_IRQ)
+				return -EINVAL;
 			mask_rtc_irq_bit(RTC_AIE);
 			return 0;
 		}
 		case RTC_AIE_ON:	/* Allow alarm interrupts.	*/
 		{
+			if (!RTC_HAS_IRQ)
+				return -EINVAL;
 			set_rtc_irq_bit(RTC_AIE);
 			return 0;
 		}
 		case RTC_PIE_OFF:	/* Mask periodic int. enab. bit	*/
 		{
+			if (!RTC_HAS_IRQ)
+				return -EINVAL;
 			mask_rtc_irq_bit(RTC_PIE);
 			if (rtc_status & RTC_TIMER_ON) {
 				del_timer(&rtc_irq_timer);
@@ -267,6 +217,8 @@
 			 * We don't really want Joe User enabling more
 			 * than 64Hz of interrupts on a multi-user machine.
 			 */
+			if (!RTC_HAS_IRQ)
+				return -EINVAL;
 			if ((rtc_freq > 64) && (!suser()))
 				return -EACCES;
 
@@ -280,11 +232,15 @@
 		}
 		case RTC_UIE_OFF:	/* Mask ints from RTC updates.	*/
 		{
+			if (!RTC_HAS_IRQ)
+				return -EINVAL;
 			mask_rtc_irq_bit(RTC_UIE);
 			return 0;
 		}
 		case RTC_UIE_ON:	/* Allow ints for RTC updates.	*/
 		{
+			if (!RTC_HAS_IRQ)
+				return -EINVAL;
 			set_rtc_irq_bit(RTC_UIE);
 			return 0;
 		}
@@ -394,8 +350,6 @@
 			yrs -= (yrs >= 2000 && RTC_CENTURY_SWITCH >= 0)
 			       ? 2000 : 1900;
 			
-			save_flags(flags);
-			cli();
 			if (is_bcd)
 			{
 				BIN_TO_BCD(sec);
@@ -406,6 +360,27 @@
 				BIN_TO_BCD(yrs);
 			}
 
+			/* If the RTC is currently in an update cycle, we may
+			 * not touch registers 0..9, so we have to wait a bit.
+			 * An update cycles takes ~ 2ms, and we wait 10..20ms,
+			 * so this should be enough. The while is just for the
+			 * case our process is delayed really long...
+			 * If the uip bit is clear, we have at least 244us to
+			 * set the RTC_SET bit to avoid other updates. This
+			 * should be no problem, since the part runs with
+			 * interrupts off.
+			 * This synchronization to the update cycle formely
+			 * has been done in user level, but it's really done
+			 * better inside the kernel...
+			 */
+			save_flags(flags);
+			cli();
+			while( CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
+				current->state = TASK_INTERRUPTIBLE;
+				current->timeout = jiffies + 2*HZ/100;
+				schedule();
+			}
+
 			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);
@@ -425,6 +400,8 @@
 		}
 		case RTC_IRQP_READ:	/* Read the periodic IRQ rate.	*/
 		{
+			if (!RTC_HAS_IRQ)
+				return -EINVAL;
 			return put_user(rtc_freq, (unsigned long *)arg);
 		}
 		case RTC_IRQP_SET:	/* Set periodic IRQ rate.	*/
@@ -435,6 +412,8 @@
 			/* 
 			 * The max we can do is 8192Hz.
 			 */
+			if (!RTC_HAS_IRQ)
+				return -EINVAL;
 			if ((arg < 2) || (arg > 8192))
 				return -EINVAL;
 			/*
@@ -509,7 +488,8 @@
 
 	if (rtc_status & RTC_TIMER_ON) {
 		rtc_status &= ~RTC_TIMER_ON;
-		del_timer(&rtc_irq_timer);
+		if (RTC_HAS_IRQ)
+			del_timer(&rtc_irq_timer);
 	}
 
 	rtc_irq_data = 0;
@@ -519,6 +499,8 @@
 
 static unsigned int rtc_poll(struct file *file, poll_table *wait)
 {
+	if (!RTC_HAS_IRQ)
+		return -EIO;
 	poll_wait(&rtc_wait, wait);
 	if (rtc_irq_data != 0)
 		return POLLIN | POLLRDNORM;
@@ -553,21 +535,22 @@
 	unsigned long flags;
 
 	/* First test whether the driver should init at all */
-	if (!CHECK_DRIVER_INIT())
+	if (!RTC_CHECK_DRIVER_INIT())
 		return -ENODEV;
 
 	printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION);
-	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(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
-		return -EIO;
+	RTC_MACH_INIT();
+	if (RTC_HAS_IRQ) {
+		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(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
+			return -EIO;
+		}
+		init_timer(&rtc_irq_timer);
+		rtc_irq_timer.function = rtc_dropped_irq;
+		rtc_wait = NULL;
 	}
 	misc_register(&rtc_dev);
-	MACH_INIT();
-	init_timer(&rtc_irq_timer);
-	rtc_irq_timer.function = rtc_dropped_irq;
-	rtc_wait = NULL;
 	save_flags(flags);
 	cli();
 	/* Initialize periodic freq. to CMOS reset default, which is 1024Hz */
--- linux-2.1.61/include/linux/mc146818rtc.h~	Fri Oct 17 14:11:26 1997
+++ linux-2.1.61/include/linux/mc146818rtc.h	Mon Oct 20 10:50:34 1997
@@ -11,10 +11,77 @@
 #ifndef _MC146818RTC_H
 #define _MC146818RTC_H
 #include <asm/io.h>
+#include <linux/config.h>
 
-#ifndef RTC_PORT
+#ifdef CONFIG_ATARI
+/* RTC in Atari machines */
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#define RTC_HAS_IRQ	(ATARIHW_PRESENT(TT_MFP))
+#define RTC_IRQ 	IRQ_TT_MFP_RTC
+#define RTC_IRQ_FLAGS	IRQ_TYPE_FAST
+#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_CHECK_DRIVER_INIT() (MACH_IS_ATARI && ATARIHW_PRESENT(TT_CLK))
+#define RTC_MACH_VARIABLES \
+    static int leap_year_fix;
+#define RTC_MACH_INIT()							\
+    do {								\
+	if (RTC_HAS_IRQ)						\
+	    /* select RTC int on H->L edge */				\
+	    tt_mfp.active_edge &= ~0x40;				\
+	leap_year_fix = (is_medusa || is_hades ||			\
+			 *(short *)0xff000002 >= 0x306);		\
+    } while(0)
+
+/* 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)
+
+#else
+/* RTC in a PC */
+
+#define RTC_HAS_IRQ 	1
+#define RTC_IRQ 	8	/* Can't see this changing soon.	*/
+#define RTC_IRQ_FLAGS	SA_INTERRUPT
 #define RTC_PORT(x)	(0x70 + (x))
+#define RTC_IO_EXTENT	0x10	/* Only really two ports, but...	*/
 #define RTC_ALWAYS_BCD	1
+
+#define RTC_CHECK_DRIVER_INIT() 1
+#define RTC_MACH_VARIABLES
+#define RTC_MACH_INIT()						\
+    do {							\
+	request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");	\
+    } while(0)
+
+/*
+ * 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_RTC_ARC
+/* Adjust starting epoch if ARC console time is being used */
+#define RTC_YEAR_BASE		-20
+#define RTC_CENTURY_SWITCH	49
+#define RTC_MINYEAR		1950
+#else
+/* standard values */
+#define RTC_YEAR_BASE		0
+#define RTC_CENTURY_SWITCH	69
+#define RTC_MINYEAR		1970
+#endif
+
 #endif
 
 #define CMOS_READ(addr) ({ \
------------------------------------------------------------------------------
--- util-linux-2.7.1/sys-utils/hwclock.c~	Mon Nov 10 15:36:40 1997
+++ util-linux-2.7.1/sys-utils/hwclock.c	Mon Nov 10 16:05:49 1997
@@ -389,8 +389,11 @@
     /* Turn on update interrupts (one per second) */
     rc = ioctl(rtc_fd, RTC_UIE_ON, 0);
     if (rc == -1) {
-      fprintf(stderr, "ioctl() to /dev/rtc to turn on update interrupts "
-              "failed, errno = %s (%d).\n", strerror(errno), errno);
+	  /* The kernel can't do that ioctl if the RTC doesn't have an
+	   * interrupt :-( Don't complain too loud, this is a normal situation */
+	  if (errno != EINVAL || debug)
+        fprintf(stderr, "ioctl() to /dev/rtc to turn on update interrupts "
+                "failed, errno = %s (%d).\n", strerror(errno), errno);
       *retcode_p = 1;
     } else {
       unsigned long dummy;
