2005-04-16 15:20:36 -07:00
/*
2008-01-26 14:10:44 +01:00
* Support for adapter interruptions
2005-04-16 15:20:36 -07:00
*
2012-07-20 11:15:04 +02:00
* Copyright IBM Corp . 1999 , 2007
2008-01-26 14:10:44 +01:00
* 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-16 15:20:36 -07:00
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/rcupdate.h>
2008-01-26 14:10:44 +01:00
# include <asm/airq.h>
2008-07-14 09:58:59 +02:00
# include <asm/isc.h>
2008-01-26 14:10:44 +01:00
# include "cio.h"
2005-04-16 15:20:36 -07:00
# include "cio_debug.h"
2008-01-26 14:10:44 +01: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-16 15:20:36 -07:00
2008-01-26 14:10:44 +01:00
union indicator_t {
unsigned long word [ NR_AIRQ_WORDS ] ;
unsigned char byte [ NR_AIRQS ] ;
} __attribute__ ( ( packed ) ) ;
2005-04-16 15:20:36 -07:00
2008-01-26 14:10:44 +01:00
struct airq_t {
adapter_int_handler_t handler ;
void * drv_data ;
} ;
2005-04-16 15:20:36 -07:00
2009-03-26 15:24:12 +01:00
static union indicator_t indicators [ MAX_ISC + 1 ] ;
static struct airq_t * airqs [ MAX_ISC + 1 ] [ NR_AIRQS ] ;
2005-04-16 15:20:36 -07:00
2008-07-14 09:58:59 +02:00
static int register_airq ( struct airq_t * airq , u8 isc )
2008-01-26 14:10:44 +01:00
{
int i ;
2005-04-16 15:20:36 -07:00
2008-01-26 14:10:44 +01:00
for ( i = 0 ; i < NR_AIRQS ; i + + )
2008-07-14 09:58:59 +02:00
if ( ! cmpxchg ( & airqs [ isc ] [ i ] , NULL , airq ) )
2008-01-26 14:10:44 +01:00
return i ;
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
}
2008-01-26 14:10:44 +01: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
2008-07-14 09:58:59 +02:00
* @ isc : isc for which the handler should be called
2008-01-26 14:10:44 +01:00
*
* 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 ,
2008-07-14 09:58:59 +02:00
void * drv_data , u8 isc )
2005-04-16 15:20:36 -07:00
{
2008-01-26 14:10:44 +01:00
struct airq_t * airq ;
char dbf_txt [ 16 ] ;
2005-04-16 15:20:36 -07:00
int ret ;
2008-07-14 09:58:59 +02:00
if ( isc > MAX_ISC )
return ERR_PTR ( - EINVAL ) ;
2008-01-26 14:10:44 +01:00
airq = kmalloc ( sizeof ( struct airq_t ) , GFP_KERNEL ) ;
if ( ! airq ) {
ret = - ENOMEM ;
goto out ;
2005-04-16 15:20:36 -07:00
}
2008-01-26 14:10:44 +01:00
airq - > handler = handler ;
airq - > drv_data = drv_data ;
2008-07-14 09:58:59 +02:00
ret = register_airq ( airq , isc ) ;
2008-01-26 14:10:44 +01:00
out :
snprintf ( dbf_txt , sizeof ( dbf_txt ) , " rairq:%d " , ret ) ;
CIO_TRACE_EVENT ( 4 , dbf_txt ) ;
2008-07-14 09:58:59 +02:00
if ( ret < 0 ) {
kfree ( airq ) ;
2008-01-26 14:10:44 +01:00
return ERR_PTR ( ret ) ;
2008-07-14 09:58:59 +02:00
} else
return & indicators [ isc ] . byte [ ret ] ;
2005-04-16 15:20:36 -07:00
}
2008-01-26 14:10:44 +01:00
EXPORT_SYMBOL ( s390_register_adapter_interrupt ) ;
2005-04-16 15:20:36 -07:00
2008-01-26 14:10:44 +01:00
/**
* s390_unregister_adapter_interrupt - unregister adapter interrupt handler
* @ ind : indicator for which the handler is to be unregistered
2008-07-14 09:58:59 +02:00
* @ isc : interruption subclass
2008-01-26 14:10:44 +01:00
*/
2008-07-14 09:58:59 +02:00
void s390_unregister_adapter_interrupt ( void * ind , u8 isc )
2005-04-16 15:20:36 -07:00
{
2008-01-26 14:10:44 +01:00
struct airq_t * airq ;
char dbf_txt [ 16 ] ;
int i ;
2005-04-16 15:20:36 -07:00
2008-07-14 09:58:59 +02:00
i = ( int ) ( ( addr_t ) ind ) - ( ( addr_t ) & indicators [ isc ] . byte [ 0 ] ) ;
2008-01-26 14:10:44 +01:00
snprintf ( dbf_txt , sizeof ( dbf_txt ) , " urairq:%d " , i ) ;
CIO_TRACE_EVENT ( 4 , dbf_txt ) ;
2008-07-14 09:58:59 +02:00
indicators [ isc ] . byte [ i ] = 0 ;
airq = xchg ( & airqs [ isc ] [ i ] , NULL ) ;
2008-01-26 14:10:44 +01:00
/*
* 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-16 15:20:36 -07:00
}
2008-01-26 14:10:44 +01:00
EXPORT_SYMBOL ( s390_unregister_adapter_interrupt ) ;
# define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
2005-04-16 15:20:36 -07:00
2008-07-14 09:58:59 +02:00
void do_adapter_IO ( u8 isc )
2008-01-26 14:10:44 +01:00
{
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 + + ) {
2008-07-14 09:58:59 +02:00
word = indicators [ isc ] . word [ w ] ;
2008-01-26 14:10:44 +01:00
i = w * NR_AIRQS_PER_WORD ;
/*
* Check bytes within word for active indicators .
*/
while ( word ) {
if ( word & INDICATOR_MASK ) {
2008-07-14 09:58:59 +02:00
airq = airqs [ isc ] [ i ] ;
2009-03-26 15:24:10 +01:00
/* Make sure gcc reads from airqs only once. */
barrier ( ) ;
2008-01-26 14:10:44 +01:00
if ( likely ( airq ) )
2008-07-14 09:58:59 +02:00
airq - > handler ( & indicators [ isc ] . byte [ i ] ,
2008-01-26 14:10:44 +01:00
airq - > drv_data ) ;
else
/*
* Reset ill - behaved indicator .
*/
2008-07-14 09:58:59 +02:00
indicators [ isc ] . byte [ i ] = 0 ;
2008-01-26 14:10:44 +01:00
}
word < < = 8 ;
i + + ;
}
}
}