2005-04-17 02:20:36 +04:00
/*
* rsrc_nonstatic . c - - Resource management routines for ! SS_CAP_STATIC_MAP sockets
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* The initial developer of the original code is David A . Hinds
* < dahinds @ users . sourceforge . net > . Portions created by David A . Hinds
* are Copyright ( C ) 1999 David A . Hinds . All Rights Reserved .
*
* ( C ) 1999 David A . Hinds
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/ioport.h>
# include <linux/timer.h>
# include <linux/pci.h>
# include <linux/device.h>
# include <asm/irq.h>
# include <asm/io.h>
# include <pcmcia/cs_types.h>
# include <pcmcia/ss.h>
# include <pcmcia/cs.h>
# include <pcmcia/bulkmem.h>
# include <pcmcia/cistpl.h>
# include "cs_internal.h"
MODULE_AUTHOR ( " David A. Hinds, Dominik Brodowski " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* Parameters that can be set with 'insmod' */
# define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
INT_MODULE_PARM ( probe_mem , 1 ) ; /* memory probe? */
# ifdef CONFIG_PCMCIA_PROBE
INT_MODULE_PARM ( probe_io , 1 ) ; /* IO port probe? */
INT_MODULE_PARM ( mem_limit , 0x10000 ) ;
# endif
/* for io_db and mem_db */
struct resource_map {
u_long base , num ;
struct resource_map * next ;
} ;
struct socket_data {
struct resource_map mem_db ;
struct resource_map io_db ;
unsigned int rsrc_mem_probe ;
} ;
2006-01-10 23:20:36 +03:00
static DEFINE_MUTEX ( rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
# define MEM_PROBE_LOW (1 << 0)
# define MEM_PROBE_HIGH (1 << 1)
/*======================================================================
Linux resource management extensions
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static struct resource *
2006-06-13 04:07:52 +04:00
make_resource ( resource_size_t b , resource_size_t n , int flags , char * name )
2005-04-17 02:20:36 +04:00
{
2005-12-11 23:18:26 +03:00
struct resource * res = kzalloc ( sizeof ( * res ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( res ) {
res - > name = name ;
res - > start = b ;
res - > end = b + n - 1 ;
res - > flags = flags ;
}
return res ;
}
static struct resource *
2006-06-13 04:07:52 +04:00
claim_region ( struct pcmcia_socket * s , resource_size_t base ,
resource_size_t size , int type , char * name )
2005-04-17 02:20:36 +04:00
{
struct resource * res , * parent ;
parent = type & IORESOURCE_MEM ? & iomem_resource : & ioport_resource ;
res = make_resource ( base , size , type | IORESOURCE_BUSY , name ) ;
if ( res ) {
# ifdef CONFIG_PCI
if ( s & & s - > cb_dev )
parent = pci_find_parent_resource ( s - > cb_dev , res ) ;
# endif
if ( ! parent | | request_resource ( parent , res ) ) {
kfree ( res ) ;
res = NULL ;
}
}
return res ;
}
static void free_region ( struct resource * res )
{
if ( res ) {
release_resource ( res ) ;
kfree ( res ) ;
}
}
/*======================================================================
These manage the internal databases of available resources .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int add_interval ( struct resource_map * map , u_long base , u_long num )
{
struct resource_map * p , * q ;
for ( p = map ; ; p = p - > next ) {
if ( ( p ! = map ) & & ( p - > base + p - > num - 1 > = base ) )
return - 1 ;
if ( ( p - > next = = map ) | | ( p - > next - > base > base + num - 1 ) )
break ;
}
q = kmalloc ( sizeof ( struct resource_map ) , GFP_KERNEL ) ;
if ( ! q ) return CS_OUT_OF_RESOURCE ;
q - > base = base ; q - > num = num ;
q - > next = p - > next ; p - > next = q ;
return CS_SUCCESS ;
}
/*====================================================================*/
static int sub_interval ( struct resource_map * map , u_long base , u_long num )
{
struct resource_map * p , * q ;
for ( p = map ; ; p = q ) {
q = p - > next ;
if ( q = = map )
break ;
if ( ( q - > base + q - > num > base ) & & ( base + num > q - > base ) ) {
if ( q - > base > = base ) {
if ( q - > base + q - > num < = base + num ) {
/* Delete whole block */
p - > next = q - > next ;
kfree ( q ) ;
/* don't advance the pointer yet */
q = p ;
} else {
/* Cut off bit from the front */
q - > num = q - > base + q - > num - base - num ;
q - > base = base + num ;
}
} else if ( q - > base + q - > num < = base + num ) {
/* Cut off bit from the end */
q - > num = base - q - > base ;
} else {
/* Split the block into two pieces */
p = kmalloc ( sizeof ( struct resource_map ) , GFP_KERNEL ) ;
if ( ! p ) return CS_OUT_OF_RESOURCE ;
p - > base = base + num ;
p - > num = q - > base + q - > num - p - > base ;
q - > num = base - q - > base ;
p - > next = q - > next ; q - > next = p ;
}
}
}
return CS_SUCCESS ;
}
/*======================================================================
These routines examine a region of IO or memory addresses to
determine what ranges might be genuinely available .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# ifdef CONFIG_PCMCIA_PROBE
static void do_io_probe ( struct pcmcia_socket * s , kio_addr_t base , kio_addr_t num )
{
struct resource * res ;
struct socket_data * s_data = s - > resource_data ;
kio_addr_t i , j , bad ;
int any ;
u_char * b , hole , most ;
printk ( KERN_INFO " cs: IO port probe %#lx-%#lx: " ,
base , base + num - 1 ) ;
/* First, what does a floating port look like? */
2005-12-11 23:18:26 +03:00
b = kzalloc ( 256 , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! b ) {
printk ( KERN_ERR " do_io_probe: unable to kmalloc 256 bytes " ) ;
return ;
}
for ( i = base , most = 0 ; i < base + num ; i + = 8 ) {
res = claim_region ( NULL , i , 8 , IORESOURCE_IO , " PCMCIA IO probe " ) ;
if ( ! res )
continue ;
hole = inb ( i ) ;
for ( j = 1 ; j < 8 ; j + + )
if ( inb ( i + j ) ! = hole ) break ;
free_region ( res ) ;
if ( ( j = = 8 ) & & ( + + b [ hole ] > b [ most ] ) )
most = hole ;
if ( b [ most ] = = 127 ) break ;
}
kfree ( b ) ;
bad = any = 0 ;
for ( i = base ; i < base + num ; i + = 8 ) {
res = claim_region ( NULL , i , 8 , IORESOURCE_IO , " PCMCIA IO probe " ) ;
if ( ! res )
continue ;
for ( j = 0 ; j < 8 ; j + + )
if ( inb ( i + j ) ! = most ) break ;
free_region ( res ) ;
if ( j < 8 ) {
if ( ! any )
printk ( " excluding " ) ;
if ( ! bad )
bad = any = i ;
} else {
if ( bad ) {
sub_interval ( & s_data - > io_db , bad , i - bad ) ;
printk ( " %#lx-%#lx " , bad , i - 1 ) ;
bad = 0 ;
}
}
}
if ( bad ) {
if ( ( num > 16 ) & & ( bad = = base ) & & ( i = = base + num ) ) {
printk ( " nothing: probe failed. \n " ) ;
return ;
} else {
sub_interval ( & s_data - > io_db , bad , i - bad ) ;
printk ( " %#lx-%#lx " , bad , i - 1 ) ;
}
}
printk ( any ? " \n " : " clean. \n " ) ;
}
# endif
/*======================================================================
This is tricky . . . when we set up CIS memory , we try to validate
the memory window space allocations .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* Validation function for cards with a valid CIS */
static int readable ( struct pcmcia_socket * s , struct resource * res , cisinfo_t * info )
{
int ret = - 1 ;
s - > cis_mem . res = res ;
s - > cis_virt = ioremap ( res - > start , s - > map_size ) ;
if ( s - > cis_virt ) {
ret = pccard_validate_cis ( s , BIND_FN_ALL , info ) ;
/* invalidate mapping and CIS cache */
iounmap ( s - > cis_virt ) ;
s - > cis_virt = NULL ;
destroy_cis_cache ( s ) ;
}
s - > cis_mem . res = NULL ;
if ( ( ret ! = 0 ) | | ( info - > Chains = = 0 ) )
return 0 ;
return 1 ;
}
/* Validation function for simple memory cards */
static int checksum ( struct pcmcia_socket * s , struct resource * res )
{
pccard_mem_map map ;
int i , a = 0 , b = - 1 , d ;
void __iomem * virt ;
virt = ioremap ( res - > start , s - > map_size ) ;
if ( virt ) {
map . map = 0 ;
map . flags = MAP_ACTIVE ;
map . speed = 0 ;
map . res = res ;
map . card_start = 0 ;
s - > ops - > set_mem_map ( s , & map ) ;
/* Don't bother checking every word... */
for ( i = 0 ; i < s - > map_size ; i + = 44 ) {
d = readl ( virt + i ) ;
a + = d ;
b & = d ;
}
map . flags = 0 ;
s - > ops - > set_mem_map ( s , & map ) ;
iounmap ( virt ) ;
}
return ( b = = - 1 ) ? - 1 : ( a > > 1 ) ;
}
static int
cis_readable ( struct pcmcia_socket * s , unsigned long base , unsigned long size )
{
struct resource * res1 , * res2 ;
cisinfo_t info1 , info2 ;
int ret = 0 ;
res1 = claim_region ( s , base , size / 2 , IORESOURCE_MEM , " cs memory probe " ) ;
res2 = claim_region ( s , base + size / 2 , size / 2 , IORESOURCE_MEM , " cs memory probe " ) ;
if ( res1 & & res2 ) {
ret = readable ( s , res1 , & info1 ) ;
ret + = readable ( s , res2 , & info2 ) ;
}
free_region ( res2 ) ;
free_region ( res1 ) ;
return ( ret = = 2 ) & & ( info1 . Chains = = info2 . Chains ) ;
}
static int
checksum_match ( struct pcmcia_socket * s , unsigned long base , unsigned long size )
{
struct resource * res1 , * res2 ;
int a = - 1 , b = - 1 ;
res1 = claim_region ( s , base , size / 2 , IORESOURCE_MEM , " cs memory probe " ) ;
res2 = claim_region ( s , base + size / 2 , size / 2 , IORESOURCE_MEM , " cs memory probe " ) ;
if ( res1 & & res2 ) {
a = checksum ( s , res1 ) ;
b = checksum ( s , res2 ) ;
}
free_region ( res2 ) ;
free_region ( res1 ) ;
return ( a = = b ) & & ( a > = 0 ) ;
}
/*======================================================================
The memory probe . If the memory list includes a 64 K - aligned block
below 1 MB , we probe in 64 K chunks , and as soon as we accumulate at
least mem_limit free space , we quit .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int do_mem_probe ( u_long base , u_long num , struct pcmcia_socket * s )
{
struct socket_data * s_data = s - > resource_data ;
u_long i , j , bad , fail , step ;
printk ( KERN_INFO " cs: memory probe 0x%06lx-0x%06lx: " ,
base , base + num - 1 ) ;
bad = fail = 0 ;
step = ( num < 0x20000 ) ? 0x2000 : ( ( num > > 4 ) & ~ 0x1fff ) ;
2005-06-28 03:28:58 +04:00
/* don't allow too large steps */
if ( step > 0x800000 )
step = 0x800000 ;
2005-04-17 02:20:36 +04:00
/* cis_readable wants to map 2x map_size */
if ( step < 2 * s - > map_size )
step = 2 * s - > map_size ;
for ( i = j = base ; i < base + num ; i = j + step ) {
if ( ! fail ) {
for ( j = i ; j < base + num ; j + = step ) {
if ( cis_readable ( s , j , step ) )
break ;
}
fail = ( ( i = = base ) & & ( j = = base + num ) ) ;
}
if ( fail ) {
for ( j = i ; j < base + num ; j + = 2 * step )
if ( checksum_match ( s , j , step ) & &
checksum_match ( s , j + step , step ) )
break ;
}
if ( i ! = j ) {
if ( ! bad ) printk ( " excluding " ) ;
printk ( " %#05lx-%#05lx " , i , j - 1 ) ;
sub_interval ( & s_data - > mem_db , i , j - i ) ;
bad + = j - i ;
}
}
printk ( bad ? " \n " : " clean. \n " ) ;
return ( num - bad ) ;
}
# ifdef CONFIG_PCMCIA_PROBE
static u_long inv_probe ( struct resource_map * m , struct pcmcia_socket * s )
{
2005-09-28 21:41:56 +04:00
struct socket_data * s_data = s - > resource_data ;
u_long ok ;
if ( m = = & s_data - > mem_db )
return 0 ;
ok = inv_probe ( m - > next , s ) ;
if ( ok ) {
if ( m - > base > = 0x100000 )
sub_interval ( & s_data - > mem_db , m - > base , m - > num ) ;
return ok ;
}
if ( m - > base < 0x100000 )
return 0 ;
return do_mem_probe ( m - > base , m - > num , s ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-28 21:41:56 +04:00
static int validate_mem ( struct pcmcia_socket * s , unsigned int probe_mask )
2005-04-17 02:20:36 +04:00
{
2005-09-28 21:41:56 +04:00
struct resource_map * m , mm ;
static unsigned char order [ ] = { 0xd0 , 0xe0 , 0xc0 , 0xf0 } ;
unsigned long b , i , ok = 0 ;
struct socket_data * s_data = s - > resource_data ;
2005-04-17 02:20:36 +04:00
2005-09-28 21:41:56 +04:00
/* We do up to four passes through the list */
if ( probe_mask & MEM_PROBE_HIGH ) {
if ( inv_probe ( s_data - > mem_db . next , s ) > 0 )
return 0 ;
printk ( KERN_NOTICE " cs: warning: no high memory space "
" available! \n " ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-09-28 21:41:56 +04:00
for ( m = s_data - > mem_db . next ; m ! = & s_data - > mem_db ; m = mm . next ) {
mm = * m ;
/* Only probe < 1 MB */
if ( mm . base > = 0x100000 )
continue ;
if ( ( mm . base | mm . num ) & 0xffff ) {
ok + = do_mem_probe ( mm . base , mm . num , s ) ;
continue ;
}
/* Special probe for 64K-aligned block */
for ( i = 0 ; i < 4 ; i + + ) {
b = order [ i ] < < 12 ;
if ( ( b > = mm . base ) & & ( b + 0x10000 < = mm . base + mm . num ) ) {
if ( ok > = mem_limit )
sub_interval ( & s_data - > mem_db , b , 0x10000 ) ;
else
ok + = do_mem_probe ( b , 0x10000 , s ) ;
}
}
2005-04-17 02:20:36 +04:00
}
2005-09-28 21:41:56 +04:00
if ( ok > 0 )
return 0 ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
# else /* CONFIG_PCMCIA_PROBE */
2005-11-17 08:29:26 +03:00
static int validate_mem ( struct pcmcia_socket * s , unsigned int probe_mask )
2005-04-17 02:20:36 +04:00
{
struct resource_map * m , mm ;
struct socket_data * s_data = s - > resource_data ;
2005-11-17 08:29:26 +03:00
unsigned long ok = 0 ;
2005-04-17 02:20:36 +04:00
for ( m = s_data - > mem_db . next ; m ! = & s_data - > mem_db ; m = mm . next ) {
mm = * m ;
2005-11-17 08:29:26 +03:00
ok + = do_mem_probe ( mm . base , mm . num , s ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 08:29:26 +03:00
if ( ok > 0 )
return 0 ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
# endif /* CONFIG_PCMCIA_PROBE */
/*
2006-01-10 23:20:36 +03:00
* Locking note : Must be called with skt_mutex held !
2005-04-17 02:20:36 +04:00
*/
2005-09-28 21:41:56 +04:00
static int pcmcia_nonstatic_validate_mem ( struct pcmcia_socket * s )
2005-04-17 02:20:36 +04:00
{
struct socket_data * s_data = s - > resource_data ;
2005-09-28 21:41:56 +04:00
unsigned int probe_mask = MEM_PROBE_LOW ;
int ret = 0 ;
if ( ! probe_mem )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-09-28 21:41:56 +04:00
if ( s - > features & SS_CAP_PAGE_REGS )
probe_mask = MEM_PROBE_HIGH ;
2005-04-17 02:20:36 +04:00
2005-09-28 21:41:56 +04:00
if ( probe_mask & ~ s_data - > rsrc_mem_probe ) {
if ( s - > state & SOCKET_PRESENT )
ret = validate_mem ( s , probe_mask ) ;
if ( ! ret )
2005-04-17 02:20:36 +04:00
s_data - > rsrc_mem_probe | = probe_mask ;
2005-09-28 21:41:56 +04:00
}
2005-04-17 02:20:36 +04:00
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-09-28 21:41:56 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
struct pcmcia_align_data {
unsigned long mask ;
unsigned long offset ;
struct resource_map * map ;
} ;
static void
pcmcia_common_align ( void * align_data , struct resource * res ,
2006-06-13 04:07:52 +04:00
resource_size_t size , resource_size_t align )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_align_data * data = align_data ;
2006-06-13 04:07:52 +04:00
resource_size_t start ;
2005-04-17 02:20:36 +04:00
/*
* Ensure that we have the correct start address
*/
start = ( res - > start & ~ data - > mask ) + data - > offset ;
if ( start < res - > start )
start + = data - > mask + 1 ;
res - > start = start ;
}
static void
2006-06-13 04:07:52 +04:00
pcmcia_align ( void * align_data , struct resource * res , resource_size_t size ,
resource_size_t align )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_align_data * data = align_data ;
struct resource_map * m ;
pcmcia_common_align ( data , res , size , align ) ;
for ( m = data - > map - > next ; m ! = data - > map ; m = m - > next ) {
unsigned long start = m - > base ;
unsigned long end = m - > base + m - > num - 1 ;
/*
* If the lower resources are not available , try aligning
* to this entry of the resource database to see if it ' ll
* fit here .
*/
if ( res - > start < start ) {
res - > start = start ;
pcmcia_common_align ( data , res , size , align ) ;
}
/*
* If we ' re above the area which was passed in , there ' s
* no point proceeding .
*/
if ( res - > start > = res - > end )
break ;
if ( ( res - > start + size - 1 ) < = end )
break ;
}
/*
* If we failed to find something suitable , ensure we fail .
*/
if ( m = = data - > map )
res - > start = res - > end ;
}
/*
* Adjust an existing IO region allocation , but making sure that we don ' t
* encroach outside the resources which the user supplied .
*/
static int nonstatic_adjust_io_region ( struct resource * res , unsigned long r_start ,
unsigned long r_end , struct pcmcia_socket * s )
{
struct resource_map * m ;
struct socket_data * s_data = s - > resource_data ;
int ret = - ENOMEM ;
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
for ( m = s_data - > io_db . next ; m ! = & s_data - > io_db ; m = m - > next ) {
unsigned long start = m - > base ;
unsigned long end = m - > base + m - > num - 1 ;
if ( start > r_start | | r_end > end )
continue ;
ret = adjust_resource ( res , r_start , r_end - r_start + 1 ) ;
break ;
}
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
/*======================================================================
These find ranges of I / O ports or memory addresses that are not
currently allocated by other devices .
The ' align ' field should reflect the number of bits of address
that need to be preserved from the initial value of * base . It
should be a power of two , greater than or equal to ' num ' . A value
of 0 means that all bits of * base are significant . * base should
also be strictly less than ' align ' .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2005-06-28 03:28:15 +04:00
static struct resource * nonstatic_find_io_region ( unsigned long base , int num ,
2005-04-17 02:20:36 +04:00
unsigned long align , struct pcmcia_socket * s )
{
2006-09-12 19:00:10 +04:00
struct resource * res = make_resource ( 0 , num , IORESOURCE_IO , s - > dev . bus_id ) ;
2005-04-17 02:20:36 +04:00
struct socket_data * s_data = s - > resource_data ;
struct pcmcia_align_data data ;
unsigned long min = base ;
int ret ;
if ( align = = 0 )
align = 0x10000 ;
data . mask = align - 1 ;
data . offset = base & data . mask ;
data . map = & s_data - > io_db ;
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PCI
if ( s - > cb_dev ) {
ret = pci_bus_alloc_resource ( s - > cb_dev - > bus , res , num , 1 ,
min , 0 , pcmcia_align , & data ) ;
} else
# endif
ret = allocate_resource ( & ioport_resource , res , num , min , ~ 0UL ,
1 , pcmcia_align , & data ) ;
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ret ! = 0 ) {
kfree ( res ) ;
res = NULL ;
}
return res ;
}
2005-06-28 03:28:15 +04:00
static struct resource * nonstatic_find_mem_region ( u_long base , u_long num ,
u_long align , int low , struct pcmcia_socket * s )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct resource * res = make_resource ( 0 , num , IORESOURCE_MEM , s - > dev . bus_id ) ;
2005-04-17 02:20:36 +04:00
struct socket_data * s_data = s - > resource_data ;
struct pcmcia_align_data data ;
unsigned long min , max ;
int ret , i ;
low = low | | ! ( s - > features & SS_CAP_PAGE_REGS ) ;
data . mask = align - 1 ;
data . offset = base & data . mask ;
data . map = & s_data - > mem_db ;
for ( i = 0 ; i < 2 ; i + + ) {
if ( low ) {
max = 0x100000UL ;
min = base < max ? base : 0 ;
} else {
max = ~ 0UL ;
min = 0x100000UL + base ;
}
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PCI
if ( s - > cb_dev ) {
ret = pci_bus_alloc_resource ( s - > cb_dev - > bus , res , num ,
1 , min , 0 ,
pcmcia_align , & data ) ;
} else
# endif
ret = allocate_resource ( & iomem_resource , res , num , min ,
max , 1 , pcmcia_align , & data ) ;
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = 0 | | low )
break ;
low = 1 ;
}
if ( ret ! = 0 ) {
kfree ( res ) ;
res = NULL ;
}
return res ;
}
2005-06-28 03:28:46 +04:00
static int adjust_memory ( struct pcmcia_socket * s , unsigned int action , unsigned long start , unsigned long end )
2005-04-17 02:20:36 +04:00
{
struct socket_data * data = s - > resource_data ;
2005-06-28 03:28:46 +04:00
unsigned long size = end - start + 1 ;
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2005-09-28 21:28:37 +04:00
if ( end < start )
2005-06-28 03:28:46 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-06-28 03:28:46 +04:00
switch ( action ) {
2005-04-17 02:20:36 +04:00
case ADD_MANAGED_RESOURCE :
2005-06-28 03:28:46 +04:00
ret = add_interval ( & data - > mem_db , start , size ) ;
2005-04-17 02:20:36 +04:00
break ;
case REMOVE_MANAGED_RESOURCE :
2005-06-28 03:28:46 +04:00
ret = sub_interval ( & data - > mem_db , start , size ) ;
if ( ! ret ) {
2005-04-17 02:20:36 +04:00
struct pcmcia_socket * socket ;
down_read ( & pcmcia_socket_list_rwsem ) ;
list_for_each_entry ( socket , & pcmcia_socket_list , socket_list )
release_cis_mem ( socket ) ;
up_read ( & pcmcia_socket_list_rwsem ) ;
}
break ;
default :
2005-06-28 03:28:46 +04:00
ret = - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-06-28 03:28:46 +04:00
static int adjust_io ( struct pcmcia_socket * s , unsigned int action , unsigned long start , unsigned long end )
2005-04-17 02:20:36 +04:00
{
struct socket_data * data = s - > resource_data ;
2005-06-28 03:28:46 +04:00
unsigned long size = end - start + 1 ;
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2005-09-28 21:28:37 +04:00
if ( end < start )
2005-06-28 03:28:46 +04:00
return - EINVAL ;
if ( end > IO_SPACE_LIMIT )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-06-28 03:28:46 +04:00
switch ( action ) {
2005-04-17 02:20:36 +04:00
case ADD_MANAGED_RESOURCE :
2005-06-28 03:28:46 +04:00
if ( add_interval ( & data - > io_db , start , size ) ! = 0 ) {
ret = - EBUSY ;
2005-04-17 02:20:36 +04:00
break ;
}
# ifdef CONFIG_PCMCIA_PROBE
if ( probe_io )
2005-06-28 03:28:46 +04:00
do_io_probe ( s , start , size ) ;
2005-04-17 02:20:36 +04:00
# endif
break ;
case REMOVE_MANAGED_RESOURCE :
2005-06-28 03:28:46 +04:00
sub_interval ( & data - > io_db , start , size ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
2005-06-28 03:28:46 +04:00
ret = - EINVAL ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static int nonstatic_adjust_resource_info ( struct pcmcia_socket * s , adjust_t * adj )
{
2005-06-28 03:28:46 +04:00
unsigned long end ;
2005-04-17 02:20:36 +04:00
switch ( adj - > Resource ) {
case RES_MEMORY_RANGE :
2005-06-28 03:28:46 +04:00
end = adj - > resource . memory . Base + adj - > resource . memory . Size - 1 ;
return adjust_memory ( s , adj - > Action , adj - > resource . memory . Base , end ) ;
2005-04-17 02:20:36 +04:00
case RES_IO_RANGE :
2005-06-28 03:28:46 +04:00
end = adj - > resource . io . BasePort + adj - > resource . io . NumPorts - 1 ;
return adjust_io ( s , adj - > Action , adj - > resource . io . BasePort , end ) ;
2005-04-17 02:20:36 +04:00
}
return CS_UNSUPPORTED_FUNCTION ;
}
2005-06-28 03:28:46 +04:00
# ifdef CONFIG_PCI
static int nonstatic_autoadd_resources ( struct pcmcia_socket * s )
{
struct resource * res ;
int i , done = 0 ;
if ( ! s - > cb_dev | | ! s - > cb_dev - > bus )
return - ENODEV ;
2005-10-31 01:59:20 +03:00
# if defined(CONFIG_X86)
2005-06-28 03:29:02 +04:00
/* If this is the root bus, the risk of hitting
* some strange system devices which aren ' t protected
* by either ACPI resource tables or properly requested
* resources is too big . Therefore , don ' t do auto - adding
* of resources at the moment .
*/
if ( s - > cb_dev - > bus - > number = = 0 )
return - EINVAL ;
# endif
2005-06-28 03:28:46 +04:00
for ( i = 0 ; i < PCI_BUS_NUM_RESOURCES ; i + + ) {
res = s - > cb_dev - > bus - > resource [ i ] ;
if ( ! res )
continue ;
if ( res - > flags & IORESOURCE_IO ) {
if ( res = = & ioport_resource )
continue ;
2006-06-13 02:17:34 +04:00
printk ( KERN_INFO " pcmcia: parent PCI bridge I/O "
" window: 0x%llx - 0x%llx \n " ,
( unsigned long long ) res - > start ,
( unsigned long long ) res - > end ) ;
2005-06-28 03:28:46 +04:00
if ( ! adjust_io ( s , ADD_MANAGED_RESOURCE , res - > start , res - > end ) )
done | = IORESOURCE_IO ;
}
if ( res - > flags & IORESOURCE_MEM ) {
if ( res = = & iomem_resource )
continue ;
2006-06-13 02:17:34 +04:00
printk ( KERN_INFO " pcmcia: parent PCI bridge Memory "
" window: 0x%llx - 0x%llx \n " ,
( unsigned long long ) res - > start ,
( unsigned long long ) res - > end ) ;
2005-06-28 03:28:46 +04:00
if ( ! adjust_memory ( s , ADD_MANAGED_RESOURCE , res - > start , res - > end ) )
done | = IORESOURCE_MEM ;
}
}
/* if we got at least one of IO, and one of MEM, we can be glad and
* activate the PCMCIA subsystem */
2005-09-28 21:29:59 +04:00
if ( done = = ( IORESOURCE_MEM | IORESOURCE_IO ) )
2005-06-28 03:28:46 +04:00
s - > resource_setup_done = 1 ;
return 0 ;
}
# else
static inline int nonstatic_autoadd_resources ( struct pcmcia_socket * s )
{
return - ENODEV ;
}
# endif
2005-04-17 02:20:36 +04:00
static int nonstatic_init ( struct pcmcia_socket * s )
{
struct socket_data * data ;
2005-12-11 23:18:26 +03:00
data = kzalloc ( sizeof ( struct socket_data ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
return - ENOMEM ;
data - > mem_db . next = & data - > mem_db ;
data - > io_db . next = & data - > io_db ;
s - > resource_data = ( void * ) data ;
2005-06-28 03:28:46 +04:00
nonstatic_autoadd_resources ( s ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void nonstatic_release_resource_db ( struct pcmcia_socket * s )
{
struct socket_data * data = s - > resource_data ;
struct resource_map * p , * q ;
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
for ( p = data - > mem_db . next ; p ! = & data - > mem_db ; p = q ) {
q = p - > next ;
kfree ( p ) ;
}
for ( p = data - > io_db . next ; p ! = & data - > io_db ; p = q ) {
q = p - > next ;
kfree ( p ) ;
}
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
}
struct pccard_resource_ops pccard_nonstatic_ops = {
. validate_mem = pcmcia_nonstatic_validate_mem ,
. adjust_io_region = nonstatic_adjust_io_region ,
. find_io = nonstatic_find_io_region ,
. find_mem = nonstatic_find_mem_region ,
. adjust_resource = nonstatic_adjust_resource_info ,
. init = nonstatic_init ,
. exit = nonstatic_release_resource_db ,
} ;
EXPORT_SYMBOL ( pccard_nonstatic_ops ) ;
/* sysfs interface to the resource database */
2006-09-12 19:00:10 +04:00
static ssize_t show_io_db ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * s = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
struct socket_data * data ;
struct resource_map * p ;
ssize_t ret = 0 ;
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
data = s - > resource_data ;
for ( p = data - > io_db . next ; p ! = & data - > io_db ; p = p - > next ) {
if ( ret > ( PAGE_SIZE - 10 ) )
continue ;
ret + = snprintf ( & buf [ ret ] , ( PAGE_SIZE - ret - 1 ) ,
" 0x%08lx - 0x%08lx \n " ,
( ( unsigned long ) p - > base ) ,
( ( unsigned long ) p - > base + p - > num - 1 ) ) ;
}
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
return ( ret ) ;
}
2006-09-12 19:00:10 +04:00
static ssize_t store_io_db ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * s = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long start_addr , end_addr ;
2005-06-28 03:28:46 +04:00
unsigned int add = ADD_MANAGED_RESOURCE ;
2005-04-17 02:20:36 +04:00
ssize_t ret = 0 ;
ret = sscanf ( buf , " + 0x%lx - 0x%lx " , & start_addr , & end_addr ) ;
if ( ret ! = 2 ) {
ret = sscanf ( buf , " - 0x%lx - 0x%lx " , & start_addr , & end_addr ) ;
2005-06-28 03:28:46 +04:00
add = REMOVE_MANAGED_RESOURCE ;
2005-04-17 02:20:36 +04:00
if ( ret ! = 2 ) {
ret = sscanf ( buf , " 0x%lx - 0x%lx " , & start_addr , & end_addr ) ;
2005-06-28 03:28:46 +04:00
add = ADD_MANAGED_RESOURCE ;
2005-04-17 02:20:36 +04:00
if ( ret ! = 2 )
return - EINVAL ;
}
}
2005-09-28 21:28:37 +04:00
if ( end_addr < start_addr )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2005-06-28 03:28:46 +04:00
ret = adjust_io ( s , add , start_addr , end_addr ) ;
2005-06-28 03:28:46 +04:00
if ( ! ret )
s - > resource_setup_new = 1 ;
2005-04-17 02:20:36 +04:00
return ret ? ret : count ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( available_resources_io , 0600 , show_io_db , store_io_db ) ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
static ssize_t show_mem_db ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * s = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
struct socket_data * data ;
struct resource_map * p ;
ssize_t ret = 0 ;
2006-01-10 23:20:36 +03:00
mutex_lock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
data = s - > resource_data ;
for ( p = data - > mem_db . next ; p ! = & data - > mem_db ; p = p - > next ) {
if ( ret > ( PAGE_SIZE - 10 ) )
continue ;
ret + = snprintf ( & buf [ ret ] , ( PAGE_SIZE - ret - 1 ) ,
" 0x%08lx - 0x%08lx \n " ,
( ( unsigned long ) p - > base ) ,
( ( unsigned long ) p - > base + p - > num - 1 ) ) ;
}
2006-01-10 23:20:36 +03:00
mutex_unlock ( & rsrc_mutex ) ;
2005-04-17 02:20:36 +04:00
return ( ret ) ;
}
2006-09-12 19:00:10 +04:00
static ssize_t store_mem_db ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * s = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long start_addr , end_addr ;
2005-06-28 03:28:46 +04:00
unsigned int add = ADD_MANAGED_RESOURCE ;
2005-04-17 02:20:36 +04:00
ssize_t ret = 0 ;
ret = sscanf ( buf , " + 0x%lx - 0x%lx " , & start_addr , & end_addr ) ;
if ( ret ! = 2 ) {
ret = sscanf ( buf , " - 0x%lx - 0x%lx " , & start_addr , & end_addr ) ;
2005-06-28 03:28:46 +04:00
add = REMOVE_MANAGED_RESOURCE ;
2005-04-17 02:20:36 +04:00
if ( ret ! = 2 ) {
ret = sscanf ( buf , " 0x%lx - 0x%lx " , & start_addr , & end_addr ) ;
2005-06-28 03:28:46 +04:00
add = ADD_MANAGED_RESOURCE ;
2005-04-17 02:20:36 +04:00
if ( ret ! = 2 )
return - EINVAL ;
}
}
2005-09-28 21:28:37 +04:00
if ( end_addr < start_addr )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2005-06-28 03:28:46 +04:00
ret = adjust_memory ( s , add , start_addr , end_addr ) ;
2005-06-28 03:28:46 +04:00
if ( ! ret )
s - > resource_setup_new = 1 ;
2005-04-17 02:20:36 +04:00
return ret ? ret : count ;
}
2006-09-12 19:00:10 +04:00
static DEVICE_ATTR ( available_resources_mem , 0600 , show_mem_db , store_mem_db ) ;
2005-04-17 02:20:36 +04:00
2006-09-12 19:00:10 +04:00
static struct device_attribute * pccard_rsrc_attributes [ ] = {
& dev_attr_available_resources_io ,
& dev_attr_available_resources_mem ,
2005-04-17 02:20:36 +04:00
NULL ,
} ;
2006-09-12 19:00:10 +04:00
static int __devinit pccard_sysfs_add_rsrc ( struct device * dev ,
2005-09-15 11:01:36 +04:00
struct class_interface * class_intf )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * s = dev_get_drvdata ( dev ) ;
struct device_attribute * * attr ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
if ( s - > resource_ops ! = & pccard_nonstatic_ops )
return 0 ;
for ( attr = pccard_rsrc_attributes ; * attr ; attr + + ) {
2006-09-12 19:00:10 +04:00
ret = device_create_file ( dev , * attr ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
break ;
}
return ret ;
}
2006-09-12 19:00:10 +04:00
static void __devexit pccard_sysfs_remove_rsrc ( struct device * dev ,
2005-09-15 11:01:36 +04:00
struct class_interface * class_intf )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * s = dev_get_drvdata ( dev ) ;
struct device_attribute * * attr ;
2005-04-17 02:20:36 +04:00
if ( s - > resource_ops ! = & pccard_nonstatic_ops )
return ;
for ( attr = pccard_rsrc_attributes ; * attr ; attr + + )
2006-09-12 19:00:10 +04:00
device_remove_file ( dev , * attr ) ;
2005-04-17 02:20:36 +04:00
}
static struct class_interface pccard_rsrc_interface = {
. class = & pcmcia_socket_class ,
2006-09-12 19:00:10 +04:00
. add_dev = & pccard_sysfs_add_rsrc ,
. remove_dev = __devexit_p ( & pccard_sysfs_remove_rsrc ) ,
2005-04-17 02:20:36 +04:00
} ;
static int __init nonstatic_sysfs_init ( void )
{
return class_interface_register ( & pccard_rsrc_interface ) ;
}
static void __exit nonstatic_sysfs_exit ( void )
{
class_interface_unregister ( & pccard_rsrc_interface ) ;
}
module_init ( nonstatic_sysfs_init ) ;
module_exit ( nonstatic_sysfs_exit ) ;