2010-06-18 21:09:59 +04:00
/*
* Derived from arch / i386 / kernel / irq . c
* Copyright ( C ) 1992 Linus Torvalds
* Adapted from arch / i386 by Gary Thomas
* Copyright ( C ) 1995 - 1996 Gary Thomas ( gdt @ linuxppc . org )
* Updated and modified by Cort Dougan < cort @ fsmlabs . com >
* Copyright ( C ) 1996 - 2001 Cort Dougan
* Adapted for Power Macintosh by Paul Mackerras
* Copyright ( C ) 1996 Paul Mackerras ( paulus @ cs . anu . edu . au )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* This file contains the code used to make IRQ descriptions in the
* device tree to actual irq numbers on an interrupt controller
* driver .
*/
# include <linux/errno.h>
2011-09-21 00:13:50 +04:00
# include <linux/list.h>
2010-06-18 21:09:59 +04:00
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/string.h>
2011-09-21 00:13:50 +04:00
# include <linux/slab.h>
2010-06-18 21:09:59 +04:00
/**
* irq_of_parse_and_map - Parse and map an interrupt into linux virq space
2013-08-31 05:44:10 +04:00
* @ dev : Device node of the device whose interrupt is to be mapped
2010-06-18 21:09:59 +04:00
* @ index : Index of the interrupt to map
*
2013-09-19 20:22:36 +04:00
* This function is a wrapper that chains of_irq_parse_one ( ) and
2010-06-18 21:09:59 +04:00
* irq_create_of_mapping ( ) to make things easier to callers
*/
unsigned int irq_of_parse_and_map ( struct device_node * dev , int index )
{
2013-09-15 19:39:11 +04:00
struct of_phandle_args oirq ;
2010-06-18 21:09:59 +04:00
2013-09-19 20:22:36 +04:00
if ( of_irq_parse_one ( dev , index , & oirq ) )
2011-12-07 03:16:26 +04:00
return 0 ;
2010-06-18 21:09:59 +04:00
2013-09-15 19:55:53 +04:00
return irq_create_of_mapping ( & oirq ) ;
2010-06-18 21:09:59 +04:00
}
EXPORT_SYMBOL_GPL ( irq_of_parse_and_map ) ;
2010-06-08 17:48:06 +04:00
/**
* of_irq_find_parent - Given a device node , find its interrupt parent node
* @ child : pointer to device node
*
* Returns a pointer to the interrupt parent node , or NULL if the interrupt
* parent could not be determined .
*/
2011-04-15 02:31:57 +04:00
struct device_node * of_irq_find_parent ( struct device_node * child )
2010-06-08 17:48:06 +04:00
{
2011-11-23 03:09:20 +04:00
struct device_node * p ;
2010-07-23 11:48:25 +04:00
const __be32 * parp ;
2010-06-08 17:48:06 +04:00
2011-11-23 03:09:20 +04:00
if ( ! of_node_get ( child ) )
2010-06-08 17:48:06 +04:00
return NULL ;
do {
2011-11-23 03:09:20 +04:00
parp = of_get_property ( child , " interrupt-parent " , NULL ) ;
2010-06-08 17:48:06 +04:00
if ( parp = = NULL )
2011-11-23 03:09:20 +04:00
p = of_get_parent ( child ) ;
2010-06-08 17:48:06 +04:00
else {
if ( of_irq_workarounds & OF_IMAP_NO_PHANDLE )
p = of_node_get ( of_irq_dflt_pic ) ;
else
2010-07-23 11:48:25 +04:00
p = of_find_node_by_phandle ( be32_to_cpup ( parp ) ) ;
2010-06-08 17:48:06 +04:00
}
2011-11-23 03:09:20 +04:00
of_node_put ( child ) ;
child = p ;
2010-06-08 17:48:06 +04:00
} while ( p & & of_get_property ( p , " #interrupt-cells " , NULL ) = = NULL ) ;
2011-11-23 03:09:20 +04:00
return p ;
2010-06-08 17:48:06 +04:00
}
/**
2013-09-19 20:22:36 +04:00
* of_irq_parse_raw - Low level interrupt tree parsing
2010-06-08 17:48:06 +04:00
* @ parent : the device interrupt parent
2013-09-16 01:32:39 +04:00
* @ addr : address specifier ( start of " reg " property of the device ) in be32 format
* @ out_irq : structure of_irq updated by this function
2010-06-08 17:48:06 +04:00
*
* Returns 0 on success and a negative number on error
*
* This function is a low - level interrupt tree walking function . It
* can be used to do a partial walk with synthetized reg and interrupts
* properties , for example when resolving PCI interrupts when no device
2013-09-16 01:32:39 +04:00
* node exist for the parent . It takes an interrupt specifier structure as
* input , walks the tree looking for any interrupt - map properties , translates
* the specifier for each map , and then returns the translated map .
2010-06-08 17:48:06 +04:00
*/
2013-09-16 01:32:39 +04:00
int of_irq_parse_raw ( const __be32 * addr , struct of_phandle_args * out_irq )
2010-06-08 17:48:06 +04:00
{
struct device_node * ipar , * tnode , * old = NULL , * newpar = NULL ;
2013-11-02 11:11:02 +04:00
__be32 initial_match_array [ MAX_PHANDLE_ARGS ] ;
2013-09-16 01:32:39 +04:00
const __be32 * match_array = initial_match_array ;
2013-11-02 11:11:02 +04:00
const __be32 * tmp , * imap , * imask , dummy_imask [ ] = { [ 0 . . . MAX_PHANDLE_ARGS ] = ~ 0 } ;
2010-06-08 17:48:06 +04:00
u32 intsize = 1 , addrsize , newintsize = 0 , newaddrsize = 0 ;
int imaplen , match , i ;
2013-10-12 01:05:10 +04:00
# ifdef DEBUG
of_print_phandle_args ( " of_irq_parse_raw: " , out_irq ) ;
# endif
2010-06-08 17:48:06 +04:00
2013-09-16 01:32:39 +04:00
ipar = of_node_get ( out_irq - > np ) ;
2010-06-08 17:48:06 +04:00
/* First get the #interrupt-cells property of the current cursor
* that tells us how to interpret the passed - in intspec . If there
* is none , we are nice and just walk up the tree
*/
do {
tmp = of_get_property ( ipar , " #interrupt-cells " , NULL ) ;
if ( tmp ! = NULL ) {
2010-06-08 17:48:08 +04:00
intsize = be32_to_cpu ( * tmp ) ;
2010-06-08 17:48:06 +04:00
break ;
}
tnode = ipar ;
ipar = of_irq_find_parent ( ipar ) ;
of_node_put ( tnode ) ;
} while ( ipar ) ;
if ( ipar = = NULL ) {
pr_debug ( " -> no parent found ! \n " ) ;
goto fail ;
}
2013-09-19 20:22:36 +04:00
pr_debug ( " of_irq_parse_raw: ipar=%s, size=%d \n " , of_node_full_name ( ipar ) , intsize ) ;
2010-06-08 17:48:06 +04:00
2013-09-16 01:32:39 +04:00
if ( out_irq - > args_count ! = intsize )
2010-06-08 17:48:06 +04:00
return - EINVAL ;
/* Look for this #address-cells. We have to implement the old linux
* trick of looking for the parent here as some device - trees rely on it
*/
old = of_node_get ( ipar ) ;
do {
tmp = of_get_property ( old , " #address-cells " , NULL ) ;
tnode = of_get_parent ( old ) ;
of_node_put ( old ) ;
old = tnode ;
} while ( old & & tmp = = NULL ) ;
of_node_put ( old ) ;
old = NULL ;
2010-06-08 17:48:08 +04:00
addrsize = ( tmp = = NULL ) ? 2 : be32_to_cpu ( * tmp ) ;
2010-06-08 17:48:06 +04:00
pr_debug ( " -> addrsize=%d \n " , addrsize ) ;
2013-11-02 11:11:02 +04:00
/* Range check so that the temporary buffer doesn't overflow */
if ( WARN_ON ( addrsize + intsize > MAX_PHANDLE_ARGS ) )
goto fail ;
2013-09-16 01:32:39 +04:00
/* Precalculate the match array - this simplifies match loop */
for ( i = 0 ; i < addrsize ; i + + )
2013-11-01 21:50:50 +04:00
initial_match_array [ i ] = addr ? addr [ i ] : 0 ;
2013-09-16 01:32:39 +04:00
for ( i = 0 ; i < intsize ; i + + )
initial_match_array [ addrsize + i ] = cpu_to_be32 ( out_irq - > args [ i ] ) ;
2010-06-08 17:48:06 +04:00
/* Now start the actual "proper" walk of the interrupt tree */
while ( ipar ! = NULL ) {
/* Now check if cursor is an interrupt-controller and if it is
* then we are done
*/
if ( of_get_property ( ipar , " interrupt-controller " , NULL ) ! =
NULL ) {
pr_debug ( " -> got it ! \n " ) ;
of_node_put ( old ) ;
return 0 ;
}
2013-11-01 21:50:50 +04:00
/*
* interrupt - map parsing does not work without a reg
* property when # address - cells ! = 0
*/
if ( addrsize & & ! addr ) {
pr_debug ( " -> no reg passed in when needed ! \n " ) ;
goto fail ;
}
2010-06-08 17:48:06 +04:00
/* Now look for an interrupt-map */
imap = of_get_property ( ipar , " interrupt-map " , & imaplen ) ;
/* No interrupt map, check for an interrupt parent */
if ( imap = = NULL ) {
pr_debug ( " -> no map, getting parent \n " ) ;
newpar = of_irq_find_parent ( ipar ) ;
goto skiplevel ;
}
imaplen / = sizeof ( u32 ) ;
/* Look for a mask */
imask = of_get_property ( ipar , " interrupt-map-mask " , NULL ) ;
2013-09-16 01:32:39 +04:00
if ( ! imask )
imask = dummy_imask ;
2010-06-08 17:48:06 +04:00
/* Parse interrupt-map */
match = 0 ;
while ( imaplen > ( addrsize + intsize + 1 ) & & ! match ) {
/* Compare specifiers */
match = 1 ;
2013-09-16 01:32:39 +04:00
for ( i = 0 ; i < ( addrsize + intsize ) ; i + + , imaplen - - )
match = ! ( ( match_array [ i ] ^ * imap + + ) & imask [ i ] ) ;
2010-06-08 17:48:06 +04:00
pr_debug ( " -> match=%d (imaplen=%d) \n " , match , imaplen ) ;
/* Get the interrupt parent */
if ( of_irq_workarounds & OF_IMAP_NO_PHANDLE )
newpar = of_node_get ( of_irq_dflt_pic ) ;
else
2010-07-23 11:48:25 +04:00
newpar = of_find_node_by_phandle ( be32_to_cpup ( imap ) ) ;
2010-06-08 17:48:06 +04:00
imap + + ;
- - imaplen ;
/* Check if not found */
if ( newpar = = NULL ) {
pr_debug ( " -> imap parent not found ! \n " ) ;
goto fail ;
}
/* Get #interrupt-cells and #address-cells of new
* parent
*/
tmp = of_get_property ( newpar , " #interrupt-cells " , NULL ) ;
if ( tmp = = NULL ) {
pr_debug ( " -> parent lacks #interrupt-cells! \n " ) ;
goto fail ;
}
2010-06-08 17:48:08 +04:00
newintsize = be32_to_cpu ( * tmp ) ;
2010-06-08 17:48:06 +04:00
tmp = of_get_property ( newpar , " #address-cells " , NULL ) ;
2010-06-08 17:48:08 +04:00
newaddrsize = ( tmp = = NULL ) ? 0 : be32_to_cpu ( * tmp ) ;
2010-06-08 17:48:06 +04:00
pr_debug ( " -> newintsize=%d, newaddrsize=%d \n " ,
newintsize , newaddrsize ) ;
/* Check for malformed properties */
2013-11-02 11:11:02 +04:00
if ( WARN_ON ( newaddrsize + newintsize > MAX_PHANDLE_ARGS ) )
goto fail ;
2010-06-08 17:48:06 +04:00
if ( imaplen < ( newaddrsize + newintsize ) )
goto fail ;
imap + = newaddrsize + newintsize ;
imaplen - = newaddrsize + newintsize ;
pr_debug ( " -> imaplen=%d \n " , imaplen ) ;
}
if ( ! match )
goto fail ;
2013-09-16 01:32:39 +04:00
/*
* Successfully parsed an interrrupt - map translation ; copy new
* interrupt specifier into the out_irq structure
*/
of_node_put ( out_irq - > np ) ;
out_irq - > np = of_node_get ( newpar ) ;
match_array = imap - newaddrsize - newintsize ;
for ( i = 0 ; i < newintsize ; i + + )
out_irq - > args [ i ] = be32_to_cpup ( imap - newintsize + i ) ;
out_irq - > args_count = intsize = newintsize ;
2010-06-08 17:48:06 +04:00
addrsize = newaddrsize ;
skiplevel :
/* Iterate again with new parent */
2012-06-15 21:50:25 +04:00
pr_debug ( " -> new parent: %s \n " , of_node_full_name ( newpar ) ) ;
2010-06-08 17:48:06 +04:00
of_node_put ( ipar ) ;
ipar = newpar ;
newpar = NULL ;
}
fail :
of_node_put ( ipar ) ;
2013-09-16 01:32:39 +04:00
of_node_put ( out_irq - > np ) ;
2010-06-08 17:48:06 +04:00
of_node_put ( newpar ) ;
return - EINVAL ;
}
2013-09-19 20:22:36 +04:00
EXPORT_SYMBOL_GPL ( of_irq_parse_raw ) ;
2010-06-08 17:48:06 +04:00
/**
2013-09-19 20:22:36 +04:00
* of_irq_parse_one - Resolve an interrupt for a device
2010-06-08 17:48:06 +04:00
* @ device : the device whose interrupt is to be resolved
* @ index : index of the interrupt to resolve
* @ out_irq : structure of_irq filled by this function
*
2013-09-16 01:32:39 +04:00
* This function resolves an interrupt for a node by walking the interrupt tree ,
* finding which interrupt controller node it is attached to , and returning the
* interrupt specifier that can be used to retrieve a Linux IRQ number .
2010-06-08 17:48:06 +04:00
*/
2013-09-15 19:39:11 +04:00
int of_irq_parse_one ( struct device_node * device , int index , struct of_phandle_args * out_irq )
2010-06-08 17:48:06 +04:00
{
struct device_node * p ;
2010-07-24 02:56:19 +04:00
const __be32 * intspec , * tmp , * addr ;
2010-06-08 17:48:06 +04:00
u32 intsize , intlen ;
2013-09-16 01:32:39 +04:00
int i , res = - EINVAL ;
2010-06-08 17:48:06 +04:00
2013-09-19 20:22:36 +04:00
pr_debug ( " of_irq_parse_one: dev=%s, index=%d \n " , of_node_full_name ( device ) , index ) ;
2010-06-08 17:48:06 +04:00
/* OldWorld mac stuff is "special", handle out of line */
if ( of_irq_workarounds & OF_IMAP_OLDWORLD_MAC )
2013-09-19 20:22:36 +04:00
return of_irq_parse_oldworld ( device , index , out_irq ) ;
2010-06-08 17:48:06 +04:00
2013-09-20 01:47:37 +04:00
/* Get the reg property (if any) */
addr = of_get_property ( device , " reg " , NULL ) ;
2010-06-08 17:48:06 +04:00
/* Get the interrupts property */
intspec = of_get_property ( device , " interrupts " , & intlen ) ;
2013-09-20 01:47:37 +04:00
if ( intspec = = NULL ) {
/* Try the new-style interrupts-extended */
res = of_parse_phandle_with_args ( device , " interrupts-extended " ,
" #interrupt-cells " , index , out_irq ) ;
if ( res )
return - EINVAL ;
return of_irq_parse_raw ( addr , out_irq ) ;
}
2010-07-24 02:56:19 +04:00
intlen / = sizeof ( * intspec ) ;
2010-06-08 17:48:06 +04:00
2010-07-24 02:56:19 +04:00
pr_debug ( " intspec=%d intlen=%d \n " , be32_to_cpup ( intspec ) , intlen ) ;
2010-06-08 17:48:06 +04:00
/* Look for the interrupt parent. */
p = of_irq_find_parent ( device ) ;
if ( p = = NULL )
return - EINVAL ;
/* Get size of interrupt specifier */
tmp = of_get_property ( p , " #interrupt-cells " , NULL ) ;
if ( tmp = = NULL )
goto out ;
2010-06-08 17:48:08 +04:00
intsize = be32_to_cpu ( * tmp ) ;
2010-06-08 17:48:06 +04:00
pr_debug ( " intsize=%d intlen=%d \n " , intsize , intlen ) ;
/* Check index */
if ( ( index + 1 ) * intsize > intlen )
goto out ;
2013-09-16 01:32:39 +04:00
/* Copy intspec into irq structure */
intspec + = index * intsize ;
out_irq - > np = p ;
out_irq - > args_count = intsize ;
for ( i = 0 ; i < intsize ; i + + )
out_irq - > args [ i ] = be32_to_cpup ( intspec + + ) ;
/* Check if there are any interrupt-map translations to process */
res = of_irq_parse_raw ( addr , out_irq ) ;
2010-06-08 17:48:06 +04:00
out :
of_node_put ( p ) ;
return res ;
}
2013-09-19 20:22:36 +04:00
EXPORT_SYMBOL_GPL ( of_irq_parse_one ) ;
2010-06-08 17:48:06 +04:00
/**
* of_irq_to_resource - Decode a node ' s IRQ and return it as a resource
* @ dev : pointer to device tree node
* @ index : zero - based index of the irq
* @ r : pointer to resource structure to return result into .
*/
int of_irq_to_resource ( struct device_node * dev , int index , struct resource * r )
{
int irq = irq_of_parse_and_map ( dev , index ) ;
/* Only dereference the resource if both the
* resource and the irq are valid . */
2011-12-07 03:16:26 +04:00
if ( r & & irq ) {
2011-12-05 18:23:56 +04:00
const char * name = NULL ;
2013-07-18 14:24:10 +04:00
memset ( r , 0 , sizeof ( * r ) ) ;
2011-12-05 18:23:56 +04:00
/*
* Get optional " interrupts-names " property to add a name
* to the resource .
*/
of_property_read_string_index ( dev , " interrupt-names " , index ,
& name ) ;
2010-06-08 17:48:06 +04:00
r - > start = r - > end = irq ;
2013-09-28 21:52:51 +04:00
r - > flags = IORESOURCE_IRQ | irqd_get_trigger_type ( irq_get_irq_data ( irq ) ) ;
2013-09-19 20:01:52 +04:00
r - > name = name ? name : of_node_full_name ( dev ) ;
2010-06-08 17:48:06 +04:00
}
return irq ;
}
EXPORT_SYMBOL_GPL ( of_irq_to_resource ) ;
2010-10-11 07:35:05 +04:00
/**
* of_irq_count - Count the number of IRQs a node uses
* @ dev : pointer to device tree node
*/
int of_irq_count ( struct device_node * dev )
{
2013-09-18 17:24:43 +04:00
struct of_phandle_args irq ;
2010-10-11 07:35:05 +04:00
int nr = 0 ;
2013-09-18 17:24:43 +04:00
while ( of_irq_parse_one ( dev , nr , & irq ) = = 0 )
2010-10-11 07:35:05 +04:00
nr + + ;
return nr ;
}
/**
* of_irq_to_resource_table - Fill in resource table with node ' s IRQ info
* @ dev : pointer to device tree node
* @ res : array of resources to fill in
* @ nr_irqs : the number of IRQs ( and upper bound for num of @ res elements )
*
* Returns the size of the filled in table ( up to @ nr_irqs ) .
*/
int of_irq_to_resource_table ( struct device_node * dev , struct resource * res ,
int nr_irqs )
{
int i ;
for ( i = 0 ; i < nr_irqs ; i + + , res + + )
2011-12-07 03:16:26 +04:00
if ( ! of_irq_to_resource ( dev , i , res ) )
2010-10-11 07:35:05 +04:00
break ;
return i ;
}
2012-08-26 11:13:09 +04:00
EXPORT_SYMBOL_GPL ( of_irq_to_resource_table ) ;
2011-09-21 00:13:50 +04:00
struct intc_desc {
struct list_head list ;
struct device_node * dev ;
struct device_node * interrupt_parent ;
} ;
/**
* of_irq_init - Scan and init matching interrupt controllers in DT
* @ matches : 0 terminated array of nodes to match and init function to call
*
* This function scans the device tree for matching interrupt controller nodes ,
* and calls their initialization functions in order with parents first .
*/
void __init of_irq_init ( const struct of_device_id * matches )
{
struct device_node * np , * parent = NULL ;
struct intc_desc * desc , * temp_desc ;
struct list_head intc_desc_list , intc_parent_list ;
INIT_LIST_HEAD ( & intc_desc_list ) ;
INIT_LIST_HEAD ( & intc_parent_list ) ;
for_each_matching_node ( np , matches ) {
if ( ! of_find_property ( np , " interrupt-controller " , NULL ) )
continue ;
/*
* Here , we allocate and populate an intc_desc with the node
* pointer , interrupt - parent device_node etc .
*/
desc = kzalloc ( sizeof ( * desc ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! desc ) )
goto err ;
desc - > dev = np ;
desc - > interrupt_parent = of_irq_find_parent ( np ) ;
2011-11-28 06:16:33 +04:00
if ( desc - > interrupt_parent = = np )
desc - > interrupt_parent = NULL ;
2011-09-21 00:13:50 +04:00
list_add_tail ( & desc - > list , & intc_desc_list ) ;
}
/*
* The root irq controller is the one without an interrupt - parent .
* That one goes first , followed by the controllers that reference it ,
* followed by the ones that reference the 2 nd level controllers , etc .
*/
while ( ! list_empty ( & intc_desc_list ) ) {
/*
* Process all controllers with the current ' parent ' .
* First pass will be looking for NULL as the parent .
* The assumption is that NULL parent means a root controller .
*/
list_for_each_entry_safe ( desc , temp_desc , & intc_desc_list , list ) {
const struct of_device_id * match ;
int ret ;
of_irq_init_cb_t irq_init_cb ;
if ( desc - > interrupt_parent ! = parent )
continue ;
list_del ( & desc - > list ) ;
match = of_match_node ( matches , desc - > dev ) ;
if ( WARN ( ! match - > data ,
" of_irq_init: no init function for %s \n " ,
match - > compatible ) ) {
kfree ( desc ) ;
continue ;
}
pr_debug ( " of_irq_init: init %s @ %p, parent %p \n " ,
match - > compatible ,
desc - > dev , desc - > interrupt_parent ) ;
2012-10-09 04:42:04 +04:00
irq_init_cb = ( of_irq_init_cb_t ) match - > data ;
2011-09-21 00:13:50 +04:00
ret = irq_init_cb ( desc - > dev , desc - > interrupt_parent ) ;
if ( ret ) {
kfree ( desc ) ;
continue ;
}
/*
* This one is now set up ; add it to the parent list so
* its children can get processed in a subsequent pass .
*/
list_add_tail ( & desc - > list , & intc_parent_list ) ;
}
/* Get the next pending parent that might have children */
2013-06-23 11:50:07 +04:00
desc = list_first_entry_or_null ( & intc_parent_list ,
typeof ( * desc ) , list ) ;
if ( ! desc ) {
2011-09-21 00:13:50 +04:00
pr_err ( " of_irq_init: children remain, but no parents \n " ) ;
break ;
}
list_del ( & desc - > list ) ;
parent = desc - > dev ;
kfree ( desc ) ;
}
list_for_each_entry_safe ( desc , temp_desc , & intc_parent_list , list ) {
list_del ( & desc - > list ) ;
kfree ( desc ) ;
}
err :
list_for_each_entry_safe ( desc , temp_desc , & intc_desc_list , list ) {
list_del ( & desc - > list ) ;
kfree ( desc ) ;
}
}