2005-04-16 15:20:36 -07:00
/*
* arch / s390 / kernel / s390_ext . c
*
* S390 version
* Copyright ( C ) 1999 , 2000 IBM Deutschland Entwicklung GmbH , IBM Corporation
* Author ( s ) : Holger Smolinski ( Holger . Smolinski @ de . ibm . com ) ,
* Martin Schwidefsky ( schwidefsky @ de . ibm . com )
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
2009-06-12 10:26:46 +02:00
# include <linux/ftrace.h>
2005-04-16 15:20:36 -07:00
# include <linux/errno.h>
# include <linux/kernel_stat.h>
# include <linux/interrupt.h>
2009-06-12 10:26:21 +02:00
# include <asm/cputime.h>
2005-04-16 15:20:36 -07:00
# include <asm/lowcore.h>
# include <asm/s390_ext.h>
2006-10-06 16:38:35 +02:00
# include <asm/irq_regs.h>
2005-04-16 15:20:36 -07:00
# include <asm/irq.h>
2008-04-17 07:46:26 +02:00
# include "entry.h"
2005-04-16 15:20:36 -07:00
/*
* ext_int_hash [ index ] is the start of the list for all external interrupts
* that hash to this index . With the current set of external interrupts
* ( 0x1202 external call , 0x1004 cpu timer , 0x2401 hwc console , 0x4000
* iucv and 0x2603 pfault ) this is always the first element .
*/
2006-07-12 16:41:55 +02:00
ext_int_info_t * ext_int_hash [ 256 ] = { NULL , } ;
2005-04-16 15:20:36 -07:00
2005-07-27 11:45:00 -07:00
static inline int ext_hash ( __u16 code )
{
return ( code + ( code > > 9 ) ) & 0xff ;
}
2005-04-16 15:20:36 -07:00
int register_external_interrupt ( __u16 code , ext_int_handler_t handler )
{
ext_int_info_t * p ;
int index ;
2006-12-13 00:35:56 -08:00
p = kmalloc ( sizeof ( ext_int_info_t ) , GFP_ATOMIC ) ;
2005-04-16 15:20:36 -07:00
if ( p = = NULL )
return - ENOMEM ;
p - > code = code ;
p - > handler = handler ;
2005-07-27 11:45:00 -07:00
index = ext_hash ( code ) ;
2005-04-16 15:20:36 -07:00
p - > next = ext_int_hash [ index ] ;
ext_int_hash [ index ] = p ;
return 0 ;
}
int register_early_external_interrupt ( __u16 code , ext_int_handler_t handler ,
ext_int_info_t * p )
{
int index ;
if ( p = = NULL )
return - EINVAL ;
p - > code = code ;
p - > handler = handler ;
2005-07-27 11:45:00 -07:00
index = ext_hash ( code ) ;
2005-04-16 15:20:36 -07:00
p - > next = ext_int_hash [ index ] ;
ext_int_hash [ index ] = p ;
return 0 ;
}
int unregister_external_interrupt ( __u16 code , ext_int_handler_t handler )
{
ext_int_info_t * p , * q ;
int index ;
2005-07-27 11:45:00 -07:00
index = ext_hash ( code ) ;
2005-04-16 15:20:36 -07:00
q = NULL ;
p = ext_int_hash [ index ] ;
while ( p ! = NULL ) {
if ( p - > code = = code & & p - > handler = = handler )
break ;
q = p ;
p = p - > next ;
}
if ( p = = NULL )
return - ENOENT ;
if ( q ! = NULL )
q - > next = p - > next ;
else
ext_int_hash [ index ] = p - > next ;
kfree ( p ) ;
return 0 ;
}
int unregister_early_external_interrupt ( __u16 code , ext_int_handler_t handler ,
ext_int_info_t * p )
{
ext_int_info_t * q ;
int index ;
if ( p = = NULL | | p - > code ! = code | | p - > handler ! = handler )
return - EINVAL ;
2005-07-27 11:45:00 -07:00
index = ext_hash ( code ) ;
2005-04-16 15:20:36 -07:00
q = ext_int_hash [ index ] ;
if ( p ! = q ) {
while ( q ! = NULL ) {
if ( q - > next = = p )
break ;
q = q - > next ;
}
if ( q = = NULL )
return - ENOENT ;
q - > next = p - > next ;
} else
ext_int_hash [ index ] = p - > next ;
return 0 ;
}
2009-06-12 10:26:46 +02:00
void __irq_entry do_extint ( struct pt_regs * regs , unsigned short code )
2005-04-16 15:20:36 -07:00
{
ext_int_info_t * p ;
int index ;
2006-10-06 16:38:35 +02:00
struct pt_regs * old_regs ;
2005-04-16 15:20:36 -07:00
2006-10-06 16:38:35 +02:00
old_regs = set_irq_regs ( regs ) ;
2008-04-17 07:46:23 +02:00
s390_idle_check ( ) ;
2008-12-31 15:11:41 +01:00
irq_enter ( ) ;
2008-04-17 07:46:25 +02:00
if ( S390_lowcore . int_clock > = S390_lowcore . clock_comparator )
/* Serve timer interrupts first. */
clock_comparator_work ( ) ;
2005-04-16 15:20:36 -07:00
kstat_cpu ( smp_processor_id ( ) ) . irqs [ EXTERNAL_INTERRUPT ] + + ;
2009-09-29 14:25:16 +02:00
if ( code ! = 0x1004 )
__get_cpu_var ( s390_idle ) . nohz_delay = 1 ;
2005-07-27 11:45:00 -07:00
index = ext_hash ( code ) ;
2005-04-16 15:20:36 -07:00
for ( p = ext_int_hash [ index ] ; p ; p = p - > next ) {
2007-02-05 21:18:19 +01:00
if ( likely ( p - > code = = code ) )
p - > handler ( code ) ;
2005-04-16 15:20:36 -07:00
}
irq_exit ( ) ;
2006-10-11 15:31:26 +02:00
set_irq_regs ( old_regs ) ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( register_external_interrupt ) ;
EXPORT_SYMBOL ( unregister_external_interrupt ) ;