diff options
author | Daniel Schaeffer <daniel@dschaeffer.localdomain> | 2008-02-01 12:27:23 -0500 |
---|---|---|
committer | Daniel Schaeffer <daniel@dschaeffer.localdomain> | 2008-02-01 12:27:23 -0500 |
commit | 50c8843a4a66f7b3005d1d1267413a88c63e2103 (patch) | |
tree | bdb9dce43fd926a451f3b890c002cc173f8caf22 /arch/x86/kernel/k8.c | |
parent | 0f7efc5cb585cf74868de2232fc6c34b94c70d20 (diff) | |
parent | 49914084e797530d9baaf51df9eda77babc98fa8 (diff) |
Merge branch '2.6.24' into 2.6.24-imx27
Conflicts:
MAINTAINERS
Makefile
arch/arm/Kconfig
arch/arm/oprofile/Kconfig
drivers/Makefile
drivers/ata/Kconfig
drivers/ata/Makefile
drivers/char/watchdog/Kconfig
drivers/char/watchdog/Makefile
drivers/ide/Kconfig
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/mmc/card/block.c
drivers/mmc/card/sdio_uart.c
drivers/mmc/core/Makefile
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_bus.c
drivers/mmc/core/sdio_cis.c
drivers/mmc/core/sdio_io.c
drivers/mmc/core/sdio_irq.c
drivers/mmc/core/sdio_ops.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/au1xmmc.c
drivers/mmc/host/tifm_sd.c
drivers/mtd/maps/Makefile
drivers/pcmcia/Kconfig
drivers/pcmcia/Makefile
fs/exec.c
include/linux/mmc/card.h
include/linux/mmc/host.h
include/linux/mmc/sdio_func.h
include/linux/mmc/sdio_ids.h
include/linux/mod_devicetable.h
mm/hugetlb.c
scripts/mod/file2alias.c
Diffstat (limited to 'arch/x86/kernel/k8.c')
-rw-r--r-- | arch/x86/kernel/k8.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/arch/x86/kernel/k8.c b/arch/x86/kernel/k8.c new file mode 100644 index 000000000000..7377ccb21335 --- /dev/null +++ b/arch/x86/kernel/k8.c @@ -0,0 +1,123 @@ +/* + * Shared support code for AMD K8 northbridges and derivates. + * Copyright 2006 Andi Kleen, SUSE Labs. Subject to GPLv2. + */ +#include <linux/gfp.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <asm/k8.h> + +int num_k8_northbridges; +EXPORT_SYMBOL(num_k8_northbridges); + +static u32 *flush_words; + +struct pci_device_id k8_nb_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1103) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1203) }, + {} +}; +EXPORT_SYMBOL(k8_nb_ids); + +struct pci_dev **k8_northbridges; +EXPORT_SYMBOL(k8_northbridges); + +static struct pci_dev *next_k8_northbridge(struct pci_dev *dev) +{ + do { + dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); + if (!dev) + break; + } while (!pci_match_id(&k8_nb_ids[0], dev)); + return dev; +} + +int cache_k8_northbridges(void) +{ + int i; + struct pci_dev *dev; + + if (num_k8_northbridges) + return 0; + + dev = NULL; + while ((dev = next_k8_northbridge(dev)) != NULL) + num_k8_northbridges++; + + k8_northbridges = kmalloc((num_k8_northbridges + 1) * sizeof(void *), + GFP_KERNEL); + if (!k8_northbridges) + return -ENOMEM; + + if (!num_k8_northbridges) { + k8_northbridges[0] = NULL; + return 0; + } + + flush_words = kmalloc(num_k8_northbridges * sizeof(u32), GFP_KERNEL); + if (!flush_words) { + kfree(k8_northbridges); + return -ENOMEM; + } + + dev = NULL; + i = 0; + while ((dev = next_k8_northbridge(dev)) != NULL) { + k8_northbridges[i] = dev; + pci_read_config_dword(dev, 0x9c, &flush_words[i++]); + } + k8_northbridges[i] = NULL; + return 0; +} +EXPORT_SYMBOL_GPL(cache_k8_northbridges); + +/* Ignores subdevice/subvendor but as far as I can figure out + they're useless anyways */ +int __init early_is_k8_nb(u32 device) +{ + struct pci_device_id *id; + u32 vendor = device & 0xffff; + device >>= 16; + for (id = k8_nb_ids; id->vendor; id++) + if (vendor == id->vendor && device == id->device) + return 1; + return 0; +} + +void k8_flush_garts(void) +{ + int flushed, i; + unsigned long flags; + static DEFINE_SPINLOCK(gart_lock); + + /* Avoid races between AGP and IOMMU. In theory it's not needed + but I'm not sure if the hardware won't lose flush requests + when another is pending. This whole thing is so expensive anyways + that it doesn't matter to serialize more. -AK */ + spin_lock_irqsave(&gart_lock, flags); + flushed = 0; + for (i = 0; i < num_k8_northbridges; i++) { + pci_write_config_dword(k8_northbridges[i], 0x9c, + flush_words[i]|1); + flushed++; + } + for (i = 0; i < num_k8_northbridges; i++) { + u32 w; + /* Make sure the hardware actually executed the flush*/ + for (;;) { + pci_read_config_dword(k8_northbridges[i], + 0x9c, &w); + if (!(w & 1)) + break; + cpu_relax(); + } + } + spin_unlock_irqrestore(&gart_lock, flags); + if (!flushed) + printk("nothing to flush?\n"); +} +EXPORT_SYMBOL_GPL(k8_flush_garts); + |