x86, vmlinux.lds: add copyright
[safe/jmp/linux-2.6] / arch / x86 / kernel / rtc.c
index 45bf54d..5d465b2 100644 (file)
@@ -1,11 +1,31 @@
 /*
  * RTC related functions
  */
-#include <linux/bcd.h>
+#include <linux/platform_device.h>
 #include <linux/mc146818rtc.h>
+#include <linux/acpi.h>
+#include <linux/bcd.h>
+#include <linux/pnp.h>
 
+#include <asm/vsyscall.h>
 #include <asm/time.h>
 
+#ifdef CONFIG_X86_32
+/*
+ * This is a special lock that is owned by the CPU and holds the index
+ * register we are working with.  It is required for NMI access to the
+ * CMOS/RTC registers.  See include/asm-i386/mc146818rtc.h for details.
+ */
+volatile unsigned long cmos_lock;
+EXPORT_SYMBOL(cmos_lock);
+#endif /* CONFIG_X86_32 */
+
+/* For two digit years assume time is always after that */
+#define CMOS_YEARS_OFFS 2000
+
+DEFINE_SPINLOCK(rtc_lock);
+EXPORT_SYMBOL(rtc_lock);
+
 /*
  * In order to set the CMOS clock precisely, set_rtc_mmss has to be
  * called 500 ms after the second nowtime has started, because when
  */
 int mach_set_rtc_mmss(unsigned long nowtime)
 {
-       int retval = 0;
        int real_seconds, real_minutes, cmos_minutes;
        unsigned char save_control, save_freq_select;
+       int retval = 0;
 
-       save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
+        /* tell the clock it's being set */
+       save_control = CMOS_READ(RTC_CONTROL);
        CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 
-       save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
+       /* stop and reset prescaler */
+       save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
        CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
 
        cmos_minutes = CMOS_READ(RTC_MINUTES);
        if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
-               BCD_TO_BIN(cmos_minutes);
+               cmos_minutes = bcd2bin(cmos_minutes);
 
        /*
         * since we're only adjusting minutes and seconds,
@@ -40,17 +62,18 @@ int mach_set_rtc_mmss(unsigned long nowtime)
         */
        real_seconds = nowtime % 60;
        real_minutes = nowtime / 60;
+       /* correct for half hour time zone */
        if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
-               real_minutes += 30;             /* correct for half hour time zone */
+               real_minutes += 30;
        real_minutes %= 60;
 
        if (abs(real_minutes - cmos_minutes) < 30) {
                if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
-                       BIN_TO_BCD(real_seconds);
-                       BIN_TO_BCD(real_minutes);
+                       real_seconds = bin2bcd(real_seconds);
+                       real_minutes = bin2bcd(real_minutes);
                }
-               CMOS_WRITE(real_seconds,RTC_SECONDS);
-               CMOS_WRITE(real_minutes,RTC_MINUTES);
+               CMOS_WRITE(real_seconds, RTC_SECONDS);
+               CMOS_WRITE(real_minutes, RTC_MINUTES);
        } else {
                printk(KERN_WARNING
                       "set_rtc_mmss: can't update from %d to %d\n",
@@ -73,53 +96,62 @@ int mach_set_rtc_mmss(unsigned long nowtime)
 
 unsigned long mach_get_cmos_time(void)
 {
-       unsigned int year, mon, day, hour, min, sec;
-
-       do {
-               sec = CMOS_READ(RTC_SECONDS);
-               min = CMOS_READ(RTC_MINUTES);
-               hour = CMOS_READ(RTC_HOURS);
-               day = CMOS_READ(RTC_DAY_OF_MONTH);
-               mon = CMOS_READ(RTC_MONTH);
-               year = CMOS_READ(RTC_YEAR);
-       } while (sec != CMOS_READ(RTC_SECONDS));
-
-       if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
-               BCD_TO_BIN(sec);
-               BCD_TO_BIN(min);
-               BCD_TO_BIN(hour);
-               BCD_TO_BIN(day);
-               BCD_TO_BIN(mon);
-               BCD_TO_BIN(year);
+       unsigned int status, year, mon, day, hour, min, sec, century = 0;
+
+       /*
+        * If UIP is clear, then we have >= 244 microseconds before
+        * RTC registers will be updated.  Spec sheet says that this
+        * is the reliable way to read RTC - registers. If UIP is set
+        * then the register access might be invalid.
+        */
+       while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+               cpu_relax();
+
+       sec = CMOS_READ(RTC_SECONDS);
+       min = CMOS_READ(RTC_MINUTES);
+       hour = CMOS_READ(RTC_HOURS);
+       day = CMOS_READ(RTC_DAY_OF_MONTH);
+       mon = CMOS_READ(RTC_MONTH);
+       year = CMOS_READ(RTC_YEAR);
+
+#ifdef CONFIG_ACPI
+       if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
+           acpi_gbl_FADT.century)
+               century = CMOS_READ(acpi_gbl_FADT.century);
+#endif
+
+       status = CMOS_READ(RTC_CONTROL);
+       WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY));
+
+       if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) {
+               sec = bcd2bin(sec);
+               min = bcd2bin(min);
+               hour = bcd2bin(hour);
+               day = bcd2bin(day);
+               mon = bcd2bin(mon);
+               year = bcd2bin(year);
        }
 
