Skip to contentSkip to conversation

rtc-acpi, with wakeup support

PATCH In review David Brownell 2 participants 5 messages Jul 15, 2006 – Sep 29, 2006

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 rtc-acpi, with wakeup support

The new RTC framework hasn't been very usable on most x86 desktop PCs ... so here's a driver using the ACPI RTC and PNPACPI. AFAICT it could mostly replace drivers/char/rtc.c on ACPI systems.

One of the goals here was to expose this RTC as a normal wakeup event source ... no /proc/acpi/alarm thing, just the same userspace interfaces as on non-ACPI RTCs, and non-RTC devices. That works somewhat ... for the very first time, I've seen ACPI wakeup behave!

But ACPI doesn't always behave after hardware wakeup events, so

echo disabled > /sys/class/rtc/rtc0/device/power/wakeup

may be appropriate on the systems where it doesn't yet work.

- Dave

p.s. A followup message will include a userspace program which
makes it easier to try the RTC wakeup mechanism.

This is the guts of a "new RTC framework" driver for the ACPI RTC, which is AT-compatible except for new registers, more nvram, and system wakeup hooks. Advantages of this vs. drivers/char/rtc.c include:

- Driver binding uses PNPACPI; it now has "real driver model support". Non-ACPI platforms should be able to reuse the core code.

- Given driver model support, now /sys/devices/.../power/wakeup and normal rtc ioctls will control RTC wakeups instead of that yucky /proc/acpi/alarm interface. This is a fully platform-neutral API, easily supported on non-PC hardware.

- This version supports wakeup events up to a year in the future (not just 24 hours) when ACPI reports RTC hardware which can do that.

The RTC bits seem to work OK (except for changing irq frequency, or using the optional "century" register -- neither is implemented), although some tools like "hwclock" expect /dev/rtc not /dev/rtc0.

ACPI wakeup is more problematic; after hardware wakeup, the software re-activation sometimes wedges. (That's not an RTC-specific problem.) I've seen one system be usable after the timer wakeup from S3/STR, but another wedged after timer wakeup from S4/swsusp.

drivers/rtc/Kconfig +15 -0
Index: g26/drivers/rtc/Kconfig
===================================================================
@@ -83,6 +83,21 @@ config RTC_INTF_DEV_UIE_EMUL
 comment "RTC drivers"
 	depends on RTC_CLASS
 
+config RTC_DRV_ACPI
+	tristate "ACPI real time clock"
+	depends on RTC_CLASS && PNPACPI
+	help
+	  Say "yes" here to get direct support for the real time clock
+	  found in every ACPI-based PC.
+
+	  This RTC can be used to wake the system from suspend-to-RAM or
+	  standby sleep modes, and on some systems from suspend-to-disk.
+
+	  This obsoletes the old /proc/acpi/alarm interface.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-acpi.
+
 config RTC_DRV_X1205
 	tristate "Xicor/Intersil X1205"
 	depends on RTC_CLASS && I2C
drivers/rtc/Makefile +1 -0
Index: g26/drivers/rtc/Makefile
===================================================================
@@ -11,6 +11,7 @@ obj-$(CONFIG_RTC_INTF_SYSFS)	+= rtc-sysf
 obj-$(CONFIG_RTC_INTF_PROC)	+= rtc-proc.o
 obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o
 
+obj-$(CONFIG_RTC_DRV_ACPI)	+= rtc-acpi.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_TEST)	+= rtc-test.o
drivers/rtc/rtc-acpi.c new file +515 -0

515 lines not on this page · raw message

drivers/acpi/sleep/proc.c +16 -1
Index: g26/drivers/acpi/sleep/proc.c
===================================================================
@@ -70,6 +70,14 @@ acpi_system_write_sleep(struct file *fil
 }
 #endif				/* CONFIG_ACPI_SLEEP_PROC_SLEEP */
 
+#if defined(CONFIG_RTC_DRV_ACPI) || defined(CONFIG_RTC_DRV_ACPI_MODULE)
+/* use code that fits into standard Linux driver frameworks */
+#else
+#define	HAVE_PRIVATE_ALARM
+#endif
+
+#ifdef	HAVE_PRIVATE_ALARM
+
 static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
 {
 	u32 sec, min, hr;
@@ -339,6 +347,8 @@ acpi_system_write_alarm(struct file *fil
       end:
 	return_VALUE(result ? result : count);
 }
+#endif	/* HAVE_PRIVATE_ALARM */
+
 
 extern struct list_head acpi_wakeup_device_list;
 extern spinlock_t acpi_device_lock;
@@ -452,6 +462,7 @@ static const struct file_operations acpi
 };
 #endif				/* CONFIG_ACPI_SLEEP_PROC_SLEEP */
 
