From 09db2a8c6721af76a2bd81d69648cf877070a501 Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Fri, 2 Aug 2013 10:10:30 -0400 Subject: [PATCH] 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 --- commands/readclock/Makefile | 5 +- commands/readclock/readclock.c | 191 ++++++++ commands/readclock/readclock.sh | 5 - distrib/sets/lists/minix/md.i386 | 1 - distrib/sets/lists/minix/mi | 1 + drivers/Makefile | 2 +- drivers/readclock/Makefile | 11 +- drivers/readclock/arch/earm/Makefile.inc | 10 + drivers/readclock/arch/earm/arch_readclock.c | 429 +++++++++++++++++ drivers/readclock/arch/earm/omap_rtc.h | 88 ++++ drivers/readclock/arch/i386/Makefile.inc | 7 + drivers/readclock/arch/i386/arch_readclock.c | 322 +++++++++++++ drivers/readclock/readclock.c | 474 ++++++------------- drivers/readclock/readclock.h | 22 + etc/rc | 14 +- etc/system.conf | 3 + include/minix/com.h | 30 ++ 17 files changed, 1261 insertions(+), 354 deletions(-) create mode 100644 commands/readclock/readclock.c delete mode 100644 commands/readclock/readclock.sh create mode 100644 drivers/readclock/arch/earm/Makefile.inc create mode 100644 drivers/readclock/arch/earm/arch_readclock.c create mode 100644 drivers/readclock/arch/earm/omap_rtc.h create mode 100644 drivers/readclock/arch/i386/Makefile.inc create mode 100644 drivers/readclock/arch/i386/arch_readclock.c create mode 100644 drivers/readclock/readclock.h diff --git a/commands/readclock/Makefile b/commands/readclock/Makefile index 813483e90..9ad094193 100644 --- a/commands/readclock/Makefile +++ b/commands/readclock/Makefile @@ -1,5 +1,8 @@ -SCRIPTS= readclock.sh +PROG= readclock +SRCS= readclock.c BINDIR= /bin + +# no man page here, it's handled in ../man/man8/ MAN= .include diff --git a/commands/readclock/readclock.c b/commands/readclock/readclock.c new file mode 100644 index 000000000..1b4d4120a --- /dev/null +++ b/commands/readclock/readclock.c @@ -0,0 +1,191 @@ +/* frontend to the readclock.drv driver for getting/setting hw clock. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/commands/readclock/readclock.sh b/commands/readclock/readclock.sh deleted file mode 100644 index ff23803bd..000000000 --- a/commands/readclock/readclock.sh +++ /dev/null @@ -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 diff --git a/distrib/sets/lists/minix/md.i386 b/distrib/sets/lists/minix/md.i386 index 6e01f485b..35d8762ec 100644 --- a/distrib/sets/lists/minix/md.i386 +++ b/distrib/sets/lists/minix/md.i386 @@ -23,7 +23,6 @@ ./sbin/at_wini minix-sys ./sbin/floppy minix-sys ./sbin/hgfs minix-sys -./sbin/readclock.drv minix-sys ./sbin/vbfs minix-sys ./sbin/virtio_blk minix-sys ./usr/bin/atnormalize minix-sys diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 3d88dc507..a327b646e 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -161,6 +161,7 @@ ./sbin/newfs_ext2 minix-sys ./sbin/newfs_ext2fs minix-sys ./sbin/procfs minix-sys +./sbin/readclock.drv minix-sys ./sys minix-sys ./tmp minix-sys ./usr minix-sys diff --git a/drivers/Makefile b/drivers/Makefile index 105310285..47a85480c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -23,7 +23,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \ .endif .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 # ${MKIMAGEONLY} != "yes" diff --git a/drivers/readclock/Makefile b/drivers/readclock/Makefile index d91e04815..10f978d49 100644 --- a/drivers/readclock/Makefile +++ b/drivers/readclock/Makefile @@ -1,14 +1,17 @@ # Makefile for readclock 'driver' PROG= readclock.drv -SRCS= readclock.c -DPADD+= ${LIBSYS} -LDADD+= -lsys +.include "arch/${MACHINE_ARCH}/Makefile.inc" + +SRCS+= readclock.c + +DPADD+= ${LIBSYS} ${LIBTIMERS} +LDADD+= -lsys -ltimers MAN= BINDIR?= /sbin -CPPFLAGS+= -D_SYSTEM=1 +CPPFLAGS+= -D_SYSTEM=1 -I${.CURDIR} .include diff --git a/drivers/readclock/arch/earm/Makefile.inc b/drivers/readclock/arch/earm/Makefile.inc new file mode 100644 index 000000000..37e9f3c04 --- /dev/null +++ b/drivers/readclock/arch/earm/Makefile.inc @@ -0,0 +1,10 @@ +# Makefile for arch-dependent readclock code +.include + +HERE=${.CURDIR}/arch/${MACHINE_ARCH} +.PATH: ${HERE} + +SRCS += arch_readclock.c omap_rtc.h + +DPADD+= ${CLKCONF} +LDADD+= -lclkconf diff --git a/drivers/readclock/arch/earm/arch_readclock.c b/drivers/readclock/arch/earm/arch_readclock.c new file mode 100644 index 000000000..509984b44 --- /dev/null +++ b/drivers/readclock/arch/earm/arch_readclock.c @@ -0,0 +1,429 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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<RTC_SS_RTC_STATUS_REG) & (1<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 */ +} diff --git a/drivers/readclock/arch/earm/omap_rtc.h b/drivers/readclock/arch/earm/omap_rtc.h new file mode 100644 index 000000000..d22cdbbf7 --- /dev/null +++ b/drivers/readclock/arch/earm/omap_rtc.h @@ -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 */ diff --git a/drivers/readclock/arch/i386/Makefile.inc b/drivers/readclock/arch/i386/Makefile.inc new file mode 100644 index 000000000..9aab16735 --- /dev/null +++ b/drivers/readclock/arch/i386/Makefile.inc @@ -0,0 +1,7 @@ +# Makefile for arch-dependent readclock code +.include + +HERE=${.CURDIR}/arch/${MACHINE_ARCH} +.PATH: ${HERE} + +SRCS += arch_readclock.c diff --git a/drivers/readclock/arch/i386/arch_readclock.c b/drivers/readclock/arch/i386/arch_readclock.c new file mode 100644 index 000000000..dc7e97d8f --- /dev/null +++ b/drivers/readclock/arch/i386/arch_readclock.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. */ +} diff --git a/drivers/readclock/readclock.c b/drivers/readclock/readclock.c index 70728b395..da01dc3db 100644 --- a/drivers/readclock/readclock.c +++ b/drivers/readclock/readclock.c @@ -1,43 +1,4 @@ -/* 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 */ -/************************************************************************/ - +/* readclock - manipulate the hardware real time clock */ #include #include @@ -47,343 +8,194 @@ #include #include #include +#include +#include #include #include #include -#include +#include +#include #include -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. */ +#include "readclock.h" -#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, - 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 - */ - -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); +/* functions for transfering struct tm to/from this driver and calling proc. */ +static int fetch_t(endpoint_t who_e, vir_bytes rtcdev_tm, struct tm *t); +static int store_t(endpoint_t who_e, vir_bytes rtcdev_tm, struct tm *t); /* SEF functions and variables. */ 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; - struct tm time2; - struct tm tmnow; - char date[64]; - time_t now, rtc; - int i, s; - unsigned char mach_id, cmos_state; + int r; + endpoint_t caller; + struct tm t; + message m; + int ipc_status, reply_status; - /* SEF local startup. */ - env_setargs(argc, argv); - sef_local_startup(); + env_setargs(argc, argv); + sef_local_startup(); - if((s=sys_readbios(MACH_ID_ADDR, &mach_id, sizeof(mach_id))) != OK) { - printf("readclock: sys_readbios failed: %d.\n", s); - exit(1); - } + while (TRUE) { - if (mach_id != PS_386 && mach_id != PC_AT) { - errmsg("Machine ID unknown." ); - printf("Machine ID byte = %02x\n", mach_id ); + /* Receive Message */ + r = sef_receive_status(ANY, &m, &ipc_status); + 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)) { - errmsg( "CMOS RAM error(s) found..." ); - printf("CMOS state = 0x%02x\n", cmos_state ); + caller = m.m_source; - if (cmos_state & CS_LOST_POWER) - 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); - } + log_debug(&log, "Got message 0x%x from 0x%x\n", m.m_type, caller); - /* Process options. */ - while (argc > 1) { - char *p = *++argv; + switch (m.m_type) { + case RTCDEV_GET_TIME: + /* 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) { - switch (*p++) { - case 'n': nflag = 1; break; - case 'w': wflag = 1; break; - case 'W': Wflag = 1; break; - case '2': y2kflag = 1; break; - default: usage(); + case RTCDEV_SET_TIME: + /* Only super user is allowed to set the time */ + if (getnuid(caller) == SUPER_USER) { + /* read time from calling process */ + reply_status = + fetch_t(caller, (vir_bytes) m.RTCDEV_TM, + &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. */ - for (i = 0; i < 10; i++) { - get_time(&time1); - now = time(NULL); + arch_exit(); + return 0; +} - time1.tm_isdst = -1; /* Do timezone calculations. */ - time2 = time1; +static int +sef_cb_init(int type, sef_init_info_t * UNUSED(info)) +{ + int r; - 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); - } + if (type == SEF_INIT_LU) { + /* Restore the state. */ + arch_lu_state_restore(); } - 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); + + r = arch_init(); + if (r != OK) { + return r; } - } 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); + + if (type != SEF_INIT_LU) { + /* Some RTCs need to do a driver announcement */ + arch_announce(); } - } - exit(0); + + return OK; } -/*===========================================================================* - * sef_local_startup * - *===========================================================================*/ -static void sef_local_startup() +static void +sef_local_startup() { - /* Let SEF perform startup. */ - sef_startup(); -} - -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.) + /* + * Register init callbacks. Use the same function for all event types */ - write_register(RTC_REG_A, RTC_A_DV_OK | RTC_A_RS_DEF); - write_register(RTC_REG_B, RTC_B_24); - } + sef_setcb_init_fresh(sef_cb_init); + sef_setcb_init_lu(sef_cb_init); + sef_setcb_init_restart(sef_cb_init); - /* Inhibit updates. */ - regB= read_register(RTC_REG_B); - write_register(RTC_REG_B, regB | RTC_B_SET); + /* + * Register live update callbacks. + */ + /* 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. */ - - 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); + /* Let SEF perform startup. */ + sef_startup(); } - -void write_register(int reg_addr, int value) +int +bcd_to_dec(int n) { - if(sys_outb(RTC_INDEX, reg_addr) != OK) { - 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); - } + return ((n >> 4) & 0x0F) * 10 + (n & 0x0F); } -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"); - exit(1); + return sys_datacopy(SELF, (vir_bytes) t, who_e, rtcdev_tm, + sizeof(struct tm)); } diff --git a/drivers/readclock/readclock.h b/drivers/readclock/readclock.h new file mode 100644 index 000000000..1ed99110c --- /dev/null +++ b/drivers/readclock/readclock.h @@ -0,0 +1,22 @@ +#ifndef __READCLOCK_H +#define __READCLOCK_H + +#include + +/* 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 */ diff --git a/etc/rc b/etc/rc index ea244857f..319676dc9 100755 --- a/etc/rc +++ b/etc/rc @@ -124,17 +124,9 @@ start) then . "$RC_TZ" fi - if [ $ARCH = i386 ] - then - # Try to read the hardware real-time clock, otherwise do it manually. - readclock || intr date -q - fi - - if [ $ARCH = earm ] - then - date 201301010000 - fi - + # Start real time clock driver & set system time, otherwise default date. + up readclock.drv + readclock || date 201301010000 # Initialize files. >/etc/utmp # /etc/utmp keeps track of logins diff --git a/etc/system.conf b/etc/system.conf index 9f1857c1c..92356994c 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -299,9 +299,12 @@ service random service readclock.drv { + ipc ALL; io 70:2; system + PRIVCTL # 4 UMAP # 14 + VIRCOPY # 15 DEVIO # 21 READBIOS # 35 ; diff --git a/include/minix/com.h b/include/minix/com.h index 9a2994d00..a7da571bb 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -27,6 +27,7 @@ * 0x1400 - 0x14FF VFS-FS transaction IDs * 0x1500 - 0x15FF Block device requests and responses * 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. */ @@ -1305,4 +1306,33 @@ #define RU_WHO m1_i1 /* who argument in getrusage call */ #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 */