2012-06-25 15:23:54 +04:00
/*
* OF helpers for IOMMU
*
* Copyright ( c ) 2012 , NVIDIA CORPORATION . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
# include <linux/export.h>
2014-08-27 19:20:32 +04:00
# include <linux/iommu.h>
2012-06-25 15:23:54 +04:00
# include <linux/limits.h>
# include <linux/of.h>
2013-12-05 05:22:53 +04:00
# include <linux/of_iommu.h>
2014-12-05 16:41:02 +03:00
# include <linux/slab.h>
2012-06-25 15:23:54 +04:00
2014-08-27 17:40:58 +04:00
static const struct of_device_id __iommu_of_table_sentinel
__used __section ( __iommu_of_table_end ) ;
2012-06-25 15:23:54 +04:00
/**
* of_get_dma_window - Parse * dma - window property and returns 0 if found .
*
* @ dn : device node
* @ prefix : prefix for property name if any
* @ index : index to start to parse
* @ busno : Returns busno if supported . Otherwise pass NULL
* @ addr : Returns address that DMA starts
* @ size : Returns the range that DMA can handle
*
* This supports different formats flexibly . " prefix " can be
* configured if any . " busno " and " index " are optionally
* specified . Set 0 ( or NULL ) if not used .
*/
int of_get_dma_window ( struct device_node * dn , const char * prefix , int index ,
unsigned long * busno , dma_addr_t * addr , size_t * size )
{
const __be32 * dma_window , * end ;
int bytes , cur_index = 0 ;
char propname [ NAME_MAX ] , addrname [ NAME_MAX ] , sizename [ NAME_MAX ] ;
if ( ! dn | | ! addr | | ! size )
return - EINVAL ;
if ( ! prefix )
prefix = " " ;
snprintf ( propname , sizeof ( propname ) , " %sdma-window " , prefix ) ;
snprintf ( addrname , sizeof ( addrname ) , " %s#dma-address-cells " , prefix ) ;
snprintf ( sizename , sizeof ( sizename ) , " %s#dma-size-cells " , prefix ) ;
dma_window = of_get_property ( dn , propname , & bytes ) ;
if ( ! dma_window )
return - ENODEV ;
end = dma_window + bytes / sizeof ( * dma_window ) ;
while ( dma_window < end ) {
u32 cells ;
const void * prop ;
/* busno is one cell if supported */
if ( busno )
* busno = be32_to_cpup ( dma_window + + ) ;
prop = of_get_property ( dn , addrname , NULL ) ;
if ( ! prop )
prop = of_get_property ( dn , " #address-cells " , NULL ) ;
cells = prop ? be32_to_cpup ( prop ) : of_n_addr_cells ( dn ) ;
if ( ! cells )
return - EINVAL ;
* addr = of_read_number ( dma_window , cells ) ;
dma_window + = cells ;
prop = of_get_property ( dn , sizename , NULL ) ;
cells = prop ? be32_to_cpup ( prop ) : of_n_size_cells ( dn ) ;
if ( ! cells )
return - EINVAL ;
* size = of_read_number ( dma_window , cells ) ;
dma_window + = cells ;
if ( cur_index + + = = index )
break ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( of_get_dma_window ) ;
2014-08-27 17:40:58 +04:00
2014-12-05 16:41:02 +03:00
struct of_iommu_node {
struct list_head list ;
struct device_node * np ;
struct iommu_ops * ops ;
} ;
static LIST_HEAD ( of_iommu_list ) ;
static DEFINE_SPINLOCK ( of_iommu_lock ) ;
void of_iommu_set_ops ( struct device_node * np , struct iommu_ops * ops )
{
struct of_iommu_node * iommu = kzalloc ( sizeof ( * iommu ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! iommu ) )
return ;
INIT_LIST_HEAD ( & iommu - > list ) ;
iommu - > np = np ;
iommu - > ops = ops ;
spin_lock ( & of_iommu_lock ) ;
list_add_tail ( & iommu - > list , & of_iommu_list ) ;
spin_unlock ( & of_iommu_lock ) ;
}
struct iommu_ops * of_iommu_get_ops ( struct device_node * np )
{
struct of_iommu_node * node ;
struct iommu_ops * ops = NULL ;
spin_lock ( & of_iommu_lock ) ;
list_for_each_entry ( node , & of_iommu_list , list )
if ( node - > np = = np ) {
ops = node - > ops ;
break ;
}
spin_unlock ( & of_iommu_lock ) ;
return ops ;
}
2015-03-03 20:52:08 +03:00
struct iommu_ops * of_iommu_configure ( struct device * dev ,
struct device_node * master_np )
2014-08-27 19:20:32 +04:00
{
struct of_phandle_args iommu_spec ;
struct device_node * np ;
struct iommu_ops * ops = NULL ;
int idx = 0 ;
2015-03-03 20:52:08 +03:00
if ( dev_is_pci ( dev ) ) {
dev_err ( dev , " IOMMU is currently not supported for PCI \n " ) ;
return NULL ;
}
2014-08-27 19:20:32 +04:00
/*
* We don ' t currently walk up the tree looking for a parent IOMMU .
* See the ` Notes : ' section of
* Documentation / devicetree / bindings / iommu / iommu . txt
*/
2015-03-03 20:52:08 +03:00
while ( ! of_parse_phandle_with_args ( master_np , " iommus " ,
2014-08-27 19:20:32 +04:00
" #iommu-cells " , idx ,
& iommu_spec ) ) {
np = iommu_spec . np ;
ops = of_iommu_get_ops ( np ) ;
if ( ! ops | | ! ops - > of_xlate | | ops - > of_xlate ( dev , & iommu_spec ) )
goto err_put_node ;
of_node_put ( np ) ;
idx + + ;
}
return ops ;
err_put_node :
of_node_put ( np ) ;
return NULL ;
}
2014-08-27 17:40:58 +04:00
void __init of_iommu_init ( void )
{
struct device_node * np ;
const struct of_device_id * match , * matches = & __iommu_of_table ;
for_each_matching_node_and_match ( np , matches , & match ) {
const of_iommu_init_fn init_fn = match - > data ;
if ( init_fn ( np ) )
pr_err ( " Failed to initialise IOMMU %s \n " ,
of_node_full_name ( np ) ) ;
}
}