Subject: Re: a patch for generic MIPS RTC

From: Atsushi Nemoto <[email-masked]>
Date: Mon, 05 Sep 2005 13:54:22 +0900 (JST)

>>>>> On Thu, 18 Aug 2005 12:54:50 +0400, Sergey Podstavin <[email-masked]> said:
spodstavin> genrtc doesn't work as a module because functions for
spodstavin> module defined in wrong place. Most architectures define
spodstavin> these functions in <asm/rtc.h>, so make MIPS follow their
spodstavin> example.  It makes the generic MIPS RTC working as a
spodstavin> module for MIPS.

It seems this fix already checked in, but I have some comments.

1. There are unnecessary (and conflicting) prototype declarations.

2. Define static variable mips_rtc_lock in rtc.h is generally a bad
   idea.  There is already rtc_lock in arch/mips/kernel/time.h.

3. We should protect rtc_set_mmss() call during get_rtc_time or
   set_rtc_time (this is not your patch's fault).

How about this patch?  The rtc_lock could be manipulated in each
RTC-dependent routines, but I choose a simple way for now.

---
Atsushi Nemoto

diff -u linux-mips/include/asm-mips/rtc.h linux/include/asm-mips/rtc.h
--- linux-mips/include/asm-mips/rtc.h	2005-09-05 10:17:18.000000000 +0900
+++ linux/include/asm-mips/rtc.h	2005-09-05 13:32:51.000000000 +0900
@@ -29,23 +29,18 @@
 #define RTC_24H 0x02            /* 24 hour mode - else hours bit 7 means pm */
 #define RTC_DST_EN 0x01         /* auto switch DST - works f. USA only */
 
-unsigned int get_rtc_time(struct rtc_time *time);
-int set_rtc_time(struct rtc_time *time);
-unsigned int get_rtc_ss(void);
-int get_rtc_pll(struct rtc_pll_info *pll);
-int set_rtc_pll(struct rtc_pll_info *pll);
-
-static DEFINE_SPINLOCK(mips_rtc_lock);
+extern spinlock_t rtc_lock;	/* in kernel/time.c */
 
 static inline unsigned int get_rtc_time(struct rtc_time *time)
 {
 	unsigned long nowtime;
+	unsigned long flags;
 
-	spin_lock(&mips_rtc_lock);
+	spin_lock_irqsave(&rtc_lock, flags);
 	nowtime = rtc_get_time();
 	to_tm(nowtime, time);
 	time->tm_year -= 1900;
-	spin_unlock(&mips_rtc_lock);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return RTC_24H;
 }
@@ -53,14 +48,15 @@
 static inline int set_rtc_time(struct rtc_time *time)
 {
 	unsigned long nowtime;
+	unsigned long flags;
 	int ret;
 
-	spin_lock(&mips_rtc_lock);
+	spin_lock_irqsave(&rtc_lock, flags);
 	nowtime = mktime(time->tm_year+1900, time->tm_mon+1,
 			time->tm_mday, time->tm_hour, time->tm_min,
 			time->tm_sec);
 	ret = rtc_set_time(nowtime);
-	spin_unlock(&mips_rtc_lock);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return ret;
 }
diff -u linux-mips/arch/mips/kernel/time.c linux/arch/mips/kernel/time.c
--- linux-mips/arch/mips/kernel/time.c	2005-08-30 11:02:01.000000000 +0900
+++ linux/arch/mips/kernel/time.c	2005-09-05 13:36:05.000000000 +0900
@@ -453,12 +453,14 @@
 	    xtime.tv_sec > last_rtc_update + 660 &&
 	    (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
 	    (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
+		spin_lock(&rtc_lock);
 		if (rtc_set_mmss(xtime.tv_sec) == 0) {
 			last_rtc_update = xtime.tv_sec;
 		} else {
 			/* do it again in 60 s */
 			last_rtc_update = xtime.tv_sec - 600;
 		}
+		spin_unlock(&rtc_lock);
 	}
 	write_sequnlock(&xtime_lock);

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Mon, 5 Sep 2005 12:08:34 +0100 (BST)

> RTC-dependent routines, but I choose a simple way for now.

 That's how other architectures do this, see e.g. 
"arch/alpha/kernel/time.c".  Why should we be different, even for now?  
Also the call is named rtc_set_mmss() for an unknown reason while all the 
others have set_rtc_mmss().

  Maciej

---

From: Atsushi Nemoto <[email-masked]>
Date: Mon, 05 Sep 2005 22:45:34 +0900 (JST)

>>>>> On Mon, 5 Sep 2005 12:08:34 +0100 (BST), "Maciej W. Rozycki" <[email-masked]> said:

macro>  That's how other architectures do this, see e.g.
macro> "arch/alpha/kernel/time.c".  Why should we be different, even
macro> for now?

Please elaborate more ?  Do you mean we should implement default
rtc_set_mmss() and take the rtc_lock in it ?  Or do you mean we should
take rtc_lock in each board-dependent rtc_set_time/rtc_set_time ?  

macro> Also the call is named rtc_set_mmss() for an unknown reason
macro> while all the others have set_rtc_mmss().

IIRC, you are (one of) the godfather of the function, aren't you?  :-)

---
Atsushi Nemoto

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Mon, 5 Sep 2005 16:25:32 +0100 (BST)

> take rtc_lock in each board-dependent rtc_set_time/rtc_set_time ?  

 I'm not sure all chips actually require it.  Certainly the null function 
does not, so that spinlock would incur an unnecessary overhead.  Therefore 
yes, it should be board- or chip-dependent.

> IIRC, you are (one of) the godfather of the function, aren't you?  :-)

 Hmm, I must have got influenced by rtc_set_time()...  Perhaps it wasn't 
that bad after all and it's all the others that should be fixed instead. 
;-)

  Maciej

---

From: Atsushi Nemoto <[email-masked]>
Date: Wed, 07 Sep 2005 00:55:17 +0900 (JST)

>>>>> On Mon, 5 Sep 2005 16:25:32 +0100 (BST), "Maciej W. Rozycki" <[email-masked]> said:

macro>  I'm not sure all chips actually require it.  Certainly the
macro> null function does not, so that spinlock would incur an
macro> unnecessary overhead.  Therefore yes, it should be board- or
macro> chip-dependent.

OK, and I also found some rtc routines might take a few SECONDS,
therefore protecting whole these rtc routines entirely with spinlock
is really bad idea.

How about this (untested) one?

* Get rid of inconsistent declarations from include/asm-mips/rtc.h
* Get rid of mips_rtc_lock.
* Use rtc_lock (and disable irq) to protect HW access in each rtc routines.

diff -ur linux-mips/arch/mips/ddb5xxx/common/rtc_ds1386.c linux/arch/mips/ddb5xxx/common/rtc_ds1386.c
--- linux-mips/arch/mips/ddb5xxx/common/rtc_ds1386.c	2004-08-14 19:56:00.000000000 +0900
+++ linux/arch/mips/ddb5xxx/common/rtc_ds1386.c	2005-09-06 19:57:17.000000000 +0900
@@ -41,7 +41,9 @@
 	u8 byte;
 	u8 temp;
 	unsigned int year, month, day, hour, minute, second;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	/* let us freeze external registers */
 	byte = READ_RTC(0xB);
 	byte &= 0x3f;
@@ -70,6 +72,7 @@
 		/* 24 hour format */
 		hour = BCD2BIN(temp & 0x3f);
 	}
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return mktime(year, month, day, hour, minute, second);
 }
