124 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			124 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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);
 | 
						|
 |