-       year += 1900;
-       if (year < 1970)
-               year += 100;
+       if (century) {
+               century = bcd2bin(century);
+               year += century * 100;
+               printk(KERN_INFO "Extended CMOS year: %d\n", century * 100);
+       } else
+               year += CMOS_YEARS_OFFS;
 
        return mktime(year, mon, day, hour, min, sec);
 }
 
-DEFINE_SPINLOCK(rtc_lock);
-EXPORT_SYMBOL(rtc_lock);
-
-/*
- * This is a special lock that is owned by the CPU and holds the index
- * register we are working with.  It is required for NMI access to the
- * CMOS/RTC registers.  See include/asm-i386/mc146818rtc.h for details.
- */
-volatile unsigned long cmos_lock = 0;
-EXPORT_SYMBOL(cmos_lock);
-
 /* Routines for accessing the CMOS RAM/RTC. */
 unsigned char rtc_cmos_read(unsigned char addr)
 {
        unsigned char val;
 
        lock_cmos_prefix(addr);
-       outb_p(addr, RTC_PORT(0));
-       val = inb_p(RTC_PORT(1));
+       outb(addr, RTC_PORT(0));
+       val = inb(RTC_PORT(1));
        lock_cmos_suffix(addr);
+
        return val;
 }
 EXPORT_SYMBOL(rtc_cmos_read);
@@ -127,19 +159,17 @@ EXPORT_SYMBOL(rtc_cmos_read);
 void rtc_cmos_write(unsigned char val, unsigned char addr)
 {
        lock_cmos_prefix(addr);
-       outb_p(addr, RTC_PORT(0));
-       outb_p(val, RTC_PORT(1));
+       outb(addr, RTC_PORT(0));
+       outb(val, RTC_PORT(1));
        lock_cmos_suffix(addr);
 }
 EXPORT_SYMBOL(rtc_cmos_write);
 
 static int set_rtc_mmss(unsigned long nowtime)
 {
-       int retval;
        unsigned long flags;
+       int retval;
 
-       /* gets recalled with irq locally disabled */
-       /* XXX - does irqsave resolve this? -johnstul */
        spin_lock_irqsave(&rtc_lock, flags);
        retval = set_wallclock(nowtime);
        spin_unlock_irqrestore(&rtc_lock, flags);
@@ -150,8 +180,7 @@ static int set_rtc_mmss(unsigned long nowtime)
 /* not static: needed by APM */
 unsigned long read_persistent_clock(void)
 {
-       unsigned long retval;
-       unsigned long flags;
+       unsigned long retval, flags;
 
        spin_lock_irqsave(&rtc_lock, flags);
        retval = get_wallclock();
@@ -164,3 +193,57 @@ int update_persistent_clock(struct timespec now)
 {
        return set_rtc_mmss(now.tv_sec);
 }
+
+unsigned long long native_read_tsc(void)
+{
+       return __native_read_tsc();
+}
+EXPORT_SYMBOL(native_read_tsc);
+
+
+static struct resource rtc_resources[] = {
+       [0] = {
+               .start  = RTC_PORT(0),
+               .end    = RTC_PORT(1),
+               .flags  = IORESOURCE_IO,
+       },
+       [1] = {
+               .start  = RTC_IRQ,
+               .end    = RTC_IRQ,
+               .flags  = IORESOURCE_IRQ,
+       }
+};
+
+static struct platform_device rtc_device = {
+       .name           = "rtc_cmos",
+       .id             = -1,
+       .resource       = rtc_resources,
+       .num_resources  = ARRAY_SIZE(rtc_resources),
+};
+
+static __init int add_rtc_cmos(void)
+{
+#ifdef CONFIG_PNP
+       static const char *ids[] __initconst =
+           { "PNP0b00", "PNP0b01", "PNP0b02", };
+       struct pnp_dev *dev;
+       struct pnp_id *id;
+       int i;
+
+       pnp_for_each_dev(dev) {
+               for (id = dev->id; id; id = id->next) {
+                       for (i = 0; i < ARRAY_SIZE(ids); i++) {
+                               if (compare_pnp_id(id, ids[i]) != 0)
+                                       return 0;
+                       }
+               }
+       }
+#endif
+
+       platform_device_register(&rtc_device);
+       dev_info(&rtc_device.dev,
+                "registered platform RTC device (no PNP device found)\n");
+
+       return 0;
+}
+device_initcall(add_rtc_cmos);