Patch, spin-locks for CMOS access under SMP

From: Richard B. Johnson (root@chaos.analogic.com)
Date: Wed Oct 18 2000 - 15:45:19 EST


This puts CMOS Chip access under a spin-lock and exports the
rtc_lock symbol. It is for 2.2.17, should patch to 2.2.18.

--- linux-2.2.17/arch/i386/kernel/i386_ksyms.c.orig Wed Oct 18 12:53:42 2000
+++ linux-2.2.17/arch/i386/kernel/i386_ksyms.c Wed Oct 18 12:55:55 2000
@@ -17,7 +17,7 @@
 #include <asm/hardirq.h>
 #include <asm/delay.h>
 #include <asm/irq.h>
-
+extern spinlock_t rtc_lock;
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(elf_fpregset_t *);
 
@@ -41,7 +41,7 @@
 EXPORT_SYMBOL(disable_irq);
 EXPORT_SYMBOL(disable_irq_nosync);
 EXPORT_SYMBOL(kernel_thread);
-
+EXPORT_SYMBOL(rtc_lock);
 EXPORT_SYMBOL(init_mm);
 
 EXPORT_SYMBOL_NOVERS(__down_failed);
--- linux-2.2.17/arch/i386/kernel/time.c.orig Wed Jun 7 17:26:42 2000
+++ linux-2.2.17/arch/i386/kernel/time.c Wed Oct 18 13:27:24 2000
@@ -28,6 +28,8 @@
  * 1998-12-24 Copyright (C) 1998 Andrea Arcangeli
  * Fixed a xtime SMP race (we need the xtime_lock rw spinlock to
  * serialize accesses to xtime/lost_ticks).
+ * 2000-10-17 rjohnson@analogic.com
+ * Spin-lock added to RTC code and spin-lock exported.
  */
 
 #include <linux/errno.h>
@@ -294,13 +296,19 @@
  *
  * BUG: This routine does not handle hour overflow properly; it just
  * sets the minutes. Usually you'll only notice that after reboot!
+ *
  */
+
+spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+
 static int set_rtc_mmss(unsigned long nowtime)
 {
+ unsigned long flags;
         int retval = 0;
         int real_seconds, real_minutes, cmos_minutes;
         unsigned char save_control, save_freq_select;
 
+ 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);
 
@@ -346,7 +354,7 @@
          */
         CMOS_WRITE(save_control, RTC_CONTROL);
         CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
-
+ spin_unlock_irqrestore(&rtc_lock, flags);
         return retval;
 }
 
