patch-1.3.98 linux/drivers/char/rtc.c
Next file: linux/drivers/char/serial.c
Previous file: linux/drivers/char/random.c
Back to the patch index
Back to the overall index
- Lines: 338
- Date:
Fri May 3 11:09:58 1996
- Orig file:
v1.3.97/linux/drivers/char/rtc.c
- Orig date:
Tue Apr 23 13:57:09 1996
diff -u --recursive --new-file v1.3.97/linux/drivers/char/rtc.c linux/drivers/char/rtc.c
@@ -30,7 +30,7 @@
*
*/
-#define RTC_VERSION "1.04"
+#define RTC_VERSION "1.05"
#define RTC_IRQ 8 /* Can't see this changing soon. */
#define RTC_IO_BASE 0x70 /* Or this... */
@@ -67,6 +67,8 @@
static struct wait_queue *rtc_wait;
+static struct timer_list rtc_irq_timer;
+
static int rtc_lseek(struct inode *inode, struct file *file, off_t offset,
int origin);
@@ -81,6 +83,7 @@
void get_rtc_time (struct tm *rtc_tm);
void get_rtc_alm_time (struct tm *alm_tm);
+void rtc_dropped_irq(unsigned long data);
inline void set_rtc_irq_bit(unsigned char bit);
inline void mask_rtc_irq_bit(unsigned char bit);
@@ -92,8 +95,10 @@
*/
#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
+#define RTC_TIMER_ON 0x02 /* missed irq timer active */
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 */
unsigned char days_in_mo[] =
@@ -119,6 +124,12 @@
rtc_irq_data &= ~0xff;
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
wake_up_interruptible(&rtc_wait);
+
+ if (rtc_status & RTC_TIMER_ON) {
+ del_timer(&rtc_irq_timer);
+ rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
+ add_timer(&rtc_irq_timer);
+ }
}
/*
@@ -157,12 +168,16 @@
break;
}
schedule();
- continue;
}
if (retval == 0) {
- memcpy_tofs(buf, &rtc_irq_data, sizeof(unsigned long));
+ unsigned long data, flags;
+ save_flags(flags);
+ cli();
+ data = rtc_irq_data;
rtc_irq_data = 0;
+ restore_flags(flags);
+ memcpy_tofs(buf, &data, sizeof(unsigned long));
retval = sizeof(unsigned long);
}
@@ -192,27 +207,30 @@
case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
{
mask_rtc_irq_bit(RTC_PIE);
+ if (rtc_status & RTC_TIMER_ON) {
+ del_timer(&rtc_irq_timer);
+ rtc_status &= ~RTC_TIMER_ON;
+ }
return 0;
}
case RTC_PIE_ON: /* Allow periodic ints */
{
- unsigned int hz;
- unsigned char tmp;
-
- save_flags(flags);
- cli();
- tmp = CMOS_READ(RTC_FREQ_SELECT) & 0x0f;
- restore_flags(flags);
-
- hz = (tmp ? (65536/(1<<tmp)) : 0);
/*
* We don't really want Joe User enabling more
* than 64Hz of interrupts on a multi-user machine.
*/
- if ((hz > 64) && (!suser()))
+ if ((rtc_freq > 64) && (!suser()))
return -EPERM;
+ if (rtc_freq == 0)
+ return -EINVAL;
+
+ if (!(rtc_status & RTC_TIMER_ON)) {
+ rtc_status |= RTC_TIMER_ON;
+ rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
+ add_timer(&rtc_irq_timer);
+ }
set_rtc_irq_bit(RTC_PIE);
return 0;
}
@@ -382,19 +400,13 @@
}
case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
{
- unsigned long hz;
int retval;
retval = verify_area(VERIFY_WRITE, (unsigned long*)arg, sizeof(unsigned long));
if (retval != 0)
return retval;
- save_flags(flags);
- cli();
- retval = CMOS_READ(RTC_FREQ_SELECT) & 0x0f;
- restore_flags(flags);
- hz = (retval ? (65536/(1<<retval)) : 0);
- memcpy_tofs((unsigned long*)arg, &hz, sizeof(unsigned long));
+ memcpy_tofs((unsigned long*)arg, &rtc_freq, sizeof(unsigned long));
return 0;
}
case RTC_IRQP_SET: /* Set periodic IRQ rate. */
@@ -405,7 +417,7 @@
/*
* The max we can do is 8192Hz.
*/
- if (arg > 8192)
+ if ((arg < 2) || (arg > 8192))
return -EINVAL;
/*
* We don't really want Joe User generating more
@@ -423,6 +435,8 @@
if ((arg != 0) && (arg != (1<<tmp)))
return -EINVAL;
+ rtc_freq = arg;
+
save_flags(flags);
cli();
val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
@@ -480,6 +494,12 @@
CMOS_WRITE(tmp, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS);
restore_flags(flags);
+
+ if (rtc_status & RTC_TIMER_ON) {
+ rtc_status &= ~RTC_TIMER_ON;
+ del_timer(&rtc_irq_timer);
+ }
+
rtc_irq_data = 0;
rtc_status &= ~RTC_IS_OPEN;
}
@@ -520,6 +540,8 @@
int rtc_init(void)
{
+ unsigned long flags;
+
printk("Real Time Clock Driver v%s\n", RTC_VERSION);
if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
{
@@ -530,11 +552,47 @@
misc_register(&rtc_dev);
/* Check region? Naaah! Just snarf it up. */
request_region(RTC_IO_BASE, RTC_IO_EXTENT, "rtc");
+ init_timer(&rtc_irq_timer);
+ rtc_irq_timer.function = rtc_dropped_irq;
rtc_wait = NULL;
+ save_flags(flags);
+ cli();
+ rtc_freq = CMOS_READ(RTC_FREQ_SELECT) & 0x0F;
+ restore_flags(flags);
+ rtc_freq = (rtc_freq ? (65536/(1<<rtc_freq)) : 0);
return 0;
}
/*
+ * At IRQ rates >= 4096Hz, an interrupt may get lost altogether.
+ * (usually during an IDE disk interrupt, with IRQ unmasking off)
+ * Since the interrupt handler doesn't get called, the IRQ status
+ * byte doesn't get read, and the RTC stops generating interrupts.
+ * A timer is set, and will call this function if/when that happens.
+ * To get it out of this stalled state, we just read the status.
+ * At least a jiffy of interrupts (rtc_freq/HZ) will have been lost.
+ * (You *really* shouldn't be trying to use a non-realtime system
+ * for something that requires a steady > 1KHz signal anyways.)
+ */
+
+void rtc_dropped_irq(unsigned long data)
+{
+ unsigned long flags;
+
+ printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", rtc_freq);
+ del_timer(&rtc_irq_timer);
+ rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
+ add_timer(&rtc_irq_timer);
+
+ save_flags(flags);
+ cli();
+ rtc_irq_data += ((rtc_freq/HZ)<<8);
+ rtc_irq_data &= ~0xff;
+ rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */
+ restore_flags(flags);
+}
+
+/*
* Info exported via "/proc/rtc".
*/
@@ -542,11 +600,11 @@
{
char *p;
struct tm tm;
- unsigned char freq, batt, ctrl;
+ unsigned char batt, ctrl;
unsigned long flags;
save_flags(flags);
- freq = CMOS_READ(RTC_FREQ_SELECT) & 0x0F;
+ cli();
batt = CMOS_READ(RTC_VALID) & RTC_VRT;
ctrl = CMOS_READ(RTC_CONTROL);
restore_flags(flags);
@@ -559,10 +617,11 @@
* There is no way to tell if the luser has the RTC set for local
* time or for Universal Standard Time (GMT). Probably local though.
*/
- p += sprintf(p, "date : %04d-%02d-%02d\n",
+ p += sprintf(p,
+ "rtc_time\t: %02d:%02d:%02d\n"
+ "rtc_date\t: %04d-%02d-%02d\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
- p += sprintf(p, "time : %02d-%02d-%02d\n",
- tm.tm_hour, tm.tm_min, tm.tm_sec);
get_rtc_alm_time(&tm);
@@ -571,44 +630,41 @@
* match any value for that particular field. Values that are
* greater than a valid time, but less than 0xc0 shouldn't appear.
*/
- p += sprintf(p, "alarm : ");
+ p += sprintf(p, "alarm\t\t: ");
if (tm.tm_hour <= 24)
- p += sprintf(p, "%02d", tm.tm_hour);
+ p += sprintf(p, "%02d:", tm.tm_hour);
else
- p += sprintf(p, "**");
- p += sprintf(p, "-");
+ p += sprintf(p, "**:");
+
if (tm.tm_min <= 59)
- p += sprintf(p, "%02d", tm.tm_min);
+ p += sprintf(p, "%02d:", tm.tm_min);
else
- p += sprintf(p, "**");
- p += sprintf(p, "-");
+ p += sprintf(p, "**:");
+
if (tm.tm_sec <= 59)
- p += sprintf(p, "%02d", tm.tm_sec);
+ p += sprintf(p, "%02d\n", tm.tm_sec);
else
- p += sprintf(p, "**");
- p += sprintf(p, "\n");
+ p += sprintf(p, "**\n");
- p += sprintf(p, "daylight : %s\n",
- ((ctrl & RTC_DST_EN) ? "yes" : "no" ));
- p += sprintf(p, "bcd : %s\n",
- ((ctrl & RTC_DM_BINARY) ? "no" : "yes" ));
- p += sprintf(p, "24hr : %s\n",
- ((ctrl & RTC_24H) ? "yes" : "no" ));
- p += sprintf(p, "sqwave : %s\n",
- ((ctrl & RTC_SQWE) ? "yes" : "no" ));
-
- p += sprintf(p, "alarm_int : %s\n",
- ((ctrl & RTC_AIE) ? "yes" : "no" ));
- p += sprintf(p, "update_int : %s\n",
- ((ctrl & RTC_UIE) ? "yes" : "no" ));
- p += sprintf(p, "periodic_int : %s\n",
- ((ctrl & RTC_PIE) ? "yes" : "no" ));
-
- p += sprintf(p, "periodic_freq : %d\n",
- (freq ? (65536/(1<<freq)) : 0));
-
- p += sprintf(p, "battery_ok : %s\n",
- (batt ? "yes" : "no"));
+ p += sprintf(p,
+ "DST_enable\t: %s\n"
+ "BCD\t\t: %s\n"
+ "24hr\t\t: %s\n"
+ "sqare_wave\t: %s\n"
+ "alarm_IRQ\t: %s\n"
+ "update_IRQ\t: %s\n"
+ "periodic_IRQ\t: %s\n"
+ "periodic_freq\t: %ld\n"
+ "batt_status\t: %s\n",
+ (ctrl & RTC_DST_EN) ? "yes" : "no",
+ (ctrl & RTC_DM_BINARY) ? "no" : "yes",
+ (ctrl & RTC_24H) ? "yes" : "no",
+ (ctrl & RTC_SQWE) ? "yes" : "no",
+ (ctrl & RTC_AIE) ? "yes" : "no",
+ (ctrl & RTC_UIE) ? "yes" : "no",
+ (ctrl & RTC_PIE) ? "yes" : "no",
+ rtc_freq,
+ batt ? "okay" : "dead");
return p - buf;
}
@@ -622,6 +678,7 @@
unsigned char uip;
save_flags(flags);
+ cli();
uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
restore_flags(flags);
return uip;
@@ -744,7 +801,7 @@
val |= bit;
CMOS_WRITE(val, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS);
- restore_flags(flags);
rtc_irq_data = 0;
+ restore_flags(flags);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this