summaryrefslogtreecommitdiffstats
path: root/arch/ppc/kernel/time.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /arch/ppc/kernel/time.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'arch/ppc/kernel/time.c')
-rw-r--r--arch/ppc/kernel/time.c293
1 files changed, 211 insertions, 82 deletions
diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c
index a0e2e0534..cdcad8204 100644
--- a/arch/ppc/kernel/time.c
+++ b/arch/ppc/kernel/time.c
@@ -24,6 +24,7 @@
#include <asm/io.h>
#include <asm/nvram.h>
#include <asm/mc146818rtc.h>
+#include <asm/processor.h>
#include <linux/timex.h>
#include <linux/config.h>
@@ -43,6 +44,13 @@ static inline int CMOS_READ(int addr)
return (inb(NVRAM_DATA));
}
+static inline int CMOS_WRITE(int addr, int val)
+{
+ outb(addr>>8, NVRAM_AS1);
+ outb(addr, NVRAM_AS0);
+ return (outb(val, NVRAM_DATA));
+}
+
/* This function must be called with interrupts disabled
* It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
*
@@ -138,9 +146,110 @@ void do_settimeofday(struct timeval *tv)
time_state = TIME_BAD;
time_maxerror = 0x70000000;
time_esterror = 0x70000000;
+ set_rtc(xtime.tv_sec);
sti();
}
+static int month_days[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+#define FEBRUARY 2
+#define STARTOFTIME 1970
+#define SECDAY 86400L
+#define SECYR (SECDAY * 365)
+#define leapyear(year) ((year) % 4 == 0)
+#define days_in_year(a) (leapyear(a) ? 366 : 365)
+#define days_in_month(a) (month_days[(a) - 1])
+
+struct _tm
+{
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_day;
+ int tm_month;
+ int tm_year;
+};
+
+static _to_tm(int tim, struct _tm * tm)
+{
+ register int i;
+ register long hms, day;
+
+ day = tim / SECDAY;
+ hms = tim % SECDAY;
+
+ /* Hours, minutes, seconds are easy */
+ tm->tm_hour = hms / 3600;
+ tm->tm_min = (hms % 3600) / 60;
+ tm->tm_sec = (hms % 3600) % 60;
+
+ /* Number of years in days */
+ for (i = STARTOFTIME; day >= days_in_year(i); i++)
+ day -= days_in_year(i);
+ tm->tm_year = i;
+
+ /* Number of months in days left */
+ if (leapyear(tm->tm_year))
+ days_in_month(FEBRUARY) = 29;
+ for (i = 1; day >= days_in_month(i); i++)
+ day -= days_in_month(i);
+ days_in_month(FEBRUARY) = 28;
+ tm->tm_month = i;
+
+ /* Days are what is left over (+1) from all that. */
+ tm->tm_day = day + 1;
+}
+
+/*
+ * Set the time into the CMOS
+ */
+static void set_rtc(unsigned long nowtime)
+{
+ int retval = 0;
+ struct _tm tm;
+ unsigned char save_control, save_freq_select;
+
+ /*if (_Processor != _PROC_IBM) return;*/
+
+ _to_tm(nowtime, &tm);
+
+ /* tell the clock it's being set */
+ save_control = CMOS_MCRTC_READ(MCRTC_CONTROL);
+ CMOS_MCRTC_WRITE((save_control|MCRTC_SET), MCRTC_CONTROL);
+ /* stop and reset prescaler */
+ save_freq_select = CMOS_MCRTC_READ(MCRTC_FREQ_SELECT);
+ CMOS_MCRTC_WRITE((save_freq_select|MCRTC_DIV_RESET2), MCRTC_FREQ_SELECT);
+
+ printk("Set RTC H:M:S M/D/Y %d:%02d:%02d %d/%d/%d\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_month, tm.tm_day, tm.tm_year);
+ if (!(save_control & MCRTC_DM_BINARY) || MCRTC_ALWAYS_BCD) {
+ BIN_TO_BCD(tm.tm_sec);
+ BIN_TO_BCD(tm.tm_min);
+ BIN_TO_BCD(tm.tm_hour);
+ BIN_TO_BCD(tm.tm_month);
+ BIN_TO_BCD(tm.tm_day);
+ BIN_TO_BCD(tm.tm_year);
+ }
+
+ CMOS_MCRTC_WRITE(tm.tm_sec, MCRTC_SECONDS);
+ CMOS_MCRTC_WRITE(tm.tm_min, MCRTC_MINUTES);
+ CMOS_MCRTC_WRITE(tm.tm_hour, MCRTC_HOURS);
+ CMOS_MCRTC_WRITE(tm.tm_month, MCRTC_MONTH);
+ CMOS_MCRTC_WRITE(tm.tm_day, MCRTC_MINUTES);
+ CMOS_MCRTC_WRITE(tm.tm_year - 1900, MCRTC_MINUTES);
+
+ /* The following flags have to be released exactly in this order,
+ * otherwise the DS12887 (popular MC146818A clone with integrated
+ * battery and quartz) will not reset the oscillator and will not
+ * update precisely 500 ms later. You won't find this mentioned in
+ * the Dallas Semiconductor data sheets, but who believes data
+ * sheets anyway ... -- Markus Kuhn
+ */
+ CMOS_MCRTC_WRITE(save_control, MCRTC_CONTROL);
+ CMOS_MCRTC_WRITE(save_freq_select, MCRTC_FREQ_SELECT);
+}
/*
* In order to set the CMOS clock precisely, set_rtc_mmss has to be
@@ -156,17 +265,19 @@ static int set_rtc_mmss(unsigned long nowtime)
unsigned char save_control, save_freq_select;
#ifdef __powerpc__
+printk("%s: %d - set TOD\n", __FILE__, __LINE__);
return (-1); /* Not implemented */
#else
- save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
- CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+printk("%s: %d - set TOD\n", __FILE__, __LINE__);
+ save_control = CMOS_MCRTC_READ(MCRTC_CONTROL); /* tell the clock it's being set */
+ CMOS_MCRTC_WRITE((save_control|MCRTC_SET), MCRTC_CONTROL);
- save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
- CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+ save_freq_select = CMOS_MCRTC_READ(MCRTC_FREQ_SELECT); /* stop and reset prescaler */
+ CMOS_MCRTC_WRITE((save_freq_select|MCRTC_DIV_RESET2), MCRTC_FREQ_SELECT);
- cmos_minutes = CMOS_READ(RTC_MINUTES);
- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ cmos_minutes = CMOS_MCRTC_READ(MCRTC_MINUTES);
+ if (!(save_control & MCRTC_DM_BINARY) || MCRTC_ALWAYS_BCD)
BCD_TO_BIN(cmos_minutes);
/*
@@ -182,12 +293,12 @@ return (-1); /* Not implemented */
real_minutes %= 60;
if (abs(real_minutes - cmos_minutes) < 30) {
- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ if (!(save_control & MCRTC_DM_BINARY) || MCRTC_ALWAYS_BCD) {
BIN_TO_BCD(real_seconds);
BIN_TO_BCD(real_minutes);
}
- CMOS_WRITE(real_seconds,RTC_SECONDS);
- CMOS_WRITE(real_minutes,RTC_MINUTES);
+ CMOS_MCRTC_WRITE(real_seconds,MCRTC_SECONDS);
+ CMOS_MCRTC_WRITE(real_minutes,MCRTC_MINUTES);
} else
retval = -1;
@@ -198,8 +309,8 @@ return (-1); /* Not implemented */
* the Dallas Semiconductor data sheets, but who believes data
* sheets anyway ... -- Markus Kuhn
*/
- CMOS_WRITE(save_control, RTC_CONTROL);
- CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+ CMOS_MCRTC_WRITE(save_control, MCRTC_CONTROL);
+ CMOS_MCRTC_WRITE(save_freq_select, MCRTC_FREQ_SELECT);
return retval;
#endif
@@ -214,27 +325,42 @@ static long last_rtc_update = 0;
*/
static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs)
{
- do_timer(regs);
-
- /*
- * If we have an externally synchronized Linux clock, then update
- * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
- * called as close as possible to 500 ms before the new second starts.
- */
- if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
- xtime.tv_usec > 500000 - (tick >> 1) &&
- xtime.tv_usec < 500000 + (tick >> 1))
- if (set_rtc_mmss(xtime.tv_sec) == 0)
- last_rtc_update = xtime.tv_sec;
- else
- last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
-#if 0
- /* As we return to user mode fire off the other CPU schedulers.. this is
- basically because we don't yet share IRQ's around. This message is
- rigged to be safe on the 386 - basically its a hack, so don't look
- closely for now.. */
- smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0);
-#endif
+ static int timeints = 0;
+
+ do_timer(regs);
+
+ /*
+ * If we have an externally synchronized Linux clock, then update
+ * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+ * called as close as possible to 500 ms before the new second starts.
+ */
+ if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
+ xtime.tv_usec > 500000 - (tick >> 1) &&
+ xtime.tv_usec < 500000 + (tick >> 1))
+ if (set_rtc_mmss(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+
+
+ /* use hard disk LED as a heartbeat instead -- much more useful
+ -- Cort */
+ switch(timeints)
+ {
+ /* act like an actual heart beat -- ie thump-thump-pause... */
+ case 0:
+ case 20:
+ hard_disk_LED(1);
+ break;
+ case 7:
+ case 27:
+ hard_disk_LED(0);
+ break;
+ case 100:
+ timeints = -1;
+ break;
+ }
+ timeints++;
}
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
@@ -270,49 +396,52 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon,
unsigned long get_cmos_time(void)
{
- unsigned int year, mon, day, hour, min, sec;
- int i;
-
- if (isBeBox[0])
- {
-#ifndef __powerpc__
- /* 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.
- * Let's hope other operating systems interpret the RTC the same way.
- */
- /* 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)
- break;
- for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
- if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
- break;
-#endif
- do { /* Isn't this overkill ? UIP above should guarantee consistency */
- sec = CMOS_MCRTC_READ(MCRTC_SECONDS);
- min = CMOS_MCRTC_READ(MCRTC_MINUTES);
- hour = CMOS_MCRTC_READ(MCRTC_HOURS);
- day = CMOS_MCRTC_READ(MCRTC_DAY_OF_MONTH);
- mon = CMOS_MCRTC_READ(MCRTC_MONTH);
- year = CMOS_MCRTC_READ(MCRTC_YEAR);
- } while (sec != CMOS_MCRTC_READ(MCRTC_SECONDS));
- } else
+ unsigned int year, mon, day, hour, min, sec;
+ int i;
+
+ if (_Processor == _PROC_IBM)
+ {
+ do { /* Isn't this overkill ? UIP above should guarantee consistency */
+ sec = CMOS_MCRTC_READ(MCRTC_SECONDS);
+ min = CMOS_MCRTC_READ(MCRTC_MINUTES);
+ hour = CMOS_MCRTC_READ(MCRTC_HOURS);
+ day = CMOS_MCRTC_READ(MCRTC_DAY_OF_MONTH);
+ mon = CMOS_MCRTC_READ(MCRTC_MONTH);
+ year = CMOS_MCRTC_READ(MCRTC_YEAR);
+ } while (sec != CMOS_MCRTC_READ(MCRTC_SECONDS));
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+ } else
+ if (_Processor == _PROC_Be)
+ {
+ do { /* Isn't this overkill ? UIP above should guarantee consistency */
+ sec = CMOS_MCRTC_READ(MCRTC_SECONDS);
+ min = CMOS_MCRTC_READ(MCRTC_MINUTES);
+ hour = CMOS_MCRTC_READ(MCRTC_HOURS);
+ day = CMOS_MCRTC_READ(MCRTC_DAY_OF_MONTH);
+ mon = CMOS_MCRTC_READ(MCRTC_MONTH);
+ year = CMOS_MCRTC_READ(MCRTC_YEAR);
+ } while (sec != CMOS_MCRTC_READ(MCRTC_SECONDS));
+ } else
{ /* Motorola PowerStack etc. */
- do { /* Isn't this overkill ? UIP above should guarantee consistency */
- sec = CMOS_READ(RTC_SECONDS);
- min = CMOS_READ(RTC_MINUTES);
- hour = CMOS_READ(RTC_HOURS);
- day = CMOS_READ(RTC_DAY_OF_MONTH);
- mon = CMOS_READ(RTC_MONTH);
- year = CMOS_READ(RTC_YEAR);
- } while (sec != CMOS_READ(RTC_SECONDS));
- BCD_TO_BIN(sec);
- BCD_TO_BIN(min);
- BCD_TO_BIN(hour);
- BCD_TO_BIN(day);
- BCD_TO_BIN(mon);
- BCD_TO_BIN(year);
+ do { /* Isn't this overkill ? UIP above should guarantee consistency */
+ sec = CMOS_READ(RTC_SECONDS);
+ min = CMOS_READ(RTC_MINUTES);
+ hour = CMOS_READ(RTC_HOURS);
+ day = CMOS_READ(RTC_DAY_OF_MONTH);
+ mon = CMOS_READ(RTC_MONTH);
+ year = CMOS_READ(RTC_YEAR);
+ } while (sec != CMOS_READ(RTC_SECONDS));
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
}
#if 0
printk("CMOS TOD - M/D/Y H:M:S = %d/%d/%d %d:%02d:%02d\n", mon, day, year, hour, min, sec);
@@ -324,13 +453,13 @@ printk("CMOS TOD - M/D/Y H:M:S = %d/%d/%d %d:%02d:%02d\n", mon, day, year, hour,
void time_init(void)
{
- void (*irq_handler)(int, struct pt_regs *);
- xtime.tv_sec = get_cmos_time();
- xtime.tv_usec = 0;
-
- /* If we have the CPU hardware time counters, use them */
- irq_handler = timer_interrupt;
- if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL) != 0)
- panic("Could not allocate timer IRQ!");
+ void (*irq_handler)(int, struct pt_regs *);
+ xtime.tv_sec = get_cmos_time();
+ xtime.tv_usec = 0;
+
+ /* If we have the CPU hardware time counters, use them */
+ irq_handler = timer_interrupt;
+ if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL) != 0)
+ panic("Could not allocate timer IRQ!");
}