readclock: add support for am335x RTC
Add support for getting/setting the am335x SoC's internal real time clock. Also, allow the power off alarm to be set. Make readclock an "always on" driver. This is needed for setting power-off alarms whenever the power button is pressed on the BBB. Replace the readclock.sh script & single run driver with a readclock program that takes the same arguments and forwards the requests on to the always up readclock driver. Change-Id: Ifd6c2acd80ae4b5e79d83df510df445c24e24a71
This commit is contained in:
parent
c4f3c7d66f
commit
09db2a8c67
17 changed files with 1261 additions and 354 deletions
|
@ -1,5 +1,8 @@
|
||||||
SCRIPTS= readclock.sh
|
PROG= readclock
|
||||||
|
SRCS= readclock.c
|
||||||
BINDIR= /bin
|
BINDIR= /bin
|
||||||
|
|
||||||
|
# no man page here, it's handled in ../man/man8/
|
||||||
MAN=
|
MAN=
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
.include <bsd.prog.mk>
|
||||||
|
|
191
commands/readclock/readclock.c
Normal file
191
commands/readclock/readclock.c
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
/* frontend to the readclock.drv driver for getting/setting hw clock. */
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <minix/type.h>
|
||||||
|
#include <minix/const.h>
|
||||||
|
#include <minix/syslib.h>
|
||||||
|
#include <minix/sysutil.h>
|
||||||
|
#include <minix/com.h>
|
||||||
|
#include <minix/rs.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
|
||||||
|
int nflag = 0; /* Tell what, but don't do it. */
|
||||||
|
int wflag = 0; /* Set the CMOS clock. */
|
||||||
|
int Wflag = 0; /* Also set the CMOS clock register bits. */
|
||||||
|
int y2kflag = 0; /* Interpret 1980 as 2000 for clock with Y2K bug. */
|
||||||
|
|
||||||
|
void errmsg(char *s);
|
||||||
|
void get_time(struct tm *t);
|
||||||
|
void set_time(struct tm *t);
|
||||||
|
void usage(void);
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct tm time1;
|
||||||
|
struct tm time2;
|
||||||
|
struct tm tmnow;
|
||||||
|
char date[64];
|
||||||
|
time_t now, rtc;
|
||||||
|
int i, s;
|
||||||
|
|
||||||
|
/* Process options. */
|
||||||
|
while (argc > 1) {
|
||||||
|
char *p = *++argv;
|
||||||
|
|
||||||
|
if (*p++ != '-')
|
||||||
|
usage();
|
||||||
|
|
||||||
|
while (*p != 0) {
|
||||||
|
switch (*p++) {
|
||||||
|
case 'n':
|
||||||
|
nflag = 1;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
wflag = 1;
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
Wflag = 1;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
y2kflag = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc--;
|
||||||
|
}
|
||||||
|
if (Wflag)
|
||||||
|
wflag = 1; /* -W implies -w */
|
||||||
|
|
||||||
|
/* Read the CMOS real time clock. */
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
get_time(&time1);
|
||||||
|
now = time(NULL);
|
||||||
|
|
||||||
|
time1.tm_isdst = -1; /* Do timezone calculations. */
|
||||||
|
time2 = time1;
|
||||||
|
|
||||||
|
rtc = mktime(&time1); /* Transform to a time_t. */
|
||||||
|
if (rtc != -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
printf
|
||||||
|
("readclock: Invalid time read from CMOS RTC: %d-%02d-%02d %02d:%02d:%02d\n",
|
||||||
|
time2.tm_year + 1900, time2.tm_mon + 1, time2.tm_mday,
|
||||||
|
time2.tm_hour, time2.tm_min, time2.tm_sec);
|
||||||
|
sleep(5);
|
||||||
|
}
|
||||||
|
if (i == 10)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
if (!wflag) {
|
||||||
|
/* Set system time. */
|
||||||
|
if (nflag) {
|
||||||
|
printf("stime(%lu)\n", (unsigned long) rtc);
|
||||||
|
} else {
|
||||||
|
if (stime(&rtc) < 0) {
|
||||||
|
errmsg("Not allowed to set time.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmnow = *localtime(&rtc);
|
||||||
|
if (strftime(date, sizeof(date),
|
||||||
|
"%a %b %d %H:%M:%S %Z %Y", &tmnow) != 0) {
|
||||||
|
if (date[8] == '0')
|
||||||
|
date[8] = ' ';
|
||||||
|
printf("%s\n", date);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Set the CMOS clock to the system time. */
|
||||||
|
tmnow = *localtime(&now);
|
||||||
|
if (nflag) {
|
||||||
|
printf("%04d-%02d-%02d %02d:%02d:%02d\n",
|
||||||
|
tmnow.tm_year + 1900,
|
||||||
|
tmnow.tm_mon + 1,
|
||||||
|
tmnow.tm_mday,
|
||||||
|
tmnow.tm_hour, tmnow.tm_min, tmnow.tm_sec);
|
||||||
|
} else {
|
||||||
|
set_time(&tmnow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
errmsg(char *s)
|
||||||
|
{
|
||||||
|
static char *prompt = "readclock: ";
|
||||||
|
|
||||||
|
printf("%s%s\n", prompt, s);
|
||||||
|
prompt = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
get_time(struct tm *t)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
message m;
|
||||||
|
endpoint_t ep;
|
||||||
|
|
||||||
|
r = minix_rs_lookup("readclock.drv", &ep);
|
||||||
|
if (r != 0) {
|
||||||
|
errmsg("Couldn't locate readclock.drv\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m.RTCDEV_TM = t;
|
||||||
|
m.RTCDEV_FLAGS = (y2kflag) ? RTCDEV_Y2KBUG : RTCDEV_NOFLAGS;
|
||||||
|
|
||||||
|
r = _syscall(ep, RTCDEV_GET_TIME, &m);
|
||||||
|
if (r != RTCDEV_REPLY || m.RTCDEV_STATUS != 0) {
|
||||||
|
errmsg("Call to readclock.drv failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_time(struct tm *t)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
message m;
|
||||||
|
endpoint_t ep;
|
||||||
|
|
||||||
|
r = minix_rs_lookup("readclock.drv", &ep);
|
||||||
|
if (r != 0) {
|
||||||
|
errmsg("Couldn't locate readclock.drv\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m.RTCDEV_TM = t;
|
||||||
|
m.RTCDEV_FLAGS = RTCDEV_NOFLAGS;
|
||||||
|
|
||||||
|
if (y2kflag) {
|
||||||
|
m.RTCDEV_FLAGS |= RTCDEV_Y2KBUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Wflag) {
|
||||||
|
m.RTCDEV_FLAGS |= RTCDEV_CMOSREG;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = _syscall(ep, RTCDEV_SET_TIME, &m);
|
||||||
|
if (r != RTCDEV_REPLY || m.RTCDEV_STATUS != 0) {
|
||||||
|
errmsg("Call to readclock.drv failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
printf("Usage: readclock [-nwW2]\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
if [ $# -gt 0 ]
|
|
||||||
then ARGS="-args $@"
|
|
||||||
fi
|
|
||||||
/bin/service up /sbin/readclock.drv -period 5HZ -script /etc/rs.single $ARGS
|
|
|
@ -23,7 +23,6 @@
|
||||||
./sbin/at_wini minix-sys
|
./sbin/at_wini minix-sys
|
||||||
./sbin/floppy minix-sys
|
./sbin/floppy minix-sys
|
||||||
./sbin/hgfs minix-sys
|
./sbin/hgfs minix-sys
|
||||||
./sbin/readclock.drv minix-sys
|
|
||||||
./sbin/vbfs minix-sys
|
./sbin/vbfs minix-sys
|
||||||
./sbin/virtio_blk minix-sys
|
./sbin/virtio_blk minix-sys
|
||||||
./usr/bin/atnormalize minix-sys
|
./usr/bin/atnormalize minix-sys
|
||||||
|
|
|
@ -161,6 +161,7 @@
|
||||||
./sbin/newfs_ext2 minix-sys
|
./sbin/newfs_ext2 minix-sys
|
||||||
./sbin/newfs_ext2fs minix-sys
|
./sbin/newfs_ext2fs minix-sys
|
||||||
./sbin/procfs minix-sys
|
./sbin/procfs minix-sys
|
||||||
|
./sbin/readclock.drv minix-sys
|
||||||
./sys minix-sys
|
./sys minix-sys
|
||||||
./tmp minix-sys
|
./tmp minix-sys
|
||||||
./usr minix-sys
|
./usr minix-sys
|
||||||
|
|
|
@ -23,7 +23,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "earm"
|
.if ${MACHINE_ARCH} == "earm"
|
||||||
SUBDIR= cat24c256 fb gpio i2c mmc log tda19988 tty random lan8710a
|
SUBDIR= cat24c256 fb gpio i2c mmc log readclock tda19988 tty random lan8710a
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
.endif # ${MKIMAGEONLY} != "yes"
|
.endif # ${MKIMAGEONLY} != "yes"
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
# Makefile for readclock 'driver'
|
# Makefile for readclock 'driver'
|
||||||
PROG= readclock.drv
|
PROG= readclock.drv
|
||||||
SRCS= readclock.c
|
|
||||||
|
|
||||||
DPADD+= ${LIBSYS}
|
.include "arch/${MACHINE_ARCH}/Makefile.inc"
|
||||||
LDADD+= -lsys
|
|
||||||
|
SRCS+= readclock.c
|
||||||
|
|
||||||
|
DPADD+= ${LIBSYS} ${LIBTIMERS}
|
||||||
|
LDADD+= -lsys -ltimers
|
||||||
|
|
||||||
MAN=
|
MAN=
|
||||||
|
|
||||||
BINDIR?= /sbin
|
BINDIR?= /sbin
|
||||||
|
|
||||||
CPPFLAGS+= -D_SYSTEM=1
|
CPPFLAGS+= -D_SYSTEM=1 -I${.CURDIR}
|
||||||
|
|
||||||
.include <minix.service.mk>
|
.include <minix.service.mk>
|
||||||
|
|
10
drivers/readclock/arch/earm/Makefile.inc
Normal file
10
drivers/readclock/arch/earm/Makefile.inc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Makefile for arch-dependent readclock code
|
||||||
|
.include <bsd.own.mk>
|
||||||
|
|
||||||
|
HERE=${.CURDIR}/arch/${MACHINE_ARCH}
|
||||||
|
.PATH: ${HERE}
|
||||||
|
|
||||||
|
SRCS += arch_readclock.c omap_rtc.h
|
||||||
|
|
||||||
|
DPADD+= ${CLKCONF}
|
||||||
|
LDADD+= -lclkconf
|
429
drivers/readclock/arch/earm/arch_readclock.c
Normal file
429
drivers/readclock/arch/earm/arch_readclock.c
Normal file
|
@ -0,0 +1,429 @@
|
||||||
|
#include <minix/syslib.h>
|
||||||
|
#include <minix/drvlib.h>
|
||||||
|
#include <minix/log.h>
|
||||||
|
#include <minix/mmio.h>
|
||||||
|
#include <minix/clkconf.h>
|
||||||
|
#include <minix/sysutil.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "omap_rtc.h"
|
||||||
|
#include "readclock.h"
|
||||||
|
|
||||||
|
/* defines the set of register */
|
||||||
|
|
||||||
|
typedef struct omap_rtc_registers
|
||||||
|
{
|
||||||
|
vir_bytes RTC_SS_SECONDS_REG;
|
||||||
|
vir_bytes RTC_SS_MINUTES_REG;
|
||||||
|
vir_bytes RTC_SS_HOURS_REG;
|
||||||
|
vir_bytes RTC_SS_DAYS_REG;
|
||||||
|
vir_bytes RTC_SS_MONTHS_REG;
|
||||||
|
vir_bytes RTC_SS_YEARS_REG;
|
||||||
|
vir_bytes RTC_SS_WEEKS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM_SECONDS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM_MINUTES_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM_HOURS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM_DAYS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM_MONTHS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM_YEARS_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_CTRL_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_STATUS_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_INTERRUPTS_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_COMP_LSB_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_COMP_MSB_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_OSC_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_SCRATCH0_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_SCRATCH1_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_SCRATCH2_REG;
|
||||||
|
vir_bytes RTC_SS_KICK0R;
|
||||||
|
vir_bytes RTC_SS_KICK1R;
|
||||||
|
vir_bytes RTC_SS_RTC_REVISION;
|
||||||
|
vir_bytes RTC_SS_RTC_SYSCONFIG;
|
||||||
|
vir_bytes RTC_SS_RTC_IRQWAKEEN;
|
||||||
|
vir_bytes RTC_SS_ALARM2_SECONDS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM2_MINUTES_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM2_HOURS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM2_DAYS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM2_MONTHS_REG;
|
||||||
|
vir_bytes RTC_SS_ALARM2_YEARS_REG;
|
||||||
|
vir_bytes RTC_SS_RTC_PMIC;
|
||||||
|
vir_bytes RTC_SS_RTC_DEBOUNCE;
|
||||||
|
} omap_rtc_registers_t;
|
||||||
|
|
||||||
|
typedef struct omap_rtc_clock
|
||||||
|
{
|
||||||
|
enum rtc_clock_type
|
||||||
|
{ am335x } clock_type;
|
||||||
|
phys_bytes mr_base;
|
||||||
|
phys_bytes mr_size;
|
||||||
|
vir_bytes mapped_addr;
|
||||||
|
omap_rtc_registers_t *regs;
|
||||||
|
} omap_rtc_clock_t;
|
||||||
|
|
||||||
|
/* Define the registers for each chip */
|
||||||
|
|
||||||
|
static omap_rtc_registers_t am335x_rtc_regs = {
|
||||||
|
.RTC_SS_SECONDS_REG = AM335X_RTC_SS_SECONDS_REG,
|
||||||
|
.RTC_SS_MINUTES_REG = AM335X_RTC_SS_MINUTES_REG,
|
||||||
|
.RTC_SS_HOURS_REG = AM335X_RTC_SS_HOURS_REG,
|
||||||
|
.RTC_SS_DAYS_REG = AM335X_RTC_SS_DAYS_REG,
|
||||||
|
.RTC_SS_MONTHS_REG = AM335X_RTC_SS_MONTHS_REG,
|
||||||
|
.RTC_SS_YEARS_REG = AM335X_RTC_SS_YEARS_REG,
|
||||||
|
.RTC_SS_WEEKS_REG = AM335X_RTC_SS_WEEKS_REG,
|
||||||
|
.RTC_SS_ALARM_SECONDS_REG = AM335X_RTC_SS_ALARM_SECONDS_REG,
|
||||||
|
.RTC_SS_ALARM_MINUTES_REG = AM335X_RTC_SS_ALARM_MINUTES_REG,
|
||||||
|
.RTC_SS_ALARM_HOURS_REG = AM335X_RTC_SS_ALARM_HOURS_REG,
|
||||||
|
.RTC_SS_ALARM_DAYS_REG = AM335X_RTC_SS_ALARM_DAYS_REG,
|
||||||
|
.RTC_SS_ALARM_MONTHS_REG = AM335X_RTC_SS_ALARM_MONTHS_REG,
|
||||||
|
.RTC_SS_ALARM_YEARS_REG = AM335X_RTC_SS_ALARM_YEARS_REG,
|
||||||
|
.RTC_SS_RTC_CTRL_REG = AM335X_RTC_SS_RTC_CTRL_REG,
|
||||||
|
.RTC_SS_RTC_STATUS_REG = AM335X_RTC_SS_RTC_STATUS_REG,
|
||||||
|
.RTC_SS_RTC_INTERRUPTS_REG = AM335X_RTC_SS_RTC_INTERRUPTS_REG,
|
||||||
|
.RTC_SS_RTC_COMP_LSB_REG = AM335X_RTC_SS_RTC_COMP_LSB_REG,
|
||||||
|
.RTC_SS_RTC_COMP_MSB_REG = AM335X_RTC_SS_RTC_COMP_MSB_REG,
|
||||||
|
.RTC_SS_RTC_OSC_REG = AM335X_RTC_SS_RTC_OSC_REG,
|
||||||
|
.RTC_SS_RTC_SCRATCH0_REG = AM335X_RTC_SS_RTC_SCRATCH0_REG,
|
||||||
|
.RTC_SS_RTC_SCRATCH1_REG = AM335X_RTC_SS_RTC_SCRATCH1_REG,
|
||||||
|
.RTC_SS_RTC_SCRATCH2_REG = AM335X_RTC_SS_RTC_SCRATCH2_REG,
|
||||||
|
.RTC_SS_KICK0R = AM335X_RTC_SS_KICK0R,
|
||||||
|
.RTC_SS_KICK1R = AM335X_RTC_SS_KICK1R,
|
||||||
|
.RTC_SS_RTC_REVISION = AM335X_RTC_SS_RTC_REVISION,
|
||||||
|
.RTC_SS_RTC_SYSCONFIG = AM335X_RTC_SS_RTC_SYSCONFIG,
|
||||||
|
.RTC_SS_RTC_IRQWAKEEN = AM335X_RTC_SS_RTC_IRQWAKEEN,
|
||||||
|
.RTC_SS_ALARM2_SECONDS_REG = AM335X_RTC_SS_ALARM2_SECONDS_REG,
|
||||||
|
.RTC_SS_ALARM2_MINUTES_REG = AM335X_RTC_SS_ALARM2_MINUTES_REG,
|
||||||
|
.RTC_SS_ALARM2_HOURS_REG = AM335X_RTC_SS_ALARM2_HOURS_REG,
|
||||||
|
.RTC_SS_ALARM2_DAYS_REG = AM335X_RTC_SS_ALARM2_DAYS_REG,
|
||||||
|
.RTC_SS_ALARM2_MONTHS_REG = AM335X_RTC_SS_ALARM2_MONTHS_REG,
|
||||||
|
.RTC_SS_ALARM2_YEARS_REG = AM335X_RTC_SS_ALARM2_YEARS_REG,
|
||||||
|
.RTC_SS_RTC_PMIC = AM335X_RTC_SS_RTC_PMIC,
|
||||||
|
.RTC_SS_RTC_DEBOUNCE = AM335X_RTC_SS_RTC_DEBOUNCE
|
||||||
|
};
|
||||||
|
|
||||||
|
static omap_rtc_clock_t rtc = {
|
||||||
|
am335x, AM335X_RTC_SS_BASE, AM335X_RTC_SS_SIZE, 0, &am335x_rtc_regs
|
||||||
|
};
|
||||||
|
|
||||||
|
/* used for logging */
|
||||||
|
static struct log log = {
|
||||||
|
.name = "omap_rtc",
|
||||||
|
.log_level = LEVEL_INFO,
|
||||||
|
.log_func = default_log
|
||||||
|
};
|
||||||
|
|
||||||
|
static u32_t use_count = 0;
|
||||||
|
static u32_t pwr_off_in_progress = 0;
|
||||||
|
|
||||||
|
static void omap_rtc_unlock(void);
|
||||||
|
static void omap_rtc_lock(void);
|
||||||
|
static int omap_rtc_clkconf(void);
|
||||||
|
|
||||||
|
/* Helper Functions for Register Access */
|
||||||
|
#define reg_read(a) (*(volatile uint32_t *)(rtc.mapped_addr + a))
|
||||||
|
#define reg_write(a,v) (*(volatile uint32_t *)(rtc.mapped_addr + a) = (v))
|
||||||
|
#define reg_set_bit(a,v) reg_write((a), reg_read((a)) | (1<<v))
|
||||||
|
#define reg_clear_bit(a,v) reg_write((a), reg_read((a)) & ~(1<<v))
|
||||||
|
#define RTC_IS_BUSY (reg_read(rtc.regs->RTC_SS_RTC_STATUS_REG) & (1<<RTC_BUSY_BIT))
|
||||||
|
|
||||||
|
/* When the RTC is running, writes should not happen when the RTC is busy.
|
||||||
|
* This macro waits until the RTC is free before doing the write.
|
||||||
|
*/
|
||||||
|
#define safe_reg_write(a,v) do { while (RTC_IS_BUSY) {micro_delay(1);} reg_write((a),(v)); } while (0)
|
||||||
|
#define safe_reg_set_bit(a,v) safe_reg_write((a), reg_read((a)) | (1<<v))
|
||||||
|
#define safe_reg_clear_bit(a,v) safe_reg_write((a), reg_read((a)) & ~(1<<v))
|
||||||
|
|
||||||
|
static void
|
||||||
|
omap_rtc_unlock(void)
|
||||||
|
{
|
||||||
|
/* Specific bit patterns need to be written to specific registers in a
|
||||||
|
* specific order to enable writing to RTC_SS registers.
|
||||||
|
*/
|
||||||
|
reg_write(rtc.regs->RTC_SS_KICK0R, AM335X_RTC_SS_KICK0R_UNLOCK_MASK);
|
||||||
|
reg_write(rtc.regs->RTC_SS_KICK1R, AM335X_RTC_SS_KICK1R_UNLOCK_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
omap_rtc_lock(void)
|
||||||
|
{
|
||||||
|
/* Write garbage to the KICK registers to enable write protect. */
|
||||||
|
reg_write(rtc.regs->RTC_SS_KICK0R, AM335X_RTC_SS_KICK0R_LOCK_MASK);
|
||||||
|
reg_write(rtc.regs->RTC_SS_KICK1R, AM335X_RTC_SS_KICK1R_LOCK_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
omap_rtc_clkconf(void)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Configure the clocks need to run the RTC */
|
||||||
|
r = clkconf_init();
|
||||||
|
if (r != OK) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = clkconf_set(CM_RTC_RTC_CLKCTRL, 0xffffffff,
|
||||||
|
CM_RTC_RTC_CLKCTRL_MASK);
|
||||||
|
if (r != OK) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = clkconf_set(CM_RTC_CLKSTCTRL, 0xffffffff, CM_RTC_CLKSTCTRL_MASK);
|
||||||
|
if (r != OK) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = clkconf_release();
|
||||||
|
if (r != OK) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_init(void)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
int rtc_rev, major, minor;
|
||||||
|
struct minix_mem_range mr;
|
||||||
|
|
||||||
|
#ifndef AM335X
|
||||||
|
/* Only the am335x (BeagleBone & BeagleBone Black) is supported ATM.
|
||||||
|
* The dm37xx (BeagleBoard-xM) doesn't have a real time clock
|
||||||
|
* built-in. Instead, it uses the RTC on the PMIC. A driver for
|
||||||
|
* the BeagleBoard-xM's PMIC still needs to be developed.
|
||||||
|
*/
|
||||||
|
log_warn(&log, "unsupported processor\n");
|
||||||
|
return ENOSYS;
|
||||||
|
#endif /* !AM335X */
|
||||||
|
|
||||||
|
if (pwr_off_in_progress) return EINVAL;
|
||||||
|
|
||||||
|
use_count++;
|
||||||
|
if (rtc.mapped_addr != 0) {
|
||||||
|
/* already intialized */
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable Clocks */
|
||||||
|
r = omap_rtc_clkconf();
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "Failed to enable clocks for RTC.\n");
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map RTC_SS Registers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Configure memory access */
|
||||||
|
mr.mr_base = rtc.mr_base; /* start addr */
|
||||||
|
mr.mr_limit = mr.mr_base + rtc.mr_size; /* end addr */
|
||||||
|
|
||||||
|
/* ask for privileges to access the RTC_SS memory range */
|
||||||
|
if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) {
|
||||||
|
log_warn(&log,
|
||||||
|
"Unable to obtain RTC memory range privileges.");
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* map the memory into this process */
|
||||||
|
rtc.mapped_addr = (vir_bytes) vm_map_phys(SELF,
|
||||||
|
(void *) rtc.mr_base, rtc.mr_size);
|
||||||
|
if (rtc.mapped_addr == (vir_bytes) MAP_FAILED) {
|
||||||
|
log_warn(&log, "Unable to map RTC registers\n");
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_rev = reg_read(rtc.regs->RTC_SS_RTC_REVISION);
|
||||||
|
major = (rtc_rev & 0x0700) >> 8;
|
||||||
|
minor = (rtc_rev & 0x001f);
|
||||||
|
log_debug(&log, "omap rtc rev %d.%d\n", major, minor);
|
||||||
|
|
||||||
|
/* Disable register write protect */
|
||||||
|
omap_rtc_unlock();
|
||||||
|
|
||||||
|
/* Set NOIDLE */
|
||||||
|
reg_write(rtc.regs->RTC_SS_RTC_SYSCONFIG, (1 << NOIDLE_BIT));
|
||||||
|
|
||||||
|
/* Enable 32kHz clock */
|
||||||
|
reg_set_bit(rtc.regs->RTC_SS_RTC_OSC_REG, EN_32KCLK_BIT);
|
||||||
|
|
||||||
|
/* Setting the stop bit starts the RTC running */
|
||||||
|
reg_set_bit(rtc.regs->RTC_SS_RTC_CTRL_REG, RTC_STOP_BIT);
|
||||||
|
|
||||||
|
/* Re-enable Write Protection */
|
||||||
|
omap_rtc_lock();
|
||||||
|
|
||||||
|
log_debug(&log, "OMAP RTC Initialized\n");
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These are the ranges used by the real time clock and struct tm.
|
||||||
|
*
|
||||||
|
* Field OMAP RTC struct tm
|
||||||
|
* ----- -------- ---------
|
||||||
|
* seconds 0 to 59 (Mask 0x7f) 0 to 59 (60 for leap seconds)
|
||||||
|
* minutes 0 to 59 (Mask 0x7f) 0 to 59
|
||||||
|
* hours 0 to 23 (Mask 0x3f) 0 to 23
|
||||||
|
* day 1 to 31 (Mask 0x3f) 1 to 31
|
||||||
|
* month 1 to 12 (Mask 0x1f) 0 to 11
|
||||||
|
* year last 2 digits of year X + 1900
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_get_time(struct tm *t, int flags)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (pwr_off_in_progress) return EINVAL;
|
||||||
|
|
||||||
|
memset(t, '\0', sizeof(struct tm));
|
||||||
|
|
||||||
|
/* Read and Convert BCD to binary (default RTC mode). */
|
||||||
|
t->tm_sec = bcd_to_dec(reg_read(rtc.regs->RTC_SS_SECONDS_REG) & 0x7f);
|
||||||
|
t->tm_min = bcd_to_dec(reg_read(rtc.regs->RTC_SS_MINUTES_REG) & 0x7f);
|
||||||
|
t->tm_hour = bcd_to_dec(reg_read(rtc.regs->RTC_SS_HOURS_REG) & 0x3f);
|
||||||
|
t->tm_mday = bcd_to_dec(reg_read(rtc.regs->RTC_SS_DAYS_REG) & 0x3f);
|
||||||
|
t->tm_mon = bcd_to_dec(reg_read(rtc.regs->RTC_SS_MONTHS_REG) & 0x1f) - 1;
|
||||||
|
t->tm_year = bcd_to_dec(reg_read(rtc.regs->RTC_SS_YEARS_REG) & 0xff) + 100;
|
||||||
|
|
||||||
|
if (t->tm_year == 100) {
|
||||||
|
/* Cold start - no date/time set - default to 2013-01-01 */
|
||||||
|
t->tm_sec = 0;
|
||||||
|
t->tm_min = 0;
|
||||||
|
t->tm_hour = 0;
|
||||||
|
t->tm_mday = 1;
|
||||||
|
t->tm_mon = 0;
|
||||||
|
t->tm_year = 113;
|
||||||
|
|
||||||
|
arch_set_time(t, RTCDEV_NOFLAGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_set_time(struct tm *t, int flags)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (pwr_off_in_progress) return EINVAL;
|
||||||
|
|
||||||
|
/* Disable Write Protection */
|
||||||
|
omap_rtc_unlock();
|
||||||
|
|
||||||
|
/* Write the date/time to the RTC registers. */
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_SECONDS_REG,
|
||||||
|
(dec_to_bcd(t->tm_sec) & 0x7f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_MINUTES_REG,
|
||||||
|
(dec_to_bcd(t->tm_min) & 0x7f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_HOURS_REG,
|
||||||
|
(dec_to_bcd(t->tm_hour) & 0x3f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_DAYS_REG,
|
||||||
|
(dec_to_bcd(t->tm_mday) & 0x3f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_MONTHS_REG,
|
||||||
|
(dec_to_bcd(t->tm_mon + 1) & 0x1f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_YEARS_REG,
|
||||||
|
(dec_to_bcd(t->tm_year % 100) & 0xff));
|
||||||
|
|
||||||
|
/* Re-enable Write Protection */
|
||||||
|
omap_rtc_lock();
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_pwr_off(void)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct tm t;
|
||||||
|
|
||||||
|
if (pwr_off_in_progress) return EINVAL;
|
||||||
|
|
||||||
|
/* wait until 3 seconds can be added without overflowing tm_sec */
|
||||||
|
do {
|
||||||
|
arch_get_time(&t, RTCDEV_NOFLAGS);
|
||||||
|
micro_delay(250000);
|
||||||
|
} while (t.tm_sec >= 57);
|
||||||
|
|
||||||
|
/* set the alarm for 3 seconds from now */
|
||||||
|
t.tm_sec += 3;
|
||||||
|
|
||||||
|
/* Disable register write protect */
|
||||||
|
omap_rtc_unlock();
|
||||||
|
|
||||||
|
/* enable power-off via ALARM2 by setting the PWR_ENABLE_EN bit. */
|
||||||
|
safe_reg_set_bit(rtc.regs->RTC_SS_RTC_PMIC, PWR_ENABLE_EN_BIT);
|
||||||
|
|
||||||
|
/* Write the date/time to the RTC registers. */
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_ALARM2_SECONDS_REG,
|
||||||
|
(dec_to_bcd(t.tm_sec) & 0x7f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_ALARM2_MINUTES_REG,
|
||||||
|
(dec_to_bcd(t.tm_min) & 0x7f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_ALARM2_HOURS_REG,
|
||||||
|
(dec_to_bcd(t.tm_hour) & 0x3f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_ALARM2_DAYS_REG,
|
||||||
|
(dec_to_bcd(t.tm_mday) & 0x3f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_ALARM2_MONTHS_REG,
|
||||||
|
(dec_to_bcd(t.tm_mon + 1) & 0x1f));
|
||||||
|
safe_reg_write(rtc.regs->RTC_SS_ALARM2_YEARS_REG,
|
||||||
|
(dec_to_bcd(t.tm_year % 100) & 0xff));
|
||||||
|
|
||||||
|
/* enable interrupt to trigger POWER_EN to go low when alarm2 hits. */
|
||||||
|
safe_reg_set_bit(rtc.regs->RTC_SS_RTC_INTERRUPTS_REG, IT_ALARM2_BIT);
|
||||||
|
|
||||||
|
/* pause the realtime clock. the kernel will enable it when safe. */
|
||||||
|
reg_clear_bit(rtc.regs->RTC_SS_RTC_CTRL_REG, RTC_STOP_BIT);
|
||||||
|
|
||||||
|
/* Set this flag to block all other operations so that the clock isn't
|
||||||
|
* accidentally re-startered and so write protect isn't re-enabled. */
|
||||||
|
pwr_off_in_progress = 1;
|
||||||
|
|
||||||
|
/* Make the kernel's job easier by not re-enabling write protection */
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
arch_exit(void)
|
||||||
|
{
|
||||||
|
use_count--;
|
||||||
|
if (use_count == 0) {
|
||||||
|
vm_unmap_phys(SELF, (void *) rtc.mapped_addr, rtc.mr_size);
|
||||||
|
rtc.mapped_addr = 0;
|
||||||
|
}
|
||||||
|
log_debug(&log, "Exiting\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_sef_cb_lu_state_save(int UNUSED(state))
|
||||||
|
{
|
||||||
|
/* Nothing to save yet -- the TPS65950 RTC driver will need this */
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_lu_state_restore(void)
|
||||||
|
{
|
||||||
|
/* Nothing to restore yet -- the TPS65950 RTC driver will need this */
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
arch_announce(void)
|
||||||
|
{
|
||||||
|
/* Nothing to announce yet -- the TPS65950 RTC driver will need this */
|
||||||
|
}
|
88
drivers/readclock/arch/earm/omap_rtc.h
Normal file
88
drivers/readclock/arch/earm/omap_rtc.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#ifndef __OMAP_RTC_REGISTERS_H
|
||||||
|
#define __OMAP_RTC_REGISTERS_H
|
||||||
|
|
||||||
|
/* RTC Addresses for am335x (BeagleBone White / BeagleBone Black) */
|
||||||
|
|
||||||
|
/* Base Addresses */
|
||||||
|
#define AM335X_RTC_SS_BASE 0x44e3e000
|
||||||
|
|
||||||
|
/* Size of RTC Register Address Range */
|
||||||
|
#define AM335X_RTC_SS_SIZE 0x1000
|
||||||
|
|
||||||
|
/* Register Offsets */
|
||||||
|
#define AM335X_RTC_SS_SECONDS_REG 0x0
|
||||||
|
#define AM335X_RTC_SS_MINUTES_REG 0x4
|
||||||
|
#define AM335X_RTC_SS_HOURS_REG 0x8
|
||||||
|
#define AM335X_RTC_SS_DAYS_REG 0xC
|
||||||
|
#define AM335X_RTC_SS_MONTHS_REG 0x10
|
||||||
|
#define AM335X_RTC_SS_YEARS_REG 0x14
|
||||||
|
#define AM335X_RTC_SS_WEEKS_REG 0x18
|
||||||
|
#define AM335X_RTC_SS_ALARM_SECONDS_REG 0x20
|
||||||
|
#define AM335X_RTC_SS_ALARM_MINUTES_REG 0x24
|
||||||
|
#define AM335X_RTC_SS_ALARM_HOURS_REG 0x28
|
||||||
|
#define AM335X_RTC_SS_ALARM_DAYS_REG 0x2C
|
||||||
|
#define AM335X_RTC_SS_ALARM_MONTHS_REG 0x30
|
||||||
|
#define AM335X_RTC_SS_ALARM_YEARS_REG 0x34
|
||||||
|
#define AM335X_RTC_SS_RTC_CTRL_REG 0x40
|
||||||
|
#define AM335X_RTC_SS_RTC_STATUS_REG 0x44
|
||||||
|
#define AM335X_RTC_SS_RTC_INTERRUPTS_REG 0x48
|
||||||
|
#define AM335X_RTC_SS_RTC_COMP_LSB_REG 0x4C
|
||||||
|
#define AM335X_RTC_SS_RTC_COMP_MSB_REG 0x50
|
||||||
|
#define AM335X_RTC_SS_RTC_OSC_REG 0x54
|
||||||
|
#define AM335X_RTC_SS_RTC_SCRATCH0_REG 0x60
|
||||||
|
#define AM335X_RTC_SS_RTC_SCRATCH1_REG 0x64
|
||||||
|
#define AM335X_RTC_SS_RTC_SCRATCH2_REG 0x68
|
||||||
|
#define AM335X_RTC_SS_KICK0R 0x6C
|
||||||
|
#define AM335X_RTC_SS_KICK1R 0x70
|
||||||
|
#define AM335X_RTC_SS_RTC_REVISION 0x74
|
||||||
|
#define AM335X_RTC_SS_RTC_SYSCONFIG 0x78
|
||||||
|
#define AM335X_RTC_SS_RTC_IRQWAKEEN 0x7C
|
||||||
|
#define AM335X_RTC_SS_ALARM2_SECONDS_REG 0x80
|
||||||
|
#define AM335X_RTC_SS_ALARM2_MINUTES_REG 0x84
|
||||||
|
#define AM335X_RTC_SS_ALARM2_HOURS_REG 0x88
|
||||||
|
#define AM335X_RTC_SS_ALARM2_DAYS_REG 0x8C
|
||||||
|
#define AM335X_RTC_SS_ALARM2_MONTHS_REG 0x90
|
||||||
|
#define AM335X_RTC_SS_ALARM2_YEARS_REG 0x94
|
||||||
|
#define AM335X_RTC_SS_RTC_PMIC 0x98
|
||||||
|
#define AM335X_RTC_SS_RTC_DEBOUNCE 0x9C
|
||||||
|
|
||||||
|
/* Constants */
|
||||||
|
#define AM335X_RTC_SS_KICK0R_UNLOCK_MASK 0x83E70B13
|
||||||
|
#define AM335X_RTC_SS_KICK1R_UNLOCK_MASK 0x95A4F1E0
|
||||||
|
|
||||||
|
#define AM335X_RTC_SS_KICK0R_LOCK_MASK 0x546f6d20
|
||||||
|
#define AM335X_RTC_SS_KICK1R_LOCK_MASK 0x436f7274
|
||||||
|
|
||||||
|
/* Bits */
|
||||||
|
|
||||||
|
/* RTC_SS_RTC_STATUS_REG */
|
||||||
|
#define RTC_BUSY_BIT 0
|
||||||
|
|
||||||
|
/* RTC_SS_RTC_CTRL_REG */
|
||||||
|
#define RTC_STOP_BIT 0
|
||||||
|
|
||||||
|
/* RTC_SS_RTC_SYSCONFIG */
|
||||||
|
#define NOIDLE_BIT 0
|
||||||
|
|
||||||
|
/* RTC_SS_RTC_OSC_REG */
|
||||||
|
#define EN_32KCLK_BIT 6
|
||||||
|
|
||||||
|
/* RTC_SS_RTC_PMIC */
|
||||||
|
#define PWR_ENABLE_EN_BIT 16
|
||||||
|
|
||||||
|
/* RTC_SS_RTC_INTERRUPTS_REG */
|
||||||
|
#define IT_ALARM2_BIT 4
|
||||||
|
|
||||||
|
/* Clocks */
|
||||||
|
#define CM_RTC_RTC_CLKCTRL 0x800
|
||||||
|
#define CM_RTC_RTC_CLKCTRL_IDLEST ((0<<17)|(0<<16))
|
||||||
|
#define CM_RTC_RTC_CLKCTRL_MODULEMODE ((1<<1)|(0<<0))
|
||||||
|
#define CM_RTC_RTC_CLKCTRL_MASK (CM_RTC_RTC_CLKCTRL_IDLEST|CM_RTC_RTC_CLKCTRL_MODULEMODE)
|
||||||
|
|
||||||
|
#define CM_RTC_CLKSTCTRL 0x804
|
||||||
|
#define CLKACTIVITY_RTC_32KCLK (1<<9)
|
||||||
|
#define CLKACTIVITY_L4_RTC_GCLK (1<<8)
|
||||||
|
#define CLKTRCTRL ((0<<1)|(0<<0))
|
||||||
|
#define CM_RTC_CLKSTCTRL_MASK (CLKACTIVITY_RTC_32KCLK|CLKACTIVITY_L4_RTC_GCLK|CLKTRCTRL)
|
||||||
|
|
||||||
|
#endif /* __OMAP_RTC_REGISTERS_H */
|
7
drivers/readclock/arch/i386/Makefile.inc
Normal file
7
drivers/readclock/arch/i386/Makefile.inc
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Makefile for arch-dependent readclock code
|
||||||
|
.include <bsd.own.mk>
|
||||||
|
|
||||||
|
HERE=${.CURDIR}/arch/${MACHINE_ARCH}
|
||||||
|
.PATH: ${HERE}
|
||||||
|
|
||||||
|
SRCS += arch_readclock.c
|
322
drivers/readclock/arch/i386/arch_readclock.c
Normal file
322
drivers/readclock/arch/i386/arch_readclock.c
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
/* readclock - read the real time clock Authors: T. Holm & E. Froese
|
||||||
|
*
|
||||||
|
* Changed to be user-space driver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* readclock.c */
|
||||||
|
/* */
|
||||||
|
/* Read the clock value from the 64 byte CMOS RAM */
|
||||||
|
/* area, then set system time. */
|
||||||
|
/* */
|
||||||
|
/* If the machine ID byte is 0xFC or 0xF8, the device */
|
||||||
|
/* /dev/mem exists and can be opened for reading, */
|
||||||
|
/* and no errors in the CMOS RAM are reported by the */
|
||||||
|
/* RTC, then the time is read from the clock RAM */
|
||||||
|
/* area maintained by the RTC. */
|
||||||
|
/* */
|
||||||
|
/* The clock RAM values are decoded and fed to mktime */
|
||||||
|
/* to make a time_t value, then stime(2) is called. */
|
||||||
|
/* */
|
||||||
|
/* This fails if: */
|
||||||
|
/* */
|
||||||
|
/* If the machine ID does not match 0xFC or 0xF8 (no */
|
||||||
|
/* error message.) */
|
||||||
|
/* */
|
||||||
|
/* If the machine ID is 0xFC or 0xF8 and /dev/mem */
|
||||||
|
/* is missing, or cannot be accessed. */
|
||||||
|
/* */
|
||||||
|
/* If the RTC reports errors in the CMOS RAM. */
|
||||||
|
/* */
|
||||||
|
/************************************************************************/
|
||||||
|
/* origination 1987-Dec-29 efth */
|
||||||
|
/* robustness 1990-Oct-06 C. Sylvain */
|
||||||
|
/* incorp. B. Evans ideas 1991-Jul-06 C. Sylvain */
|
||||||
|
/* set time & calibrate 1992-Dec-17 Kees J. Bot */
|
||||||
|
/* clock timezone 1993-Oct-10 Kees J. Bot */
|
||||||
|
/* set CMOS clock 1994-Jun-12 Kees J. Bot */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <minix/type.h>
|
||||||
|
#include <minix/const.h>
|
||||||
|
#include <minix/syslib.h>
|
||||||
|
#include <minix/sysutil.h>
|
||||||
|
#include <minix/com.h>
|
||||||
|
#include <minix/log.h>
|
||||||
|
#include <machine/cmos.h>
|
||||||
|
#include <sys/svrctl.h>
|
||||||
|
|
||||||
|
#include "readclock.h"
|
||||||
|
|
||||||
|
#define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */
|
||||||
|
|
||||||
|
#define PC_AT 0xFC /* Machine ID byte for PC/AT,
|
||||||
|
* PC/XT286, and PS/2 Models 50, 60 */
|
||||||
|
#define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */
|
||||||
|
|
||||||
|
/* Manufacturers usually use the ID value of the IBM model they emulate.
|
||||||
|
* However some manufacturers, notably HP and COMPAQ, have had different
|
||||||
|
* ideas in the past.
|
||||||
|
*
|
||||||
|
* Machine ID byte information source:
|
||||||
|
* _The Programmer's PC Sourcebook_ by Thom Hogan,
|
||||||
|
* published by Microsoft Press
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* used for logging */
|
||||||
|
static struct log log = {
|
||||||
|
.name = "cmos_clock",
|
||||||
|
.log_level = LEVEL_INFO,
|
||||||
|
.log_func = default_log
|
||||||
|
};
|
||||||
|
|
||||||
|
static int read_register(int reg_addr);
|
||||||
|
static int write_register(int reg_addr, int value);
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_init(void)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
unsigned char mach_id, cmos_state;
|
||||||
|
|
||||||
|
if ((s = sys_readbios(MACH_ID_ADDR, &mach_id, sizeof(mach_id))) != OK) {
|
||||||
|
log_warn(&log, "sys_readbios failed: %d.\n", s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mach_id != PS_386 && mach_id != PC_AT) {
|
||||||
|
log_warn(&log, "Machine ID unknown.");
|
||||||
|
log_warn(&log, "Machine ID byte = %02x\n", mach_id);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmos_state = read_register(CMOS_STATUS);
|
||||||
|
|
||||||
|
if (cmos_state & (CS_LOST_POWER | CS_BAD_CHKSUM | CS_BAD_TIME)) {
|
||||||
|
log_warn(&log, "CMOS RAM error(s) found...");
|
||||||
|
log_warn(&log, "CMOS state = 0x%02x\n", cmos_state);
|
||||||
|
|
||||||
|
if (cmos_state & CS_LOST_POWER)
|
||||||
|
log_warn(&log,
|
||||||
|
"RTC lost power. Reset CMOS RAM with SETUP.");
|
||||||
|
if (cmos_state & CS_BAD_CHKSUM)
|
||||||
|
log_warn(&log, "CMOS RAM checksum is bad. Run SETUP.");
|
||||||
|
if (cmos_state & CS_BAD_TIME)
|
||||||
|
log_warn(&log,
|
||||||
|
"Time invalid in CMOS RAM. Reset clock.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* */
|
||||||
|
/* arch_get_time( time ) */
|
||||||
|
/* */
|
||||||
|
/* Update the structure pointed to by time with the current time */
|
||||||
|
/* as read from CMOS RAM of the RTC. */
|
||||||
|
/* If necessary, the time is converted into a binary format before */
|
||||||
|
/* being stored in the structure. */
|
||||||
|
/* */
|
||||||
|
/***********************************************************************/
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_get_time(struct tm *t, int flags)
|
||||||
|
{
|
||||||
|
int osec, n;
|
||||||
|
|
||||||
|
do {
|
||||||
|
osec = -1;
|
||||||
|
n = 0;
|
||||||
|
do {
|
||||||
|
/* Clock update in progress? */
|
||||||
|
if (read_register(RTC_REG_A) & RTC_A_UIP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
t->tm_sec = read_register(RTC_SEC);
|
||||||
|
if (t->tm_sec != osec) {
|
||||||
|
/* Seconds changed. First from -1, then because the
|
||||||
|
* clock ticked, which is what we're waiting for to
|
||||||
|
* get a precise reading.
|
||||||
|
*/
|
||||||
|
osec = t->tm_sec;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
} while (n < 2);
|
||||||
|
|
||||||
|
/* Read the other registers. */
|
||||||
|
t->tm_min = read_register(RTC_MIN);
|
||||||
|
t->tm_hour = read_register(RTC_HOUR);
|
||||||
|
t->tm_mday = read_register(RTC_MDAY);
|
||||||
|
t->tm_mon = read_register(RTC_MONTH);
|
||||||
|
t->tm_year = read_register(RTC_YEAR);
|
||||||
|
|
||||||
|
/* Time stable? */
|
||||||
|
} while (read_register(RTC_SEC) != t->tm_sec
|
||||||
|
|| read_register(RTC_MIN) != t->tm_min
|
||||||
|
|| read_register(RTC_HOUR) != t->tm_hour
|
||||||
|
|| read_register(RTC_MDAY) != t->tm_mday
|
||||||
|
|| read_register(RTC_MONTH) != t->tm_mon
|
||||||
|
|| read_register(RTC_YEAR) != t->tm_year);
|
||||||
|
|
||||||
|
if ((read_register(RTC_REG_B) & RTC_B_DM_BCD) == 0) {
|
||||||
|
/* Convert BCD to binary (default RTC mode). */
|
||||||
|
t->tm_year = bcd_to_dec(t->tm_year);
|
||||||
|
t->tm_mon = bcd_to_dec(t->tm_mon);
|
||||||
|
t->tm_mday = bcd_to_dec(t->tm_mday);
|
||||||
|
t->tm_hour = bcd_to_dec(t->tm_hour);
|
||||||
|
t->tm_min = bcd_to_dec(t->tm_min);
|
||||||
|
t->tm_sec = bcd_to_dec(t->tm_sec);
|
||||||
|
}
|
||||||
|
t->tm_mon--; /* Counts from 0. */
|
||||||
|
|
||||||
|
/* Correct the year, good until 2080. */
|
||||||
|
if (t->tm_year < 80)
|
||||||
|
t->tm_year += 100;
|
||||||
|
|
||||||
|
if ((flags & RTCDEV_Y2KBUG) == RTCDEV_Y2KBUG) {
|
||||||
|
/* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */
|
||||||
|
if (t->tm_year < 100)
|
||||||
|
t->tm_year += 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
read_register(int reg_addr)
|
||||||
|
{
|
||||||
|
u32_t r;
|
||||||
|
|
||||||
|
if (sys_outb(RTC_INDEX, reg_addr) != OK) {
|
||||||
|
log_warn(&log, "outb failed of %x\n", RTC_INDEX);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (sys_inb(RTC_IO, &r) != OK) {
|
||||||
|
log_warn(&log, "inb failed of %x (index %x) failed\n", RTC_IO,
|
||||||
|
reg_addr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************/
|
||||||
|
/* */
|
||||||
|
/* arch_set_time( time ) */
|
||||||
|
/* */
|
||||||
|
/* Set the CMOS RTC to the time found in the structure. */
|
||||||
|
/* */
|
||||||
|
/***********************************************************************/
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_set_time(struct tm *t, int flags)
|
||||||
|
{
|
||||||
|
int regA, regB;
|
||||||
|
|
||||||
|
if ((flags & RTCDEV_CMOSREG) == RTCDEV_CMOSREG) {
|
||||||
|
/* Set A and B registers to their proper values according to the AT
|
||||||
|
* reference manual. (For if it gets messed up, but the BIOS doesn't
|
||||||
|
* repair it.)
|
||||||
|
*/
|
||||||
|
write_register(RTC_REG_A, RTC_A_DV_OK | RTC_A_RS_DEF);
|
||||||
|
write_register(RTC_REG_B, RTC_B_24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inhibit updates. */
|
||||||
|
regB = read_register(RTC_REG_B);
|
||||||
|
write_register(RTC_REG_B, regB | RTC_B_SET);
|
||||||
|
|
||||||
|
t->tm_mon++; /* Counts from 1. */
|
||||||
|
|
||||||
|
if ((flags & RTCDEV_Y2KBUG) == RTCDEV_Y2KBUG) {
|
||||||
|
/* Set the clock back 20 years to avoid Y2K bug, good until 2020. */
|
||||||
|
if (t->tm_year >= 100)
|
||||||
|
t->tm_year -= 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((regB & 0x04) == 0) {
|
||||||
|
/* Convert binary to BCD (default RTC mode) */
|
||||||
|
t->tm_year = dec_to_bcd(t->tm_year % 100);
|
||||||
|
t->tm_mon = dec_to_bcd(t->tm_mon);
|
||||||
|
t->tm_mday = dec_to_bcd(t->tm_mday);
|
||||||
|
t->tm_hour = dec_to_bcd(t->tm_hour);
|
||||||
|
t->tm_min = dec_to_bcd(t->tm_min);
|
||||||
|
t->tm_sec = dec_to_bcd(t->tm_sec);
|
||||||
|
}
|
||||||
|
write_register(RTC_YEAR, t->tm_year);
|
||||||
|
write_register(RTC_MONTH, t->tm_mon);
|
||||||
|
write_register(RTC_MDAY, t->tm_mday);
|
||||||
|
write_register(RTC_HOUR, t->tm_hour);
|
||||||
|
write_register(RTC_MIN, t->tm_min);
|
||||||
|
write_register(RTC_SEC, t->tm_sec);
|
||||||
|
|
||||||
|
/* Stop the clock. */
|
||||||
|
regA = read_register(RTC_REG_A);
|
||||||
|
write_register(RTC_REG_A, regA | RTC_A_DV_STOP);
|
||||||
|
|
||||||
|
/* Allow updates and restart the clock. */
|
||||||
|
write_register(RTC_REG_B, regB);
|
||||||
|
write_register(RTC_REG_A, regA);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
write_register(int reg_addr, int value)
|
||||||
|
{
|
||||||
|
if (sys_outb(RTC_INDEX, reg_addr) != OK) {
|
||||||
|
log_warn(&log, "outb failed of %x\n", RTC_INDEX);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (sys_outb(RTC_IO, value) != OK) {
|
||||||
|
log_warn(&log, "outb failed of %x (index %x)\n", RTC_IO,
|
||||||
|
reg_addr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_pwr_off(void)
|
||||||
|
{
|
||||||
|
/* Not Implemented */
|
||||||
|
return ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
arch_exit(void)
|
||||||
|
{
|
||||||
|
/* Nothing to clean up here */
|
||||||
|
log_debug(&log, "Exiting...");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_sef_cb_lu_state_save(int UNUSED(state))
|
||||||
|
{
|
||||||
|
/* This arch doesn't have state to save */
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
arch_lu_state_restore(void)
|
||||||
|
{
|
||||||
|
/* This arch doesn't have state to restore */
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
arch_announce(void)
|
||||||
|
{
|
||||||
|
/* This arch doesn't need to do anything here. */
|
||||||
|
}
|
|
@ -1,43 +1,4 @@
|
||||||
/* readclock - read the real time clock Authors: T. Holm & E. Froese
|
/* readclock - manipulate the hardware real time clock */
|
||||||
*
|
|
||||||
* Changed to be user-space driver.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* readclock.c */
|
|
||||||
/* */
|
|
||||||
/* Read the clock value from the 64 byte CMOS RAM */
|
|
||||||
/* area, then set system time. */
|
|
||||||
/* */
|
|
||||||
/* If the machine ID byte is 0xFC or 0xF8, the device */
|
|
||||||
/* /dev/mem exists and can be opened for reading, */
|
|
||||||
/* and no errors in the CMOS RAM are reported by the */
|
|
||||||
/* RTC, then the time is read from the clock RAM */
|
|
||||||
/* area maintained by the RTC. */
|
|
||||||
/* */
|
|
||||||
/* The clock RAM values are decoded and fed to mktime */
|
|
||||||
/* to make a time_t value, then stime(2) is called. */
|
|
||||||
/* */
|
|
||||||
/* This fails if: */
|
|
||||||
/* */
|
|
||||||
/* If the machine ID does not match 0xFC or 0xF8 (no */
|
|
||||||
/* error message.) */
|
|
||||||
/* */
|
|
||||||
/* If the machine ID is 0xFC or 0xF8 and /dev/mem */
|
|
||||||
/* is missing, or cannot be accessed. */
|
|
||||||
/* */
|
|
||||||
/* If the RTC reports errors in the CMOS RAM. */
|
|
||||||
/* */
|
|
||||||
/************************************************************************/
|
|
||||||
/* origination 1987-Dec-29 efth */
|
|
||||||
/* robustness 1990-Oct-06 C. Sylvain */
|
|
||||||
/* incorp. B. Evans ideas 1991-Jul-06 C. Sylvain */
|
|
||||||
/* set time & calibrate 1992-Dec-17 Kees J. Bot */
|
|
||||||
/* clock timezone 1993-Oct-10 Kees J. Bot */
|
|
||||||
/* set CMOS clock 1994-Jun-12 Kees J. Bot */
|
|
||||||
/************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -47,343 +8,194 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <minix/type.h>
|
#include <minix/type.h>
|
||||||
#include <minix/const.h>
|
#include <minix/const.h>
|
||||||
|
#include <minix/callnr.h>
|
||||||
|
#include <minix/log.h>
|
||||||
#include <minix/syslib.h>
|
#include <minix/syslib.h>
|
||||||
#include <minix/sysutil.h>
|
#include <minix/sysutil.h>
|
||||||
#include <minix/com.h>
|
#include <minix/com.h>
|
||||||
#include <machine/cmos.h>
|
#include <minix/type.h>
|
||||||
|
#include <minix/safecopies.h>
|
||||||
#include <sys/svrctl.h>
|
#include <sys/svrctl.h>
|
||||||
|
|
||||||
int nflag = 0; /* Tell what, but don't do it. */
|
#include "readclock.h"
|
||||||
int wflag = 0; /* Set the CMOS clock. */
|
|
||||||
int Wflag = 0; /* Also set the CMOS clock register bits. */
|
|
||||||
int y2kflag = 0; /* Interpret 1980 as 2000 for clock with Y2K bug. */
|
|
||||||
|
|
||||||
#define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */
|
static struct log log = {
|
||||||
|
.name = "readclock",
|
||||||
|
.log_level = LEVEL_INFO,
|
||||||
|
.log_func = default_log
|
||||||
|
};
|
||||||
|
|
||||||
#define PC_AT 0xFC /* Machine ID byte for PC/AT,
|
/* functions for transfering struct tm to/from this driver and calling proc. */
|
||||||
PC/XT286, and PS/2 Models 50, 60 */
|
static int fetch_t(endpoint_t who_e, vir_bytes rtcdev_tm, struct tm *t);
|
||||||
#define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */
|
static int store_t(endpoint_t who_e, vir_bytes rtcdev_tm, struct tm *t);
|
||||||
|
|
||||||
/* Manufacturers usually use the ID value of the IBM model they emulate.
|
|
||||||
* However some manufacturers, notably HP and COMPAQ, have had different
|
|
||||||
* ideas in the past.
|
|
||||||
*
|
|
||||||
* Machine ID byte information source:
|
|
||||||
* _The Programmer's PC Sourcebook_ by Thom Hogan,
|
|
||||||
* published by Microsoft Press
|
|
||||||
*/
|
|
||||||
|
|
||||||
void errmsg(char *s);
|
|
||||||
void get_time(struct tm *t);
|
|
||||||
int read_register(int reg_addr);
|
|
||||||
void set_time(struct tm *t);
|
|
||||||
void write_register(int reg_addr, int value);
|
|
||||||
int bcd_to_dec(int n);
|
|
||||||
int dec_to_bcd(int n);
|
|
||||||
void usage(void);
|
|
||||||
|
|
||||||
/* SEF functions and variables. */
|
/* SEF functions and variables. */
|
||||||
static void sef_local_startup(void);
|
static void sef_local_startup(void);
|
||||||
|
static int sef_cb_init(int type, sef_init_info_t * info);
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct tm time1;
|
int r;
|
||||||
struct tm time2;
|
endpoint_t caller;
|
||||||
struct tm tmnow;
|
struct tm t;
|
||||||
char date[64];
|
message m;
|
||||||
time_t now, rtc;
|
int ipc_status, reply_status;
|
||||||
int i, s;
|
|
||||||
unsigned char mach_id, cmos_state;
|
|
||||||
|
|
||||||
/* SEF local startup. */
|
env_setargs(argc, argv);
|
||||||
env_setargs(argc, argv);
|
sef_local_startup();
|
||||||
sef_local_startup();
|
|
||||||
|
|
||||||
if((s=sys_readbios(MACH_ID_ADDR, &mach_id, sizeof(mach_id))) != OK) {
|
while (TRUE) {
|
||||||
printf("readclock: sys_readbios failed: %d.\n", s);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mach_id != PS_386 && mach_id != PC_AT) {
|
/* Receive Message */
|
||||||
errmsg("Machine ID unknown." );
|
r = sef_receive_status(ANY, &m, &ipc_status);
|
||||||
printf("Machine ID byte = %02x\n", mach_id );
|
if (r != OK) {
|
||||||
|
log_warn(&log, "sef_receive_status() failed\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
exit(1);
|
if (is_ipc_notify(ipc_status)) {
|
||||||
}
|
|
||||||
|
|
||||||
cmos_state = read_register(CMOS_STATUS);
|
/* Do not reply to notifications. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (cmos_state & (CS_LOST_POWER | CS_BAD_CHKSUM | CS_BAD_TIME)) {
|
caller = m.m_source;
|
||||||
errmsg( "CMOS RAM error(s) found..." );
|
|
||||||
printf("CMOS state = 0x%02x\n", cmos_state );
|
|
||||||
|
|
||||||
if (cmos_state & CS_LOST_POWER)
|
log_debug(&log, "Got message 0x%x from 0x%x\n", m.m_type, caller);
|
||||||
errmsg( "RTC lost power. Reset CMOS RAM with SETUP." );
|
|
||||||
if (cmos_state & CS_BAD_CHKSUM)
|
|
||||||
errmsg( "CMOS RAM checksum is bad. Run SETUP." );
|
|
||||||
if (cmos_state & CS_BAD_TIME)
|
|
||||||
errmsg( "Time invalid in CMOS RAM. Reset clock." );
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process options. */
|
switch (m.m_type) {
|
||||||
while (argc > 1) {
|
case RTCDEV_GET_TIME:
|
||||||
char *p = *++argv;
|
/* Any user can read the time */
|
||||||
|
reply_status = arch_get_time(&t, m.RTCDEV_FLAGS);
|
||||||
|
if (reply_status != OK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (*p++ != '-') usage();
|
/* write results back to calling process */
|
||||||
|
reply_status =
|
||||||
|
store_t(caller, (vir_bytes) m.RTCDEV_TM, &t);
|
||||||
|
break;
|
||||||
|
|
||||||
while (*p != 0) {
|
case RTCDEV_SET_TIME:
|
||||||
switch (*p++) {
|
/* Only super user is allowed to set the time */
|
||||||
case 'n': nflag = 1; break;
|
if (getnuid(caller) == SUPER_USER) {
|
||||||
case 'w': wflag = 1; break;
|
/* read time from calling process */
|
||||||
case 'W': Wflag = 1; break;
|
reply_status =
|
||||||
case '2': y2kflag = 1; break;
|
fetch_t(caller, (vir_bytes) m.RTCDEV_TM,
|
||||||
default: usage();
|
&t);
|
||||||
|
if (reply_status != OK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply_status =
|
||||||
|
arch_set_time(&t, m.RTCDEV_FLAGS);
|
||||||
|
} else {
|
||||||
|
reply_status = EPERM;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RTCDEV_PWR_OFF:
|
||||||
|
/* Only PM is allowed to set the power off time */
|
||||||
|
if (caller == PM_PROC_NR) {
|
||||||
|
reply_status = arch_pwr_off();
|
||||||
|
} else {
|
||||||
|
reply_status = EPERM;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Unrecognized call */
|
||||||
|
reply_status = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send Reply */
|
||||||
|
m.m_type = RTCDEV_REPLY;
|
||||||
|
m.RTCDEV_STATUS = reply_status;
|
||||||
|
|
||||||
|
log_debug(&log, "Sending Reply");
|
||||||
|
|
||||||
|
r = sendnb(caller, &m);
|
||||||
|
if (r != OK) {
|
||||||
|
log_warn(&log, "sendnb() failed\n");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
argc--;
|
|
||||||
}
|
|
||||||
if (Wflag) wflag = 1; /* -W implies -w */
|
|
||||||
|
|
||||||
/* Read the CMOS real time clock. */
|
arch_exit();
|
||||||
for (i = 0; i < 10; i++) {
|
return 0;
|
||||||
get_time(&time1);
|
}
|
||||||
now = time(NULL);
|
|
||||||
|
|
||||||
time1.tm_isdst = -1; /* Do timezone calculations. */
|
static int
|
||||||
time2 = time1;
|
sef_cb_init(int type, sef_init_info_t * UNUSED(info))
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
rtc= mktime(&time1); /* Transform to a time_t. */
|
if (type == SEF_INIT_LU) {
|
||||||
if (rtc != -1) break;
|
/* Restore the state. */
|
||||||
|
arch_lu_state_restore();
|
||||||
printf(
|
|
||||||
"readclock: Invalid time read from CMOS RTC: %d-%02d-%02d %02d:%02d:%02d\n",
|
|
||||||
time2.tm_year+1900, time2.tm_mon+1, time2.tm_mday,
|
|
||||||
time2.tm_hour, time2.tm_min, time2.tm_sec);
|
|
||||||
sleep(5);
|
|
||||||
}
|
|
||||||
if (i == 10) exit(1);
|
|
||||||
|
|
||||||
if (!wflag) {
|
|
||||||
/* Set system time. */
|
|
||||||
if (nflag) {
|
|
||||||
printf("stime(%lu)\n", (unsigned long) rtc);
|
|
||||||
} else {
|
|
||||||
if (stime(&rtc) < 0) {
|
|
||||||
errmsg( "Not allowed to set time." );
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tmnow = *localtime(&rtc);
|
|
||||||
if (strftime(date, sizeof(date),
|
r = arch_init();
|
||||||
"%a %b %d %H:%M:%S %Z %Y", &tmnow) != 0) {
|
if (r != OK) {
|
||||||
if (date[8] == '0') date[8]= ' ';
|
return r;
|
||||||
printf("%s\n", date);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
/* Set the CMOS clock to the system time. */
|
if (type != SEF_INIT_LU) {
|
||||||
tmnow = *localtime(&now);
|
/* Some RTCs need to do a driver announcement */
|
||||||
if (nflag) {
|
arch_announce();
|
||||||
printf("%04d-%02d-%02d %02d:%02d:%02d\n",
|
|
||||||
tmnow.tm_year + 1900,
|
|
||||||
tmnow.tm_mon + 1,
|
|
||||||
tmnow.tm_mday,
|
|
||||||
tmnow.tm_hour,
|
|
||||||
tmnow.tm_min,
|
|
||||||
tmnow.tm_sec);
|
|
||||||
} else {
|
|
||||||
set_time(&tmnow);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
exit(0);
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*===========================================================================*
|
static void
|
||||||
* sef_local_startup *
|
sef_local_startup()
|
||||||
*===========================================================================*/
|
|
||||||
static void sef_local_startup()
|
|
||||||
{
|
{
|
||||||
/* Let SEF perform startup. */
|
/*
|
||||||
sef_startup();
|
* Register init callbacks. Use the same function for all event types
|
||||||
}
|
|
||||||
|
|
||||||
void errmsg(char *s)
|
|
||||||
{
|
|
||||||
static char *prompt = "readclock: ";
|
|
||||||
|
|
||||||
printf("%s%s\n", prompt, s);
|
|
||||||
prompt = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
/* */
|
|
||||||
/* get_time( time ) */
|
|
||||||
/* */
|
|
||||||
/* Update the structure pointed to by time with the current time */
|
|
||||||
/* as read from CMOS RAM of the RTC. */
|
|
||||||
/* If necessary, the time is converted into a binary format before */
|
|
||||||
/* being stored in the structure. */
|
|
||||||
/* */
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
void get_time(struct tm *t)
|
|
||||||
{
|
|
||||||
int osec, n;
|
|
||||||
|
|
||||||
do {
|
|
||||||
osec = -1;
|
|
||||||
n = 0;
|
|
||||||
do {
|
|
||||||
/* Clock update in progress? */
|
|
||||||
if (read_register(RTC_REG_A) & RTC_A_UIP) continue;
|
|
||||||
|
|
||||||
t->tm_sec = read_register(RTC_SEC);
|
|
||||||
if (t->tm_sec != osec) {
|
|
||||||
/* Seconds changed. First from -1, then because the
|
|
||||||
* clock ticked, which is what we're waiting for to
|
|
||||||
* get a precise reading.
|
|
||||||
*/
|
|
||||||
osec = t->tm_sec;
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
} while (n < 2);
|
|
||||||
|
|
||||||
/* Read the other registers. */
|
|
||||||
t->tm_min = read_register(RTC_MIN);
|
|
||||||
t->tm_hour = read_register(RTC_HOUR);
|
|
||||||
t->tm_mday = read_register(RTC_MDAY);
|
|
||||||
t->tm_mon = read_register(RTC_MONTH);
|
|
||||||
t->tm_year = read_register(RTC_YEAR);
|
|
||||||
|
|
||||||
/* Time stable? */
|
|
||||||
} while (read_register(RTC_SEC) != t->tm_sec
|
|
||||||
|| read_register(RTC_MIN) != t->tm_min
|
|
||||||
|| read_register(RTC_HOUR) != t->tm_hour
|
|
||||||
|| read_register(RTC_MDAY) != t->tm_mday
|
|
||||||
|| read_register(RTC_MONTH) != t->tm_mon
|
|
||||||
|| read_register(RTC_YEAR) != t->tm_year);
|
|
||||||
|
|
||||||
if ((read_register(RTC_REG_B) & RTC_B_DM_BCD) == 0) {
|
|
||||||
/* Convert BCD to binary (default RTC mode). */
|
|
||||||
t->tm_year = bcd_to_dec(t->tm_year);
|
|
||||||
t->tm_mon = bcd_to_dec(t->tm_mon);
|
|
||||||
t->tm_mday = bcd_to_dec(t->tm_mday);
|
|
||||||
t->tm_hour = bcd_to_dec(t->tm_hour);
|
|
||||||
t->tm_min = bcd_to_dec(t->tm_min);
|
|
||||||
t->tm_sec = bcd_to_dec(t->tm_sec);
|
|
||||||
}
|
|
||||||
t->tm_mon--; /* Counts from 0. */
|
|
||||||
|
|
||||||
/* Correct the year, good until 2080. */
|
|
||||||
if (t->tm_year < 80) t->tm_year += 100;
|
|
||||||
|
|
||||||
if (y2kflag) {
|
|
||||||
/* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */
|
|
||||||
if (t->tm_year < 100) t->tm_year += 20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int read_register(int reg_addr)
|
|
||||||
{
|
|
||||||
u32_t r;
|
|
||||||
|
|
||||||
if(sys_outb(RTC_INDEX, reg_addr) != OK) {
|
|
||||||
printf("cmos: outb failed of %x\n", RTC_INDEX);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if(sys_inb(RTC_IO, &r) != OK) {
|
|
||||||
printf("cmos: inb failed of %x (index %x) failed\n", RTC_IO, reg_addr);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************/
|
|
||||||
/* */
|
|
||||||
/* set_time( time ) */
|
|
||||||
/* */
|
|
||||||
/* Set the CMOS RTC to the time found in the structure. */
|
|
||||||
/* */
|
|
||||||
/***********************************************************************/
|
|
||||||
|
|
||||||
void set_time(struct tm *t)
|
|
||||||
{
|
|
||||||
int regA, regB;
|
|
||||||
|
|
||||||
if (Wflag) {
|
|
||||||
/* Set A and B registers to their proper values according to the AT
|
|
||||||
* reference manual. (For if it gets messed up, but the BIOS doesn't
|
|
||||||
* repair it.)
|
|
||||||
*/
|
*/
|
||||||
write_register(RTC_REG_A, RTC_A_DV_OK | RTC_A_RS_DEF);
|
sef_setcb_init_fresh(sef_cb_init);
|
||||||
write_register(RTC_REG_B, RTC_B_24);
|
sef_setcb_init_lu(sef_cb_init);
|
||||||
}
|
sef_setcb_init_restart(sef_cb_init);
|
||||||
|
|
||||||
/* Inhibit updates. */
|
/*
|
||||||
regB= read_register(RTC_REG_B);
|
* Register live update callbacks.
|
||||||
write_register(RTC_REG_B, regB | RTC_B_SET);
|
*/
|
||||||
|
/* Agree to update immediately when LU is requested in a valid state. */
|
||||||
|
sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
|
||||||
|
/* Support live update starting from any standard state. */
|
||||||
|
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
|
||||||
|
/* Register a custom routine to save the state. */
|
||||||
|
sef_setcb_lu_state_save(arch_sef_cb_lu_state_save);
|
||||||
|
|
||||||
t->tm_mon++; /* Counts from 1. */
|
/* Let SEF perform startup. */
|
||||||
|
sef_startup();
|
||||||
if (y2kflag) {
|
|
||||||
/* Set the clock back 20 years to avoid Y2K bug, good until 2020. */
|
|
||||||
if (t->tm_year >= 100) t->tm_year -= 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((regB & 0x04) == 0) {
|
|
||||||
/* Convert binary to BCD (default RTC mode) */
|
|
||||||
t->tm_year = dec_to_bcd(t->tm_year % 100);
|
|
||||||
t->tm_mon = dec_to_bcd(t->tm_mon);
|
|
||||||
t->tm_mday = dec_to_bcd(t->tm_mday);
|
|
||||||
t->tm_hour = dec_to_bcd(t->tm_hour);
|
|
||||||
t->tm_min = dec_to_bcd(t->tm_min);
|
|
||||||
t->tm_sec = dec_to_bcd(t->tm_sec);
|
|
||||||
}
|
|
||||||
write_register(RTC_YEAR, t->tm_year);
|
|
||||||
write_register(RTC_MONTH, t->tm_mon);
|
|
||||||
write_register(RTC_MDAY, t->tm_mday);
|
|
||||||
write_register(RTC_HOUR, t->tm_hour);
|
|
||||||
write_register(RTC_MIN, t->tm_min);
|
|
||||||
write_register(RTC_SEC, t->tm_sec);
|
|
||||||
|
|
||||||
/* Stop the clock. */
|
|
||||||
regA= read_register(RTC_REG_A);
|
|
||||||
write_register(RTC_REG_A, regA | RTC_A_DV_STOP);
|
|
||||||
|
|
||||||
/* Allow updates and restart the clock. */
|
|
||||||
write_register(RTC_REG_B, regB);
|
|
||||||
write_register(RTC_REG_A, regA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
void write_register(int reg_addr, int value)
|
bcd_to_dec(int n)
|
||||||
{
|
{
|
||||||
if(sys_outb(RTC_INDEX, reg_addr) != OK) {
|
return ((n >> 4) & 0x0F) * 10 + (n & 0x0F);
|
||||||
printf("cmos: outb failed of %x\n", RTC_INDEX);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if(sys_outb(RTC_IO, value) != OK) {
|
|
||||||
printf("cmos: outb failed of %x (index %x)\n", RTC_IO, reg_addr);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int bcd_to_dec(int n)
|
int
|
||||||
|
dec_to_bcd(int n)
|
||||||
{
|
{
|
||||||
return ((n >> 4) & 0x0F) * 10 + (n & 0x0F);
|
return ((n / 10) << 4) | (n % 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dec_to_bcd(int n)
|
static int
|
||||||
|
fetch_t(endpoint_t who_e, vir_bytes rtcdev_tm, struct tm *t)
|
||||||
{
|
{
|
||||||
return ((n / 10) << 4) | (n % 10);
|
return sys_datacopy(who_e, rtcdev_tm, SELF, (vir_bytes) t,
|
||||||
|
sizeof(struct tm));
|
||||||
}
|
}
|
||||||
|
|
||||||
void usage(void)
|
static int
|
||||||
|
store_t(endpoint_t who_e, vir_bytes rtcdev_tm, struct tm *t)
|
||||||
{
|
{
|
||||||
printf("Usage: readclock [-nwW2]\n");
|
return sys_datacopy(SELF, (vir_bytes) t, who_e, rtcdev_tm,
|
||||||
exit(1);
|
sizeof(struct tm));
|
||||||
}
|
}
|
||||||
|
|
22
drivers/readclock/readclock.h
Normal file
22
drivers/readclock/readclock.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef __READCLOCK_H
|
||||||
|
#define __READCLOCK_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* implementations provided by arch/${MACHINE_ARCH}/arch_readclock.c */
|
||||||
|
int arch_init(void); /* setup */
|
||||||
|
int arch_get_time(struct tm *t, int flags); /* read the hardware clock into t */
|
||||||
|
int arch_set_time(struct tm *t, int flags); /* set the hardware clock to t */
|
||||||
|
int arch_pwr_off(void); /* set the power off alarm to 5 sec from now. */
|
||||||
|
void arch_exit(void); /* clean up */
|
||||||
|
|
||||||
|
/* arch specific driver related functions */
|
||||||
|
int arch_sef_cb_lu_state_save(int);
|
||||||
|
int arch_lu_state_restore(void);
|
||||||
|
void arch_announce(void);
|
||||||
|
|
||||||
|
/* utility functions provided by readclock.c */
|
||||||
|
int bcd_to_dec(int n);
|
||||||
|
int dec_to_bcd(int n);
|
||||||
|
|
||||||
|
#endif /* __READCLOCK_H */
|
14
etc/rc
14
etc/rc
|
@ -124,17 +124,9 @@ start)
|
||||||
then . "$RC_TZ"
|
then . "$RC_TZ"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $ARCH = i386 ]
|
# Start real time clock driver & set system time, otherwise default date.
|
||||||
then
|
up readclock.drv
|
||||||
# Try to read the hardware real-time clock, otherwise do it manually.
|
readclock || date 201301010000
|
||||||
readclock || intr date -q
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $ARCH = earm ]
|
|
||||||
then
|
|
||||||
date 201301010000
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# Initialize files.
|
# Initialize files.
|
||||||
>/etc/utmp # /etc/utmp keeps track of logins
|
>/etc/utmp # /etc/utmp keeps track of logins
|
||||||
|
|
|
@ -299,9 +299,12 @@ service random
|
||||||
|
|
||||||
service readclock.drv
|
service readclock.drv
|
||||||
{
|
{
|
||||||
|
ipc ALL;
|
||||||
io 70:2;
|
io 70:2;
|
||||||
system
|
system
|
||||||
|
PRIVCTL # 4
|
||||||
UMAP # 14
|
UMAP # 14
|
||||||
|
VIRCOPY # 15
|
||||||
DEVIO # 21
|
DEVIO # 21
|
||||||
READBIOS # 35
|
READBIOS # 35
|
||||||
;
|
;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
* 0x1400 - 0x14FF VFS-FS transaction IDs
|
* 0x1400 - 0x14FF VFS-FS transaction IDs
|
||||||
* 0x1500 - 0x15FF Block device requests and responses
|
* 0x1500 - 0x15FF Block device requests and responses
|
||||||
* 0x1600 - 0x16FF VirtualBox (VBOX) requests (see vboxif.h)
|
* 0x1600 - 0x16FF VirtualBox (VBOX) requests (see vboxif.h)
|
||||||
|
* 0x1700 - 0x17FF Real Time Clock requests and responses
|
||||||
*
|
*
|
||||||
* Zero and negative values are widely used for OK and error responses.
|
* Zero and negative values are widely used for OK and error responses.
|
||||||
*/
|
*/
|
||||||
|
@ -1305,4 +1306,33 @@
|
||||||
#define RU_WHO m1_i1 /* who argument in getrusage call */
|
#define RU_WHO m1_i1 /* who argument in getrusage call */
|
||||||
#define RU_RUSAGE_ADDR m1_p1 /* pointer to struct rusage */
|
#define RU_RUSAGE_ADDR m1_p1 /* pointer to struct rusage */
|
||||||
|
|
||||||
|
/*===========================================================================*
|
||||||
|
* Messages for Real Time Clocks *
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/* Base type for real time clock requests and responses. */
|
||||||
|
#define RTCDEV_RQ_BASE 0x1700
|
||||||
|
#define RTCDEV_RS_BASE 0x1780
|
||||||
|
|
||||||
|
#define IS_RTCDEV_RQ(type) (((type) & ~0x7f) == RTCDEV_RQ_BASE)
|
||||||
|
#define IS_RTCDEV_RS(type) (((type) & ~0x7f) == RTCDEV_RS_BASE)
|
||||||
|
|
||||||
|
/* Message types for real time clock requests. */
|
||||||
|
#define RTCDEV_GET_TIME (RTCDEV_RQ_BASE + 0) /* get time from hw clock */
|
||||||
|
#define RTCDEV_SET_TIME (RTCDEV_RQ_BASE + 1) /* set time in hw clock */
|
||||||
|
#define RTCDEV_PWR_OFF (RTCDEV_RQ_BASE + 2) /* set time to cut the power */
|
||||||
|
|
||||||
|
/* Message types for real time clock responses. */
|
||||||
|
#define RTCDEV_REPLY (RTCDEV_RS_BASE + 0) /* general reply code */
|
||||||
|
|
||||||
|
/* Field names for real time clock messages */
|
||||||
|
#define RTCDEV_TM m2_p1 /* pointer to struct tm */
|
||||||
|
#define RTCDEV_FLAGS m2_s1 /* clock flags flags */
|
||||||
|
#define RTCDEV_STATUS m2_i2 /* OK or error code */
|
||||||
|
|
||||||
|
/* Bits in 'RTCDEV_FLAGS' field of real time clock requests. */
|
||||||
|
#define RTCDEV_NOFLAGS 0x00 /* no flags are set */
|
||||||
|
#define RTCDEV_Y2KBUG 0x01 /* Interpret 1980 as 2000 for RTC w/Y2K bug */
|
||||||
|
#define RTCDEV_CMOSREG 0x02 /* Also set the CMOS clock register bits. */
|
||||||
|
|
||||||
/* _MINIX_COM_H */
|
/* _MINIX_COM_H */
|
||||||
|
|
Loading…
Reference in a new issue