2018-01-17 19:36:44 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-01-07 01:03:22 +03:00
/*
* Freescale Management Complex ( MC ) bus driver MSI support
*
2016-10-26 19:20:33 +03:00
* Copyright ( C ) 2015 - 2016 Freescale Semiconductor , Inc .
2016-01-07 01:03:22 +03:00
* Author : German Rivera < German . Rivera @ freescale . com >
*
*/
2020-06-19 11:20:13 +03:00
# include <linux/acpi.h>
# include <linux/acpi_iort.h>
2016-01-07 01:03:22 +03:00
# include <linux/of_device.h>
# include <linux/of_address.h>
# include <linux/irq.h>
# include <linux/msi.h>
# include <linux/of.h>
# include <linux/of_irq.h>
2018-02-05 17:07:42 +03:00
# include <linux/fsl/mc.h>
2016-01-07 01:03:22 +03:00
static struct irq_chip its_msi_irq_chip = {
2016-10-17 21:42:53 +03:00
. name = " ITS-fMSI " ,
2016-01-07 01:03:22 +03:00
. irq_mask = irq_chip_mask_parent ,
. irq_unmask = irq_chip_unmask_parent ,
. irq_eoi = irq_chip_eoi_parent ,
. irq_set_affinity = msi_domain_set_affinity
} ;
2020-06-19 11:20:12 +03:00
static u32 fsl_mc_msi_domain_get_msi_id ( struct irq_domain * domain ,
struct fsl_mc_device * mc_dev )
{
struct device_node * of_node ;
u32 out_id ;
of_node = irq_domain_get_of_node ( domain ) ;
2020-06-19 11:20:13 +03:00
out_id = of_node ? of_msi_map_id ( & mc_dev - > dev , of_node , mc_dev - > icid ) :
iort_msi_map_id ( & mc_dev - > dev , mc_dev - > icid ) ;
2020-06-19 11:20:12 +03:00
return out_id ;
}
2016-01-07 01:03:22 +03:00
static int its_fsl_mc_msi_prepare ( struct irq_domain * msi_domain ,
struct device * dev ,
int nvec , msi_alloc_info_t * info )
{
struct fsl_mc_device * mc_bus_dev ;
struct msi_domain_info * msi_info ;
2017-11-17 16:38:32 +03:00
if ( ! dev_is_fsl_mc ( dev ) )
2016-01-07 01:03:22 +03:00
return - EINVAL ;
mc_bus_dev = to_fsl_mc_device ( dev ) ;
2017-11-17 16:38:32 +03:00
if ( ! ( mc_bus_dev - > flags & FSL_MC_IS_DPRC ) )
2016-01-07 01:03:22 +03:00
return - EINVAL ;
/*
* Set the device Id to be passed to the GIC - ITS :
*
* NOTE : This device id corresponds to the IOMMU stream ID
* associated with the DPRC object ( ICID ) .
*/
2020-06-19 11:20:12 +03:00
info - > scratchpad [ 0 ] . ul = fsl_mc_msi_domain_get_msi_id ( msi_domain ,
mc_bus_dev ) ;
2016-01-07 01:03:22 +03:00
msi_info = msi_get_domain_info ( msi_domain - > parent ) ;
2018-05-27 18:39:55 +03:00
/* Allocate at least 32 MSIs, and always as a power of 2 */
nvec = max_t ( int , 32 , roundup_pow_of_two ( nvec ) ) ;
2016-01-07 01:03:22 +03:00
return msi_info - > ops - > msi_prepare ( msi_domain - > parent , dev , nvec , info ) ;
}
2017-02-11 04:37:57 +03:00
static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = {
2016-01-07 01:03:22 +03:00
. msi_prepare = its_fsl_mc_msi_prepare ,
} ;
static struct msi_domain_info its_fsl_mc_msi_domain_info = {
. flags = ( MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS ) ,
. ops = & its_fsl_mc_msi_ops ,
. chip = & its_msi_irq_chip ,
} ;
static const struct of_device_id its_device_id [ ] = {
{ . compatible = " arm,gic-v3-its " , } ,
{ } ,
} ;
2020-06-19 11:20:13 +03:00
static void __init its_fsl_mc_msi_init_one ( struct fwnode_handle * handle ,
const char * name )
2016-01-07 01:03:22 +03:00
{
struct irq_domain * parent ;
struct irq_domain * mc_msi_domain ;
2020-06-19 11:20:13 +03:00
parent = irq_find_matching_fwnode ( handle , DOMAIN_BUS_NEXUS ) ;
if ( ! parent | | ! msi_get_domain_info ( parent ) ) {
pr_err ( " %s: unable to locate ITS domain \n " , name ) ;
return ;
}
mc_msi_domain = fsl_mc_msi_create_irq_domain ( handle ,
& its_fsl_mc_msi_domain_info ,
parent ) ;
if ( ! mc_msi_domain ) {
pr_err ( " %s: unable to create fsl-mc domain \n " , name ) ;
return ;
}
pr_info ( " fsl-mc MSI: %s domain created \n " , name ) ;
}
# ifdef CONFIG_ACPI
static int __init
its_fsl_mc_msi_parse_madt ( union acpi_subtable_headers * header ,
const unsigned long end )
{
struct acpi_madt_generic_translator * its_entry ;
struct fwnode_handle * dom_handle ;
const char * node_name ;
int err = 0 ;
its_entry = ( struct acpi_madt_generic_translator * ) header ;
node_name = kasprintf ( GFP_KERNEL , " ITS@0x%lx " ,
( long ) its_entry - > base_address ) ;
dom_handle = iort_find_domain_token ( its_entry - > translation_id ) ;
if ( ! dom_handle ) {
pr_err ( " %s: Unable to locate ITS domain handle \n " , node_name ) ;
err = - ENXIO ;
goto out ;
}
its_fsl_mc_msi_init_one ( dom_handle , node_name ) ;
out :
kfree ( node_name ) ;
return err ;
}
static void __init its_fsl_mc_acpi_msi_init ( void )
{
acpi_table_parse_madt ( ACPI_MADT_TYPE_GENERIC_TRANSLATOR ,
its_fsl_mc_msi_parse_madt , 0 ) ;
}
# else
static inline void its_fsl_mc_acpi_msi_init ( void ) { }
# endif
static void __init its_fsl_mc_of_msi_init ( void )
{
struct device_node * np ;
2016-01-07 01:03:22 +03:00
for ( np = of_find_matching_node ( NULL , its_device_id ) ; np ;
np = of_find_matching_node ( np , its_device_id ) ) {
2018-02-01 20:03:29 +03:00
if ( ! of_device_is_available ( np ) )
continue ;
2016-01-07 01:03:22 +03:00
if ( ! of_property_read_bool ( np , " msi-controller " ) )
continue ;
2020-06-19 11:20:13 +03:00
its_fsl_mc_msi_init_one ( of_node_to_fwnode ( np ) ,
np - > full_name ) ;
2016-01-07 01:03:22 +03:00
}
2020-06-19 11:20:13 +03:00
}
static int __init its_fsl_mc_msi_init ( void )
{
its_fsl_mc_of_msi_init ( ) ;
its_fsl_mc_acpi_msi_init ( ) ;
2016-01-07 01:03:22 +03:00
return 0 ;
}
2018-01-16 16:19:07 +03:00
early_initcall ( its_fsl_mc_msi_init ) ;