NetBSD/sys/arch/arm32/ofw/oftodclock.c

285 lines
6.9 KiB
C

/* $NetBSD: oftodclock.c,v 1.4 1998/07/07 00:48:12 mark Exp $ */
/*
* Copyright 1997
* Digital Equipment Corporation. All rights reserved.
*
* This software is furnished under license and may be used and
* copied only in accordance with the following terms and conditions.
* Subject to these conditions, you may download, copy, install,
* use, modify and distribute this software in source and/or binary
* form. No title or ownership is transferred hereby.
*
* 1) Any source code used, modified or distributed must reproduce
* and retain this copyright notice and list of conditions as
* they appear in the source file.
*
* 2) No right is granted to use any trade name, trademark, or logo of
* Digital Equipment Corporation. Neither the "Digital Equipment
* Corporation" name nor any trademark or logo of Digital Equipment
* Corporation may be used to endorse or promote products derived
* from this software without the prior written permission of
* Digital Equipment Corporation.
*
* 3) This software is provided "AS-IS" and any express or implied
* warranties, including but not limited to, any implied warranties
* of merchantability, fitness for a particular purpose, or
* non-infringement are disclaimed. In no event shall DIGITAL be
* liable for any damages whatsoever, and in particular, DIGITAL
* shall not be liable for special, indirect, consequential, or
* incidental damages or damages for lost profits, loss of
* revenue or loss of use, whether such damages arise in contract,
* negligence, tort, under statute, in equity, at law or otherwise,
* even if advised of the possibility of such damage.
*/
/*
* Real time clock (RTC) functions using OFW /rtc device:
* inittodr()
* resettodr()
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <dev/ofw/openfirm.h>
/* The OFW RTC interface, straight from wmb:
selftest ( -- error? )
Designed to be called from the "test" user interface command, e.g.
test /rtc
which displays a message if the test fails.
Basically all this does is to check the battery.
check-battery ( -- error? )
Called from an open instance; returns non-zero if the battery
is dead. Also displays a message to that effect, which it probably
should not do.
set-time ( s m h d m y -- )
get-time ( -- s m h d m y )
Called from an open instance.
seconds: 0-59
minutes: 0-59
hours: 0-23
day: 1-31
month: 1-12
year: e.g. 1997
*/
#define OFRTC_SEC 0
#define OFRTC_MIN 1
#define OFRTC_HR 2
#define OFRTC_DOM 3
#define OFRTC_MON 4
#define OFRTC_YR 5
static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
#define SECMIN ((unsigned)60) /* seconds per minute */
#define SECHOUR ((unsigned)(60*SECMIN)) /* seconds per hour */
#define SECDAY ((unsigned)(24*SECHOUR)) /* seconds per day */
#define SECYR ((unsigned)(365*SECDAY)) /* seconds per common year */
static int
yeartoday(year)
int year;
{
return ((year % 4) ? 365 : 366);
}
static int timeset = 0;
static void
setthetime(thetime, warning)
time_t thetime;
int warning;
{
timeset = 1;
time.tv_sec = thetime;
time.tv_usec = 0;
if (warning)
printf("WARNING: CHECK AND RESET THE DATE!\n");
}
static int ofrtc_phandle;
static int ofrtc_ihandle = 0;
static int ofrtcinited = 0;
static void
ofrtcinit(void)
{
char buf[256];
int l;
int chosen;
if (ofrtcinited) return;
if ((ofrtc_phandle = OF_finddevice("/rtc")) == -1)
panic("OFW RTC: no package");
if ((l = OF_getprop(ofrtc_phandle, "device_type", buf, sizeof buf - 1)) < 0)
panic("OFW RTC: no device type");
if ((l >= sizeof buf) || strcmp(buf, "rtc"))
panic("OFW RTC: bad device type");
if ((chosen = OF_finddevice("/chosen")) == -1 ||
OF_getprop(chosen, "clock", &ofrtc_ihandle, sizeof(int)) < 0) {
ofrtc_ihandle = 0;
return;
}
ofrtc_ihandle = of_decode_int((unsigned char *)&ofrtc_ihandle);
ofrtcinited = 1;
}
static int
ofrtcstatus(void)
{
char status[256];
int l;
if ((ofrtc_ihandle == 0) || (l = OF_getprop(ofrtc_phandle, "status",
status, sizeof status - 1)) < 0) {
printf("OFW RTC: old firmware does not support RTC\n");
return 0;
}
status[sizeof status - 1] = 0; /* always null terminate */
if (strcmp(status, "okay")) { /* something is wrong */
printf("RTC: %s\n", status);
return 0;
}
return 1; /* all systems are go */
}
/*
* Initialize the time of day register, based on the time base which is, e.g.
* from a filesystem.
*/
void
inittodr(base)
time_t base;
{
time_t n;
int i, days = 0;
int date[6];
int yr;
/*
* We mostly ignore the suggested time and go for the RTC clock time
* stored in the CMOS RAM. If the time can't be obtained from the
* CMOS, or if the time obtained from the CMOS is 5 or more years
* less than the suggested time, we used the suggested time. (In
* the latter case, it's likely that the CMOS battery has died.)
*/
if (base < 25*SECYR) { /* if before 1995, something's odd... */
printf("WARNING: preposterous time in file system\n");
/* read the system clock anyway */
/* happy b-day sarina */
base = 26*SECYR + 24*SECDAY + 18 * SECHOUR + 58 * SECMIN;
}
ofrtcinit();
if (!ofrtcstatus()) {
setthetime(base, 1);
return;
}
if (OF_call_method("get-time", ofrtc_ihandle, 0, 6,
date, date + 1, date + 2, date + 3, date + 4, date + 5)) {
printf("OFW RTC: get-time failed\n");
setthetime(base, 1);
return;
}
n = date[OFRTC_SEC];
n += date[OFRTC_MIN] * SECMIN;
n += date[OFRTC_HR] * SECHOUR;
n += (date[OFRTC_DOM] - 1) * SECDAY;
yr = date[OFRTC_YR];
if (yeartoday(yr) == 366)
month[1] = 29;
for (i = date[OFRTC_MON] - 2; i >= 0; i--)
days += month[i];
month[1] = 28;
for (i = 1970; i < yr; i++)
days += yeartoday(i);
n += days * 3600 * 24;
n += rtc_offset * 60;
if (base < n - 5*SECYR)
printf("WARNING: file system time much less than clock time\n");
else if (base > n + 5*SECYR) {
printf("WARNING: clock time much less than file system time\n");
printf("WARNING: using file system time\n");
setthetime(base, 1);
return;
}
setthetime(n, 0);
}
/*
* Reset the clock.
*/
void
resettodr(void)
{
time_t n;
int diff, i, j;
int sec, min, hr, dom, mon, yr;
/* old version of the firmware? */
if (ofrtc_ihandle == 0) return;
/*
* We might have been called by boot() due to a crash early
* on. Don't reset the clock chip in this case.
*/
if (!timeset)
return;
diff = rtc_offset * 60;
n = (time.tv_sec - diff) % (3600 * 24); /* hrs+mins+secs */
sec = n % 60;
n /= 60;
min = n % 60;
hr = n / 60;
n = (time.tv_sec - diff) / (3600 * 24); /* days */
for (j = 1970, i = yeartoday(j); n >= i; j++, i = yeartoday(j))
n -= i;
yr = j;
if (i == 366)
month[1] = 29;
for (i = 0; n >= month[i]; i++)
n -= month[i];
month[1] = 28;
mon = ++i;
dom = ++n;
if (OF_call_method("set-time", ofrtc_ihandle, 6, 0,
sec, min, hr, dom, mon, yr))
printf("OFW RTC: set-time failed\n");
}