[PATCH] HPET: Workaround for a BIOS workaround on AMD SB700platform

From: crane cai
Date: Wed Aug 13 2008 - 23:14:17 EST


>From 9bd2f534f986768f1944e626e37af1c323e47dbb Mon Sep 17 00:00:00 2001
From: Crane Cai <crane.cai@xxxxxxx>
Date: Thu, 14 Aug 2008 10:31:01 +0800
Subject: [PATCH] HPET: Workaround for a BIOS workaround on AMD SB700 platform

On the AMD SB700 southbridge, between the revisions 0x30 to 0x3a, when its
spread-spectrum frequency modulation feature is enabled, the base frequency
used by the HPET will not be running on average slower than nominal 14.318
MHz.
Since there is no provision in the OS for HPET to work with properly with
slower frequency, the BIOS on this platform uses SMM to emulate accesses to
the HPET config register to supply a corrected base frequency to compensate
for it.
However, due to the implementation of the SMM BIOS code, there is a time
window after the first access to the HPET, which triggers initialization of
the SMM code, in which the HPET isn't available. Thus it's necessary to wait
until the HPET emulation is ready, and this is what the patch does on the
affected machines.

Signed-off-by: XiaoGang Zheng <gang.zheng@xxxxxxx>
Signed-off-by: Crane Cai <crane.cai@xxxxxxx>
---
arch/x86/kernel/early-quirks.c | 13 +++++++++++++
arch/x86/kernel/hpet.c | 24 ++++++++++++++++++++++++
include/asm-x86/hpet.h | 2 ++
3 files changed, 39 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c
index 4353cf5..5f6f543 100644
--- a/arch/x86/kernel/early-quirks.c
+++ b/arch/x86/kernel/early-quirks.c
@@ -17,6 +17,7 @@
#include <asm/io_apic.h>
#include <asm/apic.h>
#include <asm/iommu.h>
+#include <asm/hpet.h>

static void __init fix_hypertransport_config(int num, int slot, int func)
{
@@ -95,6 +96,16 @@ static void __init nvidia_bugs(int num, int slot, int func)

}

+static void __init amd_sb700_hpet(int num, int slot, int func)
+{
+ int rev;
+ rev = read_pci_config_byte(num, slot, func, 0x08);
+ if (rev <= 0x3a && rev >= 0x30) {
+ sb700_hpet_workaround = 1;
+ printk(KERN_INFO "SB700 rev 0x3a or lower detected!\n");
+ }
+}
+
#define QFLAG_APPLY_ONCE 0x1
#define QFLAG_APPLIED 0x2
#define QFLAG_DONE (QFLAG_APPLY_ONCE|QFLAG_APPLIED)
@@ -114,6 +125,8 @@ static struct chipset early_qrk[] __initdata = {
PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, via_bugs },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB,
PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, fix_hypertransport_config },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS,
+ PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, amd_sb700_hpet },
{}
};

diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index ad2b15a..3d7f71d 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -23,6 +23,24 @@
* HPET address is set in acpi/boot.c, when an ACPI entry exists
*/
unsigned long hpet_address;
+
+/*
+ * On the AMD SB700 southbridge, between the revisions 0x30 to 0x3a, when its
+ * spread-spectrum frequency modulation feature is enabled, the base frequency
+ * used by the HPET will not be running on average slower than nominal 14.318
+ * MHz.
+ * Since there is no provision in the OS for HPET to work with properly with
+ * slower frequency, the BIOS on this platform uses SMM to emulate accesses to
+ * the HPET config register to supply a corrected base frequency to compensate
+ * for it.
+ * However, due to the implementation of the SMM BIOS code, there is a time
+ * window after the first access to the HPET, which triggers initialization of
+ * the SMM code, in which the HPET isn't available. Thus it's necessary to wait
+ * until the HPET emulation is ready, and this is what the patch does on the
+ * affected machines.
+ */
+int sb700_hpet_workaround __initdata = 0;
+
static void __iomem *hpet_virt_address;

unsigned long hpet_readl(unsigned long a)
@@ -369,6 +387,12 @@ int __init hpet_enable(void)
* Read the period and check for a sane value:
*/
hpet_period = hpet_readl(HPET_PERIOD);
+
+ if (sb700_hpet_workaround) {
+ int timeout = 1000;
+ while (0xffffffff == hpet_readl(HPET_CFG) && timeout-- != 0)
+ ;
+ }
if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD)
goto out_nohpet;

diff --git a/include/asm-x86/hpet.h b/include/asm-x86/hpet.h
index 82f1ac6..8f9a4e2 100644
--- a/include/asm-x86/hpet.h
+++ b/include/asm-x86/hpet.h
@@ -55,6 +55,8 @@
*/
#define HPET_MIN_PERIOD 100000UL

+extern int sb700_hpet_workaround;
+
/* hpet memory map physical address */
extern unsigned long hpet_address;
extern unsigned long force_hpet_address;
--
1.5.4.4



--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/