2005-04-17 02:20:36 +04:00
/*
* Firmware replacement code .
*
* Work around broken BIOSes that don ' t set an aperture or only set the
* aperture in the AGP bridge .
* If all fails map the aperture over some low memory . This is cheaper than
* doing bounce buffering . The memory is lost . This is done at early boot
* because only the bootmem allocator can allocate 32 + MB .
*
* Copyright 2002 Andi Kleen , SuSE Labs .
* $ Id : aperture . c , v 1.7 2003 / 08 / 01 03 : 36 : 18 ak Exp $
*/
# include <linux/config.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/bootmem.h>
# include <linux/mmzone.h>
# include <linux/pci_ids.h>
# include <linux/pci.h>
# include <linux/bitops.h>
# include <asm/e820.h>
# include <asm/io.h>
# include <asm/proto.h>
# include <asm/pci-direct.h>
2006-01-12 00:44:27 +03:00
# include <asm/dma.h>
2005-04-17 02:20:36 +04:00
int iommu_aperture ;
int iommu_aperture_disabled __initdata = 0 ;
int iommu_aperture_allowed __initdata = 0 ;
int fallback_aper_order __initdata = 1 ; /* 64MB */
int fallback_aper_force __initdata = 0 ;
int fix_aperture __initdata = 1 ;
2005-06-09 02:49:25 +04:00
/* This code runs before the PCI subsystem is initialized, so just
access the northbridge directly . */
2005-04-17 02:20:36 +04:00
2005-06-09 02:49:25 +04:00
# define NB_ID_3 (PCI_VENDOR_ID_AMD | (0x1103<<16))
2005-04-17 02:25:13 +04:00
2005-04-17 02:20:36 +04:00
static u32 __init allocate_aperture ( void )
{
pg_data_t * nd0 = NODE_DATA ( 0 ) ;
u32 aper_size ;
void * p ;
if ( fallback_aper_order > 7 )
fallback_aper_order = 7 ;
aper_size = ( 32 * 1024 * 1024 ) < < fallback_aper_order ;
/*
2005-06-09 02:49:25 +04:00
* Aperture has to be naturally aligned . This means an 2 GB aperture won ' t
* have much chances to find a place in the lower 4 GB of memory .
* Unfortunately we cannot move it up because that would make the
* IOMMU useless .
2005-04-17 02:20:36 +04:00
*/
p = __alloc_bootmem_node ( nd0 , aper_size , aper_size , 0 ) ;
if ( ! p | | __pa ( p ) + aper_size > 0xffffffff ) {
printk ( " Cannot allocate aperture memory hole (%p,%uK) \n " ,
p , aper_size > > 10 ) ;
if ( p )
2006-03-25 18:31:19 +03:00
free_bootmem_node ( nd0 , __pa ( p ) , aper_size ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-06-09 02:49:25 +04:00
printk ( " Mapping aperture over %d KB of RAM @ %lx \n " ,
2005-04-17 02:20:36 +04:00
aper_size > > 10 , __pa ( p ) ) ;
return ( u32 ) __pa ( p ) ;
}
static int __init aperture_valid ( char * name , u64 aper_base , u32 aper_size )
{
if ( ! aper_base )
return 0 ;
if ( aper_size < 64 * 1024 * 1024 ) {
printk ( " Aperture from %s too small (%d MB) \n " , name , aper_size > > 20 ) ;
return 0 ;
}
if ( aper_base + aper_size > = 0xffffffff ) {
printk ( " Aperture from %s beyond 4GB. Ignoring. \n " , name ) ;
return 0 ;
}
2006-04-07 21:49:24 +04:00
if ( e820_any_mapped ( aper_base , aper_base + aper_size , E820_RAM ) ) {
2005-04-17 02:20:36 +04:00
printk ( " Aperture from %s pointing to e820 RAM. Ignoring. \n " , name ) ;
return 0 ;
}
return 1 ;
}
2005-06-09 02:49:25 +04:00
/* Find a PCI capability */
2005-04-17 02:20:36 +04:00
static __u32 __init find_cap ( int num , int slot , int func , int cap )
{
u8 pos ;
int bytes ;
if ( ! ( read_pci_config_16 ( num , slot , func , PCI_STATUS ) & PCI_STATUS_CAP_LIST ) )
return 0 ;
pos = read_pci_config_byte ( num , slot , func , PCI_CAPABILITY_LIST ) ;
for ( bytes = 0 ; bytes < 48 & & pos > = 0x40 ; bytes + + ) {
u8 id ;
pos & = ~ 3 ;
id = read_pci_config_byte ( num , slot , func , pos + PCI_CAP_LIST_ID ) ;
if ( id = = 0xff )
break ;
if ( id = = cap )
return pos ;
pos = read_pci_config_byte ( num , slot , func , pos + PCI_CAP_LIST_NEXT ) ;
}
return 0 ;
}
/* Read a standard AGPv3 bridge header */
static __u32 __init read_agp ( int num , int slot , int func , int cap , u32 * order )
{
u32 apsize ;
u32 apsizereg ;
int nbits ;
u32 aper_low , aper_hi ;
u64 aper ;
printk ( " AGP bridge at %02x:%02x:%02x \n " , num , slot , func ) ;
apsizereg = read_pci_config_16 ( num , slot , func , cap + 0x14 ) ;
if ( apsizereg = = 0xffffffff ) {
printk ( " APSIZE in AGP bridge unreadable \n " ) ;
return 0 ;
}
apsize = apsizereg & 0xfff ;
/* Some BIOS use weird encodings not in the AGPv3 table. */
if ( apsize & 0xff )
apsize | = 0xf00 ;
nbits = hweight16 ( apsize ) ;
* order = 7 - nbits ;
if ( ( int ) * order < 0 ) /* < 32MB */
* order = 0 ;
aper_low = read_pci_config ( num , slot , func , 0x10 ) ;
aper_hi = read_pci_config ( num , slot , func , 0x14 ) ;
aper = ( aper_low & ~ ( ( 1 < < 22 ) - 1 ) ) | ( ( u64 ) aper_hi < < 32 ) ;
printk ( " Aperture from AGP @ %Lx size %u MB (APSIZE %x) \n " ,
aper , 32 < < * order , apsizereg ) ;
if ( ! aperture_valid ( " AGP bridge " , aper , ( 32 * 1024 * 1024 ) < < * order ) )
return 0 ;
return ( u32 ) aper ;
}
/* Look for an AGP bridge. Windows only expects the aperture in the
AGP bridge and some BIOS forget to initialize the Northbridge too .
Work around this here .
Do an PCI bus scan by hand because we ' re running before the PCI
subsystem .
All K8 AGP bridges are AGPv3 compliant , so we can do this scan
generically . It ' s probably overkill to always scan all slots because
the AGP bridges should be always an own bus on the HT hierarchy ,
but do it here for future safety . */
static __u32 __init search_agp_bridge ( u32 * order , int * valid_agp )
{
int num , slot , func ;
/* Poor man's PCI discovery */
2006-03-25 18:31:40 +03:00
for ( num = 0 ; num < 256 ; num + + ) {
2005-04-17 02:20:36 +04:00
for ( slot = 0 ; slot < 32 ; slot + + ) {
for ( func = 0 ; func < 8 ; func + + ) {
u32 class , cap ;
u8 type ;
class = read_pci_config ( num , slot , func ,
PCI_CLASS_REVISION ) ;
if ( class = = 0xffffffff )
break ;
switch ( class > > 16 ) {
case PCI_CLASS_BRIDGE_HOST :
case PCI_CLASS_BRIDGE_OTHER : /* needed? */
/* AGP bridge? */
cap = find_cap ( num , slot , func , PCI_CAP_ID_AGP ) ;
if ( ! cap )
break ;
* valid_agp = 1 ;
return read_agp ( num , slot , func , cap , order ) ;
}
/* No multi-function device? */
type = read_pci_config_byte ( num , slot , func ,
PCI_HEADER_TYPE ) ;
if ( ! ( type & 0x80 ) )
break ;
}
}
}
printk ( " No AGP bridge found \n " ) ;
return 0 ;
}
void __init iommu_hole_init ( void )
{
int fix , num ;
2005-11-05 19:25:53 +03:00
u32 aper_size , aper_alloc = 0 , aper_order = 0 , last_aper_order = 0 ;
2005-04-17 02:20:36 +04:00
u64 aper_base , last_aper_base = 0 ;
int valid_agp = 0 ;
if ( iommu_aperture_disabled | | ! fix_aperture )
return ;
printk ( " Checking aperture... \n " ) ;
fix = 0 ;
for ( num = 24 ; num < 32 ; num + + ) {
char name [ 30 ] ;
if ( read_pci_config ( 0 , num , 3 , 0x00 ) ! = NB_ID_3 )
continue ;
iommu_aperture = 1 ;
aper_order = ( read_pci_config ( 0 , num , 3 , 0x90 ) > > 1 ) & 7 ;
aper_size = ( 32 * 1024 * 1024 ) < < aper_order ;
aper_base = read_pci_config ( 0 , num , 3 , 0x94 ) & 0x7fff ;
aper_base < < = 25 ;
printk ( " CPU %d: aperture @ %Lx size %u MB \n " , num - 24 ,
aper_base , aper_size > > 20 ) ;
sprintf ( name , " northbridge cpu %d " , num - 24 ) ;
if ( ! aperture_valid ( name , aper_base , aper_size ) ) {
fix = 1 ;
break ;
}
if ( ( last_aper_order & & aper_order ! = last_aper_order ) | |
( last_aper_base & & aper_base ! = last_aper_base ) ) {
fix = 1 ;
break ;
}
last_aper_order = aper_order ;
last_aper_base = aper_base ;
}
if ( ! fix & & ! fallback_aper_force )
return ;
if ( ! fallback_aper_force )
aper_alloc = search_agp_bridge ( & aper_order , & valid_agp ) ;
if ( aper_alloc ) {
/* Got the aperture from the AGP bridge */
2005-09-12 20:49:24 +04:00
} else if ( swiotlb & & ! valid_agp ) {
/* Do nothing */
2006-02-26 06:18:22 +03:00
} else if ( ( ! no_iommu & & end_pfn > MAX_DMA32_PFN ) | |
2005-04-17 02:20:36 +04:00
force_iommu | |
valid_agp | |
fallback_aper_force ) {
printk ( " Your BIOS doesn't leave a aperture memory hole \n " ) ;
printk ( " Please enable the IOMMU option in the BIOS setup \n " ) ;
2005-06-09 02:49:25 +04:00
printk ( " This costs you %d MB of RAM \n " ,
32 < < fallback_aper_order ) ;
2005-04-17 02:20:36 +04:00
aper_order = fallback_aper_order ;
aper_alloc = allocate_aperture ( ) ;
if ( ! aper_alloc ) {
/* Could disable AGP and IOMMU here, but it's probably
not worth it . But the later users cannot deal with
bad apertures and turning on the aperture over memory
causes very strange problems , so it ' s better to
panic early . */
panic ( " Not enough memory for aperture " ) ;
}
} else {
return ;
}
/* Fix up the north bridges */
for ( num = 24 ; num < 32 ; num + + ) {
if ( read_pci_config ( 0 , num , 3 , 0x00 ) ! = NB_ID_3 )
continue ;
/* Don't enable translation yet. That is done later.
Assume this BIOS didn ' t initialise the GART so
just overwrite all previous bits */
write_pci_config ( 0 , num , 3 , 0x90 , aper_order < < 1 ) ;
write_pci_config ( 0 , num , 3 , 0x94 , aper_alloc > > 25 ) ;
}
}