2006-04-11 06:17:48 +04:00
/*
* MSI hooks for standard x86 apic
*/
# include <linux/pci.h>
# include <linux/irq.h>
2006-10-04 13:16:59 +04:00
# include <linux/msi.h>
2008-10-17 23:14:13 +04:00
# include <linux/dmar.h>
2006-06-26 16:00:02 +04:00
# include <asm/smp.h>
2006-04-11 06:17:48 +04:00
/*
* Shifts for APIC - based data
*/
# define MSI_DATA_VECTOR_SHIFT 0
# define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT)
2007-07-17 16:22:48 +04:00
# define MSI_DATA_VECTOR_MASK 0xffffff00
2006-04-11 06:17:48 +04:00
# define MSI_DATA_DELIVERY_SHIFT 8
# define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT)
# define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT)
# define MSI_DATA_LEVEL_SHIFT 14
# define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT)
# define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT)
# define MSI_DATA_TRIGGER_SHIFT 15
# define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT)
# define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT)
/*
* Shift / mask fields for APIC - based bus address
*/
2006-10-04 13:16:59 +04:00
# define MSI_TARGET_CPU_SHIFT 4
2006-04-11 06:17:48 +04:00
# define MSI_ADDR_HEADER 0xfee00000
# define MSI_ADDR_DESTID_MASK 0xfff0000f
# define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT)
# define MSI_ADDR_DESTMODE_SHIFT 2
# define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT)
# define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT)
# define MSI_ADDR_REDIRECTION_SHIFT 3
# define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT)
# define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT)
2006-10-04 13:16:59 +04:00
static struct irq_chip ia64_msi_chip ;
2006-04-11 06:17:48 +04:00
2006-10-04 13:16:59 +04:00
# ifdef CONFIG_SMP
static void ia64_set_msi_irq_affinity ( unsigned int irq , cpumask_t cpu_mask )
2006-04-11 06:17:48 +04:00
{
2006-10-04 13:16:59 +04:00
struct msi_msg msg ;
2007-07-17 16:22:48 +04:00
u32 addr , data ;
int cpu = first_cpu ( cpu_mask ) ;
2006-10-04 13:16:59 +04:00
2007-07-17 16:22:48 +04:00
if ( ! cpu_online ( cpu ) )
return ;
2008-02-25 08:32:22 +03:00
if ( irq_prepare_move ( irq , cpu ) )
2007-07-17 16:22:33 +04:00
return ;
2006-10-04 13:16:59 +04:00
read_msi_msg ( irq , & msg ) ;
2006-04-11 06:17:48 +04:00
2006-10-04 13:16:59 +04:00
addr = msg . address_lo ;
2006-04-11 06:17:48 +04:00
addr & = MSI_ADDR_DESTID_MASK ;
2007-07-17 16:22:48 +04:00
addr | = MSI_ADDR_DESTID_CPU ( cpu_physical_id ( cpu ) ) ;
2006-10-04 13:16:59 +04:00
msg . address_lo = addr ;
2006-04-11 06:17:48 +04:00
2007-07-17 16:22:48 +04:00
data = msg . data ;
data & = MSI_DATA_VECTOR_MASK ;
data | = MSI_DATA_VECTOR ( irq_to_vector ( irq ) ) ;
msg . data = data ;
2006-10-04 13:16:59 +04:00
write_msi_msg ( irq , & msg ) ;
2007-07-17 16:22:48 +04:00
irq_desc [ irq ] . affinity = cpumask_of_cpu ( cpu ) ;
2006-04-11 06:17:48 +04:00
}
2006-10-04 13:16:59 +04:00
# endif /* CONFIG_SMP */
2006-04-11 06:17:48 +04:00
2007-01-28 22:56:37 +03:00
int ia64_setup_msi_irq ( struct pci_dev * pdev , struct msi_desc * desc )
2006-04-11 06:17:48 +04:00
{
2006-10-04 13:16:59 +04:00
struct msi_msg msg ;
2006-04-11 06:17:48 +04:00
unsigned long dest_phys_id ;
2007-03-26 04:38:42 +04:00
int irq , vector ;
2007-07-17 16:22:33 +04:00
cpumask_t mask ;
2006-04-11 06:17:48 +04:00
2007-01-28 22:56:37 +03:00
irq = create_irq ( ) ;
if ( irq < 0 )
return irq ;
set_irq_msi ( irq , desc ) ;
2007-07-17 16:22:33 +04:00
cpus_and ( mask , irq_to_domain ( irq ) , cpu_online_map ) ;
dest_phys_id = cpu_physical_id ( first_cpu ( mask ) ) ;
2007-04-06 11:51:12 +04:00
vector = irq_to_vector ( irq ) ;
2006-04-11 06:17:48 +04:00
2006-10-04 13:16:59 +04:00
msg . address_hi = 0 ;
msg . address_lo =
2006-10-04 13:16:34 +04:00
MSI_ADDR_HEADER |
MSI_ADDR_DESTMODE_PHYS |
MSI_ADDR_REDIRECTION_CPU |
MSI_ADDR_DESTID_CPU ( dest_phys_id ) ;
2006-04-11 06:17:48 +04:00
2006-10-04 13:16:59 +04:00
msg . data =
2006-10-04 13:16:34 +04:00
MSI_DATA_TRIGGER_EDGE |
2006-04-11 06:17:48 +04:00
MSI_DATA_LEVEL_ASSERT |
MSI_DATA_DELIVERY_FIXED |
MSI_DATA_VECTOR ( vector ) ;
2006-10-04 13:16:59 +04:00
write_msi_msg ( irq , & msg ) ;
set_irq_chip_and_handler ( irq , & ia64_msi_chip , handle_edge_irq ) ;
2007-10-30 10:01:49 +03:00
return 0 ;
2006-04-11 06:17:48 +04:00
}
2006-10-04 13:16:59 +04:00
void ia64_teardown_msi_irq ( unsigned int irq )
2006-04-11 06:17:48 +04:00
{
2007-01-28 22:56:37 +03:00
destroy_irq ( irq ) ;
2006-04-11 06:17:48 +04:00
}
2006-10-04 13:16:59 +04:00
static void ia64_ack_msi_irq ( unsigned int irq )
{
2008-02-25 08:32:22 +03:00
irq_complete_move ( irq ) ;
2006-10-04 13:16:59 +04:00
move_native_irq ( irq ) ;
ia64_eoi ( ) ;
}
static int ia64_msi_retrigger_irq ( unsigned int irq )
{
2007-04-06 11:51:12 +04:00
unsigned int vector = irq_to_vector ( irq ) ;
2006-10-04 13:16:59 +04:00
ia64_resend_irq ( vector ) ;
return 1 ;
}
2006-04-11 06:17:48 +04:00
/*
2006-10-04 13:16:59 +04:00
* Generic ops used on most IA64 platforms .
2006-04-11 06:17:48 +04:00
*/
2006-10-04 13:16:59 +04:00
static struct irq_chip ia64_msi_chip = {
. name = " PCI-MSI " ,
. mask = mask_msi_irq ,
. unmask = unmask_msi_irq ,
. ack = ia64_ack_msi_irq ,
# ifdef CONFIG_SMP
. set_affinity = ia64_set_msi_irq_affinity ,
# endif
. retrigger = ia64_msi_retrigger_irq ,
2006-04-11 06:17:48 +04:00
} ;
2006-10-04 13:16:59 +04:00
2007-01-28 22:56:37 +03:00
int arch_setup_msi_irq ( struct pci_dev * pdev , struct msi_desc * desc )
2006-10-04 13:16:59 +04:00
{
if ( platform_setup_msi_irq )
2007-01-28 22:56:37 +03:00
return platform_setup_msi_irq ( pdev , desc ) ;
2006-10-04 13:16:59 +04:00
2007-01-28 22:56:37 +03:00
return ia64_setup_msi_irq ( pdev , desc ) ;
2006-10-04 13:16:59 +04:00
}
void arch_teardown_msi_irq ( unsigned int irq )
{
if ( platform_teardown_msi_irq )
return platform_teardown_msi_irq ( irq ) ;
return ia64_teardown_msi_irq ( irq ) ;
}
2008-10-17 23:14:13 +04:00
# ifdef CONFIG_DMAR
# ifdef CONFIG_SMP
static void dmar_msi_set_affinity ( unsigned int irq , cpumask_t mask )
{
struct irq_cfg * cfg = irq_cfg + irq ;
struct msi_msg msg ;
int cpu = first_cpu ( mask ) ;
if ( ! cpu_online ( cpu ) )
return ;
if ( irq_prepare_move ( irq , cpu ) )
return ;
dmar_msi_read ( irq , & msg ) ;
msg . data & = ~ MSI_DATA_VECTOR_MASK ;
msg . data | = MSI_DATA_VECTOR ( cfg - > vector ) ;
msg . address_lo & = ~ MSI_ADDR_DESTID_MASK ;
msg . address_lo | = MSI_ADDR_DESTID_CPU ( cpu_physical_id ( cpu ) ) ;
dmar_msi_write ( irq , & msg ) ;
irq_desc [ irq ] . affinity = mask ;
}
# endif /* CONFIG_SMP */
struct irq_chip dmar_msi_type = {
. name = " DMAR_MSI " ,
. unmask = dmar_msi_unmask ,
. mask = dmar_msi_mask ,
. ack = ia64_ack_msi_irq ,
# ifdef CONFIG_SMP
. set_affinity = dmar_msi_set_affinity ,
# endif
. retrigger = ia64_msi_retrigger_irq ,
} ;
static int
msi_compose_msg ( struct pci_dev * pdev , unsigned int irq , struct msi_msg * msg )
{
struct irq_cfg * cfg = irq_cfg + irq ;
unsigned dest ;
cpumask_t mask ;
cpus_and ( mask , irq_to_domain ( irq ) , cpu_online_map ) ;
dest = cpu_physical_id ( first_cpu ( mask ) ) ;
msg - > address_hi = 0 ;
msg - > address_lo =
MSI_ADDR_HEADER |
MSI_ADDR_DESTMODE_PHYS |
MSI_ADDR_REDIRECTION_CPU |
MSI_ADDR_DESTID_CPU ( dest ) ;
msg - > data =
MSI_DATA_TRIGGER_EDGE |
MSI_DATA_LEVEL_ASSERT |
MSI_DATA_DELIVERY_FIXED |
MSI_DATA_VECTOR ( cfg - > vector ) ;
return 0 ;
}
int arch_setup_dmar_msi ( unsigned int irq )
{
int ret ;
struct msi_msg msg ;
ret = msi_compose_msg ( NULL , irq , & msg ) ;
if ( ret < 0 )
return ret ;
dmar_msi_write ( irq , & msg ) ;
set_irq_chip_and_handler_name ( irq , & dmar_msi_type , handle_edge_irq ,
" edge " ) ;
return 0 ;
}
# endif /* CONFIG_DMAR */