@@ -81,7 +84,9 @@
 	u8 byte;
 	u8 temp;
 	u8 year, month, day, hour, minute, second;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	/* let us freeze external registers */
 	byte = READ_RTC(0xB);
 	byte &= 0x3f;
@@ -133,6 +138,7 @@
 	if (second != READ_RTC(0x1)) {
 		WRITE_RTC(0x1, second);
 	}
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/dec/time.c linux/arch/mips/dec/time.c
--- linux-mips/arch/mips/dec/time.c	2004-08-14 19:55:59.000000000 +0900
+++ linux/arch/mips/dec/time.c	2005-09-06 19:57:26.000000000 +0900
@@ -37,10 +37,25 @@
 #include <asm/dec/machtype.h>

+/*
+ * Returns true if a clock update is in progress
+ */
+static inline unsigned char dec_rtc_is_updating(void)
+{
+	unsigned char uip;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc_lock, flags);
+	uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
+	spin_unlock_irqrestore(&rtc_lock, flags);
+	return uip;
+}
+
 static unsigned long dec_rtc_get_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec, real_year;
 	int i;
+	unsigned long flags;
 
 	/* The Linux interpretation of the DS1287 clock register contents:
 	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
@@ -49,11 +64,12 @@
 	 */
 	/* read RTC exactly on falling edge of update flag */
 	for (i = 0; i < 1000000; i++)	/* may take up to 1 second... */
-		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+		if (dec_rtc_is_updating())
 			break;
 	for (i = 0; i < 1000000; i++)	/* must try at least 2.228 ms */
-		if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+		if (!dec_rtc_is_updating())
 			break;
+	spin_lock_irqsave(&rtc_lock, flags);
 	/* Isn't this overkill?  UIP above should guarantee consistency */
 	do {
 		sec = CMOS_READ(RTC_SECONDS);
@@ -78,6 +94,7 @@
 	 */
 	real_year = CMOS_READ(RTC_DEC_YEAR);
 	year += real_year - 72 + 2000;
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return mktime(year, mon, day, hour, min, sec);
 }
@@ -95,6 +112,8 @@
 	int real_seconds, real_minutes, cmos_minutes;
 	unsigned char save_control, save_freq_select;
 
+	/* irq are locally disabled here */
+	spin_lock(&rtc_lock);
 	/* tell the clock it's being set */
 	save_control = CMOS_READ(RTC_CONTROL);
 	CMOS_WRITE((save_control | RTC_SET), RTC_CONTROL);
@@ -141,6 +160,7 @@
 	 */
 	CMOS_WRITE(save_control, RTC_CONTROL);
 	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+	spin_unlock(&rtc_lock);
 
 	return retval;
 }