+#ifdef	HAVE_PRIVATE_ALARM
 static const struct file_operations acpi_system_alarm_fops = {
 	.open = acpi_system_alarm_open_fs,
 	.read = seq_read,
@@ -467,6 +478,7 @@ static u32 rtc_handler(void *context)
 
 	return ACPI_INTERRUPT_HANDLED;
 }
+#endif	/* HAVE_PRIVATE_ALARM */
 
 static int acpi_sleep_proc_init(void)
 {
@@ -484,6 +496,7 @@ static int acpi_sleep_proc_init(void)
 		entry->proc_fops = &acpi_system_sleep_fops;
 #endif
 
+#ifdef	HAVE_PRIVATE_ALARM
 	/* 'alarm' [R/W] */
 	entry =
 	    create_proc_entry("alarm", S_IFREG | S_IRUGO | S_IWUSR,
@@ -491,6 +504,9 @@ static int acpi_sleep_proc_init(void)
 	if (entry)
 		entry->proc_fops = &acpi_system_alarm_fops;
 
+	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
+#endif	/* HAVE_PRIVATE_ALARM */
+
 	/* 'wakeup device' [R/W] */
 	entry =
 	    create_proc_entry("wakeup", S_IFREG | S_IRUGO | S_IWUSR,
@@ -498,7 +514,6 @@ static int acpi_sleep_proc_init(void)
 	if (entry)
 		entry->proc_fops = &acpi_system_wakeup_device_fops;
 
-	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
 	return 0;
 }
 
drivers/acpi/utilities/utglobal.c +1 -0
Index: g26/drivers/acpi/utilities/utglobal.c
===================================================================
@@ -840,5 +840,6 @@ void acpi_ut_init_globals(void)
 	return_VOID;
 }
 
+ACPI_EXPORT_SYMBOL(acpi_gbl_FADT)
 ACPI_EXPORT_SYMBOL(acpi_dbg_level)
 ACPI_EXPORT_SYMBOL(acpi_dbg_layer)
#1 lore raw

rtcwakeup.c

Quoted 3 lines

p.s. A followup message will include a userspace program which
makes it easier to try the RTC wakeup mechanism.

For example, if your system actually supports RTC wakeup correctly:

        rtcwake -t $(date -u -d 'tomorrow 6:30am' +'%s') -m mem

will set up the system to wake up tomorrow at 6:30am, then suspend-to-RAM by writing "mem" to /sys/power/state.

Or for testing kernels, unattended scripts like this may help:

        while true
        do
                echo "suspend-to-disk for 10 minutes starting $(date)" rtcwake -s $((10 * 60)) -m disk
                sleep 500
        done

If there are many RTC utilities out there that aren't x86-specific, I didn't happen to find them. Ergo this one.

- Dave

#include <stdio.h>
#include <getopt.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>

#include <linux/rtc.h>

// #define DEBUG

/*
* rtcwake -- enter a system sleep state until specified wakeup time. *
* This uses cross-platform Linux interfaces to enter a system sleep state,
* and leave it no later than a specified time. It uses any RTC framework
* driver that supports standard driver model wakeup flags.
*
* This is normally used like the old "apmsleep" utility, to wake from a
* suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM). Most
* platforms can implement those without analogues of BIOS, APM, or ACPI. *
* On some systems, this can also be used like "nvram-wakeup", waking
* from states like ACPI S4 (suspend to disk). Not all systems have
* persistent media that are appropriate for such suspend modes. */

static char *progname;

#ifdef DEBUG
#define VERSION "1.0 dev (" __DATE__ " " __TIME__ ")"
#else
#define VERSION "0.8"
#endif

static unsigned verbose;

static int may_wakeup(const char *devname)

{
	char	buf[128], *s;
	FILE	*f;

	snprintf(buf, sizeof buf, "/sys/class/rtc/%s/device/power/wakeup",
			devname);
	f = fopen(buf, "r");
	if (!f) {
		perror(buf);
		return 0;
	}
	fgets(buf, sizeof buf, f);
	fclose(f);

	s = strchr(buf, '\n');
	if (!s)
		return 0;
	*s = 0;

	/* wakeup events could be disabled or not supported */
	return strcmp(buf, "enabled") == 0;
}

/* all times should be in UTC */
static time_t sys_time;
static time_t rtc_time;

static int get_basetimes(int fd)

{
	struct tm	tm;
	time_t		offset;
	struct rtc_time	rtc;

	/* record offset of mktime(), so we can reverse it */
	memset(&tm, 0, sizeof tm);
	tm.tm_year = 70;
	offset = mktime(&tm);

	/* read system and rtc clocks "at the same time"; both in UTC */
	sys_time = time(0);
	if (sys_time == (time_t)-1) {
		perror("read system time");
		return 0;
	}
	if (ioctl(fd, RTC_RD_TIME, &rtc) < 0) {
		perror("read rtc time");
		return 0;
	}

	/* convert rtc_time to normal arithmetic-friendly form */
	tm.tm_sec = rtc.tm_sec;
	tm.tm_min = rtc.tm_min;
	tm.tm_hour = rtc.tm_hour;
	tm.tm_mday = rtc.tm_mday;
	tm.tm_mon = rtc.tm_mon;
	tm.tm_year = rtc.tm_year;
	tm.tm_wday = rtc.tm_wday;
	tm.tm_yday = rtc.tm_yday;
	tm.tm_isdst = rtc.tm_isdst;

	if (verbose) {
		printf("\toffset  = %ld\n", offset);
		printf("\tsystime = %s", asctime(gmtime(&sys_time)));
		printf("\trtctime = %s", asctime(&tm));
	}

	rtc_time = mktime(&tm) - offset;
	if (rtc_time == (time_t)-1) {
		perror("convert rtc time");
		return 0;
	}

	return 1;
}

static int setup_alarm(int fd, time_t *wakeup)

{
	struct tm		*tm;
	struct rtc_wkalrm	wake;

	tm = gmtime(wakeup);

	wake.time.tm_sec = tm->tm_sec;
	wake.time.tm_min = tm->tm_min;
	wake.time.tm_hour = tm->tm_hour;
	wake.time.tm_mday = tm->tm_mday;
	wake.time.tm_mon = tm->tm_mon;
	wake.time.tm_year = tm->tm_year;
	wake.time.tm_wday = tm->tm_wday;
	wake.time.tm_yday = tm->tm_yday;
	wake.time.tm_isdst = tm->tm_isdst;

	/* many rtc alarms only support up to 24 hours from 'now' ... */
	if ((rtc_time + 24 * 60 * 60) > *wakeup) {
		if (ioctl(fd, RTC_ALM_SET, &wake.time) < 0) {
			perror("set rtc alarm");
			return 0;
		}
		if (ioctl(fd, RTC_AIE_ON, 0) < 0) {
			perror("enable rtc alarm");
			return 0;
		}

	/* ... so use the "more than 24 hours" request only if we must */
	} else {
		/* avoid an extra AIE_ON call */
		wake.enabled = 1;

		if (ioctl(fd, RTC_WKALM_SET, &wake) < 0) {
			perror("set rtc wake alarm");
			return 0;
		}
	}

	return 1;
}

static void suspend_system(const char *suspend)

{
	FILE	*f = fopen("/sys/power/state", "w");

	if (!f) {
		perror("/sys/power/state");
		return;
	}

	fprintf(f, "%s\n", suspend);
	fflush(f);

	/* this executes after wake from suspend */
	fclose(f);
}

int main(int argc, char **argv)

{
	static char		*devname = "rtc0";
	static unsigned		seconds = 0;
	static char		*suspend = "standby";

	int		t;
	int		fd;
	time_t		alarm = 0;

	progname = strrchr(argv[0], '/');
	if (progname)
		progname++;
	else
		progname = argv[0];
	if (chdir("/dev/") < 0) {
		perror("chdir /dev");
		return 1;
	}

	while ((t = getopt(argc, argv, "d:m:s:t:Vv")) != EOF) {
		switch (t) {

		case 'd':
			devname = optarg;
			break;

		/* what system power mode to use?  for now handle only
		 * standardized mode names; eventually when systems define
		 * their own state names, parse /sys/power/state.
		 *
		 * "on" is used just to test the RTC alarm mechanism,
		 * bypassing all the wakeup-from-sleep infrastructure.
		 */
		case 'm':
			if (strcmp(optarg, "standby") == 0
					|| strcmp(optarg, "mem") == 0
					|| strcmp(optarg, "disk") == 0
					|| strcmp(optarg, "on") == 0
					) {
				suspend = optarg;
				break;
			}
			printf("%s: unrecognized suspend state '%s'\n",
					progname, optarg);
			goto usage;

		/* alarm time, seconds-to-sleep (relative) */
		case 's':
			t = atoi(optarg);
			if (t < 0) {
				printf("%s: illegal interval %s seconds\n",
						progname, optarg);
				goto usage;
			}
			seconds = t;
			break;

		/* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */
		case 't':
			t = atoi(optarg);
			if (t < 0) {
				printf("%s: illegal time_t value %s\n",
						progname, optarg);
				goto usage;
			}
			alarm = t;
			break;

		case 'v':
			verbose++;
			break;

		case 'V':
			printf("%s: version %s\n", progname, VERSION);
			break;

		default:
usage:
			printf("usage: %s [options]"
				"\n\t"
				"-d rtc0|rtc1|...\t(select rtc)"
				"\n\t"
				"-m standby|mem|...\t(sleep mode)"
				"\n\t"
				"-s seconds\t\t(seconds to sleep)"
				"\n\t"
				"-t time_t\t\t(time to wake)"
				"\n\t"
				"-v\t\t\t(verbose messages)"
				"\n\t"
				"-V\t\t\t(show version)"
				"\n",
				progname);
			return 1;
		}
	}

	if (!alarm && !seconds) {
		printf("%s: must provide wake time\n", progname);
		goto usage;
	}

	/* this RTC must exist and (if we'll sleep) be wakeup-enabled */
	fd = open(devname, O_RDONLY);
	if (fd < 0) {
		perror(devname);
		return 1;
	}
	if (strcmp(suspend, "on") != 0 && !may_wakeup(devname)) {
		printf("%s: %s not enabled for wakeup events\n",
				progname, devname);
		return 1;
	}

	/* relative or absolute alarm time, normalized to time_t */
	if (!get_basetimes(fd))
		return 1;
	if (verbose)
		printf("alarm %ld, sys_time %ld, rtc_time %ld, seconds %u\n",
				alarm, sys_time, rtc_time, seconds);
	if (alarm) {
		if (alarm < sys_time) {
			printf("%s: time doesn't go backward to %s",
					progname, ctime(&alarm));
			return 1;
		}
		alarm += sys_time - rtc_time;
	} else
		alarm = rtc_time + seconds + 1;
	if (setup_alarm(fd, &alarm) < 0)
		return 1;

	printf("%s: wakeup from \"%s\" using %s at %s",
			progname, suspend, devname,
			ctime(&alarm));
	fflush(stdout);
	usleep(10 * 1000);

	if (strcmp(suspend, "on") != 0)
		suspend_system(suspend);
	else {
		unsigned long data;

		(void) read(fd, &data, sizeof data);
	}

	if (ioctl(fd, RTC_AIE_OFF, 0) < 0)
		perror("disable rtc alarm interrupt");

	close(fd);
	return 0;
}
#2 lore raw
In reply to David Brownell

Hmm, so -- any comments? This applies just fine to RC2 of course (it came out minutes before RC2 "shipped"). Seems to me this would be appropriate for the next MM release.

Also, given some mechanism to tell whether this alarm woke the system, this would seem to be the kind of infrastructure needed to make the "deepening suspend" work correctly. That is, idle system enters the light weight "standby" powersave mode, then if it stays idle for long enough for the timer to wake it could enter suspend-to-RAM (or that new "suspend-to-both" mode). There's certainly enough idle time on most laptops for such mechanisms to help save significant amounts of battery power, and it's best if such things don't explicitly depend on features like ACPI.

- Dave

Quoted 23 lines · David Brownell

The new RTC framework hasn't been very usable on most x86 desktop PCs ... so here's a driver using the ACPI RTC and PNPACPI. AFAICT it could mostly replace drivers/char/rtc.c on ACPI systems.

One of the goals here was to expose this RTC as a normal wakeup event source ... no /proc/acpi/alarm thing, just the same userspace interfaces as on non-ACPI RTCs, and non-RTC devices. That works somewhat ... for the very first time, I've seen ACPI wakeup behave!

But ACPI doesn't always behave after hardware wakeup events, so

echo disabled > /sys/class/rtc/rtc0/device/power/wakeup

may be appropriate on the systems where it doesn't yet work.

- Dave

p.s. A followup message will include a userspace program which
makes it easier to try the RTC wakeup mechanism.

#3 lore raw

Hi David.

Quoted 3 lines · David Brownell

Hmm, so -- any comments? This applies just fine to RC2 of course (it came out minutes before RC2 "shipped"). Seems to me this would be appropriate for the next MM release.

Didn't notice it before now. I'll forward your message to suspend2-devel (we have people there who might be interested), and try it myself.

Quoted 9 lines

Also, given some mechanism to tell whether this alarm woke the system, this would seem to be the kind of infrastructure needed to make the "deepening suspend" work correctly. That is, idle system enters the light weight "standby" powersave mode, then if it stays idle for long enough for the timer to wake it could enter suspend-to-RAM (or that new "suspend-to-both" mode). There's certainly enough idle time on most laptops for such mechanisms to help save significant amounts of battery power, and it's best if such things don't explicitly depend on features like ACPI.

Yes. I'll look at it with a view to seeing if we can use it in Suspend2. I get requests for it from time to time, and it would be good to finally be able to do it.

Regards,

Nigel

Signature · 4 lines
Nigel, Michelle and Alisdair Cunningham
5 Mitchell Street
Cobden 3266
Victoria, Australia
#4 lore raw

PATCH 1/1 rtc-acpi, with wakeup support

In reply to David Brownell

[ NOT FOR MERGING -- just a patch refresh ]

This is a "new RTC framework" driver for the ACPI RTC. It's a standard AT-compatible RTC, with ACPI extensions that are mostly invisible to the core code:

- new registers, for wakeup intervals longer than a day (up to a year)

    and a century register for Y2K/Y3K awareness;

- more nvram (not that this driver cares);
- system wakeup hooks, through ACPI (that is, southbridge hooks)

Advantages of this vs. drivers/char/rtc.c (use one _or_ the other) include:

- Driver binding now has "real driver model support":
* By preference, this uses PNPACPI;
* If that's not configured, it creates a platform_device (but that should really live someplace else, maybe ACPI init code)

- /sys/devices/.../power/wakeup and normal rtc ioctls manage RTC wakeups using platform-neutral API (also supported on some embedded Linuxes) not that yucky /proc/acpi/alarm interface.

Currently unsupported features:

- changing periodic irq frequency (char/rtc.c allows that).
- using the optional "century" register (no Linux code does, yet).

Issues of note:

- Some tools like "hwclock" expect /dev/rtc not /dev/rtc0; patches are available for util-linux-2.13-pre7 and busybox.
- ACPI wakeup doesn't always work: after hardware wakeup, the software re-activation commonly wedges. Some systems are very usable after timer wakeup from S3/STR or S4/swsusp, other are not. (This issue is generic to ACPI, not specific to RTC.) Workaround by disabling RTC wakeup via "echo disable > /sys/devices/.../wakeup".

Those issues are not caused by this code, just exposed by it.

A significant change from the previous version is that this one doesn't require PNPACPI ... although the way it works without that is klugey, the innards are now factored better. Eventually arch/*/ platform code on PCs should probably create the platform device. ACPI would then be able to just add the relevant extensions to platform_data, providing a relatively clean solution to the enumeration problem, probably even one that could work on non-ACPI hardware.

drivers/rtc/Kconfig +22 -4
Index: g26/drivers/rtc/Kconfig
===================================================================
@@ -95,6 +95,21 @@ config RTC_INTF_DEV_UIE_EMUL
 comment "RTC drivers"
 	depends on RTC_CLASS
 
+config RTC_DRV_ACPI
+	tristate "ACPI real time clock"
+	depends on RTC_CLASS && ACPI
+	help
+	  Say "yes" here to get direct support for the real time clock
+	  found in every ACPI-based PC.
+
+	  This RTC can be used to wake the system from suspend-to-RAM or
+	  standby sleep modes, and on some systems from suspend-to-disk.
+
+	  This obsoletes the old /proc/acpi/alarm interface.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-acpi.
+
 config RTC_DRV_X1205
 	tristate "Xicor/Intersil X1205"
 	depends on RTC_CLASS && I2C
@@ -221,11 +236,14 @@ config RTC_DRV_S3C
 	  will be called rtc-s3c.
 
 config RTC_DRV_M48T86
-	tristate "ST M48T86/Dallas DS12887"
-	depends on RTC_CLASS
+	tristate "ST M48T86, Dallas DS12887 and compatible"
+	depends on RTC_CLASS && !X86_PC
 	help
-	  If you say Y here you will get support for the
-	  ST M48T86 and Dallas DS12887 RTC chips.
+	  If you say Y here you will get support for the ST M48T86, Dallas
+	  DS12887, and compatible RTC chips.  To software, these are all
+	  but identical to the "cmos clock" used in x86 PCs, although this
+	  driver does not obey the locking or ACPI integration rules
+	  used on that architecture.
 
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-m48t86.
drivers/rtc/Makefile +1 -0
Index: g26/drivers/rtc/Makefile
===================================================================
@@ -15,6 +15,7 @@ obj-$(CONFIG_RTC_INTF_SYSFS)	+= rtc-sysf
 obj-$(CONFIG_RTC_INTF_PROC)	+= rtc-proc.o
 obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o
 
+obj-$(CONFIG_RTC_DRV_ACPI)	+= rtc-acpi.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_TEST)	+= rtc-test.o
drivers/rtc/rtc-acpi.c new file +725 -0

