2006-09-26 12:52:30 +04:00
/* Various workarounds for chipset bugs.
This code runs very early and can ' t use the regular PCI subsystem
The entries are keyed to PCI bridges which usually identify chipsets
uniquely .
This is only for whole classes of chipsets with specific problems which
need early invasive action ( e . g . before the timers are initialized ) .
Most PCI device specific workarounds can be done later and should be
in standard PCI quirks
Mainboard specific bugs should be handled by DMI entries .
CPU specific bugs in setup . c */
# include <linux/pci.h>
# include <linux/acpi.h>
# include <linux/pci_ids.h>
# include <asm/pci-direct.h>
# include <asm/dma.h>
2007-10-19 22:35:03 +04:00
# include <asm/io_apic.h>
# include <asm/apic.h>
2007-10-24 14:49:48 +04:00
# ifdef CONFIG_GART_IOMMU
2007-10-24 14:49:47 +04:00
# include <asm/gart.h>
2007-10-19 22:35:03 +04:00
# endif
2006-09-26 12:52:30 +04:00
2008-01-30 15:31:25 +03:00
static void __init fix_hypertransport_config ( int num , int slot , int func )
{
u32 htcfg ;
/*
* we found a hypertransport bus
* make sure that we are broadcasting
* interrupts to all cpus on the ht bus
* if we ' re using extended apic ids
*/
htcfg = read_pci_config ( num , slot , func , 0x68 ) ;
if ( htcfg & ( 1 < < 18 ) ) {
printk ( KERN_INFO " Detected use of extended apic ids on hypertransport bus \n " ) ;
if ( ( htcfg & ( 1 < < 17 ) ) = = 0 ) {
printk ( KERN_INFO " Enabling hypertransport extended apic interrupt broadcast \n " ) ;
printk ( KERN_INFO " Note this is a bios bug, please contact your hw vendor \n " ) ;
htcfg | = ( 1 < < 17 ) ;
write_pci_config ( num , slot , func , 0x68 , htcfg ) ;
}
}
}
static void __init via_bugs ( int num , int slot , int func )
2006-09-26 12:52:30 +04:00
{
2007-10-24 14:49:48 +04:00
# ifdef CONFIG_GART_IOMMU
2006-09-26 12:52:30 +04:00
if ( ( end_pfn > MAX_DMA32_PFN | | force_iommu ) & &
2007-10-24 14:49:50 +04:00
! gart_iommu_aperture_allowed ) {
2006-09-26 12:52:30 +04:00
printk ( KERN_INFO
2007-10-19 22:35:03 +04:00
" Looks like a VIA chipset. Disabling IOMMU. "
" Override with iommu=allowed \n " ) ;
2007-10-24 14:49:50 +04:00
gart_iommu_aperture_disabled = 1 ;
2006-09-26 12:52:30 +04:00
}
# endif
}
# ifdef CONFIG_ACPI
2007-10-27 22:57:43 +04:00
# ifdef CONFIG_X86_IO_APIC
2006-09-26 12:52:30 +04:00
2007-02-02 19:48:22 +03:00
static int __init nvidia_hpet_check ( struct acpi_table_header * header )
2006-09-26 12:52:30 +04:00
{
return 0 ;
}
2007-10-27 22:57:43 +04:00
# endif /* CONFIG_X86_IO_APIC */
# endif /* CONFIG_ACPI */
2006-09-26 12:52:30 +04:00
2008-01-30 15:31:25 +03:00
static void __init nvidia_bugs ( int num , int slot , int func )
2006-09-26 12:52:30 +04:00
{
# ifdef CONFIG_ACPI
2007-10-19 22:35:03 +04:00
# ifdef CONFIG_X86_IO_APIC
2006-09-26 12:52:30 +04:00
/*
* All timer overrides on Nvidia are
* wrong unless HPET is enabled .
2006-11-14 18:57:46 +03:00
* Unfortunately that ' s not true on many Asus boards .
* We don ' t know yet how to detect this automatically , but
* at least allow a command line override .
2006-09-26 12:52:30 +04:00
*/
2006-11-14 18:57:46 +03:00
if ( acpi_use_timer_override )
return ;
2007-03-09 02:28:32 +03:00
if ( acpi_table_parse ( ACPI_SIG_HPET , nvidia_hpet_check ) ) {
2006-09-26 12:52:30 +04:00
acpi_skip_timer_override = 1 ;
printk ( KERN_INFO " Nvidia board "
" detected. Ignoring ACPI "
" timer override. \n " ) ;
2006-11-14 18:57:46 +03:00
printk ( KERN_INFO " If you got timer trouble "
" try acpi_use_timer_override \n " ) ;
2006-09-26 12:52:30 +04:00
}
2007-10-19 22:35:03 +04:00
# endif
2006-09-26 12:52:30 +04:00
# endif
/* RED-PEN skip them on mptables too? */
}
2008-01-30 15:31:25 +03:00
static void __init ati_bugs ( int num , int slot , int func )
2006-09-26 12:52:30 +04:00
{
2007-10-19 22:35:03 +04:00
# ifdef CONFIG_X86_IO_APIC
2007-01-09 02:04:46 +03:00
if ( timer_over_8254 = = 1 ) {
timer_over_8254 = 0 ;
printk ( KERN_INFO
2007-10-19 22:35:03 +04:00
" ATI board detected. Disabling timer routing over 8254. \n " ) ;
2007-01-09 02:04:46 +03:00
}
2007-10-19 22:35:03 +04:00
# endif
2006-09-26 12:52:30 +04:00
}
2008-01-30 15:31:25 +03:00
# define QFLAG_APPLY_ONCE 0x1
# define QFLAG_APPLIED 0x2
# define QFLAG_DONE (QFLAG_APPLY_ONCE|QFLAG_APPLIED)
2006-09-26 12:52:30 +04:00
struct chipset {
2008-01-30 15:31:25 +03:00
u32 vendor ;
u32 device ;
u32 class ;
u32 class_mask ;
u32 flags ;
void ( * f ) ( int num , int slot , int func ) ;
2006-09-26 12:52:30 +04:00
} ;
2007-04-09 03:04:03 +04:00
static struct chipset early_qrk [ ] __initdata = {
2008-01-30 15:31:25 +03:00
{ PCI_VENDOR_ID_NVIDIA , PCI_ANY_ID ,
PCI_CLASS_BRIDGE_PCI , PCI_ANY_ID , QFLAG_APPLY_ONCE , nvidia_bugs } ,
{ PCI_VENDOR_ID_VIA , PCI_ANY_ID ,
PCI_CLASS_BRIDGE_PCI , PCI_ANY_ID , QFLAG_APPLY_ONCE , via_bugs } ,
{ PCI_VENDOR_ID_ATI , PCI_ANY_ID ,
PCI_CLASS_BRIDGE_PCI , PCI_ANY_ID , QFLAG_APPLY_ONCE , ati_bugs } ,
{ PCI_VENDOR_ID_AMD , PCI_DEVICE_ID_AMD_K8_NB ,
PCI_CLASS_BRIDGE_HOST , PCI_ANY_ID , 0 , fix_hypertransport_config } ,
2006-09-26 12:52:30 +04:00
{ }
} ;
void __init early_quirks ( void )
{
int num , slot , func ;
2006-09-26 12:52:41 +04:00
if ( ! early_pci_allowed ( ) )
return ;
2006-09-26 12:52:30 +04:00
/* Poor man's PCI discovery */
for ( num = 0 ; num < 32 ; num + + ) {
for ( slot = 0 ; slot < 32 ; slot + + ) {
for ( func = 0 ; func < 8 ; func + + ) {
2008-01-30 15:31:25 +03:00
u16 class ;
u16 vendor ;
u16 device ;
2006-09-26 12:52:30 +04:00
u8 type ;
int i ;
2008-01-30 15:31:25 +03:00
class = read_pci_config_16 ( num , slot , func ,
2006-09-26 12:52:30 +04:00
PCI_CLASS_REVISION ) ;
2008-01-30 15:31:25 +03:00
if ( class = = 0xffff )
2006-09-26 12:52:30 +04:00
break ;
2008-01-30 15:31:25 +03:00
vendor = read_pci_config_16 ( num , slot , func ,
2006-09-26 12:52:30 +04:00
PCI_VENDOR_ID ) ;
2008-01-30 15:31:25 +03:00
device = read_pci_config_16 ( num , slot , func ,
PCI_DEVICE_ID ) ;
for ( i = 0 ; early_qrk [ i ] . f ! = NULL ; i + + ) {
if ( ( ( early_qrk [ i ] . vendor = = PCI_ANY_ID ) | |
( early_qrk [ i ] . vendor = = vendor ) ) & &
( ( early_qrk [ i ] . device = = PCI_ANY_ID ) | |
( early_qrk [ i ] . device = = device ) ) & &
( ! ( ( early_qrk [ i ] . class ^ class ) &
early_qrk [ i ] . class_mask ) ) ) {
if ( ( early_qrk [ i ] . flags & QFLAG_DONE ) ! = QFLAG_DONE )
early_qrk [ i ] . f ( num , slot , func ) ;
early_qrk [ i ] . flags | = QFLAG_APPLIED ;
2006-09-26 12:52:30 +04:00
}
2008-01-30 15:31:25 +03:00
}
2006-09-26 12:52:30 +04:00
type = read_pci_config_byte ( num , slot , func ,
PCI_HEADER_TYPE ) ;
if ( ! ( type & 0x80 ) )
break ;
}
}
}
}