153981Sfujita /* 253981Sfujita * Copyright (c) 1988 University of Utah. 353981Sfujita * Copyright (c) 1992 OMRON Corporation. 453981Sfujita * Copyright (c) 1982, 1990, 1992 The Regents of the University of California. 553981Sfujita * All rights reserved. 653981Sfujita * 753981Sfujita * This code is derived from software contributed to Berkeley by 853981Sfujita * the Systems Programming Group of the University of Utah Computer 953981Sfujita * Science Department. 1053981Sfujita * 1153981Sfujita * %sccs.include.redist.c% 1253981Sfujita * 1353981Sfujita * from: Utah $Hdr: clock.c 1.18 91/01/21$ 1453981Sfujita * OMRON: $Id: clock.c,v 1.1 92/05/27 14:24:06 moti Exp $ 1553981Sfujita * 1655581Sfujita * from: hp300/hp300/clock.c 7.14 (Berkeley) 7/8/92 1753981Sfujita * 18*56149Sakito * @(#)clock.c 7.3 (Berkeley) 09/01/92 1953981Sfujita */ 2053981Sfujita 2153981Sfujita #include "param.h" 2253981Sfujita #include "kernel.h" 2353981Sfujita #include "clockreg.h" 2453981Sfujita 2555581Sfujita extern int clock_on; 2655581Sfujita 2753981Sfujita static int month_days[12] = { 2853981Sfujita 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 2953981Sfujita }; 3053981Sfujita struct bbc_tm *gmt_to_bbc(); 3153981Sfujita 3253981Sfujita volatile struct bbc *bbc = (struct bbc *)BBC_ADDR; 3353981Sfujita 3453981Sfujita int battery_clock; 3553981Sfujita int battery_chkfg; 3653981Sfujita 3753981Sfujita /* 3853981Sfujita * Machine-dependent clock routines. 3953981Sfujita * 4053981Sfujita * Startrtclock just checks battry backuped clock 4153981Sfujita * (when it does not work, starts it). 4253981Sfujita * 4353981Sfujita * Enablertclock sets flag for clock interrupt. 4453981Sfujita * 4553981Sfujita * Inittodr initializes the time of day hardware which provides 4653981Sfujita * date functions. 4753981Sfujita * 4853981Sfujita * Resettodr restores the time of day hardware after a time change. 4953981Sfujita * 5053981Sfujita */ 5153981Sfujita 5253981Sfujita /* 5353981Sfujita * Start the real-time clock. 5453981Sfujita */ 5555581Sfujita cpu_initclocks() 5653981Sfujita { 5753981Sfujita static char *rtcstrings = "RTC"; /* For compat */ 5853981Sfujita 59*56149Sakito /* set flag for clockintr. */ 60*56149Sakito clock_on = 1; 61*56149Sakito 6253981Sfujita batterychk(); 6353981Sfujita if (!battery_clock) 6453981Sfujita return; 6553981Sfujita 6653981Sfujita if (!strncmp(bbc->nvram.nv_calclock, rtcstrings, sizeof(rtcstrings))) /* Okey */ 6753981Sfujita return; 6853981Sfujita 6953981Sfujita printf("Initialize Battery Backup Clock.\n"); 7053981Sfujita bbc->cal_ctl |= BBC_WRT; 7153981Sfujita bbc->cal_sec &= ~BBC_STOP; 7253981Sfujita bbc->cal_hour |= BBC_KICK; 7353981Sfujita bbc->cal_dow &= ~BBC_FRQ; 7453981Sfujita bbc->cal_ctl &= ~BBC_WRT; 7553981Sfujita DELAY(BBC_DELAY); 7653981Sfujita bbc->cal_ctl |= BBC_WRT; 7753981Sfujita bbc->cal_hour &= ~BBC_KICK; 7853981Sfujita bbc->cal_ctl &= ~BBC_WRT; 7953981Sfujita strcpy(bbc->nvram,rtcstrings); 8053981Sfujita } 8153981Sfujita 8255581Sfujita void 8355581Sfujita setstatclockrate(newhz) 8455581Sfujita int newhz; 8553981Sfujita { 8655581Sfujita } 8753981Sfujita 8855581Sfujita microtime(tvp) 8955581Sfujita register struct timeval *tvp; 9055581Sfujita { 9155581Sfujita int s = splhigh(); 9255581Sfujita 9355581Sfujita *tvp = time; 9455581Sfujita tvp->tv_usec += tick; 9555581Sfujita while (tvp->tv_usec > 1000000) { 9655581Sfujita tvp->tv_sec++; 9755581Sfujita tvp->tv_usec -= 1000000; 9855581Sfujita } 9955581Sfujita splx(s); 10053981Sfujita } 10153981Sfujita 10253981Sfujita /* 10353981Sfujita * Initialize the time of day register, based on the time base which is, e.g. 10453981Sfujita * from a filesystem. 10553981Sfujita */ 10653981Sfujita inittodr(base) 10753981Sfujita time_t base; 10853981Sfujita { 10953981Sfujita u_long timbuf = base; /* assume no battery clock exists */ 11053981Sfujita 11153981Sfujita /* 11253981Sfujita * bbc_to_gmt converts and stores the gmt in timbuf. 11353981Sfujita * If an error is detected in bbc_to_gmt, or if the filesystem 11453981Sfujita * time is more recent than the gmt time in the clock, 11553981Sfujita * then use the filesystem time and warn the user. 11653981Sfujita */ 11753981Sfujita if (!bbc_to_gmt(&timbuf) || timbuf < base) { 11853981Sfujita printf("WARNING: bad date in battery clock\n"); 11953981Sfujita timbuf = base; 12053981Sfujita } 12153981Sfujita if (base < 5*SECYR) { 12253981Sfujita printf("WARNING: preposterous time in file system"); 12353981Sfujita timbuf = 6*SECYR + 186*SECDAY + SECDAY/2; 12453981Sfujita printf(" -- CHECK AND RESET THE DATE!\n"); 12553981Sfujita } 12653981Sfujita 12753981Sfujita /* Battery clock does not store usec's, so forget about it. */ 12853981Sfujita time.tv_sec = timbuf; 12953981Sfujita } 13053981Sfujita 13153981Sfujita resettodr() 13253981Sfujita { 13353981Sfujita register int i,s; 13453981Sfujita register struct bbc_tm *tmptr; 13553981Sfujita 13653981Sfujita tmptr = gmt_to_bbc(time.tv_sec); 13753981Sfujita 13853981Sfujita s = splimp(); 13953981Sfujita 14053981Sfujita /* set bb-clock */ 14153981Sfujita bbc->cal_ctl |= BBC_WRT; 14253981Sfujita bbc->cal_sec = binary_to_bcd(tmptr->tm_sec); 14353981Sfujita bbc->cal_min = binary_to_bcd(tmptr->tm_min); 14453981Sfujita bbc->cal_hour = binary_to_bcd(tmptr->tm_hour); 14553981Sfujita bbc->cal_day = binary_to_bcd(tmptr->tm_mday); 14653981Sfujita bbc->cal_mon = binary_to_bcd(tmptr->tm_mon); 14753981Sfujita bbc->cal_year = binary_to_bcd(tmptr->tm_year); 14853981Sfujita bbc->cal_ctl &= ~BBC_WRT; 14953981Sfujita 15053981Sfujita splx(s); 15153981Sfujita } 15253981Sfujita 15353981Sfujita struct bbc_tm * 15453981Sfujita gmt_to_bbc(tim) 15553981Sfujita long tim; 15653981Sfujita { 15753981Sfujita register int i; 15853981Sfujita register long hms, day; 15953981Sfujita static struct bbc_tm rt; 16053981Sfujita 16153981Sfujita day = tim / SECDAY; 16253981Sfujita hms = tim % SECDAY; 16353981Sfujita 16453981Sfujita /* Hours, minutes, seconds are easy */ 16553981Sfujita rt.tm_hour = hms / 3600; 16653981Sfujita rt.tm_min = (hms % 3600) / 60; 16753981Sfujita rt.tm_sec = (hms % 3600) % 60; 16853981Sfujita 16953981Sfujita /* Number of years in days */ 17053981Sfujita for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++) 17153981Sfujita day -= days_in_year(i); 17253981Sfujita rt.tm_year = i; 17353981Sfujita 17453981Sfujita /* Number of months in days left */ 17553981Sfujita if (leapyear(rt.tm_year)) 17653981Sfujita days_in_month(FEBRUARY) = 29; 17753981Sfujita for (i = 1; day >= days_in_month(i); i++) 17853981Sfujita day -= days_in_month(i); 17953981Sfujita days_in_month(FEBRUARY) = 28; 18053981Sfujita rt.tm_mon = i; 18153981Sfujita 18253981Sfujita /* Days are what is left over (+1) from all that. */ 18353981Sfujita rt.tm_mday = day + 1; 18453981Sfujita 18553981Sfujita return(&rt); 18653981Sfujita } 18753981Sfujita 18853981Sfujita bbc_to_gmt(timbuf) 18953981Sfujita u_long *timbuf; 19053981Sfujita { 19153981Sfujita register int i,s; 19253981Sfujita register u_long tmp; 19353981Sfujita int year, month, day, hour, min, sec; 19453981Sfujita 19553981Sfujita if (!battery_clock) 19653981Sfujita return(0); 19753981Sfujita 19853981Sfujita s = splimp(); 19953981Sfujita 20053981Sfujita /* read bb-clock */ 20153981Sfujita bbc->cal_ctl |= BBC_RD; 20253981Sfujita sec = bcd_to_binary(bbc->cal_sec); 20353981Sfujita min = bcd_to_binary(bbc->cal_min); 20453981Sfujita hour = bcd_to_binary(bbc->cal_hour); 20553981Sfujita day = bcd_to_binary(bbc->cal_day); 20653981Sfujita month = bcd_to_binary(bbc->cal_mon); 20753981Sfujita year = bcd_to_binary(bbc->cal_year) + 1900; 20853981Sfujita bbc->cal_ctl &= ~BBC_RD; 20953981Sfujita 21053981Sfujita splx(s); 21153981Sfujita 21253981Sfujita range_test(hour, 0, 23); 21353981Sfujita range_test(day, 1, 31); 21453981Sfujita range_test(month, 1, 12); 21553981Sfujita range_test(year, STARTOFTIME, 2000); 21653981Sfujita 21753981Sfujita tmp = 0; 21853981Sfujita 21953981Sfujita for (i = STARTOFTIME; i < year; i++) 22053981Sfujita tmp += days_in_year(i); 22153981Sfujita if (leapyear(year) && month > FEBRUARY) 22253981Sfujita tmp++; 22353981Sfujita 22453981Sfujita for (i = 1; i < month; i++) 22553981Sfujita tmp += days_in_month(i); 22653981Sfujita 22753981Sfujita tmp += (day - 1); 22853981Sfujita tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec; 22953981Sfujita 23053981Sfujita *timbuf = tmp; 23153981Sfujita return(1); 23253981Sfujita } 23353981Sfujita 23453981Sfujita batterychk() 23553981Sfujita { 23653981Sfujita static char btchkdata[] = "chk"; 23753981Sfujita 23853981Sfujita /* if already checked, return */ 23953981Sfujita if (battery_chkfg) 24053981Sfujita return; 24153981Sfujita 24253981Sfujita battery_chkfg = 1; 24353981Sfujita if (badaddr((caddr_t)bbc, 2)) 24453981Sfujita return; 24553981Sfujita 24653981Sfujita strcpy(bbc->nvram.nv_testwrite, btchkdata); 24753981Sfujita if (strncmp(bbc->nvram.nv_testwrite, btchkdata, sizeof(btchkdata))) { 24853981Sfujita printf("WARNING: calendar clock battery down\n"); 24953981Sfujita return; 25053981Sfujita } 25153981Sfujita battery_clock = 1; 25253981Sfujita return; 25353981Sfujita } 254