2005-04-17 02:20:36 +04:00
/*
* drivers / s390 / cio / airq . c
2008-01-26 16:10:44 +03:00
* Support for adapter interruptions
2005-04-17 02:20:36 +04:00
*
2008-01-26 16:10:44 +03:00
* Copyright IBM Corp . 1999 , 2007
* Author ( s ) : Ingo Adlung < adlung @ de . ibm . com >
* Cornelia Huck < cornelia . huck @ de . ibm . com >
* Arnd Bergmann < arndb @ de . ibm . com >
* Peter Oberparleiter < peter . oberparleiter @ de . ibm . com >
2005-04-17 02:20:36 +04:00
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/rcupdate.h>
2008-01-26 16:10:44 +03:00
# include <asm/airq.h>
# include "cio.h"
2005-04-17 02:20:36 +04:00
# include "cio_debug.h"
2008-01-26 16:10:44 +03:00
# define NR_AIRQS 32
# define NR_AIRQS_PER_WORD sizeof(unsigned long)
# define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD)
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:44 +03:00
union indicator_t {
unsigned long word [ NR_AIRQ_WORDS ] ;
unsigned char byte [ NR_AIRQS ] ;
} __attribute__ ( ( packed ) ) ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:44 +03:00
struct airq_t {
adapter_int_handler_t handler ;
void * drv_data ;
} ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:44 +03:00
static union indicator_t indicators ;
static struct airq_t * airqs [ NR_AIRQS ] ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:44 +03:00
static int register_airq ( struct airq_t * airq )
{
int i ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:44 +03:00
for ( i = 0 ; i < NR_AIRQS ; i + + )
if ( ! cmpxchg ( & airqs [ i ] , NULL , airq ) )
return i ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:10:44 +03:00
/**
* s390_register_adapter_interrupt ( ) - register adapter interrupt handler
* @ handler : adapter handler to be registered
* @ drv_data : driver data passed with each call to the handler
*
* Returns :
* Pointer to the indicator to be used on success
* ERR_PTR ( ) if registration failed
*/
void * s390_register_adapter_interrupt ( adapter_int_handler_t handler ,
void * drv_data )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:10:44 +03:00
struct airq_t * airq ;
char dbf_txt [ 16 ] ;
2005-04-17 02:20:36 +04:00
int ret ;
2008-01-26 16:10:44 +03:00
airq = kmalloc ( sizeof ( struct airq_t ) , GFP_KERNEL ) ;
if ( ! airq ) {
ret = - ENOMEM ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:10:44 +03:00
airq - > handler = handler ;
airq - > drv_data = drv_data ;
ret = register_airq ( airq ) ;
if ( ret < 0 )
kfree ( airq ) ;
out :
snprintf ( dbf_txt , sizeof ( dbf_txt ) , " rairq:%d " , ret ) ;
CIO_TRACE_EVENT ( 4 , dbf_txt ) ;
if ( ret < 0 )
return ERR_PTR ( ret ) ;
else
return & indicators . byte [ ret ] ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:10:44 +03:00
EXPORT_SYMBOL ( s390_register_adapter_interrupt ) ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:44 +03:00
/**
* s390_unregister_adapter_interrupt - unregister adapter interrupt handler
* @ ind : indicator for which the handler is to be unregistered
*/
void s390_unregister_adapter_interrupt ( void * ind )
2005-04-17 02:20:36 +04:00
{
2008-01-26 16:10:44 +03:00
struct airq_t * airq ;
char dbf_txt [ 16 ] ;
int i ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:44 +03:00
i = ( int ) ( ( addr_t ) ind ) - ( ( addr_t ) & indicators . byte [ 0 ] ) ;
snprintf ( dbf_txt , sizeof ( dbf_txt ) , " urairq:%d " , i ) ;
CIO_TRACE_EVENT ( 4 , dbf_txt ) ;
indicators . byte [ i ] = 0 ;
airq = xchg ( & airqs [ i ] , NULL ) ;
/*
* Allow interrupts to complete . This will ensure that the airq handle
* is no longer referenced by any interrupt handler .
*/
synchronize_sched ( ) ;
kfree ( airq ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:10:44 +03:00
EXPORT_SYMBOL ( s390_unregister_adapter_interrupt ) ;
# define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:44 +03:00
void do_adapter_IO ( void )
{
int w ;
int i ;
unsigned long word ;
struct airq_t * airq ;
/*
* Access indicator array in word - sized chunks to minimize storage
* fetch operations .
*/
for ( w = 0 ; w < NR_AIRQ_WORDS ; w + + ) {
word = indicators . word [ w ] ;
i = w * NR_AIRQS_PER_WORD ;
/*
* Check bytes within word for active indicators .
*/
while ( word ) {
if ( word & INDICATOR_MASK ) {
airq = airqs [ i ] ;
if ( likely ( airq ) )
airq - > handler ( & indicators . byte [ i ] ,
airq - > drv_data ) ;
else
/*
* Reset ill - behaved indicator .
*/
indicators . byte [ i ] = 0 ;
}
word < < = 8 ;
i + + ;
}
}
}