2005-04-17 02:20:36 +04:00
/*
* This file contains quirk handling code for PnP devices
* Some devices do not report all their resources , and need to have extra
* resources added . This is most easily accomplished at initialisation time
* when building up the resource structure for the first time .
*
* Copyright ( c ) 2000 Peter Denison < peterd @ pnd - pc . demon . co . uk >
*
* Heavily based on PCI quirks handling which is
*
* Copyright ( c ) 1999 Martin Mares < mj @ ucw . cz >
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/pnp.h>
2007-05-08 11:36:00 +04:00
# include <linux/io.h>
2007-10-17 10:31:10 +04:00
# include <linux/kallsyms.h>
2005-04-17 02:20:36 +04:00
# include "base.h"
static void quirk_awe32_resources ( struct pnp_dev * dev )
{
struct pnp_port * port , * port2 , * port3 ;
struct pnp_option * res = dev - > dependent ;
/*
* Unfortunately the isapnp_add_port_resource is too tightly bound
* into the PnP discovery sequence , and cannot be used . Link in the
* two extra ports ( at offset 0x400 and 0x800 from the one given ) by
* hand .
*/
2007-07-26 21:41:20 +04:00
for ( ; res ; res = res - > next ) {
2005-04-17 02:20:36 +04:00
port2 = pnp_alloc ( sizeof ( struct pnp_port ) ) ;
if ( ! port2 )
return ;
port3 = pnp_alloc ( sizeof ( struct pnp_port ) ) ;
if ( ! port3 ) {
kfree ( port2 ) ;
return ;
}
port = res - > port ;
memcpy ( port2 , port , sizeof ( struct pnp_port ) ) ;
memcpy ( port3 , port , sizeof ( struct pnp_port ) ) ;
port - > next = port2 ;
port2 - > next = port3 ;
port2 - > min + = 0x400 ;
port2 - > max + = 0x400 ;
port3 - > min + = 0x800 ;
port3 - > max + = 0x800 ;
2008-04-28 13:15:59 +04:00
dev_info ( & dev - > dev ,
" AWE32 quirk - added ioports 0x%lx and 0x%lx \n " ,
( unsigned long ) port2 - > min ,
( unsigned long ) port3 - > min ) ;
2005-04-17 02:20:36 +04:00
}
}
static void quirk_cmi8330_resources ( struct pnp_dev * dev )
{
struct pnp_option * res = dev - > dependent ;
unsigned long tmp ;
2007-07-26 21:41:20 +04:00
for ( ; res ; res = res - > next ) {
2005-04-17 02:20:36 +04:00
struct pnp_irq * irq ;
struct pnp_dma * dma ;
2007-07-26 21:41:20 +04:00
for ( irq = res - > irq ; irq ; irq = irq - > next ) { // Valid irqs are 5, 7, 10
2005-04-17 02:20:36 +04:00
tmp = 0x04A0 ;
bitmap_copy ( irq - > map , & tmp , 16 ) ; // 0000 0100 1010 0000
}
2007-07-26 21:41:20 +04:00
for ( dma = res - > dma ; dma ; dma = dma - > next ) // Valid 8bit dma channels are 1,3
if ( ( dma - > flags & IORESOURCE_DMA_TYPE_MASK ) = =
IORESOURCE_DMA_8BIT )
2005-04-17 02:20:36 +04:00
dma - > map = 0x000A ;
}
2008-04-28 13:15:59 +04:00
dev_info ( & dev - > dev , " CMI8330 quirk - forced possible IRQs to 5, 7, 10 "
" and DMA channels to 1, 3 \n " ) ;
2005-04-17 02:20:36 +04:00
}
static void quirk_sb16audio_resources ( struct pnp_dev * dev )
{
struct pnp_port * port ;
struct pnp_option * res = dev - > dependent ;
2007-07-26 21:41:20 +04:00
int changed = 0 ;
2005-04-17 02:20:36 +04:00
/*
* The default range on the mpu port for these devices is 0x388 - 0x388 .
* Here we increase that range so that two such cards can be
* auto - configured .
*/
2007-07-26 21:41:20 +04:00
for ( ; res ; res = res - > next ) {
2005-04-17 02:20:36 +04:00
port = res - > port ;
2007-07-26 21:41:20 +04:00
if ( ! port )
2005-04-17 02:20:36 +04:00
continue ;
port = port - > next ;
2007-07-26 21:41:20 +04:00
if ( ! port )
2005-04-17 02:20:36 +04:00
continue ;
port = port - > next ;
2007-07-26 21:41:20 +04:00
if ( ! port )
2005-04-17 02:20:36 +04:00
continue ;
2007-07-26 21:41:20 +04:00
if ( port - > min ! = port - > max )
2005-04-17 02:20:36 +04:00
continue ;
port - > max + = 0x70 ;
changed = 1 ;
}
2007-07-26 21:41:20 +04:00
if ( changed )
2008-04-28 13:15:59 +04:00
dev_info ( & dev - > dev , " SB audio device quirk - increased port range \n " ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-15 03:05:36 +04:00
static struct pnp_option * quirk_isapnp_mpu_options ( struct pnp_dev * dev )
{
struct pnp_option * head = NULL ;
struct pnp_option * prev = NULL ;
struct pnp_option * res ;
/*
* Build a functional IRQ - less variant of each MPU option .
*/
for ( res = dev - > dependent ; res ; res = res - > next ) {
struct pnp_option * curr ;
struct pnp_port * port ;
struct pnp_port * copy ;
port = res - > port ;
if ( ! port | | ! res - > irq )
continue ;
copy = pnp_alloc ( sizeof * copy ) ;
if ( ! copy )
break ;
copy - > min = port - > min ;
copy - > max = port - > max ;
copy - > align = port - > align ;
copy - > size = port - > size ;
copy - > flags = port - > flags ;
curr = pnp_build_option ( PNP_RES_PRIORITY_FUNCTIONAL ) ;
if ( ! curr ) {
kfree ( copy ) ;
break ;
}
curr - > port = copy ;
if ( prev )
prev - > next = curr ;
else
head = curr ;
prev = curr ;
}
if ( head )
dev_info ( & dev - > dev , " adding IRQ-less MPU options \n " ) ;
return head ;
}
static void quirk_ad1815_mpu_resources ( struct pnp_dev * dev )
{
struct pnp_option * res ;
struct pnp_irq * irq ;
/*
* Distribute the independent IRQ over the dependent options
*/
res = dev - > independent ;
if ( ! res )
return ;
irq = res - > irq ;
if ( ! irq | | irq - > next )
return ;
res = dev - > dependent ;
if ( ! res )
return ;
while ( 1 ) {
struct pnp_irq * copy ;
copy = pnp_alloc ( sizeof * copy ) ;
if ( ! copy )
break ;
memcpy ( copy - > map , irq - > map , sizeof copy - > map ) ;
copy - > flags = irq - > flags ;
copy - > next = res - > irq ; /* Yes, this is NULL */
res - > irq = copy ;
if ( ! res - > next )
break ;
res = res - > next ;
}
kfree ( irq ) ;
res - > next = quirk_isapnp_mpu_options ( dev ) ;
res = dev - > independent ;
res - > irq = NULL ;
}
static void quirk_isapnp_mpu_resources ( struct pnp_dev * dev )
{
struct pnp_option * res ;
res = dev - > dependent ;
if ( ! res )
return ;
while ( res - > next )
res = res - > next ;
res - > next = quirk_isapnp_mpu_options ( dev ) ;
}
2008-03-12 00:24:41 +03:00
# include <linux/pci.h>
static void quirk_system_pci_resources ( struct pnp_dev * dev )
{
struct pci_dev * pdev = NULL ;
2008-04-29 02:34:15 +04:00
struct resource * res ;
2008-03-12 00:24:41 +03:00
resource_size_t pnp_start , pnp_end , pci_start , pci_end ;
int i , j ;
/*
* Some BIOSes have PNP motherboard devices with resources that
* partially overlap PCI BARs . The PNP system driver claims these
* motherboard resources , which prevents the normal PCI driver from
* requesting them later .
*
* This patch disables the PNP resources that conflict with PCI BARs
* so they won ' t be claimed by the PNP system driver .
*/
for_each_pci_dev ( pdev ) {
for ( i = 0 ; i < DEVICE_COUNT_RESOURCE ; i + + ) {
if ( ! ( pci_resource_flags ( pdev , i ) & IORESOURCE_MEM ) | |
pci_resource_len ( pdev , i ) = = 0 )
continue ;
pci_start = pci_resource_start ( pdev , i ) ;
pci_end = pci_resource_end ( pdev , i ) ;
2008-04-29 02:34:26 +04:00
for ( j = 0 ;
( res = pnp_get_resource ( dev , IORESOURCE_MEM , j ) ) ;
j + + ) {
if ( res - > flags & IORESOURCE_UNSET | |
( res - > start = = 0 & & res - > end = = 0 ) )
2008-03-12 00:24:41 +03:00
continue ;
2008-04-29 02:34:26 +04:00
pnp_start = res - > start ;
pnp_end = res - > end ;
2008-03-12 00:24:41 +03:00
/*
* If the PNP region doesn ' t overlap the PCI
* region at all , there ' s no problem .
*/
if ( pnp_end < pci_start | | pnp_start > pci_end )
continue ;
/*
* If the PNP region completely encloses ( or is
* at least as large as ) the PCI region , that ' s
* also OK . For example , this happens when the
* PNP device describes a bridge with PCI
* behind it .
*/
if ( pnp_start < = pci_start & &
pnp_end > = pci_end )
continue ;
/*
* Otherwise , the PNP region overlaps * part * of
* the PCI region , and that might prevent a PCI
* driver from requesting its resources .
*/
dev_warn ( & dev - > dev , " mem resource "
" (0x%llx-0x%llx) overlaps %s BAR %d "
" (0x%llx-0x%llx), disabling \n " ,
( unsigned long long ) pnp_start ,
( unsigned long long ) pnp_end ,
pci_name ( pdev ) , i ,
( unsigned long long ) pci_start ,
( unsigned long long ) pci_end ) ;
2008-04-29 02:34:15 +04:00
res - > flags = 0 ;
2008-03-12 00:24:41 +03:00
}
}
}
}
2005-04-17 02:20:36 +04:00
/*
* PnP Quirks
* Cards or devices that need some tweaking due to incomplete resource info
*/
static struct pnp_fixup pnp_fixups [ ] = {
/* Soundblaster awe io port quirk */
2007-07-26 21:41:20 +04:00
{ " CTL0021 " , quirk_awe32_resources } ,
{ " CTL0022 " , quirk_awe32_resources } ,
{ " CTL0023 " , quirk_awe32_resources } ,
2005-04-17 02:20:36 +04:00
/* CMI 8330 interrupt and dma fix */
2007-07-26 21:41:20 +04:00
{ " @X@0001 " , quirk_cmi8330_resources } ,
2005-04-17 02:20:36 +04:00
/* Soundblaster audio device io port range quirk */
2007-07-26 21:41:20 +04:00
{ " CTL0001 " , quirk_sb16audio_resources } ,
{ " CTL0031 " , quirk_sb16audio_resources } ,
{ " CTL0041 " , quirk_sb16audio_resources } ,
{ " CTL0042 " , quirk_sb16audio_resources } ,
{ " CTL0043 " , quirk_sb16audio_resources } ,
{ " CTL0044 " , quirk_sb16audio_resources } ,
{ " CTL0045 " , quirk_sb16audio_resources } ,
2008-05-15 03:05:36 +04:00
/* Add IRQ-less MPU options */
{ " ADS7151 " , quirk_ad1815_mpu_resources } ,
{ " ADS7181 " , quirk_isapnp_mpu_resources } ,
{ " AZT0002 " , quirk_isapnp_mpu_resources } ,
/* PnP resources that might overlap PCI BARs */
2008-03-12 00:24:41 +03:00
{ " PNP0c01 " , quirk_system_pci_resources } ,
{ " PNP0c02 " , quirk_system_pci_resources } ,
2007-07-26 21:41:20 +04:00
{ " " }
2005-04-17 02:20:36 +04:00
} ;
void pnp_fixup_device ( struct pnp_dev * dev )
{
2008-05-15 03:05:33 +04:00
struct pnp_fixup * f ;
2007-10-17 10:31:10 +04:00
2008-05-15 03:05:33 +04:00
for ( f = pnp_fixups ; * f - > id ; f + + ) {
if ( ! compare_pnp_id ( dev - > id , f - > id ) )
continue ;
2007-10-17 10:31:10 +04:00
# ifdef DEBUG
2008-05-15 03:05:33 +04:00
dev_dbg ( & dev - > dev , " %s: calling " , f - > id ) ;
print_fn_descriptor_symbol ( " %s \n " ,
( unsigned long ) f - > quirk_function ) ;
2007-10-17 10:31:10 +04:00
# endif
2008-05-15 03:05:33 +04:00
f - > quirk_function ( dev ) ;
2005-04-17 02:20:36 +04:00
}
}