diff -ur linux-mips/arch/mips/ite-boards/generic/time.c linux/arch/mips/ite-boards/generic/time.c
--- linux-mips/arch/mips/ite-boards/generic/time.c	2005-08-30 11:01:59.000000000 +0900
+++ linux/arch/mips/ite-boards/generic/time.c	2005-09-06 19:57:42.000000000 +0900
@@ -149,15 +149,15 @@
 it8172_rtc_get_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
-	unsigned int flags;
+	unsigned long flags;
 
 	/* avoid update-in-progress. */
 	for (;;) {
-		local_irq_save(flags);
+		spin_lock_irqsave(&rtc_lock, flags);
 		if (! (CMOS_READ(RTC_REG_A) & RTC_UIP))
 			break;
 		/* don't hold intr closed all the time */
-		local_irq_restore(flags);
+		spin_unlock_irqrestore(&rtc_lock, flags);
 	}
 
 	/* Read regs. */
@@ -170,7 +170,7 @@
 		hw_to_bin(*rtc_century_reg) * 100;
 
 	/* restore interrupts */
-	local_irq_restore(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return mktime(year, mon, day, hour, min, sec);
 }
@@ -179,18 +179,18 @@
 it8172_rtc_set_time(unsigned long t)
 {
 	struct rtc_time tm;
-	unsigned int flags;
+	unsigned long flags;
 
 	/* convert */
 	to_tm(t, &tm);
 
 	/* avoid update-in-progress. */
 	for (;;) {
-		local_irq_save(flags);
+		spin_lock_irqsave(&rtc_lock, flags);
 		if (! (CMOS_READ(RTC_REG_A) & RTC_UIP))
 			break;
 		/* don't hold intr closed all the time */
-		local_irq_restore(flags);
+		spin_unlock_irqrestore(&rtc_lock, flags);
 	}
 
 	*rtc_century_reg = bin_to_hw(tm.tm_year/100);
@@ -202,7 +202,7 @@
 	CMOS_WRITE(bin_to_hw(tm.tm_year%100), RTC_YEAR);
 
 	/* restore interrupts */
-	local_irq_restore(flags);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/jmr3927/common/rtc_ds1742.c linux/arch/mips/jmr3927/common/rtc_ds1742.c
--- linux-mips/arch/mips/jmr3927/common/rtc_ds1742.c	2004-08-14 19:55:33.000000000 +0900
+++ linux/arch/mips/jmr3927/common/rtc_ds1742.c	2005-09-06 19:58:03.000000000 +0900
@@ -57,7 +57,9 @@
 {
 	unsigned int year, month, day, hour, minute, second;
 	unsigned int century;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	CMOS_WRITE(RTC_READ, RTC_CONTROL);
 	second = BCD2BIN(CMOS_READ(RTC_SECONDS) & RTC_SECONDS_MASK);
 	minute = BCD2BIN(CMOS_READ(RTC_MINUTES));
@@ -67,6 +69,7 @@
 	year = BCD2BIN(CMOS_READ(RTC_YEAR));
 	century = BCD2BIN(CMOS_READ(RTC_CENTURY) & RTC_CENTURY_MASK);
 	CMOS_WRITE(0, RTC_CONTROL);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	year += century * 100;
 
@@ -81,7 +84,9 @@
 	u8 year, month, day, hour, minute, second;
 	u8 cmos_year, cmos_month, cmos_day, cmos_hour, cmos_minute, cmos_second;
 	int cmos_century;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	CMOS_WRITE(RTC_READ, RTC_CONTROL);
 	cmos_second = (u8)(CMOS_READ(RTC_SECONDS) & RTC_SECONDS_MASK);
 	cmos_minute = (u8)CMOS_READ(RTC_MINUTES);
@@ -139,6 +144,7 @@
 
 	/* RTC_CENTURY and RTC_CONTROL share same address... */
 	CMOS_WRITE(cmos_century, RTC_CONTROL);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/lasat/ds1603.c linux/arch/mips/lasat/ds1603.c
--- linux-mips/arch/mips/lasat/ds1603.c	2005-08-30 11:02:01.000000000 +0900
+++ linux/arch/mips/lasat/ds1603.c	2005-09-06 19:58:25.000000000 +0900
@@ -8,6 +8,7 @@
 #include <asm/lasat/lasat.h>
 #include <linux/delay.h>
 #include <asm/lasat/ds1603.h>
+#include <asm/time.h>
 
 #include "ds1603.h"
 
@@ -138,19 +139,27 @@
 unsigned long ds1603_read(void)
 {
 	unsigned long word;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc_lock, flags);
 	rtc_init_op();
 	rtc_write_byte(READ_TIME_CMD);
 	word = rtc_read_word();
 	rtc_end_op();
+	spin_unlock_irqrestore(&rtc_lock, flags);
 	return word;
 }
 
 int ds1603_set(unsigned long time)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc_lock, flags);
 	rtc_init_op();
 	rtc_write_byte(SET_TIME_CMD);
 	rtc_write_word(time);
 	rtc_end_op();
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/momentum/jaguar_atx/setup.c linux/arch/mips/momentum/jaguar_atx/setup.c
--- linux-mips/arch/mips/momentum/jaguar_atx/setup.c	2005-08-30 11:02:03.000000000 +0900
+++ linux/arch/mips/momentum/jaguar_atx/setup.c	2005-09-06 19:58:40.000000000 +0900
@@ -149,7 +149,9 @@
 unsigned long m48t37y_get_time(void)
 {
 	unsigned int year, month, day, hour, min, sec;
+	unsigned long flags;
 
 	/* stop the update */
 	rtc_base[0x7ff8] = 0x40;
 
@@ -166,6 +168,7 @@
 
 	/* start the update */
 	rtc_base[0x7ff8] = 0x00;
 
 	return mktime(year, month, day, hour, min, sec);
 }
