123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- /*
- * 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, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
- {}
- };
- 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);
|