]> Zhao Yanbai Git Server - minix.git/commitdiff
readclock: add support for am335x RTC 95/695/1
authorThomas Cort <tcort@minix3.org>
Fri, 2 Aug 2013 14:10:30 +0000 (10:10 -0400)
committerThomas Cort <tcort@minix3.org>
Mon, 5 Aug 2013 12:37:51 +0000 (08:37 -0400)
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

17 files changed:
commands/readclock/Makefile
commands/readclock/readclock.c [new file with mode: 0644]
commands/readclock/readclock.sh [deleted file]
distrib/sets/lists/minix/md.i386
distrib/sets/lists/minix/mi
drivers/Makefile
drivers/readclock/Makefile
drivers/readclock/arch/earm/Makefile.inc [new file with mode: 0644]
drivers/readclock/arch/earm/arch_readclock.c [new file with mode: 0644]
drivers/readclock/arch/earm/omap_rtc.h [new file with mode: 0644]
drivers/readclock/arch/i386/Makefile.inc [new file with mode: 0644]
drivers/readclock/arch/i386/arch_readclock.c [new file with mode: 0644]
drivers/readclock/readclock.c
drivers/readclock/readclock.h [new file with mode: 0644]
etc/rc
etc/system.conf
include/minix/com.h

index 813483e906af1524258ca34dc776b80fde6a0314..9ad0941936ca6200b28251ace88843cb81e32e9b 100644 (file)
@@ -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 <bsd.prog.mk>
diff --git a/commands/readclock/readclock.c b/commands/readclock/readclock.c
new file mode 100644 (file)
index 0000000..1b4d412
--- /dev/null
@@ -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);
+}
diff --git a/commands/readclock/readclock.sh b/commands/readclock/readclock.sh
deleted file mode 100644 (file)
index ff23803..0000000
+++ /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
index 6e01f485b16a48502509d5d4d73993a293c244fe..35d8762ecaf0e362f938d95554eda3ca44462253 100644 (file)
@@ -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
index 3d88dc507629466569829c485611af7398e39d39..a327b646ef6a00f071ae8f97bde1e5f7a44f76f8 100644 (file)
 ./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
index 1053102852fbf8cc96f7e460a55f7bf93cda9d43..47a85480cd39da4f6ff22d1f5ff6a5040eea6baf 100644 (file)
@@ -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"
index d91e048156c2873ce79b5cb5c85dc077abd27f8e..10f978d49f15f56f8de23c6dd83b4a49c89a89b9 100644 (file)
@@ -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 <minix.service.mk>
diff --git a/drivers/readclock/arch/earm/Makefile.inc b/drivers/readclock/arch/earm/Makefile.inc
new file mode 100644 (file)
index 0000000..37e9f3c
--- /dev/null
@@ -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 
diff --git a/drivers/readclock/arch/earm/arch_readclock.c b/drivers/readclock/arch/earm/arch_readclock.c
new file mode 100644 (file)
index 0000000..509984b
--- /dev/null
@@ -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 */
+}
diff --git a/drivers/readclock/arch/earm/omap_rtc.h b/drivers/readclock/arch/earm/omap_rtc.h
new file mode 100644 (file)
index 0000000..d22cdbb
--- /dev/null
@@ -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 (file)
index 0000000..9aab167
--- /dev/null
@@ -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
diff --git a/drivers/readclock/arch/i386/arch_readclock.c b/drivers/readclock/arch/i386/arch_readclock.c
new file mode 100644 (file)
index 0000000..dc7e97d
--- /dev/null
@@ -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. */
+}
index 70728b3958fb019065179a24c4247997071ae039..da01dc3db3b4c8464ec20a5fb5accb563c666d5b 100644 (file)
@@ -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 <sys/types.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <minix/type.h>
 #include <minix/const.h>
+#include <minix/callnr.h>
+#include <minix/log.h>
 #include <minix/syslib.h>
 #include <minix/sysutil.h>
 #include <minix/com.h>
-#include <machine/cmos.h>
+#include <minix/type.h>
+#include <minix/safecopies.h>
 #include <sys/svrctl.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. */
