611 lines
18 KiB
C
611 lines
18 KiB
C
/*
|
|
* i386 CMOS starts out with 14 bytes clock data
|
|
* alpha has something similar, but with details
|
|
* depending on the machine type.
|
|
*
|
|
* byte 0: seconds (0-59)
|
|
* byte 2: minutes (0-59)
|
|
* byte 4: hours (0-23 in 24hr mode,
|
|
* 1-12 in 12hr mode, with high bit unset/set if am/pm)
|
|
* byte 6: weekday (1-7, Sunday=1)
|
|
* byte 7: day of the month (1-31)
|
|
* byte 8: month (1-12)
|
|
* byte 9: year (0-99)
|
|
* Numbers are stored in BCD/binary if bit 2 of byte 11 is unset/set
|
|
* The clock is in 12hr/24hr mode if bit 1 of byte 11 is unset/set
|
|
* The clock is undefined (being updated) if bit 7 of byte 10 is set.
|
|
* The clock is frozen (to be updated) by setting bit 7 of byte 11
|
|
* Bit 7 of byte 14 indicates whether the CMOS clock is reliable:
|
|
* it is 1 if RTC power has been good since this bit was last read;
|
|
* it is 0 when the battery is dead and system power has been off.
|
|
*
|
|
* Avoid setting the RTC clock within 2 seconds of the day rollover
|
|
* that starts a new month or enters daylight saving time.
|
|
*
|
|
* The century situation is messy:
|
|
* Usually byte 50 (0x32) gives the century (in BCD, so 19 or 20 hex),
|
|
* but IBM PS/2 has (part of) a checksum there and uses byte 55 (0x37).
|
|
* Sometimes byte 127 (0x7f) or Bank 1, byte 0x48 gives the century.
|
|
* The original RTC will not access any century byte; some modern
|
|
* versions will. If a modern RTC or BIOS increments the century byte
|
|
* it may go from 0x19 to 0x20, but in some buggy cases 0x1a is produced.
|
|
*/
|
|
|
|
/*
|
|
* A struct tm has int fields
|
|
* tm_sec (0-59, 60 or 61 only for leap seconds)
|
|
* tm_min (0-59)
|
|
* tm_hour (0-23)
|
|
* tm_mday (1-31)
|
|
* tm_mon (0-11)
|
|
* tm_year (number of years since 1900)
|
|
* tm_wday (0-6, 0=Sunday)
|
|
* tm_yday (0-365)
|
|
* tm_isdst (>0: yes, 0: no, <0: unknown)
|
|
*/
|
|
|
|
#include <unistd.h> /* for geteuid() */
|
|
#include <fcntl.h> /* for O_RDWR */
|
|
|
|
#include "nls.h"
|
|
|
|
#if defined(__i386__) || defined(__alpha__)
|
|
#include <asm/io.h> /* for inb, outb */
|
|
#else
|
|
void outb(int a, int b){}
|
|
int inb(int c){ return 0; }
|
|
#endif
|
|
|
|
#include "clock.h"
|
|
|
|
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
|
|
#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
|
|
|
|
#define TM_EPOCH 1900
|
|
int cmos_epoch = 1900; /* 1980 for an alpha in ARC console time */
|
|
/* One also sees 1952 (Digital Unix)
|
|
and 1958 (ALPHA_PRE_V1_2_SRM_CONSOLE) */
|
|
|
|
/* Martin Ostermann writes:
|
|
The problem with the Jensen is twofold: First, it has the clock at a
|
|
different address. Secondly, it has a distinction beween "local" and
|
|
normal bus addresses. The local ones pertain to the hardware integrated
|
|
into the chipset, like serial/parallel ports and of course, the RTC.
|
|
Those need to be addressed differently. This is handled fine in the kernel,
|
|
and it's not a problem, since this usually gets totally optimized by the
|
|
compile. But the i/o routines of (g)libc lack this support so far.
|
|
The result of this is, that the old clock program worked only on the
|
|
Jensen when USE_DEV_PORT was defined, but not with the normal inb/outb
|
|
functions.
|
|
*/
|
|
int use_dev_port = 0; /* 1 for Jensen */
|
|
int dev_port_fd;
|
|
unsigned short clock_ctl_addr = 0x70; /* 0x170 for Jensen */
|
|
unsigned short clock_data_addr = 0x71; /* 0x171 for Jensen */
|
|
|
|
|
|
int century_byte = 0; /* 0: don't access a century byte
|
|
50 (0x32): usual PC value
|
|
55 (0x37): PS/2 */
|
|
|
|
#ifdef __alpha__
|
|
int funkyTOY = 0; /* 1 for PC164/LX164/SX164 type alpha */
|
|
#endif
|
|
|
|
#ifdef __alpha
|
|
|
|
static int
|
|
is_in_cpuinfo(char *fmt, char *str)
|
|
{
|
|
FILE *cpuinfo;
|
|
char field[256];
|
|
char format[256];
|
|
int found = 0;
|
|
|
|
sprintf(format, "%s : %s", fmt, "%255s");
|
|
|
|
if ((cpuinfo = fopen ("/proc/cpuinfo", "r")) != NULL) {
|
|
while (!feof(cpuinfo)) {
|
|
if (fscanf (cpuinfo, format, field) == 1) {
|
|
if (strncmp(field, str, strlen(str)) == 0)
|
|
found = 1;
|
|
break;
|
|
}
|
|
fgets (field, 256, cpuinfo);
|
|
}
|
|
fclose(cpuinfo);
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/* Set cmos_epoch, either from user options, or by asking the kernel,
|
|
or by looking at /proc/cpu_info */
|
|
void
|
|
set_cmos_epoch(int ARCconsole, int SRM) {
|
|
unsigned long epoch;
|
|
|
|
/* Believe the user */
|
|
if (epoch_option != -1) {
|
|
cmos_epoch = epoch_option;
|
|
return;
|
|
}
|
|
|
|
if (ARCconsole)
|
|
cmos_epoch = 1980;
|
|
|
|
if (ARCconsole || SRM)
|
|
return;
|
|
|
|
|
|
/* If we can ask the kernel, we don't need guessing from /proc/cpuinfo */
|
|
if (get_epoch_rtc(&epoch, 1) == 0) {
|
|
cmos_epoch = epoch;
|
|
return;
|
|
}
|
|
|
|
/* The kernel source today says: read the year. If it is
|
|
in 11-43 then the epoch is 1980 (this covers 1991-2023).
|
|
Otherwise, if it is less than 96 then the epoch is 1952
|
|
(this covers 1952-1962 and 1996-2047). Otherwise, the epoch
|
|
is 1900 (this covers 1996-1999, or rather 1996-2155). */
|
|
|
|
|
|
/* See whether we are dealing with SRM or MILO, as they have
|
|
different "epoch" ideas. */
|
|
if (is_in_cpuinfo("system serial number", "MILO")) {
|
|
ARCconsole = 1;
|
|
if (debug) printf (_("booted from MILO\n"));
|
|
}
|
|
|
|
/* See whether we are dealing with a RUFFIAN aka Alpha PC-164 UX (or BX),
|
|
as they have REALLY different TOY (TimeOfYear) format: BCD, and not
|
|
an ARC-style epoch.
|
|
BCD is detected dynamically, but we must NOT adjust like ARC. */
|
|
if (ARCconsole && is_in_cpuinfo("system type", "Ruffian")) {
|
|
ARCconsole = 0;
|
|
if (debug) printf (_("Ruffian BCD clock\n"));
|
|
}
|
|
|
|
if (ARCconsole)
|
|
cmos_epoch = 1980;
|
|
}
|
|
|
|
void
|
|
set_cmos_access(int Jensen, int funky_toy) {
|
|
|
|
/* See whether we're dealing with a Jensen---it has a weird I/O
|
|
system. DEC was just learning how to build Alpha PCs. */
|
|
if (Jensen || is_in_cpuinfo("system type", "Jensen")) {
|
|
use_dev_port = 1;
|
|
clock_ctl_addr = 0x170;
|
|
clock_data_addr = 0x171;
|
|
if (debug) printf (_("clockport adjusted to 0x%x\n"), clock_ctl_addr);
|
|
}
|
|
|
|
/* see whether we are dealing with PC164/LX164/SX164, as they have a TOY
|
|
that must be accessed differently to work correctly. */
|
|
if (funky_toy ||
|
|
is_in_cpuinfo("system variation", "PC164") ||
|
|
is_in_cpuinfo("system variation", "LX164") ||
|
|
is_in_cpuinfo("system variation", "SX164")) {
|
|
funkyTOY = 1;
|
|
if (debug) printf (_("funky TOY!\n"));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef __i386__
|
|
|
|
/*
|
|
* Try to do CMOS access atomically, so that no other processes
|
|
* can get a time slice while we are reading or setting the clock.
|
|
* (Also, if the kernel time is synchronized with an external source,
|
|
* the kernel itself will fiddle with the RTC every 11 minutes.)
|
|
*/
|
|
|
|
static unsigned long
|
|
atomic(const char *name, unsigned long (*op)(unsigned long),
|
|
unsigned long arg)
|
|
{
|
|
unsigned long v;
|
|
__asm__ volatile ("cli");
|
|
v = (*op)(arg);
|
|
__asm__ volatile ("sti");
|
|
return v;
|
|
}
|
|
|
|
#elif __alpha__
|
|
|
|
/*
|
|
* The Alpha doesn't allow user-level code to disable interrupts (for
|
|
* good reasons). Instead, we ensure atomic operation by performing
|
|
* the operation and checking whether the high 32 bits of the cycle
|
|
* counter changed. If they did, a context switch must have occurred
|
|
* and we redo the operation. As long as the operation is reasonably
|
|
* short, it will complete atomically, eventually.
|
|
*/
|
|
|
|
static unsigned long
|
|
atomic(const char *name, unsigned long (*op)(unsigned long),
|
|
unsigned long arg)
|
|
{
|
|
unsigned long ts1, ts2, n, v;
|
|
|
|
for (n = 0; n < 1000; ++n) {
|
|
asm volatile ("rpcc %0" : "r="(ts1));
|
|
v = (*op)(arg);
|
|
asm volatile ("rpcc %0" : "r="(ts2));
|
|
|
|
if ((ts1 ^ ts2) >> 32 == 0) {
|
|
return v;
|
|
}
|
|
}
|
|
fprintf(stderr, _("%s: atomic %s failed for 1000 iterations!"), progname, name);
|
|
exit(1);
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
* Hmmh, this isn't very atomic. Maybe we should force an error
|
|
* instead?
|
|
*/
|
|
static unsigned long
|
|
atomic(const char *name, unsigned long (*op)(unsigned long),
|
|
unsigned long arg)
|
|
{
|
|
return (*op)(arg);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static inline
|
|
unsigned long cmos_read(unsigned long reg)
|
|
{
|
|
if (use_dev_port) {
|
|
unsigned char v = reg | 0x80;
|
|
lseek(dev_port_fd, clock_ctl_addr, 0);
|
|
write(dev_port_fd, &v, 1);
|
|
lseek(dev_port_fd, clock_data_addr, 0);
|
|
read(dev_port_fd, &v, 1);
|
|
return v;
|
|
} else {
|
|
/* We only want to read CMOS data, but unfortunately
|
|
writing to bit 7 disables (1) or enables (0) NMI;
|
|
since this bit is read-only we have to guess the old status.
|
|
Various docs suggest that one should disable NMI while
|
|
reading/writing CMOS data, and enable it again afterwards.
|
|
This would yield the sequence
|
|
outb (reg | 0x80, 0x70);
|
|
val = inb(0x71);
|
|
outb (0x0d, 0x70); // 0x0d: random read-only location
|
|
Other docs state that "any write to 0x70 should be followed
|
|
by an action to 0x71 or the RTC wil be left in an unknown state".
|
|
Most docs say that it doesnt matter at all what one does.
|
|
*/
|
|
/* bit 0x80: disable NMI while reading - should we?
|
|
Let us follow the kernel and not disable.
|
|
Called only with 0 <= reg < 128 */
|
|
outb (reg, clock_ctl_addr);
|
|
return inb (clock_data_addr);
|
|
}
|
|
}
|
|
|
|
static inline
|
|
unsigned long cmos_write(unsigned long reg, unsigned long val)
|
|
{
|
|
if (use_dev_port) {
|
|
unsigned char v = reg | 0x80;
|
|
lseek(dev_port_fd, clock_ctl_addr, 0);
|
|
write(dev_port_fd, &v, 1);
|
|
v = (val & 0xff);
|
|
lseek(dev_port_fd, clock_data_addr, 0);
|
|
write(dev_port_fd, &v, 1);
|
|
} else {
|
|
outb (reg, clock_ctl_addr);
|
|
outb (val, clock_data_addr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned long cmos_set_time(unsigned long arg)
|
|
{
|
|
unsigned char save_control, save_freq_select, pmbit = 0;
|
|
struct tm tm = *(struct tm *) arg;
|
|
unsigned int century;
|
|
|
|
/*
|
|
* CMOS byte 10 (clock status register A) has 3 bitfields:
|
|
* bit 7: 1 if data invalid, update in progress (read-only bit)
|
|
* (this is raised 224 us before the actual update starts)
|
|
* 6-4 select base frequency
|
|
* 010: 32768 Hz time base (default)
|
|
* 111: reset
|
|
* all other combinations are manufacturer-dependent
|
|
* (e.g.: DS1287: 010 = start oscillator, anything else = stop)
|
|
* 3-0 rate selection bits for interrupt
|
|
* 0000 none (may stop RTC)
|
|
* 0001, 0010 give same frequency as 1000, 1001
|
|
* 0011 122 microseconds (minimum, 8192 Hz)
|
|
* .... each increase by 1 halves the frequency, doubles the period
|
|
* 1111 500 milliseconds (maximum, 2 Hz)
|
|
* 0110 976.562 microseconds (default 1024 Hz)
|
|
*/
|
|
|
|
save_control = cmos_read (11); /* tell the clock it's being set */
|
|
cmos_write (11, (save_control | 0x80));
|
|
save_freq_select = cmos_read (10); /* stop and reset prescaler */
|
|
cmos_write (10, (save_freq_select | 0x70));
|
|
|
|
tm.tm_year += TM_EPOCH;
|
|
century = tm.tm_year/100;
|
|
tm.tm_year -= cmos_epoch;
|
|
tm.tm_year %= 100;
|
|
tm.tm_mon += 1;
|
|
tm.tm_wday += 1;
|
|
|
|
if (!(save_control & 0x02)) { /* 12hr mode; the default is 24hr mode */
|
|
if (tm.tm_hour == 0)
|
|
tm.tm_hour = 24;
|
|
if (tm.tm_hour > 12) {
|
|
tm.tm_hour -= 12;
|
|
pmbit = 0x80;
|
|
}
|
|
}
|
|
|
|
if (!(save_control & 0x04)) { /* BCD mode - the default */
|
|
BIN_TO_BCD(tm.tm_sec);
|
|
BIN_TO_BCD(tm.tm_min);
|
|
BIN_TO_BCD(tm.tm_hour);
|
|
BIN_TO_BCD(tm.tm_wday);
|
|
BIN_TO_BCD(tm.tm_mday);
|
|
BIN_TO_BCD(tm.tm_mon);
|
|
BIN_TO_BCD(tm.tm_year);
|
|
BIN_TO_BCD(century);
|
|
}
|
|
|
|
cmos_write (0, tm.tm_sec);
|
|
cmos_write (2, tm.tm_min);
|
|
cmos_write (4, tm.tm_hour | pmbit);
|
|
cmos_write (6, tm.tm_wday);
|
|
cmos_write (7, tm.tm_mday);
|
|
cmos_write (8, tm.tm_mon);
|
|
cmos_write (9, tm.tm_year);
|
|
if (century_byte)
|
|
cmos_write (century_byte, century);
|
|
|
|
|
|
/* The kernel sources, linux/arch/i386/kernel/time.c, have the
|
|
following comment:
|
|
|
|
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_write (11, save_control);
|
|
cmos_write (10, save_freq_select);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hclock_read(unsigned long reg) {
|
|
return atomic("clock read", cmos_read, (reg));
|
|
}
|
|
|
|
static void
|
|
hclock_set_time(const struct tm *tm) {
|
|
atomic("set time", cmos_set_time, (unsigned long)(tm));
|
|
}
|
|
|
|
static inline int
|
|
cmos_clock_busy(void) {
|
|
return
|
|
#ifdef __alpha__
|
|
/* poll bit 4 (UF) of Control Register C */
|
|
funkyTOY ? (hclock_read(12) & 0x10) :
|
|
#endif
|
|
/* poll bit 7 (UIP) of Control Register A */
|
|
(hclock_read(10) & 0x80);
|
|
}
|
|
|
|
|
|
static int
|
|
synchronize_to_clock_tick_cmos(void) {
|
|
int i;
|
|
|
|
/* Wait for rise. Should be within a second, but in case something
|
|
weird happens, we have a limit on this loop to reduce the impact
|
|
of this failure.
|
|
*/
|
|
for (i = 0; !cmos_clock_busy(); i++)
|
|
if (i >= 10000000)
|
|
return 1;
|
|
|
|
/* Wait for fall. Should be within 2.228 ms. */
|
|
for (i = 0; cmos_clock_busy(); i++)
|
|
if (i >= 1000000)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
read_hardware_clock_cmos(struct tm *tm) {
|
|
/*----------------------------------------------------------------------------
|
|
Read the hardware clock and return the current time via <tm> argument.
|
|
Assume we have an ISA machine and read the clock directly with CPU I/O
|
|
instructions.
|
|
|
|
This function is not totally reliable. It takes a finite and
|
|
unpredictable amount of time to execute the code below. During that
|
|
time, the clock may change and we may even read an invalid value in
|
|
the middle of an update. We do a few checks to minimize this
|
|
possibility, but only the kernel can actually read the clock
|
|
properly, since it can execute code in a short and predictable
|
|
amount of time (by turning of interrupts).
|
|
|
|
In practice, the chance of this function returning the wrong time is
|
|
extremely remote.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
bool got_time = FALSE;
|
|
unsigned char status, pmbit;
|
|
|
|
status = pmbit = 0; /* just for gcc */
|
|
|
|
while (!got_time) {
|
|
/* Bit 7 of Byte 10 of the Hardware Clock value is the Update In Progress
|
|
(UIP) bit, which is on while and 244 uS before the Hardware Clock
|
|
updates itself. It updates the counters individually, so reading
|
|
them during an update would produce garbage. The update takes 2mS,
|
|
so we could be spinning here that long waiting for this bit to turn
|
|
off.
|
|
|
|
Furthermore, it is pathologically possible for us to be in this
|
|
code so long that even if the UIP bit is not on at first, the
|
|
clock has changed while we were running. We check for that too,
|
|
and if it happens, we start over.
|
|
*/
|
|
|
|
if (!cmos_clock_busy()) {
|
|
/* No clock update in progress, go ahead and read */
|
|
tm->tm_sec = hclock_read(0);
|
|
tm->tm_min = hclock_read(2);
|
|
tm->tm_hour = hclock_read(4);
|
|
tm->tm_wday = hclock_read(6);
|
|
tm->tm_mday = hclock_read(7);
|
|
tm->tm_mon = hclock_read(8);
|
|
tm->tm_year = hclock_read(9);
|
|
status = hclock_read(11);
|
|
#if 0
|
|
if (century_byte)
|
|
century = hclock_read(century_byte);
|
|
#endif
|
|
|
|
/* Unless the clock changed while we were reading, consider this
|
|
a good clock read .
|
|
*/
|
|
if (tm->tm_sec == hclock_read (0))
|
|
got_time = TRUE;
|
|
}
|
|
/* Yes, in theory we could have been running for 60 seconds and
|
|
the above test wouldn't work!
|
|
*/
|
|
}
|
|
|
|
if (!(status & 0x04)) { /* BCD mode - the default */
|
|
BCD_TO_BIN(tm->tm_sec);
|
|
BCD_TO_BIN(tm->tm_min);
|
|
pmbit = (tm->tm_hour & 0x80);
|
|
tm->tm_hour &= 0x7f;
|
|
BCD_TO_BIN(tm->tm_hour);
|
|
BCD_TO_BIN(tm->tm_wday);
|
|
BCD_TO_BIN(tm->tm_mday);
|
|
BCD_TO_BIN(tm->tm_mon);
|
|
BCD_TO_BIN(tm->tm_year);
|
|
#if 0
|
|
BCD_TO_BIN(century);
|
|
#endif
|
|
}
|
|
|
|
/* We don't use the century byte of the Hardware Clock
|
|
since we don't know its address (usually 50 or 55).
|
|
Here, we follow the advice of the X/Open Base Working Group:
|
|
"if century is not specified, then values in the range [69-99]
|
|
refer to years in the twentieth century (1969 to 1999 inclusive),
|
|
and values in the range [00-68] refer to years in the twenty-first
|
|
century (2000 to 2068 inclusive)."
|
|
*/
|
|
|
|
tm->tm_wday -= 1;
|
|
tm->tm_mon -= 1;
|
|
tm->tm_year += (cmos_epoch - TM_EPOCH);
|
|
if (tm->tm_year < 69)
|
|
tm->tm_year += 100;
|
|
if (pmbit) {
|
|
tm->tm_hour += 12;
|
|
if (tm->tm_hour == 24)
|
|
tm->tm_hour = 0;
|
|
}
|
|
|
|
tm->tm_isdst = -1; /* don't know whether it's daylight */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
set_hardware_clock_cmos(const struct tm *new_broken_time) {
|
|
|
|
hclock_set_time(new_broken_time);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
i386_iopl(const int level) {
|
|
#if defined(__i386__) || defined(__alpha__)
|
|
extern int iopl(const int level);
|
|
return iopl(level);
|
|
#else
|
|
return -2;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
get_permissions_cmos(void) {
|
|
int rc;
|
|
|
|
if (use_dev_port) {
|
|
if ((dev_port_fd = open("/dev/port", O_RDWR)) < 0) {
|
|
int errsv = errno;
|
|
fprintf(stderr, _("Cannot open /dev/port: %s"), strerror(errsv));
|
|
rc = 1;
|
|
} else
|
|
rc = 0;
|
|
} else {
|
|
rc = i386_iopl(3);
|
|
if (rc == -2) {
|
|
fprintf(stderr, _("I failed to get permission because I didnt try.\n"));
|
|
} else if (rc != 0) {
|
|
rc = errno;
|
|
fprintf(stderr, _("%s is unable to get I/O port access: "
|
|
"the iopl(3) call failed.\n"), progname);
|
|
if(rc == EPERM && geteuid())
|
|
fprintf(stderr, _("Probably you need root privileges.\n"));
|
|
}
|
|
}
|
|
return rc ? 1 : 0;
|
|
}
|
|
|
|
static struct clock_ops cmos = {
|
|
"direct I/O instructions to ISA clock",
|
|
get_permissions_cmos,
|
|
read_hardware_clock_cmos,
|
|
set_hardware_clock_cmos,
|
|
synchronize_to_clock_tick_cmos,
|
|
};
|
|
|
|
|
|
/* return &cmos if cmos clock present, NULL otherwise */
|
|
/* choose this construction to avoid gcc messages about unused variables */
|
|
|
|
struct clock_ops *
|
|
probe_for_cmos_clock(void){
|
|
int have_cmos =
|
|
#if defined(__i386__) || defined(__alpha__)
|
|
TRUE;
|
|
#else
|
|
FALSE;
|
|
#endif
|
|
return have_cmos ? &cmos : NULL;
|
|
}
|