@@ -173,11 +176,13 @@
 int m48t37y_set_time(unsigned long sec)
 {
 	struct rtc_time tm;
+	unsigned long flags;
 
 	/* convert to a more useful format -- note months count from 0 */
 	to_tm(sec, &tm);
 	tm.tm_mon += 1;
 
 	/* enable writing */
 	rtc_base[0x7ff8] = 0x80;
 
@@ -201,6 +206,7 @@
 
 	/* disable writing */
 	rtc_base[0x7ff8] = 0x00;
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/momentum/ocelot_3/setup.c linux/arch/mips/momentum/ocelot_3/setup.c
--- linux-mips/arch/mips/momentum/ocelot_3/setup.c	2005-08-30 11:02:03.000000000 +0900
+++ linux/arch/mips/momentum/ocelot_3/setup.c	2005-09-06 19:58:52.000000000 +0900
@@ -135,7 +135,9 @@
 unsigned long m48t37y_get_time(void)
 {
 	unsigned int year, month, day, hour, min, sec;
+	unsigned long flags;
 
 	/* stop the update */
 	rtc_base[0x7ff8] = 0x40;
 
@@ -152,6 +154,7 @@
 
 	/* start the update */
 	rtc_base[0x7ff8] = 0x00;
 
 	return mktime(year, month, day, hour, min, sec);
 }
@@ -159,11 +162,13 @@
 int m48t37y_set_time(unsigned long sec)
 {
 	struct rtc_time tm;
+	unsigned long flags;
 
 	/* convert to a more useful format -- note months count from 0 */
 	to_tm(sec, &tm);
 	tm.tm_mon += 1;
 
 	/* enable writing */
 	rtc_base[0x7ff8] = 0x80;
 
@@ -187,6 +192,7 @@
 
 	/* disable writing */
 	rtc_base[0x7ff8] = 0x00;
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/momentum/ocelot_c/setup.c linux/arch/mips/momentum/ocelot_c/setup.c
--- linux-mips/arch/mips/momentum/ocelot_c/setup.c	2005-08-30 11:02:03.000000000 +0900
+++ linux/arch/mips/momentum/ocelot_c/setup.c	2005-09-06 19:58:59.000000000 +0900
@@ -140,7 +140,9 @@
 	unsigned char* rtc_base = (unsigned char*)0xfc800000;
 #endif
 	unsigned int year, month, day, hour, min, sec;
+	unsigned long flags;
 
 	/* stop the update */
 	rtc_base[0x7ff8] = 0x40;
 
@@ -157,6 +159,7 @@
 
 	/* start the update */
 	rtc_base[0x7ff8] = 0x00;
 
 	return mktime(year, month, day, hour, min, sec);
 }
@@ -169,11 +172,13 @@
 	unsigned char* rtc_base = (unsigned char*)0xfc800000;
 #endif
 	struct rtc_time tm;
+	unsigned long flags;
 
 	/* convert to a more useful format -- note months count from 0 */
 	to_tm(sec, &tm);
 	tm.tm_mon += 1;
 
 	/* enable writing */
 	rtc_base[0x7ff8] = 0x80;
 
@@ -197,6 +202,7 @@
 
 	/* disable writing */
 	rtc_base[0x7ff8] = 0x00;
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/pmc-sierra/yosemite/setup.c linux/arch/mips/pmc-sierra/yosemite/setup.c
--- linux-mips/arch/mips/pmc-sierra/yosemite/setup.c	2005-06-24 10:01:20.000000000 +0900
+++ linux/arch/mips/pmc-sierra/yosemite/setup.c	2005-09-06 19:59:19.000000000 +0900
@@ -73,7 +73,9 @@
 unsigned long m48t37y_get_time(void)
 {
 	unsigned int year, month, day, hour, min, sec;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	/* Stop the update to the time */
 	m48t37_base->control = 0x40;
 
@@ -88,6 +90,7 @@
 
 	/* Start the update to the time again */
 	m48t37_base->control = 0x00;
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return mktime(year, month, day, hour, min, sec);
 }
@@ -95,11 +98,13 @@
 int m48t37y_set_time(unsigned long sec)
 {
 	struct rtc_time tm;
+	unsigned long flags;
 
 	/* convert to a more useful format -- note months count from 0 */
 	to_tm(sec, &tm);
 	tm.tm_mon += 1;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	/* enable writing */
 	m48t37_base->control = 0x80;
 
@@ -123,6 +128,7 @@
 
 	/* disable writing */
 	m48t37_base->control = 0x00;
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/sgi-ip22/ip22-time.c linux/arch/mips/sgi-ip22/ip22-time.c
--- linux-mips/arch/mips/sgi-ip22/ip22-time.c	2005-08-30 11:02:04.000000000 +0900
+++ linux/arch/mips/sgi-ip22/ip22-time.c	2005-09-06 19:59:43.000000000 +0900
@@ -35,7 +35,9 @@
 {
 	unsigned int yrs, mon, day, hrs, min, sec;
 	unsigned int save_control;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	save_control = hpc3c0->rtcregs[RTC_CMD] & 0xff;
 	hpc3c0->rtcregs[RTC_CMD] = save_control | RTC_TE;
 
@@ -47,6 +49,7 @@
 	yrs = BCD2BIN(hpc3c0->rtcregs[RTC_YEAR] & 0xff);
 
 	hpc3c0->rtcregs[RTC_CMD] = save_control;
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	if (yrs < 45)
 		yrs += 30;
@@ -60,6 +63,7 @@
 {
 	struct rtc_time tm;
 	unsigned int save_control;
+	unsigned long flags;
 
 	to_tm(tim, &tm);
 
@@ -68,6 +72,7 @@
 	if (tm.tm_year >= 100)
 		tm.tm_year -= 100;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	save_control = hpc3c0->rtcregs[RTC_CMD] & 0xff;
 	hpc3c0->rtcregs[RTC_CMD] = save_control | RTC_TE;
 
@@ -80,6 +85,7 @@
 	hpc3c0->rtcregs[RTC_HUNDREDTH_SECOND] = 0;
 
 	hpc3c0->rtcregs[RTC_CMD] = save_control;
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/sibyte/swarm/rtc_m41t81.c linux/arch/mips/sibyte/swarm/rtc_m41t81.c
--- linux-mips/arch/mips/sibyte/swarm/rtc_m41t81.c	2005-08-30 11:02:04.000000000 +0900
+++ linux/arch/mips/sibyte/swarm/rtc_m41t81.c	2005-09-06 20:00:03.000000000 +0900
@@ -144,6 +144,7 @@
 int m41t81_set_time(unsigned long t)
 {
 	struct rtc_time tm;
+	unsigned long flags;
 
 	to_tm(t, &tm);
 
@@ -153,6 +154,7 @@
 	 * believe we should finish writing min within a second.
 	 */
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	tm.tm_sec = BIN2BCD(tm.tm_sec);
 	m41t81_write(M41T81REG_SC, tm.tm_sec);
 
@@ -180,6 +182,7 @@
 	tm.tm_year %= 100;
 	tm.tm_year = BIN2BCD(tm.tm_year);
 	m41t81_write(M41T81REG_YR, tm.tm_year);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return 0;
 }
@@ -187,14 +190,17 @@
 unsigned long m41t81_get_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
+	unsigned long flags;
 
 	/*
 	 * min is valid if two reads of sec are the same.
 	 */
 	for (;;) {
+		spin_lock_irqsave(&rtc_lock, flags);
 		sec = m41t81_read(M41T81REG_SC);
 		min = m41t81_read(M41T81REG_MN);
 		if (sec == m41t81_read(M41T81REG_SC)) break;
+		spin_unlock_irqrestore(&rtc_lock, flags);
 	}
 	hour = m41t81_read(M41T81REG_HR) & 0x3f;
 	day = m41t81_read(M41T81REG_DT);
@@ -207,6 +213,7 @@
 	day = BCD2BIN(day);
 	mon = BCD2BIN(mon);
 	year = BCD2BIN(year);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	year += 2000;
 
diff -ur linux-mips/arch/mips/sibyte/swarm/rtc_xicor1241.c linux/arch/mips/sibyte/swarm/rtc_xicor1241.c
--- linux-mips/arch/mips/sibyte/swarm/rtc_xicor1241.c	2005-03-04 10:19:33.000000000 +0900
+++ linux/arch/mips/sibyte/swarm/rtc_xicor1241.c	2005-09-06 20:00:28.000000000 +0900
@@ -113,9 +113,11 @@
 {
 	struct rtc_time tm;
 	int tmp;
+	unsigned long flags;
 
 	to_tm(t, &tm);
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	/* unlock writes to the CCR */
 	xicor_write(X1241REG_SR, X1241REG_SR_WEL);
 	xicor_write(X1241REG_SR, X1241REG_SR_WEL | X1241REG_SR_RWEL);
@@ -160,6 +162,7 @@
 	xicor_write(X1241REG_HR, tmp);
 
 	xicor_write(X1241REG_SR, 0);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return 0;
 }
@@ -167,7 +170,9 @@
 unsigned long xicor_get_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec, y2k;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	sec = xicor_read(X1241REG_SC);
 	min = xicor_read(X1241REG_MN);
 	hour = xicor_read(X1241REG_HR);
@@ -183,6 +188,7 @@
 	mon = xicor_read(X1241REG_MO);
 	year = xicor_read(X1241REG_YR);
 	y2k = xicor_read(X1241REG_Y2K);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	sec = BCD2BIN(sec);
 	min = BCD2BIN(min);
diff -ur linux-mips/arch/mips/tx4938/common/rtc_rx5c348.c linux/arch/mips/tx4938/common/rtc_rx5c348.c
--- linux-mips/arch/mips/tx4938/common/rtc_rx5c348.c	2005-08-30 11:02:04.000000000 +0900
+++ linux/arch/mips/tx4938/common/rtc_rx5c348.c	2005-09-06 19:56:27.000000000 +0900
@@ -67,14 +67,20 @@
 {
 	unsigned char *inbufs[1], *outbufs[1];
 	unsigned int incounts[2], outcounts[2];
+	int ret;
+	unsigned long flags;
+
 	inbufs[0] = inbuf;
 	incounts[0] = count;
 	incounts[1] = 0;
 	outbufs[0] = outbuf;
 	outcounts[0] = count;
 	outcounts[1] = 0;
-	return txx9_spi_io(srtc_chipid, &srtc_dev_desc,
-			   inbufs, incounts, outbufs, outcounts, 0);
+	spin_lock_irqsave(&rtc_lock, flags);
+	ret = txx9_spi_io(srtc_chipid, &srtc_dev_desc,
+			  inbufs, incounts, outbufs, outcounts, 0);
+	spin_unlock_irqrestore(&rtc_lock, flags);
+	return ret;
 }
 
 /*
diff -ur linux-mips/include/asm-mips/mc146818-time.h linux/include/asm-mips/mc146818-time.h
--- linux-mips/include/asm-mips/mc146818-time.h	2004-08-14 19:55:59.000000000 +0900
+++ linux/include/asm-mips/mc146818-time.h	2005-09-06 18:38:47.000000000 +0900
@@ -33,7 +33,9 @@
 	int real_seconds, real_minutes, cmos_minutes;
 	unsigned char save_control, save_freq_select;
 	int retval = 0;
+	unsigned long flags;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
 	CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 
@@ -79,14 +81,30 @@
 	 */
 	CMOS_WRITE(save_control, RTC_CONTROL);
 	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return retval;
 }
 
+/*
+ * Returns true if a clock update is in progress
+ */
+static inline unsigned char rtc_is_updating(void)
+{
+	unsigned char uip;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc_lock, flags);
+	uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
+	spin_unlock_irqrestore(&rtc_lock, flags);
+	return uip;
+}
+
 static inline unsigned long mc146818_get_cmos_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
 	int i;
+	unsigned long flags;
 
 	/*
 	 * The Linux interpretation of the CMOS clock register contents:
@@ -97,12 +115,13 @@
 
 	/* read RTC exactly on falling edge of update flag */
 	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
-		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+		if (rtc_is_updating())
 			break;
 	for (i = 0 ; i < 1000000 ; i++)	/* must try at least 2.228 ms */
-		if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+		if (!rtc_is_updating())
 			break;
 
+	spin_lock_irqsave(&rtc_lock, flags);
 	do { /* Isn't this overkill ? UIP above should guarantee consistency */
 		sec = CMOS_READ(RTC_SECONDS);
 		min = CMOS_READ(RTC_MINUTES);
@@ -121,6 +140,7 @@
 		BCD_TO_BIN(year);
 	}
 	year = mc146818_decode_year(year);
+	spin_unlock_irqrestore(&rtc_lock, flags);
 
 	return mktime(year, mon, day, hour, min, sec);
 }
diff -ur linux-mips/include/asm-mips/rtc.h linux/include/asm-mips/rtc.h
--- linux-mips/include/asm-mips/rtc.h	2005-09-05 10:17:18.000000000 +0900
+++ linux/include/asm-mips/rtc.h	2005-09-06 16:17:02.000000000 +0900
@@ -14,7 +14,6 @@
 
 #ifdef __KERNEL__
 
-#include <linux/spinlock.h>
 #include <linux/rtc.h>
 #include <asm/time.h>
 
@@ -29,23 +28,13 @@

 
 	return RTC_24H;
 }
