@ -19,6 +19,7 @@
# include <linux/errno.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/irqdomain.h>
# include "pci.h"
@ -1091,3 +1092,170 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
return nvec ;
}
EXPORT_SYMBOL ( pci_enable_msix_range ) ;
# ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
/**
* pci_msi_domain_write_msg - Helper to write MSI message to PCI config space
* @ irq_data : Pointer to interrupt data of the MSI interrupt
* @ msg : Pointer to the message
*/
void pci_msi_domain_write_msg ( struct irq_data * irq_data , struct msi_msg * msg )
{
struct msi_desc * desc = irq_data - > msi_desc ;
/*
* For MSI - X desc - > irq is always equal to irq_data - > irq . For
* MSI only the first interrupt of MULTI MSI passes the test .
*/
if ( desc - > irq = = irq_data - > irq )
__pci_write_msi_msg ( desc , msg ) ;
}
/**
* pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source
* @ dev : Pointer to the PCI device
* @ desc : Pointer to the msi descriptor
*
* The ID number is only used within the irqdomain .
*/
irq_hw_number_t pci_msi_domain_calc_hwirq ( struct pci_dev * dev ,
struct msi_desc * desc )
{
return ( irq_hw_number_t ) desc - > msi_attrib . entry_nr |
PCI_DEVID ( dev - > bus - > number , dev - > devfn ) < < 11 |
( pci_domain_nr ( dev - > bus ) & 0xFFFFFFFF ) < < 27 ;
}
static inline bool pci_msi_desc_is_multi_msi ( struct msi_desc * desc )
{
return ! desc - > msi_attrib . is_msix & & desc - > nvec_used > 1 ;
}
/**
* pci_msi_domain_check_cap - Verify that @ domain supports the capabilities for @ dev
* @ domain : The interrupt domain to check
* @ info : The domain info for verification
* @ dev : The device to check
*
* Returns :
* 0 if the functionality is supported
* 1 if Multi MSI is requested , but the domain does not support it
* - ENOTSUPP otherwise
*/
int pci_msi_domain_check_cap ( struct irq_domain * domain ,
struct msi_domain_info * info , struct device * dev )
{
struct msi_desc * desc = first_pci_msi_entry ( to_pci_dev ( dev ) ) ;
/* Special handling to support pci_enable_msi_range() */
if ( pci_msi_desc_is_multi_msi ( desc ) & &
! ( info - > flags & MSI_FLAG_MULTI_PCI_MSI ) )
return 1 ;
else if ( desc - > msi_attrib . is_msix & & ! ( info - > flags & MSI_FLAG_PCI_MSIX ) )
return - ENOTSUPP ;
return 0 ;
}
static int pci_msi_domain_handle_error ( struct irq_domain * domain ,
struct msi_desc * desc , int error )
{
/* Special handling to support pci_enable_msi_range() */
if ( pci_msi_desc_is_multi_msi ( desc ) & & error = = - ENOSPC )
return 1 ;
return error ;
}
# ifdef GENERIC_MSI_DOMAIN_OPS
static void pci_msi_domain_set_desc ( msi_alloc_info_t * arg ,
struct msi_desc * desc )
{
arg - > desc = desc ;
arg - > hwirq = pci_msi_domain_calc_hwirq ( msi_desc_to_pci_dev ( desc ) ,
desc ) ;
}
# else
# define pci_msi_domain_set_desc NULL
# endif
static struct msi_domain_ops pci_msi_domain_ops_default = {
. set_desc = pci_msi_domain_set_desc ,
. msi_check = pci_msi_domain_check_cap ,
. handle_error = pci_msi_domain_handle_error ,
} ;
static void pci_msi_domain_update_dom_ops ( struct msi_domain_info * info )
{
struct msi_domain_ops * ops = info - > ops ;
if ( ops = = NULL ) {
info - > ops = & pci_msi_domain_ops_default ;
} else {
if ( ops - > set_desc = = NULL )
ops - > set_desc = pci_msi_domain_set_desc ;
if ( ops - > msi_check = = NULL )
ops - > msi_check = pci_msi_domain_check_cap ;
if ( ops - > handle_error = = NULL )
ops - > handle_error = pci_msi_domain_handle_error ;
}
}
static void pci_msi_domain_update_chip_ops ( struct msi_domain_info * info )
{
struct irq_chip * chip = info - > chip ;
BUG_ON ( ! chip ) ;
if ( ! chip - > irq_write_msi_msg )
chip - > irq_write_msi_msg = pci_msi_domain_write_msg ;
}
/**
* pci_msi_create_irq_domain - Creat a MSI interrupt domain
* @ node : Optional device - tree node of the interrupt controller
* @ info : MSI domain info
* @ parent : Parent irq domain
*
* Updates the domain and chip ops and creates a MSI interrupt domain .
*
* Returns :
* A domain pointer or NULL in case of failure .
*/
struct irq_domain * pci_msi_create_irq_domain ( struct device_node * node ,
struct msi_domain_info * info ,
struct irq_domain * parent )
{
if ( info - > flags & MSI_FLAG_USE_DEF_DOM_OPS )
pci_msi_domain_update_dom_ops ( info ) ;
if ( info - > flags & MSI_FLAG_USE_DEF_CHIP_OPS )
pci_msi_domain_update_chip_ops ( info ) ;
return msi_create_irq_domain ( node , info , parent ) ;
}
/**
* pci_msi_domain_alloc_irqs - Allocate interrupts for @ dev in @ domain
* @ domain : The interrupt domain to allocate from
* @ dev : The device for which to allocate
* @ nvec : The number of interrupts to allocate
* @ type : Unused to allow simpler migration from the arch_XXX interfaces
*
* Returns :
* A virtual interrupt number or an error code in case of failure
*/
int pci_msi_domain_alloc_irqs ( struct irq_domain * domain , struct pci_dev * dev ,
int nvec , int type )
{
return msi_domain_alloc_irqs ( domain , & dev - > dev , nvec ) ;
}
/**
* pci_msi_domain_free_irqs - Free interrupts for @ dev in @ domain
* @ domain : The interrupt domain
* @ dev : The device for which to free interrupts
*/
void pci_msi_domain_free_irqs ( struct irq_domain * domain , struct pci_dev * dev )
{
msi_domain_free_irqs ( domain , & dev - > dev ) ;
}
# endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */