]> err.no Git - linux-2.6/commitdiff
[PATCH] Time: i386 Clocksource Drivers
authorjohn stultz <johnstul@us.ibm.com>
Mon, 26 Jun 2006 07:25:12 +0000 (00:25 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 26 Jun 2006 16:58:21 +0000 (09:58 -0700)
Implement the time sources for i386 (acpi_pm, cyclone, hpet, pit, and tsc).
With this patch, the conversion of the i386 arch to the generic timekeeping
code should be complete.

The patch should be fairly straight forward, only adding the new clocksources.

[hirofumi@mail.parknet.co.jp: acpi_pm cleanup]
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Documentation/kernel-parameters.txt
arch/i386/kernel/Makefile
arch/i386/kernel/hpet.c [new file with mode: 0644]
arch/i386/kernel/i8253.c
arch/i386/kernel/time.c
arch/i386/kernel/tsc.c
drivers/Makefile
drivers/clocksource/Makefile [new file with mode: 0644]
drivers/clocksource/acpi_pm.c [new file with mode: 0644]
drivers/clocksource/cyclone.c [new file with mode: 0644]
kernel/time/clocksource.c

index 968631678d4a0b1c9fde8156ad9dbb5929ebd4f6..2e352a605fcfef3a6dcb83d4c875635f9f89a71a 100644 (file)
@@ -180,6 +180,11 @@ running once the system is up.
                        override platform specific driver.
                        See also Documentation/acpi-hotkey.txt.
 
+       acpi_pm_good    [IA-32,X86-64]
+                       Override the pmtimer bug detection: force the kernel
+                       to assume that this machine's pmtimer latches its value
+                       and always returns good values.
+
        enable_timer_pin_1 [i386,x86-64]
                        Enable PIN 1 of APIC timer
                        Can be useful to work around chipset bugs
index f238cb6274ebf855002a4d8aac36029180d3436e..0fac85df64f1d51f4274aefd4cf3ff750427cc08 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_EFI)             += efi.o efi_stub.o
 obj-$(CONFIG_DOUBLEFAULT)      += doublefault.o
 obj-$(CONFIG_VM86)             += vm86.o
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
+obj-$(CONFIG_HPET_TIMER)       += hpet.o
 
 EXTRA_AFLAGS   := -traditional
 
diff --git a/arch/i386/kernel/hpet.c b/arch/i386/kernel/hpet.c
new file mode 100644 (file)
index 0000000..91a5bdd
--- /dev/null
@@ -0,0 +1,67 @@
+#include <linux/clocksource.h>
+#include <linux/errno.h>
+#include <linux/hpet.h>
+#include <linux/init.h>
+
+#include <asm/hpet.h>
+#include <asm/io.h>
+
+#define HPET_MASK      0xFFFFFFFF
+#define HPET_SHIFT     22
+
+/* FSEC = 10^-15 NSEC = 10^-9 */
+#define FSEC_PER_NSEC  1000000
+
+static void *hpet_ptr;
+
+static cycle_t read_hpet(void)
+{
+       return (cycle_t)readl(hpet_ptr);
+}
+
+static struct clocksource clocksource_hpet = {
+       .name           = "hpet",
+       .rating         = 250,
+       .read           = read_hpet,
+       .mask           = (cycle_t)HPET_MASK,
+       .mult           = 0, /* set below */
+       .shift          = HPET_SHIFT,
+       .is_continuous  = 1,
+};
+
+static int __init init_hpet_clocksource(void)
+{
+       unsigned long hpet_period;
+       void __iomem* hpet_base;
+       u64 tmp;
+
+       if (!hpet_address)
+               return -ENODEV;
+
+       /* calculate the hpet address: */
+       hpet_base =
+               (void __iomem*)ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
+       hpet_ptr = hpet_base + HPET_COUNTER;
+
+       /* calculate the frequency: */
+       hpet_period = readl(hpet_base + HPET_PERIOD);
+
+       /*
+        * hpet period is in femto seconds per cycle
+        * so we need to convert this to ns/cyc units
+        * aproximated by mult/2^shift
+        *
+        *  fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift
+        *  fsec/cyc * 1ns/1000000fsec * 2^shift = mult
+        *  fsec/cyc * 2^shift * 1nsec/1000000fsec = mult
+        *  (fsec/cyc << shift)/1000000 = mult
+        *  (hpet_period << shift)/FSEC_PER_NSEC = mult
+        */
+       tmp = (u64)hpet_period << HPET_SHIFT;
+       do_div(tmp, FSEC_PER_NSEC);
+       clocksource_hpet.mult = (u32)tmp;
+
+       return register_clocksource(&clocksource_hpet);
+}
+
+module_init(init_hpet_clocksource);
index 29cb2eb34363683d1bbc2ac6d587523de7b460a2..a276bceade68e2481f680e87facd9ab36b3df443 100644 (file)
@@ -2,6 +2,7 @@
  * i8253.c  8253/PIT functions
  *
  */
