From: Andi Kleen Fix a couple of bugs in the AGP support for AMD K8 and add preliminary support for the GART in the NForce3 chipsets (this is a "subvariant" of K8, but needs to set up some more registers in a shadow device) DaveJ was ok with me merging this directly. - Always manage all northbridges in the system, even when on a UP kernel - Add Nforce3 (Crush) support. It needs some special hacks (based on an older patch from Nvidia, with changes by me) - Move AMD 8151 init into an own function. - Do proper resource management for the aperture drivers/char/agp/amd64-agp.c | 186 ++++++++++++++++++++++++++++++++----------- include/linux/pci_ids.h | 2 2 files changed, 144 insertions(+), 44 deletions(-) diff -puN drivers/char/agp/amd64-agp.c~amd-k8-fixes drivers/char/agp/amd64-agp.c --- 25/drivers/char/agp/amd64-agp.c~amd-k8-fixes 2004-01-01 14:08:07.000000000 -0800 +++ 25-akpm/drivers/char/agp/amd64-agp.c 2004-01-01 14:08:07.000000000 -0800 @@ -16,11 +16,7 @@ #include "agp.h" /* Will need to be increased if AMD64 ever goes >8-way. */ -#ifdef CONFIG_SMP #define MAX_HAMMER_GARTS 8 -#else -#define MAX_HAMMER_GARTS 1 -#endif /* PTE bits. */ #define GPTE_VALID 1 @@ -35,9 +31,18 @@ #define INVGART (1<<0) #define GARTPTEERR (1<<1) +/* NVIDIA K8 registers */ +#define NVIDIA_X86_64_0_APBASE 0x10 +#define NVIDIA_X86_64_1_APBASE1 0x50 +#define NVIDIA_X86_64_1_APLIMIT1 0x54 +#define NVIDIA_X86_64_1_APSIZE 0xa8 +#define NVIDIA_X86_64_1_APBASE2 0xd8 +#define NVIDIA_X86_64_1_APLIMIT2 0xdc + static int nr_garts; static struct pci_dev * hammers[MAX_HAMMER_GARTS]; +static struct resource *aperture_resource; static int __initdata agp_try_unsupported; static int gart_iterator; @@ -250,7 +255,6 @@ struct agp_bridge_driver amd_8151_driver /* Some basic sanity checks for the aperture. */ static int __devinit aperture_valid(u64 aper, u32 size) { - static int not_first_call; u32 pfn, c; if (aper == 0) { printk(KERN_ERR PFX "No aperture\n"); @@ -279,12 +283,11 @@ static int __devinit aperture_valid(u64 Maybe better to use pci_assign_resource/pci_enable_device instead trusting the bridges? */ - if (!not_first_call && request_mem_region(aper, size, "aperture") < 0) { + if (!aperture_resource && + !(aperture_resource = request_mem_region(aper, size, "aperture"))) { printk(KERN_ERR PFX "Aperture conflicts with PCI mapping.\n"); return 0; } - - not_first_call = 1; return 1; } @@ -347,31 +350,124 @@ static __devinit int cache_nbs (struct p /* cache pci_devs of northbridges. */ while ((loop_dev = pci_find_device(PCI_VENDOR_ID_AMD, 0x1103, loop_dev)) != NULL) { + if (i == MAX_HAMMER_GARTS) { + printk(KERN_ERR PFX "Too many northbridges for AGP\n"); + return -1; + } if (fix_northbridge(loop_dev, pdev, cap_ptr) < 0) { - printk(KERN_INFO PFX "No usable aperture found.\n"); + printk(KERN_ERR PFX "No usable aperture found.\n"); #ifdef __x86_64__ /* should port this to i386 */ - printk(KERN_INFO PFX "Consider rebooting with iommu=memaper=2 to get a good aperture.\n"); + printk(KERN_ERR PFX "Consider rebooting with iommu=memaper=2 to get a good aperture.\n"); #endif return -1; } hammers[i++] = loop_dev; + } nr_garts = i; - if (i == MAX_HAMMER_GARTS) { - printk(KERN_INFO PFX "Too many northbridges for AGP\n"); - return -1; + return i == 0 ? -1 : 0; +} + +/* Handle AMD 8151 quirks */ +static void __devinit amd8151_init(struct pci_dev *pdev, struct agp_bridge_data *bridge) + +{ + char *revstring; + u8 rev_id; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); + switch (rev_id) { + case 0x01: revstring="A0"; break; + case 0x02: revstring="A1"; break; + case 0x11: revstring="B0"; break; + case 0x12: revstring="B1"; break; + case 0x13: revstring="B2"; break; + default: revstring="??"; break; } + + printk (KERN_INFO PFX "Detected AMD 8151 AGP Bridge rev %s\n", revstring); + + /* + * Work around errata. + * Chips before B2 stepping incorrectly reporting v3.5 + */ + if (rev_id < 0x13) { + printk (KERN_INFO PFX "Correcting AGP revision (reports 3.5, is really 3.0)\n"); + bridge->major_version = 3; + bridge->minor_version = 0; } - return i == 0 ? -1 : 0; +} + +static struct aper_size_info_32 nforce3_sizes[5] = +{ + {512, 131072, 7, 0x00000000 }, + {256, 65536, 6, 0x00000008 }, + {128, 32768, 5, 0x0000000C }, + {64, 16384, 4, 0x0000000E }, + {32, 8192, 3, 0x0000000F } +}; + +/* Handle shadow device of the Nvidia NForce3 */ +/* CHECK-ME original 2.4 version set up some IORRs. Check if that is needed. */ +static int __devinit nforce3_agp_init(struct pci_dev *pdev) +{ + u32 tmp, apbase, apbar, aplimit; + struct pci_dev *dev1; + int i; + unsigned size = amd64_fetch_size(); + + printk(KERN_INFO PFX "Setting up Nforce3 AGP.\n"); + + dev1 = pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(11, 0)); + if (dev1 == NULL) { + printk(KERN_INFO PFX "agpgart: Detected an NVIDIA " + "nForce3 chipset, but could not find " + "the secondary device.\n"); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(nforce3_sizes); i++) + if (nforce3_sizes[i].size == size) + break; + + if (i == ARRAY_SIZE(nforce3_sizes)) { + printk(KERN_INFO PFX "No NForce3 size found for %d\n", size); + return -ENODEV; + } + + pci_read_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, &tmp); + tmp &= ~(0xf); + tmp |= nforce3_sizes[i].size_value; + pci_write_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, tmp); + + /* shadow x86-64 registers into NVIDIA registers */ + pci_read_config_dword (hammers[0], AMD64_GARTAPERTUREBASE, &apbase); + + /* if x86-64 aperture base is beyond 4G, exit here */ + if ( (apbase & 0x7fff) >> (32 - 25) ) + return -ENODEV; + + apbase = (apbase & 0x7fff) << 25; + + pci_read_config_dword(pdev, NVIDIA_X86_64_0_APBASE, &apbar); + apbar &= ~PCI_BASE_ADDRESS_MEM_MASK; + apbar |= apbase; + pci_write_config_dword(pdev, NVIDIA_X86_64_0_APBASE, apbar); + + aplimit = apbase + (size * 1024 * 1024) - 1; + pci_write_config_dword(dev1, NVIDIA_X86_64_1_APBASE1, apbase); + pci_write_config_dword(dev1, NVIDIA_X86_64_1_APLIMIT1, aplimit); + pci_write_config_dword(dev1, NVIDIA_X86_64_1_APBASE2, apbase); + pci_write_config_dword(dev1, NVIDIA_X86_64_1_APLIMIT2, aplimit); + + return 0; } static int __devinit agp_amd64_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct agp_bridge_data *bridge; - u8 rev_id; u8 cap_ptr; - char *revstring=NULL; cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); if (!cap_ptr) @@ -385,32 +481,7 @@ static int __devinit agp_amd64_probe(str if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == PCI_DEVICE_ID_AMD_8151_0) { - - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); - switch (rev_id) { - case 0x01: revstring="A0"; - break; - case 0x02: revstring="A1"; - break; - case 0x11: revstring="B0"; - break; - case 0x12: revstring="B1"; - break; - case 0x13: revstring="B2"; - break; - default: revstring="??"; - break; - } - printk (KERN_INFO PFX "Detected AMD 8151 AGP Bridge rev %s\n", revstring); - /* - * Work around errata. - * Chips before B2 stepping incorrectly reporting v3.5 - */ - if (rev_id < 0x13) { - printk (KERN_INFO PFX "Correcting AGP revision (reports 3.5, is really 3.0)\n"); - bridge->major_version = 3; - bridge->minor_version = 0; - } + amd8151_init(pdev, bridge); } else { printk(KERN_INFO PFX "Detected AGP bridge %x\n", pdev->devfn); @@ -428,6 +499,14 @@ static int __devinit agp_amd64_probe(str return -ENODEV; } + if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) { + int ret = nforce3_agp_init(pdev); + if (ret) { + agp_put_bridge(bridge); + return ret; + } + } + pci_set_drvdata(pdev, bridge); return agp_add_bridge(bridge); } @@ -472,8 +551,25 @@ static struct pci_device_id agp_amd64_pc { .class = (PCI_CLASS_BRIDGE_HOST << 8), .class_mask = ~0, - .vendor = PCI_VENDOR_ID_SI, - .device = PCI_DEVICE_ID_SI_755, + .vendor = PCI_VENDOR_ID_VIA, + .device = PCI_DEVICE_ID_VIA_8380_0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + /* NForce3 */ + { + .class = (PCI_CLASS_BRIDGE_HOST << 8), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .class = (PCI_CLASS_BRIDGE_HOST << 8), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE3S, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, @@ -530,6 +626,8 @@ int __init agp_amd64_init(void) static void __exit agp_amd64_cleanup(void) { + if (aperture_resource) + release_resource(aperture_resource); pci_unregister_driver(&agp_amd64_pci_driver); } diff -puN include/linux/pci_ids.h~amd-k8-fixes include/linux/pci_ids.h --- 25/include/linux/pci_ids.h~amd-k8-fixes 2004-01-01 14:08:07.000000000 -0800 +++ 25-akpm/include/linux/pci_ids.h 2004-01-01 14:08:07.000000000 -0800 @@ -1034,6 +1034,8 @@ #define PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE 0x0085 #define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA 0x008e #define PCI_DEVICE_ID_NVIDIA_ITNT2 0x00A0 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3 0x00d1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S 0x00e1 #define PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE 0x00d5 #define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA 0x00e3 #define PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE 0x00e5 _