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>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/string.h>
/**
* irq_of_parse_and_map - Parse and map an interrupt into linux virq space
* @ device : Device node of the device whose interrupt is to be mapped
* @ index : Index of the interrupt to map
*
* This function is a wrapper that chains of_irq_map_one ( ) and
* irq_create_of_mapping ( ) to make things easier to callers
*/
unsigned int irq_of_parse_and_map ( struct device_node * dev , int index )
{
struct of_irq oirq ;
if ( of_irq_map_one ( dev , index , & oirq ) )
return NO_IRQ ;
return irq_create_of_mapping ( oirq . controller , oirq . specifier ,
oirq . size ) ;
}
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 .
*/
static struct device_node * of_irq_find_parent ( struct device_node * child )
{
struct device_node * p ;
const phandle * parp ;
if ( ! of_node_get ( child ) )
return NULL ;
do {
parp = of_get_property ( child , " interrupt-parent " , NULL ) ;
if ( parp = = NULL )
p = of_get_parent ( child ) ;
else {
if ( of_irq_workarounds & OF_IMAP_NO_PHANDLE )
p = of_node_get ( of_irq_dflt_pic ) ;
else
p = of_find_node_by_phandle ( * parp ) ;
}
of_node_put ( child ) ;
child = p ;
} while ( p & & of_get_property ( p , " #interrupt-cells " , NULL ) = = NULL ) ;
return p ;
}
/**
* of_irq_map_raw - Low level interrupt tree parsing
* @ parent : the device interrupt parent
* @ intspec : interrupt specifier ( " interrupts " property of the device )
* @ ointsize : size of the passed in interrupt specifier
* @ addr : address specifier ( start of " reg " property of the device )
* @ out_irq : structure of_irq filled by this function
*
* 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
* node exist for the parent .
*/
int of_irq_map_raw ( struct device_node * parent , const u32 * intspec , u32 ointsize ,
const u32 * addr , struct of_irq * out_irq )
{
struct device_node * ipar , * tnode , * old = NULL , * newpar = NULL ;
2010-06-08 17:48:08 +04:00
const __be32 * tmp , * imap , * imask ;
2010-06-08 17:48:06 +04:00
u32 intsize = 1 , addrsize , newintsize = 0 , newaddrsize = 0 ;
int imaplen , match , i ;
pr_debug ( " of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d \n " ,
parent - > full_name , intspec [ 0 ] , intspec [ 1 ] , ointsize ) ;
ipar = of_node_get ( parent ) ;
/* 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 ;
}
pr_debug ( " of_irq_map_raw: ipar=%s, size=%d \n " , ipar - > full_name , intsize ) ;
if ( ointsize ! = intsize )
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 ) ;
/* 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 " ) ;
2010-06-08 17:48:08 +04:00
for ( i = 0 ; i < intsize ; i + + )
out_irq - > specifier [ i ] =
of_read_number ( intspec + i , 1 ) ;
2010-06-08 17:48:06 +04:00
out_irq - > size = intsize ;
out_irq - > controller = ipar ;
of_node_put ( old ) ;
return 0 ;
}
/* 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 ) ;
/* If we were passed no "reg" property and we attempt to parse
* an interrupt - map , then # address - cells must be 0.
* Fail if it ' s not .
*/
if ( addr = = NULL & & addrsize ! = 0 ) {
pr_debug ( " -> no reg passed in when needed ! \n " ) ;
goto fail ;
}
/* Parse interrupt-map */
match = 0 ;
while ( imaplen > ( addrsize + intsize + 1 ) & & ! match ) {
/* Compare specifiers */
match = 1 ;
for ( i = 0 ; i < addrsize & & match ; + + i ) {
u32 mask = imask ? imask [ i ] : 0xffffffffu ;
match = ( ( addr [ i ] ^ imap [ i ] ) & mask ) = = 0 ;
}
for ( ; i < ( addrsize + intsize ) & & match ; + + i ) {
u32 mask = imask ? imask [ i ] : 0xffffffffu ;
match =
( ( intspec [ i - addrsize ] ^ imap [ i ] ) & mask ) = = 0 ;
}
imap + = addrsize + intsize ;
imaplen - = addrsize + intsize ;
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
newpar = of_find_node_by_phandle ( ( phandle ) * imap ) ;
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 */
if ( imaplen < ( newaddrsize + newintsize ) )
goto fail ;
imap + = newaddrsize + newintsize ;
imaplen - = newaddrsize + newintsize ;
pr_debug ( " -> imaplen=%d \n " , imaplen ) ;
}
if ( ! match )
goto fail ;
of_node_put ( old ) ;
old = of_node_get ( newpar ) ;
addrsize = newaddrsize ;
intsize = newintsize ;
intspec = imap - intsize ;
addr = intspec - addrsize ;
skiplevel :
/* Iterate again with new parent */
pr_debug ( " -> new parent: %s \n " , newpar ? newpar - > full_name : " <> " ) ;
of_node_put ( ipar ) ;
ipar = newpar ;
newpar = NULL ;
}
fail :
of_node_put ( ipar ) ;
of_node_put ( old ) ;
of_node_put ( newpar ) ;
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( of_irq_map_raw ) ;
/**
* of_irq_map_one - Resolve an interrupt for a device
* @ 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
*
* This function resolves an interrupt , walking the tree , for a given
* device - tree node . It ' s the high level pendant to of_irq_map_raw ( ) .
*/
int of_irq_map_one ( struct device_node * device , int index , struct of_irq * out_irq )
{
struct device_node * p ;
const u32 * intspec , * tmp , * addr ;
u32 intsize , intlen ;
int res = - EINVAL ;
pr_debug ( " of_irq_map_one: dev=%s, index=%d \n " , device - > full_name , index ) ;
/* OldWorld mac stuff is "special", handle out of line */
if ( of_irq_workarounds & OF_IMAP_OLDWORLD_MAC )
return of_irq_map_oldworld ( device , index , out_irq ) ;
/* Get the interrupts property */
intspec = of_get_property ( device , " interrupts " , & intlen ) ;
if ( intspec = = NULL )
return - EINVAL ;
intlen / = sizeof ( u32 ) ;
pr_debug ( " intspec=%d intlen=%d \n " , * intspec , intlen ) ;
/* Get the reg property (if any) */
addr = of_get_property ( device , " reg " , NULL ) ;
/* 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 ;
/* Get new specifier and map it */
res = of_irq_map_raw ( p , intspec + index * intsize , intsize ,
addr , out_irq ) ;
out :
of_node_put ( p ) ;
return res ;
}
EXPORT_SYMBOL_GPL ( of_irq_map_one ) ;
/**
* 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 . */
if ( r & & irq ! = NO_IRQ ) {
r - > start = r - > end = irq ;
r - > flags = IORESOURCE_IRQ ;
2010-06-08 17:48:12 +04:00
r - > name = dev - > full_name ;
2010-06-08 17:48:06 +04:00
}
return irq ;
}
EXPORT_SYMBOL_GPL ( of_irq_to_resource ) ;