WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 9 of 10] New/Updated drivers: New generic RTC class P

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 9 of 10] New/Updated drivers: New generic RTC class PC-style 'CMOS' driver backported from Linux Kernel Ver. 2.6.29.2
From: Daniel Kiper <dkiper@xxxxxxxxxxxx>
Date: Tue, 24 Nov 2009 13:01:19 +0100
Delivery-date: Tue, 24 Nov 2009 04:04:43 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.3.28i
# HG changeset patch
# User root@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Date 1259016856 -3600
# Node ID 5ae764557edff344c59e88413d66c1e2f9a99eb0
# Parent  1db1bb63824b25f97d127449faeb3a56f1272c97
New generic RTC class PC-style 'CMOS' driver backported
from Linux Kernel Ver. 2.6.29.2
Signed-off-by: Daniel Kiper <dkiper@xxxxxxxxxxxx>

diff -r 1db1bb63824b -r 5ae764557edf arch/i386/Kconfig
--- a/arch/i386/Kconfig Mon Nov 23 07:32:47 2009 +0000
+++ b/arch/i386/Kconfig Mon Nov 23 23:54:16 2009 +0100
@@ -237,7 +237,7 @@ config HPET_TIMER
 
 config HPET_EMULATE_RTC
        bool
-       depends on HPET_TIMER && RTC=y
+       depends on HPET_TIMER && (RTC=y || RTC=m || RTC_DRV_CMOS=m || 
RTC_DRV_CMOS=y)
        default y
 
 config NR_CPUS
diff -r 1db1bb63824b -r 5ae764557edf arch/i386/kernel/time_hpet.c
--- a/arch/i386/kernel/time_hpet.c      Mon Nov 23 07:32:47 2009 +0000
+++ b/arch/i386/kernel/time_hpet.c      Mon Nov 23 23:54:16 2009 +0100
@@ -21,6 +21,8 @@
 
 #include <asm/hpet.h>
 #include <linux/hpet.h>
+
+#include <asm-generic/rtc.h>
 
 static unsigned long hpet_period;      /* fsecs / HPET clock */
 unsigned long hpet_tick;               /* hpet clks count per tick */
