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