@@ -496,9 +504,12 @@
 /* not static: needed by APM */
 unsigned long get_cmos_time(void)
 {
+ unsigned long flags;
         unsigned int year, mon, day, hour, min, sec;
         int i;
 
+ spin_lock_irqsave(&rtc_lock, flags);
+
         /* The Linux interpretation of the CMOS clock register contents:
          * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
          * RTC registers show the second which has precisely just started.
@@ -528,6 +539,7 @@
             BCD_TO_BIN(mon);
             BCD_TO_BIN(year);
           }
+ spin_unlock_irqrestore(&rtc_lock, flags);
         if ((year += 1900) < 1970)
                 year += 100;
         return mktime(year, mon, day, hour, min, sec);
--- linux-2.2.17/drivers/block/hd.c.orig Wed Oct 18 15:28:18 2000
+++ linux-2.2.17/drivers/block/hd.c Wed Oct 18 16:36:11 2000
@@ -21,6 +21,8 @@
  * Removed 99% of above. Use Mark's ide driver for those options.
  * This is now a lightweight ST-506 driver. (Paul Gortmaker)
  *
+ * 17-OCT-2000 rjohnson@analogic.com Added spin-lock for reading
+ * CMOS chip.
  */
   
 /* Uncomment the following if you want verbose error reports. */
@@ -48,7 +50,7 @@
 
 #define MAJOR_NR HD_MAJOR
 #include <linux/blk.h>
-
+extern spinlock_t rtc_lock;
 static int revalidate_hddisk(kdev_t, int);
 
 #define HD_DELAY 0
@@ -701,6 +703,7 @@
 static void hd_geninit(struct gendisk *ignored)
 {
         int drive;
+ unsigned long flags;
 
 #ifdef __i386__
         if (!NR_HD) {
@@ -743,13 +746,14 @@
 
                 
         */
-
+ spin_lock_irqsave(&rtc_lock, flags);
                 if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) {
                         if (cmos_disks & 0x0f)
                                 NR_HD = 2;
                         else
                                 NR_HD = 1;
                 }
+ spin_unlock_irqrestore(&rtc_lock, flags);
         }
 #endif /* __i386__ */
         for (drive=0 ; drive < NR_HD ; drive++) {
--- linux-2.2.17/drivers/block/ide-probe.c.orig Wed Oct 18 15:38:05 2000
+++ linux-2.2.17/drivers/block/ide-probe.c Wed Oct 18 16:37:33 2000
@@ -18,6 +18,8 @@
  * by Andrea Arcangeli
  * Version 1.03 fix for (hwif->chipset == ide_4drives)
  * Version 1.04 fixed buggy treatments of known flash memory cards
+ * 17-OCT-2000 rjohnson@analogic.com Added spin-locks for reading CMOS
+ * chip.
  */
 
 #undef REALLY_SLOW_IO /* most systems can safely undef this */
@@ -41,6 +43,8 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
+extern spinlock_t rtc_lock;
+
 #include "ide.h"
 
 static inline void do_identify (ide_drive_t *drive, byte cmd)
@@ -386,6 +390,7 @@
 static void probe_cmos_for_drives (ide_hwif_t *hwif)
 {
 #ifdef __i386__
+ unsigned long flags;
         extern struct drive_info_struct drive_info;
         byte cmos_disks, *BIOS = (byte *) &drive_info;
         int unit;
@@ -394,8 +399,10 @@
         if (hwif->chipset == ide_pdc4030 && hwif->channel != 0)
                 return;
 #endif /* CONFIG_BLK_DEV_PDC4030 */
+ spin_lock_irqsave(&rtc_lock, flags);
         outb_p(0x12,0x70); /* specify CMOS address 0x12 */
         cmos_disks = inb_p(0x71); /* read the data from 0x12 */
+ spin_unlock_irqrestore(&rtc_lock, flags);
         /* Extract drive geometry from CMOS+BIOS if not already setup */
         for (unit = 0; unit < MAX_DRIVES; ++unit) {
                 ide_drive_t *drive = &hwif->drives[unit];
--- linux-2.2.17/drivers/char/rtc.c.orig Wed Oct 18 14:52:39 2000
+++ linux-2.2.17/drivers/char/rtc.c Wed Oct 18 15:22:31 2000
@@ -33,9 +33,10 @@
  * DEC Alpha as the CMOS clock is also used for other things.
  * 1.09 Nikita Schmidt: epoch support and some Alpha cleanup.
  *
+ * 1.10 rjohnson@analogic.com: Added spin-locks for SMP.
  */
 
-#define RTC_VERSION "1.09"
+#define RTC_VERSION "1.10"
 
 #define RTC_IRQ 8 /* Can't see this changing soon. */
 #define RTC_IO_EXTENT 0x10 /* Only really two ports, but... */
@@ -46,6 +47,9 @@
  * design of the RTC, we don't want two different things trying to
  * get to it at once. (e.g. the periodic 11 min sync from time.c vs.
  * this driver.)
+ *
+ * 17-OCT-2000 rjohnson@analogic.com. All reads/writes are now
+ * protected by spin-locks.
  */
 
 #include <linux/types.h>
@@ -102,6 +106,7 @@
 unsigned char rtc_status = 0; /* bitmapped status byte. */
 unsigned long rtc_freq = 0; /* Current periodic IRQ rate */
 unsigned long rtc_irq_data = 0; /* our output to the world */
+extern spinlock_t rtc_lock;
 
 /*
  * If this driver ever becomes modularised, it will be really nice
@@ -276,8 +281,7 @@
                 if (sec >= 60)
                         sec = 0xff;
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
                 if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ||
                     RTC_ALWAYS_BCD)
                 {
@@ -288,7 +292,7 @@
                 CMOS_WRITE(hrs, RTC_HOURS_ALARM);
                 CMOS_WRITE(min, RTC_MINUTES_ALARM);
                 CMOS_WRITE(sec, RTC_SECONDS_ALARM);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
                 return 0;
         }
@@ -336,12 +340,11 @@
                 if ((yrs -= epoch) > 255) /* They are unsigned */
                         return -EINVAL;
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
                 if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
                     || RTC_ALWAYS_BCD) {
                         if (yrs > 169) {
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
                                 return -EINVAL;
                         }
                         if (yrs >= 100)
@@ -370,7 +373,7 @@
                 CMOS_WRITE(save_control, RTC_CONTROL);
                 CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
                 return 0;
         }
         case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
@@ -405,12 +408,11 @@
 
                 rtc_freq = arg;
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
                 val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
                 val |= (16 - tmp);
                 CMOS_WRITE(val, RTC_FREQ_SELECT);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
                 return 0;
         }
 #ifdef __alpha__
@@ -465,15 +467,14 @@
         unsigned char tmp;
         unsigned long flags;
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         tmp = CMOS_READ(RTC_CONTROL);
         tmp &= ~RTC_PIE;
         tmp &= ~RTC_AIE;
         tmp &= ~RTC_UIE;
         CMOS_WRITE(tmp, RTC_CONTROL);
         CMOS_READ(RTC_INTR_FLAGS);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
         if (rtc_status & RTC_TIMER_ON) {
                 rtc_status &= ~RTC_TIMER_ON;
@@ -546,11 +547,10 @@
                 while (jiffies - uip_watchdog < 2*HZ/100)
                         barrier();
         
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         year = CMOS_READ(RTC_YEAR);
         ctrl = CMOS_READ(RTC_CONTROL);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
         
         if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
                 BCD_TO_BIN(year); /* This should never happen... */
@@ -568,11 +568,10 @@
         init_timer(&rtc_irq_timer);
         rtc_irq_timer.function = rtc_dropped_irq;
         rtc_wait = NULL;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         /* Initialize periodic freq. to CMOS reset default, which is 1024Hz */
         CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
         rtc_freq = 1024;
         return 0;
 }
@@ -596,12 +595,11 @@
         printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", rtc_freq);
         mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         rtc_irq_data += ((rtc_freq/HZ)<<8);
         rtc_irq_data &= ~0xff;
         rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 }
 
 /*
@@ -615,11 +613,10 @@
         unsigned char batt, ctrl;
         unsigned long flags;
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         batt = CMOS_READ(RTC_VALID) & RTC_VRT;
         ctrl = CMOS_READ(RTC_CONTROL);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
         p = buf;
 
@@ -690,10 +687,9 @@
         unsigned long flags;
         unsigned char uip;
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
         return uip;
 }
 
@@ -723,8 +719,7 @@
          * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
          * by the RTC when initially set to a non-zero value.
          */
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
         rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
         rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
@@ -732,7 +727,7 @@
         rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
         rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
         ctrl = CMOS_READ(RTC_CONTROL);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
         if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
         {
@@ -763,13 +758,12 @@
          * Only the values that we read from the RTC are set. That
          * means only tm_hour, tm_min, and tm_sec.
          */
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         alm_tm->tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
         alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM);
         alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM);
         ctrl = CMOS_READ(RTC_CONTROL);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
         if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
         {
@@ -794,13 +788,12 @@
         unsigned char val;
         unsigned long flags;
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         val = CMOS_READ(RTC_CONTROL);
         val &= ~bit;
         CMOS_WRITE(val, RTC_CONTROL);
         CMOS_READ(RTC_INTR_FLAGS);
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
         rtc_irq_data = 0;
 }
 
