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>
2008-07-11 05:23:42 +04:00
# include <asm/iommu.h>
2008-11-27 20:39:15 +03:00
# include <asm/gart.h>
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 ) ) {
2008-01-30 15:31:26 +03:00
printk ( KERN_INFO " Detected use of extended apic ids "
" on hypertransport bus \n " ) ;
2008-01-30 15:31:25 +03:00
if ( ( htcfg & ( 1 < < 17 ) ) = = 0 ) {
2008-01-30 15:31:26 +03:00
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 " ) ;
2008-01-30 15:31:25 +03:00
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
2008-06-25 09:14:09 +04:00
if ( ( max_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-10-14 23:01:15 +04:00
# if defined(CONFIG_ACPI) && defined(CONFIG_X86_IO_APIC)
static u32 __init ati_ixp4x0_rev ( int num , int slot , int func )
2008-10-07 02:11:22 +04:00
{
u32 d ;
u8 b ;
b = read_pci_config_byte ( num , slot , func , 0xac ) ;
b & = ~ ( 1 < < 5 ) ;
write_pci_config_byte ( num , slot , func , 0xac , b ) ;
d = read_pci_config ( num , slot , func , 0x70 ) ;
d | = 1 < < 8 ;
write_pci_config ( num , slot , func , 0x70 , d ) ;
d = read_pci_config ( num , slot , func , 0x8 ) ;
d & = 0xff ;
return d ;
}
static void __init ati_bugs ( int num , int slot , int func )
{
u32 d ;
u8 b ;
if ( acpi_use_timer_override )
return ;
d = ati_ixp4x0_rev ( num , slot , func ) ;
if ( d < 0x82 )
acpi_skip_timer_override = 1 ;
else {
/* check for IRQ0 interrupt swap */
outb ( 0x72 , 0xcd6 ) ; b = inb ( 0xcd7 ) ;
if ( ! ( b & 0x2 ) )
acpi_skip_timer_override = 1 ;
}
if ( acpi_skip_timer_override ) {
printk ( KERN_INFO " SB4X0 revision 0x%x \n " , d ) ;
printk ( KERN_INFO " Ignoring ACPI timer override. \n " ) ;
printk ( KERN_INFO " If you got timer trouble "
" try acpi_use_timer_override \n " ) ;
}
}
2008-10-14 23:01:15 +04:00
static u32 __init ati_sbx00_rev ( int num , int slot , int func )
{
2011-02-24 17:53:46 +03:00
u32 d ;
2008-10-14 23:01:15 +04:00
d = read_pci_config ( num , slot , func , 0x8 ) ;
d & = 0xff ;
return d ;
}
static void __init ati_bugs_contd ( int num , int slot , int func )
{
u32 d , rev ;
rev = ati_sbx00_rev ( num , slot , func ) ;
2011-02-24 17:53:46 +03:00
if ( rev > = 0x40 )
acpi_fix_pin2_polarity = 1 ;
2011-03-15 17:31:37 +03:00
/*
* SB600 : revisions 0x11 , 0x12 , 0x13 , 0x14 , . . .
* SB700 : revisions 0x39 , 0x3a , . . .
* SB800 : revisions 0x40 , 0x41 , . . .
*/
if ( rev > = 0x39 )
2008-10-14 23:01:15 +04:00
return ;
2011-02-24 17:53:46 +03:00
if ( acpi_use_timer_override )
return ;
2008-10-14 23:01:15 +04:00
/* check for IRQ0 interrupt swap */
d = read_pci_config ( num , slot , func , 0x64 ) ;
if ( ! ( d & ( 1 < < 14 ) ) )
acpi_skip_timer_override = 1 ;
if ( acpi_skip_timer_override ) {
printk ( KERN_INFO " SB600 revision 0x%x \n " , rev ) ;
printk ( KERN_INFO " Ignoring ACPI timer override. \n " ) ;
printk ( KERN_INFO " If you got timer trouble "
" try acpi_use_timer_override \n " ) ;
}
}
# else
static void __init ati_bugs ( int num , int slot , int func )
{
}
static void __init ati_bugs_contd ( int num , int slot , int func )
{
}
# endif
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
} ;
2009-01-09 23:17:39 +03:00
/*
* Only works for devices on the root bus . If you add any devices
* not on bus 0 readd another loop level in early_quirks ( ) . But
* be careful because at least the Nvidia quirk here relies on
* only matching on bus 0.
*/
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_AMD , PCI_DEVICE_ID_AMD_K8_NB ,
PCI_CLASS_BRIDGE_HOST , PCI_ANY_ID , 0 , fix_hypertransport_config } ,
2008-10-07 02:11:22 +04:00
{ PCI_VENDOR_ID_ATI , PCI_DEVICE_ID_ATI_IXP400_SMBUS ,
PCI_CLASS_SERIAL_SMBUS , PCI_ANY_ID , 0 , ati_bugs } ,
2008-10-14 23:01:15 +04:00
{ PCI_VENDOR_ID_ATI , PCI_DEVICE_ID_ATI_SBX00_SMBUS ,
PCI_CLASS_SERIAL_SMBUS , PCI_ANY_ID , 0 , ati_bugs_contd } ,
2006-09-26 12:52:30 +04:00
{ }
} ;
2008-06-17 02:29:45 +04:00
/**
* check_dev_quirk - apply early quirks to a given PCI device
* @ num : bus number
* @ slot : slot number
* @ func : PCI function
*
* Check the vendor & device ID against the early quirks table .
*
* If the device is single function , let early_quirks ( ) know so we don ' t
* poke at this device again .
*/
static int __init check_dev_quirk ( int num , int slot , int func )
2008-01-30 15:31:26 +03:00
{
u16 class ;
u16 vendor ;
u16 device ;
u8 type ;
int i ;
class = read_pci_config_16 ( num , slot , func , PCI_CLASS_DEVICE ) ;
if ( class = = 0xffff )
2008-06-17 02:29:45 +04:00
return - 1 ; /* no class, treat as single function */
2008-01-30 15:31:26 +03:00
vendor = read_pci_config_16 ( num , slot , func , PCI_VENDOR_ID ) ;
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 ;
}
}
type = read_pci_config_byte ( num , slot , func ,
PCI_HEADER_TYPE ) ;
if ( ! ( type & 0x80 ) )
2008-06-17 02:29:45 +04:00
return - 1 ;
return 0 ;
2008-01-30 15:31:26 +03:00
}
2006-09-26 12:52:30 +04:00
void __init early_quirks ( void )
{
2009-01-09 23:17:39 +03:00
int 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 */
2009-01-09 23:17:39 +03:00
/* Only scan the root bus */
for ( slot = 0 ; slot < 32 ; slot + + )
for ( func = 0 ; func < 8 ; func + + ) {
/* Only probe function 0 on single fn devices */
if ( check_dev_quirk ( 0 , slot , func ) )
break ;
}
2006-09-26 12:52:30 +04:00
}