2005-04-17 02:20:36 +04:00
/*
2012-07-20 13:15:04 +04:00
* Copyright IBM Corp . 2004 , 2011
2011-05-26 11:48:24 +04:00
* Author ( s ) : Martin Schwidefsky < schwidefsky @ de . ibm . com > ,
* Holger Smolinski < Holger . Smolinski @ de . ibm . com > ,
* Thomas Spatzier < tspat @ de . ibm . com > ,
2005-04-17 02:20:36 +04:00
*
* This file contains interrupt related functions .
*/
# include <linux/kernel_stat.h>
# include <linux/interrupt.h>
# include <linux/seq_file.h>
2007-02-05 23:16:44 +03:00
# include <linux/proc_fs.h>
# include <linux/profile.h>
2011-05-26 11:48:24 +04: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>
2014-03-05 02:57:29 +04:00
# include <linux/irq.h>
2011-05-26 11:48:24 +04:00
# include <asm/irq_regs.h>
# include <asm/cputime.h>
# include <asm/lowcore.h>
# include <asm/irq.h>
2013-06-27 11:01:09 +04:00
# include <asm/hw_irq.h>
2011-05-26 11:48:24 +04:00
# include "entry.h"
2005-04-17 02:20:36 +04:00
2013-01-02 18:18:18 +04:00
DEFINE_PER_CPU_SHARED_ALIGNED ( struct irq_stat , irq_stat ) ;
EXPORT_PER_CPU_SYMBOL_GPL ( irq_stat ) ;
2011-01-05 14:47:28 +03:00
struct irq_class {
2014-07-22 18:58:52 +04:00
int irq ;
2011-01-05 14:47:28 +03:00
char * name ;
char * desc ;
} ;
2013-01-02 18:18:18 +04:00
/*
2013-03-16 15:53:05 +04:00
* The list of " main " irq classes on s390 . This is the list of interrupts
2013-01-02 18:18:18 +04: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 .
*/
2013-06-27 11:01:09 +04:00
static const struct irq_class irqclass_main_desc [ NR_IRQS_BASE ] = {
2014-07-22 18:58:52 +04:00
{ . irq = EXT_INTERRUPT , . name = " EXT " } ,
{ . irq = IO_INTERRUPT , . name = " I/O " } ,
{ . irq = THIN_INTERRUPT , . name = " AIO " } ,
2013-01-02 18:18:18 +04:00
} ;
/*
* 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 ] = {
2014-07-22 18:58:52 +04:00
{ . irq = IRQEXT_CLK , . name = " CLK " , . desc = " [EXT] Clock Comparator " } ,
{ . irq = IRQEXT_EXC , . name = " EXC " , . desc = " [EXT] External Call " } ,
{ . irq = IRQEXT_EMS , . name = " EMS " , . desc = " [EXT] Emergency Signal " } ,
{ . irq = IRQEXT_TMR , . name = " TMR " , . desc = " [EXT] CPU Timer " } ,
{ . irq = IRQEXT_TLA , . name = " TAL " , . desc = " [EXT] Timing Alert " } ,
{ . irq = IRQEXT_PFL , . name = " PFL " , . desc = " [EXT] Pseudo Page Fault " } ,
{ . irq = IRQEXT_DSD , . name = " DSD " , . desc = " [EXT] DASD Diag " } ,
{ . irq = IRQEXT_VRT , . name = " VRT " , . desc = " [EXT] Virtio " } ,
{ . irq = IRQEXT_SCP , . name = " SCP " , . desc = " [EXT] Service Call " } ,
{ . irq = IRQEXT_IUC , . name = " IUC " , . desc = " [EXT] IUCV " } ,
{ . irq = IRQEXT_CMS , . name = " CMS " , . desc = " [EXT] CPU-Measurement: Sampling " } ,
{ . irq = IRQEXT_CMC , . name = " CMC " , . desc = " [EXT] CPU-Measurement: Counter " } ,
{ . irq = IRQEXT_CMR , . name = " CMR " , . desc = " [EXT] CPU-Measurement: RI " } ,
{ . irq = IRQIO_CIO , . name = " CIO " , . desc = " [I/O] Common I/O Layer Interrupt " } ,
{ . irq = IRQIO_QAI , . name = " QAI " , . desc = " [I/O] QDIO Adapter Interrupt " } ,
{ . irq = IRQIO_DAS , . name = " DAS " , . desc = " [I/O] DASD " } ,
{ . irq = IRQIO_C15 , . name = " C15 " , . desc = " [I/O] 3215 " } ,
{ . irq = IRQIO_C70 , . name = " C70 " , . desc = " [I/O] 3270 " } ,
{ . irq = IRQIO_TAP , . name = " TAP " , . desc = " [I/O] Tape " } ,
{ . irq = IRQIO_VMR , . name = " VMR " , . desc = " [I/O] Unit Record Devices " } ,
{ . irq = IRQIO_LCS , . name = " LCS " , . desc = " [I/O] LCS " } ,
{ . irq = IRQIO_CLW , . name = " CLW " , . desc = " [I/O] CLAW " } ,
{ . irq = IRQIO_CTC , . name = " CTC " , . desc = " [I/O] CTC " } ,
{ . irq = IRQIO_APB , . name = " APB " , . desc = " [I/O] AP Bus " } ,
{ . irq = IRQIO_ADM , . name = " ADM " , . desc = " [I/O] EADM Subchannel " } ,
{ . irq = IRQIO_CSC , . name = " CSC " , . desc = " [I/O] CHSC Subchannel " } ,
{ . irq = IRQIO_PCI , . name = " PCI " , . desc = " [I/O] PCI Interrupt " } ,
{ . irq = IRQIO_MSI , . name = " MSI " , . desc = " [I/O] MSI Interrupt " } ,
{ . irq = IRQIO_VIR , . name = " VIR " , . desc = " [I/O] Virtual I/O Devices " } ,
{ . irq = IRQIO_VAI , . name = " VAI " , . desc = " [I/O] Virtual I/O Devices AI " } ,
{ . irq = NMI_NMI , . name = " NMI " , . desc = " [NMI] Machine Check " } ,
{ . irq = CPU_RST , . name = " RST " , . desc = " [CPU] CPU Restart " } ,
2011-01-05 14:47:28 +03:00
} ;
2013-06-27 11:01:09 +04:00
void __init init_IRQ ( void )
{
init_cio_interrupts ( ) ;
init_airq_interrupts ( ) ;
init_ext_interrupts ( ) ;
}
void do_IRQ ( struct pt_regs * regs , int irq )
{
struct pt_regs * old_regs ;
old_regs = set_irq_regs ( regs ) ;
irq_enter ( ) ;
if ( S390_lowcore . int_clock > = S390_lowcore . clock_comparator )
/* Serve timer interrupts first. */
clock_comparator_work ( ) ;
generic_handle_irq ( irq ) ;
irq_exit ( ) ;
set_irq_regs ( old_regs ) ;
}
2005-04-17 02:20:36 +04:00
/*
* show_interrupts is needed by / proc / interrupts .
*/
int show_interrupts ( struct seq_file * p , void * v )
{
2014-07-22 18:58:52 +04:00
int index = * ( loff_t * ) v ;
int cpu , irq ;
2005-04-17 02:20:36 +04:00
2008-05-15 18:52:39 +04:00
get_online_cpus ( ) ;
2014-07-22 18:58:52 +04:00
if ( index = = 0 ) {
2005-04-17 02:20:36 +04:00
seq_puts ( p , " " ) ;
2013-01-02 18:18:18 +04:00
for_each_online_cpu ( cpu )
seq_printf ( p , " CPU%d " , cpu ) ;
2005-04-17 02:20:36 +04:00
seq_putc ( p , ' \n ' ) ;
2013-06-27 11:01:09 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2014-07-22 18:58:52 +04:00
if ( index < NR_IRQS ) {
if ( index > = NR_IRQS_BASE )
2013-06-27 11:01:09 +04:00
goto out ;
2014-07-22 18:58:52 +04:00
/* Adjust index to process irqclass_main_desc array entries */
index - - ;
seq_printf ( p , " %s: " , irqclass_main_desc [ index ] . name ) ;
irq = irqclass_main_desc [ index ] . irq ;
2013-01-02 18:18:18 +04:00
for_each_online_cpu ( cpu )
2013-06-27 11:01:09 +04:00
seq_printf ( p , " %10u " , kstat_irqs_cpu ( irq , cpu ) ) ;
2013-01-02 18:18:18 +04:00
seq_putc ( p , ' \n ' ) ;
2013-06-27 11:01:09 +04:00
goto out ;
2013-01-02 18:18:18 +04:00
}
2014-07-22 18:58:52 +04:00
for ( index = 0 ; index < NR_ARCH_IRQS ; index + + ) {
seq_printf ( p , " %s: " , irqclass_sub_desc [ index ] . name ) ;
irq = irqclass_sub_desc [ index ] . irq ;
2013-01-02 18:18:18 +04:00
for_each_online_cpu ( cpu )
2013-06-27 11:01:09 +04:00
seq_printf ( p , " %10u " ,
per_cpu ( irq_stat , cpu ) . irqs [ irq ] ) ;
2014-07-22 18:58:52 +04:00
if ( irqclass_sub_desc [ index ] . desc )
seq_printf ( p , " %s " , irqclass_sub_desc [ index ] . desc ) ;
2013-01-02 18:18:18 +04:00
seq_putc ( p , ' \n ' ) ;
}
2013-06-27 11:01:09 +04:00
out :
2008-05-15 18:52:39 +04:00
put_online_cpus ( ) ;
2013-01-02 18:18:18 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2014-05-07 19:44:20 +04:00
unsigned int arch_dynirq_lower_bound ( unsigned int from )
{
return from < THIN_INTERRUPT ? THIN_INTERRUPT : from ;
}
2005-04-17 02:20:36 +04:00
/*
* Switch to the asynchronous interrupt stack for softirq execution .
*/
2013-09-05 17:49:45 +04:00
void do_softirq_own_stack ( void )
2005-04-17 02:20:36 +04:00
{
2013-09-05 17:49:45 +04:00
unsigned long old , new ;
/* 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 " ) ;
} else {
/* We are already on the async stack. */
__do_softirq ( ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-02-05 23:16:44 +03:00
2011-05-26 11:48:24 +04:00
/*
2011-07-24 12:48:27 +04:00
* ext_int_hash [ index ] is the list head for all external interrupts that hash
* to this index .
2011-05-26 11:48:24 +04:00
*/
2013-09-09 09:44:43 +04:00
static struct hlist_head ext_int_hash [ 32 ] ____cacheline_aligned ;
2011-05-26 11:48:24 +04:00
struct ext_int_info {
ext_int_handler_t handler ;
2013-09-03 16:12:07 +04:00
struct hlist_node entry ;
2011-07-24 12:48:27 +04:00
struct rcu_head rcu ;
2013-09-03 16:12:07 +04:00
u16 code ;
2011-05-26 11:48:24 +04:00
} ;
2011-07-24 12:48:27 +04:00
/* ext_int_hash_lock protects the handler lists for external interrupts */
2013-09-06 21:10:48 +04:00
static DEFINE_SPINLOCK ( ext_int_hash_lock ) ;
2011-07-24 12:48:27 +04:00
2011-05-26 11:48:24 +04:00
static inline int ext_hash ( u16 code )
{
2013-09-09 09:44:43 +04:00
BUILD_BUG_ON ( ! is_power_of_2 ( ARRAY_SIZE ( ext_int_hash ) ) ) ;
return ( code + ( code > > 9 ) ) & ( ARRAY_SIZE ( ext_int_hash ) - 1 ) ;
2011-05-26 11:48:24 +04:00
}
2014-03-31 17:24:08 +04:00
int register_external_irq ( u16 code , ext_int_handler_t handler )
2011-05-26 11:48:24 +04:00
{
struct ext_int_info * p ;
2011-07-24 12:48:27 +04:00
unsigned long flags ;
2011-05-26 11:48:24 +04: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 12:48:27 +04:00
spin_lock_irqsave ( & ext_int_hash_lock , flags ) ;
2013-09-03 16:12:07 +04:00
hlist_add_head_rcu ( & p - > entry , & ext_int_hash [ index ] ) ;
2011-07-24 12:48:27 +04:00
spin_unlock_irqrestore ( & ext_int_hash_lock , flags ) ;
2011-05-26 11:48:24 +04:00
return 0 ;
}
2014-03-31 17:24:08 +04:00
EXPORT_SYMBOL ( register_external_irq ) ;
2011-05-26 11:48:24 +04:00
2014-03-31 17:24:08 +04:00
int unregister_external_irq ( u16 code , ext_int_handler_t handler )
2011-05-26 11:48:24 +04:00
{
2011-07-24 12:48:27 +04:00
struct ext_int_info * p ;
unsigned long flags ;
int index = ext_hash ( code ) ;
2011-05-26 11:48:24 +04:00
2011-07-24 12:48:27 +04:00
spin_lock_irqsave ( & ext_int_hash_lock , flags ) ;
2013-09-03 16:12:07 +04:00
hlist_for_each_entry_rcu ( p , & ext_int_hash [ index ] , entry ) {
2011-07-24 12:48:27 +04:00
if ( p - > code = = code & & p - > handler = = handler ) {
2013-09-03 16:12:07 +04:00
hlist_del_rcu ( & p - > entry ) ;
2012-01-07 04:59:51 +04:00
kfree_rcu ( p , rcu ) ;
2011-07-24 12:48:27 +04:00
}
2012-04-11 16:28:09 +04:00
}
2011-07-24 12:48:27 +04:00
spin_unlock_irqrestore ( & ext_int_hash_lock , flags ) ;
2011-05-26 11:48:24 +04:00
return 0 ;
}
2014-03-31 17:24:08 +04:00
EXPORT_SYMBOL ( unregister_external_irq ) ;
2011-05-26 11:48:24 +04:00
2013-06-27 11:01:09 +04:00
static irqreturn_t do_ext_interrupt ( int irq , void * dummy )
2011-05-26 11:48:24 +04:00
{
2013-06-27 11:01:09 +04:00
struct pt_regs * regs = get_irq_regs ( ) ;
2013-06-17 16:54:02 +04:00
struct ext_code ext_code ;
2011-05-26 11:48:24 +04:00
struct ext_int_info * p ;
int index ;
2013-06-17 16:54:02 +04:00
ext_code = * ( struct ext_code * ) & regs - > int_code ;
2014-03-31 17:24:08 +04:00
if ( ext_code . code ! = EXT_IRQ_CLK_COMP )
2011-05-26 11:48:24 +04:00
__get_cpu_var ( s390_idle ) . nohz_delay = 1 ;
2011-07-24 12:48:27 +04:00
2012-03-11 19:59:31 +04:00
index = ext_hash ( ext_code . code ) ;
2011-07-24 12:48:27 +04:00
rcu_read_lock ( ) ;
2013-09-03 16:12:07 +04:00
hlist_for_each_entry_rcu ( p , & ext_int_hash [ index ] , entry ) {
if ( unlikely ( p - > code ! = ext_code . code ) )
continue ;
p - > handler ( ext_code , regs - > int_parm , regs - > int_parm_long ) ;
}
2011-07-24 12:48:27 +04:00
rcu_read_unlock ( ) ;
2013-06-27 11:01:09 +04:00
return IRQ_HANDLED ;
2011-05-26 11:48:24 +04:00
}
2013-06-27 11:01:09 +04:00
static struct irqaction external_interrupt = {
. name = " EXT " ,
. handler = do_ext_interrupt ,
} ;
void __init init_ext_interrupts ( void )
2011-07-24 12:48:27 +04:00
{
2013-06-27 11:01:09 +04:00
int idx ;
for ( idx = 0 ; idx < ARRAY_SIZE ( ext_int_hash ) ; idx + + )
2013-09-03 16:12:07 +04:00
INIT_HLIST_HEAD ( & ext_int_hash [ idx ] ) ;
2013-06-27 11:01:09 +04:00
irq_set_chip_and_handler ( EXT_INTERRUPT ,
& dummy_irq_chip , handle_percpu_irq ) ;
setup_irq ( EXT_INTERRUPT , & external_interrupt ) ;
2011-07-24 12:48:27 +04:00
}
2013-09-04 15:35:45 +04:00
static DEFINE_SPINLOCK ( irq_subclass_lock ) ;
static unsigned char irq_subclass_refcount [ 64 ] ;
2011-05-26 11:48:24 +04:00
2013-09-04 15:35:45 +04:00
void irq_subclass_register ( enum irq_subclass subclass )
2011-05-26 11:48:24 +04:00
{
2013-09-04 15:35:45 +04:00
spin_lock ( & irq_subclass_lock ) ;
if ( ! irq_subclass_refcount [ subclass ] )
ctl_set_bit ( 0 , subclass ) ;
irq_subclass_refcount [ subclass ] + + ;
spin_unlock ( & irq_subclass_lock ) ;
2011-05-26 11:48:24 +04:00
}
2013-09-04 15:35:45 +04:00
EXPORT_SYMBOL ( irq_subclass_register ) ;
2011-05-26 11:48:24 +04:00
2013-09-04 15:35:45 +04:00
void irq_subclass_unregister ( enum irq_subclass subclass )
2011-05-26 11:48:24 +04:00
{
2013-09-04 15:35:45 +04:00
spin_lock ( & irq_subclass_lock ) ;
irq_subclass_refcount [ subclass ] - - ;
if ( ! irq_subclass_refcount [ subclass ] )
ctl_clear_bit ( 0 , subclass ) ;
spin_unlock ( & irq_subclass_lock ) ;
2011-05-26 11:48:24 +04:00
}
2013-09-04 15:35:45 +04:00
EXPORT_SYMBOL ( irq_subclass_unregister ) ;