@@ -425,7 +427,7 @@ irqreturn_t hpet_rtc_interrupt(int irq, 
        hpet_rtc_timer_reinit();
 
        if (UIE_on | AIE_on) {
-               rtc_get_rtc_time(&curr_time);
+               get_rtc_time(&curr_time);
        }
        if (UIE_on) {
                if (curr_time.tm_sec != prev_update_sec) {
diff -r 1db1bb63824b -r 5ae764557edf arch/x86_64/Kconfig
--- a/arch/x86_64/Kconfig       Mon Nov 23 07:32:47 2009 +0000
+++ b/arch/x86_64/Kconfig       Mon Nov 23 23:54:16 2009 +0100
@@ -426,7 +426,7 @@ config HPET_TIMER
 
 config HPET_EMULATE_RTC
        bool "Provide RTC interrupt"
-       depends on HPET_TIMER && RTC=y
+       depends on HPET_TIMER && (RTC=y || RTC=m || RTC_DRV_CMOS=m || 
RTC_DRV_CMOS=y)
 
 # Mark as embedded because too many people got it wrong.
 # The code disables itself when not needed.
diff -r 1db1bb63824b -r 5ae764557edf arch/x86_64/kernel/time.c
--- a/arch/x86_64/kernel/time.c Mon Nov 23 07:32:47 2009 +0000
+++ b/arch/x86_64/kernel/time.c Mon Nov 23 23:54:16 2009 +0100
@@ -39,6 +39,7 @@
 #include <asm/sections.h>
 #include <linux/cpufreq.h>
 #include <linux/hpet.h>
+#include <asm-generic/rtc.h>
 #ifdef CONFIG_X86_LOCAL_APIC
 #include <asm/apic.h>
 #endif
@@ -1272,7 +1273,7 @@ irqreturn_t hpet_rtc_interrupt(int irq, 
        hpet_rtc_timer_reinit();
 
        if (UIE_on | AIE_on) {
-               rtc_get_rtc_time(&curr_time);
+               get_rtc_time(&curr_time);
        }
        if (UIE_on) {
                if (curr_time.tm_sec != prev_update_sec) {
diff -r 1db1bb63824b -r 5ae764557edf drivers/acpi/utilities/utglobal.c
--- a/drivers/acpi/utilities/utglobal.c Mon Nov 23 07:32:47 2009 +0000
+++ b/drivers/acpi/utilities/utglobal.c Mon Nov 23 23:54:16 2009 +0100
@@ -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)
diff -r 1db1bb63824b -r 5ae764557edf drivers/char/Kconfig
--- a/drivers/char/Kconfig      Mon Nov 23 07:32:47 2009 +0000
+++ b/drivers/char/Kconfig      Mon Nov 23 23:54:16 2009 +0100
@@ -715,6 +715,12 @@ config NVRAM
          To compile this driver as a module, choose M here: the
          module will be called nvram.
 
+#
+# These legacy RTC drivers just cause too many conflicts with the generic
+# RTC framework ... let's not even try to coexist any more.
+#
+if RTC_LIB=n
+
 config RTC
        tristate "Enhanced Real Time Clock Support"
        depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV 
&& !ARM
@@ -808,6 +814,8 @@ config S3C2410_RTC
          RTC (Realtime Clock) driver for the clock inbuilt into the
          Samsung S3C2410. This can provide periodic interrupt rates
          from 1Hz to 64Hz for user programs, and wakeup from Alarm.
+
+endif # RTC_LIB
 
 config COBALT_LCD
        bool "Support for Cobalt LCD"
@@ -1012,7 +1020,7 @@ config HPET
          non-periodioc and/or periodic.
 
 config HPET_RTC_IRQ
-       bool "HPET Control RTC IRQ" if !HPET_EMULATE_RTC
+       bool "HPET Control RTC IRQ" if RTC_LIB=n && !HPET_EMULATE_RTC
        default n
        depends on HPET
        help
diff -r 1db1bb63824b -r 5ae764557edf drivers/rtc/Kconfig
--- a/drivers/rtc/Kconfig       Mon Nov 23 07:32:47 2009 +0000
+++ b/drivers/rtc/Kconfig       Mon Nov 23 23:54:16 2009 +0100
@@ -82,6 +82,29 @@ config RTC_INTF_DEV_UIE_EMUL
 
 comment "RTC drivers"
        depends on RTC_CLASS
+
+# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h>
+# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
+# global rtc_lock ... it's not yet just another platform_device.
+
+config RTC_DRV_CMOS
+       tristate "PC-style 'CMOS'"
+       depends on RTC_CLASS && (X86 || ALPHA || ARM || M32R || ATARI || PPC || 
MIPS || SPARC64)
+       default y if X86
+       help
+         Say "yes" here to get direct support for the real time clock
+         found in every PC or ACPI-based system, and some other boards.
+         Specifically the original MC146818, compatibles like those in
+         PC south bridges, the DS12887 or M48T86, some multifunction
+         or LPC bus chips, and so on.
+
+         Your system will need to define the platform device used by
+         this driver, otherwise it won't be accessible. This means
+         you can safely enable this driver if you don't know whether
+         or not your board has this kind of hardware.
+
+         This driver can also be built as a module. If so, the module
+         will be called rtc-cmos.
 
 config RTC_DRV_X1205
        tristate "Xicor/Intersil X1205"
diff -r 1db1bb63824b -r 5ae764557edf drivers/rtc/Makefile
--- a/drivers/rtc/Makefile      Mon Nov 23 07:32:47 2009 +0000
+++ b/drivers/rtc/Makefile      Mon Nov 23 23:54:16 2009 +0100
@@ -14,6 +14,7 @@ obj-$(CONFIG_RTC_DRV_X1205)   += rtc-x1205
 obj-$(CONFIG_RTC_DRV_X1205)    += rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_ISL1208)  += rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_TEST)     += rtc-test.o
+obj-$(CONFIG_RTC_DRV_CMOS)     += rtc-cmos.o
 obj-$(CONFIG_RTC_DRV_DS1307)   += rtc-ds1307.o
 obj-$(CONFIG_RTC_DRV_DS1672)   += rtc-ds1672.o
 obj-$(CONFIG_RTC_DRV_DS1742)   += rtc-ds1742.o
diff -r 1db1bb63824b -r 5ae764557edf include/linux/mc146818rtc.h
--- a/include/linux/mc146818rtc.h       Mon Nov 23 07:32:47 2009 +0000
+++ b/include/linux/mc146818rtc.h       Mon Nov 23 23:54:16 2009 +0100
@@ -18,6 +18,23 @@
 #ifdef __KERNEL__
 #include <linux/spinlock.h>            /* spinlock_t */
 extern spinlock_t rtc_lock;            /* serialize CMOS RAM access */
+
+/* Some RTCs extend the mc146818 register set to support alarms of more
+ * than 24 hours in the future; or dates that include a century code.
+ * This platform_data structure can pass this information to the driver.
+ *
+ * Also, some platforms need suspend()/resume() hooks to kick in special
+ * handling of wake alarms, e.g. activating ACPI BIOS hooks or setting up
+ * a separate wakeup alarm used by some almost-clone chips.
+ */
+struct cmos_rtc_board_info {
+       void    (*wake_on)(struct device *dev);
+       void    (*wake_off)(struct device *dev);
+
+       u8      rtc_day_alarm;          /* zero, or register index */
+       u8      rtc_mon_alarm;          /* zero, or register index */
+       u8      rtc_century;            /* zero, or register index */
+};
 #endif
 
 /**********************************************************************
diff -r 1db1bb63824b -r 5ae764557edf drivers/rtc/rtc-cmos.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/rtc/rtc-cmos.c    Mon Nov 23 23:54:16 2009 +0100
@@ -0,0 +1,1150 @@
+/*
+ * RTC class driver for "CMOS RTC":  PCs, ACPI, etc
+ *
+ * Copyright (C) 1996 Paul Gortmaker (drivers/char/rtc.c)
+ * Copyright (C) 2006 David Brownell (convert to new framework)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * The original "cmos clock" chip was an MC146818 chip, now obsolete.
+ * That defined the register interface now provided by all PCs, some
+ * non-PC systems, and incorporated into ACPI.  Modern PC chipsets
+ * integrate an MC146818 clone in their southbridge, and boards use
+ * that instead of discrete clones like the DS12887 or M48T86.  There
+ * are also clones that connect using the LPC bus.
+ *
+ * That register API is also used directly by various other drivers
+ * (notably for integrated NVRAM), infrastructure (x86 has code to
+ * bypass the RTC framework, directly reading the RTC during boot
+ * and updating minutes/seconds for systems using NTP synch) and
+ * utilities (like userspace 'hwclock', if no /dev node exists).
+ *
+ * So **ALL** calls to CMOS_READ and CMOS_WRITE must be done with
+ * interrupts disabled, holding the global rtc_lock, to exclude those
+ * other drivers and utilities on correctly configured systems.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/log2.h>
+
+/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
+#include <asm-generic/rtc.h>
+
+typedef irqreturn_t (*irq_handler_t)(int, void *, struct pt_regs *);
+
+struct cmos_rtc {
+       struct rtc_device       *rtc;
+       struct device           *dev;
+       int                     irq;
+       struct resource         *iomem;
+
+       void                    (*wake_on)(struct device *);
+       void                    (*wake_off)(struct device *);
+
+       u8                      enabled_wake;
+       u8                      suspend_ctrl;
+
+       /* newer hardware extends the original register set */
+       u8                      day_alrm;
+       u8                      mon_alrm;
+       u8                      century;
+};
+
+/* both platform and pnp busses use negative numbers for invalid irqs */
+#define is_valid_irq(n)                ((n) > 0)
+
+static const char driver_name[] = "rtc_cmos";
+
+/* The RTC_INTR register may have e.g. RTC_PF set even if RTC_PIE is clear;
+ * always mask it against the irq enable bits in RTC_CONTROL.  Bit values
+ * are the same: PF==PIE, AF=AIE, UF=UIE; so RTC_IRQMASK works with both.
+ */
+#define        RTC_IRQMASK     (RTC_PF | RTC_AF | RTC_UF)
+
+static inline int is_intr(u8 rtc_intr)
+{
+       if (!(rtc_intr & RTC_IRQF))
+               return 0;
+       return rtc_intr & RTC_IRQMASK;
+}
+
+/*----------------------------------------------------------------*/
+
+/* Much modern x86 hardware has HPETs (10+ MHz timers) which, because
+ * many BIOS programmers don't set up "sane mode" IRQ routing, are mostly
+ * used in a broken "legacy replacement" mode.  The breakage includes
+ * HPET #1 hijacking the IRQ for this RTC, and being unavailable for
+ * other (better) use.
+ *
+ * When that broken mode is in use, platform glue provides a partial
+ * emulation of hardware RTC IRQ facilities using HPET #1.  We don't
+ * want to use HPET for anything except those IRQs though...
+ */
+#ifdef CONFIG_HPET_EMULATE_RTC
+extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs 
*regs);
+#else
+
+#define is_hpet_enabled() 0
+#define hpet_rtc_timer_init() 0
+
+static inline int hpet_mask_rtc_irq_bit(unsigned long mask)
+{
+       return 0;
+}
+
+static inline int hpet_set_rtc_irq_bit(unsigned long mask)
+{
+       return 0;
+}
+
+static inline int
+hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
+{
+       return 0;
+}
+
+static inline int hpet_set_periodic_freq(unsigned long freq)
+{
+       return 0;
+}
+
+static inline irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct 
pt_regs *regs)
+{
+       return 0;
+}
+
+#endif
+
+/*----------------------------------------------------------------*/
+
+#ifdef RTC_PORT
+
+/* Most newer x86 systems have two register banks, the first used
+ * for RTC and NVRAM and the second only for NVRAM.  Caller must
+ * own rtc_lock ... and we won't worry about access during NMI.
+ */
+#define can_bank2      1
+
+static inline unsigned char cmos_read_bank2(unsigned char addr)
+{
+       outb(addr, RTC_PORT(2));
+       return inb(RTC_PORT(3));
+}
+
+static inline void cmos_write_bank2(unsigned char val, unsigned char addr)
+{
+       outb(addr, RTC_PORT(2));
+       outb(val, RTC_PORT(2));
+}
+
+#else
+
+#define can_bank2      0
+
+static inline unsigned char cmos_read_bank2(unsigned char addr)
+{
+       return 0;
+}
+
+static inline void cmos_write_bank2(unsigned char val, unsigned char addr)
+{
+}
+
+#endif
+
+/*----------------------------------------------------------------*/
+
+static int cmos_read_time(struct device *dev, struct rtc_time *t)
+{
+       /* REVISIT:  if the clock has a "century" register, use
+        * that instead of the heuristic in get_rtc_time().
+        * That'll make Y3K compatility (year > 2070) easy!
+        */
+       get_rtc_time(t);
+       return 0;
+}
+
+static int cmos_set_time(struct device *dev, struct rtc_time *t)
+{
+       /* REVISIT:  set the "century" register if available
+        *
+        * NOTE: this ignores the issue whereby updating the seconds
+        * takes effect exactly 500ms after we write the register.
+        * (Also queueing and other delays before we get this far.)
+        */
+       return set_rtc_time(t);
+}
+
+static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned char   rtc_control;
+
+       if (!is_valid_irq(cmos->irq))
+               return -EIO;
+
+       /* Basic alarms only support hour, minute, and seconds fields.
+        * Some also support day and month, for alarms up to a year in
+        * the future.
+        */
+       t->time.tm_mday = -1;
+       t->time.tm_mon = -1;
+
+       spin_lock_irq(&rtc_lock);
+       t->time.tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
+       t->time.tm_min = CMOS_READ(RTC_MINUTES_ALARM);
+       t->time.tm_hour = CMOS_READ(RTC_HOURS_ALARM);
+
+       if (cmos->day_alrm) {
+               /* ignore upper bits on readback per ACPI spec */
+               t->time.tm_mday = CMOS_READ(cmos->day_alrm) & 0x3f;
+               if (!t->time.tm_mday)
+                       t->time.tm_mday = -1;
+
+               if (cmos->mon_alrm) {
+                       t->time.tm_mon = CMOS_READ(cmos->mon_alrm);
+                       if (!t->time.tm_mon)
+                               t->time.tm_mon = -1;
+               }
+       }
+
+       rtc_control = CMOS_READ(RTC_CONTROL);
+       spin_unlock_irq(&rtc_lock);
+
+       /* REVISIT this assumes PC style usage:  always BCD */
+
+       if (((unsigned)t->time.tm_sec) < 0x60)
+               t->time.tm_sec = BCD2BIN(t->time.tm_sec);
+       else
+               t->time.tm_sec = -1;
+       if (((unsigned)t->time.tm_min) < 0x60)
+               t->time.tm_min = BCD2BIN(t->time.tm_min);
+       else
+               t->time.tm_min = -1;
+       if (((unsigned)t->time.tm_hour) < 0x24)
+               t->time.tm_hour = BCD2BIN(t->time.tm_hour);
+       else
+               t->time.tm_hour = -1;
+
+       if (cmos->day_alrm) {
+               if (((unsigned)t->time.tm_mday) <= 0x31)
+                       t->time.tm_mday = BCD2BIN(t->time.tm_mday);
+               else
+                       t->time.tm_mday = -1;
+               if (cmos->mon_alrm) {
+                       if (((unsigned)t->time.tm_mon) <= 0x12)
+                               t->time.tm_mon = BCD2BIN(t->time.tm_mon) - 1;
+                       else
+                               t->time.tm_mon = -1;
+               }
+       }
+       t->time.tm_year = -1;
+
+       t->enabled = !!(rtc_control & RTC_AIE);
+       t->pending = 0;
+
+       return 0;
+}
+
+static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control)
+{
+       unsigned char   rtc_intr;
+
+       /* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
+        * allegedly some older rtcs need that to handle irqs properly
+        */
+       rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
+
+       if (is_hpet_enabled())
+               return;
+
+       rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
+       if (is_intr(rtc_intr))
+               rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr);
+}
+
+static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask)
+{
+       unsigned char   rtc_control;
+
+       /* flush any pending IRQ status, notably for update irqs,
+        * before we enable new IRQs
+        */
+       rtc_control = CMOS_READ(RTC_CONTROL);
+       cmos_checkintr(cmos, rtc_control);
+
+       rtc_control |= mask;
+       CMOS_WRITE(rtc_control, RTC_CONTROL);
+       hpet_set_rtc_irq_bit(mask);
+
+       cmos_checkintr(cmos, rtc_control);
+}
+
+static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
+{
+       unsigned char   rtc_control;
+
+       rtc_control = CMOS_READ(RTC_CONTROL);
+       rtc_control &= ~mask;
+       CMOS_WRITE(rtc_control, RTC_CONTROL);
+       hpet_mask_rtc_irq_bit(mask);
+
+       cmos_checkintr(cmos, rtc_control);
+}
+
+static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned char   mon, mday, hrs, min, sec;
+
+       if (!is_valid_irq(cmos->irq))
+               return -EIO;
+
+       /* REVISIT this assumes PC style usage:  always BCD */
+
+       /* Writing 0xff means "don't care" or "match all".  */
+
+       mon = t->time.tm_mon + 1;
+       mon = (mon <= 12) ? BIN2BCD(mon) : 0xff;
+
+       mday = t->time.tm_mday;
+       mday = (mday >= 1 && mday <= 31) ? BIN2BCD(mday) : 0xff;
+
+       hrs = t->time.tm_hour;
+       hrs = (hrs < 24) ? BIN2BCD(hrs) : 0xff;
+
+       min = t->time.tm_min;
+       min = (min < 60) ? BIN2BCD(min) : 0xff;
+
+       sec = t->time.tm_sec;
+       sec = (sec < 60) ? BIN2BCD(sec) : 0xff;
+
+       spin_lock_irq(&rtc_lock);
+
+       /* next rtc irq must not be from previous alarm setting */
+       cmos_irq_disable(cmos, RTC_AIE);
+
+       /* update alarm */
+       CMOS_WRITE(hrs, RTC_HOURS_ALARM);
+       CMOS_WRITE(min, RTC_MINUTES_ALARM);
+       CMOS_WRITE(sec, RTC_SECONDS_ALARM);
+
+       /* the system may support an "enhanced" alarm */
+       if (cmos->day_alrm) {
+               CMOS_WRITE(mday, cmos->day_alrm);
+               if (cmos->mon_alrm)
+                       CMOS_WRITE(mon, cmos->mon_alrm);
+       }
+
+       /* FIXME the HPET alarm glue currently ignores day_alrm
+        * and mon_alrm ...
+        */
+       hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec);
+
+       if (t->enabled)
+               cmos_irq_enable(cmos, RTC_AIE);
+
+       spin_unlock_irq(&rtc_lock);
+
+       return 0;
+}
+
+static int cmos_irq_set_freq(struct device *dev, int freq)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       int             f;
+       unsigned long   flags;
+
+       if (!is_valid_irq(cmos->irq))
+               return -ENXIO;
+
+       if (!is_power_of_2(freq))
+               return -EINVAL;
+       /* 0 = no irqs; 1 = 2^15 Hz ... 15 = 2^0 Hz */
+       f = ffs(freq);
+       if (f-- > 16)
+               return -EINVAL;
+       f = 16 - f;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       hpet_set_periodic_freq(freq);
+       CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
+       spin_unlock_irqrestore(&rtc_lock, flags);
+
+       return 0;
+}
+
+static int cmos_irq_set_state(struct device *dev, int enabled)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned long   flags;
+
+       if (!is_valid_irq(cmos->irq))
+               return -ENXIO;
+
+       spin_lock_irqsave(&rtc_lock, flags);
+
+       if (enabled)
+               cmos_irq_enable(cmos, RTC_PIE);
+       else
+               cmos_irq_disable(cmos, RTC_PIE);
+
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return 0;
+}
+
+#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)
+
+static int
+cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned long   flags;
+
+       switch (cmd) {
+       case RTC_AIE_OFF:
+       case RTC_AIE_ON:
+       case RTC_UIE_OFF:
+       case RTC_UIE_ON:
+               if (!is_valid_irq(cmos->irq))
+                       return -EINVAL;
+               break;
+       /* PIE ON/OFF is handled by cmos_irq_set_state() */
+       default:
+               return -ENOIOCTLCMD;
+       }
+
+       spin_lock_irqsave(&rtc_lock, flags);
+       switch (cmd) {
+       case RTC_AIE_OFF:       /* alarm off */
+               cmos_irq_disable(cmos, RTC_AIE);
+               break;
+       case RTC_AIE_ON:        /* alarm on */
+               cmos_irq_enable(cmos, RTC_AIE);
+               break;
+       case RTC_UIE_OFF:       /* update off */
+               cmos_irq_disable(cmos, RTC_UIE);
+               break;
+       case RTC_UIE_ON:        /* update on */
+               cmos_irq_enable(cmos, RTC_UIE);
+               break;
+       }
+       spin_unlock_irqrestore(&rtc_lock, flags);
+       return 0;
+}
+
+#else
+#define        cmos_rtc_ioctl  NULL
+#endif
+
+#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
+
+static int cmos_procfs(struct device *dev, struct seq_file *seq)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned char   rtc_control, valid;
+
+       spin_lock_irq(&rtc_lock);
+       rtc_control = CMOS_READ(RTC_CONTROL);
+       valid = CMOS_READ(RTC_VALID);
+       spin_unlock_irq(&rtc_lock);
+
+       /* NOTE:  at least ICH6 reports battery status using a different
+        * (non-RTC) bit; and SQWE is ignored on many current systems.
+        */
+       return seq_printf(seq,
+                       "periodic_IRQ\t: %s\n"
+                       "update_IRQ\t: %s\n"
+                       "HPET_emulated\t: %s\n"
+                       // "square_wave\t: %s\n"
+                       // "BCD\t\t: %s\n"
+                       "DST_enable\t: %s\n"
+                       "periodic_freq\t: %d\n"
+                       "batt_status\t: %s\n",
+                       (rtc_control & RTC_PIE) ? "yes" : "no",
+                       (rtc_control & RTC_UIE) ? "yes" : "no",
+                       is_hpet_enabled() ? "yes" : "no",
+                       // (rtc_control & RTC_SQWE) ? "yes" : "no",
+                       // (rtc_control & RTC_DM_BINARY) ? "no" : "yes",
+                       (rtc_control & RTC_DST_EN) ? "yes" : "no",
+                       cmos->rtc->irq_freq,
+                       (valid & RTC_VRT) ? "okay" : "dead");
+}
+
+#else
+#define        cmos_procfs     NULL
+#endif
+
+static struct rtc_class_ops cmos_rtc_ops = {
+       .ioctl          = cmos_rtc_ioctl,
+       .read_time      = cmos_read_time,
+       .set_time       = cmos_set_time,
+       .read_alarm     = cmos_read_alarm,
+       .set_alarm      = cmos_set_alarm,
+       .proc           = cmos_procfs,
+       .irq_set_freq   = cmos_irq_set_freq,
+       .irq_set_state  = cmos_irq_set_state,
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * All these chips have at least 64 bytes of address space, shared by
+ * RTC registers and NVRAM.  Most of those bytes of NVRAM are used
+ * by boot firmware.  Modern chips have 128 or 256 bytes.
+ */
+
+#define NVRAM_OFFSET   (RTC_REG_D + 1)
+
+static struct bin_attribute nvram;
+
+static ssize_t
+cmos_nvram_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+       int     retval;
+
+       if (unlikely(off >= nvram.size))
+               return 0;
+       if (unlikely(off < 0))
+               return -EINVAL;
+       if ((off + count) > nvram.size)
+               count = nvram.size - off;
+
+       off += NVRAM_OFFSET;
+       spin_lock_irq(&rtc_lock);
+       for (retval = 0; count; count--, off++, retval++) {
+               if (off < 128)
+                       *buf++ = CMOS_READ(off);
+               else if (can_bank2)
+                       *buf++ = cmos_read_bank2(off);
+               else
+                       break;
+       }
+       spin_unlock_irq(&rtc_lock);
+
+       return retval;
+}
+
+static ssize_t
+cmos_nvram_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+       struct cmos_rtc *cmos;
+       int             retval;
+
+       cmos = dev_get_drvdata(container_of(kobj, struct device, kobj));
+       if (unlikely(off >= nvram.size))
+               return -EFBIG;
+       if (unlikely(off < 0))
+               return -EINVAL;
+       if ((off + count) > nvram.size)
+               count = nvram.size - off;
+
+       /* NOTE:  on at least PCs and Ataris, the boot firmware uses a
+        * checksum on part of the NVRAM data.  That's currently ignored
+        * here.  If userspace is smart enough to know what fields of
+        * NVRAM to update, updating checksums is also part of its job.
+        */
+       off += NVRAM_OFFSET;
+       spin_lock_irq(&rtc_lock);
+       for (retval = 0; count; count--, off++, retval++) {
+               /* don't trash RTC registers */
+               if (off == cmos->day_alrm
+                               || off == cmos->mon_alrm
+                               || off == cmos->century)
+                       buf++;
+               else if (off < 128)
+                       CMOS_WRITE(*buf++, off);
+               else if (can_bank2)
+                       cmos_write_bank2(*buf++, off);
+               else
+                       break;
+       }
+       spin_unlock_irq(&rtc_lock);
+
+       return retval;
+}
+
+static struct bin_attribute nvram = {
+       .attr = {
+               .name   = "nvram",
+               .mode   = S_IRUGO | S_IWUSR,
+       },
+
+       .read   = cmos_nvram_read,
+       .write  = cmos_nvram_write,
+       /* size gets set up later */
+};
+
+/*----------------------------------------------------------------*/
+
+static struct cmos_rtc cmos_rtc;
+
+irqreturn_t rtc_interrupt(int irq, void *p, struct pt_regs *regs)
+{
+       u8              irqstat;
+       u8              rtc_control;
+
+       spin_lock(&rtc_lock);
+
+       /* When the HPET interrupt handler calls us, the interrupt
+        * status is passed as arg1 instead of the irq number.  But
+        * always clear irq status, even when HPET is in the way.
+        *
+        * Note that HPET and RTC are almost certainly out of phase,
+        * giving different IRQ status ...
+        */
+       irqstat = CMOS_READ(RTC_INTR_FLAGS);
+       rtc_control = CMOS_READ(RTC_CONTROL);
+       if (is_hpet_enabled())
+               irqstat = (unsigned long)irq & 0xF0;
+       irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
+
+       /* All Linux RTC alarms should be treated as if they were oneshot.
+        * Similar code may be needed in system wakeup paths, in case the
+        * alarm woke the system.
+        */
+       if (irqstat & RTC_AIE) {
+               rtc_control &= ~RTC_AIE;
+               CMOS_WRITE(rtc_control, RTC_CONTROL);
+               hpet_mask_rtc_irq_bit(RTC_AIE);
+
+               CMOS_READ(RTC_INTR_FLAGS);
+       }
+       spin_unlock(&rtc_lock);
+
+       if (is_intr(irqstat)) {
+               rtc_update_irq(p, 1, irqstat);
+               return IRQ_HANDLED;
+       } else
+               return IRQ_NONE;
+}
+
+#ifdef CONFIG_PNP
+#define        INITSECTION
+
+#else
+#define        INITSECTION     __init
+#endif
+
+static int INITSECTION
+cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
+{
+       struct cmos_rtc_board_info      *info = dev->platform_data;
+       int                             retval = 0;
+       unsigned char                   rtc_control;
+       unsigned                        address_space;
+
+       /* there can be only one ... */
+       if (cmos_rtc.dev)
+               return -EBUSY;
+
+       if (!ports)
+               return -ENODEV;
+
+       /* Claim I/O ports ASAP, minimizing conflict with legacy driver.
+        *
+        * REVISIT non-x86 systems may instead use memory space resources
+        * (needing ioremap etc), not i/o space resources like this ...
+        */
+       ports = request_region(ports->start,
+                       ports->end + 1 - ports->start,
+                       driver_name);
+       if (!ports) {
+               dev_dbg(dev, "i/o registers already in use\n");
+               return -EBUSY;
+       }
+
+       cmos_rtc.irq = rtc_irq;
+       cmos_rtc.iomem = ports;
+
+       /* Heuristic to deduce NVRAM size ... do what the legacy NVRAM
+        * driver did, but don't reject unknown configs.   Old hardware
+        * won't address 128 bytes.  Newer chips have multiple banks,
+        * though they may not be listed in one I/O resource.
+        */
+#if    defined(CONFIG_ATARI)
+       address_space = 64;
+#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || 
defined(__sparc__)
+       address_space = 128;
+#else
+#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes.
+       address_space = 128;
+#endif
+       if (can_bank2 && ports->end > (ports->start + 1))
+               address_space = 256;
+
+       /* For ACPI systems extension info comes from the FADT.  On others,
+        * board specific setup provides it as appropriate.  Systems where
+        * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and
+        * some almost-clones) can provide hooks to make that behave.
+        *
+        * Note that ACPI doesn't preclude putting these registers into
+        * "extended" areas of the chip, including some that we won't yet
+        * expect CMOS_READ and friends to handle.
+        */
+       if (info) {
+               if (info->rtc_day_alarm && info->rtc_day_alarm < 128)
+                       cmos_rtc.day_alrm = info->rtc_day_alarm;
+               if (info->rtc_mon_alarm && info->rtc_mon_alarm < 128)
+                       cmos_rtc.mon_alrm = info->rtc_mon_alarm;
+               if (info->rtc_century && info->rtc_century < 128)
+                       cmos_rtc.century = info->rtc_century;
+
+               if (info->wake_on && info->wake_off) {
+                       cmos_rtc.wake_on = info->wake_on;
+                       cmos_rtc.wake_off = info->wake_off;
+               }
+       }
+
+       cmos_rtc.rtc = rtc_device_register(driver_name, dev,
+                               &cmos_rtc_ops, THIS_MODULE);
+       if (IS_ERR(cmos_rtc.rtc)) {
+               retval = PTR_ERR(cmos_rtc.rtc);
+               goto cleanup0;
+       }
+
+       cmos_rtc.dev = dev;
+       dev_set_drvdata(dev, &cmos_rtc);
+       rename_region(ports, cmos_rtc.rtc->class_dev.class_id);
+
+       spin_lock_irq(&rtc_lock);
+
+       /* force periodic irq to CMOS reset default of 1024Hz;
+        *
+        * REVISIT it's been reported that at least one x86_64 ALI mobo
+        * doesn't use 32KHz here ... for portability we might need to
+        * do something about other clock frequencies.
+        */
+       cmos_rtc.rtc->irq_freq = 1024;
+       hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq);
+       CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
+
+       /* disable irqs */
+       cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE);
+
+       rtc_control = CMOS_READ(RTC_CONTROL);
+
+       spin_unlock_irq(&rtc_lock);
+
+       /* FIXME teach the alarm code how to handle binary mode;
+        * <asm-generic/rtc.h> doesn't know 12-hour mode either.
+        */
+       if (is_valid_irq(rtc_irq) &&
+           (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))) {
+               dev_dbg(dev, "only 24-hr BCD mode supported\n");
+               retval = -ENXIO;
+               goto cleanup1;
+       }
+
+       if (is_valid_irq(rtc_irq)) {
+               irq_handler_t rtc_cmos_int_handler;
+
+               if (is_hpet_enabled())
+                       rtc_cmos_int_handler = hpet_rtc_interrupt;
+               else
+                       rtc_cmos_int_handler = rtc_interrupt;
+
+               retval = request_irq(rtc_irq, rtc_cmos_int_handler,
+                               IRQF_DISABLED, cmos_rtc.rtc->class_dev.class_id,
+                               &cmos_rtc.rtc->class_dev);
+               if (retval < 0) {
+                       dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
+                       goto cleanup1;
+               }
+       }
+       hpet_rtc_timer_init();
+
+       /* export at least the first block of NVRAM */
+       nvram.size = address_space - NVRAM_OFFSET;
+       retval = sysfs_create_bin_file(&dev->kobj, &nvram);
+       if (retval < 0) {
+               dev_dbg(dev, "can't create nvram file? %d\n", retval);
+               goto cleanup2;
+       }
+
+       pr_info("%s: alarms up to one %s%s, %zd bytes nvram%s\n",
+                       cmos_rtc.rtc->class_dev.class_id,
+                       is_valid_irq(rtc_irq)
+                               ?  (cmos_rtc.mon_alrm
+                                       ? "year"
+                                       : (cmos_rtc.day_alrm
+                                               ? "month" : "day"))
+                               : "no",
+                       cmos_rtc.century ? ", y3k" : "",
+                       nvram.size,
+                       is_hpet_enabled() ? ", hpet irqs" : "");
+
+       return 0;
+
+cleanup2:
+       if (is_valid_irq(rtc_irq))
+               free_irq(rtc_irq, &cmos_rtc.rtc->class_dev);
+cleanup1:
+       cmos_rtc.dev = NULL;
+       rtc_device_unregister(cmos_rtc.rtc);
+cleanup0:
+       release_region(ports->start, ports->end + 1 - ports->start);
+       return retval;
+}
+
+static void cmos_do_shutdown(void)
+{
+       spin_lock_irq(&rtc_lock);
+       cmos_irq_disable(&cmos_rtc, RTC_IRQMASK);
+       spin_unlock_irq(&rtc_lock);
+}
+
+static void __exit cmos_do_remove(struct device *dev)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       struct resource *ports;
+
+       cmos_do_shutdown();
+
+       sysfs_remove_bin_file(&dev->kobj, &nvram);
+
+       if (is_valid_irq(cmos->irq)) {
+               free_irq(cmos->irq, &cmos->rtc->class_dev);
+       }
+
+       rtc_device_unregister(cmos->rtc);
+       cmos->rtc = NULL;
+
+       ports = cmos->iomem;
+       release_region(ports->start, ports->end + 1 - ports->start);
+       cmos->iomem = NULL;
+
+       cmos->dev = NULL;
+       dev_set_drvdata(dev, NULL);
+}
+
+#ifdef CONFIG_PM
+
+static int cmos_suspend(struct device *dev, pm_message_t mesg)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned char   tmp;
+
+       /* only the alarm might be a wakeup event source */
+       spin_lock_irq(&rtc_lock);
+       cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL);
+       if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
+               unsigned char   mask;
+
+               if (device_may_wakeup(dev))
+                       mask = RTC_IRQMASK & ~RTC_AIE;
+               else
+                       mask = RTC_IRQMASK;
+               tmp &= ~mask;
+               CMOS_WRITE(tmp, RTC_CONTROL);
+               hpet_mask_rtc_irq_bit(mask);
+
+               cmos_checkintr(cmos, tmp);
+       }
+       spin_unlock_irq(&rtc_lock);
+
+       if (tmp & RTC_AIE) {
+               cmos->enabled_wake = 1;
+               if (cmos->wake_on)
+                       cmos->wake_on(dev);
+               else
+                       enable_irq_wake(cmos->irq);
+       }
+
+       pr_debug("%s: suspend%s, ctrl %02x\n",
+                       cmos_rtc.rtc->class_dev.class_id,
+                       (tmp & RTC_AIE) ? ", alarm may wake" : "",
+                       tmp);
+
+       return 0;
+}
+
+static int cmos_resume(struct device *dev)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned char   tmp = cmos->suspend_ctrl;
+
+       /* re-enable any irqs previously active */
+       if (tmp & RTC_IRQMASK) {
+               unsigned char   mask;
+
+               if (cmos->enabled_wake) {
+                       if (cmos->wake_off)
+                               cmos->wake_off(dev);
+                       else
+                               disable_irq_wake(cmos->irq);
+                       cmos->enabled_wake = 0;
+               }
+
+               spin_lock_irq(&rtc_lock);
+               do {
+                       CMOS_WRITE(tmp, RTC_CONTROL);
+                       hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK);
+
+                       mask = CMOS_READ(RTC_INTR_FLAGS);
+                       mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
+                       if (!is_hpet_enabled() || !is_intr(mask))
+                               break;
+
+                       /* force one-shot behavior if HPET blocked
+                        * the wake alarm's irq
+                        */
+                       rtc_update_irq(&cmos->rtc->class_dev, 1, mask);
+                       tmp &= ~RTC_AIE;
+                       hpet_mask_rtc_irq_bit(RTC_AIE);
+               } while (mask & RTC_AIE);
+               spin_unlock_irq(&rtc_lock);
+       }
+
+       pr_debug("%s: resume, ctrl %02x\n",
+                       cmos_rtc.rtc->class_dev.class_id,
+                       tmp);
+
+       return 0;
+}
+
+#else
+#define        cmos_suspend    NULL
+#define        cmos_resume     NULL
+#endif
+
+/*----------------------------------------------------------------*/
+
+/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus.
+ * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs
+ * probably list them in similar PNPBIOS tables; so PNP is more common.
+ *
+ * We don't use legacy "poke at the hardware" probing.  Ancient PCs that
+ * predate even PNPBIOS should set up platform_bus devices.
+ */
+
+#ifdef CONFIG_ACPI
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_PM
+static u32 rtc_handler(void *context)
+{
+       acpi_clear_event(ACPI_EVENT_RTC);
+       acpi_disable_event(ACPI_EVENT_RTC, 0);
+       return ACPI_INTERRUPT_HANDLED;
+}
+
+static inline void rtc_wake_setup(void)
+{
+       acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
+       /*
+        * After the RTC handler is installed, the Fixed_RTC event should
+        * be disabled. Only when the RTC alarm is set will it be enabled.
+        */
+       acpi_clear_event(ACPI_EVENT_RTC);
+       acpi_disable_event(ACPI_EVENT_RTC, 0);
+}
+
+static void rtc_wake_on(struct device *dev)
+{
+       acpi_clear_event(ACPI_EVENT_RTC);
+       acpi_enable_event(ACPI_EVENT_RTC, 0);
+}
+
+static void rtc_wake_off(struct device *dev)
+{
+       acpi_disable_event(ACPI_EVENT_RTC, 0);
+}
+#else
+#define rtc_wake_setup()       do{}while(0)
+#define rtc_wake_on            NULL
+#define rtc_wake_off           NULL
+#endif
+
+/* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find
+ * its device node and pass extra config data.  This helps its driver use
+ * capabilities that the now-obsolete mc146818 didn't have, and informs it
+ * that this board's RTC is wakeup-capable (per ACPI spec).
+ */
+static struct cmos_rtc_board_info acpi_rtc_info;
+
+static void __devinit
+cmos_wake_setup(struct device *dev)
+{
+       if (acpi_disabled)
+               return;
+
+       rtc_wake_setup();
+       acpi_rtc_info.wake_on = rtc_wake_on;
+       acpi_rtc_info.wake_off = rtc_wake_off;
+
+       /* workaround bug in some ACPI tables */
+       if (acpi_gbl_FADT->mon_alrm && !acpi_gbl_FADT->day_alrm) {
+               dev_dbg(dev, "bogus FADT month_alarm (%d)\n",
+                       acpi_gbl_FADT->mon_alrm);
+               acpi_gbl_FADT->mon_alrm = 0;
+       }
+
+       acpi_rtc_info.rtc_day_alarm = acpi_gbl_FADT->day_alrm;
+       acpi_rtc_info.rtc_mon_alarm = acpi_gbl_FADT->mon_alrm;
+       acpi_rtc_info.rtc_century = acpi_gbl_FADT->century;
+
+       /* NOTE:  S4_RTC_WAKE is NOT currently useful to Linux */
+       if (acpi_gbl_FADT->rtcs4)
+               dev_info(dev, "RTC can wake from S4\n");
+
+       dev->platform_data = &acpi_rtc_info;
+
+       /* RTC always wakes from S1/S2/S3, and often S4/STD */
+       device_init_wakeup(dev, 1);
+}
+
+#else
+
+static void __devinit
+cmos_wake_setup(struct device *dev)
+{
+}
+
+#endif
+
+#ifdef CONFIG_PNP
+
+#include <linux/pnp.h>
+
+static int __devinit
+cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
+{
+       cmos_wake_setup(&pnp->dev);
+
+       if (pnp_port_start(pnp,0) == 0x70 && !pnp_irq_valid(pnp,0))
+               /* Some machines contain a PNP entry for the RTC, but
+                * don't define the IRQ. It should always be safe to
+                * hardcode it in these cases
+                */
+               return cmos_do_probe(&pnp->dev, &pnp->res.port_resource[0], 8);
+       else
+               return cmos_do_probe(&pnp->dev,
+                               &pnp->res.port_resource[0],
+                               pnp->res.irq_resource[0].start);
+}
+
+static void __exit cmos_pnp_remove(struct pnp_dev *pnp)
+{
+       cmos_do_remove(&pnp->dev);
+}
+
+#ifdef CONFIG_PM
+
+static int cmos_pnp_suspend(struct pnp_dev *pnp, pm_message_t mesg)
+{
+       return cmos_suspend(&pnp->dev, mesg);
+}
+
+static int cmos_pnp_resume(struct pnp_dev *pnp)
+{
+       return cmos_resume(&pnp->dev);
+}
+
+#else
+#define        cmos_pnp_suspend        NULL
+#define        cmos_pnp_resume         NULL
+#endif
+
+static void cmos_pnp_shutdown(struct device *pdev)
+{
+       cmos_do_shutdown();
+}
+
+static const struct pnp_device_id rtc_ids[] = {
+       { .id = "PNP0b00", },
+       { .id = "PNP0b01", },
+       { .id = "PNP0b02", },
+       { },
+};
+MODULE_DEVICE_TABLE(pnp, rtc_ids);
+
+static struct pnp_driver cmos_pnp_driver = {
+       .name           = (char *) driver_name,
+       .id_table       = rtc_ids,
+       .probe          = cmos_pnp_probe,
+       .remove         = __exit_p(cmos_pnp_remove),
+
+       /* flag ensures resume() gets called, and stops syslog spam */
+       .flags          = PNP_DRIVER_RES_DO_NOT_CHANGE,
+       .suspend        = cmos_pnp_suspend,
+       .resume         = cmos_pnp_resume,
+       .driver         = {
+               .name     = (char *)driver_name,
+               .shutdown = cmos_pnp_shutdown,
+       }
+};
+
+#endif /* CONFIG_PNP */
+
+/*----------------------------------------------------------------*/
+
+static int __exit cmos_platform_remove(struct platform_device *pdev)
+{
+       cmos_do_remove(&pdev->dev);
+       return 0;
+}
+
+static void cmos_platform_shutdown(struct platform_device *pdev)
+{
+       cmos_do_shutdown();
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:rtc_cmos");
+
+static struct platform_driver cmos_platform_driver = {
+       .remove         = __exit_p(cmos_platform_remove),
+       .shutdown       = cmos_platform_shutdown,
+       .driver = {
+               .name           = (char *) driver_name,
+               .suspend        = cmos_suspend,
+               .resume         = cmos_resume,
+       }
+};
+
+static int __init cmos_init(void)
+{
+       int retval = 0;
+
+#ifdef CONFIG_PNP
+       pnp_register_driver(&cmos_pnp_driver);
+#endif
+
+       if (!cmos_rtc.dev)
+               retval = platform_driver_register(&cmos_platform_driver);
+
+       if (retval == 0)
+               return 0;
+
+#ifdef CONFIG_PNP
+       pnp_unregister_driver(&cmos_pnp_driver);
+#endif
+       return retval;
+}
+module_init(cmos_init);
+
+static void __exit cmos_exit(void)
+{
+#ifdef CONFIG_PNP
+       pnp_unregister_driver(&cmos_pnp_driver);
+#endif
+       platform_driver_unregister(&cmos_platform_driver);
+}
+module_exit(cmos_exit);
+
+
+MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs");
+MODULE_LICENSE("GPL");
diff -r 1db1bb63824b -r 5ae764557edf include/linux/log2.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/include/linux/log2.h      Mon Nov 23 23:54:16 2009 +0100
@@ -0,0 +1,209 @@
+/* Integer base 2 logarithm calculation
+ *
+ * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_LOG2_H
+#define _LINUX_LOG2_H
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+/*
+ * deal with unrepresentable constant logarithms
+ */
+extern __attribute__((const, noreturn))
+int ____ilog2_NaN(void);
+
+/*
+ * non-constant log of base 2 calculators
+ * - the arch may override these in asm/bitops.h if they can be implemented
+ *   more efficiently than using fls() and fls64()
+ * - the arch is not required to handle n==0 if implementing the fallback
+ */
+#ifndef CONFIG_ARCH_HAS_ILOG2_U32
+static inline __attribute__((const))
+int __ilog2_u32(u32 n)
+{
+       return fls(n) - 1;
+}
+#endif
+
+#ifndef CONFIG_ARCH_HAS_ILOG2_U64
+static inline __attribute__((const))
+int __ilog2_u64(u64 n)
+{
+       return fls64(n) - 1;
+}
+#endif
+
+/*
+ *  Determine whether some value is a power of two, where zero is
+ * *not* considered a power of two.
+ */
+
+static inline __attribute__((const))
+int is_power_of_2(unsigned long n)
+{
+       return (n != 0 && ((n & (n - 1)) == 0));
+}
+
+/*
+ * round up to nearest power of two
+ */
+static inline __attribute__((const))
+unsigned long __roundup_pow_of_two(unsigned long n)
+{
+       return 1UL << fls_long(n - 1);
+}
+
+/*
+ * round down to nearest power of two
+ */
+static inline __attribute__((const))
+unsigned long __rounddown_pow_of_two(unsigned long n)
+{
+       return 1UL << (fls_long(n) - 1);
+}
+
+/**
+ * ilog2 - log of base 2 of 32-bit or a 64-bit unsigned value
+ * @n - parameter
+ *
+ * constant-capable log of base 2 calculation
+ * - this can be used to initialise global variables from constant data, hence
+ *   the massive ternary operator construction
+ *
+ * selects the appropriately-sized optimised version depending on sizeof(n)
+ */
+#define ilog2(n)                               \
+(                                              \
+       __builtin_constant_p(n) ? (             \
+               (n) < 1 ? ____ilog2_NaN() :     \
+               (n) & (1ULL << 63) ? 63 :       \
+               (n) & (1ULL << 62) ? 62 :       \
+               (n) & (1ULL << 61) ? 61 :       \
+               (n) & (1ULL << 60) ? 60 :       \
+               (n) & (1ULL << 59) ? 59 :       \
+               (n) & (1ULL << 58) ? 58 :       \
+               (n) & (1ULL << 57) ? 57 :       \
+               (n) & (1ULL << 56) ? 56 :       \
+               (n) & (1ULL << 55) ? 55 :       \
+               (n) & (1ULL << 54) ? 54 :       \
+               (n) & (1ULL << 53) ? 53 :       \
+               (n) & (1ULL << 52) ? 52 :       \
+               (n) & (1ULL << 51) ? 51 :       \
+               (n) & (1ULL << 50) ? 50 :       \
+               (n) & (1ULL << 49) ? 49 :       \
+               (n) & (1ULL << 48) ? 48 :       \
+               (n) & (1ULL << 47) ? 47 :       \
+               (n) & (1ULL << 46) ? 46 :       \
+               (n) & (1ULL << 45) ? 45 :       \
+               (n) & (1ULL << 44) ? 44 :       \
+               (n) & (1ULL << 43) ? 43 :       \
+               (n) & (1ULL << 42) ? 42 :       \
+               (n) & (1ULL << 41) ? 41 :       \
+               (n) & (1ULL << 40) ? 40 :       \
+               (n) & (1ULL << 39) ? 39 :       \
+               (n) & (1ULL << 38) ? 38 :       \
+               (n) & (1ULL << 37) ? 37 :       \
+               (n) & (1ULL << 36) ? 36 :       \
+               (n) & (1ULL << 35) ? 35 :       \
+               (n) & (1ULL << 34) ? 34 :       \
+               (n) & (1ULL << 33) ? 33 :       \
+               (n) & (1ULL << 32) ? 32 :       \
+               (n) & (1ULL << 31) ? 31 :       \
+               (n) & (1ULL << 30) ? 30 :       \
+               (n) & (1ULL << 29) ? 29 :       \
+               (n) & (1ULL << 28) ? 28 :       \
+               (n) & (1ULL << 27) ? 27 :       \
+               (n) & (1ULL << 26) ? 26 :       \
+               (n) & (1ULL << 25) ? 25 :       \
+               (n) & (1ULL << 24) ? 24 :       \
+               (n) & (1ULL << 23) ? 23 :       \
+               (n) & (1ULL << 22) ? 22 :       \
+               (n) & (1ULL << 21) ? 21 :       \
+               (n) & (1ULL << 20) ? 20 :       \
+               (n) & (1ULL << 19) ? 19 :       \
+               (n) & (1ULL << 18) ? 18 :       \
+               (n) & (1ULL << 17) ? 17 :       \
+               (n) & (1ULL << 16) ? 16 :       \
+               (n) & (1ULL << 15) ? 15 :       \
+               (n) & (1ULL << 14) ? 14 :       \
+               (n) & (1ULL << 13) ? 13 :       \
+               (n) & (1ULL << 12) ? 12 :       \
+               (n) & (1ULL << 11) ? 11 :       \
+               (n) & (1ULL << 10) ? 10 :       \
+               (n) & (1ULL <<  9) ?  9 :       \
+               (n) & (1ULL <<  8) ?  8 :       \
+               (n) & (1ULL <<  7) ?  7 :       \
+               (n) & (1ULL <<  6) ?  6 :       \
+               (n) & (1ULL <<  5) ?  5 :       \
+               (n) & (1ULL <<  4) ?  4 :       \
+               (n) & (1ULL <<  3) ?  3 :       \
+               (n) & (1ULL <<  2) ?  2 :       \
+               (n) & (1ULL <<  1) ?  1 :       \
+               (n) & (1ULL <<  0) ?  0 :       \
+               ____ilog2_NaN()                 \
+                                  ) :          \
+       (sizeof(n) <= 4) ?                      \
+       __ilog2_u32(n) :                        \
+       __ilog2_u64(n)                          \
+ )
+
+/**
+ * roundup_pow_of_two - round the given value up to nearest power of two
+ * @n - parameter
+ *
+ * round the given value up to the nearest power of two
+ * - the result is undefined when n == 0
+ * - this can be used to initialise global variables from constant data
+ */
+#define roundup_pow_of_two(n)                  \
+(                                              \
+       __builtin_constant_p(n) ? (             \
+               (n == 1) ? 1 :                  \
+               (1UL << (ilog2((n) - 1) + 1))   \
+                                  ) :          \
+       __roundup_pow_of_two(n)                 \
+ )
+
+/**
+ * rounddown_pow_of_two - round the given value down to nearest power of two
+ * @n - parameter
+ *
+ * round the given value down to the nearest power of two
+ * - the result is undefined when n == 0
+ * - this can be used to initialise global variables from constant data
+ */
+#define rounddown_pow_of_two(n)                        \
+(                                              \
+       __builtin_constant_p(n) ? (             \
+               (n == 1) ? 0 :                  \
+               (1UL << ilog2(n))) :            \
+       __rounddown_pow_of_two(n)               \
+ )
+
+/**
+ * order_base_2 - calculate the (rounded up) base 2 order of the argument
+ * @n: parameter
+ *
+ * The first few values calculated by this routine:
+ *  ob2(0) = 0
+ *  ob2(1) = 0
+ *  ob2(2) = 1
+ *  ob2(3) = 2
+ *  ob2(4) = 2
+ *  ob2(5) = 3
+ *  ... and so on.
+ */
+
+#define order_base_2(n) ilog2(roundup_pow_of_two(n))
+
+#endif /* _LINUX_LOG2_H */

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH 9 of 10] New/Updated drivers: New generic RTC class PC-style 'CMOS' driver backported from Linux Kernel Ver. 2.6.29.2, Daniel Kiper <=