@@ -809,12 +802,11 @@
         unsigned char val;
         unsigned long flags;
 
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&rtc_lock, flags);
         val = CMOS_READ(RTC_CONTROL);
         val |= bit;
         CMOS_WRITE(val, RTC_CONTROL);
         CMOS_READ(RTC_INTR_FLAGS);
         rtc_irq_data = 0;
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 }
--- linux-2.2.17/drivers/sgi/char/ds1286.c.orig Wed Oct 18 15:52:13 2000
+++ linux-2.2.17/drivers/sgi/char/ds1286.c Wed Oct 18 15:57:07 2000
@@ -73,9 +73,7 @@
 
 static inline unsigned char ds1286_is_updating(void);
 
-#ifdef __SMP__
-static spinlock_t ds1286_lock = SPIN_LOCK_UNLOCKED;
-#endif
+extern spinlock_t rtc_lock;
 
 /*
  * Bits in rtc_status. (7 bits of room for future expansion)
@@ -158,11 +156,11 @@
                 if (!capable(CAP_SYS_TIME))
                         return -EACCES;
 
- spin_lock_irqsave(&ds1286_lock, flags);
+ spin_lock_irqsave(&rtc_lock, flags);
                 val = CMOS_READ(RTC_CMD);
                 val |= RTC_TDM;
                 CMOS_WRITE(val, RTC_CMD);
- spin_unlock_irqrestore(&ds1286_lock, flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
                 return 0;
         }
@@ -174,11 +172,11 @@
                 if (!capable(CAP_SYS_TIME))
                         return -EACCES;
 
- spin_lock_irqsave(&ds1286_lock, flags);
+ spin_lock_irqsave(&rtc_lock, flags);
                 val = CMOS_READ(RTC_CMD);
                 val &= ~RTC_TDM;
                 CMOS_WRITE(val, RTC_CMD);
- spin_unlock_irqrestore(&ds1286_lock, flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
                 return 0;
         }
@@ -190,11 +188,11 @@
                 if (!capable(CAP_SYS_TIME))
                         return -EACCES;
 
- spin_lock_irqsave(&ds1286_lock, flags);
+ spin_lock_irqsave(&rtc_lock, flags);
                 val = CMOS_READ(RTC_CMD);
                 val |= RTC_WAM;
                 CMOS_WRITE(val, RTC_CMD);
- spin_unlock_irqrestore(&ds1286_lock, flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
                 return 0;
         }
@@ -206,11 +204,11 @@
                 if (!capable(CAP_SYS_TIME))
                         return -EACCES;
 
- spin_lock_irqsave(&ds1286_lock, flags);
+ spin_lock_irqsave(&rtc_lock, flags);
                 val = CMOS_READ(RTC_CMD);
                 val &= ~RTC_WAM;
                 CMOS_WRITE(val, RTC_CMD);
- spin_unlock_irqrestore(&ds1286_lock, flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
                 return 0;
         }
@@ -315,7 +313,7 @@
                 BIN_TO_BCD(mon);
                 BIN_TO_BCD(yrs);
 
- spin_lock_irqsave(&ds1286_lock, flags);
+ spin_lock_irqsave(&rtc_lock, flags);
                 save_control = CMOS_READ(RTC_CMD);
                 CMOS_WRITE((save_control|RTC_TE), RTC_CMD);
 
@@ -328,7 +326,7 @@
                 CMOS_WRITE(0, RTC_HUNDREDTH_SECOND);
 
                 CMOS_WRITE(save_control, RTC_CMD);
- spin_unlock_irqrestore(&ds1286_lock, flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
                 return 0;
         }
@@ -515,7 +513,7 @@
          * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
          * by the RTC when initially set to a non-zero value.
          */
