2012-05-19 15:11:47 +09:00
# define pr_fmt(fmt) "irq: " fmt
2012-02-16 01:37:49 -07:00
# include <linux/debugfs.h>
# include <linux/hardirq.h>
# include <linux/interrupt.h>
2011-07-26 03:19:06 -06:00
# include <linux/irq.h>
2012-02-16 01:37:49 -07:00
# include <linux/irqdesc.h>
2011-07-26 03:19:06 -06:00
# include <linux/irqdomain.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of.h>
2011-07-26 03:19:06 -06:00
# include <linux/of_address.h>
2012-06-03 22:04:34 -07:00
# include <linux/topology.h>
2012-02-16 01:37:49 -07:00
# include <linux/seq_file.h>
2011-07-26 03:19:06 -06:00
# include <linux/slab.h>
2012-02-16 01:37:49 -07:00
# include <linux/smp.h>
# include <linux/fs.h>
2011-07-26 03:19:06 -06:00
2012-02-14 14:06:55 -07:00
# define IRQ_DOMAIN_MAP_LEGACY 0 / * driver allocated fixed range of irqs.
* ie . legacy 8259 , gets irqs 1. .15 */
2012-02-14 14:06:54 -07:00
# define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
# define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
# define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */
2011-07-26 03:19:06 -06:00
static LIST_HEAD ( irq_domain_list ) ;
static DEFINE_MUTEX ( irq_domain_mutex ) ;
2012-02-16 01:37:49 -07:00
static DEFINE_MUTEX ( revmap_trees_mutex ) ;
2012-02-14 14:06:53 -07:00
static struct irq_domain * irq_default_domain ;
2012-02-16 01:37:49 -07:00
/**
2012-02-14 14:06:54 -07:00
* irq_domain_alloc ( ) - Allocate a new irq_domain data structure
2012-02-16 01:37:49 -07:00
* @ of_node : optional device - tree node of the interrupt controller
* @ revmap_type : type of reverse mapping to use
2012-02-14 14:06:53 -07:00
* @ ops : map / unmap domain callbacks
2012-02-14 14:06:54 -07:00
* @ host_data : Controller private data pointer
2012-02-16 01:37:49 -07:00
*
2012-02-14 14:06:54 -07:00
* Allocates and initialize and irq_domain structure . Caller is expected to
* register allocated irq_domain with irq_domain_register ( ) . Returns pointer
* to IRQ domain , or NULL on failure .
2012-02-16 01:37:49 -07:00
*/
2012-02-14 14:06:54 -07:00
static struct irq_domain * irq_domain_alloc ( struct device_node * of_node ,
unsigned int revmap_type ,
2012-01-26 12:12:14 -07:00
const struct irq_domain_ops * ops ,
2012-02-14 14:06:54 -07:00
void * host_data )
2012-02-16 01:37:49 -07:00
{
2012-02-14 14:06:54 -07:00
struct irq_domain * domain ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:34 -07:00
domain = kzalloc_node ( sizeof ( * domain ) , GFP_KERNEL ,
of_node_to_nid ( of_node ) ) ;
2012-02-14 14:06:54 -07:00
if ( WARN_ON ( ! domain ) )
2012-02-16 01:37:49 -07:00
return NULL ;
/* Fill structure */
2012-02-14 14:06:53 -07:00
domain - > revmap_type = revmap_type ;
domain - > ops = ops ;
2012-02-14 14:06:54 -07:00
domain - > host_data = host_data ;
2012-02-14 14:06:53 -07:00
domain - > of_node = of_node_get ( of_node ) ;
2012-02-16 01:37:49 -07:00
2012-02-14 14:06:54 -07:00
return domain ;
}
2012-05-19 15:11:41 +09:00
static void irq_domain_free ( struct irq_domain * domain )
{
of_node_put ( domain - > of_node ) ;
kfree ( domain ) ;
}
2012-02-14 14:06:54 -07:00
static void irq_domain_add ( struct irq_domain * domain )
{
mutex_lock ( & irq_domain_mutex ) ;
list_add ( & domain - > link , & irq_domain_list ) ;
mutex_unlock ( & irq_domain_mutex ) ;
2012-05-19 15:11:47 +09:00
pr_debug ( " Allocated domain of type %d @0x%p \n " ,
2012-02-14 14:06:54 -07:00
domain - > revmap_type , domain ) ;
}
2012-05-19 15:11:41 +09:00
/**
* irq_domain_remove ( ) - Remove an irq domain .
* @ domain : domain to remove
*
* This routine is used to remove an irq domain . The caller must ensure
* that all mappings within the domain have been disposed of prior to
* use , depending on the revmap type .
*/
void irq_domain_remove ( struct irq_domain * domain )
{
mutex_lock ( & irq_domain_mutex ) ;
switch ( domain - > revmap_type ) {
case IRQ_DOMAIN_MAP_LEGACY :
/*
* Legacy domains don ' t manage their own irq_desc
* allocations , we expect the caller to handle irq_desc
* freeing on their own .
*/
break ;
case IRQ_DOMAIN_MAP_TREE :
/*
* radix_tree_delete ( ) takes care of destroying the root
* node when all entries are removed . Shout if there are
* any mappings left .
*/
WARN_ON ( domain - > revmap_data . tree . height ) ;
break ;
case IRQ_DOMAIN_MAP_LINEAR :
kfree ( domain - > revmap_data . linear . revmap ) ;
domain - > revmap_data . linear . size = 0 ;
break ;
case IRQ_DOMAIN_MAP_NOMAP :
break ;
}
list_del ( & domain - > link ) ;
/*
* If the going away domain is the default one , reset it .
*/
if ( unlikely ( irq_default_domain = = domain ) )
irq_set_default_host ( NULL ) ;
mutex_unlock ( & irq_domain_mutex ) ;
2012-05-19 15:11:47 +09:00
pr_debug ( " Removed domain of type %d @0x%p \n " ,
2012-05-19 15:11:41 +09:00
domain - > revmap_type , domain ) ;
irq_domain_free ( domain ) ;
}
2012-05-19 15:11:42 +09:00
EXPORT_SYMBOL_GPL ( irq_domain_remove ) ;
2012-05-19 15:11:41 +09:00
2012-02-14 14:06:55 -07:00
static unsigned int irq_domain_legacy_revmap ( struct irq_domain * domain ,
irq_hw_number_t hwirq )
{
irq_hw_number_t first_hwirq = domain - > revmap_data . legacy . first_hwirq ;
int size = domain - > revmap_data . legacy . size ;
if ( WARN_ON ( hwirq < first_hwirq | | hwirq > = first_hwirq + size ) )
return 0 ;
return hwirq - first_hwirq + domain - > revmap_data . legacy . first_irq ;
}
2012-07-05 12:19:19 +01:00
/**
* irq_domain_add_simple ( ) - Allocate and register a simple irq_domain .
* @ of_node : pointer to interrupt controller ' s device tree node .
* @ size : total number of irqs in mapping
* @ first_irq : first number of irq block assigned to the domain
* @ ops : map / unmap domain callbacks
* @ host_data : Controller private data pointer
*
* Allocates a legacy irq_domain if irq_base is positive or a linear
* domain otherwise .
*
* This is intended to implement the expected behaviour for most
* interrupt controllers which is that a linear mapping should
* normally be used unless the system requires a legacy mapping in
* order to support supplying interrupt numbers during non - DT
* registration of devices .
*/
struct irq_domain * irq_domain_add_simple ( struct device_node * of_node ,
unsigned int size ,
unsigned int first_irq ,
const struct irq_domain_ops * ops ,
void * host_data )
{
if ( first_irq > 0 )
return irq_domain_add_legacy ( of_node , size , first_irq , 0 ,
ops , host_data ) ;
else
return irq_domain_add_linear ( of_node , size , ops , host_data ) ;
}
2012-02-14 14:06:54 -07:00
/**
* irq_domain_add_legacy ( ) - Allocate and register a legacy revmap irq_domain .
* @ of_node : pointer to interrupt controller ' s device tree node .
2012-02-14 14:06:55 -07:00
* @ size : total number of irqs in legacy mapping
* @ first_irq : first number of irq block assigned to the domain
* @ first_hwirq : first hwirq number to use for the translation . Should normally
* be ' 0 ' , but a positive integer can be used if the effective
* hwirqs numbering does not begin at zero .
2012-02-14 14:06:54 -07:00
* @ ops : map / unmap domain callbacks
* @ host_data : Controller private data pointer
*
* Note : the map ( ) callback will be called before this function returns
* for all legacy interrupts except 0 ( which is always the invalid irq for
* a legacy controller ) .
*/
struct irq_domain * irq_domain_add_legacy ( struct device_node * of_node ,
2012-02-14 14:06:55 -07:00
unsigned int size ,
unsigned int first_irq ,
irq_hw_number_t first_hwirq ,
2012-01-26 12:12:14 -07:00
const struct irq_domain_ops * ops ,
2012-02-14 14:06:54 -07:00
void * host_data )
{
2012-02-14 14:06:55 -07:00
struct irq_domain * domain ;
2012-02-14 14:06:54 -07:00
unsigned int i ;
domain = irq_domain_alloc ( of_node , IRQ_DOMAIN_MAP_LEGACY , ops , host_data ) ;
if ( ! domain )
return NULL ;
2012-02-14 14:06:55 -07:00
domain - > revmap_data . legacy . first_irq = first_irq ;
domain - > revmap_data . legacy . first_hwirq = first_hwirq ;
domain - > revmap_data . legacy . size = size ;
2012-02-16 01:37:49 -07:00
mutex_lock ( & irq_domain_mutex ) ;
2012-02-14 14:06:55 -07:00
/* Verify that all the irqs are available */
for ( i = 0 ; i < size ; i + + ) {
int irq = first_irq + i ;
struct irq_data * irq_data = irq_get_irq_data ( irq ) ;
if ( WARN_ON ( ! irq_data | | irq_data - > domain ) ) {
2012-02-14 14:06:54 -07:00
mutex_unlock ( & irq_domain_mutex ) ;
2012-05-19 15:11:41 +09:00
irq_domain_free ( domain ) ;
2012-02-14 14:06:54 -07:00
return NULL ;
2012-02-16 01:37:49 -07:00
}
}
2012-02-14 14:06:55 -07:00
/* Claim all of the irqs before registering a legacy domain */
for ( i = 0 ; i < size ; i + + ) {
struct irq_data * irq_data = irq_get_irq_data ( first_irq + i ) ;
irq_data - > hwirq = first_hwirq + i ;
2012-02-14 14:06:54 -07:00
irq_data - > domain = domain ;
2012-02-14 14:06:55 -07:00
}
mutex_unlock ( & irq_domain_mutex ) ;
for ( i = 0 ; i < size ; i + + ) {
int irq = first_irq + i ;
int hwirq = first_hwirq + i ;
/* IRQ0 gets ignored */
if ( ! irq )
continue ;
2012-02-14 14:06:54 -07:00
/* Legacy flags are left to default at this point,
* one can then use irq_create_mapping ( ) to
* explicitly change them
*/
2012-06-03 22:04:39 -07:00
if ( ops - > map )
ops - > map ( domain , irq , hwirq ) ;
2012-02-14 14:06:54 -07:00
/* Clear norequest flags */
2012-02-14 14:06:55 -07:00
irq_clear_status_flags ( irq , IRQ_NOREQUEST ) ;
2012-02-16 01:37:49 -07:00
}
2012-02-14 14:06:55 -07:00
irq_domain_add ( domain ) ;
2012-02-14 14:06:54 -07:00
return domain ;
}
2012-05-19 15:11:42 +09:00
EXPORT_SYMBOL_GPL ( irq_domain_add_legacy ) ;
2012-02-14 14:06:54 -07:00
/**
2012-06-20 17:00:30 +08:00
* irq_domain_add_linear ( ) - Allocate and register a linear revmap irq_domain .
2012-02-14 14:06:54 -07:00
* @ of_node : pointer to interrupt controller ' s device tree node .
2012-05-19 12:15:35 +01:00
* @ size : Number of interrupts in the domain .
2012-02-14 14:06:54 -07:00
* @ ops : map / unmap domain callbacks
* @ host_data : Controller private data pointer
*/
struct irq_domain * irq_domain_add_linear ( struct device_node * of_node ,
unsigned int size ,
2012-01-26 12:12:14 -07:00
const struct irq_domain_ops * ops ,
2012-02-14 14:06:54 -07:00
void * host_data )
{
struct irq_domain * domain ;
unsigned int * revmap ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:34 -07:00
revmap = kzalloc_node ( sizeof ( * revmap ) * size , GFP_KERNEL ,
of_node_to_nid ( of_node ) ) ;
2012-02-14 14:06:54 -07:00
if ( WARN_ON ( ! revmap ) )
return NULL ;
2012-02-16 01:37:49 -07:00
2012-02-14 14:06:54 -07:00
domain = irq_domain_alloc ( of_node , IRQ_DOMAIN_MAP_LINEAR , ops , host_data ) ;
if ( ! domain ) {
kfree ( revmap ) ;
return NULL ;
}
domain - > revmap_data . linear . size = size ;
domain - > revmap_data . linear . revmap = revmap ;
irq_domain_add ( domain ) ;
return domain ;
}
2012-05-19 15:11:42 +09:00
EXPORT_SYMBOL_GPL ( irq_domain_add_linear ) ;
2012-02-14 14:06:54 -07:00
struct irq_domain * irq_domain_add_nomap ( struct device_node * of_node ,
2012-02-15 15:06:08 -07:00
unsigned int max_irq ,
2012-01-26 12:12:14 -07:00
const struct irq_domain_ops * ops ,
2012-02-14 14:06:54 -07:00
void * host_data )
{
struct irq_domain * domain = irq_domain_alloc ( of_node ,
IRQ_DOMAIN_MAP_NOMAP , ops , host_data ) ;
2012-02-15 15:06:08 -07:00
if ( domain ) {
domain - > revmap_data . nomap . max_irq = max_irq ? max_irq : ~ 0 ;
2012-02-14 14:06:54 -07:00
irq_domain_add ( domain ) ;
2012-02-15 15:06:08 -07:00
}
2012-02-14 14:06:54 -07:00
return domain ;
}
2012-05-19 15:11:42 +09:00
EXPORT_SYMBOL_GPL ( irq_domain_add_nomap ) ;
2012-02-14 14:06:54 -07:00
/**
* irq_domain_add_tree ( )
* @ of_node : pointer to interrupt controller ' s device tree node .
* @ ops : map / unmap domain callbacks
*
* Note : The radix tree will be allocated later during boot automatically
* ( the reverse mapping will use the slow path until that happens ) .
*/
struct irq_domain * irq_domain_add_tree ( struct device_node * of_node ,
2012-01-26 12:12:14 -07:00
const struct irq_domain_ops * ops ,
2012-02-14 14:06:54 -07:00
void * host_data )
{
struct irq_domain * domain = irq_domain_alloc ( of_node ,
IRQ_DOMAIN_MAP_TREE , ops , host_data ) ;
if ( domain ) {
INIT_RADIX_TREE ( & domain - > revmap_data . tree , GFP_KERNEL ) ;
irq_domain_add ( domain ) ;
}
2012-02-14 14:06:53 -07:00
return domain ;
2012-02-16 01:37:49 -07:00
}
2012-05-19 15:11:42 +09:00
EXPORT_SYMBOL_GPL ( irq_domain_add_tree ) ;
2012-02-16 01:37:49 -07:00
/**
* irq_find_host ( ) - Locates a domain for a given device node
* @ node : device - tree node of the interrupt controller
*/
struct irq_domain * irq_find_host ( struct device_node * node )
{
struct irq_domain * h , * found = NULL ;
2012-01-26 12:12:14 -07:00
int rc ;
2012-02-16 01:37:49 -07:00
/* We might want to match the legacy controller last since
* it might potentially be set to match all interrupts in
* the absence of a device node . This isn ' t a problem so far
* yet though . . .
*/
mutex_lock ( & irq_domain_mutex ) ;
2012-01-26 12:12:14 -07:00
list_for_each_entry ( h , & irq_domain_list , link ) {
if ( h - > ops - > match )
rc = h - > ops - > match ( h , node ) ;
else
rc = ( h - > of_node ! = NULL ) & & ( h - > of_node = = node ) ;
if ( rc ) {
2012-02-16 01:37:49 -07:00
found = h ;
break ;
}
2012-01-26 12:12:14 -07:00
}
2012-02-16 01:37:49 -07:00
mutex_unlock ( & irq_domain_mutex ) ;
return found ;
}
EXPORT_SYMBOL_GPL ( irq_find_host ) ;
/**
* irq_set_default_host ( ) - Set a " default " irq domain
2012-02-14 14:06:53 -07:00
* @ domain : default domain pointer
2012-02-16 01:37:49 -07:00
*
* For convenience , it ' s possible to set a " default " domain that will be used
* whenever NULL is passed to irq_create_mapping ( ) . It makes life easier for
* platforms that want to manipulate a few hard coded interrupt numbers that
* aren ' t properly represented in the device - tree .
*/
2012-02-14 14:06:53 -07:00
void irq_set_default_host ( struct irq_domain * domain )
2012-02-16 01:37:49 -07:00
{
2012-05-19 15:11:47 +09:00
pr_debug ( " Default domain set to @0x%p \n " , domain ) ;
2012-02-16 01:37:49 -07:00
2012-02-14 14:06:53 -07:00
irq_default_domain = domain ;
2012-02-16 01:37:49 -07:00
}
2012-05-19 15:11:42 +09:00
EXPORT_SYMBOL_GPL ( irq_set_default_host ) ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:35 -07:00
static void irq_domain_disassociate_many ( struct irq_domain * domain ,
unsigned int irq_base , int count )
{
/*
* disassociate in reverse order ;
* not strictly necessary , but nice for unwinding
*/
while ( count - - ) {
int irq = irq_base + count ;
struct irq_data * irq_data = irq_get_irq_data ( irq ) ;
irq_hw_number_t hwirq = irq_data - > hwirq ;
if ( WARN_ON ( ! irq_data | | irq_data - > domain ! = domain ) )
continue ;
irq_set_status_flags ( irq , IRQ_NOREQUEST ) ;
/* remove chip and handler */
irq_set_chip_and_handler ( irq , NULL , NULL ) ;
/* Make sure it's completed */
synchronize_irq ( irq ) ;
/* Tell the PIC about it */
if ( domain - > ops - > unmap )
domain - > ops - > unmap ( domain , irq ) ;
smp_mb ( ) ;
irq_data - > domain = NULL ;
irq_data - > hwirq = 0 ;
/* Clear reverse map */
switch ( domain - > revmap_type ) {
case IRQ_DOMAIN_MAP_LINEAR :
if ( hwirq < domain - > revmap_data . linear . size )
domain - > revmap_data . linear . revmap [ hwirq ] = 0 ;
break ;
case IRQ_DOMAIN_MAP_TREE :
mutex_lock ( & revmap_trees_mutex ) ;
radix_tree_delete ( & domain - > revmap_data . tree , hwirq ) ;
mutex_unlock ( & revmap_trees_mutex ) ;
break ;
}
}
}
2012-06-17 16:17:04 -06:00
int irq_domain_associate_many ( struct irq_domain * domain , unsigned int irq_base ,
irq_hw_number_t hwirq_base , int count )
2012-02-16 01:37:49 -07:00
{
2012-06-17 16:17:04 -06:00
unsigned int virq = irq_base ;
irq_hw_number_t hwirq = hwirq_base ;
2012-07-20 10:33:19 +01:00
int i , ret ;
2012-02-16 01:37:49 -07:00
2012-06-17 16:17:04 -06:00
pr_debug ( " %s(%s, irqbase=%i, hwbase=%i, count=%i) \n " , __func__ ,
of_node_full_name ( domain - > of_node ) , irq_base , ( int ) hwirq_base , count ) ;
2012-02-16 01:37:49 -07:00
2012-06-17 16:17:04 -06:00
for ( i = 0 ; i < count ; i + + ) {
struct irq_data * irq_data = irq_get_irq_data ( virq + i ) ;
if ( WARN ( ! irq_data , " error: irq_desc not allocated; "
" irq=%i hwirq=0x%x \n " , virq + i , ( int ) hwirq + i ) )
return - EINVAL ;
if ( WARN ( irq_data - > domain , " error: irq_desc already associated; "
" irq=%i hwirq=0x%x \n " , virq + i , ( int ) hwirq + i ) )
return - EINVAL ;
} ;
for ( i = 0 ; i < count ; i + + , virq + + , hwirq + + ) {
struct irq_data * irq_data = irq_get_irq_data ( virq ) ;
irq_data - > hwirq = hwirq ;
irq_data - > domain = domain ;
2012-07-20 10:33:19 +01:00
if ( domain - > ops - > map ) {
ret = domain - > ops - > map ( domain , virq , hwirq ) ;
if ( ret ! = 0 ) {
pr_err ( " irq-%i==>hwirq-0x%lx mapping failed: %d \n " ,
virq , hwirq , ret ) ;
WARN_ON ( 1 ) ;
irq_data - > domain = NULL ;
irq_data - > hwirq = 0 ;
goto err_unmap ;
}
2012-06-17 16:17:04 -06:00
}
switch ( domain - > revmap_type ) {
case IRQ_DOMAIN_MAP_LINEAR :
if ( hwirq < domain - > revmap_data . linear . size )
domain - > revmap_data . linear . revmap [ hwirq ] = virq ;
break ;
case IRQ_DOMAIN_MAP_TREE :
mutex_lock ( & revmap_trees_mutex ) ;
2012-06-03 22:04:37 -07:00
radix_tree_insert ( & domain - > revmap_data . tree , hwirq , irq_data ) ;
2012-06-17 16:17:04 -06:00
mutex_unlock ( & revmap_trees_mutex ) ;
break ;
}
2012-06-03 22:04:36 -07:00
2012-06-17 16:17:04 -06:00
irq_clear_status_flags ( virq , IRQ_NOREQUEST ) ;
}
2012-02-16 01:37:49 -07:00
return 0 ;
2012-06-17 16:17:04 -06:00
err_unmap :
irq_domain_disassociate_many ( domain , irq_base , i ) ;
return - EINVAL ;
2012-02-16 01:37:49 -07:00
}
2012-06-17 16:17:04 -06:00
EXPORT_SYMBOL_GPL ( irq_domain_associate_many ) ;
2012-02-16 01:37:49 -07:00
/**
* irq_create_direct_mapping ( ) - Allocate an irq for direct mapping
2012-02-14 14:06:53 -07:00
* @ domain : domain to allocate the irq for or NULL for default domain
2012-02-16 01:37:49 -07:00
*
* This routine is used for irq controllers which can choose the hardware
* interrupt numbers they generate . In such a case it ' s simplest to use
* the linux irq as the hardware interrupt number .
*/
2012-02-14 14:06:53 -07:00
unsigned int irq_create_direct_mapping ( struct irq_domain * domain )
2012-02-16 01:37:49 -07:00
{
unsigned int virq ;
2012-02-14 14:06:53 -07:00
if ( domain = = NULL )
domain = irq_default_domain ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:38 -07:00
if ( WARN_ON ( ! domain | | domain - > revmap_type ! = IRQ_DOMAIN_MAP_NOMAP ) )
return 0 ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:34 -07:00
virq = irq_alloc_desc_from ( 1 , of_node_to_nid ( domain - > of_node ) ) ;
2012-02-14 14:06:52 -07:00
if ( ! virq ) {
2012-05-19 15:11:47 +09:00
pr_debug ( " create_direct virq allocation failed \n " ) ;
2012-02-14 14:06:52 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
}
2012-02-15 15:06:08 -07:00
if ( virq > = domain - > revmap_data . nomap . max_irq ) {
2012-02-16 01:37:49 -07:00
pr_err ( " ERROR: no free irqs available below %i maximum \n " ,
2012-02-15 15:06:08 -07:00
domain - > revmap_data . nomap . max_irq ) ;
2012-02-16 01:37:49 -07:00
irq_free_desc ( virq ) ;
return 0 ;
}
2012-05-19 15:11:47 +09:00
pr_debug ( " create_direct obtained virq %d \n " , virq ) ;
2012-02-16 01:37:49 -07:00
2012-06-17 16:17:04 -06:00
if ( irq_domain_associate ( domain , virq , virq ) ) {
2012-02-16 01:37:49 -07:00
irq_free_desc ( virq ) ;
2012-02-14 14:06:52 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
}
return virq ;
}
2012-05-19 15:11:42 +09:00
EXPORT_SYMBOL_GPL ( irq_create_direct_mapping ) ;
2012-02-16 01:37:49 -07:00
/**
* irq_create_mapping ( ) - Map a hardware interrupt into linux irq space
2012-02-14 14:06:53 -07:00
* @ domain : domain owning this hardware interrupt or NULL for default domain
* @ hwirq : hardware irq number in that domain space
2012-02-16 01:37:49 -07:00
*
* Only one mapping per hardware interrupt is permitted . Returns a linux
* irq number .
* If the sense / trigger is to be specified , set_irq_type ( ) should be called
* on the number returned from that call .
*/
2012-02-14 14:06:53 -07:00
unsigned int irq_create_mapping ( struct irq_domain * domain ,
2012-02-16 01:37:49 -07:00
irq_hw_number_t hwirq )
{
2012-04-05 16:52:13 -07:00
unsigned int hint ;
int virq ;
2012-02-16 01:37:49 -07:00
2012-05-19 15:11:47 +09:00
pr_debug ( " irq_create_mapping(0x%p, 0x%lx) \n " , domain , hwirq ) ;
2012-02-16 01:37:49 -07:00
2012-02-14 14:06:53 -07:00
/* Look for default domain if nececssary */
if ( domain = = NULL )
domain = irq_default_domain ;
if ( domain = = NULL ) {
2012-05-19 15:11:47 +09:00
pr_warning ( " irq_create_mapping called for "
" NULL domain, hwirq=%lx \n " , hwirq ) ;
2012-02-16 01:37:49 -07:00
WARN_ON ( 1 ) ;
2012-02-14 14:06:52 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
}
2012-05-19 15:11:47 +09:00
pr_debug ( " -> using domain @%p \n " , domain ) ;
2012-02-16 01:37:49 -07:00
/* Check if mapping already exists */
2012-02-14 14:06:53 -07:00
virq = irq_find_mapping ( domain , hwirq ) ;
2012-02-14 14:06:52 -07:00
if ( virq ) {
2012-05-19 15:11:47 +09:00
pr_debug ( " -> existing mapping on virq %d \n " , virq ) ;
2012-02-16 01:37:49 -07:00
return virq ;
}
/* Get a virtual interrupt number */
2012-02-14 14:06:55 -07:00
if ( domain - > revmap_type = = IRQ_DOMAIN_MAP_LEGACY )
return irq_domain_legacy_revmap ( domain , hwirq ) ;
/* Allocate a virtual interrupt number */
2012-02-15 15:06:08 -07:00
hint = hwirq % nr_irqs ;
2012-02-14 14:06:55 -07:00
if ( hint = = 0 )
hint + + ;
2012-06-03 22:04:34 -07:00
virq = irq_alloc_desc_from ( hint , of_node_to_nid ( domain - > of_node ) ) ;
2012-04-05 16:52:13 -07:00
if ( virq < = 0 )
2012-06-03 22:04:34 -07:00
virq = irq_alloc_desc_from ( 1 , of_node_to_nid ( domain - > of_node ) ) ;
2012-04-05 16:52:13 -07:00
if ( virq < = 0 ) {
2012-05-19 15:11:47 +09:00
pr_debug ( " -> virq allocation failed \n " ) ;
2012-02-14 14:06:55 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
}
2012-06-17 16:17:04 -06:00
if ( irq_domain_associate ( domain , virq , hwirq ) ) {
2012-06-03 22:04:35 -07:00
irq_free_desc ( virq ) ;
2012-02-14 14:06:52 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
}
2012-05-19 15:11:47 +09:00
pr_debug ( " irq %lu on domain %s mapped to virtual irq %u \n " ,
2012-06-03 22:04:33 -07:00
hwirq , of_node_full_name ( domain - > of_node ) , virq ) ;
2012-02-16 01:37:49 -07:00
return virq ;
}
EXPORT_SYMBOL_GPL ( irq_create_mapping ) ;
2012-06-17 16:17:04 -06:00
/**
* irq_create_strict_mappings ( ) - Map a range of hw irqs to fixed linux irqs
* @ domain : domain owning the interrupt range
* @ irq_base : beginning of linux IRQ range
* @ hwirq_base : beginning of hardware IRQ range
* @ count : Number of interrupts to map
*
* This routine is used for allocating and mapping a range of hardware
* irqs to linux irqs where the linux irq numbers are at pre - defined
* locations . For use by controllers that already have static mappings
* to insert in to the domain .
*
* Non - linear users can use irq_create_identity_mapping ( ) for IRQ - at - a - time
* domain insertion .
*
* 0 is returned upon success , while any failure to establish a static
* mapping is treated as an error .
*/
int irq_create_strict_mappings ( struct irq_domain * domain , unsigned int irq_base ,
irq_hw_number_t hwirq_base , int count )
{
int ret ;
ret = irq_alloc_descs ( irq_base , irq_base , count ,
of_node_to_nid ( domain - > of_node ) ) ;
if ( unlikely ( ret < 0 ) )
return ret ;
ret = irq_domain_associate_many ( domain , irq_base , hwirq_base , count ) ;
if ( unlikely ( ret < 0 ) ) {
irq_free_descs ( irq_base , count ) ;
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( irq_create_strict_mappings ) ;
2012-02-16 01:37:49 -07:00
unsigned int irq_create_of_mapping ( struct device_node * controller ,
const u32 * intspec , unsigned int intsize )
{
2012-02-14 14:06:53 -07:00
struct irq_domain * domain ;
2012-02-16 01:37:49 -07:00
irq_hw_number_t hwirq ;
unsigned int type = IRQ_TYPE_NONE ;
unsigned int virq ;
2012-02-14 14:06:53 -07:00
domain = controller ? irq_find_host ( controller ) : irq_default_domain ;
if ( ! domain ) {
2012-02-24 08:07:06 -07:00
# ifdef CONFIG_MIPS
/*
* Workaround to avoid breaking interrupt controller drivers
* that don ' t yet register an irq_domain . This is temporary
* code . ~ ~ ~ gcl , Feb 24 , 2012
*
* Scheduled for removal in Linux v3 .6 . That should be enough
* time .
*/
if ( intsize > 0 )
return intspec [ 0 ] ;
# endif
2012-05-19 15:11:47 +09:00
pr_warning ( " no irq domain found for %s ! \n " ,
2012-06-03 22:04:33 -07:00
of_node_full_name ( controller ) ) ;
2012-02-14 14:06:52 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
}
2012-02-14 14:06:53 -07:00
/* If domain has no translation, then we assume interrupt line */
if ( domain - > ops - > xlate = = NULL )
2012-02-16 01:37:49 -07:00
hwirq = intspec [ 0 ] ;
else {
2012-02-14 14:06:53 -07:00
if ( domain - > ops - > xlate ( domain , controller , intspec , intsize ,
2012-02-16 01:37:49 -07:00
& hwirq , & type ) )
2012-02-14 14:06:52 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
}
/* Create mapping */
2012-02-14 14:06:53 -07:00
virq = irq_create_mapping ( domain , hwirq ) ;
2012-02-14 14:06:52 -07:00
if ( ! virq )
2012-02-16 01:37:49 -07:00
return virq ;
/* Set type if specified and different than the current one */
if ( type ! = IRQ_TYPE_NONE & &
type ! = ( irqd_get_trigger_type ( irq_get_irq_data ( virq ) ) ) )
irq_set_irq_type ( virq , type ) ;
return virq ;
}
EXPORT_SYMBOL_GPL ( irq_create_of_mapping ) ;
/**
* irq_dispose_mapping ( ) - Unmap an interrupt
* @ virq : linux irq number of the interrupt to unmap
*/
void irq_dispose_mapping ( unsigned int virq )
{
struct irq_data * irq_data = irq_get_irq_data ( virq ) ;
2012-02-14 14:06:53 -07:00
struct irq_domain * domain ;
2012-02-16 01:37:49 -07:00
2012-02-14 14:06:52 -07:00
if ( ! virq | | ! irq_data )
2012-02-16 01:37:49 -07:00
return ;
2012-02-14 14:06:53 -07:00
domain = irq_data - > domain ;
if ( WARN_ON ( domain = = NULL ) )
2012-02-16 01:37:49 -07:00
return ;
/* Never unmap legacy interrupts */
2012-02-14 14:06:53 -07:00
if ( domain - > revmap_type = = IRQ_DOMAIN_MAP_LEGACY )
2012-02-16 01:37:49 -07:00
return ;
2012-06-03 22:04:35 -07:00
irq_domain_disassociate_many ( domain , virq , 1 ) ;
2012-02-16 01:37:49 -07:00
irq_free_desc ( virq ) ;
}
EXPORT_SYMBOL_GPL ( irq_dispose_mapping ) ;
/**
* irq_find_mapping ( ) - Find a linux irq from an hw irq number .
2012-02-14 14:06:53 -07:00
* @ domain : domain owning this hardware interrupt
* @ hwirq : hardware irq number in that domain space
2012-02-16 01:37:49 -07:00
*/
2012-02-14 14:06:53 -07:00
unsigned int irq_find_mapping ( struct irq_domain * domain ,
2012-02-16 01:37:49 -07:00
irq_hw_number_t hwirq )
{
2012-06-03 22:04:39 -07:00
struct irq_data * data ;
2012-02-16 01:37:49 -07:00
2012-02-14 14:06:53 -07:00
/* Look for default domain if nececssary */
if ( domain = = NULL )
domain = irq_default_domain ;
if ( domain = = NULL )
2012-02-14 14:06:52 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:39 -07:00
switch ( domain - > revmap_type ) {
case IRQ_DOMAIN_MAP_LEGACY :
2012-02-14 14:06:55 -07:00
return irq_domain_legacy_revmap ( domain , hwirq ) ;
2012-06-03 22:04:39 -07:00
case IRQ_DOMAIN_MAP_LINEAR :
return irq_linear_revmap ( domain , hwirq ) ;
case IRQ_DOMAIN_MAP_TREE :
rcu_read_lock ( ) ;
data = radix_tree_lookup ( & domain - > revmap_data . tree , hwirq ) ;
rcu_read_unlock ( ) ;
if ( data )
return data - > irq ;
break ;
case IRQ_DOMAIN_MAP_NOMAP :
data = irq_get_irq_data ( hwirq ) ;
2012-02-14 14:06:53 -07:00
if ( data & & ( data - > domain = = domain ) & & ( data - > hwirq = = hwirq ) )
2012-06-03 22:04:39 -07:00
return hwirq ;
break ;
}
2012-02-14 14:06:52 -07:00
return 0 ;
2012-02-16 01:37:49 -07:00
}
EXPORT_SYMBOL_GPL ( irq_find_mapping ) ;
/**
* irq_linear_revmap ( ) - Find a linux irq from a hw irq number .
2012-02-14 14:06:53 -07:00
* @ domain : domain owning this hardware interrupt
* @ hwirq : hardware irq number in that domain space
2012-02-16 01:37:49 -07:00
*
2012-06-03 22:04:39 -07:00
* This is a fast path that can be called directly by irq controller code to
* save a handful of instructions .
2012-02-16 01:37:49 -07:00
*/
2012-02-14 14:06:53 -07:00
unsigned int irq_linear_revmap ( struct irq_domain * domain ,
2012-02-16 01:37:49 -07:00
irq_hw_number_t hwirq )
{
2012-06-03 22:04:39 -07:00
BUG_ON ( domain - > revmap_type ! = IRQ_DOMAIN_MAP_LINEAR ) ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:39 -07:00
/* Check revmap bounds; complain if exceeded */
if ( WARN_ON ( hwirq > = domain - > revmap_data . linear . size ) )
return 0 ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:39 -07:00
return domain - > revmap_data . linear . revmap [ hwirq ] ;
2012-02-16 01:37:49 -07:00
}
2012-05-19 15:11:42 +09:00
EXPORT_SYMBOL_GPL ( irq_linear_revmap ) ;
2012-02-16 01:37:49 -07:00
2012-03-29 14:10:30 -06:00
# ifdef CONFIG_IRQ_DOMAIN_DEBUG
2012-02-16 01:37:49 -07:00
static int virq_debug_show ( struct seq_file * m , void * private )
{
unsigned long flags ;
struct irq_desc * desc ;
const char * p ;
static const char none [ ] = " none " ;
void * data ;
int i ;
2012-04-11 00:26:25 -06:00
seq_printf ( m , " %-5s %-7s %-15s %-*s %s \n " , " irq " , " hwirq " ,
2012-04-12 14:42:15 -06:00
" chip name " , ( int ) ( 2 * sizeof ( void * ) + 2 ) , " chip data " ,
" domain name " ) ;
2012-02-16 01:37:49 -07:00
for ( i = 1 ; i < nr_irqs ; i + + ) {
desc = irq_to_desc ( i ) ;
if ( ! desc )
continue ;
raw_spin_lock_irqsave ( & desc - > lock , flags ) ;
if ( desc - > action & & desc - > action - > handler ) {
struct irq_chip * chip ;
seq_printf ( m , " %5d " , i ) ;
seq_printf ( m , " 0x%05lx " , desc - > irq_data . hwirq ) ;
chip = irq_desc_get_chip ( desc ) ;
if ( chip & & chip - > name )
p = chip - > name ;
else
p = none ;
seq_printf ( m , " %-15s " , p ) ;
data = irq_desc_get_chip_data ( desc ) ;
2012-04-11 00:26:25 -06:00
seq_printf ( m , data ? " 0x%p " : " %p " , data ) ;
2012-02-16 01:37:49 -07:00
2012-06-03 22:04:33 -07:00
if ( desc - > irq_data . domain )
p = of_node_full_name ( desc - > irq_data . domain - > of_node ) ;
2012-02-16 01:37:49 -07:00
else
p = none ;
seq_printf ( m , " %s \n " , p ) ;
}
raw_spin_unlock_irqrestore ( & desc - > lock , flags ) ;
}
return 0 ;
}
static int virq_debug_open ( struct inode * inode , struct file * file )
{
return single_open ( file , virq_debug_show , inode - > i_private ) ;
}
static const struct file_operations virq_debug_fops = {
. open = virq_debug_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int __init irq_debugfs_init ( void )
{
2012-03-29 14:10:30 -06:00
if ( debugfs_create_file ( " irq_domain_mapping " , S_IRUGO , NULL ,
2012-02-16 01:37:49 -07:00
NULL , & virq_debug_fops ) = = NULL )
return - ENOMEM ;
return 0 ;
}
__initcall ( irq_debugfs_init ) ;
2012-03-29 14:10:30 -06:00
# endif /* CONFIG_IRQ_DOMAIN_DEBUG */
2012-02-16 01:37:49 -07:00
2012-01-26 11:26:52 -07:00
/**
* irq_domain_xlate_onecell ( ) - Generic xlate for direct one cell bindings
*
* Device Tree IRQ specifier translation function which works with one cell
* bindings where the cell value maps directly to the hwirq number .
*/
int irq_domain_xlate_onecell ( struct irq_domain * d , struct device_node * ctrlr ,
const u32 * intspec , unsigned int intsize ,
unsigned long * out_hwirq , unsigned int * out_type )
2011-07-26 03:19:06 -06:00
{
2012-01-26 11:26:52 -07:00
if ( WARN_ON ( intsize < 1 ) )
2011-07-26 03:19:06 -06:00
return - EINVAL ;
* out_hwirq = intspec [ 0 ] ;
* out_type = IRQ_TYPE_NONE ;
return 0 ;
}
2012-01-26 11:26:52 -07:00
EXPORT_SYMBOL_GPL ( irq_domain_xlate_onecell ) ;
/**
* irq_domain_xlate_twocell ( ) - Generic xlate for direct two cell bindings
*
* Device Tree IRQ specifier translation function which works with two cell
* bindings where the cell values map directly to the hwirq number
* and linux irq flags .
*/
int irq_domain_xlate_twocell ( struct irq_domain * d , struct device_node * ctrlr ,
const u32 * intspec , unsigned int intsize ,
irq_hw_number_t * out_hwirq , unsigned int * out_type )
{
if ( WARN_ON ( intsize < 2 ) )
return - EINVAL ;
* out_hwirq = intspec [ 0 ] ;
* out_type = intspec [ 1 ] & IRQ_TYPE_SENSE_MASK ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( irq_domain_xlate_twocell ) ;
/**
* irq_domain_xlate_onetwocell ( ) - Generic xlate for one or two cell bindings
*
* Device Tree IRQ specifier translation function which works with either one
* or two cell bindings where the cell values map directly to the hwirq number
* and linux irq flags .
*
* Note : don ' t use this function unless your interrupt controller explicitly
* supports both one and two cell bindings . For the majority of controllers
* the _onecell ( ) or _twocell ( ) variants above should be used .
*/
int irq_domain_xlate_onetwocell ( struct irq_domain * d ,
struct device_node * ctrlr ,
const u32 * intspec , unsigned int intsize ,
unsigned long * out_hwirq , unsigned int * out_type )
{
if ( WARN_ON ( intsize < 1 ) )
return - EINVAL ;
* out_hwirq = intspec [ 0 ] ;
* out_type = ( intsize > 1 ) ? intspec [ 1 ] : IRQ_TYPE_NONE ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( irq_domain_xlate_onetwocell ) ;
2011-07-26 03:19:06 -06:00
2012-01-26 12:12:14 -07:00
const struct irq_domain_ops irq_domain_simple_ops = {
2012-01-26 11:26:52 -07:00
. xlate = irq_domain_xlate_onetwocell ,
2012-02-14 14:06:57 -07:00
} ;
EXPORT_SYMBOL_GPL ( irq_domain_simple_ops ) ;
# ifdef CONFIG_OF_IRQ
2011-07-26 03:19:06 -06:00
void irq_domain_generate_simple ( const struct of_device_id * match ,
u64 phys_base , unsigned int irq_start )
{
struct device_node * node ;
2012-02-14 14:06:48 -07:00
pr_debug ( " looking for phys_base=%llx, irq_start=%i \n " ,
2011-07-26 03:19:06 -06:00
( unsigned long long ) phys_base , ( int ) irq_start ) ;
node = of_find_matching_node_by_address ( NULL , match , phys_base ) ;
if ( node )
2012-01-10 17:09:30 -07:00
irq_domain_add_legacy ( node , 32 , irq_start , 0 ,
& irq_domain_simple_ops , NULL ) ;
2011-07-26 03:19:06 -06:00
}
EXPORT_SYMBOL_GPL ( irq_domain_generate_simple ) ;
2012-02-14 14:06:57 -07:00
# endif