-
-#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
- */
-
-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);
+#include "readclock.h"
+
+static struct log log = {
+       .name = "readclock",
+       .log_level = LEVEL_INFO,
+       .log_func = default_log
+};
+
+/* 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;
-
-  /* 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);
-  }
-
-  if (mach_id != PS_386 && mach_id != PC_AT) {
-       errmsg("Machine ID unknown." );
-       printf("Machine ID byte = %02x\n", mach_id );
-
-       exit(1);
-  }
-
-  cmos_state = read_register(CMOS_STATUS);
-
-  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 );
-
-       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);
-  }
-
-  /* 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();
+       int r;
+       endpoint_t caller;
+       struct tm t;
+       message m;
+       int ipc_status, reply_status;
+
+       env_setargs(argc, argv);
+       sef_local_startup();
+
+       while (TRUE) {
+
+               /* Receive Message */
+               r = sef_receive_status(ANY, &m, &ipc_status);
+               if (r != OK) {
+                       log_warn(&log, "sef_receive_status() 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);
-
-       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);
-}
 
-/*===========================================================================*
- *                            sef_local_startup                             *
- *===========================================================================*/
-static void sef_local_startup()
-{
-  /* Let SEF perform startup. */
-  sef_startup();
-}
+               if (is_ipc_notify(ipc_status)) {
 
-void errmsg(char *s)
-{
-  static char *prompt = "readclock: ";
+                       /* Do not reply to notifications. */
+                       continue;
+               }
 
-  printf("%s%s\n", prompt, s);
-  prompt = "";
-}
+               caller = m.m_source;
+
+               log_debug(&log, "Got message 0x%x from 0x%x\n", m.m_type, caller);
+
+               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;
+                       }
+
+                       /* write results back to calling process */
+                       reply_status =
+                           store_t(caller, (vir_bytes) m.RTCDEV_TM, &t);
+                       break;
+
+               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;
 
-/***********************************************************************/
-/*                                                                     */
-/*    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.                                   */
-/*                                                                     */
-/***********************************************************************/
+               log_debug(&log, "Sending Reply");
 
-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++;
+               r = sendnb(caller, &m);
+               if (r != OK) {
+                       log_warn(&log, "sendnb() failed\n");
+                       continue;
                }
-       } 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;
-  }
-}
+       }
 
+       arch_exit();
+       return 0;
+}
 
-int read_register(int reg_addr)
+static int
+sef_cb_init(int type, sef_init_info_t * UNUSED(info))
 {
-  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;
-}
+       int r;
 
+       if (type == SEF_INIT_LU) {
+               /* Restore the state. */
+               arch_lu_state_restore();
+       }
 
+       r = arch_init();
+       if (r != OK) {
+               return r;
+       }
 
-/***********************************************************************/
-/*                                                                     */
-/*    set_time( time )                                                 */
-/*                                                                     */
-/*    Set the CMOS RTC to the time found in the structure.             */
-/*                                                                     */
-/***********************************************************************/
+       if (type != SEF_INIT_LU) {
+               /* Some RTCs need to do a driver announcement */
+               arch_announce();
+       }
 
-void set_time(struct tm *t)
+       return OK;
+}
+
+static void
+sef_local_startup()
 {
-  int regA, regB;
+       /*
+        * Register init callbacks. Use the same function for all event types
+        */
+       sef_setcb_init_fresh(sef_cb_init);
+       sef_setcb_init_lu(sef_cb_init);
+       sef_setcb_init_restart(sef_cb_init);
 
-  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 live update callbacks.
         */
-       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 (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);
+       /* 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);
+
+       /* 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 (file)
index 0000000..1ed9911
--- /dev/null
@@ -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 */
diff --git a/etc/rc b/etc/rc
index ea244857fcd6bb3060fbe696f5fc1310719e12e0..319676dc9d41b02a0ed2028651ecbaf03b389a8e 100755 (executable)
--- 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
index 9f1857c1c88db50b29f4d2b3ad4d1ac4d69df85d..92356994c39588325c2f8006090feb2c1fe80144 100644 (file)
@@ -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
        ;
index 9a2994d005e0441f8eec3665ca93af697b2e105e..a7da571bb3e5d48830b7cbf6eaa5b7fa756ab5c2 100644 (file)
@@ -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.
  */
 #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 */