2005-04-16 15:20:36 -07:00
/*
2011-01-05 12:47:41 +01:00
* Copyright IBM Corp . 1999 , 2010
* Author ( s ) : Holger Smolinski < Holger . Smolinski @ de . ibm . com > ,
* Martin Schwidefsky < schwidefsky @ de . ibm . com > ,
2005-04-16 15:20:36 -07:00
*/
2011-01-05 12:47:41 +01:00
# include <linux/kernel_stat.h>
# include <linux/interrupt.h>
2005-04-16 15:20:36 -07:00
# include <linux/module.h>
# include <linux/kernel.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>
2011-01-05 12:47:41 +01:00
# include <linux/slab.h>
2005-04-16 15:20:36 -07:00
# include <asm/s390_ext.h>
2006-10-06 16:38:35 +02:00
# include <asm/irq_regs.h>
2011-01-05 12:47:41 +01:00
# include <asm/cputime.h>
# include <asm/lowcore.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
2011-01-05 12:47:41 +01:00
struct ext_int_info {
struct ext_int_info * next ;
ext_int_handler_t handler ;
__u16 code ;
} ;
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 .
*/
2011-01-05 12:47:41 +01:00
static struct ext_int_info * ext_int_hash [ 256 ] ;
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 )
{
2011-01-05 12:47:41 +01:00
struct ext_int_info * p ;
int index ;
2005-04-16 15:20:36 -07:00
2011-01-05 12:47:41 +01:00
p = kmalloc ( sizeof ( * p ) , GFP_ATOMIC ) ;
if ( ! p )
return - ENOMEM ;
p - > code = code ;
p - > handler = handler ;
2005-07-27 11:45:00 -07:00
index = ext_hash ( code ) ;
2011-01-05 12:47:41 +01:00
p - > next = ext_int_hash [ index ] ;
ext_int_hash [ index ] = p ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2011-01-05 12:47:41 +01:00
EXPORT_SYMBOL ( register_external_interrupt ) ;
2005-04-16 15:20:36 -07:00
int unregister_external_interrupt ( __u16 code , ext_int_handler_t handler )
{
2011-01-05 12:47:41 +01:00
struct ext_int_info * p , * q ;
2005-04-16 15:20:36 -07:00
int index ;
2005-07-27 11:45:00 -07:00
index = ext_hash ( code ) ;
2011-01-05 12:47:41 +01:00
q = NULL ;
p = ext_int_hash [ index ] ;
while ( p ) {
if ( p - > code = = code & & p - > handler = = handler )
break ;
q = p ;
p = p - > next ;
}
if ( ! p )
return - ENOENT ;
if ( q )
2005-04-16 15:20:36 -07:00
q - > next = p - > next ;
2011-01-05 12:47:41 +01:00
else
2005-04-16 15:20:36 -07:00
ext_int_hash [ index ] = p - > next ;
2011-01-05 12:47:41 +01:00
kfree ( p ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2011-01-05 12:47:41 +01:00
EXPORT_SYMBOL ( unregister_external_interrupt ) ;
2005-04-16 15:20:36 -07:00
2010-10-25 16:10:38 +02:00
void __irq_entry do_extint ( struct pt_regs * regs , unsigned int ext_int_code ,
unsigned int param32 , unsigned long param64 )
2005-04-16 15:20:36 -07:00
{
2010-10-25 16:10:38 +02:00
struct pt_regs * old_regs ;
unsigned short code ;
2011-01-05 12:47:41 +01:00
struct ext_int_info * p ;
int index ;
2005-04-16 15:20:36 -07:00
2010-10-25 16:10:38 +02:00
code = ( unsigned short ) ext_int_code ;
2006-10-06 16:38:35 +02:00
old_regs = set_irq_regs ( regs ) ;
2010-05-17 10:00:03 +02:00
s390_idle_check ( regs , S390_lowcore . int_clock ,
S390_lowcore . async_enter_timer ) ;
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 ;
2011-01-05 12:47:41 +01: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 ) )
2010-10-25 16:10:38 +02:00
p - > handler ( ext_int_code , param32 , param64 ) ;
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
}