+#include <linux/clocksource.h>
 #include <linux/spinlock.h>
 #include <linux/jiffies.h>
 #include <linux/sysdev.h>
@@ -30,3 +31,55 @@ void setup_pit_timer(void)
        outb(LATCH >> 8 , PIT_CH0);     /* MSB */
        spin_unlock_irqrestore(&i8253_lock, flags);
 }
+
+/*
+ * Since the PIT overflows every tick, its not very useful
+ * to just read by itself. So use jiffies to emulate a free
+ * running counter:
+ */
+static cycle_t pit_read(void)
+{
+       unsigned long flags;
+       int count;
+       u64 jifs;
+
+       spin_lock_irqsave(&i8253_lock, flags);
+       outb_p(0x00, PIT_MODE); /* latch the count ASAP */
+       count = inb_p(PIT_CH0); /* read the latched count */
+       count |= inb_p(PIT_CH0) << 8;
+
+       /* VIA686a test code... reset the latch if count > max + 1 */
+       if (count > LATCH) {
+               outb_p(0x34, PIT_MODE);
+               outb_p(LATCH & 0xff, PIT_CH0);
+               outb(LATCH >> 8, PIT_CH0);
+               count = LATCH - 1;
+       }
+       spin_unlock_irqrestore(&i8253_lock, flags);
+
+       jifs = jiffies_64;
+
+       jifs -= INITIAL_JIFFIES;
+       count = (LATCH-1) - count;
+
+       return (cycle_t)(jifs * LATCH) + count;
+}
+
+static struct clocksource clocksource_pit = {
+       .name   = "pit",
+       .rating = 110,
+       .read   = pit_read,
+       .mask   = (cycle_t)-1,
+       .mult   = 0,
+       .shift  = 20,
+};
+
+static int __init init_pit_clocksource(void)
+{
+       if (num_possible_cpus() > 4) /* PIT does not scale! */
+               return 0;
+
+       clocksource_pit.mult = clocksource_hz2mult(CLOCK_TICK_RATE, 20);
+       return register_clocksource(&clocksource_pit);
+}
+module_init(init_pit_clocksource);
index 2a6ab86ffc1571b76842d13eb92035c264588a38..5f43d0410122764fc8dd752b90878cc9fed26841 100644 (file)
@@ -82,9 +82,6 @@ extern unsigned long wall_jiffies;
 DEFINE_SPINLOCK(rtc_lock);
 EXPORT_SYMBOL(rtc_lock);
 
-/* XXX - necessary to keep things compiling. to be removed later */
-u32 pmtmr_ioport;
-
 /*
  * 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
index 96b307495e5ff00d589865614479f23fdef61cc6..7713f86389afd76bda7d7848c11a5831894e8379 100644 (file)
@@ -4,11 +4,14 @@
  * See comments there for proper credits.
  */
 
+#include <linux/clocksource.h>
 #include <linux/workqueue.h>
 #include <linux/cpufreq.h>
 #include <linux/jiffies.h>
 #include <linux/init.h>
+#include <linux/dmi.h>
 
+#include <asm/delay.h>
 #include <asm/tsc.h>
 #include <asm/delay.h>
 #include <asm/io.h>
