2012-09-26 14:44:45 +04:00
# include <linux/seq_file.h>
2012-09-26 14:44:35 +04:00
# include <linux/cpumask.h>
2012-03-30 22:47:00 +04:00
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
2012-07-06 15:28:37 +04:00
# include <linux/msi.h>
2012-09-26 14:44:38 +04:00
# include <linux/irq.h>
# include <linux/pci.h>
2012-07-06 15:28:37 +04:00
# include <asm/hw_irq.h>
# include <asm/irq_remapping.h>
2012-09-26 14:44:35 +04:00
# include <asm/processor.h>
# include <asm/x86_init.h>
# include <asm/apic.h>
2012-03-30 22:47:00 +04:00
2012-03-30 22:47:08 +04:00
# include "irq_remapping.h"
2012-03-30 22:47:00 +04:00
2012-03-30 22:47:07 +04:00
int irq_remapping_enabled ;
2012-03-30 22:47:00 +04:00
2012-03-30 22:47:07 +04:00
int disable_irq_remap ;
2013-04-17 00:38:32 +04:00
int irq_remap_broken ;
2012-03-30 22:47:00 +04:00
int disable_sourceid_checking ;
int no_x2apic_optout ;
static struct irq_remap_ops * remap_ops ;
2012-09-26 14:44:38 +04:00
static int msi_alloc_remapped_irq ( struct pci_dev * pdev , int irq , int nvec ) ;
static int msi_setup_remapped_irq ( struct pci_dev * pdev , unsigned int irq ,
int index , int sub_handle ) ;
2012-09-26 14:44:39 +04:00
static int set_remapped_irq_affinity ( struct irq_data * data ,
const struct cpumask * mask ,
bool force ) ;
2012-09-26 14:44:38 +04:00
2012-09-26 14:44:51 +04:00
static bool irq_remapped ( struct irq_cfg * cfg )
{
return ( cfg - > remapped = = 1 ) ;
}
2012-09-26 14:44:35 +04:00
static void irq_remapping_disable_io_apic ( void )
{
/*
* With interrupt - remapping , for now we will use virtual wire A
* mode , as virtual wire B is little complex ( need to configure
* both IOAPIC RTE as well as interrupt - remapping table entry ) .
* As this gets called during crash dump , keep this simple for
* now .
*/
if ( cpu_has_apic | | apic_from_smp_config ( ) )
disconnect_bsp_APIC ( 0 ) ;
}
2012-09-26 14:44:38 +04:00
static int do_setup_msi_irqs ( struct pci_dev * dev , int nvec )
{
2013-05-13 13:06:17 +04:00
int node , ret , sub_handle , nvec_pow2 , index = 0 ;
2012-09-26 14:44:38 +04:00
unsigned int irq ;
struct msi_desc * msidesc ;
WARN_ON ( ! list_is_singular ( & dev - > msi_list ) ) ;
msidesc = list_entry ( dev - > msi_list . next , struct msi_desc , list ) ;
WARN_ON ( msidesc - > irq ) ;
WARN_ON ( msidesc - > msi_attrib . multiple ) ;
2013-05-13 13:06:17 +04:00
WARN_ON ( msidesc - > nvec_used ) ;
2012-09-26 14:44:38 +04:00
node = dev_to_node ( & dev - > dev ) ;
irq = __create_irqs ( get_nr_irqs_gsi ( ) , nvec , node ) ;
if ( irq = = 0 )
return - ENOSPC ;
2013-05-13 13:06:17 +04:00
nvec_pow2 = __roundup_pow_of_two ( nvec ) ;
msidesc - > nvec_used = nvec ;
msidesc - > msi_attrib . multiple = ilog2 ( nvec_pow2 ) ;
2012-09-26 14:44:38 +04:00
for ( sub_handle = 0 ; sub_handle < nvec ; sub_handle + + ) {
if ( ! sub_handle ) {
2013-05-13 13:06:17 +04:00
index = msi_alloc_remapped_irq ( dev , irq , nvec_pow2 ) ;
2012-09-26 14:44:38 +04:00
if ( index < 0 ) {
ret = index ;
goto error ;
}
} else {
ret = msi_setup_remapped_irq ( dev , irq + sub_handle ,
index , sub_handle ) ;
if ( ret < 0 )
goto error ;
}
ret = setup_msi_irq ( dev , msidesc , irq , sub_handle ) ;
if ( ret < 0 )
goto error ;
}
return 0 ;
error :
destroy_irqs ( irq , nvec ) ;
/*
* Restore altered MSI descriptor fields and prevent just destroyed
* IRQs from tearing down again in default_teardown_msi_irqs ( )
*/
msidesc - > irq = 0 ;
2013-05-13 13:06:17 +04:00
msidesc - > nvec_used = 0 ;
2012-09-26 14:44:38 +04:00
msidesc - > msi_attrib . multiple = 0 ;
return ret ;
}
static int do_setup_msix_irqs ( struct pci_dev * dev , int nvec )
{
int node , ret , sub_handle , index = 0 ;
struct msi_desc * msidesc ;
unsigned int irq ;
node = dev_to_node ( & dev - > dev ) ;
irq = get_nr_irqs_gsi ( ) ;
sub_handle = 0 ;
list_for_each_entry ( msidesc , & dev - > msi_list , list ) {
irq = create_irq_nr ( irq , node ) ;
if ( irq = = 0 )
return - 1 ;
if ( sub_handle = = 0 )
ret = index = msi_alloc_remapped_irq ( dev , irq , nvec ) ;
else
ret = msi_setup_remapped_irq ( dev , irq , index , sub_handle ) ;
if ( ret < 0 )
goto error ;
ret = setup_msi_irq ( dev , msidesc , irq , 0 ) ;
if ( ret < 0 )
goto error ;
sub_handle + = 1 ;
irq + = 1 ;
}
return 0 ;
error :
destroy_irq ( irq ) ;
return ret ;
}
static int irq_remapping_setup_msi_irqs ( struct pci_dev * dev ,
int nvec , int type )
{
if ( type = = PCI_CAP_ID_MSI )
return do_setup_msi_irqs ( dev , nvec ) ;
else
return do_setup_msix_irqs ( dev , nvec ) ;
}
2013-12-18 10:36:45 +04:00
static void eoi_ioapic_pin_remapped ( int apic , int pin , int vector )
2012-09-26 14:44:50 +04:00
{
/*
* Intr - remapping uses pin number as the virtual vector
* in the RTE . Actual vector is programmed in
* intr - remapping table entry . Hence for the io - apic
* EOI we use the pin number .
*/
io_apic_eoi ( apic , pin ) ;
}
2012-09-26 14:44:35 +04:00
static void __init irq_remapping_modify_x86_ops ( void )
{
2012-09-26 14:44:37 +04:00
x86_io_apic_ops . disable = irq_remapping_disable_io_apic ;
2012-09-26 14:44:39 +04:00
x86_io_apic_ops . set_affinity = set_remapped_irq_affinity ;
2012-09-26 14:44:40 +04:00
x86_io_apic_ops . setup_entry = setup_ioapic_remapped_entry ;
2012-09-26 14:44:50 +04:00
x86_io_apic_ops . eoi_ioapic_pin = eoi_ioapic_pin_remapped ;
2012-09-26 14:44:38 +04:00
x86_msi . setup_msi_irqs = irq_remapping_setup_msi_irqs ;
2012-09-26 14:44:37 +04:00
x86_msi . setup_hpet_msi = setup_hpet_msi_remapped ;
2012-09-26 14:44:49 +04:00
x86_msi . compose_msi_msg = compose_remapped_msi_msg ;
2012-09-26 14:44:35 +04:00
}
2012-03-30 22:47:00 +04:00
static __init int setup_nointremap ( char * str )
{
2012-03-30 22:47:07 +04:00
disable_irq_remap = 1 ;
2012-03-30 22:47:00 +04:00
return 0 ;
}
early_param ( " nointremap " , setup_nointremap ) ;
2012-03-30 22:47:07 +04:00
static __init int setup_irqremap ( char * str )
2012-03-30 22:47:00 +04:00
{
if ( ! str )
return - EINVAL ;
while ( * str ) {
if ( ! strncmp ( str , " on " , 2 ) )
2012-03-30 22:47:07 +04:00
disable_irq_remap = 0 ;
2012-03-30 22:47:00 +04:00
else if ( ! strncmp ( str , " off " , 3 ) )
2012-03-30 22:47:07 +04:00
disable_irq_remap = 1 ;
2012-03-30 22:47:00 +04:00
else if ( ! strncmp ( str , " nosid " , 5 ) )
disable_sourceid_checking = 1 ;
else if ( ! strncmp ( str , " no_x2apic_optout " , 16 ) )
no_x2apic_optout = 1 ;
str + = strcspn ( str , " , " ) ;
while ( * str = = ' , ' )
str + + ;
}
return 0 ;
}
2012-03-30 22:47:07 +04:00
early_param ( " intremap " , setup_irqremap ) ;
2012-03-30 22:47:00 +04:00
2012-03-30 22:47:07 +04:00
void __init setup_irq_remapping_ops ( void )
2012-03-30 22:47:00 +04:00
{
remap_ops = & intel_irq_remap_ops ;
2012-06-26 18:59:34 +04:00
# ifdef CONFIG_AMD_IOMMU
if ( amd_iommu_irq_ops . prepare ( ) = = 0 )
remap_ops = & amd_iommu_irq_ops ;
# endif
2012-03-30 22:47:00 +04:00
}
2013-04-17 00:38:32 +04:00
void set_irq_remapping_broken ( void )
{
irq_remap_broken = 1 ;
}
2012-03-30 22:47:07 +04:00
int irq_remapping_supported ( void )
2012-03-30 22:47:00 +04:00
{
2012-03-30 22:47:07 +04:00
if ( disable_irq_remap )
2012-03-30 22:47:00 +04:00
return 0 ;
if ( ! remap_ops | | ! remap_ops - > supported )
return 0 ;
return remap_ops - > supported ( ) ;
}
2012-03-30 22:47:07 +04:00
int __init irq_remapping_prepare ( void )
2012-03-30 22:47:00 +04:00
{
2012-03-30 22:47:07 +04:00
if ( ! remap_ops | | ! remap_ops - > prepare )
2012-03-30 22:47:00 +04:00
return - ENODEV ;
2012-03-30 22:47:07 +04:00
return remap_ops - > prepare ( ) ;
2012-03-30 22:47:00 +04:00
}
2012-03-30 22:47:07 +04:00
int __init irq_remapping_enable ( void )
2012-03-30 22:47:00 +04:00
{
2012-09-26 14:44:35 +04:00
int ret ;
2012-03-30 22:47:07 +04:00
if ( ! remap_ops | | ! remap_ops - > enable )
2012-03-30 22:47:00 +04:00
return - ENODEV ;
2012-09-26 14:44:35 +04:00
ret = remap_ops - > enable ( ) ;
if ( irq_remapping_enabled )
irq_remapping_modify_x86_ops ( ) ;
return ret ;
2012-03-30 22:47:00 +04:00
}
2012-03-30 22:47:01 +04:00
2012-03-30 22:47:07 +04:00
void irq_remapping_disable ( void )
2012-03-30 22:47:01 +04:00
{
2012-09-26 14:44:33 +04:00
if ( ! irq_remapping_enabled | |
! remap_ops | |
! remap_ops - > disable )
2012-03-30 22:47:01 +04:00
return ;
2012-03-30 22:47:07 +04:00
remap_ops - > disable ( ) ;
2012-03-30 22:47:01 +04:00
}
2012-03-30 22:47:07 +04:00
int irq_remapping_reenable ( int mode )
2012-03-30 22:47:01 +04:00
{
2012-09-26 14:44:33 +04:00
if ( ! irq_remapping_enabled | |
! remap_ops | |
! remap_ops - > reenable )
2012-03-30 22:47:01 +04:00
return 0 ;
2012-03-30 22:47:07 +04:00
return remap_ops - > reenable ( mode ) ;
2012-03-30 22:47:01 +04:00
}
2012-03-30 22:47:07 +04:00
int __init irq_remap_enable_fault_handling ( void )
2012-03-30 22:47:01 +04:00
{
2012-09-26 14:44:33 +04:00
if ( ! irq_remapping_enabled )
return 0 ;
2012-03-30 22:47:01 +04:00
if ( ! remap_ops | | ! remap_ops - > enable_faulting )
return - ENODEV ;
return remap_ops - > enable_faulting ( ) ;
}
2012-03-30 22:47:02 +04:00
2012-03-30 22:47:07 +04:00
int setup_ioapic_remapped_entry ( int irq ,
struct IO_APIC_route_entry * entry ,
unsigned int destination , int vector ,
struct io_apic_irq_attr * attr )
2012-03-30 22:47:02 +04:00
{
if ( ! remap_ops | | ! remap_ops - > setup_ioapic_entry )
return - ENODEV ;
return remap_ops - > setup_ioapic_entry ( irq , entry , destination ,
vector , attr ) ;
}
2012-03-30 22:47:03 +04:00
2014-01-06 10:18:26 +04:00
static int set_remapped_irq_affinity ( struct irq_data * data ,
const struct cpumask * mask , bool force )
2012-03-30 22:47:03 +04:00
{
2012-06-15 05:28:49 +04:00
if ( ! config_enabled ( CONFIG_SMP ) | | ! remap_ops | |
! remap_ops - > set_affinity )
2012-03-30 22:47:03 +04:00
return 0 ;
return remap_ops - > set_affinity ( data , mask , force ) ;
}
2012-03-30 22:47:04 +04:00
2012-03-30 22:47:07 +04:00
void free_remapped_irq ( int irq )
2012-03-30 22:47:04 +04:00
{
2012-09-26 14:44:47 +04:00
struct irq_cfg * cfg = irq_get_chip_data ( irq ) ;
2012-03-30 22:47:04 +04:00
if ( ! remap_ops | | ! remap_ops - > free_irq )
return ;
2012-09-26 14:44:47 +04:00
if ( irq_remapped ( cfg ) )
remap_ops - > free_irq ( irq ) ;
2012-03-30 22:47:04 +04:00
}
2012-03-30 22:47:05 +04:00
2012-03-30 22:47:07 +04:00
void compose_remapped_msi_msg ( struct pci_dev * pdev ,
unsigned int irq , unsigned int dest ,
struct msi_msg * msg , u8 hpet_id )
2012-03-30 22:47:05 +04:00
{
2012-09-26 14:44:49 +04:00
struct irq_cfg * cfg = irq_get_chip_data ( irq ) ;
2012-03-30 22:47:05 +04:00
2012-09-26 14:44:49 +04:00
if ( ! irq_remapped ( cfg ) )
native_compose_msi_msg ( pdev , irq , dest , msg , hpet_id ) ;
else if ( remap_ops & & remap_ops - > compose_msi_msg )
remap_ops - > compose_msi_msg ( pdev , irq , dest , msg , hpet_id ) ;
2012-03-30 22:47:05 +04:00
}
2012-09-26 14:44:38 +04:00
static int msi_alloc_remapped_irq ( struct pci_dev * pdev , int irq , int nvec )
2012-03-30 22:47:05 +04:00
{
if ( ! remap_ops | | ! remap_ops - > msi_alloc_irq )
return - ENODEV ;
return remap_ops - > msi_alloc_irq ( pdev , irq , nvec ) ;
}
2012-09-26 14:44:38 +04:00
static int msi_setup_remapped_irq ( struct pci_dev * pdev , unsigned int irq ,
int index , int sub_handle )
2012-03-30 22:47:05 +04:00
{
if ( ! remap_ops | | ! remap_ops - > msi_setup_irq )
return - ENODEV ;
return remap_ops - > msi_setup_irq ( pdev , irq , index , sub_handle ) ;
}
2012-03-30 22:47:07 +04:00
int setup_hpet_msi_remapped ( unsigned int irq , unsigned int id )
2012-03-30 22:47:05 +04:00
{
if ( ! remap_ops | | ! remap_ops - > setup_hpet_msi )
return - ENODEV ;
return remap_ops - > setup_hpet_msi ( irq , id ) ;
}
2012-09-26 14:44:41 +04:00
void panic_if_irq_remap ( const char * msg )
{
if ( irq_remapping_enabled )
panic ( msg ) ;
}
2012-09-26 14:44:45 +04:00
static void ir_ack_apic_edge ( struct irq_data * data )
{
ack_APIC_irq ( ) ;
}
static void ir_ack_apic_level ( struct irq_data * data )
{
ack_APIC_irq ( ) ;
eoi_ioapic_irq ( data - > irq , data - > chip_data ) ;
}
static void ir_print_prefix ( struct irq_data * data , struct seq_file * p )
{
seq_printf ( p , " IR-%s " , data - > chip - > name ) ;
}
void irq_remap_modify_chip_defaults ( struct irq_chip * chip )
{
chip - > irq_print_chip = ir_print_prefix ;
chip - > irq_ack = ir_ack_apic_edge ;
chip - > irq_eoi = ir_ack_apic_level ;
chip - > irq_set_affinity = x86_io_apic_ops . set_affinity ;
}
2012-09-26 14:44:48 +04:00
bool setup_remapped_irq ( int irq , struct irq_cfg * cfg , struct irq_chip * chip )
{
if ( ! irq_remapped ( cfg ) )
return false ;
irq_set_status_flags ( irq , IRQ_MOVE_PCNTXT ) ;
irq_remap_modify_chip_defaults ( chip ) ;
return true ;
}