2018-03-15 00:15:19 +03:00
// SPDX-License-Identifier: GPL-2.0
2018-03-20 16:17:04 +03:00
// Copyright 2017 Thomas Gleixner <tglx@linutronix.de>
2017-06-20 02:37:17 +03:00
# include <linux/irqdomain.h>
# include <linux/irq.h>
2017-08-18 11:11:56 +03:00
# include <linux/uaccess.h>
2017-06-20 02:37:17 +03:00
# include "internals.h"
static struct dentry * irq_dir ;
struct irq_bit_descr {
unsigned int mask ;
char * name ;
} ;
# define BIT_MASK_DESCR(m) { .mask = m, .name = #m }
static void irq_debug_show_bits ( struct seq_file * m , int ind , unsigned int state ,
const struct irq_bit_descr * sd , int size )
{
int i ;
for ( i = 0 ; i < size ; i + + , sd + + ) {
if ( state & sd - > mask )
seq_printf ( m , " %*s%s \n " , ind + 12 , " " , sd - > name ) ;
}
}
# ifdef CONFIG_SMP
static void irq_debug_show_masks ( struct seq_file * m , struct irq_desc * desc )
{
struct irq_data * data = irq_desc_get_irq_data ( desc ) ;
struct cpumask * msk ;
msk = irq_data_get_affinity_mask ( data ) ;
seq_printf ( m , " affinity: %*pbl \n " , cpumask_pr_args ( msk ) ) ;
2017-06-20 02:37:38 +03:00
# ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
msk = irq_data_get_effective_affinity_mask ( data ) ;
seq_printf ( m , " effectiv: %*pbl \n " , cpumask_pr_args ( msk ) ) ;
# endif
2017-06-20 02:37:17 +03:00
# ifdef CONFIG_GENERIC_PENDING_IRQ
msk = desc - > pending_mask ;
seq_printf ( m , " pending: %*pbl \n " , cpumask_pr_args ( msk ) ) ;
# endif
}
# else
static void irq_debug_show_masks ( struct seq_file * m , struct irq_desc * desc ) { }
# endif
static const struct irq_bit_descr irqchip_flags [ ] = {
BIT_MASK_DESCR ( IRQCHIP_SET_TYPE_MASKED ) ,
BIT_MASK_DESCR ( IRQCHIP_EOI_IF_HANDLED ) ,
BIT_MASK_DESCR ( IRQCHIP_MASK_ON_SUSPEND ) ,
BIT_MASK_DESCR ( IRQCHIP_ONOFFLINE_ENABLED ) ,
BIT_MASK_DESCR ( IRQCHIP_SKIP_SET_WAKE ) ,
BIT_MASK_DESCR ( IRQCHIP_ONESHOT_SAFE ) ,
BIT_MASK_DESCR ( IRQCHIP_EOI_THREADED ) ,
2018-06-22 12:52:48 +03:00
BIT_MASK_DESCR ( IRQCHIP_SUPPORTS_LEVEL_MSI ) ,
2019-01-31 17:53:58 +03:00
BIT_MASK_DESCR ( IRQCHIP_SUPPORTS_NMI ) ,
2017-06-20 02:37:17 +03:00
} ;
static void
irq_debug_show_chip ( struct seq_file * m , struct irq_data * data , int ind )
{
struct irq_chip * chip = data - > chip ;
if ( ! chip ) {
seq_printf ( m , " chip: None \n " ) ;
return ;
}
seq_printf ( m , " %*schip: %s \n " , ind , " " , chip - > name ) ;
seq_printf ( m , " %*sflags: 0x%lx \n " , ind + 1 , " " , chip - > flags ) ;
irq_debug_show_bits ( m , ind , chip - > flags , irqchip_flags ,
ARRAY_SIZE ( irqchip_flags ) ) ;
}
static void
irq_debug_show_data ( struct seq_file * m , struct irq_data * data , int ind )
{
seq_printf ( m , " %*sdomain: %s \n " , ind , " " ,
data - > domain ? data - > domain - > name : " " ) ;
seq_printf ( m , " %*shwirq: 0x%lx \n " , ind + 1 , " " , data - > hwirq ) ;
irq_debug_show_chip ( m , data , ind + 1 ) ;
2017-09-14 00:29:06 +03:00
if ( data - > domain & & data - > domain - > ops & & data - > domain - > ops - > debug_show )
data - > domain - > ops - > debug_show ( m , NULL , data , ind + 1 ) ;
2017-06-20 02:37:17 +03:00
# ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
if ( ! data - > parent_data )
return ;
seq_printf ( m , " %*sparent: \n " , ind + 1 , " " ) ;
irq_debug_show_data ( m , data - > parent_data , ind + 4 ) ;
# endif
}
static const struct irq_bit_descr irqdata_states [ ] = {
BIT_MASK_DESCR ( IRQ_TYPE_EDGE_RISING ) ,
BIT_MASK_DESCR ( IRQ_TYPE_EDGE_FALLING ) ,
BIT_MASK_DESCR ( IRQ_TYPE_LEVEL_HIGH ) ,
BIT_MASK_DESCR ( IRQ_TYPE_LEVEL_LOW ) ,
BIT_MASK_DESCR ( IRQD_LEVEL ) ,
BIT_MASK_DESCR ( IRQD_ACTIVATED ) ,
BIT_MASK_DESCR ( IRQD_IRQ_STARTED ) ,
BIT_MASK_DESCR ( IRQD_IRQ_DISABLED ) ,
BIT_MASK_DESCR ( IRQD_IRQ_MASKED ) ,
BIT_MASK_DESCR ( IRQD_IRQ_INPROGRESS ) ,
BIT_MASK_DESCR ( IRQD_PER_CPU ) ,
BIT_MASK_DESCR ( IRQD_NO_BALANCING ) ,
2017-06-20 02:37:52 +03:00
BIT_MASK_DESCR ( IRQD_SINGLE_TARGET ) ,
2017-06-20 02:37:17 +03:00
BIT_MASK_DESCR ( IRQD_MOVE_PCNTXT ) ,
BIT_MASK_DESCR ( IRQD_AFFINITY_SET ) ,
BIT_MASK_DESCR ( IRQD_SETAFFINITY_PENDING ) ,
BIT_MASK_DESCR ( IRQD_AFFINITY_MANAGED ) ,
BIT_MASK_DESCR ( IRQD_MANAGED_SHUTDOWN ) ,
2017-12-29 18:44:34 +03:00
BIT_MASK_DESCR ( IRQD_CAN_RESERVE ) ,
2020-01-31 17:26:52 +03:00
BIT_MASK_DESCR ( IRQD_MSI_NOMASK_QUIRK ) ,
2017-06-20 02:37:17 +03:00
BIT_MASK_DESCR ( IRQD_FORWARDED_TO_VCPU ) ,
BIT_MASK_DESCR ( IRQD_WAKEUP_STATE ) ,
BIT_MASK_DESCR ( IRQD_WAKEUP_ARMED ) ,
} ;
static const struct irq_bit_descr irqdesc_states [ ] = {
BIT_MASK_DESCR ( _IRQ_NOPROBE ) ,
BIT_MASK_DESCR ( _IRQ_NOREQUEST ) ,
BIT_MASK_DESCR ( _IRQ_NOTHREAD ) ,
BIT_MASK_DESCR ( _IRQ_NOAUTOEN ) ,
BIT_MASK_DESCR ( _IRQ_NESTED_THREAD ) ,
BIT_MASK_DESCR ( _IRQ_PER_CPU_DEVID ) ,
BIT_MASK_DESCR ( _IRQ_IS_POLLED ) ,
BIT_MASK_DESCR ( _IRQ_DISABLE_UNLAZY ) ,
} ;
static const struct irq_bit_descr irqdesc_istates [ ] = {
BIT_MASK_DESCR ( IRQS_AUTODETECT ) ,
BIT_MASK_DESCR ( IRQS_SPURIOUS_DISABLED ) ,
BIT_MASK_DESCR ( IRQS_POLL_INPROGRESS ) ,
BIT_MASK_DESCR ( IRQS_ONESHOT ) ,
BIT_MASK_DESCR ( IRQS_REPLAY ) ,
BIT_MASK_DESCR ( IRQS_WAITING ) ,
BIT_MASK_DESCR ( IRQS_PENDING ) ,
BIT_MASK_DESCR ( IRQS_SUSPENDED ) ,
2019-01-31 17:53:58 +03:00
BIT_MASK_DESCR ( IRQS_NMI ) ,
2017-06-20 02:37:17 +03:00
} ;
static int irq_debug_show ( struct seq_file * m , void * p )
{
struct irq_desc * desc = m - > private ;
struct irq_data * data ;
raw_spin_lock_irq ( & desc - > lock ) ;
data = irq_desc_get_irq_data ( desc ) ;
2019-03-25 22:32:28 +03:00
seq_printf ( m , " handler: %ps \n " , desc - > handle_irq ) ;
2017-09-14 00:29:05 +03:00
seq_printf ( m , " device: %s \n " , desc - > dev_name ) ;
2017-06-20 02:37:17 +03:00
seq_printf ( m , " status: 0x%08x \n " , desc - > status_use_accessors ) ;
irq_debug_show_bits ( m , 0 , desc - > status_use_accessors , irqdesc_states ,
ARRAY_SIZE ( irqdesc_states ) ) ;
seq_printf ( m , " istate: 0x%08x \n " , desc - > istate ) ;
irq_debug_show_bits ( m , 0 , desc - > istate , irqdesc_istates ,
ARRAY_SIZE ( irqdesc_istates ) ) ;
seq_printf ( m , " ddepth: %u \n " , desc - > depth ) ;
seq_printf ( m , " wdepth: %u \n " , desc - > wake_depth ) ;
seq_printf ( m , " dstate: 0x%08x \n " , irqd_get ( data ) ) ;
irq_debug_show_bits ( m , 0 , irqd_get ( data ) , irqdata_states ,
ARRAY_SIZE ( irqdata_states ) ) ;
seq_printf ( m , " node: %d \n " , irq_data_get_node ( data ) ) ;
irq_debug_show_masks ( m , desc ) ;
irq_debug_show_data ( m , data , 0 ) ;
raw_spin_unlock_irq ( & desc - > lock ) ;
return 0 ;
}
static int irq_debug_open ( struct inode * inode , struct file * file )
{
return single_open ( file , irq_debug_show , inode - > i_private ) ;
}
2017-08-18 11:11:56 +03:00
static ssize_t irq_debug_write ( struct file * file , const char __user * user_buf ,
size_t count , loff_t * ppos )
{
struct irq_desc * desc = file_inode ( file ) - > i_private ;
char buf [ 8 ] = { 0 , } ;
size_t size ;
size = min ( sizeof ( buf ) - 1 , count ) ;
if ( copy_from_user ( buf , user_buf , size ) )
return - EFAULT ;
if ( ! strncmp ( buf , " trigger " , size ) ) {
unsigned long flags ;
int err ;
/* Try the HW interface first */
err = irq_set_irqchip_state ( irq_desc_get_irq ( desc ) ,
IRQCHIP_STATE_PENDING , true ) ;
if ( ! err )
return count ;
/*
* Otherwise , try to inject via the resend interface ,
* which may or may not succeed .
*/
chip_bus_lock ( desc ) ;
raw_spin_lock_irqsave ( & desc - > lock , flags ) ;
2019-01-31 17:53:58 +03:00
if ( irq_settings_is_level ( desc ) | | desc - > istate & IRQS_NMI ) {
/* Can't do level nor NMIs, sorry */
2017-08-18 11:11:56 +03:00
err = - EINVAL ;
} else {
desc - > istate | = IRQS_PENDING ;
check_irq_resend ( desc ) ;
err = 0 ;
}
raw_spin_unlock_irqrestore ( & desc - > lock , flags ) ;
chip_bus_sync_unlock ( desc ) ;
return err ? err : count ;
}
return count ;
}
2017-06-20 02:37:17 +03:00
static const struct file_operations dfs_irq_ops = {
. open = irq_debug_open ,
2017-08-18 11:11:56 +03:00
. write = irq_debug_write ,
2017-06-20 02:37:17 +03:00
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2017-09-14 00:29:05 +03:00
void irq_debugfs_copy_devname ( int irq , struct device * dev )
{
struct irq_desc * desc = irq_to_desc ( irq ) ;
const char * name = dev_name ( dev ) ;
if ( name )
desc - > dev_name = kstrdup ( name , GFP_KERNEL ) ;
}
2017-06-20 02:37:17 +03:00
void irq_add_debugfs_entry ( unsigned int irq , struct irq_desc * desc )
{
char name [ 10 ] ;
if ( ! irq_dir | | ! desc | | desc - > debugfs_file )
return ;
sprintf ( name , " %d " , irq ) ;
2017-08-18 11:11:56 +03:00
desc - > debugfs_file = debugfs_create_file ( name , 0644 , irq_dir , desc ,
2017-06-20 02:37:17 +03:00
& dfs_irq_ops ) ;
}
static int __init irq_debugfs_init ( void )
{
struct dentry * root_dir ;
int irq ;
root_dir = debugfs_create_dir ( " irq " , NULL ) ;
irq_domain_debugfs_init ( root_dir ) ;
irq_dir = debugfs_create_dir ( " irqs " , root_dir ) ;
irq_lock_sparse ( ) ;
for_each_active_irq ( irq )
irq_add_debugfs_entry ( irq , irq_to_desc ( irq ) ) ;
irq_unlock_sparse ( ) ;
return 0 ;
}
__initcall ( irq_debugfs_init ) ;