--- /dev/null
+
+From: Jiri Bohac <jbohac@suse.cz>
+Subject: allow 64-bit mode for HPET Timer0
+References: bnc#456700
+
+The kernel uses the HPET timers in 32-bit mode for clock-events.
+While 32 bits, with a wrap-around time of >4 minutes, is probably
+good enough for the clock-event purposes, on some chipsets this
+has a negative side-effect on the HPET main counter.
+
+Unlike the original HPET specification 1.0 from 2004, which does not
+mention any side-effects of setting TN_32MODE_CNF on the
+individual timers, the ICH9 documentation, for example, says:
+
+ NOTE: When this bit is set to ‘1’, the hardware counter will
+ do a 32-bit operation on comparator match and rollovers, thus
+ the upper 32-bit of the Timer 0 Comparator Value register is
+ ignored. The upper 32-bit of the main counter is not involved
+ in any rollover from lower 32-bit of the main counter and
+ becomes all zeros.
+
+(see http://www.intel.com/assets/pdf/datasheet/316972.pdf, page
+819, section 21.1.5, Bit 8). I've seen this behaviour also on
+ICH8. I have no idea what other chipsets are affected. But I have
+seen AMD chipsets that Do The Right Thing.
+
+This means, that when the kernel configures the Timer 0 to 32-bit
+mode, on these chipsets it also cripples the 64-bit main counter
+to 32 bits.
+
+The HPET may be mmapped in userspace and the main counter
+accessed directly by applications, expecting a 64-bit main
+counter.
+
+This patch allows the Timer0 to be configured in 64-bit mode
+on x86_64 when a hpet64 command-line option is specified.
+
+Signed-off-by: Jiri Bohac <jbohac@suse.cz>
+
+---
+ arch/x86/kernel/hpet.c | 89 +++++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 79 insertions(+), 10 deletions(-)
+
+--- a/arch/x86/kernel/hpet.c 2009-01-24 16:39:47.000000000 +0100
++++ b/arch/x86/kernel/hpet.c 2009-01-24 16:56:04.000000000 +0100
+@@ -24,6 +24,7 @@
+ */
+ unsigned long hpet_address;
+ static void __iomem *hpet_virt_address;
++static int hpet_legacy_use_64_bits;
+
+ unsigned long hpet_readl(unsigned long a)
+ {
+@@ -37,6 +38,33 @@ static inline void hpet_writel(unsigned
+
+ #ifdef CONFIG_X86_64
+ #include <asm/pgtable.h>
++static inline unsigned long hpet_read_value(unsigned long a)
++{
++ if (hpet_legacy_use_64_bits)
++ return readq(hpet_virt_address + a);
++ else
++ return readl(hpet_virt_address + a);
++}
++
++static void hpet_write_value(unsigned long d, unsigned long a)
++{
++ if (hpet_legacy_use_64_bits)
++ writeq(d, hpet_virt_address + a);
++ else
++ writel(d, hpet_virt_address + a);
++}
++
++#else
++
++static inline unsigned long hpet_read_value(unsigned long a)
++{
++ return readl(hpet_virt_address + a);
++}
++
++static void hpet_write_value(unsigned long d, unsigned long a)
++{
++ writel(d, hpet_virt_address + a);
++}
+ #endif
+
+ static inline void hpet_set_mapping(void)
+@@ -78,6 +106,17 @@ static int __init disable_hpet(char *str
+ }
+ __setup("nohpet", disable_hpet);
+
++#ifdef CONFIG_X86_64
++static int hpet64 = 0;
++static int __init hpet64_setup(char *str)
++{
++ hpet64 = 1;
++ return 1;
++}
++__setup("hpet64", hpet64_setup);
++#endif
++
++
+ static inline int is_hpet_capable(void)
+ {
+ return (!boot_hpet_disable && hpet_address);
+@@ -141,6 +180,7 @@ static void hpet_reserve_platform_timers
+ * Common hpet info
+ */
+ static unsigned long hpet_period;
++static int hpet_legacy_use_64_bits; /* configure T0 in 64-bit mode? */
+
+ static void hpet_legacy_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt);
+@@ -192,10 +232,38 @@ static void hpet_enable_legacy_int(void)
+ hpet_legacy_int_enabled = 1;
+ }
+
++static int timer0_use_64_bits(void)
++{
++#ifndef CONFIG_X86_64
++ /* using the HPET in 64-bit mode without atomic 64-bit
++ * accesses is too inefficient
++ */
++ return 0;
++#else
++
++ if (unlikely(hpet64)) {
++ u32 id, t0_cfg;
++ id = hpet_readl(HPET_ID);
++ t0_cfg = hpet_readl(HPET_T0_CFG);
++
++ if ((id & HPET_ID_64BIT) && (t0_cfg & HPET_TN_64BIT_CAP)) {
++ printk(KERN_DEBUG "hpet timer0 configured in 64-bit mode\n");
++ return 1;
++ }
++ else {
++ printk(KERN_DEBUG "hpet timer0 does not support 64-bit mode\n");
++ return 0;
++ }
++ }
++ else return 0;
++#endif
++}
++
+ static void hpet_legacy_clockevent_register(void)
+ {
+ /* Start HPET legacy interrupts */
+ hpet_enable_legacy_int();
++ hpet_legacy_use_64_bits = timer0_use_64_bits();
+
+ /*
+ * The mult factor is defined as (include/linux/clockchips.h)
+@@ -233,26 +301,28 @@ static void hpet_legacy_set_mode(enum cl
+ case CLOCK_EVT_MODE_PERIODIC:
+ delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * hpet_clockevent.mult;
+ delta >>= hpet_clockevent.shift;
+- now = hpet_readl(HPET_COUNTER);
++ now = hpet_read_value(HPET_COUNTER);
+ cmp = now + (unsigned long) delta;
+ cfg = hpet_readl(HPET_T0_CFG);
+ cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
+- HPET_TN_SETVAL | HPET_TN_32BIT;
++ HPET_TN_SETVAL |
++ (hpet_legacy_use_64_bits ? 0 : HPET_TN_32BIT);
+ hpet_writel(cfg, HPET_T0_CFG);
+ /*
+ * The first write after writing TN_SETVAL to the
+ * config register sets the counter value, the second
+ * write sets the period.
+ */
+- hpet_writel(cmp, HPET_T0_CMP);
++ hpet_write_value(cmp, HPET_T0_CMP);
+ udelay(1);
+- hpet_writel((unsigned long) delta, HPET_T0_CMP);
++ hpet_write_value((unsigned long) delta, HPET_T0_CMP);
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ cfg = hpet_readl(HPET_T0_CFG);
+ cfg &= ~HPET_TN_PERIODIC;
+- cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
++ cfg |= HPET_TN_ENABLE |
++ (hpet_legacy_use_64_bits ? 0 : HPET_TN_32BIT);
+ hpet_writel(cfg, HPET_T0_CFG);
+ break;
+
+@@ -272,11 +342,11 @@ static void hpet_legacy_set_mode(enum cl
+ static int hpet_legacy_next_event(unsigned long delta,
+ struct clock_event_device *evt)
+ {
+- u32 cnt;
++ unsigned long cnt;
+
+- cnt = hpet_readl(HPET_COUNTER);
++ cnt = hpet_read_value(HPET_COUNTER);
+ cnt += (u32) delta;
+- hpet_writel(cnt, HPET_T0_CMP);
++ hpet_write_value(cnt, HPET_T0_CMP);
+
+ hpet_readl(HPET_T0_CMP); /* pre-read for bnc#433746 */
+ /*
+@@ -284,9 +354,9 @@ static int hpet_legacy_next_event(unsign
+ * what we wrote hit the chip before we compare it to the
+ * counter.
+ */
+- WARN_ON_ONCE((u32)hpet_readl(HPET_T0_CMP) != cnt);
++ WARN_ON_ONCE((u32)hpet_readl(HPET_T0_CMP) != (u32)cnt);
+
+- return (s32)((u32)hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0;
++ return (s32)((u32)hpet_readl(HPET_COUNTER) - (u32)cnt) >= 0 ? -ETIME : 0;
+ }
+
+ /*
+--- a/Documentation/kernel-parameters.txt 2009-01-24 16:39:47.000000000 +0100
++++ b/Documentation/kernel-parameters.txt 2009-01-24 16:46:51.000000000 +0100
+@@ -475,6 +475,8 @@ and is between 256 and 4096 characters.
+ force: allow force enabled of undocumented chips (ICH4,
+ VIA, nVidia)
+
++ hpet64 [X86-64,HPET] enable 64-bit mode of the HPET timer (bnc#456700)
++
+ com20020= [HW,NET] ARCnet - COM20020 chipset
+ Format:
+ <io>[,<irq>[,<nodeID>[,<backplane>[,<ckp>[,<timeout>]]]]]