@@ -55,12 +44,10 @@
 	unsigned long nowtime;
 	int ret;

 	return ret;
 }
diff -ur linux-mips/include/asm-mips/time.h linux/include/asm-mips/time.h
--- linux-mips/include/asm-mips/time.h	2004-08-14 19:54:51.000000000 +0900
+++ linux/include/asm-mips/time.h	2005-09-06 19:38:52.000000000 +0900
@@ -20,6 +20,9 @@
 #include <linux/linkage.h>
 #include <linux/ptrace.h>
 #include <linux/rtc.h>
+#include <linux/spinlock.h>
+
+extern spinlock_t rtc_lock;
 
 /*
  * RTC ops.  By default, they point to no-RTC functions.

---

From: Atsushi Nemoto <[email-masked]>
Date: Tue, 20 Sep 2005 01:48:36 +0900 (JST)

>>>>> On Wed, 07 Sep 2005 00:55:17 +0900 (JST), Atsushi Nemoto <[email-masked]> said:

anemo> OK, and I also found some rtc routines might take a few
anemo> SECONDS, therefore protecting whole these rtc routines entirely
anemo> with spinlock is really bad idea.

anemo> How about this (untested) one?

Now one fix for asm-mips/rtc.h is done.
Here is a revised patch.  Comments are welcome.  Thank you.

diff -ur linux-mips/arch/mips/ddb5xxx/common/rtc_ds1386.c linux/arch/mips/ddb5xxx/common/rtc_ds1386.c
--- linux-mips/arch/mips/ddb5xxx/common/rtc_ds1386.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/ddb5xxx/common/rtc_ds1386.c	2005-09-20 01:39:27.315888352 +0900
@@ -41,7 +41,9 @@
 	u8 byte;
 	u8 temp;
 	unsigned int year, month, day, hour, minute, second;
+	unsigned long flags;

 
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/dec/time.c linux/arch/mips/dec/time.c
--- linux-mips/arch/mips/dec/time.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/dec/time.c	2005-09-20 01:39:27.316888200 +0900
@@ -37,10 +37,25 @@
 #include <asm/dec/machtype.h>

+/*

 
 
 	return retval;
 }
diff -ur linux-mips/arch/mips/ite-boards/generic/time.c linux/arch/mips/ite-boards/generic/time.c
--- linux-mips/arch/mips/ite-boards/generic/time.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/ite-boards/generic/time.c	2005-09-20 01:39:27.316888200 +0900
@@ -149,15 +149,15 @@
 it8172_rtc_get_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
-	unsigned int flags;
+	unsigned long flags;

 	/* Read regs. */
