2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2014-11-25 18:47:22 +00:00
/*
* ARM GIC v2m MSI ( - X ) support
* Support for Message Signaled Interrupts for systems that
* implement ARM Generic Interrupt Controller : GICv2m .
*
* Copyright ( C ) 2014 Advanced Micro Devices , Inc .
* Authors : Suravee Suthikulpanit < suravee . suthikulpanit @ amd . com >
* Harish Kasiviswanathan < harish . kasiviswanathan @ amd . com >
* Brandon Anderson < brandon . anderson @ amd . com >
*/
# define pr_fmt(fmt) "GICv2m: " fmt
2015-12-10 08:55:30 -08:00
# include <linux/acpi.h>
2022-08-16 18:28:04 +01:00
# include <linux/iommu.h>
2014-11-25 18:47:22 +00:00
# include <linux/irq.h>
# include <linux/irqdomain.h>
# include <linux/kernel.h>
2019-09-03 13:30:59 +02:00
# include <linux/pci.h>
2015-12-10 08:55:30 -08:00
# include <linux/msi.h>
2014-11-25 18:47:22 +00:00
# include <linux/of_address.h>
# include <linux/of_pci.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
2016-06-08 18:53:34 +01:00
# include <linux/irqchip/arm-gic.h>
2022-11-21 15:39:32 +01:00
# include <linux/irqchip/arm-gic-common.h>
2014-11-25 18:47:22 +00:00
/*
* MSI_TYPER :
* [ 31 : 26 ] Reserved
* [ 25 : 16 ] lowest SPI assigned to MSI
* [ 15 : 10 ] Reserved
* [ 9 : 0 ] Numer of SPIs assigned to MSI
*/
# define V2M_MSI_TYPER 0x008
# define V2M_MSI_TYPER_BASE_SHIFT 16
# define V2M_MSI_TYPER_BASE_MASK 0x3FF
# define V2M_MSI_TYPER_NUM_MASK 0x3FF
# define V2M_MSI_SETSPI_NS 0x040
# define V2M_MIN_SPI 32
# define V2M_MAX_SPI 1019
2015-10-06 15:32:38 -07:00
# define V2M_MSI_IIDR 0xFCC
2014-11-25 18:47:22 +00:00
# define V2M_MSI_TYPER_BASE_SPI(x) \
( ( ( x ) > > V2M_MSI_TYPER_BASE_SHIFT ) & V2M_MSI_TYPER_BASE_MASK )
# define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK)
2015-10-06 15:32:38 -07:00
/* APM X-Gene with GICv2m MSI_IIDR register value */
# define XGENE_GICV2M_MSI_IIDR 0x06000170
2016-05-05 09:32:01 -07:00
/* Broadcom NS2 GICv2m MSI_IIDR register value */
# define BCM_NS2_GICV2M_MSI_IIDR 0x0000013f
2015-10-06 15:32:38 -07:00
/* List of flags for specific v2m implementation */
# define GICV2M_NEEDS_SPI_OFFSET 0x00000001
2019-06-10 13:52:01 +03:00
# define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002
2015-10-06 15:32:38 -07:00
2015-10-14 12:27:17 +01:00
static LIST_HEAD ( v2m_nodes ) ;
static DEFINE_SPINLOCK ( v2m_lock ) ;
2014-11-25 18:47:22 +00:00
struct v2m_data {
2015-10-14 12:27:17 +01:00
struct list_head entry ;
2015-12-10 08:55:29 -08:00
struct fwnode_handle * fwnode ;
2014-11-25 18:47:22 +00:00
struct resource res ; /* GICv2m resource */
void __iomem * base ; /* GICv2m virt address */
u32 spi_start ; /* The SPI number that MSIs start */
u32 nr_spis ; /* The number of SPIs for MSIs */
2016-05-05 09:32:01 -07:00
u32 spi_offset ; /* offset to be subtracted from SPI number */
2014-11-25 18:47:22 +00:00
unsigned long * bm ; /* MSI vector bitmap */
2015-10-06 15:32:38 -07:00
u32 flags ; /* v2m flags for specific implementation */
2014-11-25 18:47:22 +00:00
} ;
static void gicv2m_mask_msi_irq ( struct irq_data * d )
{
pci_msi_mask_irq ( d ) ;
irq_chip_mask_parent ( d ) ;
}
static void gicv2m_unmask_msi_irq ( struct irq_data * d )
{
pci_msi_unmask_irq ( d ) ;
irq_chip_unmask_parent ( d ) ;
}
static struct irq_chip gicv2m_msi_irq_chip = {
. name = " MSI " ,
. irq_mask = gicv2m_mask_msi_irq ,
. irq_unmask = gicv2m_unmask_msi_irq ,
. irq_eoi = irq_chip_eoi_parent ,
} ;
static struct msi_domain_info gicv2m_msi_domain_info = {
. flags = ( MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
2018-02-06 18:55:33 +00:00
MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI ) ,
2014-11-25 18:47:22 +00:00
. chip = & gicv2m_msi_irq_chip ,
} ;
2019-06-10 13:52:01 +03:00
static phys_addr_t gicv2m_get_msi_addr ( struct v2m_data * v2m , int hwirq )
{
if ( v2m - > flags & GICV2M_GRAVITON_ADDRESS_ONLY )
return v2m - > res . start | ( ( hwirq - 32 ) < < 3 ) ;
else
return v2m - > res . start + V2M_MSI_SETSPI_NS ;
}
2014-11-25 18:47:22 +00:00
static void gicv2m_compose_msi_msg ( struct irq_data * data , struct msi_msg * msg )
{
struct v2m_data * v2m = irq_data_get_irq_chip_data ( data ) ;
2019-06-10 13:52:01 +03:00
phys_addr_t addr = gicv2m_get_msi_addr ( v2m , data - > hwirq ) ;
2014-11-25 18:47:22 +00:00
2015-09-13 12:14:33 +01:00
msg - > address_hi = upper_32_bits ( addr ) ;
msg - > address_lo = lower_32_bits ( addr ) ;
2015-10-06 15:32:38 -07:00
2019-06-10 13:52:01 +03:00
if ( v2m - > flags & GICV2M_GRAVITON_ADDRESS_ONLY )
msg - > data = 0 ;
else
msg - > data = data - > hwirq ;
2015-10-06 15:32:38 -07:00
if ( v2m - > flags & GICV2M_NEEDS_SPI_OFFSET )
2016-05-05 09:32:01 -07:00
msg - > data - = v2m - > spi_offset ;
2016-09-12 17:13:59 +01:00
2019-05-01 14:58:20 +01:00
iommu_dma_compose_msi_msg ( irq_data_get_msi_desc ( data ) , msg ) ;
2014-11-25 18:47:22 +00:00
}
static struct irq_chip gicv2m_irq_chip = {
. name = " GICv2m " ,
. irq_mask = irq_chip_mask_parent ,
. irq_unmask = irq_chip_unmask_parent ,
. irq_eoi = irq_chip_eoi_parent ,
2016-02-19 15:00:29 +00:00
. irq_set_affinity = irq_chip_set_affinity_parent ,
2014-11-25 18:47:22 +00:00
. irq_compose_msi_msg = gicv2m_compose_msi_msg ,
} ;
static int gicv2m_irq_gic_domain_alloc ( struct irq_domain * domain ,
unsigned int virq ,
irq_hw_number_t hwirq )
{
2015-10-13 12:51:33 +01:00
struct irq_fwspec fwspec ;
2014-11-25 18:47:22 +00:00
struct irq_data * d ;
int err ;
2015-10-13 12:51:33 +01:00
if ( is_of_node ( domain - > parent - > fwnode ) ) {
fwspec . fwnode = domain - > parent - > fwnode ;
fwspec . param_count = 3 ;
fwspec . param [ 0 ] = 0 ;
fwspec . param [ 1 ] = hwirq - 32 ;
fwspec . param [ 2 ] = IRQ_TYPE_EDGE_RISING ;
2015-12-10 08:55:30 -08:00
} else if ( is_fwnode_irqchip ( domain - > parent - > fwnode ) ) {
fwspec . fwnode = domain - > parent - > fwnode ;
fwspec . param_count = 2 ;
fwspec . param [ 0 ] = hwirq ;
fwspec . param [ 1 ] = IRQ_TYPE_EDGE_RISING ;
2015-10-13 12:51:33 +01:00
} else {
return - EINVAL ;
}
2014-11-25 18:47:22 +00:00
2015-10-13 12:51:33 +01:00
err = irq_domain_alloc_irqs_parent ( domain , virq , 1 , & fwspec ) ;
2014-11-25 18:47:22 +00:00
if ( err )
return err ;
/* Configure the interrupt line to be edge */
d = irq_domain_get_irq_data ( domain - > parent , virq ) ;
d - > chip - > irq_set_type ( d , IRQ_TYPE_EDGE_RISING ) ;
return 0 ;
}
2018-02-06 18:55:33 +00:00
static void gicv2m_unalloc_msi ( struct v2m_data * v2m , unsigned int hwirq ,
int nr_irqs )
2014-11-25 18:47:22 +00:00
{
2015-10-14 12:27:17 +01:00
spin_lock ( & v2m_lock ) ;
2018-02-06 18:55:33 +00:00
bitmap_release_region ( v2m - > bm , hwirq - v2m - > spi_start ,
get_count_order ( nr_irqs ) ) ;
2015-10-14 12:27:17 +01:00
spin_unlock ( & v2m_lock ) ;
2014-11-25 18:47:22 +00:00
}
static int gicv2m_irq_domain_alloc ( struct irq_domain * domain , unsigned int virq ,
unsigned int nr_irqs , void * args )
{
2019-05-01 14:58:20 +01:00
msi_alloc_info_t * info = args ;
2015-10-14 12:27:17 +01:00
struct v2m_data * v2m = NULL , * tmp ;
2018-02-06 18:55:33 +00:00
int hwirq , offset , i , err = 0 ;
2014-11-25 18:47:22 +00:00
2015-10-14 12:27:17 +01:00
spin_lock ( & v2m_lock ) ;
list_for_each_entry ( tmp , & v2m_nodes , entry ) {
2018-02-06 18:55:33 +00:00
offset = bitmap_find_free_region ( tmp - > bm , tmp - > nr_spis ,
get_count_order ( nr_irqs ) ) ;
if ( offset > = 0 ) {
2015-10-14 12:27:17 +01:00
v2m = tmp ;
break ;
}
}
spin_unlock ( & v2m_lock ) ;
2014-11-25 18:47:22 +00:00
2015-10-14 12:27:17 +01:00
if ( ! v2m )
return - ENOSPC ;
2014-11-25 18:47:22 +00:00
hwirq = v2m - > spi_start + offset ;
2019-05-01 14:58:20 +01:00
err = iommu_dma_prepare_msi ( info - > desc ,
2019-06-10 13:52:01 +03:00
gicv2m_get_msi_addr ( v2m , hwirq ) ) ;
2019-05-01 14:58:20 +01:00
if ( err )
return err ;
2018-02-06 18:55:33 +00:00
for ( i = 0 ; i < nr_irqs ; i + + ) {
err = gicv2m_irq_gic_domain_alloc ( domain , virq + i , hwirq + i ) ;
if ( err )
goto fail ;
2014-11-25 18:47:22 +00:00
2018-02-06 18:55:33 +00:00
irq_domain_set_hwirq_and_chip ( domain , virq + i , hwirq + i ,
& gicv2m_irq_chip , v2m ) ;
}
2014-11-25 18:47:22 +00:00
return 0 ;
2018-02-06 18:55:33 +00:00
fail :
irq_domain_free_irqs_parent ( domain , virq , nr_irqs ) ;
2018-06-22 10:52:50 +01:00
gicv2m_unalloc_msi ( v2m , hwirq , nr_irqs ) ;
2018-02-06 18:55:33 +00:00
return err ;
2014-11-25 18:47:22 +00:00
}
static void gicv2m_irq_domain_free ( struct irq_domain * domain ,
unsigned int virq , unsigned int nr_irqs )
{
struct irq_data * d = irq_domain_get_irq_data ( domain , virq ) ;
struct v2m_data * v2m = irq_data_get_irq_chip_data ( d ) ;
2018-02-06 18:55:33 +00:00
gicv2m_unalloc_msi ( v2m , d - > hwirq , nr_irqs ) ;
2014-11-25 18:47:22 +00:00
irq_domain_free_irqs_parent ( domain , virq , nr_irqs ) ;
}
static const struct irq_domain_ops gicv2m_domain_ops = {
. alloc = gicv2m_irq_domain_alloc ,
. free = gicv2m_irq_domain_free ,
} ;
static bool is_msi_spi_valid ( u32 base , u32 num )
{
if ( base < V2M_MIN_SPI ) {
pr_err ( " Invalid MSI base SPI (base:%u) \n " , base ) ;
return false ;
}
if ( ( num = = 0 ) | | ( base + num > V2M_MAX_SPI ) ) {
pr_err ( " Number of SPIs (%u) exceed maximum (%u) \n " ,
num , V2M_MAX_SPI - V2M_MIN_SPI + 1 ) ;
return false ;
}
return true ;
}
2015-07-28 14:46:24 +01:00
static struct irq_chip gicv2m_pmsi_irq_chip = {
. name = " pMSI " ,
} ;
static struct msi_domain_ops gicv2m_pmsi_ops = {
} ;
static struct msi_domain_info gicv2m_pmsi_domain_info = {
. flags = ( MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS ) ,
. ops = & gicv2m_pmsi_ops ,
. chip = & gicv2m_pmsi_irq_chip ,
} ;
2022-11-21 15:39:33 +01:00
static void __init gicv2m_teardown ( void )
2015-10-14 12:27:17 +01:00
{
struct v2m_data * v2m , * tmp ;
list_for_each_entry_safe ( v2m , tmp , & v2m_nodes , entry ) {
list_del ( & v2m - > entry ) ;
2021-06-18 18:16:53 +03:00
bitmap_free ( v2m - > bm ) ;
2015-10-14 12:27:17 +01:00
iounmap ( v2m - > base ) ;
2015-12-10 08:55:29 -08:00
of_node_put ( to_of_node ( v2m - > fwnode ) ) ;
2015-12-10 08:55:30 -08:00
if ( is_fwnode_irqchip ( v2m - > fwnode ) )
irq_domain_free_fwnode ( v2m - > fwnode ) ;
2015-10-14 12:27:17 +01:00
kfree ( v2m ) ;
}
}
2022-11-21 15:39:33 +01:00
static __init int gicv2m_allocate_domains ( struct irq_domain * parent )
2015-10-14 12:27:17 +01:00
{
struct irq_domain * inner_domain , * pci_domain , * plat_domain ;
struct v2m_data * v2m ;
v2m = list_first_entry_or_null ( & v2m_nodes , struct v2m_data , entry ) ;
if ( ! v2m )
return 0 ;
2023-02-13 11:42:57 +01:00
inner_domain = irq_domain_create_hierarchy ( parent , 0 , 0 , v2m - > fwnode ,
& gicv2m_domain_ops , v2m ) ;
2015-10-14 12:27:17 +01:00
if ( ! inner_domain ) {
pr_err ( " Failed to create GICv2m domain \n " ) ;
return - ENOMEM ;
}
2017-06-22 11:42:50 +01:00
irq_domain_update_bus_token ( inner_domain , DOMAIN_BUS_NEXUS ) ;
2015-12-10 08:55:29 -08:00
pci_domain = pci_msi_create_irq_domain ( v2m - > fwnode ,
2015-10-14 12:27:17 +01:00
& gicv2m_msi_domain_info ,
inner_domain ) ;
2015-12-10 08:55:29 -08:00
plat_domain = platform_msi_create_irq_domain ( v2m - > fwnode ,
2015-10-14 12:27:17 +01:00
& gicv2m_pmsi_domain_info ,
inner_domain ) ;
if ( ! pci_domain | | ! plat_domain ) {
pr_err ( " Failed to create MSI domains \n " ) ;
if ( plat_domain )
irq_domain_remove ( plat_domain ) ;
if ( pci_domain )
irq_domain_remove ( pci_domain ) ;
irq_domain_remove ( inner_domain ) ;
return - ENOMEM ;
}
return 0 ;
}
2015-12-10 08:55:29 -08:00
static int __init gicv2m_init_one ( struct fwnode_handle * fwnode ,
u32 spi_start , u32 nr_spis ,
2019-06-10 13:52:01 +03:00
struct resource * res , u32 flags )
2014-11-25 18:47:22 +00:00
{
int ret ;
struct v2m_data * v2m ;
v2m = kzalloc ( sizeof ( struct v2m_data ) , GFP_KERNEL ) ;
2021-06-09 22:05:34 +08:00
if ( ! v2m )
2014-11-25 18:47:22 +00:00
return - ENOMEM ;
2015-10-14 12:27:17 +01:00
INIT_LIST_HEAD ( & v2m - > entry ) ;
2015-12-10 08:55:29 -08:00
v2m - > fwnode = fwnode ;
2019-06-10 13:52:01 +03:00
v2m - > flags = flags ;
2015-10-14 12:27:17 +01:00
2015-12-10 08:55:29 -08:00
memcpy ( & v2m - > res , res , sizeof ( struct resource ) ) ;
2014-11-25 18:47:22 +00:00
v2m - > base = ioremap ( v2m - > res . start , resource_size ( & v2m - > res ) ) ;
if ( ! v2m - > base ) {
pr_err ( " Failed to map GICv2m resource \n " ) ;
ret = - ENOMEM ;
goto err_free_v2m ;
}
2015-12-10 08:55:29 -08:00
if ( spi_start & & nr_spis ) {
v2m - > spi_start = spi_start ;
v2m - > nr_spis = nr_spis ;
2014-11-25 18:47:22 +00:00
} else {
2019-06-10 13:52:01 +03:00
u32 typer ;
/* Graviton should always have explicit spi_start/nr_spis */
if ( v2m - > flags & GICV2M_GRAVITON_ADDRESS_ONLY ) {
ret = - EINVAL ;
goto err_iounmap ;
}
typer = readl_relaxed ( v2m - > base + V2M_MSI_TYPER ) ;
2014-11-25 18:47:22 +00:00
v2m - > spi_start = V2M_MSI_TYPER_BASE_SPI ( typer ) ;
v2m - > nr_spis = V2M_MSI_TYPER_NUM_SPI ( typer ) ;
}
if ( ! is_msi_spi_valid ( v2m - > spi_start , v2m - > nr_spis ) ) {
ret = - EINVAL ;
goto err_iounmap ;
}
2015-10-06 15:32:38 -07:00
/*
* APM X - Gene GICv2m implementation has an erratum where
* the MSI data needs to be the offset from the spi_start
* in order to trigger the correct MSI interrupt . This is
* different from the standard GICv2m implementation where
* the MSI data is the absolute value within the range from
* spi_start to ( spi_start + num_spis ) .
2016-05-05 09:32:01 -07:00
*
2021-03-22 04:21:30 +01:00
* Broadcom NS2 GICv2m implementation has an erratum where the MSI data
2016-05-05 09:32:01 -07:00
* is ' spi_number - 32 '
2019-06-10 13:52:01 +03:00
*
* Reading that register fails on the Graviton implementation
2015-10-06 15:32:38 -07:00
*/
2019-06-10 13:52:01 +03:00
if ( ! ( v2m - > flags & GICV2M_GRAVITON_ADDRESS_ONLY ) ) {
switch ( readl_relaxed ( v2m - > base + V2M_MSI_IIDR ) ) {
case XGENE_GICV2M_MSI_IIDR :
v2m - > flags | = GICV2M_NEEDS_SPI_OFFSET ;
v2m - > spi_offset = v2m - > spi_start ;
break ;
case BCM_NS2_GICV2M_MSI_IIDR :
v2m - > flags | = GICV2M_NEEDS_SPI_OFFSET ;
v2m - > spi_offset = 32 ;
break ;
}
2016-05-05 09:32:01 -07:00
}
2021-06-18 18:16:53 +03:00
v2m - > bm = bitmap_zalloc ( v2m - > nr_spis , GFP_KERNEL ) ;
2014-11-25 18:47:22 +00:00
if ( ! v2m - > bm ) {
ret = - ENOMEM ;
goto err_iounmap ;
}
2015-10-14 12:27:17 +01:00
list_add_tail ( & v2m - > entry , & v2m_nodes ) ;
2014-11-25 18:47:22 +00:00
2015-12-22 16:24:23 -08:00
pr_info ( " range%pR, SPI[%d:%d] \n " , res ,
v2m - > spi_start , ( v2m - > spi_start + v2m - > nr_spis - 1 ) ) ;
2014-11-25 18:47:22 +00:00
return 0 ;
err_iounmap :
iounmap ( v2m - > base ) ;
err_free_v2m :
kfree ( v2m ) ;
return ret ;
}
2022-11-21 15:39:33 +01:00
static __initconst struct of_device_id gicv2m_device_id [ ] = {
2014-11-25 18:47:22 +00:00
{ . compatible = " arm,gic-v2m-frame " , } ,
{ } ,
} ;
2015-12-10 08:55:30 -08:00
static int __init gicv2m_of_init ( struct fwnode_handle * parent_handle ,
struct irq_domain * parent )
2014-11-25 18:47:22 +00:00
{
int ret = 0 ;
2015-12-10 08:55:30 -08:00
struct device_node * node = to_of_node ( parent_handle ) ;
2014-11-25 18:47:22 +00:00
struct device_node * child ;
for ( child = of_find_matching_node ( node , gicv2m_device_id ) ; child ;
child = of_find_matching_node ( child , gicv2m_device_id ) ) {
2015-12-10 08:55:29 -08:00
u32 spi_start = 0 , nr_spis = 0 ;
struct resource res ;
2023-03-10 08:47:10 -06:00
if ( ! of_property_read_bool ( child , " msi-controller " ) )
2014-11-25 18:47:22 +00:00
continue ;
2015-12-10 08:55:29 -08:00
ret = of_address_to_resource ( child , 0 , & res ) ;
if ( ret ) {
pr_err ( " Failed to allocate v2m resource. \n " ) ;
break ;
}
if ( ! of_property_read_u32 ( child , " arm,msi-base-spi " ,
& spi_start ) & &
! of_property_read_u32 ( child , " arm,msi-num-spis " , & nr_spis ) )
pr_info ( " DT overriding V2M MSI_TYPER (base:%u, num:%u) \n " ,
spi_start , nr_spis ) ;
2019-06-10 13:52:01 +03:00
ret = gicv2m_init_one ( & child - > fwnode , spi_start , nr_spis ,
& res , 0 ) ;
2014-11-25 18:47:22 +00:00
if ( ret ) {
2015-12-16 11:03:24 +00:00
of_node_put ( child ) ;
2014-11-25 18:47:22 +00:00
break ;
}
}
2015-10-14 12:27:17 +01:00
if ( ! ret )
ret = gicv2m_allocate_domains ( parent ) ;
if ( ret )
gicv2m_teardown ( ) ;
2014-11-25 18:47:22 +00:00
return ret ;
}
2015-12-10 08:55:30 -08:00
# ifdef CONFIG_ACPI
static int acpi_num_msi ;
2022-11-21 15:39:33 +01:00
static __init struct fwnode_handle * gicv2m_get_fwnode ( struct device * dev )
2015-12-10 08:55:30 -08:00
{
struct v2m_data * data ;
if ( WARN_ON ( acpi_num_msi < = 0 ) )
return NULL ;
/* We only return the fwnode of the first MSI frame. */
data = list_first_entry_or_null ( & v2m_nodes , struct v2m_data , entry ) ;
if ( ! data )
return NULL ;
return data - > fwnode ;
}
2022-11-21 15:39:33 +01:00
static __init bool acpi_check_amazon_graviton_quirks ( void )
2019-06-10 13:52:01 +03:00
{
static struct acpi_table_madt * madt ;
acpi_status status ;
bool rc = false ;
# define ACPI_AMZN_OEM_ID "AMAZON"
status = acpi_get_table ( ACPI_SIG_MADT , 0 ,
( struct acpi_table_header * * ) & madt ) ;
if ( ACPI_FAILURE ( status ) | | ! madt )
return rc ;
rc = ! memcmp ( madt - > header . oem_id , ACPI_AMZN_OEM_ID , ACPI_OEM_ID_SIZE ) ;
acpi_put_table ( ( struct acpi_table_header * ) madt ) ;
return rc ;
}
2015-12-10 08:55:30 -08:00
static int __init
2019-03-11 14:55:57 -06:00
acpi_parse_madt_msi ( union acpi_subtable_headers * header ,
2015-12-10 08:55:30 -08:00
const unsigned long end )
{
int ret ;
struct resource res ;
u32 spi_start = 0 , nr_spis = 0 ;
struct acpi_madt_generic_msi_frame * m ;
struct fwnode_handle * fwnode ;
2019-06-10 13:52:01 +03:00
u32 flags = 0 ;
2015-12-10 08:55:30 -08:00
m = ( struct acpi_madt_generic_msi_frame * ) header ;
if ( BAD_MADT_ENTRY ( m , end ) )
return - EINVAL ;
res . start = m - > base_address ;
2015-12-22 16:24:23 -08:00
res . end = m - > base_address + SZ_4K - 1 ;
res . flags = IORESOURCE_MEM ;
2015-12-10 08:55:30 -08:00
2019-06-10 13:52:01 +03:00
if ( acpi_check_amazon_graviton_quirks ( ) ) {
pr_info ( " applying Amazon Graviton quirk \n " ) ;
res . end = res . start + SZ_8K - 1 ;
flags | = GICV2M_GRAVITON_ADDRESS_ONLY ;
gicv2m_msi_domain_info . flags & = ~ MSI_FLAG_MULTI_PCI_MSI ;
}
2015-12-10 08:55:30 -08:00
if ( m - > flags & ACPI_MADT_OVERRIDE_SPI_VALUES ) {
spi_start = m - > spi_base ;
nr_spis = m - > spi_count ;
pr_info ( " ACPI overriding V2M MSI_TYPER (base:%u, num:%u) \n " ,
spi_start , nr_spis ) ;
}
2019-07-31 16:13:42 +01:00
fwnode = irq_domain_alloc_fwnode ( & res . start ) ;
2015-12-10 08:55:30 -08:00
if ( ! fwnode ) {
pr_err ( " Unable to allocate GICv2m domain token \n " ) ;
return - EINVAL ;
}
2019-06-10 13:52:01 +03:00
ret = gicv2m_init_one ( fwnode , spi_start , nr_spis , & res , flags ) ;
2015-12-10 08:55:30 -08:00
if ( ret )
irq_domain_free_fwnode ( fwnode ) ;
return ret ;
}
static int __init gicv2m_acpi_init ( struct irq_domain * parent )
{
int ret ;
if ( acpi_num_msi > 0 )
return 0 ;
acpi_num_msi = acpi_table_parse_madt ( ACPI_MADT_TYPE_GENERIC_MSI_FRAME ,
acpi_parse_madt_msi , 0 ) ;
if ( acpi_num_msi < = 0 )
goto err_out ;
ret = gicv2m_allocate_domains ( parent ) ;
if ( ret )
goto err_out ;
pci_msi_register_fwnode_provider ( & gicv2m_get_fwnode ) ;
return 0 ;
err_out :
gicv2m_teardown ( ) ;
return - EINVAL ;
}
# else /* CONFIG_ACPI */
static int __init gicv2m_acpi_init ( struct irq_domain * parent )
{
return - EINVAL ;
}
# endif /* CONFIG_ACPI */
int __init gicv2m_init ( struct fwnode_handle * parent_handle ,
struct irq_domain * parent )
{
if ( is_of_node ( parent_handle ) )
return gicv2m_of_init ( parent_handle , parent ) ;
return gicv2m_acpi_init ( parent ) ;
}