2019-05-30 02:57:49 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-06-25 15:23:54 +04:00
/*
* OF helpers for IOMMU
*
* Copyright ( c ) 2012 , NVIDIA CORPORATION . All rights reserved .
*/
# 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>
2019-12-19 15:03:42 +03:00
# include <linux/module.h>
2019-09-03 14:30:59 +03:00
# include <linux/msi.h>
2012-06-25 15:23:54 +04:00
# 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>
2019-12-19 15:03:42 +03:00
# include <linux/pci.h>
2014-12-05 16:41:02 +03:00
# include <linux/slab.h>
2018-09-10 16:49:17 +03:00
# include <linux/fsl/mc.h>
2012-06-25 15:23:54 +04:00
2017-08-04 19:29:06 +03:00
# define NO_IOMMU 1
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 ;
2019-12-19 15:03:42 +03:00
int ret ;
2017-04-10 14:20:56 +03:00
ops = iommu_ops_from_fwnode ( fwnode ) ;
2017-04-10 14:20:57 +03:00
if ( ( ops & & ! ops - > of_xlate ) | |
2018-07-09 18:41:52 +03:00
! of_device_is_available ( iommu_spec - > np ) )
2017-08-04 19:29:06 +03:00
return NO_IOMMU ;
2017-04-10 14:20:56 +03:00
2019-12-19 15:03:42 +03:00
ret = iommu_fwspec_init ( dev , & iommu_spec - > np - > fwnode , ops ) ;
if ( ret )
return ret ;
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 )
2018-07-09 18:41:51 +03:00
return driver_deferred_probe_check_state ( dev ) ;
2017-04-10 14:20:56 +03:00
2019-12-19 15:03:42 +03:00
if ( ! try_module_get ( ops - > owner ) )
return - ENODEV ;
ret = ops - > of_xlate ( dev , iommu_spec ) ;
module_put ( ops - > owner ) ;
return ret ;
2017-04-10 14:20:56 +03:00
}
2020-06-19 11:20:08 +03:00
static int of_iommu_configure_dev_id ( struct device_node * master_np ,
struct device * dev ,
const u32 * id )
2016-09-12 19:13:41 +03:00
{
2017-05-31 20:52:29 +03:00
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
2020-06-19 11:20:08 +03:00
err = of_map_id ( master_np , * id , " iommu-map " ,
" iommu-map-mask " , & iommu_spec . np ,
iommu_spec . args ) ;
2017-04-10 14:20:56 +03:00
if ( err )
2017-08-04 19:29:06 +03:00
return err = = - ENODEV ? NO_IOMMU : err ;
2016-09-12 19:13:41 +03:00
2020-06-19 11:20:08 +03:00
err = of_iommu_xlate ( 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
}
2020-06-19 11:20:08 +03:00
static int of_iommu_configure_dev ( struct device_node * master_np ,
struct device * dev )
2018-09-10 16:49:17 +03:00
{
2020-06-19 11:20:08 +03:00
struct of_phandle_args iommu_spec ;
int err = NO_IOMMU , idx = 0 ;
while ( ! of_parse_phandle_with_args ( master_np , " iommus " ,
" #iommu-cells " ,
idx , & iommu_spec ) ) {
err = of_iommu_xlate ( dev , & iommu_spec ) ;
of_node_put ( iommu_spec . np ) ;
idx + + ;
if ( err )
break ;
}
2018-09-10 16:49:17 +03:00
return err ;
}
2020-06-19 11:20:08 +03:00
struct of_pci_iommu_alias_info {
struct device * dev ;
struct device_node * np ;
} ;
static int of_pci_iommu_init ( struct pci_dev * pdev , u16 alias , void * data )
{
struct of_pci_iommu_alias_info * info = data ;
u32 input_id = alias ;
return of_iommu_configure_dev_id ( info - > np , info - > dev , & input_id ) ;
}
static int of_iommu_configure_device ( struct device_node * master_np ,
struct device * dev , const u32 * id )
{
return ( id ) ? of_iommu_configure_dev_id ( master_np , dev , id ) :
of_iommu_configure_dev ( master_np , dev ) ;
}
2017-04-10 14:20:56 +03:00
const struct iommu_ops * of_iommu_configure ( struct device * dev ,
2020-06-19 11:20:08 +03:00
struct device_node * master_np ,
const u32 * id )
2017-04-10 14:20:56 +03:00
{
2017-05-31 20:52:29 +03:00
const struct iommu_ops * ops = NULL ;
2018-11-29 16:01:00 +03:00
struct iommu_fwspec * fwspec = dev_iommu_fwspec_get ( dev ) ;
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 ,
} ;
2019-12-19 15:03:38 +03:00
pci_request_acs ( ) ;
2017-05-31 20:52:29 +03:00
err = pci_for_each_dma_alias ( to_pci_dev ( dev ) ,
of_pci_iommu_init , & info ) ;
} else {
2020-06-19 11:20:08 +03:00
err = of_iommu_configure_device ( master_np , dev , id ) ;
2020-01-15 15:52:29 +03:00
}
2018-11-29 16:01:00 +03:00
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
*/
2018-11-29 16:01:00 +03:00
if ( ! err ) {
/* The fwspec pointer changed, read it again */
fwspec = dev_iommu_fwspec_get ( dev ) ;
ops = fwspec - > ops ;
}
2017-04-10 14:20:57 +03:00
/*
* If we have reason to believe the IOMMU driver missed the initial
2018-12-05 16:39:45 +03:00
* probe for dev , replay it to get things in order .
2017-04-10 14:20:57 +03:00
*/
2019-01-07 20:04:50 +03:00
if ( ! err & & dev - > bus & & ! device_iommu_mapped ( dev ) )
2018-12-05 16:39:45 +03:00
err = iommu_probe_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
}