1. 01 6月, 2007 1 次提交
  2. 09 5月, 2007 4 次提交
    • M
      rtc-cmos: make it load on PNPBIOS systems · 41ac8df9
      Marko Vrh 提交于
      Replace CONFIG_PNPACPI with CONFIG_PNP, so it loads on ACPI-less PNPBIOS
      systems.
      Signed-off-by: NMarko Vrh <mvrh@freeshells.ch>
      Acked-by: NDavid Brownell <dbrownell@users.sourceforge.net>
      Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
      Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
      41ac8df9
    • D
      rtc-cmos wakeup interface · 87ac84f4
      David Brownell 提交于
      I finally got around to testing the updated wakeup event hooks for rtc-cmos,
      and they follow in two patches:
      
       - Interface update ... when a simple enable_irq_wake() doesn't suffice,
         the platform data can hold suspend/resume callback hooks.
      
       - ACPI implementation ... provides callback hooks to do ACPI magic, and
         eliminate the legacy /proc/acpi/alarm file.
      
      The interface update could go into 2.6.21, but that's not essential; they
      will be NOPs on most PCs, without the ACPI stuff.
      
      I suspect the ACPI folk may have opinions about how to merge that second
      patch, and how to obsolete that legacy procfs file.  I'd like to see that
      merge into 2.6.22 if possible...
      
      As for how to kick it in ... two ways:
      
       - The appended "rtcwake" program; updated since the last time it was
         posted, it deals much better with timezones and DST.
      
       - Write the /sys/class/rtc/.../wakealarm file, then go to sleep.
      
      For some reason RTC wake from "swsusp" stopped working on a system where
      it previously worked; the alarm setting appears to get clobbered.  But
      on the bright side, RTC wake from "standby" worked on a system that had
      never been able to resume from that state before ... IDEACPI is my guess
      as to why it finally started to work.  It's the old "two steps forward,
      one step back" dance, I guess.
      
      - Dave
      
      /* gcc -Wall -Os -o rtcwake rtcwake.c */
      
      #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>
      
      /* constants from legacy PC/AT hardware */
      #define	RTC_PF	0x40
      #define	RTC_AF	0x20
      #define	RTC_UF	0x10
      
      /*
       * 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.
       *
       * The best way to set the system's RTC is so that it holds the current
       * time in UTC.  Use the "-l" flag to tell this program that the system
       * RTC uses a local timezone instead (maybe you dual-boot MS-Windows).
       */
      
      static char		*progname;
      
      #ifdef	DEBUG
      #define	VERSION	"1.0 dev (" __DATE__ " " __TIME__ ")"
      #else
      #define	VERSION	"0.9"
      #endif
      
      static unsigned		verbose;
      static int		rtc_is_utc = -1;
      
      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;
      	struct rtc_time	rtc;
      
      	/* this process works in RTC time, except when working
      	 * with the system clock (which always uses UTC).
      	 */
      	if (rtc_is_utc)
      		setenv("TZ", "UTC", 1);
      	tzset();
      
      	/* read rtc and system clocks "at the same time", or as
      	 * precisely (+/- a second) as we can read them.
      	 */
      	if (ioctl(fd, RTC_RD_TIME, &rtc) < 0) {
      		perror("read rtc time");
      		return 0;
      	}
      	sys_time = time(0);
      	if (sys_time == (time_t)-1) {
      		perror("read system time");
      		return 0;
      	}
      
      	/* convert rtc_time to normal arithmetic-friendly form,
      	 * updating tm.tm_wday as used by asctime().
      	 */
      	memset(&tm, 0, sizeof tm);
      	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_isdst = rtc.tm_isdst;	/* stays unspecified? */
      	rtc_time = mktime(&tm);
      
      	if (rtc_time == (time_t)-1) {
      		perror("convert rtc time");
      		return 0;
      	}
      
      	if (verbose) {
      		if (!rtc_is_utc) {
      			printf("\ttzone   = %ld\n", timezone);
      			printf("\ttzname  = %s\n", tzname[daylight]);
      			gmtime_r(&rtc_time, &tm);
      		}
      		printf("\tsystime = %ld, (UTC) %s",
      				(long) sys_time, asctime(gmtime(&sys_time)));
      		printf("\trtctime = %ld, (UTC) %s",
      				(long) rtc_time, asctime(&tm));
      	}
      
      	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:lm:s:t:uVv")) != EOF) {
      		switch (t) {
      
      		case 'd':
      			devname = optarg;
      			break;
      
      		case 'l':
      			rtc_is_utc = 0;
      			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 'u':
      			rtc_is_utc = 1;
      			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"
      				"-l\t\t\t(RTC uses local timezone)"
      				"\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"
      				"-u\t\t\t(RTC uses UTC)"
      				"\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;
      	}
      
      	/* REVISIT:  if /etc/adjtime exists, read it to see what
      	 * the util-linux version of hwclock assumes.
      	 */
      	if (rtc_is_utc == -1) {
      		printf("%s: assuming RTC uses UTC ...\n", progname);
      		rtc_is_utc = 1;
      	}
      
      	/* 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;
      
      	sync();
      	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;
      
      		do {
      			t = read(fd, &data, sizeof data);
      			if (t < 0) {
      				perror("rtc read");
      				break;
      			}
      			if (verbose)
      				printf("... %s: %03lx\n", devname, data);
      		} while (!(data & RTC_AF));
      	}
      
      	if (ioctl(fd, RTC_AIE_OFF, 0) < 0)
      		perror("disable rtc alarm interrupt");
      
      	close(fd);
      	return 0;
      }
      
      This patch:
      
      Make rtc-cmos do the relevant magic so this RTC can wake the system from a
      sleep state.  That magic comes in two basic flavors:
      
       - Straightforward:  enable_irq_wake(), the way it'd work on most SOC chips;
         or generally with system sleep states which don't disable core IRQ logic.
      
       - Roundabout, using non-IRQ platform hooks.  This is needed with ACPI and
         one almost-clone chip which uses a special wakeup-only alarm.  (That's
         the RTC used on Footbridge boards, FWIW, which don't do PM in Linux.)
      
      A separate patch implements those hooks for ACPI platforms, so that rtc_cmos
      can issue system wakeup events (and its sysfs "wakealarm" attribute works on
      at least some systems).
      Signed-off-by: NDavid Brownell <dbrownell@users.sourceforge.net>
      Cc: Alessandro Zummo <a.zummo@towertech.it>
      Cc: Len Brown <lenb@kernel.org>
      Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
      Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
      87ac84f4
    • D
      rtc: remove rest of class_device · cd966209
      David Brownell 提交于
      Finish converting the RTC framework so it no longer uses class_device.
      Signed-off-by: NDavid Brownell <dbrownell@users.sourceforge.net>
      Acked-by: NGreg Kroah-Hartman <gregkh@suse.de>
      Acked-By: NAlessandro Zummo <a.zummo@towertech.it>
      Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
      Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
      cd966209
    • D
      rtc: rtc interfaces don't use class_device · ab6a2d70
      David Brownell 提交于
      This patch removes class_device from the programming interface that the RTC
      framework exposes to the rest of the kernel.  Now an rtc_device is passed,
      which is more type-safe and streamlines all the relevant code.
      Signed-off-by: NDavid Brownell <dbrownell@users.sourceforge.net>
      Acked-by: NGreg Kroah-Hartman <gregkh@suse.de>
      Acked-By: NAlessandro Zummo <a.zummo@towertech.it>
      Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
      Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
      ab6a2d70
  3. 03 4月, 2007 1 次提交
  4. 12 2月, 2007 1 次提交
    • D
      [PATCH] RTC framework driver for CMOS RTCs · 7be2c7c9
      David Brownell 提交于
      This is an "RTC framework" driver for the "CMOS" RTCs which are standard on
      PCs and some other platforms.  That's MC146818 compatible silicon.
      Advantages of this vs.  drivers/char/rtc.c (use one _or_ the other, only
      one will be able to claim the RTC irq) include:
      
       - This leverages both the new RTC framework and the driver model; both
         PNPACPI and platform device modes are supported.  (A separate patch
         creates a platform device on PCs where PNPACPI isn't configured.)
      
       - It supports common extensions like longer alarms.  (A separate patch
         exports that information from ACPI through platform_data.)
      
       - Likewise, system wakeup events use "real driver model support", with
         policy control via sysfs "wakeup" attributes and and using normal rtc
         ioctls to manage wakeup.  (Patch in the works.  The ACPI hooks are
         known; /proc/acpi/alarm can vanish.  Making it work with EFI will
         be a minor challenge to someone with e.g. a MiniMac.)
      
      It's not yet been tested on non-x86 systems, without ACPI, or with HPET.
      And the RTC framework will surely have teething pains on "mainstream"
      PC-based systems (though must embedded Linux systems use it heavily), not
      limited to sorting out the "/dev/rtc0" issue (udev easily tweaked).  Also,
      the ALSA rtctimer code doesn't use the new RTC API.
      
      Otherwise, this should be a no-known-regressions replacement for the old
      drivers/char/rtc.c driver, and should help the non-embedded distros (and
      the new timekeeping code) start to switch to the framework.
      
      Note also that any systems using "rtc-m48t86" are candidates to switch over
      to this more functional driver; the platform data is different, and the way
      bytes are read is different, but otherwise those chips should be compatible.
      
      [akpm@osdl.org: sparc32 fix]
      [akpm@osdl.org: sparc64 fix]
      Signed-off-by: NDavid Brownell <dbrownell@users.sourceforge.net>
      Cc: Woody Suwalski <woodys@xandros.com>
      Cc: Alessandro Zummo <alessandro.zummo@towertech.it>
      Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
      Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
      7be2c7c9