725 lines not on this page · raw message

drivers/acpi/sleep/proc.c +16 -1
Index: g26/drivers/acpi/sleep/proc.c
===================================================================
@@ -70,6 +70,14 @@ acpi_system_write_sleep(struct file *fil
 }
 #endif				/* CONFIG_ACPI_SLEEP_PROC_SLEEP */
 
+#if defined(CONFIG_RTC_DRV_ACPI) || defined(CONFIG_RTC_DRV_ACPI_MODULE)
+/* use code that fits into standard Linux driver frameworks */
+#else
+#define	HAVE_PRIVATE_ALARM
+#endif
+
+#ifdef	HAVE_PRIVATE_ALARM
+
 static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
 {
 	u32 sec, min, hr;
@@ -339,6 +347,8 @@ acpi_system_write_alarm(struct file *fil
       end:
 	return_VALUE(result ? result : count);
 }
+#endif	/* HAVE_PRIVATE_ALARM */
+
 
 extern struct list_head acpi_wakeup_device_list;
 extern spinlock_t acpi_device_lock;
@@ -452,6 +462,7 @@ static const struct file_operations acpi
 };
 #endif				/* CONFIG_ACPI_SLEEP_PROC_SLEEP */
 
+#ifdef	HAVE_PRIVATE_ALARM
 static const struct file_operations acpi_system_alarm_fops = {
 	.open = acpi_system_alarm_open_fs,
 	.read = seq_read,
@@ -467,6 +478,7 @@ static u32 rtc_handler(void *context)
 
 	return ACPI_INTERRUPT_HANDLED;
 }
