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 /* 23*8936SDan.Mick@Sun.COM * Copyright 2009 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 #include <sys/param.h> 353446Smrj #include <sys/time.h> 363446Smrj #include <sys/systm.h> 373446Smrj 383446Smrj #include <sys/cpuvar.h> 393446Smrj #include <sys/clock.h> 403446Smrj #include <sys/debug.h> 413446Smrj #include <sys/rtc.h> 423446Smrj #include <sys/archsystm.h> 433446Smrj #include <sys/sysmacros.h> 443446Smrj #include <sys/lockstat.h> 453446Smrj #include <sys/stat.h> 463446Smrj #include <sys/sunddi.h> 473446Smrj 485295Srandyf #include <sys/acpi/acpi.h> 495295Srandyf #include <sys/acpica.h> 505295Srandyf 513446Smrj static int todpc_rtcget(unsigned char *buf); 523446Smrj static void todpc_rtcput(unsigned char *buf); 533446Smrj 545295Srandyf #define CLOCK_RES 1000 /* 1 microsec in nanosecs */ 555295Srandyf 565295Srandyf int clock_res = CLOCK_RES; 575295Srandyf 585295Srandyf /* 595295Srandyf * The minimum sleep time till an alarm can be fired. 605295Srandyf * This can be tuned in /etc/system, but if the value is too small, 615295Srandyf * there is a danger that it will be missed if it takes too long to 625295Srandyf * get from the set point to sleep. Or that it can fire quickly, and 635295Srandyf * generate a power spike on the hardware. And small values are 645295Srandyf * probably only usefull for test setups. 655295Srandyf */ 665295Srandyf int clock_min_alarm = 4; 675295Srandyf 683446Smrj /* 693446Smrj * Machine-dependent clock routines. 703446Smrj */ 713446Smrj 725295Srandyf extern long gmt_lag; 735295Srandyf 745295Srandyf struct rtc_offset { 755295Srandyf int8_t loaded; 765295Srandyf uint8_t day_alrm; 775295Srandyf uint8_t mon_alrm; 785295Srandyf uint8_t century; 795295Srandyf }; 805295Srandyf 815295Srandyf static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0}; 825295Srandyf 835295Srandyf 845295Srandyf /* 855295Srandyf * Entry point for ACPI to pass RTC or other clock values that 865295Srandyf * are useful to TOD. 875295Srandyf */ 885295Srandyf void 897851SDana.Myers@Sun.COM pc_tod_set_rtc_offsets(ACPI_TABLE_FADT *fadt) { 905295Srandyf int ok = 0; 915295Srandyf 925295Srandyf /* 935295Srandyf * ASSERT is for debugging, but we don't want the machine 945295Srandyf * falling over because for some reason we didn't get a valid 955295Srandyf * pointer. 965295Srandyf */ 975295Srandyf ASSERT(fadt); 985295Srandyf if (fadt == NULL) { 995295Srandyf return; 1005295Srandyf } 1015295Srandyf 1027851SDana.Myers@Sun.COM if (fadt->DayAlarm) { 1037851SDana.Myers@Sun.COM pc_rtc_offset.day_alrm = fadt->DayAlarm; 1045295Srandyf ok = 1; 1055295Srandyf } 1065295Srandyf 1077851SDana.Myers@Sun.COM if (fadt->MonthAlarm) { 1087851SDana.Myers@Sun.COM pc_rtc_offset.mon_alrm = fadt->MonthAlarm; 1095295Srandyf ok = 1; 1105295Srandyf } 1115295Srandyf 1125295Srandyf if (fadt->Century) { 1135295Srandyf pc_rtc_offset.century = fadt->Century; 1145295Srandyf ok = 1; 1155295Srandyf } 1165295Srandyf 1175295Srandyf pc_rtc_offset.loaded = ok; 1185295Srandyf } 1195295Srandyf 1205295Srandyf 1213446Smrj /* 1223446Smrj * Write the specified time into the clock chip. 1233446Smrj * Must be called with tod_lock held. 1243446Smrj */ 1253446Smrj /*ARGSUSED*/ 1263446Smrj static void 1273446Smrj todpc_set(tod_ops_t *top, timestruc_t ts) 1283446Smrj { 1293446Smrj todinfo_t tod = utc_to_tod(ts.tv_sec - ggmtl()); 1303446Smrj struct rtc_t rtc; 1313446Smrj 1323446Smrj ASSERT(MUTEX_HELD(&tod_lock)); 1333446Smrj 1343446Smrj if (todpc_rtcget((unsigned char *)&rtc)) 1353446Smrj return; 1363446Smrj 1373446Smrj /* 1383446Smrj * rtc bytes are in binary-coded decimal, so we have to convert. 1393446Smrj * We assume that we wrap the rtc year back to zero at 2000. 1403446Smrj */ 1413446Smrj /* LINTED: YRBASE = 0 for x86 */ 1423446Smrj tod.tod_year -= YRBASE; 1433446Smrj if (tod.tod_year >= 100) { 1443446Smrj tod.tod_year -= 100; 1453446Smrj rtc.rtc_century = BYTE_TO_BCD(20); /* 20xx year */ 1463446Smrj } else 1473446Smrj rtc.rtc_century = BYTE_TO_BCD(19); /* 19xx year */ 1483446Smrj rtc.rtc_yr = BYTE_TO_BCD(tod.tod_year); 1493446Smrj rtc.rtc_mon = BYTE_TO_BCD(tod.tod_month); 1503446Smrj rtc.rtc_dom = BYTE_TO_BCD(tod.tod_day); 1513446Smrj /* dow < 10, so no conversion */ 1523446Smrj rtc.rtc_dow = (unsigned char)tod.tod_dow; 1533446Smrj rtc.rtc_hr = BYTE_TO_BCD(tod.tod_hour); 1543446Smrj rtc.rtc_min = BYTE_TO_BCD(tod.tod_min); 1553446Smrj rtc.rtc_sec = BYTE_TO_BCD(tod.tod_sec); 1563446Smrj 1573446Smrj todpc_rtcput((unsigned char *)&rtc); 1583446Smrj } 1593446Smrj 1603446Smrj /* 1613446Smrj * Read the current time from the clock chip and convert to UNIX form. 1623446Smrj * Assumes that the year in the clock chip is valid. 1633446Smrj * Must be called with tod_lock held. 1643446Smrj */ 1653446Smrj /*ARGSUSED*/ 1663446Smrj static timestruc_t 1673446Smrj todpc_get(tod_ops_t *top) 1683446Smrj { 1693446Smrj timestruc_t ts; 1703446Smrj todinfo_t tod; 1713446Smrj struct rtc_t rtc; 1723446Smrj int compute_century; 1733446Smrj static int century_warn = 1; /* only warn once, not each time called */ 1743446Smrj static int range_warn = 1; 1753446Smrj 1763446Smrj ASSERT(MUTEX_HELD(&tod_lock)); 1773446Smrj 1783446Smrj if (todpc_rtcget((unsigned char *)&rtc)) { 1793446Smrj ts.tv_sec = 0; 1803446Smrj ts.tv_nsec = 0; 1813446Smrj tod_fault_reset(); 1823446Smrj return (ts); 1833446Smrj } 1843446Smrj 1853446Smrj /* assume that we wrap the rtc year back to zero at 2000 */ 1863446Smrj tod.tod_year = BCD_TO_BYTE(rtc.rtc_yr); 1873446Smrj if (tod.tod_year < 69) { 1883446Smrj if (range_warn && tod.tod_year > 38) { 1893446Smrj cmn_err(CE_WARN, "hardware real-time clock is out " 1905295Srandyf "of range -- time needs to be reset"); 1913446Smrj range_warn = 0; 1923446Smrj } 1933446Smrj tod.tod_year += 100 + YRBASE; /* 20xx year */ 1943446Smrj compute_century = 20; 1953446Smrj } else { 1963446Smrj /* LINTED: YRBASE = 0 for x86 */ 1973446Smrj tod.tod_year += YRBASE; /* 19xx year */ 1983446Smrj compute_century = 19; 1993446Smrj } 2003446Smrj if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) { 2013446Smrj cmn_err(CE_NOTE, 2025295Srandyf "The hardware real-time clock appears to have the " 2035295Srandyf "wrong century: %d.\nSolaris will still operate " 2045295Srandyf "correctly, but other OS's/firmware agents may " 2055295Srandyf "not.\nUse date(1) to set the date to the current " 2065295Srandyf "time to correct the RTC.", 2075295Srandyf BCD_TO_BYTE(rtc.rtc_century)); 2083446Smrj century_warn = 0; 2093446Smrj } 2103446Smrj tod.tod_month = BCD_TO_BYTE(rtc.rtc_mon); 2113446Smrj tod.tod_day = BCD_TO_BYTE(rtc.rtc_dom); 2123446Smrj tod.tod_dow = rtc.rtc_dow; /* dow < 10, so no conversion needed */ 2133446Smrj tod.tod_hour = BCD_TO_BYTE(rtc.rtc_hr); 2143446Smrj tod.tod_min = BCD_TO_BYTE(rtc.rtc_min); 2153446Smrj tod.tod_sec = BCD_TO_BYTE(rtc.rtc_sec); 2163446Smrj 2173446Smrj ts.tv_sec = tod_to_utc(tod) + ggmtl(); 2183446Smrj ts.tv_nsec = 0; 2193446Smrj 2203446Smrj return (ts); 2213446Smrj } 2223446Smrj 2235295Srandyf #include <sys/promif.h> 2245295Srandyf /* 2255295Srandyf * Write the specified wakeup alarm into the clock chip. 2265295Srandyf * Must be called with tod_lock held. 2275295Srandyf */ 2285295Srandyf void 2295295Srandyf /*ARGSUSED*/ 2305295Srandyf todpc_setalarm(tod_ops_t *top, int nsecs) 2315295Srandyf { 2325295Srandyf struct rtc_t rtc; 2335295Srandyf int delta, asec, amin, ahr, adom, amon; 2345295Srandyf int day_alrm = pc_rtc_offset.day_alrm; 2355295Srandyf int mon_alrm = pc_rtc_offset.mon_alrm; 2365295Srandyf 2375295Srandyf ASSERT(MUTEX_HELD(&tod_lock)); 2385295Srandyf 2395295Srandyf /* A delay of zero is not allowed */ 2405295Srandyf if (nsecs == 0) 2415295Srandyf return; 2425295Srandyf 2435295Srandyf /* Make sure that we delay no less than the minimum time */ 2445295Srandyf if (nsecs < clock_min_alarm) 2455295Srandyf nsecs = clock_min_alarm; 2465295Srandyf 2475295Srandyf if (todpc_rtcget((unsigned char *)&rtc)) 2485295Srandyf return; 2495295Srandyf 2505295Srandyf /* 2515295Srandyf * Compute alarm secs, mins and hrs, and where appropriate, dom 2525295Srandyf * and mon. rtc bytes are in binary-coded decimal, so we have 2535295Srandyf * to convert. 2545295Srandyf */ 2555295Srandyf delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec); 2565295Srandyf asec = delta % 60; 2575295Srandyf 2585295Srandyf delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min); 2595295Srandyf amin = delta % 60; 2605295Srandyf 2615295Srandyf delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr); 2625295Srandyf ahr = delta % 24; 2635295Srandyf 2645295Srandyf if (day_alrm == 0 && delta >= 24) { 2655295Srandyf prom_printf("No day alarm - set to end of today!\n"); 2665295Srandyf asec = 59; 2675295Srandyf amin = 59; 2685295Srandyf ahr = 23; 2695295Srandyf } else { 2705295Srandyf int mon = BCD_TO_BYTE(rtc.rtc_mon); 2715295Srandyf static int dpm[] = 2725295Srandyf {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 2735295Srandyf 2745295Srandyf adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom); 2755295Srandyf 2765295Srandyf if (mon_alrm == 0) { 2775295Srandyf if (adom > dpm[mon]) { 2785295Srandyf prom_printf("No mon alarm - " 2795295Srandyf "set to end of current month!\n"); 2805295Srandyf asec = 59; 2815295Srandyf amin = 59; 2825295Srandyf ahr = 23; 2835295Srandyf adom = dpm[mon]; 2845295Srandyf } 2855295Srandyf } else { 2865295Srandyf for (amon = mon; 2875295Srandyf amon <= 12 && adom > dpm[amon]; amon++) { 2885295Srandyf adom -= dpm[amon]; 2895295Srandyf } 2905295Srandyf if (amon > 12) { 2915295Srandyf prom_printf("Alarm too far in future - " 2925295Srandyf "set to end of current year!\n"); 2935295Srandyf asec = 59; 2945295Srandyf amin = 59; 2955295Srandyf ahr = 23; 2965295Srandyf adom = dpm[12]; 2975295Srandyf amon = 12; 2985295Srandyf } 2995295Srandyf rtc.rtc_amon = BYTE_TO_BCD(amon); 3005295Srandyf } 3015295Srandyf 3025295Srandyf rtc.rtc_adom = BYTE_TO_BCD(adom); 3035295Srandyf } 3045295Srandyf 3055295Srandyf rtc.rtc_asec = BYTE_TO_BCD(asec); 3065295Srandyf rtc.rtc_amin = BYTE_TO_BCD(amin); 3075295Srandyf rtc.rtc_ahr = BYTE_TO_BCD(ahr); 3085295Srandyf 3095295Srandyf rtc.rtc_statusb |= RTC_AIE; /* Enable alarm interrupt */ 3105295Srandyf 3115295Srandyf todpc_rtcput((unsigned char *)&rtc); 3125295Srandyf } 3135295Srandyf 3145295Srandyf /* 3155295Srandyf * Clear an alarm. This is effectively setting an alarm of 0. 3165295Srandyf */ 3175295Srandyf void 3185295Srandyf /*ARGSUSED*/ 3195295Srandyf todpc_clralarm(tod_ops_t *top) 3205295Srandyf { 3215295Srandyf mutex_enter(&tod_lock); 3225295Srandyf todpc_setalarm(top, 0); 3235295Srandyf mutex_exit(&tod_lock); 3245295Srandyf } 3255295Srandyf 3263446Smrj /* 3273446Smrj * Routine to read contents of real time clock to the specified buffer. 3283446Smrj * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read 3293446Smrj * else 0. 3303446Smrj * The routine will busy wait for the Update-In-Progress flag to clear. 3313446Smrj * On completion of the reads the Seconds register is re-read and the 3323446Smrj * UIP flag is rechecked to confirm that an clock update did not occur 3333446Smrj * during the accesses. Routine will error exit after 256 attempts. 3343446Smrj * (See bugid 1158298.) 3353446Smrj * Routine returns RTC_NREG (which is 15) bytes of data, as given in the 3363446Smrj * technical reference. This data includes both time and status registers. 3373446Smrj */ 3383446Smrj 3393446Smrj static int 3403446Smrj todpc_rtcget(unsigned char *buf) 3413446Smrj { 3423446Smrj unsigned char reg; 3433446Smrj int i; 3443446Smrj int retries = 256; 3453446Smrj unsigned char *rawp; 3465295Srandyf unsigned char century = RTC_CENTURY; 3475295Srandyf unsigned char day_alrm; 3485295Srandyf unsigned char mon_alrm; 3493446Smrj 3503446Smrj ASSERT(MUTEX_HELD(&tod_lock)); 3513446Smrj 3525295Srandyf day_alrm = pc_rtc_offset.day_alrm; 3535295Srandyf mon_alrm = pc_rtc_offset.mon_alrm; 3545295Srandyf if (pc_rtc_offset.century != 0) { 3555295Srandyf century = pc_rtc_offset.century; 3565295Srandyf } 3575295Srandyf 3583446Smrj outb(RTC_ADDR, RTC_D); /* check if clock valid */ 3593446Smrj reg = inb(RTC_DATA); 3603446Smrj if ((reg & RTC_VRT) == 0) 3613446Smrj return (ENXIO); 3623446Smrj 3633446Smrj checkuip: 3643446Smrj if (retries-- < 0) 3653446Smrj return (EAGAIN); 3663446Smrj outb(RTC_ADDR, RTC_A); /* check if update in progress */ 3673446Smrj reg = inb(RTC_DATA); 3683446Smrj if (reg & RTC_UIP) { 3693446Smrj tenmicrosec(); 3703446Smrj goto checkuip; 3713446Smrj } 3723446Smrj 3733446Smrj for (i = 0, rawp = buf; i < RTC_NREG; i++) { 3743446Smrj outb(RTC_ADDR, i); 3753446Smrj *rawp++ = inb(RTC_DATA); 3763446Smrj } 3775295Srandyf outb(RTC_ADDR, century); /* do century */ 3783446Smrj ((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA); 3795295Srandyf 3805295Srandyf if (day_alrm > 0) { 3815295Srandyf outb(RTC_ADDR, day_alrm); 3825295Srandyf ((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f; 3835295Srandyf } 3845295Srandyf if (mon_alrm > 0) { 3855295Srandyf outb(RTC_ADDR, mon_alrm); 3865295Srandyf ((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA); 3875295Srandyf } 3885295Srandyf 3893446Smrj outb(RTC_ADDR, 0); /* re-read Seconds register */ 3903446Smrj reg = inb(RTC_DATA); 3913446Smrj if (reg != ((struct rtc_t *)buf)->rtc_sec || 3923446Smrj (((struct rtc_t *)buf)->rtc_statusa & RTC_UIP)) 3933446Smrj /* update occured during reads */ 3943446Smrj goto checkuip; 3953446Smrj 3963446Smrj return (0); 3973446Smrj } 3983446Smrj 3993446Smrj /* 4003446Smrj * This routine writes the contents of the given buffer to the real time 4013446Smrj * clock. It is given RTC_NREGP bytes of data, which are the 10 bytes used 4023446Smrj * to write the time and set the alarm. It should be called with the priority 4033446Smrj * raised to 5. 4043446Smrj */ 4053446Smrj static void 4063446Smrj todpc_rtcput(unsigned char *buf) 4073446Smrj { 4083446Smrj unsigned char reg; 4093446Smrj int i; 4105295Srandyf unsigned char century = RTC_CENTURY; 4115295Srandyf unsigned char day_alrm = pc_rtc_offset.day_alrm; 4125295Srandyf unsigned char mon_alrm = pc_rtc_offset.mon_alrm; 413*8936SDan.Mick@Sun.COM unsigned char tmp; 4145295Srandyf 4155295Srandyf if (pc_rtc_offset.century != 0) { 4165295Srandyf century = pc_rtc_offset.century; 4175295Srandyf } 4183446Smrj 4193446Smrj outb(RTC_ADDR, RTC_B); 4203446Smrj reg = inb(RTC_DATA); 4213446Smrj outb(RTC_ADDR, RTC_B); 4223446Smrj outb(RTC_DATA, reg | RTC_SET); /* allow time set now */ 4233446Smrj for (i = 0; i < RTC_NREGP; i++) { /* set the time */ 4243446Smrj outb(RTC_ADDR, i); 4253446Smrj outb(RTC_DATA, buf[i]); 4263446Smrj } 4275295Srandyf outb(RTC_ADDR, century); /* do century */ 4283446Smrj outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century); 4295295Srandyf 4305295Srandyf if (day_alrm > 0) { 4315295Srandyf outb(RTC_ADDR, day_alrm); 432*8936SDan.Mick@Sun.COM /* preserve RTC_VRT bit; some virt envs accept writes there */ 433*8936SDan.Mick@Sun.COM tmp = inb(RTC_DATA) & RTC_VRT; 434*8936SDan.Mick@Sun.COM tmp |= ((struct rtc_t *)buf)->rtc_adom & ~RTC_VRT; 435*8936SDan.Mick@Sun.COM outb(RTC_DATA, tmp); 4365295Srandyf } 4375295Srandyf if (mon_alrm > 0) { 4385295Srandyf outb(RTC_ADDR, mon_alrm); 4395295Srandyf outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon); 4405295Srandyf } 4415295Srandyf 4425295Srandyf outb(RTC_ADDR, RTC_B); 4435295Srandyf reg = inb(RTC_DATA); 4443446Smrj outb(RTC_ADDR, RTC_B); 4453446Smrj outb(RTC_DATA, reg & ~RTC_SET); /* allow time update */ 4463446Smrj } 4473446Smrj 4483446Smrj static tod_ops_t todpc_ops = { 4493446Smrj TOD_OPS_VERSION, 4503446Smrj todpc_get, 4513446Smrj todpc_set, 4525295Srandyf NULL, 4535295Srandyf NULL, 4545295Srandyf todpc_setalarm, 4555295Srandyf todpc_clralarm, 4563446Smrj NULL 4573446Smrj }; 4583446Smrj 4593446Smrj /* 4603446Smrj * Initialize for the default TOD ops vector for use on hardware. 4613446Smrj */ 4623446Smrj 4633446Smrj tod_ops_t *tod_ops = &todpc_ops; 464