- spin_lock_irqsave(&ds1286_lock, flags);
+ spin_lock_irqsave(&rtc_lock, flags);
         save_control = CMOS_READ(RTC_CMD);
         CMOS_WRITE((save_control|RTC_TE), RTC_CMD);
 
@@ -527,7 +525,7 @@
         rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
 
         CMOS_WRITE(save_control, RTC_CMD);
- spin_unlock_irqrestore(&ds1286_lock, flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
         BCD_TO_BIN(rtc_tm->tm_sec);
         BCD_TO_BIN(rtc_tm->tm_min);
@@ -557,12 +555,12 @@
          * Only the values that we read from the RTC are set. That
          * means only tm_wday, tm_hour, tm_min.
          */
- spin_lock_irqsave(&ds1286_lock, flags);
+ spin_lock_irqsave(&rtc_lock, flags);
         alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM) & 0x7f;
         alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM) & 0x1f;
         alm_tm->tm_wday = CMOS_READ(RTC_DAY_ALARM) & 0x07;
         cmd = CMOS_READ(RTC_CMD);
- spin_unlock_irqrestore(&ds1286_lock, flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
 
         BCD_TO_BIN(alm_tm->tm_min);
         BCD_TO_BIN(alm_tm->tm_hour);

Cheers,
Dick Johnson

Penguin : Linux version 2.2.17 on an i686 machine (801.18 BogoMips).

"Memory is like gasoline. You use it up when you are running. Of
course you get it all back when you reboot..."; Actual explanation
obtained from the Micro$oft help desk.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Mon Oct 23 2000 - 21:00:13 EST