diff options
author | randyf <none@none> | 2007-10-20 16:00:42 -0700 |
---|---|---|
committer | randyf <none@none> | 2007-10-20 16:00:42 -0700 |
commit | 2df1fe9ca32bb227b9158c67f5c00b54c20b10fd (patch) | |
tree | 358c576f885c00d42a760d9e35e5b66e77209fe2 /usr/src/uts/i86pc/io/todpc_subr.c | |
parent | 10b3fbf593a6678eec9b50a01903ef4eb73111e4 (diff) | |
download | illumos-gate-2df1fe9ca32bb227b9158c67f5c00b54c20b10fd.tar.gz |
PSARC/2005/469 X86 Energy Star compliance
PSARC/2006/632 PSMI extension for state save and restore
6330209 nge needs to support DDI_SUSPEND/DDI_RESUME
6381827 Suspend to RAM on x86
6393154 audio810 needs to support DDI_SUSPEND/DDI_RESUME
6397047 fd, fdc needs to support Suspend/Resume
6401974 cannot enter S3 with ohci PME enable set on Tyan 2865 with Sun or Tyan 2.01 BIOS
6422613 memscrubber doesn't re-acquire lock before CALLB_CPR_EXIT
6455736 ata/dadk/cmdk should support DDI_SUSPEND/DDI_RESUME
6511370 CPR on SPARC regression
6586018 TODOP Macros in i86pc/sys/machclock.h not in sun4u/sun4v equivilent (Sparc only)
6610124 It takes more than 3 minutes after printing "pci_pre_resume nv_sata:0"
6617143 powerd/pmconfig emits a different default message for an existing on or off action.
--HG--
rename : usr/src/cmd/power/power.conf => usr/src/cmd/power/power.conf.sparc
Diffstat (limited to 'usr/src/uts/i86pc/io/todpc_subr.c')
-rw-r--r-- | usr/src/uts/i86pc/io/todpc_subr.c | 229 |
1 files changed, 220 insertions, 9 deletions
diff --git a/usr/src/uts/i86pc/io/todpc_subr.c b/usr/src/uts/i86pc/io/todpc_subr.c index 7e55876af3..5b92cb9077 100644 --- a/usr/src/uts/i86pc/io/todpc_subr.c +++ b/usr/src/uts/i86pc/io/todpc_subr.c @@ -47,13 +47,79 @@ #include <sys/stat.h> #include <sys/sunddi.h> +#include <sys/acpi/acpi.h> +#include <sys/acpica.h> + static int todpc_rtcget(unsigned char *buf); static void todpc_rtcput(unsigned char *buf); +#define CLOCK_RES 1000 /* 1 microsec in nanosecs */ + +int clock_res = CLOCK_RES; + +/* + * The minimum sleep time till an alarm can be fired. + * This can be tuned in /etc/system, but if the value is too small, + * there is a danger that it will be missed if it takes too long to + * get from the set point to sleep. Or that it can fire quickly, and + * generate a power spike on the hardware. And small values are + * probably only usefull for test setups. + */ +int clock_min_alarm = 4; + /* * Machine-dependent clock routines. */ +extern long gmt_lag; + +struct rtc_offset { + int8_t loaded; + uint8_t day_alrm; + uint8_t mon_alrm; + uint8_t century; +}; + +static struct rtc_offset pc_rtc_offset = {0, 0, 0, 0}; + + +/* + * Entry point for ACPI to pass RTC or other clock values that + * are useful to TOD. + */ +void +pc_tod_set_rtc_offsets(FADT_DESCRIPTOR *fadt) { + int ok = 0; + + /* + * ASSERT is for debugging, but we don't want the machine + * falling over because for some reason we didn't get a valid + * pointer. + */ + ASSERT(fadt); + if (fadt == NULL) { + return; + } + + if (fadt->DayAlrm) { + pc_rtc_offset.day_alrm = fadt->DayAlrm; + ok = 1; + } + + if (fadt->MonAlrm) { + pc_rtc_offset.mon_alrm = fadt->MonAlrm; + ok = 1; + } + + if (fadt->Century) { + pc_rtc_offset.century = fadt->Century; + ok = 1; + } + + pc_rtc_offset.loaded = ok; +} + + /* * Write the specified time into the clock chip. * Must be called with tod_lock held. @@ -123,7 +189,7 @@ todpc_get(tod_ops_t *top) if (tod.tod_year < 69) { if (range_warn && tod.tod_year > 38) { cmn_err(CE_WARN, "hardware real-time clock is out " - "of range -- time needs to be reset"); + "of range -- time needs to be reset"); range_warn = 0; } tod.tod_year += 100 + YRBASE; /* 20xx year */ @@ -135,12 +201,12 @@ todpc_get(tod_ops_t *top) } if (century_warn && BCD_TO_BYTE(rtc.rtc_century) != compute_century) { cmn_err(CE_NOTE, - "The hardware real-time clock appears to have the " - "wrong century: %d.\nSolaris will still operate " - "correctly, but other OS's/firmware agents may " - "not.\nUse date(1) to set the date to the current " - "time to correct the RTC.", - BCD_TO_BYTE(rtc.rtc_century)); + "The hardware real-time clock appears to have the " + "wrong century: %d.\nSolaris will still operate " + "correctly, but other OS's/firmware agents may " + "not.\nUse date(1) to set the date to the current " + "time to correct the RTC.", + BCD_TO_BYTE(rtc.rtc_century)); century_warn = 0; } tod.tod_month = BCD_TO_BYTE(rtc.rtc_mon); @@ -156,6 +222,109 @@ todpc_get(tod_ops_t *top) return (ts); } +#include <sys/promif.h> +/* + * Write the specified wakeup alarm into the clock chip. + * Must be called with tod_lock held. + */ +void +/*ARGSUSED*/ +todpc_setalarm(tod_ops_t *top, int nsecs) +{ + struct rtc_t rtc; + int delta, asec, amin, ahr, adom, amon; + int day_alrm = pc_rtc_offset.day_alrm; + int mon_alrm = pc_rtc_offset.mon_alrm; + + ASSERT(MUTEX_HELD(&tod_lock)); + + /* A delay of zero is not allowed */ + if (nsecs == 0) + return; + + /* Make sure that we delay no less than the minimum time */ + if (nsecs < clock_min_alarm) + nsecs = clock_min_alarm; + + if (todpc_rtcget((unsigned char *)&rtc)) + return; + + /* + * Compute alarm secs, mins and hrs, and where appropriate, dom + * and mon. rtc bytes are in binary-coded decimal, so we have + * to convert. + */ + delta = nsecs + BCD_TO_BYTE(rtc.rtc_sec); + asec = delta % 60; + + delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_min); + amin = delta % 60; + + delta = (delta / 60) + BCD_TO_BYTE(rtc.rtc_hr); + ahr = delta % 24; + + if (day_alrm == 0 && delta >= 24) { + prom_printf("No day alarm - set to end of today!\n"); + asec = 59; + amin = 59; + ahr = 23; + } else { + int mon = BCD_TO_BYTE(rtc.rtc_mon); + static int dpm[] = + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + adom = (delta / 24) + BCD_TO_BYTE(rtc.rtc_dom); + + if (mon_alrm == 0) { + if (adom > dpm[mon]) { + prom_printf("No mon alarm - " + "set to end of current month!\n"); + asec = 59; + amin = 59; + ahr = 23; + adom = dpm[mon]; + } + } else { + for (amon = mon; + amon <= 12 && adom > dpm[amon]; amon++) { + adom -= dpm[amon]; + } + if (amon > 12) { + prom_printf("Alarm too far in future - " + "set to end of current year!\n"); + asec = 59; + amin = 59; + ahr = 23; + adom = dpm[12]; + amon = 12; + } + rtc.rtc_amon = BYTE_TO_BCD(amon); + } + + rtc.rtc_adom = BYTE_TO_BCD(adom); + } + + rtc.rtc_asec = BYTE_TO_BCD(asec); + rtc.rtc_amin = BYTE_TO_BCD(amin); + rtc.rtc_ahr = BYTE_TO_BCD(ahr); + + rtc.rtc_statusb |= RTC_AIE; /* Enable alarm interrupt */ + + todpc_rtcput((unsigned char *)&rtc); +} + +/* + * Clear an alarm. This is effectively setting an alarm of 0. + */ +void +/*ARGSUSED*/ +todpc_clralarm(tod_ops_t *top) +{ + mutex_enter(&tod_lock); + todpc_setalarm(top, 0); + mutex_exit(&tod_lock); +} + /* * Routine to read contents of real time clock to the specified buffer. * Returns ENXIO if clock not valid, or EAGAIN if clock data cannot be read @@ -176,9 +345,18 @@ todpc_rtcget(unsigned char *buf) int i; int retries = 256; unsigned char *rawp; + unsigned char century = RTC_CENTURY; + unsigned char day_alrm; + unsigned char mon_alrm; ASSERT(MUTEX_HELD(&tod_lock)); + day_alrm = pc_rtc_offset.day_alrm; + mon_alrm = pc_rtc_offset.mon_alrm; + if (pc_rtc_offset.century != 0) { + century = pc_rtc_offset.century; + } + outb(RTC_ADDR, RTC_D); /* check if clock valid */ reg = inb(RTC_DATA); if ((reg & RTC_VRT) == 0) @@ -198,8 +376,18 @@ checkuip: outb(RTC_ADDR, i); *rawp++ = inb(RTC_DATA); } - outb(RTC_ADDR, RTC_CENTURY); /* do century */ + outb(RTC_ADDR, century); /* do century */ ((struct rtc_t *)buf)->rtc_century = inb(RTC_DATA); + + if (day_alrm > 0) { + outb(RTC_ADDR, day_alrm); + ((struct rtc_t *)buf)->rtc_adom = inb(RTC_DATA) & 0x3f; + } + if (mon_alrm > 0) { + outb(RTC_ADDR, mon_alrm); + ((struct rtc_t *)buf)->rtc_amon = inb(RTC_DATA); + } + outb(RTC_ADDR, 0); /* re-read Seconds register */ reg = inb(RTC_DATA); if (reg != ((struct rtc_t *)buf)->rtc_sec || @@ -221,6 +409,13 @@ todpc_rtcput(unsigned char *buf) { unsigned char reg; int i; + unsigned char century = RTC_CENTURY; + unsigned char day_alrm = pc_rtc_offset.day_alrm; + unsigned char mon_alrm = pc_rtc_offset.mon_alrm; + + if (pc_rtc_offset.century != 0) { + century = pc_rtc_offset.century; + } outb(RTC_ADDR, RTC_B); reg = inb(RTC_DATA); @@ -230,8 +425,20 @@ todpc_rtcput(unsigned char *buf) outb(RTC_ADDR, i); outb(RTC_DATA, buf[i]); } - outb(RTC_ADDR, RTC_CENTURY); /* do century */ + outb(RTC_ADDR, century); /* do century */ outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_century); + + if (day_alrm > 0) { + outb(RTC_ADDR, day_alrm); + outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_adom); + } + if (mon_alrm > 0) { + outb(RTC_ADDR, mon_alrm); + outb(RTC_DATA, ((struct rtc_t *)buf)->rtc_amon); + } + + outb(RTC_ADDR, RTC_B); + reg = inb(RTC_DATA); outb(RTC_ADDR, RTC_B); outb(RTC_DATA, reg & ~RTC_SET); /* allow time update */ } @@ -240,6 +447,10 @@ static tod_ops_t todpc_ops = { TOD_OPS_VERSION, todpc_get, todpc_set, + NULL, + NULL, + todpc_setalarm, + todpc_clralarm, NULL }; |