2005-04-16 15:20:36 -07:00
/*
2012-07-20 11:15:04 +02:00
* Copyright IBM Corp . 2004 , 2011
2011-05-26 09:48:24 +02:00
* Author ( s ) : Martin Schwidefsky < schwidefsky @ de . ibm . com > ,
* Holger Smolinski < Holger . Smolinski @ de . ibm . com > ,
* Thomas Spatzier < tspat @ de . ibm . com > ,
2005-04-16 15:20:36 -07:00
*
* This file contains interrupt related functions .
*/
# include <linux/kernel_stat.h>
# include <linux/interrupt.h>
# include <linux/seq_file.h>
2007-02-05 21:16:44 +01:00
# include <linux/proc_fs.h>
# include <linux/profile.h>
2011-05-26 09:48:24 +02:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/ftrace.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/cpu.h>
# include <asm/irq_regs.h>
# include <asm/cputime.h>
# include <asm/lowcore.h>
# include <asm/irq.h>
# include "entry.h"
2005-04-16 15:20:36 -07:00
2013-01-02 15:18:18 +01:00
DEFINE_PER_CPU_SHARED_ALIGNED ( struct irq_stat , irq_stat ) ;
EXPORT_PER_CPU_SYMBOL_GPL ( irq_stat ) ;
2011-01-05 12:47:28 +01:00
struct irq_class {
char * name ;
char * desc ;
} ;
2013-01-02 15:18:18 +01:00
/*
2013-03-16 20:53:05 +09:00
* The list of " main " irq classes on s390 . This is the list of interrupts
2013-01-02 15:18:18 +01:00
* that appear both in / proc / stat ( " intr " line ) and / proc / interrupts .
* Historically only external and I / O interrupts have been part of / proc / stat .
* We can ' t add the split external and I / O sub classes since the first field
* in the " intr " line in / proc / stat is supposed to be the sum of all other
* fields .
* Since the external and I / O interrupt fields are already sums we would end
* up with having a sum which accounts each interrupt twice .
*/
static const struct irq_class irqclass_main_desc [ NR_IRQS ] = {
2012-09-17 06:05:16 +02:00
[ EXTERNAL_INTERRUPT ] = { . name = " EXT " } ,
2013-01-02 15:18:18 +01:00
[ IO_INTERRUPT ] = { . name = " I/O " }
} ;
/*
* The list of split external and I / O interrupts that appear only in
* / proc / interrupts .
* In addition this list contains non external / I / O events like NMIs .
*/
static const struct irq_class irqclass_sub_desc [ NR_ARCH_IRQS ] = {
[ IRQEXT_CLK ] = { . name = " CLK " , . desc = " [EXT] Clock Comparator " } ,
[ IRQEXT_EXC ] = { . name = " EXC " , . desc = " [EXT] External Call " } ,
[ IRQEXT_EMS ] = { . name = " EMS " , . desc = " [EXT] Emergency Signal " } ,
[ IRQEXT_TMR ] = { . name = " TMR " , . desc = " [EXT] CPU Timer " } ,
[ IRQEXT_TLA ] = { . name = " TAL " , . desc = " [EXT] Timing Alert " } ,
[ IRQEXT_PFL ] = { . name = " PFL " , . desc = " [EXT] Pseudo Page Fault " } ,
[ IRQEXT_DSD ] = { . name = " DSD " , . desc = " [EXT] DASD Diag " } ,
[ IRQEXT_VRT ] = { . name = " VRT " , . desc = " [EXT] Virtio " } ,
[ IRQEXT_SCP ] = { . name = " SCP " , . desc = " [EXT] Service Call " } ,
[ IRQEXT_IUC ] = { . name = " IUC " , . desc = " [EXT] IUCV " } ,
[ IRQEXT_CMS ] = { . name = " CMS " , . desc = " [EXT] CPU-Measurement: Sampling " } ,
[ IRQEXT_CMC ] = { . name = " CMC " , . desc = " [EXT] CPU-Measurement: Counter " } ,
[ IRQEXT_CMR ] = { . name = " CMR " , . desc = " [EXT] CPU-Measurement: RI " } ,
[ IRQIO_CIO ] = { . name = " CIO " , . desc = " [I/O] Common I/O Layer Interrupt " } ,
[ IRQIO_QAI ] = { . name = " QAI " , . desc = " [I/O] QDIO Adapter Interrupt " } ,
[ IRQIO_DAS ] = { . name = " DAS " , . desc = " [I/O] DASD " } ,
[ IRQIO_C15 ] = { . name = " C15 " , . desc = " [I/O] 3215 " } ,
[ IRQIO_C70 ] = { . name = " C70 " , . desc = " [I/O] 3270 " } ,
[ IRQIO_TAP ] = { . name = " TAP " , . desc = " [I/O] Tape " } ,
[ IRQIO_VMR ] = { . name = " VMR " , . desc = " [I/O] Unit Record Devices " } ,
[ IRQIO_LCS ] = { . name = " LCS " , . desc = " [I/O] LCS " } ,
[ IRQIO_CLW ] = { . name = " CLW " , . desc = " [I/O] CLAW " } ,
[ IRQIO_CTC ] = { . name = " CTC " , . desc = " [I/O] CTC " } ,
[ IRQIO_APB ] = { . name = " APB " , . desc = " [I/O] AP Bus " } ,
[ IRQIO_ADM ] = { . name = " ADM " , . desc = " [I/O] EADM Subchannel " } ,
[ IRQIO_CSC ] = { . name = " CSC " , . desc = " [I/O] CHSC Subchannel " } ,
[ IRQIO_PCI ] = { . name = " PCI " , . desc = " [I/O] PCI Interrupt " } ,
[ IRQIO_MSI ] = { . name = " MSI " , . desc = " [I/O] MSI Interrupt " } ,
2013-02-24 13:07:18 -08:00
[ IRQIO_VIR ] = { . name = " VIR " , . desc = " [I/O] Virtual I/O Devices " } ,
2012-09-17 06:05:16 +02:00
[ NMI_NMI ] = { . name = " NMI " , . desc = " [NMI] Machine Check " } ,
2013-01-02 16:54:12 +01:00
[ CPU_RST ] = { . name = " RST " , . desc = " [CPU] CPU Restart " } ,
2011-01-05 12:47:28 +01:00
} ;
2005-04-16 15:20:36 -07:00
/*
* show_interrupts is needed by / proc / interrupts .
*/
int show_interrupts ( struct seq_file * p , void * v )
{
2013-01-02 15:18:18 +01:00
int irq = * ( loff_t * ) v ;
int cpu ;
2005-04-16 15:20:36 -07:00
2008-05-15 16:52:39 +02:00
get_online_cpus ( ) ;
2013-01-02 15:18:18 +01:00
if ( irq = = 0 ) {
2005-04-16 15:20:36 -07:00
seq_puts ( p , " " ) ;
2013-01-02 15:18:18 +01:00
for_each_online_cpu ( cpu )
seq_printf ( p , " CPU%d " , cpu ) ;
2005-04-16 15:20:36 -07:00
seq_putc ( p , ' \n ' ) ;
}
2013-01-02 15:18:18 +01:00
if ( irq < NR_IRQS ) {
seq_printf ( p , " %s: " , irqclass_main_desc [ irq ] . name ) ;
for_each_online_cpu ( cpu )
seq_printf ( p , " %10u " , kstat_cpu ( cpu ) . irqs [ irq ] ) ;
seq_putc ( p , ' \n ' ) ;
goto skip_arch_irqs ;
}
for ( irq = 0 ; irq < NR_ARCH_IRQS ; irq + + ) {
seq_printf ( p , " %s: " , irqclass_sub_desc [ irq ] . name ) ;
for_each_online_cpu ( cpu )
seq_printf ( p , " %10u " , per_cpu ( irq_stat , cpu ) . irqs [ irq ] ) ;
if ( irqclass_sub_desc [ irq ] . desc )
seq_printf ( p , " %s " , irqclass_sub_desc [ irq ] . desc ) ;
seq_putc ( p , ' \n ' ) ;
}
skip_arch_irqs :
2008-05-15 16:52:39 +02:00
put_online_cpus ( ) ;
2013-01-02 15:18:18 +01:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
/*
* Switch to the asynchronous interrupt stack for softirq execution .
*/
asmlinkage void do_softirq ( void )
{
unsigned long flags , old , new ;
if ( in_interrupt ( ) )
return ;
local_irq_save ( flags ) ;
if ( local_softirq_pending ( ) ) {
/* Get current stack pointer. */
asm volatile ( " la %0,0(15) " : " = a " (old)) ;
/* Check against async. stack address range. */
new = S390_lowcore . async_stack ;
if ( ( ( new - old ) > > ( PAGE_SHIFT + THREAD_ORDER ) ) ! = 0 ) {
/* Need to switch to the async. stack. */
new - = STACK_FRAME_OVERHEAD ;
( ( struct stack_frame * ) new ) - > back_chain = old ;
asm volatile ( " la 15,0(%0) \n "
" basr 14,%2 \n "
" la 15,0(%1) \n "
: : " a " ( new ) , " a " ( old ) ,
" a " ( __do_softirq )
: " 0 " , " 1 " , " 2 " , " 3 " , " 4 " , " 5 " , " 14 " ,
" cc " , " memory " ) ;
2012-04-11 14:28:09 +02:00
} else {
2005-04-16 15:20:36 -07:00
/* We are already on the async stack. */
__do_softirq ( ) ;
2012-04-11 14:28:09 +02:00
}
2005-04-16 15:20:36 -07:00
}
local_irq_restore ( flags ) ;
}
2007-02-05 21:16:44 +01:00
2009-02-11 10:37:29 +01:00
# ifdef CONFIG_PROC_FS
2007-02-05 21:16:44 +01:00
void init_irq_proc ( void )
{
2013-04-01 20:48:34 -04:00
if ( proc_mkdir ( " irq " , NULL ) )
create_prof_cpu_mask ( ) ;
2007-02-05 21:16:44 +01:00
}
2009-02-11 10:37:29 +01:00
# endif
2011-05-26 09:48:24 +02:00
/*
2011-07-24 10:48:27 +02:00
* ext_int_hash [ index ] is the list head for all external interrupts that hash
* to this index .
2011-05-26 09:48:24 +02:00
*/
2011-07-24 10:48:27 +02:00
static struct list_head ext_int_hash [ 256 ] ;
2011-05-26 09:48:24 +02:00
struct ext_int_info {
ext_int_handler_t handler ;
u16 code ;
2011-07-24 10:48:27 +02:00
struct list_head entry ;
struct rcu_head rcu ;
2011-05-26 09:48:24 +02:00
} ;
2011-07-24 10:48:27 +02:00
/* ext_int_hash_lock protects the handler lists for external interrupts */
DEFINE_SPINLOCK ( ext_int_hash_lock ) ;
static void __init init_external_interrupts ( void )
{
int idx ;
for ( idx = 0 ; idx < ARRAY_SIZE ( ext_int_hash ) ; idx + + )
INIT_LIST_HEAD ( & ext_int_hash [ idx ] ) ;
}
2011-05-26 09:48:24 +02:00
static inline int ext_hash ( u16 code )
{
return ( code + ( code > > 9 ) ) & 0xff ;
}
int register_external_interrupt ( u16 code , ext_int_handler_t handler )
{
struct ext_int_info * p ;
2011-07-24 10:48:27 +02:00
unsigned long flags ;
2011-05-26 09:48:24 +02:00
int index ;
p = kmalloc ( sizeof ( * p ) , GFP_ATOMIC ) ;
if ( ! p )
return - ENOMEM ;
p - > code = code ;
p - > handler = handler ;
index = ext_hash ( code ) ;
2011-07-24 10:48:27 +02:00
spin_lock_irqsave ( & ext_int_hash_lock , flags ) ;
list_add_rcu ( & p - > entry , & ext_int_hash [ index ] ) ;
spin_unlock_irqrestore ( & ext_int_hash_lock , flags ) ;
2011-05-26 09:48:24 +02:00
return 0 ;
}
EXPORT_SYMBOL ( register_external_interrupt ) ;
int unregister_external_interrupt ( u16 code , ext_int_handler_t handler )
{
2011-07-24 10:48:27 +02:00
struct ext_int_info * p ;
unsigned long flags ;
int index = ext_hash ( code ) ;
2011-05-26 09:48:24 +02:00
2011-07-24 10:48:27 +02:00
spin_lock_irqsave ( & ext_int_hash_lock , flags ) ;
2012-04-11 14:28:09 +02:00
list_for_each_entry_rcu ( p , & ext_int_hash [ index ] , entry ) {
2011-07-24 10:48:27 +02:00
if ( p - > code = = code & & p - > handler = = handler ) {
list_del_rcu ( & p - > entry ) ;
2012-01-06 16:59:51 -08:00
kfree_rcu ( p , rcu ) ;
2011-07-24 10:48:27 +02:00
}
2012-04-11 14:28:09 +02:00
}
2011-07-24 10:48:27 +02:00
spin_unlock_irqrestore ( & ext_int_hash_lock , flags ) ;
2011-05-26 09:48:24 +02:00
return 0 ;
}
EXPORT_SYMBOL ( unregister_external_interrupt ) ;
2013-06-17 14:54:02 +02:00
void __irq_entry do_extint ( struct pt_regs * regs )
2011-05-26 09:48:24 +02:00
{
2013-06-17 14:54:02 +02:00
struct ext_code ext_code ;
2011-05-26 09:48:24 +02:00
struct pt_regs * old_regs ;
struct ext_int_info * p ;
int index ;
old_regs = set_irq_regs ( regs ) ;
irq_enter ( ) ;
2012-04-11 14:28:09 +02:00
if ( S390_lowcore . int_clock > = S390_lowcore . clock_comparator ) {
2011-05-26 09:48:24 +02:00
/* Serve timer interrupts first. */
clock_comparator_work ( ) ;
2012-04-11 14:28:09 +02:00
}
2013-01-02 14:01:23 +01:00
kstat_incr_irqs_this_cpu ( EXTERNAL_INTERRUPT , NULL ) ;
2013-06-17 14:54:02 +02:00
ext_code = * ( struct ext_code * ) & regs - > int_code ;
2012-03-11 11:59:31 -04:00
if ( ext_code . code ! = 0x1004 )
2011-05-26 09:48:24 +02:00
__get_cpu_var ( s390_idle ) . nohz_delay = 1 ;
2011-07-24 10:48:27 +02:00
2012-03-11 11:59:31 -04:00
index = ext_hash ( ext_code . code ) ;
2011-07-24 10:48:27 +02:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( p , & ext_int_hash [ index ] , entry )
2012-03-11 11:59:31 -04:00
if ( likely ( p - > code = = ext_code . code ) )
2013-06-17 14:54:02 +02:00
p - > handler ( ext_code , regs - > int_parm ,
regs - > int_parm_long ) ;
2011-07-24 10:48:27 +02:00
rcu_read_unlock ( ) ;
2011-05-26 09:48:24 +02:00
irq_exit ( ) ;
set_irq_regs ( old_regs ) ;
}
2011-07-24 10:48:27 +02:00
void __init init_IRQ ( void )
{
init_external_interrupts ( ) ;
}
2011-05-26 09:48:24 +02:00
static DEFINE_SPINLOCK ( sc_irq_lock ) ;
static int sc_irq_refcount ;
void service_subclass_irq_register ( void )
{
spin_lock ( & sc_irq_lock ) ;
if ( ! sc_irq_refcount )
ctl_set_bit ( 0 , 9 ) ;
sc_irq_refcount + + ;
spin_unlock ( & sc_irq_lock ) ;
}
EXPORT_SYMBOL ( service_subclass_irq_register ) ;
void service_subclass_irq_unregister ( void )
{
spin_lock ( & sc_irq_lock ) ;
sc_irq_refcount - - ;
if ( ! sc_irq_refcount )
ctl_clear_bit ( 0 , 9 ) ;
spin_unlock ( & sc_irq_lock ) ;
}
EXPORT_SYMBOL ( service_subclass_irq_unregister ) ;
2012-03-23 11:13:05 +01:00
static DEFINE_SPINLOCK ( ma_subclass_lock ) ;
static int ma_subclass_refcount ;
void measurement_alert_subclass_register ( void )
{
spin_lock ( & ma_subclass_lock ) ;
if ( ! ma_subclass_refcount )
ctl_set_bit ( 0 , 5 ) ;
ma_subclass_refcount + + ;
spin_unlock ( & ma_subclass_lock ) ;
}
EXPORT_SYMBOL ( measurement_alert_subclass_register ) ;
void measurement_alert_subclass_unregister ( void )
{
spin_lock ( & ma_subclass_lock ) ;
ma_subclass_refcount - - ;
if ( ! ma_subclass_refcount )
ctl_clear_bit ( 0 , 5 ) ;
spin_unlock ( & ma_subclass_lock ) ;
}
EXPORT_SYMBOL ( measurement_alert_subclass_unregister ) ;
2013-06-10 15:34:04 +02:00
2013-06-14 01:18:44 +01:00
# ifdef CONFIG_SMP
2013-06-10 15:34:04 +02:00
void synchronize_irq ( unsigned int irq )
{
/*
* Not needed , the handler is protected by a lock and IRQs that occur
* after the handler is deleted are just NOPs .
*/
}
EXPORT_SYMBOL_GPL ( synchronize_irq ) ;
2013-06-14 01:18:44 +01:00
# endif
2013-06-10 15:34:04 +02:00
# ifndef CONFIG_PCI
/* Only PCI devices have dynamically-defined IRQ handlers */
int request_irq ( unsigned int irq , irq_handler_t handler ,
unsigned long irqflags , const char * devname , void * dev_id )
{
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( request_irq ) ;
void free_irq ( unsigned int irq , void * dev_id )
{
WARN_ON ( 1 ) ;
}
EXPORT_SYMBOL_GPL ( free_irq ) ;
void enable_irq ( unsigned int irq )
{
WARN_ON ( 1 ) ;
}
EXPORT_SYMBOL_GPL ( enable_irq ) ;
void disable_irq ( unsigned int irq )
{
WARN_ON ( 1 ) ;
}
EXPORT_SYMBOL_GPL ( disable_irq ) ;
# endif /* !CONFIG_PCI */
void disable_irq_nosync ( unsigned int irq )
{
disable_irq ( irq ) ;
}
EXPORT_SYMBOL_GPL ( disable_irq_nosync ) ;
unsigned long probe_irq_on ( void )
{
return 0 ;
}
EXPORT_SYMBOL_GPL ( probe_irq_on ) ;
int probe_irq_off ( unsigned long val )
{
return 0 ;
}
EXPORT_SYMBOL_GPL ( probe_irq_off ) ;
unsigned int probe_irq_mask ( unsigned long val )
{
return val ;
}
EXPORT_SYMBOL_GPL ( probe_irq_mask ) ;