linux/arch/x86/kernel/early-quirks.c
<<
>>
Prefs
   1/* Various workarounds for chipset bugs.
   2   This code runs very early and can't use the regular PCI subsystem
   3   The entries are keyed to PCI bridges which usually identify chipsets
   4   uniquely.
   5   This is only for whole classes of chipsets with specific problems which
   6   need early invasive action (e.g. before the timers are initialized).
   7   Most PCI device specific workarounds can be done later and should be
   8   in standard PCI quirks
   9   Mainboard specific bugs should be handled by DMI entries.
  10   CPU specific bugs in setup.c */
  11
  12#include <linux/pci.h>
  13#include <linux/acpi.h>
  14#include <linux/pci_ids.h>
  15#include <asm/pci-direct.h>
  16#include <asm/dma.h>
  17#include <asm/io_apic.h>
  18#include <asm/apic.h>
  19#include <asm/iommu.h>
  20
  21static void __init fix_hypertransport_config(int num, int slot, int func)
  22{
  23        u32 htcfg;
  24        /*
  25         * we found a hypertransport bus
  26         * make sure that we are broadcasting
  27         * interrupts to all cpus on the ht bus
  28         * if we're using extended apic ids
  29         */
  30        htcfg = read_pci_config(num, slot, func, 0x68);
  31        if (htcfg & (1 << 18)) {
  32                printk(KERN_INFO "Detected use of extended apic ids "
  33                                 "on hypertransport bus\n");
  34                if ((htcfg & (1 << 17)) == 0) {
  35                        printk(KERN_INFO "Enabling hypertransport extended "
  36                                         "apic interrupt broadcast\n");
  37                        printk(KERN_INFO "Note this is a bios bug, "
  38                                         "please contact your hw vendor\n");
  39                        htcfg |= (1 << 17);
  40                        write_pci_config(num, slot, func, 0x68, htcfg);
  41                }
  42        }
  43
  44
  45}
  46
  47static void __init via_bugs(int  num, int slot, int func)
  48{
  49#ifdef CONFIG_GART_IOMMU
  50        if ((max_pfn > MAX_DMA32_PFN ||  force_iommu) &&
  51            !gart_iommu_aperture_allowed) {
  52                printk(KERN_INFO
  53                       "Looks like a VIA chipset. Disabling IOMMU."
  54                       " Override with iommu=allowed\n");
  55                gart_iommu_aperture_disabled = 1;
  56        }
  57#endif
  58}
  59
  60#ifdef CONFIG_ACPI
  61#ifdef CONFIG_X86_IO_APIC
  62
  63static int __init nvidia_hpet_check(struct acpi_table_header *header)
  64{
  65        return 0;
  66}
  67#endif /* CONFIG_X86_IO_APIC */
  68#endif /* CONFIG_ACPI */
  69
  70static void __init nvidia_bugs(int num, int slot, int func)
  71{
  72#ifdef CONFIG_ACPI
  73#ifdef CONFIG_X86_IO_APIC
  74        /*
  75         * All timer overrides on Nvidia are
  76         * wrong unless HPET is enabled.
  77         * Unfortunately that's not true on many Asus boards.
  78         * We don't know yet how to detect this automatically, but
  79         * at least allow a command line override.
  80         */
  81        if (acpi_use_timer_override)
  82                return;
  83
  84        if (acpi_table_parse(ACPI_SIG_HPET, nvidia_hpet_check)) {
  85                acpi_skip_timer_override = 1;
  86                printk(KERN_INFO "Nvidia board "
  87                       "detected. Ignoring ACPI "
  88                       "timer override.\n");
  89                printk(KERN_INFO "If you got timer trouble "
  90                        "try acpi_use_timer_override\n");
  91        }
  92#endif
  93#endif
  94        /* RED-PEN skip them on mptables too? */
  95
  96}
  97
  98#if defined(CONFIG_ACPI) && defined(CONFIG_X86_IO_APIC)
  99static u32 __init ati_ixp4x0_rev(int num, int slot, int func)
 100{
 101        u32 d;
 102        u8  b;
 103
 104        b = read_pci_config_byte(num, slot, func, 0xac);
 105        b &= ~(1<<5);
 106        write_pci_config_byte(num, slot, func, 0xac, b);
 107
 108        d = read_pci_config(num, slot, func, 0x70);
 109        d |= 1<<8;
 110        write_pci_config(num, slot, func, 0x70, d);
 111
 112        d = read_pci_config(num, slot, func, 0x8);
 113        d &= 0xff;
 114        return d;
 115}
 116
 117static void __init ati_bugs(int num, int slot, int func)
 118{
 119        u32 d;
 120        u8  b;
 121
 122        if (acpi_use_timer_override)
 123                return;
 124
 125        d = ati_ixp4x0_rev(num, slot, func);
 126        if (d  < 0x82)
 127                acpi_skip_timer_override = 1;
 128        else {
 129                /* check for IRQ0 interrupt swap */
 130                outb(0x72, 0xcd6); b = inb(0xcd7);
 131                if (!(b & 0x2))
 132                        acpi_skip_timer_override = 1;
 133        }
 134
 135        if (acpi_skip_timer_override) {
 136                printk(KERN_INFO "SB4X0 revision 0x%x\n", d);
 137                printk(KERN_INFO "Ignoring ACPI timer override.\n");
 138                printk(KERN_INFO "If you got timer trouble "
 139                       "try acpi_use_timer_override\n");
 140        }
 141}
 142
 143static u32 __init ati_sbx00_rev(int num, int slot, int func)
 144{
 145        u32 old, d;
 146
 147        d = read_pci_config(num, slot, func, 0x70);
 148        old = d;
 149        d &= ~(1<<8);
 150        write_pci_config(num, slot, func, 0x70, d);
 151        d = read_pci_config(num, slot, func, 0x8);
 152        d &= 0xff;
 153        write_pci_config(num, slot, func, 0x70, old);
 154
 155        return d;
 156}
 157
 158static void __init ati_bugs_contd(int num, int slot, int func)
 159{
 160        u32 d, rev;
 161
 162        if (acpi_use_timer_override)
 163                return;
 164
 165        rev = ati_sbx00_rev(num, slot, func);
 166        if (rev > 0x13)
 167                return;
 168
 169        /* check for IRQ0 interrupt swap */
 170        d = read_pci_config(num, slot, func, 0x64);
 171        if (!(d & (1<<14)))
 172                acpi_skip_timer_override = 1;
 173
 174        if (acpi_skip_timer_override) {
 175                printk(KERN_INFO "SB600 revision 0x%x\n", rev);
 176                printk(KERN_INFO "Ignoring ACPI timer override.\n");
 177                printk(KERN_INFO "If you got timer trouble "
 178                       "try acpi_use_timer_override\n");
 179        }
 180}
 181#else
 182static void __init ati_bugs(int num, int slot, int func)
 183{
 184}
 185
 186static void __init ati_bugs_contd(int num, int slot, int func)
 187{
 188}
 189#endif
 190
 191#define QFLAG_APPLY_ONCE        0x1
 192#define QFLAG_APPLIED           0x2
 193#define QFLAG_DONE              (QFLAG_APPLY_ONCE|QFLAG_APPLIED)
 194struct chipset {
 195        u32 vendor;
 196        u32 device;
 197        u32 class;
 198        u32 class_mask;
 199        u32 flags;
 200        void (*f)(int num, int slot, int func);
 201};
 202
 203static struct chipset early_qrk[] __initdata = {
 204        { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
 205          PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, nvidia_bugs },
 206        { PCI_VENDOR_ID_VIA, PCI_ANY_ID,
 207          PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, via_bugs },
 208        { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB,
 209          PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, fix_hypertransport_config },
 210        { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS,
 211          PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs },
 212        { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS,
 213          PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs_contd },
 214        {}
 215};
 216
 217/**
 218 * check_dev_quirk - apply early quirks to a given PCI device
 219 * @num: bus number
 220 * @slot: slot number
 221 * @func: PCI function
 222 *
 223 * Check the vendor & device ID against the early quirks table.
 224 *
 225 * If the device is single function, let early_quirks() know so we don't
 226 * poke at this device again.
 227 */
 228static int __init check_dev_quirk(int num, int slot, int func)
 229{
 230        u16 class;
 231        u16 vendor;
 232        u16 device;
 233        u8 type;
 234        int i;
 235
 236        class = read_pci_config_16(num, slot, func, PCI_CLASS_DEVICE);
 237
 238        if (class == 0xffff)
 239                return -1; /* no class, treat as single function */
 240
 241        vendor = read_pci_config_16(num, slot, func, PCI_VENDOR_ID);
 242
 243        device = read_pci_config_16(num, slot, func, PCI_DEVICE_ID);
 244
 245        for (i = 0; early_qrk[i].f != NULL; i++) {
 246                if (((early_qrk[i].vendor == PCI_ANY_ID) ||
 247                        (early_qrk[i].vendor == vendor)) &&
 248                        ((early_qrk[i].device == PCI_ANY_ID) ||
 249                        (early_qrk[i].device == device)) &&
 250                        (!((early_qrk[i].class ^ class) &
 251                            early_qrk[i].class_mask))) {
 252                                if ((early_qrk[i].flags &
 253                                     QFLAG_DONE) != QFLAG_DONE)
 254                                        early_qrk[i].f(num, slot, func);
 255                                early_qrk[i].flags |= QFLAG_APPLIED;
 256                        }
 257        }
 258
 259        type = read_pci_config_byte(num, slot, func,
 260                                    PCI_HEADER_TYPE);
 261        if (!(type & 0x80))
 262                return -1;
 263
 264        return 0;
 265}
 266
 267void __init early_quirks(void)
 268{
 269        int num, slot, func;
 270
 271        if (!early_pci_allowed())
 272                return;
 273
 274        /* Poor man's PCI discovery */
 275        for (num = 0; num < 32; num++)
 276                for (slot = 0; slot < 32; slot++)
 277                        for (func = 0; func < 8; func++) {
 278                                /* Only probe function 0 on single fn devices */
 279                                if (check_dev_quirk(num, slot, func))
 280                                        break;
 281                        }
 282}
 283
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.