2006-06-26 15:56:40 +04:00
/*
* 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 [ ] = {
2008-09-02 15:13:40 +04:00
{ 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 ) } ,
2006-06-26 15:56:40 +04:00
{ }
} ;
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 ;
2007-05-24 00:57:43 +04:00
2006-06-26 15:56:40 +04:00
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 ;
2007-05-24 00:57:43 +04:00
if ( ! num_k8_northbridges ) {
k8_northbridges [ 0 ] = NULL ;
return 0 ;
}
2006-06-26 15:56:40 +04:00
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 ) {
2007-04-13 19:13:42 +04:00
k8_northbridges [ i ] = dev ;
pci_read_config_dword ( dev , 0x9c , & flush_words [ i + + ] ) ;
2006-06-26 15:56:40 +04:00
}
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 ) ;