+#endif	/* HAVE_PRIVATE_ALARM */
 
 static int acpi_sleep_proc_init(void)
 {
@@ -484,6 +496,7 @@ static int acpi_sleep_proc_init(void)
 		entry->proc_fops = &acpi_system_sleep_fops;
 #endif
 
+#ifdef	HAVE_PRIVATE_ALARM
 	/* 'alarm' [R/W] */
 	entry =
 	    create_proc_entry("alarm", S_IFREG | S_IRUGO | S_IWUSR,
@@ -491,6 +504,9 @@ static int acpi_sleep_proc_init(void)
 	if (entry)
 		entry->proc_fops = &acpi_system_alarm_fops;
 
+	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
+#endif	/* HAVE_PRIVATE_ALARM */
+
 	/* 'wakeup device' [R/W] */
 	entry =
 	    create_proc_entry("wakeup", S_IFREG | S_IRUGO | S_IWUSR,
@@ -498,7 +514,6 @@ static int acpi_sleep_proc_init(void)
 	if (entry)
 		entry->proc_fops = &acpi_system_wakeup_device_fops;
 
-	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
 	return 0;
 }
 
drivers/acpi/utilities/utglobal.c +1 -0
Index: g26/drivers/acpi/utilities/utglobal.c
===================================================================
@@ -840,5 +840,6 @@ void acpi_ut_init_globals(void)
 	return_VOID;
 }
 
+ACPI_EXPORT_SYMBOL(acpi_gbl_FADT)
 ACPI_EXPORT_SYMBOL(acpi_dbg_level)
 ACPI_EXPORT_SYMBOL(acpi_dbg_layer)