2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
* rsrc_nonstatic . c - - Resource management routines for ! SS_CAP_STATIC_MAP sockets
*
* 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>
2009-12-08 00:11:45 +03:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
# include <pcmcia/ss.h>
# include <pcmcia/cistpl.h>
# include "cs_internal.h"
2010-03-07 18:41:57 +03:00
/* moved to rsrc_mgr.c
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " David A. Hinds, Dominik Brodowski " ) ;
MODULE_LICENSE ( " GPL " ) ;
2010-03-07 18:41:57 +03:00
*/
2005-04-17 02:20:36 +04:00
/* 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 ;
2010-02-17 18:25:53 +03:00
struct resource_map mem_db_valid ;
2005-04-17 02:20:36 +04:00
struct resource_map io_db ;
} ;
# define MEM_PROBE_LOW (1 << 0)
# define MEM_PROBE_HIGH (1 << 1)
2010-07-24 14:33:29 +04:00
/* Action field */
# define REMOVE_MANAGED_RESOURCE 1
# define ADD_MANAGED_RESOURCE 2
2005-04-17 02:20:36 +04:00
/*======================================================================
Linux resource management extensions
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
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 ;
2010-03-07 18:41:57 +03:00
res = pcmcia_make_resource ( base , size , type | IORESOURCE_BUSY , name ) ;
2005-04-17 02:20:36 +04:00
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 )
{
2008-08-03 12:22:47 +04:00
struct resource_map * p , * q ;
2005-04-17 02:20:36 +04:00
2008-08-03 12:22:47 +04:00
for ( p = map ; ; p = p - > next ) {
2010-02-17 16:35:33 +03:00
if ( ( p ! = map ) & & ( p - > base + p - > num > = base ) ) {
p - > num = max ( num + base - p - > base , p - > num ) ;
return 0 ;
}
2008-08-03 12:22:47 +04:00
if ( ( p - > next = = map ) | | ( p - > next - > base > base + num - 1 ) )
break ;
}
q = kmalloc ( sizeof ( struct resource_map ) , GFP_KERNEL ) ;
if ( ! q ) {
printk ( KERN_WARNING " out of memory to update resources \n " ) ;
return - ENOMEM ;
}
q - > base = base ; q - > num = num ;
q - > next = p - > next ; p - > next = q ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*====================================================================*/
static int sub_interval ( struct resource_map * map , u_long base , u_long num )
{
2009-12-08 00:11:45 +03:00
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 ) {
printk ( KERN_WARNING " out of memory to update resources \n " ) ;
return - ENOMEM ;
}
p - > base = base + num ;
p - > num = q - > base + q - > num - p - > base ;
q - > num = base - q - > base ;
p - > next = q - > next ; q - > next = p ;
}
2008-08-03 12:22:47 +04:00
}
2009-12-08 00:11:45 +03:00
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*======================================================================
These routines examine a region of IO or memory addresses to
determine what ranges might be genuinely available .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# ifdef CONFIG_PCMCIA_PROBE
2008-02-05 09:27:35 +03:00
static void do_io_probe ( struct pcmcia_socket * s , unsigned int base ,
unsigned int num )
2005-04-17 02:20:36 +04:00
{
2009-12-08 00:11:45 +03:00
struct resource * res ;
struct socket_data * s_data = s - > resource_data ;
unsigned int i , j , bad ;
int any ;
u_char * b , hole , most ;
2014-10-10 20:12:47 +04:00
dev_info ( & s - > dev , " cs: IO port probe %#x-%#x: " , base , base + num - 1 ) ;
2009-12-08 00:11:45 +03:00
/* First, what does a floating port look like? */
b = kzalloc ( 256 , GFP_KERNEL ) ;
if ( ! b ) {
2014-10-10 20:12:47 +04:00
pr_cont ( " \n " ) ;
dev_err ( & s - > dev , " do_io_probe: unable to kmalloc 256 bytes \n " ) ;
2009-12-08 00:11:45 +03:00
return ;
}
for ( i = base , most = 0 ; i < base + num ; i + = 8 ) {
2010-04-08 21:23:07 +04:00
res = claim_region ( s , i , 8 , IORESOURCE_IO , " PCMCIA ioprobe " ) ;
2009-12-08 00:11:45 +03:00
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 ) {
2010-04-08 21:23:07 +04:00
res = claim_region ( s , i , 8 , IORESOURCE_IO , " PCMCIA ioprobe " ) ;
if ( ! res ) {
if ( ! any )
2014-10-10 20:12:47 +04:00
pr_cont ( " excluding " ) ;
2010-04-08 21:23:07 +04:00
if ( ! bad )
bad = any = i ;
2009-12-08 00:11:45 +03:00
continue ;
2010-04-08 21:23:07 +04:00
}
2009-12-08 00:11:45 +03:00
for ( j = 0 ; j < 8 ; j + + )
if ( inb ( i + j ) ! = most )
break ;
free_region ( res ) ;
if ( j < 8 ) {
if ( ! any )
2014-10-10 20:12:47 +04:00
pr_cont ( " excluding " ) ;
2009-12-08 00:11:45 +03:00
if ( ! bad )
bad = any = i ;
} else {
if ( bad ) {
sub_interval ( & s_data - > io_db , bad , i - bad ) ;
2014-10-10 20:12:47 +04:00
pr_cont ( " %#x-%#x " , bad , i - 1 ) ;
2009-12-08 00:11:45 +03:00
bad = 0 ;
}
}
}
if ( bad ) {
if ( ( num > 16 ) & & ( bad = = base ) & & ( i = = base + num ) ) {
2010-04-08 21:23:07 +04:00
sub_interval ( & s_data - > io_db , bad , i - bad ) ;
2014-10-10 20:12:47 +04:00
pr_cont ( " nothing: probe failed. \n " ) ;
2009-12-08 00:11:45 +03:00
return ;
} else {
sub_interval ( & s_data - > io_db , bad , i - bad ) ;
2014-10-10 20:12:47 +04:00
pr_cont ( " %#x-%#x " , bad , i - 1 ) ;
2009-12-08 00:11:45 +03:00
}
}
2014-10-10 20:12:47 +04:00
pr_cont ( " %s \n " , ! any ? " clean " : " " ) ;
2005-04-17 02:20:36 +04:00
}
# endif
2010-01-03 00:22:50 +03:00
/*======================================================================*/
2005-04-17 02:20:36 +04:00
2021-03-12 14:02:34 +03:00
/*
2010-01-03 00:22:50 +03:00
* readable ( ) - iomem validation function for cards with a valid CIS
*/
2008-06-19 22:12:34 +04:00
static int readable ( struct pcmcia_socket * s , struct resource * res ,
unsigned int * count )
2005-04-17 02:20:36 +04:00
{
2010-01-03 00:22:50 +03:00
int ret = - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-02-17 20:00:07 +03:00
if ( s - > fake_cis ) {
dev_dbg ( & s - > dev , " fake CIS is being used: can't validate mem \n " ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
s - > cis_mem . res = res ;
s - > cis_virt = ioremap ( res - > start , s - > map_size ) ;
if ( s - > cis_virt ) {
2010-01-12 23:42:51 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2010-01-06 15:57:43 +03:00
/* as we're only called from pcmcia.c, we're safe */
if ( s - > callback - > validate )
ret = s - > callback - > validate ( s , count ) ;
2010-01-02 14:28:04 +03:00
/* invalidate mapping */
2010-01-12 23:42:51 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
iounmap ( s - > cis_virt ) ;
s - > cis_virt = NULL ;
}
s - > cis_mem . res = NULL ;
2010-01-03 00:22:50 +03:00
if ( ( ret ) | | ( * count = = 0 ) )
return - EINVAL ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2021-03-12 14:02:34 +03:00
/*
2010-01-03 00:22:50 +03:00
* checksum ( ) - iomem validation function for simple memory cards
*/
static int checksum ( struct pcmcia_socket * s , struct resource * res ,
unsigned int * value )
2005-04-17 02:20:36 +04:00
{
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 ) ;
}
2010-01-03 00:22:50 +03:00
if ( b = = - 1 )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-01-03 00:22:50 +03:00
* value = a ;
2005-04-17 02:20:36 +04:00
2010-01-03 00:22:50 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-01-03 00:22:50 +03:00
/**
* do_validate_mem ( ) - low level validate a memory region for PCMCIA use
* @ s : PCMCIA socket to validate
* @ base : start address of resource to check
* @ size : size of resource to check
* @ validate : validation function to use
*
* do_validate_mem ( ) splits up the memory region which is to be checked
* into two parts . Both are passed to the @ validate ( ) function . If
* @ validate ( ) returns non - zero , or the value parameter to @ validate ( )
* is zero , or the value parameter is different between both calls ,
* the check fails , and - EINVAL is returned . Else , 0 is returned .
*/
static int do_validate_mem ( struct pcmcia_socket * s ,
unsigned long base , unsigned long size ,
2021-03-12 14:02:39 +03:00
int ( * validate ) ( struct pcmcia_socket * s ,
struct resource * res ,
unsigned int * value ) )
2005-04-17 02:20:36 +04:00
{
2010-02-17 18:25:53 +03:00
struct socket_data * s_data = s - > resource_data ;
2005-04-17 02:20:36 +04:00
struct resource * res1 , * res2 ;
2010-01-03 00:22:50 +03:00
unsigned int info1 = 1 , info2 = 1 ;
int ret = - EINVAL ;
2005-04-17 02:20:36 +04:00
2009-12-08 00:11:45 +03:00
res1 = claim_region ( s , base , size / 2 , IORESOURCE_MEM , " PCMCIA memprobe " ) ;
res2 = claim_region ( s , base + size / 2 , size / 2 , IORESOURCE_MEM ,
" PCMCIA memprobe " ) ;
2005-04-17 02:20:36 +04:00
if ( res1 & & res2 ) {
2010-01-03 00:22:50 +03:00
ret = 0 ;
if ( validate ) {
ret = validate ( s , res1 , & info1 ) ;
ret + = validate ( s , res2 , & info2 ) ;
}
2005-04-17 02:20:36 +04:00
}
2018-01-22 23:36:27 +03:00
dev_dbg ( & s - > dev , " cs: memory probe 0x%06lx-0x%06lx: %pr %pr %u %u %u " ,
2010-01-03 00:22:50 +03:00
base , base + size - 1 , res1 , res2 , ret , info1 , info2 ) ;
2005-04-17 02:20:36 +04:00
2013-01-21 02:14:43 +04:00
free_region ( res2 ) ;
free_region ( res1 ) ;
2010-01-03 00:22:50 +03:00
if ( ( ret ) | | ( info1 ! = info2 ) | | ( info1 = = 0 ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-02-17 18:25:53 +03:00
if ( validate & & ! s - > fake_cis ) {
/* move it to the validated data set */
add_interval ( & s_data - > mem_db_valid , base , size ) ;
sub_interval ( & s_data - > mem_db , base , size ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-03 00:22:50 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-01-03 00:22:50 +03:00
/**
* do_mem_probe ( ) - validate a memory region for PCMCIA use
* @ s : PCMCIA socket to validate
* @ base : start address of resource to check
* @ num : size of resource to check
* @ validate : validation function to use
* @ fallback : validation function to use if validate fails
*
* do_mem_probe ( ) checks a memory region for use by the PCMCIA subsystem .
* To do so , the area is split up into sensible parts , and then passed
* into the @ validate ( ) function . Only if @ validate ( ) and @ fallback ( ) fail ,
2022-01-27 10:15:09 +03:00
* the area is marked as unavailable for use by the PCMCIA subsystem . The
2010-01-03 00:22:50 +03:00
* function returns the size of the usable memory area .
*/
static int do_mem_probe ( struct pcmcia_socket * s , u_long base , u_long num ,
2021-03-12 14:02:39 +03:00
int ( * validate ) ( struct pcmcia_socket * s ,
struct resource * res ,
unsigned int * value ) ,
int ( * fallback ) ( struct pcmcia_socket * s ,
struct resource * res ,
unsigned int * value ) )
2005-04-17 02:20:36 +04:00
{
2009-12-08 00:11:45 +03:00
struct socket_data * s_data = s - > resource_data ;
u_long i , j , bad , fail , step ;
2014-10-10 20:12:47 +04:00
dev_info ( & s - > dev , " cs: memory probe 0x%06lx-0x%06lx: " ,
base , base + num - 1 ) ;
2009-12-08 00:11:45 +03:00
bad = fail = 0 ;
step = ( num < 0x20000 ) ? 0x2000 : ( ( num > > 4 ) & ~ 0x1fff ) ;
/* don't allow too large steps */
if ( step > 0x800000 )
step = 0x800000 ;
/* 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 ) {
2010-01-03 00:22:50 +03:00
if ( ! do_validate_mem ( s , j , step , validate ) )
2009-12-08 00:11:45 +03:00
break ;
}
fail = ( ( i = = base ) & & ( j = = base + num ) ) ;
}
2010-01-03 00:22:50 +03:00
if ( ( fail ) & & ( fallback ) ) {
for ( j = i ; j < base + num ; j + = step )
if ( ! do_validate_mem ( s , j , step , fallback ) )
2009-12-08 00:11:45 +03:00
break ;
}
if ( i ! = j ) {
if ( ! bad )
2014-10-10 20:12:47 +04:00
pr_cont ( " excluding " ) ;
pr_cont ( " %#05lx-%#05lx " , i , j - 1 ) ;
2009-12-08 00:11:45 +03:00
sub_interval ( & s_data - > mem_db , i , j - i ) ;
bad + = j - i ;
}
}
2014-10-10 20:12:47 +04:00
pr_cont ( " %s \n " , ! bad ? " clean " : " " ) ;
2009-12-08 00:11:45 +03:00
return num - bad ;
2005-04-17 02:20:36 +04:00
}
2010-01-03 00:22:50 +03:00
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PCMCIA_PROBE
2010-01-03 00:22:50 +03:00
/**
* inv_probe ( ) - top - to - bottom search for one usuable high memory area
* @ s : PCMCIA socket to validate
* @ m : resource_map to check
*/
2005-04-17 02:20:36 +04:00
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 ;
2010-01-03 00:22:50 +03:00
return do_mem_probe ( s , m - > base , m - > num , readable , checksum ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-03 00:22:50 +03:00
/**
* validate_mem ( ) - memory probe function
* @ s : PCMCIA socket to validate
* @ probe_mask : MEM_PROBE_LOW | MEM_PROBE_HIGH
*
* 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 . Returns 0 on usuable ports .
*/
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 ;
2010-02-17 18:25:53 +03:00
if ( s_data - > mem_db_valid . next ! = & s_data - > mem_db_valid )
return 0 ;
2014-10-10 20:12:47 +04:00
dev_notice ( & s - > dev ,
2008-08-02 23:36:19 +04:00
" cs: warning: no high memory space available! \n " ) ;
2005-09-28 21:41:56 +04:00
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 ) {
2010-01-03 00:22:50 +03:00
ok + = do_mem_probe ( s , mm . base , mm . num , readable ,
checksum ) ;
2005-09-28 21:41:56 +04:00
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
2010-01-03 00:22:50 +03:00
ok + = do_mem_probe ( s , b , 0x10000 ,
readable , checksum ) ;
2005-09-28 21:41:56 +04:00
}
}
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 */
2010-01-03 00:22:50 +03:00
/**
* validate_mem ( ) - memory probe function
* @ s : PCMCIA socket to validate
* @ probe_mask : ignored
*
* Returns 0 on usuable ports .
*/
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 ;
2010-01-03 00:22:50 +03:00
ok + = do_mem_probe ( s , mm . base , mm . num , readable , checksum ) ;
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 */
2010-01-03 00:22:50 +03:00
/**
* pcmcia_nonstatic_validate_mem ( ) - try to validate iomem for PCMCIA use
* @ s : PCMCIA socket to validate
*
* This is tricky . . . when we set up CIS memory , we try to validate
* the memory window space allocations .
*
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 ;
2010-02-17 18:25:53 +03:00
int ret ;
2005-09-28 21:41:56 +04:00
2010-02-17 18:25:53 +03:00
if ( ! probe_mem | | ! ( s - > state & SOCKET_PRESENT ) )
2005-09-28 21:41:56 +04:00
return 0 ;
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
2010-02-17 18:25:53 +03:00
ret = validate_mem ( s , probe_mask ) ;
2005-04-17 02:20:36 +04:00
2010-02-17 18:25:53 +03:00
if ( s_data - > mem_db_valid . next ! = & s_data - > mem_db_valid )
return 0 ;
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 ;
} ;
2010-04-04 20:10:35 +04:00
static resource_size_t pcmcia_common_align ( struct pcmcia_align_data * align_data ,
resource_size_t start )
2005-04-17 02:20:36 +04:00
{
2010-04-04 20:10:35 +04:00
resource_size_t ret ;
2005-04-17 02:20:36 +04:00
/*
* Ensure that we have the correct start address
*/
2010-04-04 20:10:35 +04:00
ret = ( start & ~ align_data - > mask ) + align_data - > offset ;
if ( ret < start )
ret + = align_data - > mask + 1 ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2010-01-01 19:40:49 +03:00
static resource_size_t
2010-01-01 19:40:50 +03:00
pcmcia_align ( void * align_data , const 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 ;
2010-01-01 19:40:49 +03:00
resource_size_t start ;
2005-04-17 02:20:36 +04:00
2010-04-04 20:10:35 +04:00
start = pcmcia_common_align ( data , res - > start ) ;
2005-04-17 02:20:36 +04:00
for ( m = data - > map - > next ; m ! = data - > map ; m = m - > next ) {
2010-04-04 20:10:35 +04:00
unsigned long map_start = m - > base ;
unsigned long map_end = m - > base + m - > num - 1 ;
2005-04-17 02:20:36 +04:00
/*
* If the lower resources are not available , try aligning
* to this entry of the resource database to see if it ' ll
* fit here .
*/
2010-04-04 20:10:35 +04:00
if ( start < map_start )
start = pcmcia_common_align ( data , map_start ) ;
2005-04-17 02:20:36 +04:00
/*
* If we ' re above the area which was passed in , there ' s
* no point proceeding .
*/
2010-04-04 20:10:35 +04:00
if ( start > = res - > end )
2005-04-17 02:20:36 +04:00
break ;
2010-04-04 20:10:35 +04:00
if ( ( start + size - 1 ) < = map_end )
2005-04-17 02:20:36 +04:00
break ;
}
/*
* If we failed to find something suitable , ensure we fail .
*/
if ( m = = data - > map )
2010-01-01 19:40:49 +03:00
start = res - > end ;
return start ;
2005-04-17 02:20:36 +04:00
}
/*
* Adjust an existing IO region allocation , but making sure that we don ' t
* encroach outside the resources which the user supplied .
*/
2010-03-20 15:10:47 +03:00
static int __nonstatic_adjust_io_region ( struct pcmcia_socket * s ,
unsigned long r_start ,
unsigned long r_end )
2005-04-17 02:20:36 +04:00
{
struct resource_map * m ;
struct socket_data * s_data = s - > resource_data ;
int ret = - ENOMEM ;
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 ;
2010-03-20 15:10:47 +03:00
ret = 0 ;
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 ' .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2010-03-20 15:10:47 +03:00
static struct resource * __nonstatic_find_io_region ( struct pcmcia_socket * s ,
unsigned long base , int num ,
unsigned long align )
2005-04-17 02:20:36 +04:00
{
2010-03-07 18:41:57 +03:00
struct resource * res = pcmcia_make_resource ( 0 , num , IORESOURCE_IO ,
dev_name ( & s - > dev ) ) ;
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 ;
2021-11-30 19:59:23 +03:00
if ( ! res )
return NULL ;
2005-04-17 02:20:36 +04:00
data . mask = align - 1 ;
data . offset = base & data . mask ;
data . map = & s_data - > io_db ;
# 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 ) ;
if ( ret ! = 0 ) {
kfree ( res ) ;
res = NULL ;
}
return res ;
}
2010-03-20 15:10:47 +03:00
static int nonstatic_find_io ( struct pcmcia_socket * s , unsigned int attr ,
unsigned int * base , unsigned int num ,
2010-07-25 15:10:22 +04:00
unsigned int align , struct resource * * parent )
2010-03-20 15:10:47 +03:00
{
int i , ret = 0 ;
/* Check for an already-allocated window that must conflict with
* what was asked for . It is a hack because it does not catch all
* potential conflicts , just the most obvious ones .
*/
for ( i = 0 ; i < MAX_IO_WIN ; i + + ) {
if ( ! s - > io [ i ] . res )
continue ;
if ( ! * base )
continue ;
if ( ( s - > io [ i ] . res - > start & ( align - 1 ) ) = = * base )
return - EBUSY ;
}
for ( i = 0 ; i < MAX_IO_WIN ; i + + ) {
struct resource * res = s - > io [ i ] . res ;
unsigned int try ;
if ( res & & ( res - > flags & IORESOURCE_BITS ) ! =
( attr & IORESOURCE_BITS ) )
continue ;
if ( ! res ) {
if ( align = = 0 )
align = 0x10000 ;
res = s - > io [ i ] . res = __nonstatic_find_io_region ( s ,
* base , num ,
align ) ;
if ( ! res )
return - EINVAL ;
* base = res - > start ;
s - > io [ i ] . res - > flags =
( ( res - > flags & ~ IORESOURCE_BITS ) |
( attr & IORESOURCE_BITS ) ) ;
s - > io [ i ] . InUse = num ;
2010-07-25 15:10:22 +04:00
* parent = res ;
2010-03-20 15:10:47 +03:00
return 0 ;
}
/* Try to extend top of window */
try = res - > end + 1 ;
if ( ( * base = = 0 ) | | ( * base = = try ) ) {
ret = __nonstatic_adjust_io_region ( s , res - > start ,
res - > end + num ) ;
if ( ! ret ) {
ret = adjust_resource ( s - > io [ i ] . res , res - > start ,
2011-06-09 20:13:32 +04:00
resource_size ( res ) + num ) ;
2010-03-20 15:10:47 +03:00
if ( ret )
continue ;
* base = try ;
s - > io [ i ] . InUse + = num ;
2010-07-25 15:10:22 +04:00
* parent = res ;
2010-03-20 15:10:47 +03:00
return 0 ;
}
}
/* Try to extend bottom of window */
try = res - > start - num ;
if ( ( * base = = 0 ) | | ( * base = = try ) ) {
ret = __nonstatic_adjust_io_region ( s ,
res - > start - num ,
res - > end ) ;
if ( ! ret ) {
ret = adjust_resource ( s - > io [ i ] . res ,
2011-06-09 20:13:32 +04:00
res - > start - num ,
resource_size ( res ) + num ) ;
2010-03-20 15:10:47 +03:00
if ( ret )
continue ;
* base = try ;
s - > io [ i ] . InUse + = num ;
2010-07-25 15:10:22 +04:00
* parent = res ;
2010-03-20 15:10:47 +03:00
return 0 ;
}
}
}
return - EINVAL ;
}
2009-12-08 00:11:45 +03:00
static struct resource * nonstatic_find_mem_region ( u_long base , u_long num ,
2005-06-28 03:28:15 +04:00
u_long align , int low , struct pcmcia_socket * s )
2005-04-17 02:20:36 +04:00
{
2010-03-07 18:41:57 +03:00
struct resource * res = pcmcia_make_resource ( 0 , num , IORESOURCE_MEM ,
dev_name ( & s - > dev ) ) ;
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 ;
2010-02-17 18:25:53 +03:00
int ret , i , j ;
2005-04-17 02:20:36 +04:00
2021-11-30 21:11:40 +03:00
if ( ! res )
return NULL ;
2005-04-17 02:20:36 +04:00
low = low | | ! ( s - > features & SS_CAP_PAGE_REGS ) ;
data . mask = align - 1 ;
data . offset = base & data . mask ;
for ( i = 0 ; i < 2 ; i + + ) {
2010-02-17 18:25:53 +03:00
data . map = & s_data - > mem_db_valid ;
2005-04-17 02:20:36 +04:00
if ( low ) {
max = 0x100000UL ;
min = base < max ? base : 0 ;
} else {
max = ~ 0UL ;
min = 0x100000UL + base ;
}
2010-02-17 18:25:53 +03:00
for ( j = 0 ; j < 2 ; j + + ) {
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PCI
2010-02-17 18:25:53 +03:00
if ( s - > cb_dev ) {
ret = pci_bus_alloc_resource ( s - > cb_dev - > bus ,
res , num , 1 , min , 0 ,
pcmcia_align , & data ) ;
} else
2005-04-17 02:20:36 +04:00
# endif
2010-02-17 18:25:53 +03:00
{
ret = allocate_resource ( & iomem_resource ,
res , num , min , max , 1 ,
pcmcia_align , & data ) ;
}
if ( ret = = 0 )
break ;
data . map = & s_data - > mem_db ;
}
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
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 ) ;
2010-01-03 00:22:50 +03:00
if ( ! ret )
do_mem_probe ( s , start , size , NULL , NULL ) ;
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 ) ;
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
}
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 ;
2010-04-15 21:01:53 +04:00
unsigned long size ;
2005-06-28 03:28:46 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2010-03-23 18:05:00 +03:00
# if defined(CONFIG_X86)
/* on x86, avoid anything < 0x100 for it is often used for
* legacy platform devices */
if ( start < 0x100 )
start = 0x100 ;
# endif
2010-04-15 21:01:53 +04:00
size = end - start + 1 ;
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
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 ;
}
return ret ;
}
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)
2010-03-29 23:40:35 +04:00
/* If this is the root bus, the risk of hitting some strange
* system devices is too high : If a driver isn ' t loaded , the
* resources are not claimed ; even if a driver is loaded , it
* may not request all resources or even the wrong one . We
* can neither trust the rest of the kernel nor ACPI / PNP and
* CRS parsing to get it right . Therefore , use several
* safeguards :
*
* - Do not auto - add resources if the CardBus bridge is on
* the PCI root bus
*
* - Avoid any I / O ports < 0x100 .
*
* - On PCI - PCI bridges , only use resources which are set up
* exclusively for the secondary PCI bus : the risk of hitting
* system devices is quite low , as they usually aren ' t
* connected to the secondary PCI bus .
2005-06-28 03:29:02 +04:00
*/
if ( s - > cb_dev - > bus - > number = = 0 )
return - EINVAL ;
2010-03-29 23:40:35 +04:00
for ( i = 0 ; i < PCI_BRIDGE_RESOURCE_NUM ; i + + ) {
res = s - > cb_dev - > bus - > resource [ i ] ;
# else
2010-02-23 20:24:31 +03:00
pci_bus_for_each_resource ( s - > cb_dev - > bus , res , i ) {
2010-03-29 23:40:35 +04:00
# endif
2005-06-28 03:28:46 +04:00
if ( ! res )
continue ;
if ( res - > flags & IORESOURCE_IO ) {
2010-03-29 23:40:35 +04:00
/* safeguard against the root resource, where the
* risk of hitting any other device would be too
* high */
2005-06-28 03:28:46 +04:00
if ( res = = & ioport_resource )
continue ;
2010-03-29 23:40:35 +04:00
2014-10-10 20:12:47 +04:00
dev_info ( & s - > cb_dev - > dev ,
" pcmcia: parent PCI bridge window: %pR \n " ,
res ) ;
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 ) {
2010-03-29 23:40:35 +04:00
/* safeguard against the root resource, where the
* risk of hitting any other device would be too
* high */
2005-06-28 03:28:46 +04:00
if ( res = = & iomem_resource )
continue ;
2010-03-29 23:40:35 +04:00
2014-10-10 20:12:47 +04:00
dev_info ( & s - > cb_dev - > dev ,
" pcmcia: parent PCI bridge window: %pR \n " ,
res ) ;
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 ;
2010-02-17 18:25:53 +03:00
data - > mem_db_valid . next = & data - > mem_db_valid ;
2005-04-17 02:20:36 +04:00
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 ;
2010-02-17 18:25:53 +03:00
for ( p = data - > mem_db_valid . next ; p ! = & data - > mem_db_valid ; p = q ) {
q = p - > next ;
kfree ( p ) ;
}
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 ) ;
}
2023-05-12 21:45:29 +03:00
kfree ( data ) ;
2005-04-17 02:20:36 +04:00
}
struct pccard_resource_ops pccard_nonstatic_ops = {
. validate_mem = pcmcia_nonstatic_validate_mem ,
2010-03-20 15:10:47 +03:00
. find_io = nonstatic_find_io ,
2005-04-17 02:20:36 +04:00
. find_mem = nonstatic_find_mem_region ,
. 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 ;
2010-01-17 21:31:45 +03:00
mutex_lock ( & s - > ops_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 ;
2021-10-15 10:13:23 +03:00
ret + = sysfs_emit_at ( buf , ret ,
2009-12-08 00:11:45 +03:00
" 0x%08lx - 0x%08lx \n " ,
( ( unsigned long ) p - > base ) ,
( ( unsigned long ) p - > base + p - > num - 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-17 21:31:45 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2009-12-08 00:11:45 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
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 ;
2009-12-08 00:11:45 +03:00
ret = sscanf ( buf , " + 0x%lx - 0x%lx " , & start_addr , & end_addr ) ;
2005-04-17 02:20:36 +04:00
if ( ret ! = 2 ) {
2009-12-08 00:11:45 +03:00
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 ) {
2009-12-08 00:11:45 +03:00
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 ;
2010-01-17 21:31:45 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2005-06-28 03:28:46 +04:00
ret = adjust_io ( s , add , start_addr , end_addr ) ;
2010-01-17 21:31:45 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
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 ;
2010-01-17 21:31:45 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2005-04-17 02:20:36 +04:00
data = s - > resource_data ;
2010-02-17 18:25:53 +03:00
for ( p = data - > mem_db_valid . next ; p ! = & data - > mem_db_valid ;
p = p - > next ) {
if ( ret > ( PAGE_SIZE - 10 ) )
continue ;
2021-10-15 10:13:23 +03:00
ret + = sysfs_emit_at ( buf , ret ,
2010-02-17 18:25:53 +03:00
" 0x%08lx - 0x%08lx \n " ,
( ( unsigned long ) p - > base ) ,
( ( unsigned long ) p - > base + p - > num - 1 ) ) ;
}
2005-04-17 02:20:36 +04:00
for ( p = data - > mem_db . next ; p ! = & data - > mem_db ; p = p - > next ) {
if ( ret > ( PAGE_SIZE - 10 ) )
continue ;
2021-10-15 10:13:23 +03:00
ret + = sysfs_emit_at ( buf , ret ,
2009-12-08 00:11:45 +03:00
" 0x%08lx - 0x%08lx \n " ,
( ( unsigned long ) p - > base ) ,
( ( unsigned long ) p - > base + p - > num - 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-17 21:31:45 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
2009-12-08 00:11:45 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
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 ;
2009-12-08 00:11:45 +03:00
ret = sscanf ( buf , " + 0x%lx - 0x%lx " , & start_addr , & end_addr ) ;
2005-04-17 02:20:36 +04:00
if ( ret ! = 2 ) {
2009-12-08 00:11:45 +03:00
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 ) {
2009-12-08 00:11:45 +03:00
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 ;
2010-01-17 21:31:45 +03:00
mutex_lock ( & s - > ops_mutex ) ;
2005-06-28 03:28:46 +04:00
ret = adjust_memory ( s , add , start_addr , end_addr ) ;
2010-01-17 21:31:45 +03:00
mutex_unlock ( & s - > ops_mutex ) ;
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
2008-06-12 23:13:55 +04:00
static struct attribute * pccard_rsrc_attributes [ ] = {
& dev_attr_available_resources_io . attr ,
& dev_attr_available_resources_mem . attr ,
2005-04-17 02:20:36 +04:00
NULL ,
} ;
2008-06-12 23:13:55 +04:00
static const struct attribute_group rsrc_attributes = {
. attrs = pccard_rsrc_attributes ,
} ;
2023-04-02 20:58:49 +03:00
static int pccard_sysfs_add_rsrc ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2006-09-12 19:00:10 +04:00
struct pcmcia_socket * s = dev_get_drvdata ( dev ) ;
2008-06-12 23:13:55 +04:00
2005-04-17 02:20:36 +04:00
if ( s - > resource_ops ! = & pccard_nonstatic_ops )
return 0 ;
2008-06-12 23:13:55 +04:00
return sysfs_create_group ( & dev - > kobj , & rsrc_attributes ) ;
2005-04-17 02:20:36 +04:00
}
2023-04-02 20:58:49 +03:00
static void pccard_sysfs_remove_rsrc ( struct device * dev )
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
if ( s - > resource_ops ! = & pccard_nonstatic_ops )
return ;
2008-06-12 23:13:55 +04:00
sysfs_remove_group ( & dev - > kobj , & rsrc_attributes ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-01 15:34:50 +04:00
static struct class_interface pccard_rsrc_interface __refdata = {
2005-04-17 02:20:36 +04:00
. class = & pcmcia_socket_class ,
2006-09-12 19:00:10 +04:00
. add_dev = & pccard_sysfs_add_rsrc ,
2012-11-19 22:20:38 +04:00
. remove_dev = & 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 ) ;