2005-04-17 02:20:36 +04:00
/*
* File : msi . c
* Purpose : PCI Message Signaled Interrupt ( MSI )
*
* Copyright ( C ) 2003 - 2004 Intel
* Copyright ( C ) Tom Long Nguyen ( tom . l . nguyen @ intel . com )
*/
2006-10-04 13:16:41 +04:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
# include <linux/mm.h>
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/smp_lock.h>
# include <linux/pci.h>
# include <linux/proc_fs.h>
2006-10-04 13:16:59 +04:00
# include <linux/msi.h>
2005-04-17 02:20:36 +04:00
# include <asm/errno.h>
# include <asm/io.h>
# include <asm/smp.h>
# include "pci.h"
# include "msi.h"
2006-12-07 07:33:20 +03:00
static struct kmem_cache * msi_cachep ;
2005-04-17 02:20:36 +04:00
static int pci_msi_enable = 1 ;
static int msi_cache_init ( void )
{
2006-09-27 12:51:03 +04:00
msi_cachep = kmem_cache_create ( " msi_cache " , sizeof ( struct msi_desc ) ,
0 , SLAB_HWCACHE_ALIGN , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ! msi_cachep )
return - ENOMEM ;
return 0 ;
}
2006-10-04 13:16:41 +04:00
static void msi_set_mask_bit ( unsigned int irq , int flag )
2005-04-17 02:20:36 +04:00
{
struct msi_desc * entry ;
2007-01-28 22:52:03 +03:00
entry = get_irq_msi ( irq ) ;
2006-10-04 13:16:57 +04:00
BUG_ON ( ! entry | | ! entry - > dev ) ;
2005-04-17 02:20:36 +04:00
switch ( entry - > msi_attrib . type ) {
case PCI_CAP_ID_MSI :
2006-10-04 13:16:57 +04:00
if ( entry - > msi_attrib . maskbit ) {
2007-01-18 07:50:05 +03:00
int pos ;
u32 mask_bits ;
2006-10-04 13:16:57 +04:00
pos = ( long ) entry - > mask_base ;
pci_read_config_dword ( entry - > dev , pos , & mask_bits ) ;
mask_bits & = ~ ( 1 ) ;
mask_bits | = flag ;
pci_write_config_dword ( entry - > dev , pos , mask_bits ) ;
}
2005-04-17 02:20:36 +04:00
break ;
case PCI_CAP_ID_MSIX :
{
int offset = entry - > msi_attrib . entry_nr * PCI_MSIX_ENTRY_SIZE +
PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET ;
writel ( flag , entry - > mask_base + offset ) ;
break ;
}
default :
2006-10-04 13:16:57 +04:00
BUG ( ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2006-10-04 13:16:59 +04:00
void read_msi_msg ( unsigned int irq , struct msi_msg * msg )
2005-04-17 02:20:36 +04:00
{
2007-01-28 22:52:03 +03:00
struct msi_desc * entry = get_irq_msi ( irq ) ;
2006-10-04 13:16:33 +04:00
switch ( entry - > msi_attrib . type ) {
case PCI_CAP_ID_MSI :
{
struct pci_dev * dev = entry - > dev ;
int pos = entry - > msi_attrib . pos ;
u16 data ;
pci_read_config_dword ( dev , msi_lower_address_reg ( pos ) ,
& msg - > address_lo ) ;
if ( entry - > msi_attrib . is_64 ) {
pci_read_config_dword ( dev , msi_upper_address_reg ( pos ) ,
& msg - > address_hi ) ;
pci_read_config_word ( dev , msi_data_reg ( pos , 1 ) , & data ) ;
} else {
msg - > address_hi = 0 ;
pci_read_config_word ( dev , msi_data_reg ( pos , 1 ) , & data ) ;
}
msg - > data = data ;
break ;
}
case PCI_CAP_ID_MSIX :
{
void __iomem * base ;
base = entry - > mask_base +
entry - > msi_attrib . entry_nr * PCI_MSIX_ENTRY_SIZE ;
msg - > address_lo = readl ( base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET ) ;
msg - > address_hi = readl ( base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET ) ;
msg - > data = readl ( base + PCI_MSIX_ENTRY_DATA_OFFSET ) ;
break ;
}
default :
BUG ( ) ;
}
}
2005-04-17 02:20:36 +04:00
2006-10-04 13:16:59 +04:00
void write_msi_msg ( unsigned int irq , struct msi_msg * msg )
2006-10-04 13:16:33 +04:00
{
2007-01-28 22:52:03 +03:00
struct msi_desc * entry = get_irq_msi ( irq ) ;
2005-04-17 02:20:36 +04:00
switch ( entry - > msi_attrib . type ) {
case PCI_CAP_ID_MSI :
{
2006-10-04 13:16:33 +04:00
struct pci_dev * dev = entry - > dev ;
int pos = entry - > msi_attrib . pos ;
pci_write_config_dword ( dev , msi_lower_address_reg ( pos ) ,
msg - > address_lo ) ;
if ( entry - > msi_attrib . is_64 ) {
pci_write_config_dword ( dev , msi_upper_address_reg ( pos ) ,
msg - > address_hi ) ;
pci_write_config_word ( dev , msi_data_reg ( pos , 1 ) ,
msg - > data ) ;
} else {
pci_write_config_word ( dev , msi_data_reg ( pos , 0 ) ,
msg - > data ) ;
}
2005-04-17 02:20:36 +04:00
break ;
}
case PCI_CAP_ID_MSIX :
{
2006-10-04 13:16:33 +04:00
void __iomem * base ;
base = entry - > mask_base +
entry - > msi_attrib . entry_nr * PCI_MSIX_ENTRY_SIZE ;
writel ( msg - > address_lo ,
base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET ) ;
writel ( msg - > address_hi ,
base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET ) ;
writel ( msg - > data , base + PCI_MSIX_ENTRY_DATA_OFFSET ) ;
2005-04-17 02:20:36 +04:00
break ;
}
default :
2006-10-04 13:16:33 +04:00
BUG ( ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-10-04 13:16:33 +04:00
2006-10-04 13:16:59 +04:00
void mask_msi_irq ( unsigned int irq )
2005-04-17 02:20:36 +04:00
{
2006-10-04 13:16:41 +04:00
msi_set_mask_bit ( irq , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:16:59 +04:00
void unmask_msi_irq ( unsigned int irq )
2005-04-17 02:20:36 +04:00
{
2006-10-04 13:16:41 +04:00
msi_set_mask_bit ( irq , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:16:41 +04:00
static int msi_free_irq ( struct pci_dev * dev , int irq ) ;
2007-01-18 07:50:05 +03:00
2005-04-17 02:20:36 +04:00
static int msi_init ( void )
{
static int status = - ENOMEM ;
if ( ! status )
return status ;
2006-01-14 10:34:53 +03:00
status = msi_cache_init ( ) ;
if ( status < 0 ) {
2005-04-17 02:20:36 +04:00
pci_msi_enable = 0 ;
printk ( KERN_WARNING " PCI: MSI cache init failed \n " ) ;
return status ;
}
2006-04-11 06:17:48 +04:00
2005-04-17 02:20:36 +04:00
return status ;
}
static struct msi_desc * alloc_msi_entry ( void )
{
struct msi_desc * entry ;
2006-09-27 12:51:03 +04:00
entry = kmem_cache_zalloc ( msi_cachep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
return NULL ;
entry - > link . tail = entry - > link . head = 0 ; /* single message */
entry - > dev = NULL ;
return entry ;
}
2006-10-04 13:16:59 +04:00
static int create_msi_irq ( void )
2005-04-17 02:20:36 +04:00
{
2006-10-04 13:16:41 +04:00
struct msi_desc * entry ;
int irq ;
entry = alloc_msi_entry ( ) ;
if ( ! entry )
return - ENOMEM ;
2006-01-26 03:42:11 +03:00
2006-10-04 13:16:41 +04:00
irq = create_irq ( ) ;
if ( irq < 0 ) {
kmem_cache_free ( msi_cachep , entry ) ;
return - EBUSY ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:16:41 +04:00
2007-01-28 22:52:03 +03:00
set_irq_msi ( irq , entry ) ;
2006-10-04 13:16:41 +04:00
return irq ;
}
static void destroy_msi_irq ( unsigned int irq )
{
struct msi_desc * entry ;
2007-01-28 22:52:03 +03:00
entry = get_irq_msi ( irq ) ;
2006-10-04 13:16:41 +04:00
set_irq_chip ( irq , NULL ) ;
2007-01-28 22:52:03 +03:00
set_irq_msi ( irq , NULL ) ;
2006-10-04 13:16:41 +04:00
destroy_irq ( irq ) ;
kmem_cache_free ( msi_cachep , entry ) ;
2005-04-17 02:20:36 +04:00
}
static void enable_msi_mode ( struct pci_dev * dev , int pos , int type )
{
u16 control ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
if ( type = = PCI_CAP_ID_MSI ) {
/* Set enabled bits to single MSI & enable MSI_enable bit */
msi_enable ( control , 1 ) ;
pci_write_config_word ( dev , msi_control_reg ( pos ) , control ) ;
2006-05-26 06:58:27 +04:00
dev - > msi_enabled = 1 ;
2005-04-17 02:20:36 +04:00
} else {
msix_enable ( control ) ;
pci_write_config_word ( dev , msi_control_reg ( pos ) , control ) ;
2006-05-26 06:58:27 +04:00
dev - > msix_enabled = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-12-08 01:56:06 +03:00
pci_intx ( dev , 0 ) ; /* disable intx */
2005-04-17 02:20:36 +04:00
}
2005-08-17 02:15:58 +04:00
void disable_msi_mode ( struct pci_dev * dev , int pos , int type )
2005-04-17 02:20:36 +04:00
{
u16 control ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
if ( type = = PCI_CAP_ID_MSI ) {
/* Set enabled bits to single MSI & enable MSI_enable bit */
msi_disable ( control ) ;
pci_write_config_word ( dev , msi_control_reg ( pos ) , control ) ;
2006-05-26 06:58:27 +04:00
dev - > msi_enabled = 0 ;
2005-04-17 02:20:36 +04:00
} else {
msix_disable ( control ) ;
pci_write_config_word ( dev , msi_control_reg ( pos ) , control ) ;
2006-05-26 06:58:27 +04:00
dev - > msix_enabled = 0 ;
2005-04-17 02:20:36 +04:00
}
2006-12-08 01:56:06 +03:00
pci_intx ( dev , 1 ) ; /* enable intx */
2005-04-17 02:20:36 +04:00
}
2006-02-08 12:11:38 +03:00
# ifdef CONFIG_PM
2007-01-25 11:34:08 +03:00
static int __pci_save_msi_state ( struct pci_dev * dev )
2006-02-08 12:11:38 +03:00
{
int pos , i = 0 ;
u16 control ;
struct pci_cap_saved_state * save_state ;
u32 * cap ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSI ) ;
if ( pos < = 0 | | dev - > no_msi )
return 0 ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
if ( ! ( control & PCI_MSI_FLAGS_ENABLE ) )
return 0 ;
save_state = kzalloc ( sizeof ( struct pci_cap_saved_state ) + sizeof ( u32 ) * 5 ,
GFP_KERNEL ) ;
if ( ! save_state ) {
printk ( KERN_ERR " Out of memory in pci_save_msi_state \n " ) ;
return - ENOMEM ;
}
cap = & save_state - > data [ 0 ] ;
pci_read_config_dword ( dev , pos , & cap [ i + + ] ) ;
control = cap [ 0 ] > > 16 ;
pci_read_config_dword ( dev , pos + PCI_MSI_ADDRESS_LO , & cap [ i + + ] ) ;
if ( control & PCI_MSI_FLAGS_64BIT ) {
pci_read_config_dword ( dev , pos + PCI_MSI_ADDRESS_HI , & cap [ i + + ] ) ;
pci_read_config_dword ( dev , pos + PCI_MSI_DATA_64 , & cap [ i + + ] ) ;
} else
pci_read_config_dword ( dev , pos + PCI_MSI_DATA_32 , & cap [ i + + ] ) ;
if ( control & PCI_MSI_FLAGS_MASKBIT )
pci_read_config_dword ( dev , pos + PCI_MSI_MASK_BIT , & cap [ i + + ] ) ;
save_state - > cap_nr = PCI_CAP_ID_MSI ;
pci_add_saved_cap ( dev , save_state ) ;
return 0 ;
}
2007-01-25 11:34:08 +03:00
static void __pci_restore_msi_state ( struct pci_dev * dev )
2006-02-08 12:11:38 +03:00
{
int i = 0 , pos ;
u16 control ;
struct pci_cap_saved_state * save_state ;
u32 * cap ;
save_state = pci_find_saved_cap ( dev , PCI_CAP_ID_MSI ) ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSI ) ;
if ( ! save_state | | pos < = 0 )
return ;
cap = & save_state - > data [ 0 ] ;
control = cap [ i + + ] > > 16 ;
pci_write_config_dword ( dev , pos + PCI_MSI_ADDRESS_LO , cap [ i + + ] ) ;
if ( control & PCI_MSI_FLAGS_64BIT ) {
pci_write_config_dword ( dev , pos + PCI_MSI_ADDRESS_HI , cap [ i + + ] ) ;
pci_write_config_dword ( dev , pos + PCI_MSI_DATA_64 , cap [ i + + ] ) ;
} else
pci_write_config_dword ( dev , pos + PCI_MSI_DATA_32 , cap [ i + + ] ) ;
if ( control & PCI_MSI_FLAGS_MASKBIT )
pci_write_config_dword ( dev , pos + PCI_MSI_MASK_BIT , cap [ i + + ] ) ;
pci_write_config_word ( dev , pos + PCI_MSI_FLAGS , control ) ;
enable_msi_mode ( dev , pos , PCI_CAP_ID_MSI ) ;
pci_remove_saved_cap ( save_state ) ;
kfree ( save_state ) ;
}
2007-01-25 11:34:08 +03:00
static int __pci_save_msix_state ( struct pci_dev * dev )
2006-02-08 12:11:38 +03:00
{
int pos ;
2006-10-04 13:16:41 +04:00
int irq , head , tail = 0 ;
2006-02-08 12:11:38 +03:00
u16 control ;
struct pci_cap_saved_state * save_state ;
2007-01-28 22:42:52 +03:00
if ( ! dev - > msix_enabled )
return 0 ;
2006-02-08 12:11:38 +03:00
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
if ( pos < = 0 | | dev - > no_msi )
return 0 ;
2006-04-11 06:17:48 +04:00
/* save the capability */
2006-02-08 12:11:38 +03:00
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
if ( ! ( control & PCI_MSIX_FLAGS_ENABLE ) )
return 0 ;
save_state = kzalloc ( sizeof ( struct pci_cap_saved_state ) + sizeof ( u16 ) ,
GFP_KERNEL ) ;
if ( ! save_state ) {
printk ( KERN_ERR " Out of memory in pci_save_msix_state \n " ) ;
return - ENOMEM ;
}
* ( ( u16 * ) & save_state - > data [ 0 ] ) = control ;
2006-04-11 06:17:48 +04:00
/* save the table */
2007-01-28 22:42:52 +03:00
irq = head = dev - > first_msi_irq ;
2006-04-11 06:17:48 +04:00
while ( head ! = tail ) {
struct msi_desc * entry ;
2007-01-28 22:52:03 +03:00
entry = get_irq_msi ( irq ) ;
2006-10-04 13:16:59 +04:00
read_msi_msg ( irq , & entry - > msg_save ) ;
2006-04-11 06:17:48 +04:00
2007-01-28 22:52:03 +03:00
tail = entry - > link . tail ;
2006-10-04 13:16:41 +04:00
irq = tail ;
2006-04-11 06:17:48 +04:00
}
2006-02-08 12:11:38 +03:00
save_state - > cap_nr = PCI_CAP_ID_MSIX ;
pci_add_saved_cap ( dev , save_state ) ;
return 0 ;
}
2007-01-25 11:34:08 +03:00
int pci_save_msi_state ( struct pci_dev * dev )
{
int rc ;
rc = __pci_save_msi_state ( dev ) ;
if ( rc )
return rc ;
rc = __pci_save_msix_state ( dev ) ;
return rc ;
}
static void __pci_restore_msix_state ( struct pci_dev * dev )
2006-02-08 12:11:38 +03:00
{
u16 save ;
int pos ;
2006-10-04 13:16:41 +04:00
int irq , head , tail = 0 ;
2006-02-08 12:11:38 +03:00
struct msi_desc * entry ;
struct pci_cap_saved_state * save_state ;
2007-01-28 22:42:52 +03:00
if ( ! dev - > msix_enabled )
return ;
2006-02-08 12:11:38 +03:00
save_state = pci_find_saved_cap ( dev , PCI_CAP_ID_MSIX ) ;
if ( ! save_state )
return ;
save = * ( ( u16 * ) & save_state - > data [ 0 ] ) ;
pci_remove_saved_cap ( save_state ) ;
kfree ( save_state ) ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
if ( pos < = 0 )
return ;
/* route the table */
2007-01-28 22:42:52 +03:00
irq = head = dev - > first_msi_irq ;
2006-02-08 12:11:38 +03:00
while ( head ! = tail ) {
2007-01-28 22:52:03 +03:00
entry = get_irq_msi ( irq ) ;
2006-10-04 13:16:59 +04:00
write_msi_msg ( irq , & entry - > msg_save ) ;
2006-02-08 12:11:38 +03:00
2007-01-28 22:52:03 +03:00
tail = entry - > link . tail ;
2006-10-04 13:16:41 +04:00
irq = tail ;
2006-02-08 12:11:38 +03:00
}
pci_write_config_word ( dev , msi_control_reg ( pos ) , save ) ;
enable_msi_mode ( dev , pos , PCI_CAP_ID_MSIX ) ;
}
2007-01-25 11:34:08 +03:00
void pci_restore_msi_state ( struct pci_dev * dev )
{
__pci_restore_msi_state ( dev ) ;
__pci_restore_msix_state ( dev ) ;
}
2007-01-18 07:50:05 +03:00
# endif /* CONFIG_PM */
2006-02-08 12:11:38 +03:00
2005-04-17 02:20:36 +04:00
/**
* msi_capability_init - configure device ' s MSI capability structure
* @ dev : pointer to the pci_dev data structure of MSI device function
*
2005-05-04 04:38:30 +04:00
* Setup the MSI capability structure of device function with a single
2006-10-04 13:16:41 +04:00
* MSI irq , regardless of device function is capable of handling
2005-04-17 02:20:36 +04:00
* multiple messages . A return of zero indicates the successful setup
2006-10-04 13:16:41 +04:00
* of an entry zero with the new MSI irq or non - zero for otherwise .
2005-04-17 02:20:36 +04:00
* */
static int msi_capability_init ( struct pci_dev * dev )
{
2006-04-11 06:17:48 +04:00
int status ;
2005-04-17 02:20:36 +04:00
struct msi_desc * entry ;
2006-10-04 13:16:41 +04:00
int pos , irq ;
2005-04-17 02:20:36 +04:00
u16 control ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSI ) ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
/* MSI Entry Initialization */
2006-10-04 13:16:59 +04:00
irq = create_msi_irq ( ) ;
2006-10-04 13:16:41 +04:00
if ( irq < 0 )
return irq ;
2007-01-28 22:52:03 +03:00
entry = get_irq_msi ( irq ) ;
2006-10-04 13:16:41 +04:00
entry - > link . head = irq ;
entry - > link . tail = irq ;
2005-04-17 02:20:36 +04:00
entry - > msi_attrib . type = PCI_CAP_ID_MSI ;
2006-10-04 13:16:33 +04:00
entry - > msi_attrib . is_64 = is_64bit_address ( control ) ;
2005-04-17 02:20:36 +04:00
entry - > msi_attrib . entry_nr = 0 ;
entry - > msi_attrib . maskbit = is_mask_bit_support ( control ) ;
2006-10-04 13:16:41 +04:00
entry - > msi_attrib . default_irq = dev - > irq ; /* Save IOAPIC IRQ */
2006-10-04 13:16:33 +04:00
entry - > msi_attrib . pos = pos ;
2005-04-17 02:20:36 +04:00
if ( is_mask_bit_support ( control ) ) {
entry - > mask_base = ( void __iomem * ) ( long ) msi_mask_bits_reg ( pos ,
is_64bit_address ( control ) ) ;
}
2006-10-04 13:16:59 +04:00
entry - > dev = dev ;
if ( entry - > msi_attrib . maskbit ) {
unsigned int maskbits , temp ;
/* All MSIs are unmasked by default, Mask them all */
pci_read_config_dword ( dev ,
msi_mask_bits_reg ( pos , is_64bit_address ( control ) ) ,
& maskbits ) ;
temp = ( 1 < < multi_msi_capable ( control ) ) ;
temp = ( ( temp - 1 ) & ~ temp ) ;
maskbits | = temp ;
pci_write_config_dword ( dev ,
msi_mask_bits_reg ( pos , is_64bit_address ( control ) ) ,
maskbits ) ;
}
2005-04-17 02:20:36 +04:00
/* Configure MSI capability structure */
2006-10-04 13:16:59 +04:00
status = arch_setup_msi_irq ( irq , dev ) ;
if ( status < 0 ) {
2006-10-04 13:16:41 +04:00
destroy_msi_irq ( irq ) ;
2006-04-11 06:17:48 +04:00
return status ;
}
2006-02-08 12:11:38 +03:00
2007-01-28 22:42:52 +03:00
dev - > first_msi_irq = irq ;
2007-01-28 22:52:03 +03:00
set_irq_msi ( irq , entry ) ;
2005-04-17 02:20:36 +04:00
/* Set MSI enabled bits */
enable_msi_mode ( dev , pos , PCI_CAP_ID_MSI ) ;
2006-10-04 13:16:59 +04:00
dev - > irq = irq ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/**
* msix_capability_init - configure device ' s MSI - X capability
* @ dev : pointer to the pci_dev data structure of MSI - X device function
2005-10-23 22:57:38 +04:00
* @ entries : pointer to an array of struct msix_entry entries
* @ nvec : number of @ entries
2005-04-17 02:20:36 +04:00
*
2005-05-04 04:38:30 +04:00
* Setup the MSI - X capability structure of device function with a
2006-10-04 13:16:41 +04:00
* single MSI - X irq . A return of zero indicates the successful setup of
* requested MSI - X entries with allocated irqs or non - zero for otherwise .
2005-04-17 02:20:36 +04:00
* */
static int msix_capability_init ( struct pci_dev * dev ,
struct msix_entry * entries , int nvec )
{
struct msi_desc * head = NULL , * tail = NULL , * entry = NULL ;
2006-04-11 06:17:48 +04:00
int status ;
2006-10-04 13:16:41 +04:00
int irq , pos , i , j , nr_entries , temp = 0 ;
2006-02-17 10:58:29 +03:00
unsigned long phys_addr ;
u32 table_offset ;
2005-04-17 02:20:36 +04:00
u16 control ;
u8 bir ;
void __iomem * base ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
/* Request & Map MSI-X table region */
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
nr_entries = multi_msix_capable ( control ) ;
2006-02-17 10:58:29 +03:00
pci_read_config_dword ( dev , msix_table_offset_reg ( pos ) , & table_offset ) ;
2005-04-17 02:20:36 +04:00
bir = ( u8 ) ( table_offset & PCI_MSIX_FLAGS_BIRMASK ) ;
2006-02-17 10:58:29 +03:00
table_offset & = ~ PCI_MSIX_FLAGS_BIRMASK ;
phys_addr = pci_resource_start ( dev , bir ) + table_offset ;
2005-04-17 02:20:36 +04:00
base = ioremap_nocache ( phys_addr , nr_entries * PCI_MSIX_ENTRY_SIZE ) ;
if ( base = = NULL )
return - ENOMEM ;
/* MSI-X Table Initialization */
for ( i = 0 ; i < nvec ; i + + ) {
2006-10-04 13:16:59 +04:00
irq = create_msi_irq ( ) ;
2006-10-04 13:16:41 +04:00
if ( irq < 0 )
2005-04-17 02:20:36 +04:00
break ;
2007-01-28 22:52:03 +03:00
entry = get_irq_msi ( irq ) ;
2005-04-17 02:20:36 +04:00
j = entries [ i ] . entry ;
2006-10-04 13:16:41 +04:00
entries [ i ] . vector = irq ;
2005-04-17 02:20:36 +04:00
entry - > msi_attrib . type = PCI_CAP_ID_MSIX ;
2006-10-04 13:16:33 +04:00
entry - > msi_attrib . is_64 = 1 ;
2005-04-17 02:20:36 +04:00
entry - > msi_attrib . entry_nr = j ;
entry - > msi_attrib . maskbit = 1 ;
2006-10-04 13:16:41 +04:00
entry - > msi_attrib . default_irq = dev - > irq ;
2006-10-04 13:16:33 +04:00
entry - > msi_attrib . pos = pos ;
2005-04-17 02:20:36 +04:00
entry - > dev = dev ;
entry - > mask_base = base ;
if ( ! head ) {
2006-10-04 13:16:41 +04:00
entry - > link . head = irq ;
entry - > link . tail = irq ;
2005-04-17 02:20:36 +04:00
head = entry ;
} else {
entry - > link . head = temp ;
entry - > link . tail = tail - > link . tail ;
2006-10-04 13:16:41 +04:00
tail - > link . tail = irq ;
head - > link . head = irq ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:16:41 +04:00
temp = irq ;
2005-04-17 02:20:36 +04:00
tail = entry ;
/* Configure MSI-X capability structure */
2006-10-04 13:16:59 +04:00
status = arch_setup_msi_irq ( irq , dev ) ;
2006-10-04 13:16:41 +04:00
if ( status < 0 ) {
destroy_msi_irq ( irq ) ;
2006-04-11 06:17:48 +04:00
break ;
2006-10-04 13:16:41 +04:00
}
2006-04-11 06:17:48 +04:00
2007-01-28 22:52:03 +03:00
set_irq_msi ( irq , entry ) ;
2005-04-17 02:20:36 +04:00
}
if ( i ! = nvec ) {
2006-10-04 13:16:35 +04:00
int avail = i - 1 ;
2005-04-17 02:20:36 +04:00
i - - ;
for ( ; i > = 0 ; i - - ) {
2006-10-04 13:16:41 +04:00
irq = ( entries + i ) - > vector ;
msi_free_irq ( dev , irq ) ;
2005-04-17 02:20:36 +04:00
( entries + i ) - > vector = 0 ;
}
2006-10-04 13:16:35 +04:00
/* If we had some success report the number of irqs
* we succeeded in setting up .
*/
if ( avail < = 0 )
avail = - EBUSY ;
return avail ;
2005-04-17 02:20:36 +04:00
}
2007-01-28 22:42:52 +03:00
dev - > first_msi_irq = entries [ 0 ] . vector ;
2005-04-17 02:20:36 +04:00
/* Set MSI-X enabled bits */
enable_msi_mode ( dev , pos , PCI_CAP_ID_MSIX ) ;
return 0 ;
}
2006-08-31 09:55:07 +04:00
/**
* pci_msi_supported - check whether MSI may be enabled on device
* @ dev : pointer to the pci_dev data structure of MSI device function
*
2006-10-05 12:24:31 +04:00
* Look at global flags , the device itself , and its parent busses
* to return 0 if MSI are supported for the device .
2006-08-31 09:55:07 +04:00
* */
static
int pci_msi_supported ( struct pci_dev * dev )
{
struct pci_bus * bus ;
2006-10-05 12:24:31 +04:00
/* MSI must be globally enabled and supported by the device */
2006-08-31 09:55:07 +04:00
if ( ! pci_msi_enable | | ! dev | | dev - > no_msi )
return - EINVAL ;
2006-10-05 12:24:31 +04:00
/* Any bridge which does NOT route MSI transactions from it's
* secondary bus to it ' s primary bus must set NO_MSI flag on
* the secondary pci_bus .
* We expect only arch - specific PCI host bus controller driver
* or quirks for specific PCI bridges to be setting NO_MSI .
*/
2006-08-31 09:55:07 +04:00
for ( bus = dev - > bus ; bus ; bus = bus - > parent )
if ( bus - > bus_flags & PCI_BUS_FLAGS_NO_MSI )
return - EINVAL ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* pci_enable_msi - configure device ' s MSI capability structure
* @ dev : pointer to the pci_dev data structure of MSI device function
*
* Setup the MSI capability structure of device function with
2006-10-04 13:16:41 +04:00
* a single MSI irq upon its software driver call to request for
2005-04-17 02:20:36 +04:00
* MSI mode enabled on its hardware device function . A return of zero
* indicates the successful setup of an entry zero with the new MSI
2006-10-04 13:16:41 +04:00
* irq or non - zero for otherwise .
2005-04-17 02:20:36 +04:00
* */
int pci_enable_msi ( struct pci_dev * dev )
{
2007-01-28 22:42:52 +03:00
int pos , status ;
2005-04-17 02:20:36 +04:00
2006-08-31 09:55:07 +04:00
if ( pci_msi_supported ( dev ) < 0 )
return - EINVAL ;
2006-02-14 19:52:22 +03:00
2006-01-14 10:34:53 +03:00
status = msi_init ( ) ;
if ( status < 0 )
2005-04-17 02:20:36 +04:00
return status ;
2006-01-14 10:34:53 +03:00
pos = pci_find_capability ( dev , PCI_CAP_ID_MSI ) ;
if ( ! pos )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2007-01-28 22:42:52 +03:00
WARN_ON ( ! ! dev - > msi_enabled ) ;
2005-04-17 02:20:36 +04:00
2006-10-04 13:16:41 +04:00
/* Check whether driver already requested for MSI-X irqs */
2006-01-14 10:34:53 +03:00
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
2007-01-28 22:42:52 +03:00
if ( pos > 0 & & dev - > msix_enabled ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " PCI: %s: Can't enable MSI. "
2007-01-28 22:42:52 +03:00
" Device already has MSI-X enabled \n " ,
2005-04-17 02:20:36 +04:00
pci_name ( dev ) ) ;
return - EINVAL ;
}
status = msi_capability_init ( dev ) ;
return status ;
}
void pci_disable_msi ( struct pci_dev * dev )
{
struct msi_desc * entry ;
2006-10-04 13:16:41 +04:00
int pos , default_irq ;
2005-04-17 02:20:36 +04:00
u16 control ;
2006-03-06 08:33:34 +03:00
if ( ! pci_msi_enable )
return ;
2006-01-14 10:34:53 +03:00
if ( ! dev )
return ;
2006-03-06 08:33:34 +03:00
2007-01-28 22:42:52 +03:00
if ( ! dev - > msi_enabled )
return ;
2006-01-14 10:34:53 +03:00
pos = pci_find_capability ( dev , PCI_CAP_ID_MSI ) ;
if ( ! pos )
2005-04-17 02:20:36 +04:00
return ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
if ( ! ( control & PCI_MSI_FLAGS_ENABLE ) )
return ;
2007-01-28 22:42:52 +03:00
2006-10-04 13:16:31 +04:00
disable_msi_mode ( dev , pos , PCI_CAP_ID_MSI ) ;
2007-01-28 22:52:03 +03:00
entry = get_irq_msi ( dev - > first_msi_irq ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry | | ! entry - > dev | | entry - > msi_attrib . type ! = PCI_CAP_ID_MSI ) {
return ;
}
2007-01-28 22:42:52 +03:00
if ( irq_has_action ( dev - > first_msi_irq ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " PCI: %s: pci_disable_msi() called without "
2006-10-04 13:16:41 +04:00
" free_irq() on MSI irq %d \n " ,
2007-01-28 22:42:52 +03:00
pci_name ( dev ) , dev - > first_msi_irq ) ;
BUG_ON ( irq_has_action ( dev - > first_msi_irq ) ) ;
2005-04-17 02:20:36 +04:00
} else {
2006-10-04 13:16:41 +04:00
default_irq = entry - > msi_attrib . default_irq ;
2007-01-28 22:42:52 +03:00
msi_free_irq ( dev , dev - > first_msi_irq ) ;
2006-10-04 13:16:31 +04:00
2006-10-04 13:16:41 +04:00
/* Restore dev->irq to its default pin-assertion irq */
dev - > irq = default_irq ;
2005-04-17 02:20:36 +04:00
}
2007-01-28 22:42:52 +03:00
dev - > first_msi_irq = 0 ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:16:41 +04:00
static int msi_free_irq ( struct pci_dev * dev , int irq )
2005-04-17 02:20:36 +04:00
{
struct msi_desc * entry ;
int head , entry_nr , type ;
void __iomem * base ;
2006-10-04 13:16:59 +04:00
arch_teardown_msi_irq ( irq ) ;
2006-04-11 06:17:48 +04:00
2007-01-28 22:52:03 +03:00
entry = get_irq_msi ( irq ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry | | entry - > dev ! = dev ) {
return - EINVAL ;
}
type = entry - > msi_attrib . type ;
entry_nr = entry - > msi_attrib . entry_nr ;
head = entry - > link . head ;
base = entry - > mask_base ;
2007-01-28 22:52:03 +03:00
get_irq_msi ( entry - > link . head ) - > link . tail = entry - > link . tail ;
get_irq_msi ( entry - > link . tail ) - > link . head = entry - > link . head ;
2005-04-17 02:20:36 +04:00
entry - > dev = NULL ;
2006-10-04 13:16:41 +04:00
destroy_msi_irq ( irq ) ;
2005-04-17 02:20:36 +04:00
if ( type = = PCI_CAP_ID_MSIX ) {
2006-10-04 13:16:41 +04:00
writel ( 1 , base + entry_nr * PCI_MSIX_ENTRY_SIZE +
PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET ) ;
2005-04-17 02:20:36 +04:00
2006-10-04 13:16:41 +04:00
if ( head = = irq )
2005-04-17 02:20:36 +04:00
iounmap ( base ) ;
}
return 0 ;
}
/**
* pci_enable_msix - configure device ' s MSI - X capability structure
* @ dev : pointer to the pci_dev data structure of MSI - X device function
2005-06-07 10:07:46 +04:00
* @ entries : pointer to an array of MSI - X entries
2006-10-04 13:16:41 +04:00
* @ nvec : number of MSI - X irqs requested for allocation by device driver
2005-04-17 02:20:36 +04:00
*
* Setup the MSI - X capability structure of device function with the number
2006-10-04 13:16:41 +04:00
* of requested irqs upon its software driver call to request for
2005-04-17 02:20:36 +04:00
* MSI - X mode enabled on its hardware device function . A return of zero
* indicates the successful configuration of MSI - X capability structure
2006-10-04 13:16:41 +04:00
* with new allocated MSI - X irqs . A return of < 0 indicates a failure .
2005-04-17 02:20:36 +04:00
* Or a return of > 0 indicates that driver request is exceeding the number
2006-10-04 13:16:41 +04:00
* of irqs available . Driver should use the returned value to re - send
2005-04-17 02:20:36 +04:00
* its request .
* */
int pci_enable_msix ( struct pci_dev * dev , struct msix_entry * entries , int nvec )
{
2006-10-04 13:16:35 +04:00
int status , pos , nr_entries ;
2007-01-28 22:42:52 +03:00
int i , j ;
2005-04-17 02:20:36 +04:00
u16 control ;
2006-08-31 09:55:07 +04:00
if ( ! entries | | pci_msi_supported ( dev ) < 0 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-01-14 10:34:53 +03:00
status = msi_init ( ) ;
if ( status < 0 )
2005-04-17 02:20:36 +04:00
return status ;
2006-01-14 10:34:53 +03:00
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
if ( ! pos )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
nr_entries = multi_msix_capable ( control ) ;
if ( nvec > nr_entries )
return - EINVAL ;
/* Check for any invalid entries */
for ( i = 0 ; i < nvec ; i + + ) {
if ( entries [ i ] . entry > = nr_entries )
return - EINVAL ; /* invalid entry */
for ( j = i + 1 ; j < nvec ; j + + ) {
if ( entries [ i ] . entry = = entries [ j ] . entry )
return - EINVAL ; /* duplicate entry */
}
}
2007-01-28 22:42:52 +03:00
WARN_ON ( ! ! dev - > msix_enabled ) ;
2006-10-04 13:16:31 +04:00
2006-10-04 13:16:41 +04:00
/* Check whether driver already requested for MSI irq */
2005-04-17 02:20:36 +04:00
if ( pci_find_capability ( dev , PCI_CAP_ID_MSI ) > 0 & &
2007-01-28 22:42:52 +03:00
dev - > msi_enabled ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " PCI: %s: Can't enable MSI-X. "
2006-10-04 13:16:41 +04:00
" Device already has an MSI irq assigned \n " ,
2005-04-17 02:20:36 +04:00
pci_name ( dev ) ) ;
return - EINVAL ;
}
status = msix_capability_init ( dev , entries , nvec ) ;
return status ;
}
void pci_disable_msix ( struct pci_dev * dev )
{
2007-01-28 22:42:52 +03:00
int irq , head , tail = 0 , warning = 0 ;
int pos ;
2005-04-17 02:20:36 +04:00
u16 control ;
2006-03-06 08:33:34 +03:00
if ( ! pci_msi_enable )
return ;
2006-01-14 10:34:53 +03:00
if ( ! dev )
return ;
2007-01-28 22:42:52 +03:00
if ( ! dev - > msix_enabled )
return ;
2006-01-14 10:34:53 +03:00
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
if ( ! pos )
2005-04-17 02:20:36 +04:00
return ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
if ( ! ( control & PCI_MSIX_FLAGS_ENABLE ) )
return ;
2006-10-04 13:16:31 +04:00
disable_msi_mode ( dev , pos , PCI_CAP_ID_MSIX ) ;
2007-01-28 22:42:52 +03:00
irq = head = dev - > first_msi_irq ;
while ( head ! = tail ) {
2007-01-28 22:52:03 +03:00
tail = get_irq_msi ( irq ) - > link . tail ;
2007-01-28 22:42:52 +03:00
if ( irq_has_action ( irq ) )
warning = 1 ;
else if ( irq ! = head ) /* Release MSI-X irq */
msi_free_irq ( dev , irq ) ;
irq = tail ;
}
msi_free_irq ( dev , irq ) ;
if ( warning ) {
printk ( KERN_WARNING " PCI: %s: pci_disable_msix() called without "
" free_irq() on all MSI-X irqs \n " ,
pci_name ( dev ) ) ;
BUG_ON ( warning > 0 ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-28 22:42:52 +03:00
dev - > first_msi_irq = 0 ;
2005-04-17 02:20:36 +04:00
}
/**
2006-10-04 13:16:41 +04:00
* msi_remove_pci_irq_vectors - reclaim MSI ( X ) irqs to unused state
2005-04-17 02:20:36 +04:00
* @ dev : pointer to the pci_dev data structure of MSI ( X ) device function
*
2005-05-04 04:38:30 +04:00
* Being called during hotplug remove , from which the device function
2006-10-04 13:16:41 +04:00
* is hot - removed . All previous assigned MSI / MSI - X irqs , if
2005-04-17 02:20:36 +04:00
* allocated for this device function , are reclaimed to unused state ,
* which may be used later on .
* */
void msi_remove_pci_irq_vectors ( struct pci_dev * dev )
{
if ( ! pci_msi_enable | | ! dev )
return ;
2007-01-28 22:45:54 +03:00
if ( dev - > msi_enabled ) {
2007-01-28 22:42:52 +03:00
if ( irq_has_action ( dev - > first_msi_irq ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " PCI: %s: msi_remove_pci_irq_vectors() "
2006-10-04 13:16:41 +04:00
" called without free_irq() on MSI irq %d \n " ,
2007-01-28 22:42:52 +03:00
pci_name ( dev ) , dev - > first_msi_irq ) ;
BUG_ON ( irq_has_action ( dev - > first_msi_irq ) ) ;
2006-10-04 13:16:41 +04:00
} else /* Release MSI irq assigned to this device */
2007-01-28 22:42:52 +03:00
msi_free_irq ( dev , dev - > first_msi_irq ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-28 22:45:54 +03:00
if ( dev - > msix_enabled ) {
2006-10-04 13:16:41 +04:00
int irq , head , tail = 0 , warning = 0 ;
2005-04-17 02:20:36 +04:00
void __iomem * base = NULL ;
2007-01-28 22:42:52 +03:00
irq = head = dev - > first_msi_irq ;
2005-04-17 02:20:36 +04:00
while ( head ! = tail ) {
2007-01-28 22:52:03 +03:00
tail = get_irq_msi ( irq ) - > link . tail ;
base = get_irq_msi ( irq ) - > mask_base ;
2006-10-04 13:16:56 +04:00
if ( irq_has_action ( irq ) )
2005-04-17 02:20:36 +04:00
warning = 1 ;
2006-10-04 13:16:41 +04:00
else if ( irq ! = head ) /* Release MSI-X irq */
msi_free_irq ( dev , irq ) ;
irq = tail ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:16:41 +04:00
msi_free_irq ( dev , irq ) ;
2005-04-17 02:20:36 +04:00
if ( warning ) {
iounmap ( base ) ;
printk ( KERN_WARNING " PCI: %s: msi_remove_pci_irq_vectors() "
2006-10-04 13:16:41 +04:00
" called without free_irq() on all MSI-X irqs \n " ,
2005-04-17 02:20:36 +04:00
pci_name ( dev ) ) ;
BUG_ON ( warning > 0 ) ;
}
}
}
2006-03-06 08:33:34 +03:00
void pci_no_msi ( void )
{
pci_msi_enable = 0 ;
}
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( pci_enable_msi ) ;
EXPORT_SYMBOL ( pci_disable_msi ) ;
EXPORT_SYMBOL ( pci_enable_msix ) ;
EXPORT_SYMBOL ( pci_disable_msix ) ;