2019-06-03 08:44:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-07-28 16:46:22 +03:00
/*
* Copyright ( C ) 2013 - 2015 ARM Limited , All Rights Reserved .
* Author : Marc Zyngier < marc . zyngier @ arm . com >
*/
2017-03-07 15:40:02 +03:00
# include <linux/acpi_iort.h>
2015-07-28 16:46:22 +03:00
# include <linux/device.h>
# include <linux/msi.h>
# include <linux/of.h>
# include <linux/of_irq.h>
static struct irq_chip its_pmsi_irq_chip = {
. name = " ITS-pMSI " ,
} ;
2017-03-07 15:40:00 +03:00
static int of_pmsi_get_dev_id ( struct irq_domain * domain , struct device * dev ,
u32 * dev_id )
2015-07-28 16:46:22 +03:00
{
2015-09-18 16:07:40 +03:00
int ret , index = 0 ;
2015-07-28 16:46:22 +03:00
/* Suck the DeviceID out of the msi-parent property */
2015-09-18 16:07:40 +03:00
do {
struct of_phandle_args args ;
ret = of_parse_phandle_with_args ( dev - > of_node ,
" msi-parent " , " #msi-cells " ,
index , & args ) ;
if ( args . np = = irq_domain_get_of_node ( domain ) ) {
if ( WARN_ON ( args . args_count ! = 1 ) )
return - EINVAL ;
2017-03-07 15:40:00 +03:00
* dev_id = args . args [ 0 ] ;
2015-09-18 16:07:40 +03:00
break ;
}
2017-08-10 17:41:17 +03:00
index + + ;
2015-09-18 16:07:40 +03:00
} while ( ! ret ) ;
2017-03-07 15:40:00 +03:00
return ret ;
}
2017-03-29 17:13:07 +03:00
int __weak iort_pmsi_get_dev_id ( struct device * dev , u32 * dev_id )
{
return - 1 ;
}
2017-03-07 15:40:00 +03:00
static int its_pmsi_prepare ( struct irq_domain * domain , struct device * dev ,
int nvec , msi_alloc_info_t * info )
{
struct msi_domain_info * msi_info ;
u32 dev_id ;
int ret ;
msi_info = msi_get_domain_info ( domain - > parent ) ;
2017-03-29 17:13:07 +03:00
if ( dev - > of_node )
ret = of_pmsi_get_dev_id ( domain , dev , & dev_id ) ;
else
ret = iort_pmsi_get_dev_id ( dev , & dev_id ) ;
2015-07-28 16:46:22 +03:00
if ( ret )
return ret ;
/* ITS specific DeviceID, as the core ITS ignores dev. */
info - > scratchpad [ 0 ] . ul = dev_id ;
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 ) ) ;
2015-07-28 16:46:22 +03:00
return msi_info - > ops - > msi_prepare ( domain - > parent ,
dev , nvec , info ) ;
}
static struct msi_domain_ops its_pmsi_ops = {
. msi_prepare = its_pmsi_prepare ,
} ;
static struct msi_domain_info its_pmsi_domain_info = {
. flags = ( MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS ) ,
. ops = & its_pmsi_ops ,
. chip = & its_pmsi_irq_chip ,
} ;
2017-06-22 13:41:13 +03:00
static const struct of_device_id its_device_id [ ] = {
2015-07-28 16:46:22 +03:00
{ . compatible = " arm,gic-v3-its " , } ,
{ } ,
} ;
2017-03-07 15:40:01 +03:00
static int __init its_pmsi_init_one ( struct fwnode_handle * fwnode ,
const char * name )
2015-07-28 16:46:22 +03:00
{
struct irq_domain * parent ;
2017-03-07 15:40:01 +03:00
parent = irq_find_matching_fwnode ( fwnode , DOMAIN_BUS_NEXUS ) ;
if ( ! parent | | ! msi_get_domain_info ( parent ) ) {
pr_err ( " %s: unable to locate ITS domain \n " , name ) ;
return - ENXIO ;
}
if ( ! platform_msi_create_irq_domain ( fwnode , & its_pmsi_domain_info ,
parent ) ) {
pr_err ( " %s: unable to create platform domain \n " , name ) ;
return - ENXIO ;
}
pr_info ( " Platform MSI: %s domain created \n " , name ) ;
return 0 ;
}
2017-03-07 15:40:02 +03:00
# ifdef CONFIG_ACPI
static int __init
2019-03-11 23:55:57 +03:00
its_pmsi_parse_madt ( union acpi_subtable_headers * header ,
2017-03-07 15:40:02 +03:00
const unsigned long end )
{
struct acpi_madt_generic_translator * its_entry ;
struct fwnode_handle * domain_handle ;
const char * node_name ;
int err = - ENXIO ;
its_entry = ( struct acpi_madt_generic_translator * ) header ;
node_name = kasprintf ( GFP_KERNEL , " ITS@0x%lx " ,
( long ) its_entry - > base_address ) ;
domain_handle = iort_find_domain_token ( its_entry - > translation_id ) ;
if ( ! domain_handle ) {
pr_err ( " %s: Unable to locate ITS domain handle \n " , node_name ) ;
goto out ;
}
err = its_pmsi_init_one ( domain_handle , node_name ) ;
out :
kfree ( node_name ) ;
return err ;
}
static void __init its_pmsi_acpi_init ( void )
{
acpi_table_parse_madt ( ACPI_MADT_TYPE_GENERIC_TRANSLATOR ,
its_pmsi_parse_madt , 0 ) ;
}
# else
static inline void its_pmsi_acpi_init ( void ) { }
# endif
2017-03-07 15:40:01 +03:00
static void __init its_pmsi_of_init ( void )
{
struct device_node * np ;
2015-07-28 16:46: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 ;
2015-07-28 16:46:22 +03:00
if ( ! of_property_read_bool ( np , " msi-controller " ) )
continue ;
2017-03-07 15:40:01 +03:00
its_pmsi_init_one ( of_node_to_fwnode ( np ) , np - > full_name ) ;
2015-07-28 16:46:22 +03:00
}
2017-03-07 15:40:01 +03:00
}
2015-07-28 16:46:22 +03:00
2017-03-07 15:40:01 +03:00
static int __init its_pmsi_init ( void )
{
its_pmsi_of_init ( ) ;
2017-03-07 15:40:02 +03:00
its_pmsi_acpi_init ( ) ;
2015-07-28 16:46:22 +03:00
return 0 ;
}
early_initcall ( its_pmsi_init ) ;