13446Smrj /* 23446Smrj * CDDL HEADER START 33446Smrj * 43446Smrj * The contents of this file are subject to the terms of the 53446Smrj * Common Development and Distribution License (the "License"). 63446Smrj * You may not use this file except in compliance with the License. 73446Smrj * 83446Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93446Smrj * or http://www.opensolaris.org/os/licensing. 103446Smrj * See the License for the specific language governing permissions 113446Smrj * and limitations under the License. 123446Smrj * 133446Smrj * When distributing Covered Code, include this CDDL HEADER in each 143446Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153446Smrj * If applicable, add the following below this CDDL HEADER, with the 163446Smrj * fields enclosed by brackets "[]" replaced with your own identifying 173446Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 183446Smrj * 193446Smrj * CDDL HEADER END 203446Smrj */ 213446Smrj 223446Smrj /* 233446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 243446Smrj * Use is subject to license terms. 253446Smrj */ 263446Smrj 273446Smrj /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 283446Smrj /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 293446Smrj /* All Rights Reserved */ 303446Smrj 313446Smrj /* Copyright (c) 1987, 1988 Microsoft Corporation */ 323446Smrj /* All Rights Reserved */ 333446Smrj 343446Smrj #pragma ident "%Z%%M% %I% %E% SMI" 353446Smrj 363446Smrj #include <sys/param.h> 373446Smrj #include <sys/time.h> 383446Smrj #include <sys/systm.h> 393446Smrj 403446Smrj #include <sys/cpuvar.h> 413446Smrj #include <sys/clock.h> 423446Smrj #include <sys/debug.h> 433446Smrj #include <sys/rtc.h> 443446Smrj #include <sys/archsystm.h> 453446Smrj #include <sys/sysmacros.h> 463446Smrj #include <sys/lockstat.h> 473446Smrj #include <sys/stat.h> 483446Smrj #include <sys/sunddi.h> 493446Smrj 50*5295Srandyf #include <sys/acpi/acpi.h> 51*5295Srandyf #include <sys/acpica.h> 52*5295Srandyf 533446Smrj static int todpc_rtcget(unsigned char *buf); 543446Smrj static void todpc_rtcput(unsigned char *buf); 553446Smrj 56*5295Srandyf #define CLOCK_RES 1000 /* 1 microsec in nanosecs */ 57*5295Srandyf 58*5295Srandyf int clock_res = CLOCK_RES; 59*5295Srandyf 60*5295Srandyf /* 61*5295Srandyf * The minimum sleep time till an alarm can be fired. 62*5295Srandyf * This can be tuned in /etc/system, but if the value is too small, 63*5295Srandyf * there is a danger that it will be missed if it takes too long to 64*5295Srandyf * get from the set point to sleep. Or that it can fire quickly, and 65*5295Srandyf * generate a power spike on the hardware. And small values are 66*5295Srandyf * probably only usefull for test setups. 67*5295Srandyf */ 68*5295Srandyf int clock_min_alarm = 4; 69*5295Srandyf 703446Smrj /* 713446Smrj * Machine-dependent clock routines. 723446Smrj */ 733446Smrj 74*5295Srandyf extern long gmt_lag; 75*5295Srandyf 76*5295Srandyf struct rtc_offset { 77*5295Srandyf int8_t loaded; 78*5295Srandyf uint8_t day_alrm; 79*5295Srandyf uint8_t mon_alrm; 80*5295Srandyf uint8_t century; 81*5295Srandyf }; 82*5295Srandyf 83*5295Srandyf static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0}; 84*5295Srandyf 85*5295Srandyf 86*5295Srandyf /* 87*5295Srandyf * Entry point for ACPI to pass RTC or other clock values that 88*5295Srandyf * are useful to TOD. 89*5295Srandyf */ 90*5295Srandyf void 91*5295Srandyf pc_tod_set_rtc_offsets(FADT_DESCRIPTOR *fadt) { 92*5295Srandyf int ok = 0; 93*5295Srandyf 94*5295Srandyf /* 95*5295Srandyf * ASSERT is for debugging, but we don't want the machine 96*5295Srandyf * falling over because for some reason we didn't get a valid 97*5295Srandyf * pointer. 98*5295Srandyf */ 99*5295Srandyf ASSERT(fadt); 100*5295Srandyf if (fadt == NULL) { 101*5295Srandyf return; 102*5295Srandyf } 103*5295Srandyf 104*5295Srandyf if (fadt->DayAlrm) { 105*5295Srandyf pc_rtc_offset.day_alrm = fadt->DayAlrm; 106*5295Srandyf ok = 1; 107*5295Srandyf } 108*5295Srandyf 109*5295Srandyf if (fadt->MonAlrm) { 110*5295Srandyf pc_rtc_offset.mon_alrm = fadt->MonAlrm; 111*5295Srandyf ok = 1; 112*5295Srandyf } 113*5295Srandyf 114*5295Srandyf if (fadt->Century) { 115*5295Srandyf pc_rtc_offset.century = fadt->Century; 116*5295Srandyf ok = 1; 117*5295Srandyf } 118*5295Srandyf 119*5295Srandyf pc_rtc_offset.loaded = ok; 120*5295Srandyf } 121*5295Srandyf 122*5295Srandyf 1233446Smrj /* 1243446Smrj * Write the specified time into the clock chip. 1253446Smrj * Must be called with tod_lock held. 1263446Smrj */ 1273446Smrj /*ARGSUSED*/ 1283446Smrj static void 1293446Smrj todpc_set(tod_ops_t *top, timestruc_t ts) 1303446Smrj { 1313446Smrj todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl()); 1323446Smrj struct rtc_t rtc; 1333446Smrj 1343446Smrj ASSERT(MUTEX_HELD(&tod_lock)); 1353446Smrj 1363446Smrj if (todpc_rtcget((unsigned char *)&rtc)) 1373446Smrj return; 1383446Smrj 1393446Smrj /* 1403446Smrj * rtc bytes are in binary-coded decimal, so we have to convert. 1413446Smrj * We assume that we wrap the rtc year back to zero at 2000. 1423446Smrj */ 1433446Smrj /* LINTED: YRBASE = 0 for x86 */ 1443446Smrj tod.tod_year -= YRBASE; 1453446Smrj if (tod.tod_year >= 100) { 1463446Smrj tod.tod_year -= 100; 1473446Smrj rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */ 1483446Smrj } else 1493446Smrj rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */ 1503446Smrj rtc.rtc_yr = BYTE_TO_BCD(tod.tod_year); 1513446Smrj rtc.rtc_mon = BYTE_TO_BCD(tod.tod_month); 1523446Smrj rtc.rtc_dom = BYTE_TO_BCD(tod.tod_day); 1533446Smrj /* dow < 10, so no conversion */ 1543446Smrj rtc.rtc_dow = (unsigned char)tod.tod_dow; 1553446Smrj rtc.rtc_hr = BYTE_TO_BCD(tod.tod_hour); 1563446Smrj rtc.rtc_min = BYTE_TO_BCD(tod.tod_min); 1573446Smrj rtc.rtc_sec = BYTE_TO_BCD(tod.tod_sec); 1583446Smrj 1593446Smrj todpc_rtcput((unsigned char *)&rtc); 1603446Smrj } 1613446Smrj 1623446Smrj /* 1633446Smrj * Read the current time from the clock chip and convert to UNIX form. 1643446Smrj * Assumes that the year in the clock chip is valid. 1653446Smrj * Must be called with tod_lock held. 1663446Smrj */ 1673446Smrj /*ARGSUSED*/ 1683446Smrj static timestruc_t 1693446Smrj todpc_get(tod_ops_t *top) 1703446Smrj { 1713446Smrj timestruc_t ts; 1723446Smrj todinfo_t tod; 1733446Smrj struct rtc_t rtc; 1743446Smrj int compute_century; 1753446Smrj static int century_warn = 1; /* only warn once, not each time called */ 1763446Smrj static int range_warn = 1; 1773446Smrj 1783446Smrj ASSERT(MUTEX_HELD(&tod_lock)); 1793446Smrj 1803446Smrj if (todpc_rtcget((unsigned char *)&rtc)) { 1813446Smrj ts.tv_sec = 0; 1823446Smrj ts.tv_nsec = 0; 1833446Smrj tod_fault_reset(); 1843446Smrj return (ts); 1853446Smrj } 1863446Smrj 1873446Smrj /* assume that we wrap the rtc year back to zero at 2000 */ 1883446Smrj tod.tod_year = BCD_TO_BYTE(rtc.rtc_yr); 1893446Smrj if (tod.tod_year < 69) { 1903446Smrj if (range_warn && tod.tod_year > 38) { 1913446Smrj cmn_err(CE_WARN, "hardware real-time clock is out " 192*5295Srandyf "of range -- time needs to be reset"); 1933446Smrj range_warn = 0; 1943446Smrj } 1953446Smrj tod.tod_year += 100 + YRBASE; /* 20xx year */ 1963446Smrj compute_century = 20; 1973446Smrj } else { 1983446Smrj /* LINTED: YRBASE = 0 for x86 */ 1993446Smrj tod.tod_year += YRBASE; /* 19xx year */ 2003446Smrj compute_century = 19; 2013446Smrj } 2023446Smrj if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) { 2033446Smrj cmn_err(CE_NOTE, 204*5295Srandyf "The hardware real-time clock appears to have the " 205*5295Srandyf "wrong century: %d.\nSolaris will still operate " 206*5295Srandyf "correctly, but other OS's/firmware agents may " 207*5295Srandyf "not.\nUse date(1) to set the date to the current " 208*5295Srandyf "time to correct the RTC.", 209*5295Srandyf BCD_TO_BYTE(rtc.rtc_century)); 2103446Smrj century_warn = 0; 2113446Smrj } 2123446Smrj tod.tod_month = BCD_TO_BYTE(rtc.rtc_mon); 2133446Smrj tod.tod_day = BCD_TO_BYTE(rtc.rtc_dom); 2143446Smrj tod.tod_dow = rtc.rtc_dow; /* dow < 10, so no conversion needed */ 2153446Smrj tod.tod_hour = BCD_TO_BYTE(rtc.rtc_hr); 2163446Smrj tod.tod_min = BCD_TO_BYTE(rtc.rtc_min); 2173446Smrj tod.tod_sec = BCD_TO_BYTE(rtc.rtc_sec); 2183446Smrj 2193446Smrj ts.tv_sec = tod_to_utc(tod) + ggmtl(); 2203446Smrj ts.tv_nsec = 0; 2213446Smrj 2223446Smrj return (ts); 2233446Smrj } 2243446Smrj 225*5295Srandyf #include <sys/promif.h> 226*5295Srandyf /* 227*5295Srandyf * Write the specified wakeup alarm into the clock chip. 228*5295Srandyf * Must be called with tod_lock held. 229*5295Srandyf */ 230*5295Srandyf void 231*5295Srandyf /*ARGSUSED*/ 232*5295Srandyf todpc_setalarm(tod_ops_t *top, int nsecs) 233*5295Srandyf { 234*5295Srandyf struct rtc_t rtc; 235*5295Srandyf int delta, asec, amin, ahr, adom, amon; 236*5295Srandyf int day_alrm = pc_rtc_offset.day_alrm; 237*5295Srandyf int mon_alrm = pc_rtc_offset.mon_alrm; 238*5295Srandyf 239*5295Srandyf ASSERT(MUTEX_HELD(&tod_lock)); 240*5295Srandyf 241*5295Srandyf /* A delay of zero is not allowed */ 242*5295Srandyf if (nsecs == 0) 243*5295Srandyf return; 244*5295Srandyf 245*5295Srandyf /* Make sure that we delay no less than the minimum time */ 246*5295Srandyf if (nsecs < clock_min_alarm) 247*5295Srandyf nsecs = clock_min_alarm; 248*5295Srandyf 249*5295Srandyf if (todpc_rtcget((unsigned char *)&rtc)) 250*5295Srandyf return; 251*5295Srandyf 252*5295Srandyf /* 253*5295Srandyf * Compute alarm secs, mins and hrs, and where appropriate, dom 254*5295Srandyf * and mon. rtc bytes are in binary-coded decimal, so we have 255*5295Srandyf * to convert. 256*5295Srandyf */ 257*5295Srandyf delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec); 258*5295Srandyf asec = delta % 60; 259*5295Srandyf 260*5295Srandyf delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min); 261*5295Srandyf amin = delta % 60; 262*5295Srandyf 263*5295Srandyf delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr); 264*5295Srandyf ahr = delta % 24; 265*5295Srandyf 266*5295Srandyf if (day_alrm == 0 && delta >= 24) { 267*5295Srandyf prom_printf("No day alarm - set to end of today!\n"); 268*5295Srandyf asec = 59; 269*5295Srandyf amin = 59; 270*5295Srandyf ahr = 23; 271*5295Srandyf } else { 272*5295Srandyf int mon = BCD_TO_BYTE(rtc.rtc_mon); 273*5295Srandyf static int dpm[] = 274*5295Srandyf {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 275*5295Srandyf 276*5295Srandyf adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom); 277*5295Srandyf 278*5295Srandyf if (mon_alrm == 0) { 279*5295Srandyf if (adom > dpm[mon]) { 280*5295Srandyf prom_printf("No mon alarm - " 281*5295Srandyf "set to end of current month!\n"); 282*5295Srandyf asec = 59; 283*5295Srandyf amin = 59; 284*5295Srandyf ahr = 23; 285*5295Srandyf adom = dpm[mon]; 286*5295Srandyf } 287*5295Srandyf } else { 288*5295Srandyf for (amon = mon; 289*5295Srandyf amon <= 12 && adom > dpm[amon]; amon++) { 290*5295Srandyf adom -= dpm[amon]; 291*5295Srandyf } 292*5295Srandyf if (amon > 12) { 293*5295Srandyf prom_printf("Alarm too far in future - " 294*5295Srandyf "set to end of current year!\n"); 295*5295Srandyf asec = 59; 296*5295Srandyf amin = 59; 297*5295Srandyf ahr = 23; 298*5295Srandyf adom = dpm[12]; 299*5295Srandyf amon = 12; 300*5295Srandyf } 301*5295Srandyf rtc.rtc_amon = BYTE_TO_BCD(amon); 302*5295Srandyf } 303*5295Srandyf 304*5295Srandyf rtc.rtc_adom = BYTE_TO_BCD(adom); 305*5295Srandyf } 306*5295Srandyf 307*5295Srandyf rtc.rtc_asec = BYTE_TO_BCD(asec); 308*5295Srandyf rtc.rtc_amin = BYTE_TO_BCD(amin); 309*5295Srandyf rtc.rtc_ahr = BYTE_TO_BCD(ahr); 310*5295Srandyf 311*5295Srandyf rtc.rtc_statusb |= RTC_AIE; /* Enable alarm interrupt */ 312*5295Srandyf 313*5295Srandyf todpc_rtcput((unsigned char *)&rtc); 314*5295Srandyf } 315*5295Srandyf 316*5295Srandyf /* 317*5295Srandyf * Clear an alarm. This is effectively setting an alarm of 0. 318*5295Srandyf */ 319*5295Srandyf void 320*5295Srandyf /*ARGSUSED*/ 321*5295Srandyf todpc_clralarm(tod_ops_t *top) 322*5295Srandyf { 323*5295Srandyf mutex_enter(&tod_lock); 324*5295Srandyf todpc_setalarm(top, 0); 325*5295Srandyf mutex_exit(&tod_lock); 326*5295Srandyf } 327*5295Srandyf 3283446Smrj /* 3293446Smrj * Routine to read contents of real time clock to the specified buffer. 3303446Smrj * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read 3313446Smrj * else 0. 3323446Smrj * The routine will busy wait for the Update-In-Progress flag to clear. 3333446Smrj * On completion of the reads the Seconds register is re-read and the 3343446Smrj * UIP flag is rechecked to confirm that an clock update did not occur 3353446Smrj * during the accesses. Routine will error exit after 256 attempts. 3363446Smrj * (See bugid 1158298.) 3373446Smrj * Routine returns RTC_NREG (which is 15) bytes of data, as given in the 3383446Smrj * technical reference. This data includes both time and status registers. 3393446Smrj */ 3403446Smrj 3413446Smrj static int 3423446Smrj todpc_rtcget(unsigned char *buf) 3433446Smrj { 3443446Smrj unsigned char reg; 3453446Smrj int i; 3463446Smrj int retries = 256; 3473446Smrj unsigned char *rawp; 348*5295Srandyf unsigned char century = RTC_CENTURY; 349*5295Srandyf unsigned char day_alrm; 350*5295Srandyf unsigned char mon_alrm; 3513446Smrj 3523446Smrj ASSERT(MUTEX_HELD(&tod_lock)); 3533446Smrj 354*5295Srandyf day_alrm = pc_rtc_offset.day_alrm; 355*5295Srandyf mon_alrm = pc_rtc_offset.mon_alrm; 356*5295Srandyf if (pc_rtc_offset.century != 0) { 357*5295Srandyf century = pc_rtc_offset.century; 358*5295Srandyf } 359*5295Srandyf 3603446Smrj outb(RTC_ADDR, RTC_D); /* check if clock valid */ 3613446Smrj reg = inb(RTC_DATA); 3623446Smrj if ((reg & RTC_VRT) == 0) 3633446Smrj return (ENXIO); 3643446Smrj 3653446Smrj checkuip: 3663446Smrj if (retries-- < 0) 3673446Smrj return (EAGAIN); 3683446Smrj outb(RTC_ADDR, RTC_A); /* check if update in progress */ 3693446Smrj reg = inb(RTC_DATA); 3703446Smrj if (reg & RTC_UIP) { 3713446Smrj tenmicrosec(); 3723446Smrj goto checkuip; 3733446Smrj } 3743446Smrj 3753446Smrj for (i = 0, rawp = buf; i < RTC_NREG; i++) { 3763446Smrj outb(RTC_ADDR, i); 3773446Smrj *rawp++ = inb(RTC_DATA); 3783446Smrj } 379*5295Srandyf outb(RTC_ADDR, century); /* do century */ 3803446Smrj ((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA); 381*5295Srandyf 382*5295Srandyf if (day_alrm > 0) { 383*5295Srandyf outb(RTC_ADDR, day_alrm); 384*5295Srandyf ((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f; 385*5295Srandyf } 386*5295Srandyf if (mon_alrm > 0) { 387*5295Srandyf outb(RTC_ADDR, mon_alrm); 388*5295Srandyf ((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA); 389*5295Srandyf } 390*5295Srandyf 3913446Smrj outb(RTC_ADDR, 0); /* re-read Seconds register */ 3923446Smrj reg = inb(RTC_DATA); 3933446Smrj if (reg != ((struct rtc_t *)buf)->rtc_sec || 3943446Smrj (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP)) 3953446Smrj /* update occured during reads */ 3963446Smrj goto checkuip; 3973446Smrj 3983446Smrj return (0); 3993446Smrj } 4003446Smrj 4013446Smrj /* 4023446Smrj * This routine writes the contents of the given buffer to the real time 4033446Smrj * clock. It is given RTC_NREGP bytes of data, which are the 10 bytes used 4043446Smrj * to write the time and set the alarm. It should be called with the priority 4053446Smrj * raised to 5. 4063446Smrj */ 4073446Smrj static void 4083446Smrj todpc_rtcput(unsigned char *buf) 4093446Smrj { 4103446Smrj unsigned char reg; 4113446Smrj int i; 412*5295Srandyf unsigned char century = RTC_CENTURY; 413*5295Srandyf unsigned char day_alrm = pc_rtc_offset.day_alrm; 414*5295Srandyf unsigned char mon_alrm = pc_rtc_offset.mon_alrm; 415*5295Srandyf 416*5295Srandyf if (pc_rtc_offset.century != 0) { 417*5295Srandyf century = pc_rtc_offset.century; 418*5295Srandyf } 4193446Smrj 4203446Smrj outb(RTC_ADDR, RTC_B); 4213446Smrj reg = inb(RTC_DATA); 4223446Smrj outb(RTC_ADDR, RTC_B); 4233446Smrj outb(RTC_DATA, reg | RTC_SET); /* allow time set now */ 4243446Smrj for (i = 0; i < RTC_NREGP; i++) { /* set the time */ 4253446Smrj outb(RTC_ADDR, i); 4263446Smrj outb(RTC_DATA, buf[i]); 4273446Smrj } 428*5295Srandyf outb(RTC_ADDR, century); /* do century */ 4293446Smrj outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century); 430*5295Srandyf 431*5295Srandyf if (day_alrm > 0) { 432*5295Srandyf outb(RTC_ADDR, day_alrm); 433*5295Srandyf outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_adom); 434*5295Srandyf } 435*5295Srandyf if (mon_alrm > 0) { 436*5295Srandyf outb(RTC_ADDR, mon_alrm); 437*5295Srandyf outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon); 438*5295Srandyf } 439*5295Srandyf 440*5295Srandyf outb(RTC_ADDR, RTC_B); 441*5295Srandyf reg = inb(RTC_DATA); 4423446Smrj outb(RTC_ADDR, RTC_B); 4433446Smrj outb(RTC_DATA, reg & ~RTC_SET); /* allow time update */ 4443446Smrj } 4453446Smrj 4463446Smrj static tod_ops_t todpc_ops = { 4473446Smrj TOD_OPS_VERSION, 4483446Smrj todpc_get, 4493446Smrj todpc_set, 450*5295Srandyf NULL, 451*5295Srandyf NULL, 452*5295Srandyf todpc_setalarm, 453*5295Srandyf todpc_clralarm, 4543446Smrj NULL 4553446Smrj }; 4563446Smrj 4573446Smrj /* 4583446Smrj * Initialize for the default TOD ops vector for use on hardware. 4593446Smrj */ 4603446Smrj 4613446Smrj tod_ops_t *tod_ops = &todpc_ops; 462