summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc/io/todpc_subr.c
diff options
context:
space:
mode:
authorrandyf <none@none>2007-10-20 16:00:42 -0700
committerrandyf <none@none>2007-10-20 16:00:42 -0700
commit2df1fe9ca32bb227b9158c67f5c00b54c20b10fd (patch)
tree358c576f885c00d42a760d9e35e5b66e77209fe2 /usr/src/uts/i86pc/io/todpc_subr.c
parent10b3fbf593a6678eec9b50a01903ef4eb73111e4 (diff)
downloadillumos-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.c229
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
};