CMOS reads and writes

tenthumbs@cybernex.net
Mon, 04 Nov 1996 18:55:04 GMT


A while ago, there were some messages about the CMOS clock changing by large
amounts during a reboot. I have also seen as much as a 17 second change when
rebooting either into or from Linux on my PC.

Looking around the source, I found that the CMOS_READ and CMOS_WRITE macros
in include/linux/mc146818rtc.h assume that interrupts are already disabled.
I wrote a small patch (attached at the end of this message) which converts
these macros into inline functions that handle the grubby details. This
patch should work for any 2.0 and 2.1 kernel.

The upside is that my particular problem seems to be fixed, and it does make
things somewhat more modular. I've been running a 2.0.22 kernel with this
patch for a while without any problems.

The downside is that:
1) there's code in an include file (which I don't like),
2) the timings have changed since there's more code,
3) it's rather inefficient since the save_flags/restore_flags macros are
more than is really needed.

If this is useful, I would suggest that it really should be
architecture-dependent just in case timings become critical.

Thanks for reading.

--- mc146818rtc.h.orig Tue Oct 15 02:32:26 1996
+++ mc146818rtc.h Wed Oct 30 21:44:15 1996
@@ -11,20 +11,38 @@
#ifndef _MC146818RTC_H
#define _MC146818RTC_H
#include <asm/io.h>
+#include <asm/system.h>

#ifndef RTC_PORT
#define RTC_PORT(x) (0x70 + (x))
#define RTC_ALWAYS_BCD 1
#endif

-#define CMOS_READ(addr) ({ \
-outb_p((addr),RTC_PORT(0)); \
-inb_p(RTC_PORT(1)); \
-})
-#define CMOS_WRITE(val, addr) ({ \
-outb_p((addr),RTC_PORT(0)); \
-outb_p((val),RTC_PORT(1)); \
-})
+static inline unsigned char CMOS_READ(int addr)
+{
+ unsigned char result;
+ register unsigned long flags;
+
+ save_flags(flags); /* overkill, of a sort */
+ cli();
+ outb_p ( addr | 0x80, RTC_PORT(0)); /* disable NMI + select addr */
+ result = inb_p (RTC_PORT(1));
+ outb_p ( 0, RTC_PORT(0)); /* enable NMI */
+ restore_flags(flags);
+ return result;
+}
+
+static inline void CMOS_WRITE (int val, int addr)
+{
+ register unsigned long flags;
+
+ save_flags(flags); /* overkill, of a sort */
+ cli();
+ outb_p (addr | 0x80, RTC_PORT(0)); /* disable NMI + select addr */
+ outb_p (val, RTC_PORT(1));
+ outb_p (0, RTC_PORT(0)); /* enable NMI */
+ restore_flags(flags);
+}

/**********************************************************************
* register summary