1ae115bc7Smrj /* 2ae115bc7Smrj * CDDL HEADER START 3ae115bc7Smrj * 4ae115bc7Smrj * The contents of this file are subject to the terms of the 5ae115bc7Smrj * Common Development and Distribution License (the "License"). 6ae115bc7Smrj * You may not use this file except in compliance with the License. 7ae115bc7Smrj * 8ae115bc7Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9ae115bc7Smrj * or http://www.opensolaris.org/os/licensing. 10ae115bc7Smrj * See the License for the specific language governing permissions 11ae115bc7Smrj * and limitations under the License. 12ae115bc7Smrj * 13ae115bc7Smrj * When distributing Covered Code, include this CDDL HEADER in each 14ae115bc7Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15ae115bc7Smrj * If applicable, add the following below this CDDL HEADER, with the 16ae115bc7Smrj * fields enclosed by brackets "[]" replaced with your own identifying 17ae115bc7Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 18ae115bc7Smrj * 19ae115bc7Smrj * CDDL HEADER END 20ae115bc7Smrj */ 21*8500cbf9SDan Kruchinin /* 22*8500cbf9SDan Kruchinin * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 23*8500cbf9SDan Kruchinin */ 24ae115bc7Smrj /* 258fc99e42STrevor Thompson * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 26ae115bc7Smrj * Use is subject to license terms. 27ae115bc7Smrj */ 28ae115bc7Smrj 29ae115bc7Smrj /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 30ae115bc7Smrj /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 31ae115bc7Smrj /* All Rights Reserved */ 32ae115bc7Smrj 33ae115bc7Smrj /* Copyright (c) 1987, 1988 Microsoft Corporation */ 34ae115bc7Smrj /* All Rights Reserved */ 35ae115bc7Smrj 36ae115bc7Smrj #include <sys/param.h> 37ae115bc7Smrj #include <sys/time.h> 38ae115bc7Smrj #include <sys/systm.h> 39ae115bc7Smrj 40ae115bc7Smrj #include <sys/cpuvar.h> 41ae115bc7Smrj #include <sys/clock.h> 42ae115bc7Smrj #include <sys/debug.h> 43ae115bc7Smrj #include <sys/rtc.h> 44ae115bc7Smrj #include <sys/archsystm.h> 45ae115bc7Smrj #include <sys/sysmacros.h> 46ae115bc7Smrj #include <sys/lockstat.h> 47ae115bc7Smrj #include <sys/stat.h> 48ae115bc7Smrj #include <sys/sunddi.h> 49ae115bc7Smrj 502df1fe9cSrandyf #include <sys/acpi/acpi.h> 512df1fe9cSrandyf #include <sys/acpica.h> 522df1fe9cSrandyf 53ae115bc7Smrj static int todpc_rtcget(unsigned char *buf); 54ae115bc7Smrj static void todpc_rtcput(unsigned char *buf); 55ae115bc7Smrj 562df1fe9cSrandyf #define CLOCK_RES 1000 /* 1 microsec in nanosecs */ 572df1fe9cSrandyf 582df1fe9cSrandyf int clock_res = CLOCK_RES; 592df1fe9cSrandyf 602df1fe9cSrandyf /* 612df1fe9cSrandyf * The minimum sleep time till an alarm can be fired. 622df1fe9cSrandyf * This can be tuned in /etc/system, but if the value is too small, 632df1fe9cSrandyf * there is a danger that it will be missed if it takes too long to 642df1fe9cSrandyf * get from the set point to sleep. Or that it can fire quickly, and 652df1fe9cSrandyf * generate a power spike on the hardware. And small values are 662df1fe9cSrandyf * probably only usefull for test setups. 672df1fe9cSrandyf */ 682df1fe9cSrandyf int clock_min_alarm = 4; 692df1fe9cSrandyf 70ae115bc7Smrj /* 71ae115bc7Smrj * Machine-dependent clock routines. 72ae115bc7Smrj */ 73ae115bc7Smrj 742df1fe9cSrandyf extern long gmt_lag; 752df1fe9cSrandyf 762df1fe9cSrandyf struct rtc_offset { 772df1fe9cSrandyf int8_t loaded; 782df1fe9cSrandyf uint8_t day_alrm; 792df1fe9cSrandyf uint8_t mon_alrm; 802df1fe9cSrandyf uint8_t century; 812df1fe9cSrandyf }; 822df1fe9cSrandyf 832df1fe9cSrandyf static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0}; 842df1fe9cSrandyf 852df1fe9cSrandyf 862df1fe9cSrandyf /* 872df1fe9cSrandyf * Entry point for ACPI to pass RTC or other clock values that 882df1fe9cSrandyf * are useful to TOD. 892df1fe9cSrandyf */ 902df1fe9cSrandyf void 91db2bae30SDana Myers pc_tod_set_rtc_offsets(ACPI_TABLE_FADT *fadt) { 922df1fe9cSrandyf int ok = 0; 932df1fe9cSrandyf 942df1fe9cSrandyf /* 952df1fe9cSrandyf * ASSERT is for debugging, but we don't want the machine 962df1fe9cSrandyf * falling over because for some reason we didn't get a valid 972df1fe9cSrandyf * pointer. 982df1fe9cSrandyf */ 992df1fe9cSrandyf ASSERT(fadt); 1002df1fe9cSrandyf if (fadt == NULL) { 1012df1fe9cSrandyf return; 1022df1fe9cSrandyf } 1032df1fe9cSrandyf 104db2bae30SDana Myers if (fadt->DayAlarm) { 105db2bae30SDana Myers pc_rtc_offset.day_alrm = fadt->DayAlarm; 1062df1fe9cSrandyf ok = 1; 1072df1fe9cSrandyf } 1082df1fe9cSrandyf 109db2bae30SDana Myers if (fadt->MonthAlarm) { 110db2bae30SDana Myers pc_rtc_offset.mon_alrm = fadt->MonthAlarm; 1112df1fe9cSrandyf ok = 1; 1122df1fe9cSrandyf } 1132df1fe9cSrandyf 1142df1fe9cSrandyf if (fadt->Century) { 1152df1fe9cSrandyf pc_rtc_offset.century = fadt->Century; 1162df1fe9cSrandyf ok = 1; 1172df1fe9cSrandyf } 1182df1fe9cSrandyf 1192df1fe9cSrandyf pc_rtc_offset.loaded = ok; 1202df1fe9cSrandyf } 1212df1fe9cSrandyf 1222df1fe9cSrandyf 123ae115bc7Smrj /* 124ae115bc7Smrj * Write the specified time into the clock chip. 125ae115bc7Smrj * Must be called with tod_lock held. 126ae115bc7Smrj */ 127ae115bc7Smrj /*ARGSUSED*/ 128ae115bc7Smrj static void 129ae115bc7Smrj todpc_set(tod_ops_t *top, timestruc_t ts) 130ae115bc7Smrj { 131ae115bc7Smrj todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl()); 132ae115bc7Smrj struct rtc_t rtc; 133ae115bc7Smrj 134ae115bc7Smrj ASSERT(MUTEX_HELD(&tod_lock)); 135ae115bc7Smrj 136ae115bc7Smrj if (todpc_rtcget((unsigned char *)&rtc)) 137ae115bc7Smrj return; 138ae115bc7Smrj 139ae115bc7Smrj /* 140ae115bc7Smrj * rtc bytes are in binary-coded decimal, so we have to convert. 141ae115bc7Smrj * We assume that we wrap the rtc year back to zero at 2000. 142ae115bc7Smrj */ 143ae115bc7Smrj /* LINTED: YRBASE = 0 for x86 */ 144ae115bc7Smrj tod.tod_year -= YRBASE; 145ae115bc7Smrj if (tod.tod_year >= 100) { 146ae115bc7Smrj tod.tod_year -= 100; 147ae115bc7Smrj rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */ 148ae115bc7Smrj } else 149ae115bc7Smrj rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */ 150ae115bc7Smrj rtc.rtc_yr = BYTE_TO_BCD(tod.tod_year); 151ae115bc7Smrj rtc.rtc_mon = BYTE_TO_BCD(tod.tod_month); 152ae115bc7Smrj rtc.rtc_dom = BYTE_TO_BCD(tod.tod_day); 153ae115bc7Smrj /* dow < 10, so no conversion */ 154ae115bc7Smrj rtc.rtc_dow = (unsigned char)tod.tod_dow; 155ae115bc7Smrj rtc.rtc_hr = BYTE_TO_BCD(tod.tod_hour); 156ae115bc7Smrj rtc.rtc_min = BYTE_TO_BCD(tod.tod_min); 157ae115bc7Smrj rtc.rtc_sec = BYTE_TO_BCD(tod.tod_sec); 158ae115bc7Smrj 159ae115bc7Smrj todpc_rtcput((unsigned char *)&rtc); 160ae115bc7Smrj } 161ae115bc7Smrj 162ae115bc7Smrj /* 163ae115bc7Smrj * Read the current time from the clock chip and convert to UNIX form. 164ae115bc7Smrj * Assumes that the year in the clock chip is valid. 165ae115bc7Smrj * Must be called with tod_lock held. 166ae115bc7Smrj */ 167ae115bc7Smrj /*ARGSUSED*/ 168ae115bc7Smrj static timestruc_t 169ae115bc7Smrj todpc_get(tod_ops_t *top) 170ae115bc7Smrj { 171ae115bc7Smrj timestruc_t ts; 172ae115bc7Smrj todinfo_t tod; 173ae115bc7Smrj struct rtc_t rtc; 174ae115bc7Smrj int compute_century; 175ae115bc7Smrj static int century_warn = 1; /* only warn once, not each time called */ 176ae115bc7Smrj static int range_warn = 1; 177ae115bc7Smrj 178ae115bc7Smrj ASSERT(MUTEX_HELD(&tod_lock)); 179ae115bc7Smrj 180ae115bc7Smrj if (todpc_rtcget((unsigned char *)&rtc)) { 1818fc99e42STrevor Thompson tod_status_set(TOD_GET_FAILED); 182*8500cbf9SDan Kruchinin return (hrestime); 183ae115bc7Smrj } 184ae115bc7Smrj 185ae115bc7Smrj /* assume that we wrap the rtc year back to zero at 2000 */ 186ae115bc7Smrj tod.tod_year = BCD_TO_BYTE(rtc.rtc_yr); 187ae115bc7Smrj if (tod.tod_year < 69) { 188ae115bc7Smrj if (range_warn && tod.tod_year > 38) { 189ae115bc7Smrj cmn_err(CE_WARN, "hardware real-time clock is out " 190ae115bc7Smrj "of range -- time needs to be reset"); 191ae115bc7Smrj range_warn = 0; 192ae115bc7Smrj } 193ae115bc7Smrj tod.tod_year += 100 + YRBASE; /* 20xx year */ 194ae115bc7Smrj compute_century = 20; 195ae115bc7Smrj } else { 196ae115bc7Smrj /* LINTED: YRBASE = 0 for x86 */ 197ae115bc7Smrj tod.tod_year += YRBASE; /* 19xx year */ 198ae115bc7Smrj compute_century = 19; 199ae115bc7Smrj } 200ae115bc7Smrj if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) { 201ae115bc7Smrj cmn_err(CE_NOTE, 202ae115bc7Smrj "The hardware real-time clock appears to have the " 203ae115bc7Smrj "wrong century: %d.\nSolaris will still operate " 204ae115bc7Smrj "correctly, but other OS's/firmware agents may " 205ae115bc7Smrj "not.\nUse date(1) to set the date to the current " 206ae115bc7Smrj "time to correct the RTC.", 207ae115bc7Smrj BCD_TO_BYTE(rtc.rtc_century)); 208ae115bc7Smrj century_warn = 0; 209ae115bc7Smrj } 210ae115bc7Smrj tod.tod_month = BCD_TO_BYTE(rtc.rtc_mon); 211ae115bc7Smrj tod.tod_day = BCD_TO_BYTE(rtc.rtc_dom); 212ae115bc7Smrj tod.tod_dow = rtc.rtc_dow; /* dow < 10, so no conversion needed */ 213ae115bc7Smrj tod.tod_hour = BCD_TO_BYTE(rtc.rtc_hr); 214ae115bc7Smrj tod.tod_min = BCD_TO_BYTE(rtc.rtc_min); 215ae115bc7Smrj tod.tod_sec = BCD_TO_BYTE(rtc.rtc_sec); 216ae115bc7Smrj 2178fc99e42STrevor Thompson /* read was successful so ensure failure flag is clear */ 2188fc99e42STrevor Thompson tod_status_clear(TOD_GET_FAILED); 2198fc99e42STrevor Thompson 220ae115bc7Smrj ts.tv_sec = tod_to_utc(tod) + ggmtl(); 221ae115bc7Smrj ts.tv_nsec = 0; 222ae115bc7Smrj 223ae115bc7Smrj return (ts); 224ae115bc7Smrj } 225ae115bc7Smrj 2262df1fe9cSrandyf #include <sys/promif.h> 2272df1fe9cSrandyf /* 2282df1fe9cSrandyf * Write the specified wakeup alarm into the clock chip. 2292df1fe9cSrandyf * Must be called with tod_lock held. 2302df1fe9cSrandyf */ 2312df1fe9cSrandyf void 2322df1fe9cSrandyf /*ARGSUSED*/ 2332df1fe9cSrandyf todpc_setalarm(tod_ops_t *top, int nsecs) 2342df1fe9cSrandyf { 2352df1fe9cSrandyf struct rtc_t rtc; 2362df1fe9cSrandyf int delta, asec, amin, ahr, adom, amon; 2372df1fe9cSrandyf int day_alrm = pc_rtc_offset.day_alrm; 2382df1fe9cSrandyf int mon_alrm = pc_rtc_offset.mon_alrm; 2392df1fe9cSrandyf 2402df1fe9cSrandyf ASSERT(MUTEX_HELD(&tod_lock)); 2412df1fe9cSrandyf 2422df1fe9cSrandyf /* A delay of zero is not allowed */ 2432df1fe9cSrandyf if (nsecs == 0) 2442df1fe9cSrandyf return; 2452df1fe9cSrandyf 2462df1fe9cSrandyf /* Make sure that we delay no less than the minimum time */ 2472df1fe9cSrandyf if (nsecs < clock_min_alarm) 2482df1fe9cSrandyf nsecs = clock_min_alarm; 2492df1fe9cSrandyf 2502df1fe9cSrandyf if (todpc_rtcget((unsigned char *)&rtc)) 2512df1fe9cSrandyf return; 2522df1fe9cSrandyf 2532df1fe9cSrandyf /* 2542df1fe9cSrandyf * Compute alarm secs, mins and hrs, and where appropriate, dom 2552df1fe9cSrandyf * and mon. rtc bytes are in binary-coded decimal, so we have 2562df1fe9cSrandyf * to convert. 2572df1fe9cSrandyf */ 2582df1fe9cSrandyf delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec); 2592df1fe9cSrandyf asec = delta % 60; 2602df1fe9cSrandyf 2612df1fe9cSrandyf delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min); 2622df1fe9cSrandyf amin = delta % 60; 2632df1fe9cSrandyf 2642df1fe9cSrandyf delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr); 2652df1fe9cSrandyf ahr = delta % 24; 2662df1fe9cSrandyf 2672df1fe9cSrandyf if (day_alrm == 0 && delta >= 24) { 2682df1fe9cSrandyf prom_printf("No day alarm - set to end of today!\n"); 2692df1fe9cSrandyf asec = 59; 2702df1fe9cSrandyf amin = 59; 2712df1fe9cSrandyf ahr = 23; 2722df1fe9cSrandyf } else { 2732df1fe9cSrandyf int mon = BCD_TO_BYTE(rtc.rtc_mon); 2742df1fe9cSrandyf static int dpm[] = 2752df1fe9cSrandyf {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 2762df1fe9cSrandyf 2772df1fe9cSrandyf adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom); 2782df1fe9cSrandyf 2792df1fe9cSrandyf if (mon_alrm == 0) { 2802df1fe9cSrandyf if (adom > dpm[mon]) { 2812df1fe9cSrandyf prom_printf("No mon alarm - " 2822df1fe9cSrandyf "set to end of current month!\n"); 2832df1fe9cSrandyf asec = 59; 2842df1fe9cSrandyf amin = 59; 2852df1fe9cSrandyf ahr = 23; 2862df1fe9cSrandyf adom = dpm[mon]; 2872df1fe9cSrandyf } 2882df1fe9cSrandyf } else { 2892df1fe9cSrandyf for (amon = mon; 2902df1fe9cSrandyf amon <= 12 && adom > dpm[amon]; amon++) { 2912df1fe9cSrandyf adom -= dpm[amon]; 2922df1fe9cSrandyf } 2932df1fe9cSrandyf if (amon > 12) { 2942df1fe9cSrandyf prom_printf("Alarm too far in future - " 2952df1fe9cSrandyf "set to end of current year!\n"); 2962df1fe9cSrandyf asec = 59; 2972df1fe9cSrandyf amin = 59; 2982df1fe9cSrandyf ahr = 23; 2992df1fe9cSrandyf adom = dpm[12]; 3002df1fe9cSrandyf amon = 12; 3012df1fe9cSrandyf } 3022df1fe9cSrandyf rtc.rtc_amon = BYTE_TO_BCD(amon); 3032df1fe9cSrandyf } 3042df1fe9cSrandyf 3052df1fe9cSrandyf rtc.rtc_adom = BYTE_TO_BCD(adom); 3062df1fe9cSrandyf } 3072df1fe9cSrandyf 3082df1fe9cSrandyf rtc.rtc_asec = BYTE_TO_BCD(asec); 3092df1fe9cSrandyf rtc.rtc_amin = BYTE_TO_BCD(amin); 3102df1fe9cSrandyf rtc.rtc_ahr = BYTE_TO_BCD(ahr); 3112df1fe9cSrandyf 3122df1fe9cSrandyf rtc.rtc_statusb |= RTC_AIE; /* Enable alarm interrupt */ 3132df1fe9cSrandyf 3142df1fe9cSrandyf todpc_rtcput((unsigned char *)&rtc); 3152df1fe9cSrandyf } 3162df1fe9cSrandyf 3172df1fe9cSrandyf /* 3182df1fe9cSrandyf * Clear an alarm. This is effectively setting an alarm of 0. 3192df1fe9cSrandyf */ 3202df1fe9cSrandyf void 3212df1fe9cSrandyf /*ARGSUSED*/ 3222df1fe9cSrandyf todpc_clralarm(tod_ops_t *top) 3232df1fe9cSrandyf { 3242df1fe9cSrandyf mutex_enter(&tod_lock); 3252df1fe9cSrandyf todpc_setalarm(top, 0); 3262df1fe9cSrandyf mutex_exit(&tod_lock); 3272df1fe9cSrandyf } 3282df1fe9cSrandyf 329ae115bc7Smrj /* 330ae115bc7Smrj * Routine to read contents of real time clock to the specified buffer. 331ae115bc7Smrj * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read 332ae115bc7Smrj * else 0. 333ae115bc7Smrj * The routine will busy wait for the Update-In-Progress flag to clear. 334ae115bc7Smrj * On completion of the reads the Seconds register is re-read and the 335ae115bc7Smrj * UIP flag is rechecked to confirm that an clock update did not occur 336ae115bc7Smrj * during the accesses. Routine will error exit after 256 attempts. 337ae115bc7Smrj * (See bugid 1158298.) 338ae115bc7Smrj * Routine returns RTC_NREG (which is 15) bytes of data, as given in the 339ae115bc7Smrj * technical reference. This data includes both time and status registers. 340ae115bc7Smrj */ 341ae115bc7Smrj 342ae115bc7Smrj static int 343ae115bc7Smrj todpc_rtcget(unsigned char *buf) 344ae115bc7Smrj { 345ae115bc7Smrj unsigned char reg; 346ae115bc7Smrj int i; 347ae115bc7Smrj int retries = 256; 348ae115bc7Smrj unsigned char *rawp; 3492df1fe9cSrandyf unsigned char century = RTC_CENTURY; 3502df1fe9cSrandyf unsigned char day_alrm; 3512df1fe9cSrandyf unsigned char mon_alrm; 352ae115bc7Smrj 353ae115bc7Smrj ASSERT(MUTEX_HELD(&tod_lock)); 354ae115bc7Smrj 3552df1fe9cSrandyf day_alrm = pc_rtc_offset.day_alrm; 3562df1fe9cSrandyf mon_alrm = pc_rtc_offset.mon_alrm; 3572df1fe9cSrandyf if (pc_rtc_offset.century != 0) { 3582df1fe9cSrandyf century = pc_rtc_offset.century; 3592df1fe9cSrandyf } 3602df1fe9cSrandyf 361ae115bc7Smrj outb(RTC_ADDR, RTC_D); /* check if clock valid */ 362ae115bc7Smrj reg = inb(RTC_DATA); 363ae115bc7Smrj if ((reg & RTC_VRT) == 0) 364ae115bc7Smrj return (ENXIO); 365ae115bc7Smrj 366ae115bc7Smrj checkuip: 367ae115bc7Smrj if (retries-- < 0) 368ae115bc7Smrj return (EAGAIN); 369ae115bc7Smrj outb(RTC_ADDR, RTC_A); /* check if update in progress */ 370ae115bc7Smrj reg = inb(RTC_DATA); 371ae115bc7Smrj if (reg & RTC_UIP) { 372ae115bc7Smrj tenmicrosec(); 373ae115bc7Smrj goto checkuip; 374ae115bc7Smrj } 375ae115bc7Smrj 376ae115bc7Smrj for (i = 0, rawp = buf; i < RTC_NREG; i++) { 377ae115bc7Smrj outb(RTC_ADDR, i); 378ae115bc7Smrj *rawp++ = inb(RTC_DATA); 379ae115bc7Smrj } 3802df1fe9cSrandyf outb(RTC_ADDR, century); /* do century */ 381ae115bc7Smrj ((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA); 3822df1fe9cSrandyf 3832df1fe9cSrandyf if (day_alrm > 0) { 3842df1fe9cSrandyf outb(RTC_ADDR, day_alrm); 3852df1fe9cSrandyf ((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f; 3862df1fe9cSrandyf } 3872df1fe9cSrandyf if (mon_alrm > 0) { 3882df1fe9cSrandyf outb(RTC_ADDR, mon_alrm); 3892df1fe9cSrandyf ((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA); 3902df1fe9cSrandyf } 3912df1fe9cSrandyf 392ae115bc7Smrj outb(RTC_ADDR, 0); /* re-read Seconds register */ 393ae115bc7Smrj reg = inb(RTC_DATA); 394ae115bc7Smrj if (reg != ((struct rtc_t *)buf)->rtc_sec || 395ae115bc7Smrj (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP)) 396ae115bc7Smrj /* update occured during reads */ 397ae115bc7Smrj goto checkuip; 398ae115bc7Smrj 399ae115bc7Smrj return (0); 400ae115bc7Smrj } 401ae115bc7Smrj 402ae115bc7Smrj /* 403ae115bc7Smrj * This routine writes the contents of the given buffer to the real time 404ae115bc7Smrj * clock. It is given RTC_NREGP bytes of data, which are the 10 bytes used 405ae115bc7Smrj * to write the time and set the alarm. It should be called with the priority 406ae115bc7Smrj * raised to 5. 407ae115bc7Smrj */ 408ae115bc7Smrj static void 409ae115bc7Smrj todpc_rtcput(unsigned char *buf) 410ae115bc7Smrj { 411ae115bc7Smrj unsigned char reg; 412ae115bc7Smrj int i; 4132df1fe9cSrandyf unsigned char century = RTC_CENTURY; 4142df1fe9cSrandyf unsigned char day_alrm = pc_rtc_offset.day_alrm; 4152df1fe9cSrandyf unsigned char mon_alrm = pc_rtc_offset.mon_alrm; 4165b3a1fb0SDan Mick unsigned char tmp; 4172df1fe9cSrandyf 4182df1fe9cSrandyf if (pc_rtc_offset.century != 0) { 4192df1fe9cSrandyf century = pc_rtc_offset.century; 4202df1fe9cSrandyf } 421ae115bc7Smrj 422ae115bc7Smrj outb(RTC_ADDR, RTC_B); 423ae115bc7Smrj reg = inb(RTC_DATA); 424ae115bc7Smrj outb(RTC_ADDR, RTC_B); 425ae115bc7Smrj outb(RTC_DATA, reg | RTC_SET); /* allow time set now */ 426ae115bc7Smrj for (i = 0; i < RTC_NREGP; i++) { /* set the time */ 427ae115bc7Smrj outb(RTC_ADDR, i); 428ae115bc7Smrj outb(RTC_DATA, buf[i]); 429ae115bc7Smrj } 4302df1fe9cSrandyf outb(RTC_ADDR, century); /* do century */ 431ae115bc7Smrj outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century); 4322df1fe9cSrandyf 4332df1fe9cSrandyf if (day_alrm > 0) { 4342df1fe9cSrandyf outb(RTC_ADDR, day_alrm); 4355b3a1fb0SDan Mick /* preserve RTC_VRT bit; some virt envs accept writes there */ 4365b3a1fb0SDan Mick tmp = inb(RTC_DATA) & RTC_VRT; 4375b3a1fb0SDan Mick tmp |= ((struct rtc_t *)buf)->rtc_adom & ~RTC_VRT; 4385b3a1fb0SDan Mick outb(RTC_DATA, tmp); 4392df1fe9cSrandyf } 4402df1fe9cSrandyf if (mon_alrm > 0) { 4412df1fe9cSrandyf outb(RTC_ADDR, mon_alrm); 4422df1fe9cSrandyf outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon); 4432df1fe9cSrandyf } 4442df1fe9cSrandyf 4452df1fe9cSrandyf outb(RTC_ADDR, RTC_B); 4462df1fe9cSrandyf reg = inb(RTC_DATA); 447ae115bc7Smrj outb(RTC_ADDR, RTC_B); 448ae115bc7Smrj outb(RTC_DATA, reg & ~RTC_SET); /* allow time update */ 449ae115bc7Smrj } 450ae115bc7Smrj 451ae115bc7Smrj static tod_ops_t todpc_ops = { 452ae115bc7Smrj TOD_OPS_VERSION, 453ae115bc7Smrj todpc_get, 454ae115bc7Smrj todpc_set, 4552df1fe9cSrandyf NULL, 4562df1fe9cSrandyf NULL, 4572df1fe9cSrandyf todpc_setalarm, 4582df1fe9cSrandyf todpc_clralarm, 459ae115bc7Smrj NULL 460ae115bc7Smrj }; 461ae115bc7Smrj 462ae115bc7Smrj /* 463ae115bc7Smrj * Initialize for the default TOD ops vector for use on hardware. 464ae115bc7Smrj */ 465ae115bc7Smrj 466ae115bc7Smrj tod_ops_t *tod_ops = &todpc_ops; 467