@@ -170,7 +170,7 @@
 
 	/* restore interrupts */

 	/* convert */
 	to_tm(t, &tm);

 
 	/* restore interrupts */
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/jmr3927/common/rtc_ds1742.c linux/arch/mips/jmr3927/common/rtc_ds1742.c
--- linux-mips/arch/mips/jmr3927/common/rtc_ds1742.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/jmr3927/common/rtc_ds1742.c	2005-09-20 01:39:27.316888200 +0900
@@ -57,7 +57,9 @@
 {
 	unsigned int year, month, day, hour, minute, second;
 	unsigned int century;
+	unsigned long flags;

 	year += century * 100;
 
@@ -81,7 +84,9 @@

 
 	return 0;
 }
diff -ur linux-mips/arch/mips/lasat/ds1603.c linux/arch/mips/lasat/ds1603.c
--- linux-mips/arch/mips/lasat/ds1603.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/lasat/ds1603.c	2005-09-20 01:39:27.317888048 +0900
@@ -8,6 +8,7 @@
 #include <asm/lasat/lasat.h>
 #include <linux/delay.h>
 #include <asm/lasat/ds1603.h>
+#include <asm/time.h>
 
 #include "ds1603.h"
 
@@ -138,19 +139,27 @@

 	return 0;
 }