@@ -315,3 +318,161 @@ static int __init cpufreq_tsc(void)
 core_initcall(cpufreq_tsc);
 
 #endif
+
+/* clock source code */
+
+static unsigned long current_tsc_khz = 0;
+static int tsc_update_callback(void);
+
+static cycle_t read_tsc(void)
+{
+       cycle_t ret;
+
+       rdtscll(ret);
+
+       return ret;
+}
+
+static struct clocksource clocksource_tsc = {
+       .name                   = "tsc",
+       .rating                 = 300,
+       .read                   = read_tsc,
+       .mask                   = (cycle_t)-1,
+       .mult                   = 0, /* to be set */
+       .shift                  = 22,
+       .update_callback        = tsc_update_callback,
+       .is_continuous          = 1,
+};
+
+static int tsc_update_callback(void)
+{
+       int change = 0;
+
+       /* check to see if we should switch to the safe clocksource: */
+       if (clocksource_tsc.rating != 50 && check_tsc_unstable()) {
+               clocksource_tsc.rating = 50;
+               reselect_clocksource();
+               change = 1;
+       }
+
+       /* only update if tsc_khz has changed: */
+       if (current_tsc_khz != tsc_khz) {
+               current_tsc_khz = tsc_khz;
+               clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
+                                                       clocksource_tsc.shift);
+               change = 1;
+       }
+
+       return change;
+}
+
+static int __init dmi_mark_tsc_unstable(struct dmi_system_id *d)
+{
+       printk(KERN_NOTICE "%s detected: marking TSC unstable.\n",
+                      d->ident);
+       mark_tsc_unstable();
+       return 0;
+}
+
+/* List of systems that have known TSC problems */
+static struct dmi_system_id __initdata bad_tsc_dmi_table[] = {
+       {
+        .callback = dmi_mark_tsc_unstable,
+        .ident = "IBM Thinkpad 380XD",
+        .matches = {
+                    DMI_MATCH(DMI_BOARD_VENDOR, "IBM"),
+                    DMI_MATCH(DMI_BOARD_NAME, "2635FA0"),
+                    },
+        },
+        {}
+};
+
+#define TSC_FREQ_CHECK_INTERVAL (10*MSEC_PER_SEC) /* 10sec in MS */
+static struct timer_list verify_tsc_freq_timer;
+
+/* XXX - Probably should add locking */
+static void verify_tsc_freq(unsigned long unused)
+{
+       static u64 last_tsc;
+       static unsigned long last_jiffies;
+
+       u64 now_tsc, interval_tsc;
+       unsigned long now_jiffies, interval_jiffies;
+
+
+       if (check_tsc_unstable())
+               return;
+
+       rdtscll(now_tsc);
+       now_jiffies = jiffies;
+
+       if (!last_jiffies) {
+               goto out;
+       }
+
+       interval_jiffies = now_jiffies - last_jiffies;
+       interval_tsc = now_tsc - last_tsc;
+       interval_tsc *= HZ;
+       do_div(interval_tsc, cpu_khz*1000);
+
+       if (interval_tsc < (interval_jiffies * 3 / 4)) {
+               printk("TSC appears to be running slowly. "
+                       "Marking it as unstable\n");
+               mark_tsc_unstable();
+               return;
+       }
+
+out:
+       last_tsc = now_tsc;
+       last_jiffies = now_jiffies;
+       /* set us up to go off on the next interval: */
+       mod_timer(&verify_tsc_freq_timer,
+               jiffies + msecs_to_jiffies(TSC_FREQ_CHECK_INTERVAL));
+}
+
+/*
+ * Make an educated guess if the TSC is trustworthy and synchronized
+ * over all CPUs.
+ */
+static __init int unsynchronized_tsc(void)
+{
+       /*
+        * Intel systems are normally all synchronized.
+        * Exceptions must mark TSC as unstable:
+        */
+       if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
+               return 0;
+
+       /* assume multi socket systems are not synchronized: */
+       return num_possible_cpus() > 1;
+}
+
+static int __init init_tsc_clocksource(void)
+{
+
+       if (cpu_has_tsc && tsc_khz && !tsc_disable) {
+               /* check blacklist */
+               dmi_check_system(bad_tsc_dmi_table);
+
+               if (unsynchronized_tsc()) /* mark unstable if unsynced */
+                       mark_tsc_unstable();
+               current_tsc_khz = tsc_khz;
+               clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
+                                                       clocksource_tsc.shift);
+               /* lower the rating if we already know its unstable: */
+               if (check_tsc_unstable())
+                       clocksource_tsc.rating = 50;
+
+               init_timer(&verify_tsc_freq_timer);
+               verify_tsc_freq_timer.function = verify_tsc_freq;
+               verify_tsc_freq_timer.expires =
+                       jiffies + msecs_to_jiffies(TSC_FREQ_CHECK_INTERVAL);
+               add_timer(&verify_tsc_freq_timer);
+
+               return register_clocksource(&clocksource_tsc);
+       }
+
+       return 0;
+}
+
+module_init(init_tsc_clocksource);
index 3c5170310bd0d407277fe5bb36a5cef87de8ba3c..fc2d744a4e4a75e3960589df663a84650c9a85b7 100644 (file)
@@ -74,4 +74,5 @@ obj-$(CONFIG_SGI_SN)          += sn/
 obj-y                          += firmware/
 obj-$(CONFIG_CRYPTO)           += crypto/
 obj-$(CONFIG_SUPERH)           += sh/
