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>
2016-09-12 19:13:41 +03:00
# include <linux/of_pci.h>
2014-12-05 16:41:02 +03:00
# include <linux/slab.h>
2012-06-25 15:23:54 +04:00
2017-08-04 19:29:06 +03:00
# define NO_IOMMU 1
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
2017-04-10 14:20:57 +03:00
static bool of_iommu_driver_present ( struct device_node * np )
{
/*
* If the IOMMU still isn ' t ready by the time we reach init , assume
* it never will be . We don ' t want to defer indefinitely , nor attempt
* to dereference __iommu_of_table after it ' s been freed .
*/
2017-05-16 21:42:42 +03:00
if ( system_state > = SYSTEM_RUNNING )
2017-04-10 14:20:57 +03:00
return false ;
return of_match_node ( & __iommu_of_table , np ) ;
}
2017-08-04 19:29:06 +03:00
static int of_iommu_xlate ( struct device * dev ,
struct of_phandle_args * iommu_spec )
2017-04-10 14:20:56 +03:00
{
const struct iommu_ops * ops ;
struct fwnode_handle * fwnode = & iommu_spec - > np - > fwnode ;
int err ;
ops = iommu_ops_from_fwnode ( fwnode ) ;
2017-04-10 14:20:57 +03:00
if ( ( ops & & ! ops - > of_xlate ) | |
2017-05-27 16:47:40 +03:00
! of_device_is_available ( iommu_spec - > np ) | |
2017-04-10 14:20:57 +03:00
( ! ops & & ! of_iommu_driver_present ( iommu_spec - > np ) ) )
2017-08-04 19:29:06 +03:00
return NO_IOMMU ;
2017-04-10 14:20:56 +03:00
err = iommu_fwspec_init ( dev , & iommu_spec - > np - > fwnode , ops ) ;
if ( err )
2017-08-04 19:29:06 +03:00
return err ;
2017-04-10 14:20:57 +03:00
/*
* The otherwise - empty fwspec handily serves to indicate the specific
* IOMMU device we ' re waiting for , which will be useful if we ever get
* a proper probe - ordering dependency mechanism in future .
*/
if ( ! ops )
2017-08-04 19:29:06 +03:00
return - EPROBE_DEFER ;
2017-04-10 14:20:56 +03:00
2017-08-04 19:29:06 +03:00
return ops - > of_xlate ( dev , iommu_spec ) ;
2017-04-10 14:20:56 +03:00
}
2017-05-31 20:52:29 +03:00
struct of_pci_iommu_alias_info {
struct device * dev ;
struct device_node * np ;
} ;
2016-09-12 19:13:41 +03:00
2017-05-31 20:52:29 +03:00
static int of_pci_iommu_init ( struct pci_dev * pdev , u16 alias , void * data )
2016-09-12 19:13:41 +03:00
{
2017-05-31 20:52:29 +03:00
struct of_pci_iommu_alias_info * info = data ;
struct of_phandle_args iommu_spec = { . args_count = 1 } ;
2017-04-10 14:20:56 +03:00
int err ;
2016-09-12 19:13:41 +03:00
2017-05-31 20:52:29 +03:00
err = of_pci_map_rid ( info - > np , alias , " iommu-map " ,
2017-04-10 14:20:56 +03:00
" iommu-map-mask " , & iommu_spec . np ,
iommu_spec . args ) ;
if ( err )
2017-08-04 19:29:06 +03:00
return err = = - ENODEV ? NO_IOMMU : err ;
2016-09-12 19:13:41 +03:00
2017-08-04 19:29:06 +03:00
err = of_iommu_xlate ( info - > dev , & iommu_spec ) ;
2016-09-12 19:13:41 +03:00
of_node_put ( iommu_spec . np ) ;
2017-09-21 13:20:58 +03:00
return err ;
2017-04-10 14:20:56 +03:00
}
const struct iommu_ops * of_iommu_configure ( struct device * dev ,
struct device_node * master_np )
{
2017-05-31 20:52:29 +03:00
const struct iommu_ops * ops = NULL ;
2017-04-10 14:20:57 +03:00
struct iommu_fwspec * fwspec = dev - > iommu_fwspec ;
2017-08-04 19:29:06 +03:00
int err = NO_IOMMU ;
2017-04-10 14:20:56 +03:00
if ( ! master_np )
return NULL ;
2017-04-10 14:20:57 +03:00
if ( fwspec ) {
if ( fwspec - > ops )
return fwspec - > ops ;
/* In the deferred case, start again from scratch */
iommu_fwspec_free ( dev ) ;
}
2017-05-31 20:52:29 +03: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
*/
if ( dev_is_pci ( dev ) ) {
struct of_pci_iommu_alias_info info = {
. dev = dev ,
. np = master_np ,
} ;
err = pci_for_each_dma_alias ( to_pci_dev ( dev ) ,
of_pci_iommu_init , & info ) ;
} else {
struct of_phandle_args iommu_spec ;
int idx = 0 ;
while ( ! of_parse_phandle_with_args ( master_np , " iommus " ,
" #iommu-cells " ,
idx , & iommu_spec ) ) {
2017-08-04 19:29:06 +03:00
err = of_iommu_xlate ( dev , & iommu_spec ) ;
2017-05-31 20:52:29 +03:00
of_node_put ( iommu_spec . np ) ;
idx + + ;
2017-08-04 19:29:06 +03:00
if ( err )
2017-05-31 20:52:29 +03:00
break ;
}
}
2017-08-04 19:29:06 +03:00
/*
* Two success conditions can be represented by non - negative err here :
* > 0 : there is no IOMMU , or one was unavailable for non - fatal reasons
* 0 : we found an IOMMU , and dev - > fwspec is initialised appropriately
* < 0 : any actual error
*/
if ( ! err )
ops = dev - > iommu_fwspec - > ops ;
2017-04-10 14:20:57 +03:00
/*
* If we have reason to believe the IOMMU driver missed the initial
* add_device callback for dev , replay it to get things in order .
*/
2017-08-04 19:29:06 +03:00
if ( ops & & ops - > add_device & & dev - > bus & & ! dev - > iommu_group )
2017-05-31 20:52:29 +03:00
err = ops - > add_device ( dev ) ;
2014-08-27 19:20:32 +04:00
2017-05-27 16:47:41 +03:00
/* Ignore all other errors apart from EPROBE_DEFER */
2017-08-04 19:29:06 +03:00
if ( err = = - EPROBE_DEFER ) {
ops = ERR_PTR ( err ) ;
} else if ( err < 0 ) {
dev_dbg ( dev , " Adding to IOMMU failed: %d \n " , err ) ;
2017-05-27 16:47:41 +03:00
ops = NULL ;
}
2017-04-10 14:21:02 +03:00
return ops ;
2014-08-27 19:20:32 +04:00
}