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/pci.h>
# include <linux/proc_fs.h>
2006-10-04 13:16:59 +04:00
# include <linux/msi.h>
2007-04-27 05:21:38 +04:00
# include <linux/smp.h>
2005-04-17 02:20:36 +04:00
# include <asm/errno.h>
# include <asm/io.h>
# include "pci.h"
# include "msi.h"
static int pci_msi_enable = 1 ;
2007-12-12 01:19:41 +03:00
/* Arch hooks */
2009-01-19 03:31:00 +03:00
# ifndef arch_msi_check_device
int arch_msi_check_device ( struct pci_dev * dev , int nvec , int type )
2007-12-12 01:19:41 +03:00
{
return 0 ;
}
2009-01-19 03:31:00 +03:00
# endif
2007-12-12 01:19:41 +03:00
2009-01-19 03:31:00 +03:00
# ifndef arch_setup_msi_irqs
int arch_setup_msi_irqs ( struct pci_dev * dev , int nvec , int type )
2007-12-12 01:19:41 +03:00
{
struct msi_desc * entry ;
int ret ;
2009-03-17 15:54:10 +03:00
/*
* If an architecture wants to support multiple MSI , it needs to
* override arch_setup_msi_irqs ( )
*/
if ( type = = PCI_CAP_ID_MSI & & nvec > 1 )
return 1 ;
2007-12-12 01:19:41 +03:00
list_for_each_entry ( entry , & dev - > msi_list , list ) {
ret = arch_setup_msi_irq ( dev , entry ) ;
2009-02-11 14:27:02 +03:00
if ( ret < 0 )
2007-12-12 01:19:41 +03:00
return ret ;
2009-02-11 14:27:02 +03:00
if ( ret > 0 )
return - ENOSPC ;
2007-12-12 01:19:41 +03:00
}
return 0 ;
}
2009-01-19 03:31:00 +03:00
# endif
2007-12-12 01:19:41 +03:00
2009-01-19 03:31:00 +03:00
# ifndef arch_teardown_msi_irqs
void arch_teardown_msi_irqs ( struct pci_dev * dev )
2007-12-12 01:19:41 +03:00
{
struct msi_desc * entry ;
list_for_each_entry ( entry , & dev - > msi_list , list ) {
2009-03-17 15:54:10 +03:00
int i , nvec ;
if ( entry - > irq = = 0 )
continue ;
nvec = 1 < < entry - > msi_attrib . multiple ;
for ( i = 0 ; i < nvec ; i + + )
arch_teardown_msi_irq ( entry - > irq + i ) ;
2007-12-12 01:19:41 +03:00
}
}
2009-01-19 03:31:00 +03:00
# endif
2007-12-12 01:19:41 +03:00
2009-06-16 16:31:45 +04:00
static void msi_set_enable ( struct pci_dev * dev , int pos , int enable )
2007-03-05 11:30:10 +03:00
{
u16 control ;
2009-06-16 16:31:45 +04:00
BUG_ON ( ! pos ) ;
2007-03-05 11:30:10 +03:00
2009-06-16 16:31:45 +04:00
pci_read_config_word ( dev , pos + PCI_MSI_FLAGS , & control ) ;
control & = ~ PCI_MSI_FLAGS_ENABLE ;
if ( enable )
control | = PCI_MSI_FLAGS_ENABLE ;
pci_write_config_word ( dev , pos + PCI_MSI_FLAGS , control ) ;
2008-05-19 08:48:17 +04:00
}
2007-03-05 11:30:10 +03:00
static void msix_set_enable ( struct pci_dev * dev , int enable )
{
int pos ;
u16 control ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
if ( pos ) {
pci_read_config_word ( dev , pos + PCI_MSIX_FLAGS , & control ) ;
control & = ~ PCI_MSIX_FLAGS_ENABLE ;
if ( enable )
control | = PCI_MSIX_FLAGS_ENABLE ;
pci_write_config_word ( dev , pos + PCI_MSIX_FLAGS , control ) ;
}
}
2009-01-22 03:19:19 +03:00
static inline __attribute_const__ u32 msi_mask ( unsigned x )
{
2009-02-09 06:27:47 +03:00
/* Don't shift by >= width of type */
if ( x > = 5 )
return 0xffffffff ;
return ( 1 < < ( 1 < < x ) ) - 1 ;
2009-01-22 03:19:19 +03:00
}
2009-03-17 15:54:09 +03:00
static inline __attribute_const__ u32 msi_capable_mask ( u16 control )
2007-03-30 22:54:08 +04:00
{
2009-03-17 15:54:09 +03:00
return msi_mask ( ( control > > 1 ) & 7 ) ;
}
2007-03-30 22:54:08 +04:00
2009-03-17 15:54:09 +03:00
static inline __attribute_const__ u32 msi_enabled_mask ( u16 control )
{
return msi_mask ( ( control > > 4 ) & 7 ) ;
2007-03-30 22:54:08 +04:00
}
2008-07-26 01:42:58 +04:00
/*
* PCI 2.3 does not specify mask bits for each MSI interrupt . Attempting to
* mask all MSI interrupts by clearing the MSI enable bit does not work
* reliably as devices without an INTx disable bit will then generate a
* level IRQ which will never be cleared .
*/
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
static u32 __msi_mask_irq ( struct msi_desc * desc , u32 mask , u32 flag )
2005-04-17 02:20:36 +04:00
{
2009-03-17 15:54:09 +03:00
u32 mask_bits = desc - > masked ;
2005-04-17 02:20:36 +04:00
2009-03-17 15:54:09 +03:00
if ( ! desc - > msi_attrib . maskbit )
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
return 0 ;
2009-03-17 15:54:09 +03:00
mask_bits & = ~ mask ;
mask_bits | = flag ;
pci_write_config_dword ( desc - > dev , desc - > mask_pos , mask_bits ) ;
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
return mask_bits ;
}
static void msi_mask_irq ( struct msi_desc * desc , u32 mask , u32 flag )
{
desc - > masked = __msi_mask_irq ( desc , mask , flag ) ;
2009-03-17 15:54:09 +03:00
}
/*
* This internal function does not flush PCI writes to the device .
* All users must ensure that they read from the device before either
* assuming that the device state is up to date , or returning out of this
* file . This saves a few milliseconds when initialising devices with lots
* of MSI - X interrupts .
*/
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
static u32 __msix_mask_irq ( struct msi_desc * desc , u32 flag )
2009-03-17 15:54:09 +03:00
{
u32 mask_bits = desc - > masked ;
unsigned offset = desc - > msi_attrib . entry_nr * PCI_MSIX_ENTRY_SIZE +
2009-06-23 12:40:04 +04:00
PCI_MSIX_ENTRY_VECTOR_CTRL ;
2009-03-17 15:54:09 +03:00
mask_bits & = ~ 1 ;
mask_bits | = flag ;
writel ( mask_bits , desc - > mask_base + offset ) ;
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
return mask_bits ;
}
static void msix_mask_irq ( struct msi_desc * desc , u32 flag )
{
desc - > masked = __msix_mask_irq ( desc , flag ) ;
2009-03-17 15:54:09 +03:00
}
2009-03-17 15:54:06 +03:00
2009-03-17 15:54:09 +03:00
static void msi_set_mask_bit ( unsigned irq , u32 flag )
{
struct msi_desc * desc = get_irq_msi ( irq ) ;
2009-03-17 15:54:06 +03:00
2009-03-17 15:54:09 +03:00
if ( desc - > msi_attrib . is_msix ) {
msix_mask_irq ( desc , flag ) ;
readl ( desc - > mask_base ) ; /* Flush write to device */
} else {
2009-03-17 15:54:10 +03:00
unsigned offset = irq - desc - > dev - > irq ;
msi_mask_irq ( desc , 1 < < offset , flag < < offset ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-17 15:54:09 +03:00
}
void mask_msi_irq ( unsigned int irq )
{
msi_set_mask_bit ( irq , 1 ) ;
}
void unmask_msi_irq ( unsigned int irq )
{
msi_set_mask_bit ( irq , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2008-12-06 05:58:34 +03:00
void read_msi_msg_desc ( struct irq_desc * desc , struct msi_msg * msg )
2005-04-17 02:20:36 +04:00
{
2008-12-06 05:58:34 +03:00
struct msi_desc * entry = get_irq_desc_msi ( desc ) ;
2009-03-17 15:54:06 +03:00
if ( entry - > msi_attrib . is_msix ) {
void __iomem * base = entry - > mask_base +
entry - > msi_attrib . entry_nr * PCI_MSIX_ENTRY_SIZE ;
2009-06-23 12:40:04 +04:00
msg - > address_lo = readl ( base + PCI_MSIX_ENTRY_LOWER_ADDR ) ;
msg - > address_hi = readl ( base + PCI_MSIX_ENTRY_UPPER_ADDR ) ;
msg - > data = readl ( base + PCI_MSIX_ENTRY_DATA ) ;
2009-03-17 15:54:06 +03:00
} else {
2006-10-04 13:16:33 +04:00
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 ;
2007-10-03 22:15:11 +04:00
pci_read_config_word ( dev , msi_data_reg ( pos , 0 ) , & data ) ;
2006-10-04 13:16:33 +04:00
}
msg - > data = data ;
}
}
2005-04-17 02:20:36 +04:00
2008-12-06 05:58:34 +03:00
void read_msi_msg ( unsigned int irq , struct msi_msg * msg )
2006-10-04 13:16:33 +04:00
{
2008-12-06 05:58:34 +03:00
struct irq_desc * desc = irq_to_desc ( irq ) ;
read_msi_msg_desc ( desc , msg ) ;
}
void write_msi_msg_desc ( struct irq_desc * desc , struct msi_msg * msg )
{
struct msi_desc * entry = get_irq_desc_msi ( desc ) ;
2009-03-17 15:54:06 +03:00
if ( entry - > msi_attrib . is_msix ) {
void __iomem * base ;
base = entry - > mask_base +
entry - > msi_attrib . entry_nr * PCI_MSIX_ENTRY_SIZE ;
2009-06-23 12:40:04 +04:00
writel ( msg - > address_lo , base + PCI_MSIX_ENTRY_LOWER_ADDR ) ;
writel ( msg - > address_hi , base + PCI_MSIX_ENTRY_UPPER_ADDR ) ;
writel ( msg - > data , base + PCI_MSIX_ENTRY_DATA ) ;
2009-03-17 15:54:06 +03:00
} else {
2006-10-04 13:16:33 +04:00
struct pci_dev * dev = entry - > dev ;
int pos = entry - > msi_attrib . pos ;
2009-03-17 15:54:10 +03:00
u16 msgctl ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & msgctl ) ;
msgctl & = ~ PCI_MSI_FLAGS_QSIZE ;
msgctl | = entry - > msi_attrib . multiple < < 4 ;
pci_write_config_word ( dev , msi_control_reg ( pos ) , msgctl ) ;
2006-10-04 13:16:33 +04:00
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
}
2007-03-08 23:04:57 +03:00
entry - > msg = * msg ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:16:33 +04:00
2008-12-06 05:58:34 +03:00
void write_msi_msg ( unsigned int irq , struct msi_msg * msg )
{
struct irq_desc * desc = irq_to_desc ( irq ) ;
write_msi_msg_desc ( desc , msg ) ;
}
2007-04-18 13:39:22 +04:00
static int msi_free_irqs ( struct pci_dev * dev ) ;
2007-01-18 07:50:05 +03:00
2009-03-17 15:54:07 +03:00
static struct msi_desc * alloc_msi_entry ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2009-03-17 15:54:07 +03:00
struct msi_desc * desc = kzalloc ( sizeof ( * desc ) , GFP_KERNEL ) ;
if ( ! desc )
2005-04-17 02:20:36 +04:00
return NULL ;
2009-03-17 15:54:07 +03:00
INIT_LIST_HEAD ( & desc - > list ) ;
desc - > dev = dev ;
2005-04-17 02:20:36 +04:00
2009-03-17 15:54:07 +03:00
return desc ;
2005-04-17 02:20:36 +04:00
}
2007-10-25 12:16:30 +04:00
static void pci_intx_for_msi ( struct pci_dev * dev , int enable )
{
if ( ! ( dev - > dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG ) )
pci_intx ( dev , enable ) ;
}
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
{
2007-03-08 23:04:57 +03:00
int pos ;
2006-02-08 12:11:38 +03:00
u16 control ;
2007-03-08 23:04:57 +03:00
struct msi_desc * entry ;
2006-02-08 12:11:38 +03:00
2007-03-05 11:30:10 +03:00
if ( ! dev - > msi_enabled )
return ;
2007-03-08 23:04:57 +03:00
entry = get_irq_msi ( dev - > irq ) ;
pos = entry - > msi_attrib . pos ;
2006-02-08 12:11:38 +03:00
2007-10-25 12:16:30 +04:00
pci_intx_for_msi ( dev , 0 ) ;
2009-06-16 16:31:45 +04:00
msi_set_enable ( dev , pos , 0 ) ;
2007-03-08 23:04:57 +03:00
write_msi_msg ( dev - > irq , & entry - > msg ) ;
pci_read_config_word ( dev , pos + PCI_MSI_FLAGS , & control ) ;
2009-03-17 15:54:09 +03:00
msi_mask_irq ( entry , msi_capable_mask ( control ) , entry - > masked ) ;
2008-08-07 19:52:37 +04:00
control & = ~ PCI_MSI_FLAGS_QSIZE ;
2009-03-17 15:54:10 +03:00
control | = ( entry - > msi_attrib . multiple < < 4 ) | PCI_MSI_FLAGS_ENABLE ;
2006-02-08 12:11:38 +03:00
pci_write_config_word ( dev , pos + PCI_MSI_FLAGS , control ) ;
2007-01-25 11:34:08 +03:00
}
static void __pci_restore_msix_state ( struct pci_dev * dev )
2006-02-08 12:11:38 +03:00
{
int pos ;
struct msi_desc * entry ;
2007-03-08 23:04:57 +03:00
u16 control ;
2006-02-08 12:11:38 +03:00
2007-01-28 22:42:52 +03:00
if ( ! dev - > msix_enabled )
return ;
2009-06-19 06:15:59 +04:00
BUG_ON ( list_empty ( & dev - > msi_list ) ) ;
entry = list_entry ( dev - > msi_list . next , struct msi_desc , list ) ;
pos = entry - > msi_attrib . pos ;
pci_read_config_word ( dev , pos + PCI_MSIX_FLAGS , & control ) ;
2007-01-28 22:42:52 +03:00
2006-02-08 12:11:38 +03:00
/* route the table */
2007-10-25 12:16:30 +04:00
pci_intx_for_msi ( dev , 0 ) ;
2009-06-19 06:15:59 +04:00
control | = PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL ;
pci_write_config_word ( dev , pos + PCI_MSIX_FLAGS , control ) ;
2006-02-08 12:11:38 +03:00
2007-04-05 11:19:10 +04:00
list_for_each_entry ( entry , & dev - > msi_list , list ) {
write_msi_msg ( entry - > irq , & entry - > msg ) ;
2009-03-17 15:54:09 +03:00
msix_mask_irq ( entry , entry - > masked ) ;
2006-02-08 12:11:38 +03:00
}
2007-03-08 23:04:57 +03:00
control & = ~ PCI_MSIX_FLAGS_MASKALL ;
pci_write_config_word ( dev , pos + PCI_MSIX_FLAGS , control ) ;
2006-02-08 12:11:38 +03:00
}
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-11-08 00:43:59 +03:00
EXPORT_SYMBOL_GPL ( pci_restore_msi_state ) ;
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
2009-03-17 15:54:10 +03:00
* @ nvec : number of interrupts to allocate
2005-04-17 02:20:36 +04:00
*
2009-03-17 15:54:10 +03:00
* Setup the MSI capability structure of the device with the requested
* number of interrupts . A return value of zero indicates the successful
* setup of an entry with the new MSI irq . A negative return value indicates
* an error , and a positive return value indicates the number of interrupts
* which could have been allocated .
*/
static int msi_capability_init ( struct pci_dev * dev , int nvec )
2005-04-17 02:20:36 +04:00
{
struct msi_desc * entry ;
2007-04-18 13:39:21 +04:00
int pos , ret ;
2005-04-17 02:20:36 +04:00
u16 control ;
2009-03-17 15:54:09 +03:00
unsigned mask ;
2005-04-17 02:20:36 +04:00
pos = pci_find_capability ( dev , PCI_CAP_ID_MSI ) ;
2009-06-16 16:31:45 +04:00
msi_set_enable ( dev , pos , 0 ) ; /* Disable MSI during set up */
2005-04-17 02:20:36 +04:00
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
/* MSI Entry Initialization */
2009-03-17 15:54:07 +03:00
entry = alloc_msi_entry ( dev ) ;
2007-01-28 22:56:37 +03:00
if ( ! entry )
return - ENOMEM ;
2006-10-04 13:16:41 +04:00
2009-03-17 15:54:06 +03:00
entry - > msi_attrib . is_msix = 0 ;
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 ;
2009-03-17 15:54:09 +03:00
2009-04-20 05:54:59 +04:00
entry - > mask_pos = msi_mask_reg ( pos , entry - > msi_attrib . is_64 ) ;
2009-03-17 15:54:09 +03:00
/* All MSIs are unmasked by default, Mask them all */
if ( entry - > msi_attrib . maskbit )
pci_read_config_dword ( dev , entry - > mask_pos , & entry - > masked ) ;
mask = msi_capable_mask ( control ) ;
msi_mask_irq ( entry , mask , mask ) ;
2007-06-01 11:46:32 +04:00
list_add_tail ( & entry - > list , & dev - > msi_list ) ;
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-04-18 13:39:21 +04:00
2005-04-17 02:20:36 +04:00
/* Configure MSI capability structure */
2009-03-17 15:54:10 +03:00
ret = arch_setup_msi_irqs ( dev , nvec , PCI_CAP_ID_MSI ) ;
2007-04-18 13:39:21 +04:00
if ( ret ) {
2009-06-23 12:39:27 +04:00
msi_mask_irq ( entry , mask , ~ mask ) ;
2007-04-18 13:39:22 +04:00
msi_free_irqs ( dev ) ;
2007-04-18 13:39:21 +04:00
return ret ;
2006-04-11 06:17:48 +04:00
}
2007-01-28 22:56:37 +03:00
2005-04-17 02:20:36 +04:00
/* Set MSI enabled bits */
2007-10-25 12:16:30 +04:00
pci_intx_for_msi ( dev , 0 ) ;
2009-06-16 16:31:45 +04:00
msi_set_enable ( dev , pos , 1 ) ;
2007-03-05 11:30:10 +03:00
dev - > msi_enabled = 1 ;
2005-04-17 02:20:36 +04:00
2007-04-18 13:39:21 +04:00
dev - > irq = entry - > 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 )
{
2007-04-05 11:19:10 +04:00
struct msi_desc * entry ;
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-04-18 13:39:21 +04:00
int pos , i , j , nr_entries , ret ;
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 ) ;
2009-06-19 06:15:59 +04:00
pci_read_config_word ( dev , pos + PCI_MSIX_FLAGS , & control ) ;
/* Ensure MSI-X is disabled while it is set up */
control & = ~ PCI_MSIX_FLAGS_ENABLE ;
pci_write_config_word ( dev , pos + PCI_MSIX_FLAGS , control ) ;
2005-04-17 02:20:36 +04:00
/* Request & Map MSI-X table region */
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 ;
for ( i = 0 ; i < nvec ; i + + ) {
2009-03-17 15:54:07 +03:00
entry = alloc_msi_entry ( dev ) ;
2009-06-24 07:08:27 +04:00
if ( ! entry ) {
if ( ! i )
iounmap ( base ) ;
else
msi_free_irqs ( dev ) ;
/* No enough memory. Don't try again */
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
j = entries [ i ] . entry ;
2009-03-17 15:54:06 +03:00
entry - > msi_attrib . is_msix = 1 ;
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 ;
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 - > mask_base = base ;
2007-01-28 22:56:37 +03:00
2007-06-01 11:46:32 +04:00
list_add_tail ( & entry - > list , & dev - > msi_list ) ;
2005-04-17 02:20:36 +04:00
}
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-04-18 13:39:21 +04:00
ret = arch_setup_msi_irqs ( dev , nvec , PCI_CAP_ID_MSIX ) ;
2009-02-11 14:27:02 +03:00
if ( ret < 0 ) {
/* If we had some success report the number of irqs
* we succeeded in setting up . */
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-04-18 13:39:21 +04:00
int avail = 0 ;
list_for_each_entry ( entry , & dev - > msi_list , list ) {
if ( entry - > irq ! = 0 ) {
avail + + ;
}
2005-04-17 02:20:36 +04:00
}
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-04-18 13:39:21 +04:00
2009-02-11 14:27:02 +03:00
if ( avail ! = 0 )
ret = avail ;
}
2007-04-18 13:39:22 +04:00
2009-02-11 14:27:02 +03:00
if ( ret ) {
msi_free_irqs ( dev ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-04-18 13:39:21 +04:00
2009-06-19 06:15:59 +04:00
/*
* Some devices require MSI - X to be enabled before we can touch the
* MSI - X registers . We need to mask all the vectors to prevent
* interrupts coming in before they ' re fully set up .
*/
control | = PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE ;
pci_write_config_word ( dev , pos + PCI_MSIX_FLAGS , control ) ;
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-04-18 13:39:21 +04:00
i = 0 ;
list_for_each_entry ( entry , & dev - > msi_list , list ) {
entries [ i ] . vector = entry - > irq ;
set_irq_msi ( entry - > irq , entry ) ;
2009-06-19 06:15:59 +04:00
j = entries [ i ] . entry ;
entry - > masked = readl ( base + j * PCI_MSIX_ENTRY_SIZE +
2009-06-23 12:40:04 +04:00
PCI_MSIX_ENTRY_VECTOR_CTRL ) ;
2009-06-19 06:15:59 +04:00
msix_mask_irq ( entry , 1 ) ;
MSI: Give archs the option to allocate all MSI/Xs at once.
This patch introduces an optional function, arch_setup_msi_irqs(),
(note the plural) which gives an arch the opportunity to do per-device
setup for MSI/X and then allocate all the requested MSI/Xs at once.
If that's not required by the arch, the default version simply calls
arch_setup_msi_irq() for each MSI irq required.
arch_setup_msi_irqs() is passed a pdev, attached to the pdev is a list
of msi_descs with irq == 0, it is up to the arch to connect these up to
an irq (via set_irq_msi()) or return an error. For convenience the number
of vectors and the type are passed also.
All msi_descs with irq != 0 are considered allocated, and the arch
teardown routine will be called on them when necessary.
The existing semantics of pci_enable_msix() are that if the requested
number of irqs can not be allocated, the maximum number that _could_ be
allocated is returned. To support that, we define that in case of an
error from arch_setup_msi_irqs(), the number of msi_descs with irq != 0
are considered allocated, and are counted toward the "max that could be
allocated".
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-04-18 13:39:21 +04:00
i + + ;
}
2009-06-19 06:15:59 +04:00
/* Set MSI-X enabled bits and unmask the function */
2007-10-25 12:16:30 +04:00
pci_intx_for_msi ( dev , 0 ) ;
2007-03-05 11:30:10 +03:00
dev - > msix_enabled = 1 ;
2005-04-17 02:20:36 +04:00
2009-06-19 06:15:59 +04:00
control & = ~ PCI_MSIX_FLAGS_MASKALL ;
pci_write_config_word ( dev , pos + PCI_MSIX_FLAGS , control ) ;
2009-05-08 17:13:33 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-08-31 09:55:07 +04:00
/**
2007-04-05 11:19:07 +04:00
* pci_msi_check_device - check whether MSI may be enabled on a device
2006-08-31 09:55:07 +04:00
* @ dev : pointer to the pci_dev data structure of MSI device function
2007-04-05 11:19:08 +04:00
* @ nvec : how many MSIs have been requested ?
2007-03-22 13:51:39 +03:00
* @ type : are we checking for MSI or MSI - X ?
2006-08-31 09:55:07 +04:00
*
2006-10-05 12:24:31 +04:00
* Look at global flags , the device itself , and its parent busses
2007-04-05 11:19:07 +04:00
* to determine if MSI / - X are supported for the device . If MSI / - X is
* supported return 0 , else return an error code .
2006-08-31 09:55:07 +04:00
* */
2007-04-05 11:19:08 +04:00
static int pci_msi_check_device ( struct pci_dev * dev , int nvec , int type )
2006-08-31 09:55:07 +04:00
{
struct pci_bus * bus ;
2007-04-05 11:19:08 +04:00
int ret ;
2006-08-31 09:55:07 +04:00
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 ;
2007-04-05 11:19:12 +04:00
/*
* You can ' t ask to have 0 or less MSIs configured .
* a ) it ' s stupid . .
* b ) the list manipulation code assumes nvec > = 1.
*/
if ( nvec < 1 )
return - ERANGE ;
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 ;
2007-04-05 11:19:08 +04:00
ret = arch_msi_check_device ( dev , nvec , type ) ;
if ( ret )
return ret ;
2007-03-22 13:51:39 +03:00
if ( ! pci_find_capability ( dev , type ) )
return - EINVAL ;
2006-08-31 09:55:07 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
2009-03-17 15:54:10 +03:00
* pci_enable_msi_block - configure device ' s MSI capability structure
* @ dev : device to configure
* @ nvec : number of interrupts to configure
2005-04-17 02:20:36 +04:00
*
2009-03-17 15:54:10 +03:00
* Allocate IRQs for a device with the MSI capability .
* This function returns a negative errno if an error occurs . If it
* is unable to allocate the number of interrupts requested , it returns
* the number of interrupts it might be able to allocate . If it successfully
* allocates at least the number of interrupts requested , it returns 0 and
* updates the @ dev ' s irq member to the lowest new interrupt number ; the
* other interrupt numbers allocated to this device are consecutive .
*/
int pci_enable_msi_block ( struct pci_dev * dev , unsigned int nvec )
2005-04-17 02:20:36 +04:00
{
2009-03-17 15:54:10 +03:00
int status , pos , maxvec ;
u16 msgctl ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSI ) ;
if ( ! pos )
return - EINVAL ;
pci_read_config_word ( dev , pos + PCI_MSI_FLAGS , & msgctl ) ;
maxvec = 1 < < ( ( msgctl & PCI_MSI_FLAGS_QMASK ) > > 1 ) ;
if ( nvec > maxvec )
return maxvec ;
2005-04-17 02:20:36 +04:00
2009-03-17 15:54:10 +03:00
status = pci_msi_check_device ( dev , nvec , PCI_CAP_ID_MSI ) ;
2007-04-05 11:19:08 +04:00
if ( status )
return status ;
2005-04-17 02:20:36 +04:00
2007-01-28 22:42:52 +03:00
WARN_ON ( ! ! dev - > msi_enabled ) ;
2005-04-17 02:20:36 +04:00
2009-03-17 15:54:10 +03:00
/* Check whether driver already requested MSI-X irqs */
2007-03-05 11:30:10 +03:00
if ( dev - > msix_enabled ) {
2008-06-13 20:52:11 +04:00
dev_info ( & dev - > dev , " can't enable MSI "
" (MSI-X already enabled) \n " ) ;
2007-03-05 11:30:10 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2009-03-17 15:54:10 +03:00
status = msi_capability_init ( dev , nvec ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
2009-03-17 15:54:10 +03:00
EXPORT_SYMBOL ( pci_enable_msi_block ) ;
2005-04-17 02:20:36 +04:00
2009-03-17 15:54:09 +03:00
void pci_msi_shutdown ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2009-03-17 15:54:09 +03:00
struct msi_desc * desc ;
u32 mask ;
u16 ctrl ;
2009-06-16 16:31:45 +04:00
unsigned pos ;
2005-04-17 02:20:36 +04:00
2007-03-22 13:51:39 +03:00
if ( ! pci_msi_enable | | ! dev | | ! dev - > msi_enabled )
2007-01-28 22:42:52 +03:00
return ;
2009-06-16 16:31:45 +04:00
BUG_ON ( list_empty ( & dev - > msi_list ) ) ;
desc = list_first_entry ( & dev - > msi_list , struct msi_desc , list ) ;
pos = desc - > msi_attrib . pos ;
msi_set_enable ( dev , pos , 0 ) ;
2007-10-25 12:16:30 +04:00
pci_intx_for_msi ( dev , 1 ) ;
2007-03-05 11:30:10 +03:00
dev - > msi_enabled = 0 ;
2006-10-04 13:16:31 +04:00
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
/* Return the device with MSI unmasked as initial states */
2009-06-16 16:31:45 +04:00
pci_read_config_word ( dev , pos + PCI_MSI_FLAGS , & ctrl ) ;
2009-03-17 15:54:09 +03:00
mask = msi_capable_mask ( ctrl ) ;
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
/* Keep cached state to be restored */
__msi_mask_irq ( desc , mask , ~ mask ) ;
2007-03-22 13:51:27 +03:00
/* Restore dev->irq to its default pin-assertion irq */
2009-03-17 15:54:09 +03:00
dev - > irq = desc - > msi_attrib . default_irq ;
2008-04-24 01:58:09 +04:00
}
2009-03-17 15:54:06 +03:00
2008-04-24 01:58:09 +04:00
void pci_disable_msi ( struct pci_dev * dev )
{
if ( ! pci_msi_enable | | ! dev | | ! dev - > msi_enabled )
return ;
pci_msi_shutdown ( dev ) ;
msi_free_irqs ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-22 13:51:34 +03:00
EXPORT_SYMBOL ( pci_disable_msi ) ;
2005-04-17 02:20:36 +04:00
2007-04-18 13:39:22 +04:00
static int msi_free_irqs ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2007-04-18 13:39:22 +04:00
struct msi_desc * entry , * tmp ;
2007-03-22 13:51:34 +03:00
2007-05-12 00:26:44 +04:00
list_for_each_entry ( entry , & dev - > msi_list , list ) {
2009-03-17 15:54:10 +03:00
int i , nvec ;
if ( ! entry - > irq )
continue ;
nvec = 1 < < entry - > msi_attrib . multiple ;
for ( i = 0 ; i < nvec ; i + + )
BUG_ON ( irq_has_action ( entry - > irq + i ) ) ;
2007-05-12 00:26:44 +04:00
}
2005-04-17 02:20:36 +04:00
2007-04-18 13:39:22 +04:00
arch_teardown_msi_irqs ( dev ) ;
2005-04-17 02:20:36 +04:00
2007-04-18 13:39:22 +04:00
list_for_each_entry_safe ( entry , tmp , & dev - > msi_list , list ) {
2009-03-17 15:54:06 +03:00
if ( entry - > msi_attrib . is_msix ) {
2007-06-01 11:46:33 +04:00
if ( list_is_last ( & entry - > list , & dev - > msi_list ) )
iounmap ( entry - > mask_base ) ;
2007-04-18 13:39:22 +04:00
}
list_del ( & entry - > list ) ;
kfree ( entry ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2009-01-24 02:21:14 +03:00
/**
* pci_msix_table_size - return the number of device ' s MSI - X table entries
* @ dev : pointer to the pci_dev data structure of MSI - X device function
*/
int pci_msix_table_size ( struct pci_dev * dev )
{
int pos ;
u16 control ;
pos = pci_find_capability ( dev , PCI_CAP_ID_MSIX ) ;
if ( ! pos )
return 0 ;
pci_read_config_word ( dev , msi_control_reg ( pos ) , & control ) ;
return multi_msix_capable ( control ) ;
}
2005-04-17 02:20:36 +04:00
/**
* 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
2009-05-07 12:28:41 +04:00
* of irqs or MSI - X vectors available . Driver should use the returned value to
* re - send its request .
2005-04-17 02:20:36 +04:00
* */
int pci_enable_msix ( struct pci_dev * dev , struct msix_entry * entries , int nvec )
{
2009-01-24 02:21:14 +03:00
int status , nr_entries ;
2007-01-28 22:42:52 +03:00
int i , j ;
2005-04-17 02:20:36 +04:00
2007-04-05 11:19:08 +04:00
if ( ! entries )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2007-04-05 11:19:08 +04:00
status = pci_msi_check_device ( dev , nvec , PCI_CAP_ID_MSIX ) ;
if ( status )
return status ;
2009-01-24 02:21:14 +03:00
nr_entries = pci_msix_table_size ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( nvec > nr_entries )
2009-05-07 12:28:41 +04:00
return nr_entries ;
2005-04-17 02:20:36 +04:00
/* 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 */
2007-03-05 11:30:10 +03:00
if ( dev - > msi_enabled ) {
2008-06-13 20:52:11 +04:00
dev_info ( & dev - > dev , " can't enable MSI-X "
" (MSI IRQ already assigned) \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
status = msix_capability_init ( dev , entries , nvec ) ;
return status ;
}
2007-03-22 13:51:34 +03:00
EXPORT_SYMBOL ( pci_enable_msix ) ;
2005-04-17 02:20:36 +04:00
2007-03-22 13:51:33 +03:00
static void msix_free_all_irqs ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2007-04-18 13:39:22 +04:00
msi_free_irqs ( dev ) ;
2007-03-22 13:51:33 +03:00
}
2008-04-24 01:58:09 +04:00
void pci_msix_shutdown ( struct pci_dev * dev )
2007-03-22 13:51:33 +03:00
{
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
struct msi_desc * entry ;
2007-03-22 13:51:39 +03:00
if ( ! pci_msi_enable | | ! dev | | ! dev - > msix_enabled )
2007-01-28 22:42:52 +03:00
return ;
PCI MSI: Fix restoration of MSI/MSI-X mask states in suspend/resume
There are 2 problems on mask states in suspend/resume.
[1]:
It is better to restore the mask states of MSI/MSI-X to initial states
(MSI is unmasked, MSI-X is masked) when we release the device.
The pci_msi_shutdown() does the restoration of mask states for MSI,
while the msi_free_irqs() does it for MSI-X. In other words, in the
"disable" path both of MSI and MSI-X are handled, but in the "shutdown"
path only MSI is handled.
MSI:
pci_disable_msi()
=> pci_msi_shutdown()
[ mask states for MSI restored ]
=> msi_set_enable(dev, pos, 0);
=> msi_free_irqs()
MSI-X:
pci_disable_msix()
=> pci_msix_shutdown()
=> msix_set_enable(dev, 0);
=> msix_free_all_irqs
=> msi_free_irqs()
[ mask states for MSI-X restored ]
This patch moves the masking for MSI-X from msi_free_irqs() to
pci_msix_shutdown().
This change has some positive side effects:
- It prevents OS from touching mask states before reading preserved
bits in the register, which can be happen if msi_free_irqs() is
called from error path in msix_capability_init().
- It also prevents touching the register after turning off MSI-X in
"disable" path, which can be a problem on some devices.
[2]:
We have cache of the mask state in msi_desc, which is automatically
updated when msi/msix_mask_irq() is called. This cached states are
used for the resume.
But since what need to be restored in the resume is the states before
the shutdown on the suspend, calling msi/msix_mask_irq() from
pci_msi/msix_shutdown() is not appropriate.
This patch introduces __msi/msix_mask_irq() that do mask as same
as msi/msix_mask_irq() but does not update cached state, for use
in pci_msi/msix_shutdown().
[updated: get rid of msi/msix_mask_irq_nocache() (proposed by Matthew Wilcox)]
Reviewed-by: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2009-06-24 07:08:09 +04:00
/* Return the device with MSI-X masked as initial states */
list_for_each_entry ( entry , & dev - > msi_list , list ) {
/* Keep cached states to be restored */
__msix_mask_irq ( entry , 1 ) ;
}
2007-03-05 11:30:10 +03:00
msix_set_enable ( dev , 0 ) ;
2007-10-25 12:16:30 +04:00
pci_intx_for_msi ( dev , 1 ) ;
2007-03-05 11:30:10 +03:00
dev - > msix_enabled = 0 ;
2008-04-24 01:58:09 +04:00
}
2009-08-06 06:31:27 +04:00
2008-04-24 01:58:09 +04:00
void pci_disable_msix ( struct pci_dev * dev )
{
if ( ! pci_msi_enable | | ! dev | | ! dev - > msix_enabled )
return ;
pci_msix_shutdown ( dev ) ;
2007-03-22 13:51:33 +03:00
msix_free_all_irqs ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-22 13:51:34 +03:00
EXPORT_SYMBOL ( pci_disable_msix ) ;
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-04-18 13:39:22 +04:00
if ( dev - > msi_enabled )
msi_free_irqs ( dev ) ;
2005-04-17 02:20:36 +04:00
2007-03-22 13:51:33 +03:00
if ( dev - > msix_enabled )
msix_free_all_irqs ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-06 08:33:34 +03:00
void pci_no_msi ( void )
{
pci_msi_enable = 0 ;
}
2007-04-05 11:19:08 +04:00
2008-11-11 01:31:05 +03:00
/**
* pci_msi_enabled - is MSI enabled ?
*
* Returns true if MSI has not been disabled by the command - line option
* pci = nomsi .
* */
int pci_msi_enabled ( void )
2008-10-17 08:52:51 +04:00
{
2008-11-11 01:31:05 +03:00
return pci_msi_enable ;
2008-10-17 08:52:51 +04:00
}
2008-11-11 01:31:05 +03:00
EXPORT_SYMBOL ( pci_msi_enabled ) ;
2008-10-17 08:52:51 +04:00
2008-11-11 01:31:05 +03:00
void pci_msi_init_pci_dev ( struct pci_dev * dev )
2008-10-17 08:52:51 +04:00
{
2008-11-11 01:31:05 +03:00
INIT_LIST_HEAD ( & dev - > msi_list ) ;
2008-10-17 08:52:51 +04:00
}