+obj-$(CONFIG_GENERIC_TIME)     += clocksource/
 obj-$(CONFIG_DMA_ENGINE)       += dma/
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
new file mode 100644 (file)
index 0000000..be3511a
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
+obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
new file mode 100644 (file)
index 0000000..a0e5cde
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * linux/drivers/clocksource/acpi_pm.c
+ *
+ * This file contains the ACPI PM based clocksource.
+ *
+ * This code was largely moved from the i386 timer_pm.c file
+ * which was (C) Dominik Brodowski <linux@brodo.de> 2003
+ * and contained the following comments:
+ *
+ * Driver to use the Power Management Timer (PMTMR) available in some
+ * southbridges as primary timing source for the Linux kernel.
+ *
+ * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
+ * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
+ *
+ * This file is licensed under the GPL v2.
+ */
+
+#include <linux/clocksource.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+/* Number of PMTMR ticks expected during calibration run */
+#define PMTMR_TICKS_PER_SEC 3579545
+
+/*
+ * The I/O port the PMTMR resides at.
+ * The location is detected during setup_arch(),
+ * in arch/i386/acpi/boot.c
+ */
+u32 pmtmr_ioport;
+
+#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
+
+static inline u32 read_pmtmr(void)
+{
+       /* mask the output to 24 bits */
+       return inl(pmtmr_ioport) & ACPI_PM_MASK;
+}
+
+static cycle_t acpi_pm_read_verified(void)
+{
+       u32 v1 = 0, v2 = 0, v3 = 0;
+
+       /*
+        * It has been reported that because of various broken
+        * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM clock
+        * source is not latched, so you must read it multiple
+        * times to ensure a safe value is read:
+        */
+       do {
+               v1 = read_pmtmr();
+               v2 = read_pmtmr();
+               v3 = read_pmtmr();
+       } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
+                       || (v3 > v1 && v3 < v2));
+
+       return (cycle_t)v2;
+}
+
+static cycle_t acpi_pm_read(void)
+{
+       return (cycle_t)read_pmtmr();
+}
+
+static struct clocksource clocksource_acpi_pm = {
+       .name           = "acpi_pm",
+       .rating         = 200,
+       .read           = acpi_pm_read,
+       .mask           = (cycle_t)ACPI_PM_MASK,
+       .mult           = 0, /*to be caluclated*/
+       .shift          = 22,
+       .is_continuous  = 1,
+};
+
+
+#ifdef CONFIG_PCI
+static int acpi_pm_good;
+static int __init acpi_pm_good_setup(char *__str)
+{
+       acpi_pm_good = 1;
+       return 1;
+}
+__setup("acpi_pm_good", acpi_pm_good_setup);
+
+static inline void acpi_pm_need_workaround(void)
+{
+       clocksource_acpi_pm.read = acpi_pm_read_verified;
+       clocksource_acpi_pm.rating = 110;
+}
+
+/*
+ * PIIX4 Errata:
+ *
+ * The power management timer may return improper results when read.
+ * Although the timer value settles properly after incrementing,
+ * while incrementing there is a 3 ns window every 69.8 ns where the
+ * timer value is indeterminate (a 4.2% chance that the data will be
+ * incorrect when read). As a result, the ACPI free running count up
+ * timer specification is violated due to erroneous reads.
+ */
+static void __devinit acpi_pm_check_blacklist(struct pci_dev *dev)
+{
+       u8 rev;
+
+       if (acpi_pm_good)
+               return;
+
+       pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
+       /* the bug has been fixed in PIIX4M */
+       if (rev < 3) {
+               printk(KERN_WARNING "* Found PM-Timer Bug on the chipset."
+                      " Due to workarounds for a bug,\n"
+                      "* this clock source is slow. Consider trying"
+                      " other clock sources\n");
+
+               acpi_pm_need_workaround();
+       }
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3,
+                       acpi_pm_check_blacklist);
+
+static void __devinit acpi_pm_check_graylist(struct pci_dev *dev)
+{
+       if (acpi_pm_good)
+               return;
+
+       printk(KERN_WARNING "* The chipset may have PM-Timer Bug. Due to"
+              " workarounds for a bug,\n"
+              "* this clock source is slow. If you are sure your timer"
+              " does not have\n"
+              "* this bug, please use \"acpi_pm_good\" to disable the"
+              " workaround\n");
+
+       acpi_pm_need_workaround();
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
+                       acpi_pm_check_graylist);
+#endif
+
+
+static int __init init_acpi_pm_clocksource(void)
+{
+       u32 value1, value2;
+       unsigned int i;
+
+       if (!pmtmr_ioport)
+               return -ENODEV;
+
+       clocksource_acpi_pm.mult = clocksource_hz2mult(PMTMR_TICKS_PER_SEC,
+                                               clocksource_acpi_pm.shift);
+
+       /* "verify" this timing source: */
+       value1 = read_pmtmr();
+       for (i = 0; i < 10000; i++) {
+               value2 = read_pmtmr();
+               if (value2 == value1)
+                       continue;
+               if (value2 > value1)
+                       goto pm_good;
+               if ((value2 < value1) && ((value2) < 0xFFF))
+                       goto pm_good;
+               printk(KERN_INFO "PM-Timer had inconsistent results:"
+                       " 0x%#x, 0x%#x - aborting.\n", value1, value2);
+               return -EINVAL;
+       }
+       printk(KERN_INFO "PM-Timer had no reasonable result:"
+                       " 0x%#x - aborting.\n", value1);
+       return -ENODEV;
+
+pm_good:
+       return register_clocksource(&clocksource_acpi_pm);
+}
+
+module_init(init_acpi_pm_clocksource);
diff --git a/drivers/clocksource/cyclone.c b/drivers/clocksource/cyclone.c
new file mode 100644 (file)
index 0000000..444eb11
--- /dev/null
@@ -0,0 +1,119 @@
+#include <linux/clocksource.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/timex.h>
+#include <linux/init.h>
+
+#include <asm/pgtable.h>
+#include <asm/io.h>
+
+#include "mach_timer.h"
+
+#define CYCLONE_CBAR_ADDR      0xFEB00CD0      /* base address ptr */
+#define CYCLONE_PMCC_OFFSET    0x51A0          /* offset to control register */
+#define CYCLONE_MPCS_OFFSET    0x51A8          /* offset to select register */
+#define CYCLONE_MPMC_OFFSET    0x51D0          /* offset to count register */
+#define CYCLONE_TIMER_FREQ     99780000        /* 100Mhz, but not really */
+#define CYCLONE_TIMER_MASK     0xFFFFFFFF      /* 32 bit mask */
+
+int use_cyclone = 0;
+static void __iomem *cyclone_ptr;
+
+static cycle_t read_cyclone(void)
+{
+       return (cycle_t)readl(cyclone_ptr);
+}
+
+static struct clocksource clocksource_cyclone = {
+       .name           = "cyclone",
+       .rating         = 250,
+       .read           = read_cyclone,
+       .mask           = (cycle_t)CYCLONE_TIMER_MASK,
+       .mult           = 10,
+       .shift          = 0,
+       .is_continuous  = 1,
+};
+
+static int __init init_cyclone_clocksource(void)
+{
+       unsigned long base;     /* saved value from CBAR */
+       unsigned long offset;
+       u32 __iomem* volatile cyclone_timer;    /* Cyclone MPMC0 register */
+       u32 __iomem* reg;
+       int i;
+
+       /* make sure we're on a summit box: */
+       if (!use_cyclone)
+               return -ENODEV;
+
+       printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
+
+       /* find base address: */
+       offset = CYCLONE_CBAR_ADDR;
+       reg = ioremap_nocache(offset, sizeof(reg));
+       if (!reg) {
+               printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
+               return -ENODEV;
+       }
+       /* even on 64bit systems, this is only 32bits: */
+       base = readl(reg);
+       if (!base) {
+               printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
+               return -ENODEV;
+       }
+       iounmap(reg);
+
+       /* setup PMCC: */
+       offset = base + CYCLONE_PMCC_OFFSET;
+       reg = ioremap_nocache(offset, sizeof(reg));
+       if (!reg) {
+               printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
+               return -ENODEV;
+       }
+       writel(0x00000001,reg);
+       iounmap(reg);
+
+       /* setup MPCS: */
+       offset = base + CYCLONE_MPCS_OFFSET;
+       reg = ioremap_nocache(offset, sizeof(reg));
+       if (!reg) {
+               printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
+               return -ENODEV;
+       }
+       writel(0x00000001,reg);
+       iounmap(reg);
+
+       /* map in cyclone_timer: */
+       offset = base + CYCLONE_MPMC_OFFSET;
+       cyclone_timer = ioremap_nocache(offset, sizeof(u64));
+       if (!cyclone_timer) {
+               printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
+               return -ENODEV;
+       }
+
+       /* quick test to make sure its ticking: */
+       for (i = 0; i < 3; i++){
+               u32 old = readl(cyclone_timer);
+               int stall = 100;
+
+               while (stall--)
+                       barrier();
+
+               if (readl(cyclone_timer) == old) {
+                       printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
+                       iounmap(cyclone_timer);
+                       cyclone_timer = NULL;
+                       return -ENODEV;
+               }
+       }
+       cyclone_ptr = cyclone_timer;
+
+       /* sort out mult/shift values: */
+       clocksource_cyclone.shift = 22;
+       clocksource_cyclone.mult = clocksource_hz2mult(CYCLONE_TIMER_FREQ,
+                                               clocksource_cyclone.shift);
+
+       return register_clocksource(&clocksource_cyclone);
+}
+
+module_init(init_cyclone_clocksource);
index 4288bfa12c3f03c2645b0fa9caf5747d0b982302..a9f387ea83b0a52181b0f10ddb10c63ccd1ea2e7 100644 (file)
@@ -174,7 +174,7 @@ EXPORT_SYMBOL(register_clocksource);
  * reselect_clocksource - Rescan list for next clocksource
  *
  * A quick helper function to be used if a clocksource changes its
- * rating. Forces the clocksource list to be re-scaned for the best
+ * rating. Forces the clocksource list to be re-scanned for the best
  * clocksource.
  */
 void reselect_clocksource(void)
@@ -336,8 +336,13 @@ __setup("clocksource=", boot_override_clocksource);
  */
 static int __init boot_override_clock(char* str)
 {
-       printk("Warning! clock= boot option is deprecated.\n");
-
+       if (!strcmp(str, "pmtmr")) {
+               printk("Warning: clock=pmtmr is deprecated. "
+                       "Use clocksource=acpi_pm.\n");
+               return boot_override_clocksource("acpi_pm");
+       }
+       printk("Warning! clock= boot option is deprecated. "
+               "Use clocksource=xyz\n");
        return boot_override_clocksource(str);
 }