Commit cad8b410 authored by Dave Jones's avatar Dave Jones Committed by Dave Jones

[AGPGART] Numerous AMD64 gart driver cleanups.

From Andi Kleen.

- Fix the help text for the 8151 driver
- Fix the dependencies (must be compiled in when the IOMMU is in)
- Add __setup options for when the AGP driver is compiled in:
agp=off  agp=try_unsupported
Currently only supported for the K8 driver, the other drivers
would need fixes in their module init functions too.
- Add try_unsupported support for the K8 driver.
- Add some aperture sanity checking to the K8 driver.
There are unfortunately still BIOS around that get it wrong.
- Also try to read the aperture from the AGP bridge if it is bogus
in the Northbridge. Windows only looks into the bridge and some BIOS only
put the aperture there.
[These two changes are only useful for 32bit kernels. The 64bit kernel
checks this in aperture.c anyways, and fixes it. The 32bit kernel
cannot fix a complety broken aperture currently, but at least it will
not crash now]
- Clean up handling for multiple northbridges. The paths are the same
now for as for a single NB.
- Some other minor cleanups. 
parent 2de02a8e
...@@ -63,15 +63,19 @@ config AGP_AMD ...@@ -63,15 +63,19 @@ config AGP_AMD
You should say Y here if you use XFree86 3.3.6 or 4.x and want to You should say Y here if you use XFree86 3.3.6 or 4.x and want to
use GLX or DRI. If unsure, say N. use GLX or DRI. If unsure, say N.
# RED-PEN this option is misnamed, it's not 8151 specific
config AGP_AMD_8151 config AGP_AMD_8151
tristate "AMD Opteron/Athlon64 on-CPU GART support" tristate "AMD Opteron/Athlon64 on-CPU GART support" if !GART_IOMMU
depends on AGP && X86 depends on AGP && X86
default GART_IOMMU default y if GART_IOMMU
help help
This option gives you AGP support for the GLX component of This option gives you AGP support for the GLX component of
XFree86 4.x using the on-CPU AGP bridge of the AMD Athlon64/Opteron CPUs. XFree86 4.x using the on-CPU northbridge of the AMD Athlon64/Opteron CPUs.
You still need an external AGP bridge like the AMD 8151, VIA
K8T400M, SiS755. It may also support other AGP bridges when loaded
with agp_try_unsupported=1.
You should say Y here if you use XFree86 3.3.6 or 4.x and want to You should say Y here if you use XFree86 3.3.6 or 4.x and want to
use GLX or DRI. If unsure, say N use GLX or DRI. If unsure, say Y
config AGP_INTEL config AGP_INTEL
tristate "Intel 440LX/BX/GX, I8xx and E7x05 chipset support" tristate "Intel 440LX/BX/GX, I8xx and E7x05 chipset support"
......
...@@ -406,6 +406,9 @@ void global_cache_flush(void); ...@@ -406,6 +406,9 @@ void global_cache_flush(void);
void get_agp_version(struct agp_bridge_data *bridge); void get_agp_version(struct agp_bridge_data *bridge);
unsigned long agp_generic_mask_memory(unsigned long addr, int type); unsigned long agp_generic_mask_memory(unsigned long addr, int type);
extern int agp_off;
extern int agp_try_unsupported_boot;
/* Standard agp registers */ /* Standard agp registers */
#define AGPSTAT 0x4 #define AGPSTAT 0x4
#define AGPCMD 0x8 #define AGPCMD 0x8
......
...@@ -8,12 +8,6 @@ ...@@ -8,12 +8,6 @@
* work is done in the northbridge(s). * work is done in the northbridge(s).
*/ */
/*
* On x86-64 the AGP driver needs to be initialized early by the IOMMU
* code. When you use this driver as a template for a new K8 AGP bridge
* driver don't forget to change arch/x86_64/kernel/pci-gart.c too -AK.
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -21,7 +15,11 @@ ...@@ -21,7 +15,11 @@
#include "agp.h" #include "agp.h"
/* Will need to be increased if hammer ever goes >8-way. */ /* Will need to be increased if hammer ever goes >8-way. */
#ifdef CONFIG_SMP
#define MAX_HAMMER_GARTS 8 #define MAX_HAMMER_GARTS 8
#else
#define MAX_HAMMER_GARTS 1
#endif
/* PTE bits. */ /* PTE bits. */
#define GPTE_VALID 1 #define GPTE_VALID 1
...@@ -39,6 +37,8 @@ ...@@ -39,6 +37,8 @@
static int nr_garts; static int nr_garts;
static struct pci_dev * hammers[MAX_HAMMER_GARTS]; static struct pci_dev * hammers[MAX_HAMMER_GARTS];
static int __initdata agp_try_unsupported;
static int gart_iterator; static int gart_iterator;
#define for_each_nb() for(gart_iterator=0;gart_iterator<nr_garts;gart_iterator++) #define for_each_nb() for(gart_iterator=0;gart_iterator<nr_garts;gart_iterator++)
...@@ -246,24 +246,123 @@ struct agp_bridge_driver amd_8151_driver = { ...@@ -246,24 +246,123 @@ struct agp_bridge_driver amd_8151_driver = {
.agp_destroy_page = agp_generic_destroy_page, .agp_destroy_page = agp_generic_destroy_page,
}; };
/* Some basic sanity checks for the aperture. */
static int __init aperture_valid(u64 aper, u32 size)
{
static int not_first_call;
u32 pfn, c;
if (aper == 0) {
printk(KERN_ERR "No aperture\n");
return 0;
}
if (size < 32*1024*1024) {
printk(KERN_ERR "Aperture too small (%d MB)\n", size>>20);
return 0;
}
if (aper + size > 0xffffffff) {
printk(KERN_ERR "Aperture out of bounds\n");
return 0;
}
pfn = aper >> PAGE_SHIFT;
for (c = 0; c < size/PAGE_SIZE; c++) {
if (!pfn_valid(pfn + c))
break;
if (!PageReserved(pfn_to_page(pfn + c))) {
printk(KERN_ERR "Aperture pointing to RAM\n");
return 0;
}
}
/* Request the Aperture. This catches cases when someone else
already put a mapping in there - happens with some very broken BIOS
#ifdef CONFIG_SMP Maybe better to use pci_assign_resource/pci_enable_device instead trusting
static int cache_nbs (void) the bridges? */
if (!not_first_call && request_mem_region(aper, size, "aperture") < 0) {
printk(KERN_ERR "Aperture conflicts with PCI mapping.\n");
return 0;
}
not_first_call = 1;
return 1;
}
/*
* W*s centric BIOS sometimes only set up the aperture in the AGP
* bridge, not the northbridge. On AMD64 this is handled early
* in aperture.c, but when GART_IOMMU is not enabled or we run
* on a 32bit kernel this needs to be redone.
* Unfortunately it is impossible to fix the aperture here because it's too late
* to allocate that much memory. But at least error out cleanly instead of
* crashing.
*/
static __init int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp,
u16 cap)
{
u32 aper_low, aper_hi;
u64 aper, nb_aper;
int order = 0;
u32 nb_order, nb_base;
u16 apsize;
pci_read_config_dword(nb, 0x90, &nb_order);
nb_order = (nb_order >> 1) & 7;
pci_read_config_dword(nb, 0x94, &nb_base);
nb_aper = nb_base << 25;
if (aperture_valid(nb_aper, (32*1024*1024)<<nb_order)) {
return 0;
}
/* Northbridge seems to contain crap. Try the AGP bridge. */
pci_read_config_word(agp, cap+0x14, &apsize);
if (apsize == 0xffff)
return -1;
apsize &= 0xfff;
/* Some BIOS use weird encodings not in the AGPv3 table. */
if (apsize & 0xff)
apsize |= 0xf00;
order = 7 - hweight16(apsize);
pci_read_config_dword(agp, 0x10, &aper_low);
pci_read_config_dword(agp, 0x14, &aper_hi);
aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
printk(KERN_INFO "Aperture from AGP @ %Lx size %u MB\n", aper, 32 << order);
if (order < 0 || !aperture_valid(aper, (32*1024*1024)<<order))
return -1;
pci_write_config_dword(nb, 0x90, order << 1);
pci_write_config_dword(nb, 0x94, aper >> 25);
return 0;
}
static __init int cache_nbs (struct pci_dev *pdev, u32 cap_ptr)
{ {
struct pci_dev *loop_dev = NULL; struct pci_dev *loop_dev = NULL;
int i = 0; int i = 0;
/* cache pci_devs of northbridges. */ /* cache pci_devs of northbridges. */
while ((loop_dev = pci_find_device(PCI_VENDOR_ID_AMD, 0x1103, loop_dev)) != NULL) { while ((loop_dev = pci_find_device(PCI_VENDOR_ID_AMD, 0x1103, loop_dev))
!= NULL) {
if (fix_northbridge(loop_dev, pdev, cap_ptr) < 0) {
printk("No usable aperture found.\n");
#ifdef __x86_64__
/* should port this to i386 */
printk("Consider rebooting with iommu=memaper=2 to get a good aperture.\n");
#endif
return -1;
}
hammers[i++] = loop_dev; hammers[i++] = loop_dev;
nr_garts = i; nr_garts = i;
if (i == MAX_HAMMER_GARTS) if (i == MAX_HAMMER_GARTS) {
printk(KERN_INFO "Too many northbridges for AGP\n");
return -1; return -1;
} }
return 0; }
return i == 0 ? -1 : 0;
} }
#endif
static int __init agp_amdk8_probe(struct pci_dev *pdev, static int __init agp_amdk8_probe(struct pci_dev *pdev,
const struct pci_device_id *ent) const struct pci_device_id *ent)
...@@ -277,7 +376,7 @@ static int __init agp_amdk8_probe(struct pci_dev *pdev, ...@@ -277,7 +376,7 @@ static int __init agp_amdk8_probe(struct pci_dev *pdev,
if (!cap_ptr) if (!cap_ptr)
return -ENODEV; return -ENODEV;
printk(KERN_INFO PFX "Detected Opteron/Athlon64 on-CPU GART\n"); /* Could check for AGPv3 here */
bridge = agp_alloc_bridge(); bridge = agp_alloc_bridge();
if (!bridge) if (!bridge)
...@@ -311,6 +410,9 @@ static int __init agp_amdk8_probe(struct pci_dev *pdev, ...@@ -311,6 +410,9 @@ static int __init agp_amdk8_probe(struct pci_dev *pdev,
bridge->major_version = 3; bridge->major_version = 3;
bridge->minor_version = 0; bridge->minor_version = 0;
} }
} else {
printk(KERN_INFO PFX "Detected AGP bridge %x\n",
pdev->devfn);
} }
bridge->driver = &amd_8151_driver; bridge->driver = &amd_8151_driver;
...@@ -320,22 +422,10 @@ static int __init agp_amdk8_probe(struct pci_dev *pdev, ...@@ -320,22 +422,10 @@ static int __init agp_amdk8_probe(struct pci_dev *pdev,
/* Fill in the mode register */ /* Fill in the mode register */
pci_read_config_dword(pdev, bridge->capndx+PCI_AGP_STATUS, &bridge->mode); pci_read_config_dword(pdev, bridge->capndx+PCI_AGP_STATUS, &bridge->mode);
#ifdef CONFIG_SMP if (cache_nbs(pdev, cap_ptr) == -1) {
if (cache_nbs() == -1) {
agp_put_bridge(bridge); agp_put_bridge(bridge);
return -ENOMEM; return -ENODEV;
}
#else
{
struct pci_dev *loop_dev = NULL;
while ((loop_dev = pci_find_device(PCI_VENDOR_ID_AMD, 0x1103, loop_dev)) != NULL) {
/* For UP, we only care about the first GART. */
hammers[0] = loop_dev;
nr_garts = 1;
break;
}
} }
#endif
pci_set_drvdata(pdev, bridge); pci_set_drvdata(pdev, bridge);
return agp_add_bridge(bridge); return agp_add_bridge(bridge);
...@@ -345,6 +435,8 @@ static void __devexit agp_amdk8_remove(struct pci_dev *pdev) ...@@ -345,6 +435,8 @@ static void __devexit agp_amdk8_remove(struct pci_dev *pdev)
{ {
struct agp_bridge_data *bridge = pci_get_drvdata(pdev); struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
release_mem_region(virt_to_phys(bridge->gatt_table_real),
x86_64_aperture_sizes[bridge->aperture_size_idx].size);
agp_remove_bridge(bridge); agp_remove_bridge(bridge);
agp_put_bridge(bridge); agp_put_bridge(bridge);
} }
...@@ -396,10 +488,43 @@ static struct pci_driver agp_amdk8_pci_driver = { ...@@ -396,10 +488,43 @@ static struct pci_driver agp_amdk8_pci_driver = {
.remove = agp_amdk8_remove, .remove = agp_amdk8_remove,
}; };
/* Not static due to IOMMU code calling it early. */ /* Not static due to IOMMU code calling it early. */
int __init agp_amdk8_init(void) int __init agp_amdk8_init(void)
{ {
return pci_module_init(&agp_amdk8_pci_driver); int err = 0;
if (agp_off)
return -EINVAL;
if (pci_module_init(&agp_amdk8_pci_driver) == 0) {
struct pci_dev *dev;
if (!agp_try_unsupported && !agp_try_unsupported_boot) {
printk(KERN_INFO "No supported AGP bridge found.\n");
#ifdef MODULE
printk(KERN_INFO "You can try agp_try_unsupported=1\n");
#else
printk(KERN_INFO "You can boot with agp=try_unsupported\n");
#endif
return -ENODEV;
}
/* First check that we have at least one K8 NB */
if (!pci_find_device(PCI_VENDOR_ID_AMD, 0x1103, NULL))
return -ENODEV;
/* Look for any AGP bridge */
dev = NULL;
err = -ENODEV;
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev))) {
if (!pci_find_capability(dev, PCI_CAP_ID_AGP))
continue;
/* Only one bridge supported right now */
if (agp_amdk8_probe(dev, NULL) == 0) {
err = 0;
break;
}
}
}
return err;
} }
static void __exit agp_amdk8_cleanup(void) static void __exit agp_amdk8_cleanup(void)
...@@ -414,6 +539,6 @@ module_init(agp_amdk8_init); ...@@ -414,6 +539,6 @@ module_init(agp_amdk8_init);
module_exit(agp_amdk8_cleanup); module_exit(agp_amdk8_cleanup);
#endif #endif
MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>"); MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>, Andi Kleen");
MODULE_PARM(agp_try_unsupported, "1i");
MODULE_LICENSE("GPL and additional rights"); MODULE_LICENSE("GPL and additional rights");
...@@ -301,9 +301,14 @@ void agp_remove_bridge(struct agp_bridge_data *bridge) ...@@ -301,9 +301,14 @@ void agp_remove_bridge(struct agp_bridge_data *bridge)
} }
EXPORT_SYMBOL_GPL(agp_remove_bridge); EXPORT_SYMBOL_GPL(agp_remove_bridge);
int agp_off;
int agp_try_unsupported_boot;
EXPORT_SYMBOL(agp_off);
EXPORT_SYMBOL(agp_try_unsupported_boot);
static int __init agp_init(void) static int __init agp_init(void)
{ {
if (!agp_off)
printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Dave Jones\n", printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Dave Jones\n",
AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR); AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR);
return 0; return 0;
...@@ -313,6 +318,15 @@ void __exit agp_exit(void) ...@@ -313,6 +318,15 @@ void __exit agp_exit(void)
{ {
} }
static __init int agp_setup(char *s)
{
if (!strcmp(s,"off"))
agp_off = 1;
if (!strcmp(s,"try_unsupported"))
agp_try_unsupported_boot = 1;
return 1;
}
__setup("agp=", agp_setup);
MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>"); MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>");
MODULE_DESCRIPTION("AGP GART driver"); MODULE_DESCRIPTION("AGP GART driver");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment