Index: linux/arch/x86_64/kernel/early-quirks.c =================================================================== --- linux.orig/arch/x86_64/kernel/early-quirks.c +++ linux/arch/x86_64/kernel/early-quirks.c @@ -15,21 +15,101 @@ #include #include #include +#include -static void __init via_bugs(void) +#ifdef CONFIG_HPET +/* None of these chipsets have enumerated HPETs, so this string is fine. */ +#define HPET_NAME "HPET 0" + +struct hpet_dev { + u32 id; + u8 bus; + u8 slot; + u8 func; + u8 reg; + u32 value; +}; + +/* List of chipset that have buggy BIOSes that do not properly enumerate + * or enable working HPETs. */ +static struct hpet_dev hpets[] __initdata = { + { PCI_VENDOR_ID_NVIDIA | (0x0050 << 16), /* NF4 */ + 0, 1, 0, 0x44, 0xfefff001 }, + { PCI_VENDOR_ID_NVIDIA | (0x0051 << 16), /* NF5 */ + 0, 1, 0, 0x44, 0xfefff001 }, + { PCI_VENDOR_ID_VIA | (PCI_DEVICE_ID_VIA_8237 << 16), + 0, 17, 0, 0x68, 0xfed00080 }, + {} +}; + +static void __init __enable_hpet(int i) { -#ifdef CONFIG_IOMMU - if ((end_pfn > MAX_DMA32_PFN || force_iommu) && - !iommu_aperture_allowed) { - printk(KERN_INFO - "Looks like a VIA chipset. Disabling IOMMU. Override with iommu=allowed\n"); - iommu_aperture_disabled = 1; + struct resource *hpet_res; + u32 addr; + u8 bus = hpets[i].bus; + u8 slot = hpets[i].slot; + u8 func = hpets[i].func; + u8 reg = hpets[i].reg; + char name[] = HPET_NAME; + + addr = read_pci_config(bus, slot, func, reg); + addr &= 0xffffff00; /* mask out control byte */ + + if (!addr) { + printk(KERN_INFO "HPET not set up by BIOS. Enabling.\n"); + + /* + * The upper 22 bits are the iomem address. The lower 8 bits + * is a control register. The extra space is reserved. + */ + addr = hpets[i].value; + write_pci_config(bus, slot, func, reg, addr); + addr &= 0xffffff00; } -#endif + + hpet_res = alloc_bootmem(sizeof(*hpet_res) + sizeof name); + if (!hpet_res) { + printk(KERN_INFO "HPET setup failed: alloc_bootmem().\n"); + return; + } + + memset(hpet_res, 0, sizeof(*hpet_res)); + hpet_res->name = (void *)&hpet_res[1]; + hpet_res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + strncpy((char *)hpet_res->name, name, sizeof name); + hpet_res->start = addr; + hpet_res->end = hpet_res->start + HPET_MMAP_SIZE - 1; + + if (request_resource(&iomem_resource, hpet_res)) { + printk(KERN_INFO "HPET quirk failed. Could not " + "reserve resources for HPET at base: %#x\n", addr); + return; + } + + hpet_address = addr; + printk(KERN_INFO "HPET quirk. Enabled hidden HPET that BIOS had " + "configured at base: %#x\n", addr); + return; } -#ifdef CONFIG_ACPI +static void __init enable_hpet(void) +{ + int i; + + if (hpet_address || nohpet) + return; + for (i = 0; hpets[i].value; ++i) { + u32 vid = read_pci_config(hpets[i].bus, hpets[i].slot, + hpets[i].func, PCI_VENDOR_ID); + if (vid == hpets[i].id) + __enable_hpet(i); + } + +} +#endif + +#ifdef CONFIG_ACPI static int __init nvidia_hpet_check(struct acpi_table_header *header) { return 0; @@ -58,10 +138,24 @@ static void __init nvidia_bugs(void) "try acpi_use_timer_override\n"); } #endif + enable_hpet(); /* RED-PEN skip them on mptables too? */ } +static void __init via_bugs(void) +{ +#ifdef CONFIG_IOMMU + if ((end_pfn > MAX_DMA32_PFN || force_iommu) && + !iommu_aperture_allowed) { + printk(KERN_INFO + "Looks like a VIA chipset. Disabling IOMMU. Override with iommu=allowed\n"); + iommu_aperture_disabled = 1; + } +#endif + enable_hpet(); +} + static void __init ati_bugs(void) { if (timer_over_8254 == 1) {