amd_nb.c 3.8 KB
Newer Older
1 2 3 4 5
/*
 * Shared support code for AMD K8 northbridges and derivates.
 * Copyright 2006 Andi Kleen, SUSE Labs. Subject to GPLv2.
 */
#include <linux/types.h>
6
#include <linux/slab.h>
7 8 9 10
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/spinlock.h>
11
#include <asm/amd_nb.h>
12 13 14

static u32 *flush_words;

15
struct pci_device_id amd_nb_misc_ids[] = {
16 17
	{ 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) },
18
	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_MISC) },
19 20
	{}
};
21
EXPORT_SYMBOL(amd_nb_misc_ids);
22

23 24
struct amd_northbridge_info amd_northbridges;
EXPORT_SYMBOL(amd_northbridges);
25

26 27
static struct pci_dev *next_northbridge(struct pci_dev *dev,
					struct pci_device_id *ids)
28 29 30 31 32
{
	do {
		dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
		if (!dev)
			break;
33
	} while (!pci_match_id(ids, dev));
34 35 36
	return dev;
}

37
int amd_cache_northbridges(void)
38
{
39 40 41
	int i = 0;
	struct amd_northbridge *nb;
	struct pci_dev *misc;
42

43
	if (amd_nb_num())
44 45
		return 0;

46 47 48
	misc = NULL;
	while ((misc = next_northbridge(misc, amd_nb_misc_ids)) != NULL)
		i++;
49

50 51
	if (i == 0)
		return 0;
52

53 54
	nb = kzalloc(i * sizeof(struct amd_northbridge), GFP_KERNEL);
	if (!nb)
55 56
		return -ENOMEM;

57 58
	amd_northbridges.nb = nb;
	amd_northbridges.num = i;
59

60 61 62 63 64 65 66 67 68 69
	misc = NULL;
	for (i = 0; i != amd_nb_num(); i++) {
		node_to_amd_nb(i)->misc = misc =
			next_northbridge(misc, amd_nb_misc_ids);
        }

	/* some CPU families (e.g. family 0x11) do not support GART */
	if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 ||
	    boot_cpu_data.x86 == 0x15)
		amd_northbridges.flags |= AMD_NB_GART;
70 71 72

	return 0;
}
73
EXPORT_SYMBOL_GPL(amd_cache_northbridges);
74 75 76

/* Ignores subdevice/subvendor but as far as I can figure out
   they're useless anyways */
77
int __init early_is_amd_nb(u32 device)
78 79 80 81
{
	struct pci_device_id *id;
	u32 vendor = device & 0xffff;
	device >>= 16;
82
	for (id = amd_nb_misc_ids; id->vendor; id++)
83 84 85 86 87
		if (vendor == id->vendor && device == id->device)
			return 1;
	return 0;
}

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
int amd_cache_gart(void)
{
       int i;

       if (!amd_nb_has_feature(AMD_NB_GART))
               return 0;

       flush_words = kmalloc(amd_nb_num() * sizeof(u32), GFP_KERNEL);
       if (!flush_words) {
               amd_northbridges.flags &= ~AMD_NB_GART;
               return -ENOMEM;
       }

       for (i = 0; i != amd_nb_num(); i++)
               pci_read_config_dword(node_to_amd_nb(i)->misc, 0x9c,
                                     &flush_words[i]);

       return 0;
}

108
void amd_flush_garts(void)
109 110 111 112 113
{
	int flushed, i;
	unsigned long flags;
	static DEFINE_SPINLOCK(gart_lock);

114
	if (!amd_nb_has_feature(AMD_NB_GART))
115 116
		return;

117 118 119 120 121 122
	/* 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;
123 124 125
	for (i = 0; i < amd_nb_num(); i++) {
		pci_write_config_dword(node_to_amd_nb(i)->misc, 0x9c,
				       flush_words[i] | 1);
126 127
		flushed++;
	}
128
	for (i = 0; i < amd_nb_num(); i++) {
129 130 131
		u32 w;
		/* Make sure the hardware actually executed the flush*/
		for (;;) {
132
			pci_read_config_dword(node_to_amd_nb(i)->misc,
133 134 135 136 137 138 139 140 141 142
					      0x9c, &w);
			if (!(w & 1))
				break;
			cpu_relax();
		}
	}
	spin_unlock_irqrestore(&gart_lock, flags);
	if (!flushed)
		printk("nothing to flush?\n");
}
143
EXPORT_SYMBOL_GPL(amd_flush_garts);
144

145
static __init int init_amd_nbs(void)
146 147 148
{
	int err = 0;

149
	err = amd_cache_northbridges();
150 151

	if (err < 0)
152
		printk(KERN_NOTICE "AMD NB: Cannot enumerate AMD northbridges.\n");
153

154 155 156 157
	if (amd_cache_gart() < 0)
		printk(KERN_NOTICE "AMD NB: Cannot initialize GART flush words, "
		       "GART support disabled.\n");

158 159 160 161
	return err;
}

/* This has to go after the PCI subsystem */
162
fs_initcall(init_amd_nbs);