diff -ur linux-mips/arch/mips/momentum/jaguar_atx/setup.c linux/arch/mips/momentum/jaguar_atx/setup.c
--- linux-mips/arch/mips/momentum/jaguar_atx/setup.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/momentum/jaguar_atx/setup.c	2005-09-20 01:39:27.317888048 +0900
@@ -149,7 +149,9 @@
 unsigned long m48t37y_get_time(void)
 {
 	unsigned int year, month, day, hour, min, sec;
+	unsigned long flags;

@@ -166,6 +168,7 @@
 
 	/* start the update */
 	rtc_base[0x7ff8] = 0x00;

 
 
@@ -201,6 +206,7 @@
 
 	/* disable writing */
 	rtc_base[0x7ff8] = 0x00;
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/momentum/ocelot_3/setup.c linux/arch/mips/momentum/ocelot_3/setup.c
--- linux-mips/arch/mips/momentum/ocelot_3/setup.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/momentum/ocelot_3/setup.c	2005-09-20 01:39:27.317888048 +0900
@@ -135,7 +135,9 @@
 unsigned long m48t37y_get_time(void)
 {
 	unsigned int year, month, day, hour, min, sec;
+	unsigned long flags;

@@ -152,6 +154,7 @@
 
 	/* start the update */
 	rtc_base[0x7ff8] = 0x00;

 
 
@@ -187,6 +192,7 @@
 
 	/* disable writing */
 	rtc_base[0x7ff8] = 0x00;
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/momentum/ocelot_c/setup.c linux/arch/mips/momentum/ocelot_c/setup.c
--- linux-mips/arch/mips/momentum/ocelot_c/setup.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/momentum/ocelot_c/setup.c	2005-09-20 01:39:27.318887896 +0900
@@ -140,7 +140,9 @@
 	unsigned char* rtc_base = (unsigned char*)0xfc800000;
 #endif
 	unsigned int year, month, day, hour, min, sec;
+	unsigned long flags;

@@ -157,6 +159,7 @@
 
 	/* start the update */
 	rtc_base[0x7ff8] = 0x00;

 
 
@@ -197,6 +202,7 @@
 
 	/* disable writing */
 	rtc_base[0x7ff8] = 0x00;
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/pmc-sierra/yosemite/setup.c linux/arch/mips/pmc-sierra/yosemite/setup.c
--- linux-mips/arch/mips/pmc-sierra/yosemite/setup.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/pmc-sierra/yosemite/setup.c	2005-09-20 01:39:27.318887896 +0900
@@ -73,7 +73,9 @@
 unsigned long m48t37y_get_time(void)
 {
 	unsigned int year, month, day, hour, min, sec;
+	unsigned long flags;

@@ -88,6 +90,7 @@

 

@@ -123,6 +128,7 @@
 
 	/* disable writing */
 
 	return 0;
 }
diff -ur linux-mips/arch/mips/sgi-ip22/ip22-time.c linux/arch/mips/sgi-ip22/ip22-time.c
--- linux-mips/arch/mips/sgi-ip22/ip22-time.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/sgi-ip22/ip22-time.c	2005-09-20 01:39:27.359881664 +0900
@@ -35,7 +35,9 @@
 {
 	unsigned int yrs, mon, day, hrs, min, sec;
 	unsigned int save_control;
+	unsigned long flags;

@@ -47,6 +49,7 @@

 	if (yrs < 45)
 		yrs += 30;
@@ -60,6 +63,7 @@
 {
 	struct rtc_time tm;
 
 	to_tm(tim, &tm);
 
@@ -68,6 +72,7 @@
 	if (tm.tm_year >= 100)
 		tm.tm_year -= 100;

@@ -80,6 +85,7 @@

 	return 0;
 }
diff -ur linux-mips/arch/mips/sibyte/swarm/rtc_m41t81.c linux/arch/mips/sibyte/swarm/rtc_m41t81.c
--- linux-mips/arch/mips/sibyte/swarm/rtc_m41t81.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/sibyte/swarm/rtc_m41t81.c	2005-09-20 01:39:27.360881512 +0900
@@ -144,6 +144,7 @@
 int m41t81_set_time(unsigned long t)
 {
 	struct rtc_time tm;
+	unsigned long flags;
 
 	to_tm(t, &tm);
 
@@ -153,6 +154,7 @@

@@ -180,6 +182,7 @@
 	tm.tm_year %= 100;
 
 	return 0;
 }
@@ -187,14 +190,17 @@
 
 	/*
 
 	year += 2000;
 
diff -ur linux-mips/arch/mips/sibyte/swarm/rtc_xicor1241.c linux/arch/mips/sibyte/swarm/rtc_xicor1241.c
--- linux-mips/arch/mips/sibyte/swarm/rtc_xicor1241.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/sibyte/swarm/rtc_xicor1241.c	2005-09-20 01:39:27.361881360 +0900
@@ -113,9 +113,11 @@
 {
 	struct rtc_time tm;
 	int tmp;
+	unsigned long flags;
 
 	to_tm(t, &tm);

 
 	return 0;
 }
@@ -167,7 +170,9 @@

 	sec = BCD2BIN(sec);
 	min = BCD2BIN(min);
--- linux-mips/arch/mips/tx4938/common/rtc_rx5c348.c	2005-09-07 00:26:47.000000000 +0900
+++ linux/arch/mips/tx4938/common/rtc_rx5c348.c	2005-09-20 01:39:27.362881208 +0900
 
 /*
diff -ur linux-mips/include/asm-mips/mc146818-time.h linux/include/asm-mips/mc146818-time.h
--- linux-mips/include/asm-mips/mc146818-time.h	2005-09-07 00:26:47.000000000 +0900
+++ linux/include/asm-mips/mc146818-time.h	2005-09-20 01:39:27.363881056 +0900
@@ -33,7 +33,9 @@
 	int real_seconds, real_minutes, cmos_minutes;
 	unsigned char save_control, save_freq_select;
 	int retval = 0;
+	unsigned long flags;

@@ -79,14 +81,30 @@
 	 */
 
 	return retval;
 }
 
+/*
 
 	/*

 
 	return mktime(year, mon, day, hour, min, sec);
 }
diff -ur linux-mips/include/asm-mips/rtc.h linux/include/asm-mips/rtc.h
--- linux-mips/include/asm-mips/rtc.h	2005-09-20 01:32:52.109968784 +0900
+++ linux/include/asm-mips/rtc.h	2005-09-20 01:39:27.366880600 +0900
@@ -14,7 +14,6 @@
 
 #ifdef __KERNEL__

@@ -29,17 +28,13 @@

 
 	return RTC_24H;
 }
@@ -49,12 +44,10 @@
 	unsigned long nowtime;
 	int ret;

 	return ret;
 }
diff -ur linux-mips/include/asm-mips/time.h linux/include/asm-mips/time.h
--- linux-mips/include/asm-mips/time.h	2005-09-07 00:26:47.000000000 +0900
+++ linux/include/asm-mips/time.h	2005-09-20 01:39:27.367880448 +0900
@@ -20,6 +20,9 @@
 #include <linux/linkage.h>
 #include <linux/ptrace.h>
 #include <linux/rtc.h>
+#include <linux/spinlock.h>
+
+extern spinlock_t rtc_lock;
 
 /*