subr_clock.c revision 116182
193835Stmm/* 293835Stmm * Copyright (c) 1988 University of Utah. 393835Stmm * Copyright (c) 1982, 1990, 1993 493835Stmm * The Regents of the University of California. All rights reserved. 593835Stmm * 693835Stmm * This code is derived from software contributed to Berkeley by 793835Stmm * the Systems Programming Group of the University of Utah Computer 893835Stmm * Science Department. 993835Stmm * 1093835Stmm * Redistribution and use in source and binary forms, with or without 1193835Stmm * modification, are permitted provided that the following conditions 1293835Stmm * are met: 1393835Stmm * 1. Redistributions of source code must retain the above copyright 1493835Stmm * notice, this list of conditions and the following disclaimer. 1593835Stmm * 2. Redistributions in binary form must reproduce the above copyright 1693835Stmm * notice, this list of conditions and the following disclaimer in the 1793835Stmm * documentation and/or other materials provided with the distribution. 1893835Stmm * 3. All advertising materials mentioning features or use of this software 1993835Stmm * must display the following acknowledgement: 2093835Stmm * This product includes software developed by the University of 2193835Stmm * California, Berkeley and its contributors. 2293835Stmm * 4. Neither the name of the University nor the names of its contributors 2393835Stmm * may be used to endorse or promote products derived from this software 2493835Stmm * without specific prior written permission. 2593835Stmm * 2693835Stmm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2793835Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2893835Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2993835Stmm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3093835Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3193835Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3293835Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3393835Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3493835Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3593835Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3693835Stmm * SUCH DAMAGE. 3793835Stmm * 3893835Stmm * from: Utah $Hdr: clock.c 1.18 91/01/21$ 3993835Stmm * from: @(#)clock.c 8.2 (Berkeley) 1/12/94 4093835Stmm * from: NetBSD: clock_subr.c,v 1.6 2001/07/07 17:04:02 thorpej Exp 4193835Stmm * and 4293835Stmm * from: src/sys/i386/isa/clock.c,v 1.176 2001/09/04 4393835Stmm */ 4493835Stmm 4593835Stmm/* 4693835Stmm * Helpers for time-of-day clocks. This is useful for architectures that need 4793835Stmm * support multiple models of such clocks, and generally serves to make the 4893835Stmm * code more machine-independent. 4993835Stmm * If the clock in question can also be used as a time counter, the driver 5093835Stmm * needs to initiate this. 5193835Stmm * This code is not yet used by all architectures. 5293835Stmm */ 5393835Stmm 5493835Stmm/* 5593835Stmm * Generic routines to convert between a POSIX date 5693835Stmm * (seconds since 1/1/1970) and yr/mo/day/hr/min/sec 5793835Stmm * Derived from NetBSD arch/hp300/hp300/clock.c 5893835Stmm */ 59116182Sobrien 60116182Sobrien#include <sys/cdefs.h> 61116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/subr_clock.c 116182 2003-06-11 00:56:59Z obrien $"); 62116182Sobrien 6393835Stmm#include <sys/param.h> 6493835Stmm#include <sys/systm.h> 6593835Stmm#include <sys/kernel.h> 6693835Stmm#include <sys/bus.h> 6793835Stmm#include <sys/clock.h> 6893835Stmm#include <sys/sysctl.h> 6993835Stmm#include <sys/timetc.h> 7093835Stmm 71101484Stmm/* XXX: for the CPU_* sysctl OID constants. */ 72101484Stmm#include <machine/cpu.h> 73101484Stmm 7493835Stmm#include "clock_if.h" 7593835Stmm 7693835Stmmstatic __inline int leapyear(int year); 7793835Stmmstatic int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS); 7893835Stmm 7993835Stmm#define FEBRUARY 2 8093835Stmm#define days_in_year(y) (leapyear(y) ? 366 : 365) 8193835Stmm#define days_in_month(y, m) \ 8293835Stmm (month_days[(m) - 1] + (m == FEBRUARY ? leapyear(y) : 0)) 8393835Stmm/* Day of week. Days are counted from 1/1/1970, which was a Thursday */ 8493835Stmm#define day_of_week(days) (((days) + 4) % 7) 8593835Stmm 8693835Stmmstatic const int month_days[12] = { 8793835Stmm 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 8893835Stmm}; 8993835Stmm 9093835Stmmstatic device_t clock_dev = NULL; 9193835Stmmstatic long clock_res; 9293835Stmm 9393835Stmmint adjkerntz; /* local offset from GMT in seconds */ 9493835Stmmint disable_rtc_set; /* disable resettodr() if != 0 */ 9593835Stmmint wall_cmos_clock; /* wall CMOS clock assumed if != 0 */ 9693835Stmm 9793835Stmm/* 9893835Stmm * These have traditionally been in machdep, but should probably be moved to 9993835Stmm * kern. 10093835Stmm */ 101101484StmmSYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, 10293835Stmm &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); 10393835Stmm 104101484StmmSYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, 10593835Stmm CTLFLAG_RW, &disable_rtc_set, 0, ""); 10693835Stmm 107101484StmmSYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, 10893835Stmm CTLFLAG_RW, &wall_cmos_clock, 0, ""); 10993835Stmm 11093835Stmmstatic int 11193835Stmmsysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) 11293835Stmm{ 11393835Stmm int error; 11493835Stmm error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, 11593835Stmm req); 11693835Stmm if (!error && req->newptr) 11793835Stmm resettodr(); 11893835Stmm return (error); 11993835Stmm} 12093835Stmm 12193835Stmm/* 12293835Stmm * This inline avoids some unnecessary modulo operations 12393835Stmm * as compared with the usual macro: 12493835Stmm * ( ((year % 4) == 0 && 12593835Stmm * (year % 100) != 0) || 12693835Stmm * ((year % 400) == 0) ) 12793835Stmm * It is otherwise equivalent. 12893835Stmm */ 12993835Stmmstatic __inline int 13093835Stmmleapyear(int year) 13193835Stmm{ 13293835Stmm int rv = 0; 13393835Stmm 13493835Stmm if ((year & 3) == 0) { 13593835Stmm rv = 1; 13693835Stmm if ((year % 100) == 0) { 13793835Stmm rv = 0; 13893835Stmm if ((year % 400) == 0) 13993835Stmm rv = 1; 14093835Stmm } 14193835Stmm } 14293835Stmm return (rv); 14393835Stmm} 14493835Stmm 14593835Stmmint 14693835Stmmclock_ct_to_ts(struct clocktime *ct, struct timespec *ts) 14793835Stmm{ 14893835Stmm time_t secs; 14993835Stmm int i, year, days; 15093835Stmm 15193835Stmm year = ct->year; 15293835Stmm 15393835Stmm /* Sanity checks. */ 15493835Stmm if (ct->mon < 1 || ct->mon > 12 || ct->day < 1 || 15593835Stmm ct->day > days_in_month(year, ct->mon) || 15693835Stmm ct->hour > 23 || ct->min > 59 || ct->sec > 59 || 15793835Stmm ct->year > 2037) /* time_t overflow */ 15893835Stmm return (EINVAL); 15993835Stmm 16093835Stmm /* 16193835Stmm * Compute days since start of time 16293835Stmm * First from years, then from months. 16393835Stmm */ 16493835Stmm days = 0; 16593835Stmm for (i = POSIX_BASE_YEAR; i < year; i++) 16693835Stmm days += days_in_year(i); 16793835Stmm 16893835Stmm /* Months */ 16993835Stmm for (i = 1; i < ct->mon; i++) 17093835Stmm days += days_in_month(year, i); 17193835Stmm days += (ct->day - 1); 17293835Stmm 17393835Stmm /* Another sanity check. */ 17493835Stmm if (ct->dow != -1 && ct->dow != day_of_week(days)) 17593835Stmm return (EINVAL); 17693835Stmm 17793835Stmm /* Add hours, minutes, seconds. */ 17893835Stmm secs = ((days * 24 + ct->hour) * 60 + ct->min) * 60 + ct->sec; 17993835Stmm 18093835Stmm ts->tv_sec = secs; 18193835Stmm ts->tv_nsec = ct->nsec; 18293835Stmm return (0); 18393835Stmm} 18493835Stmm 18593835Stmmvoid 18693835Stmmclock_ts_to_ct(struct timespec *ts, struct clocktime *ct) 18793835Stmm{ 18893835Stmm int i, year, days; 18993835Stmm time_t rsec; /* remainder seconds */ 19093835Stmm time_t secs; 19193835Stmm 19293835Stmm secs = ts->tv_sec; 19393835Stmm days = secs / SECDAY; 19493835Stmm rsec = secs % SECDAY; 19593835Stmm 19693835Stmm ct->dow = day_of_week(days); 19793835Stmm 19893835Stmm /* Subtract out whole years, counting them in i. */ 19993835Stmm for (year = POSIX_BASE_YEAR; days >= days_in_year(year); year++) 20093835Stmm days -= days_in_year(year); 20193835Stmm ct->year = year; 20293835Stmm 20393835Stmm /* Subtract out whole months, counting them in i. */ 20493835Stmm for (i = 1; days >= days_in_month(year, i); i++) 20593835Stmm days -= days_in_month(year, i); 20693835Stmm ct->mon = i; 20793835Stmm 20893835Stmm /* Days are what is left over (+1) from all that. */ 20993835Stmm ct->day = days + 1; 21093835Stmm 21193835Stmm /* Hours, minutes, seconds are easy */ 21293835Stmm ct->hour = rsec / 3600; 21393835Stmm rsec = rsec % 3600; 21493835Stmm ct->min = rsec / 60; 21593835Stmm rsec = rsec % 60; 21693835Stmm ct->sec = rsec; 21793835Stmm ct->nsec = ts->tv_nsec; 21893835Stmm} 21993835Stmm 22093835Stmmvoid 22193835Stmmclock_register(device_t dev, long res) 22293835Stmm{ 22393835Stmm 22493835Stmm if (clock_dev != NULL) { 22593835Stmm if (clock_res > res) { 22693835Stmm if (bootverbose) { 22793835Stmm device_printf(dev, "not installed as " 22893835Stmm "time-of-day clock: clock %s has higher " 22993835Stmm "resolution\n", device_get_name(clock_dev)); 23093835Stmm } 23193835Stmm return; 23293835Stmm } else { 23393835Stmm if (bootverbose) { 23493835Stmm device_printf(clock_dev, "removed as " 23593835Stmm "time-of-day clock: clock %s has higher " 23693835Stmm "resolution\n", device_get_name(dev)); 23793835Stmm } 23893835Stmm } 23993835Stmm } 24093835Stmm clock_dev = dev; 24193835Stmm clock_res = res; 24293835Stmm if (bootverbose) { 24393835Stmm device_printf(dev, "registered as a time-of-day clock " 24493835Stmm "(resolution %ldus)\n", res); 24593835Stmm } 24693835Stmm} 24793835Stmm 24893835Stmm/* 24993835Stmm * inittodr and settodr derived from the i386 versions written 25093835Stmm * by Christoph Robitschko <chmr@edvz.tu-graz.ac.at>, reintroduced and 25193835Stmm * updated by Chris Stenton <chris@gnome.co.uk> 8/10/94 25293835Stmm */ 25393835Stmm 25493835Stmm/* 25593835Stmm * Initialize the time of day register, based on the time base which is, e.g. 25693835Stmm * from a filesystem. 25793835Stmm */ 25893835Stmmvoid 25993835Stmminittodr(time_t base) 26093835Stmm{ 26193835Stmm struct timespec diff, ref, ts; 26293835Stmm int error; 26393835Stmm 26493835Stmm if (base) { 26593835Stmm ref.tv_sec = base; 26693835Stmm ref.tv_nsec = 0; 26793835Stmm tc_setclock(&ref); 26893835Stmm } 26993835Stmm 27093835Stmm if (clock_dev == NULL) { 27193835Stmm printf("warning: no time-of-day clock registered, system time " 27293835Stmm "will not be set accurately\n"); 27393835Stmm return; 27493835Stmm } 27593835Stmm error = CLOCK_GETTIME(clock_dev, &ts); 27693835Stmm if (error != 0 && error != EINVAL) { 27793835Stmm printf("warning: clock_gettime failed (%d), the system time " 27893835Stmm "will not be set accurately\n", error); 27993835Stmm return; 28093835Stmm } 28193835Stmm if (error == EINVAL || ts.tv_sec < 0) { 28293835Stmm printf("Invalid time in real time clock.\n"); 28393835Stmm printf("Check and reset the date immediately!\n"); 28493835Stmm } 28593835Stmm 286110299Sphk ts.tv_sec += tz_minuteswest * 60 + 28793835Stmm (wall_cmos_clock ? adjkerntz : 0); 28893835Stmm 28993835Stmm if (timespeccmp(&ref, &ts, >)) { 29093835Stmm diff = ref; 29193835Stmm timespecsub(&ref, &ts); 29293835Stmm } else { 29393835Stmm diff = ts; 29493835Stmm timespecsub(&diff, &ref); 29593835Stmm } 29693835Stmm if (ts.tv_sec >= 2) { 29793835Stmm /* badly off, adjust it */ 29893835Stmm tc_setclock(&ts); 29993835Stmm } 30093835Stmm} 30193835Stmm 30293835Stmm/* 30393835Stmm * Write system time back to RTC 30493835Stmm */ 30593835Stmmvoid 30693835Stmmresettodr() 30793835Stmm{ 30893835Stmm struct timespec ts; 30993835Stmm int error; 31093835Stmm 31193835Stmm if (disable_rtc_set || clock_dev == NULL) 31293835Stmm return; 31393835Stmm 31493835Stmm getnanotime(&ts); 315110299Sphk ts.tv_sec -= tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); 31693835Stmm if ((error = CLOCK_SETTIME(clock_dev, &ts)) != 0) { 31793835Stmm printf("warning: clock_settime failed (%d), time-of-day clock " 31893835Stmm "not adjusted to system time\n", error); 31993835Stmm return; 32093835Stmm } 32193835Stmm} 322