}
}
-static void msi_set_enable(struct pci_dev *dev, int enable)
+static void __msi_set_enable(struct pci_dev *dev, int pos, int enable)
{
- int pos;
u16 control;
- pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
if (pos) {
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
control &= ~PCI_MSI_FLAGS_ENABLE;
}
}
+static void msi_set_enable(struct pci_dev *dev, int enable)
+{
+ __msi_set_enable(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), enable);
+}
+
static void msix_set_enable(struct pci_dev *dev, int enable)
{
int pos;
}
}
-static void msi_set_mask_bit(unsigned int irq, int flag)
+/*
+ * 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.
+ *
+ * Returns 1 if it succeeded in masking the interrupt and 0 if the device
+ * doesn't support MSI masking.
+ */
+static int msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
{
struct msi_desc *entry;
pos = (long)entry->mask_base;
pci_read_config_dword(entry->dev, pos, &mask_bits);
- mask_bits &= ~(1);
- mask_bits |= flag;
+ mask_bits &= ~(mask);
+ mask_bits |= flag & mask;
pci_write_config_dword(entry->dev, pos, mask_bits);
} else {
- msi_set_enable(entry->dev, !flag);
+ return 0;
}
break;
case PCI_CAP_ID_MSIX:
break;
}
entry->msi_attrib.masked = !!flag;
+ return 1;
}
void read_msi_msg(unsigned int irq, struct msi_msg *msg)
void mask_msi_irq(unsigned int irq)
{
- msi_set_mask_bit(irq, 1);
+ msi_set_mask_bits(irq, 1, 1);
msix_flush_writes(irq);
}
void unmask_msi_irq(unsigned int irq)
{
- msi_set_mask_bit(irq, 0);
+ msi_set_mask_bits(irq, 1, 0);
msix_flush_writes(irq);
}
msi_set_enable(dev, 0);
write_msi_msg(dev->irq, &entry->msg);
if (entry->msi_attrib.maskbit)
- msi_set_mask_bit(dev->irq, entry->msi_attrib.masked);
+ msi_set_mask_bits(dev->irq, entry->msi_attrib.maskbits_mask,
+ entry->msi_attrib.masked);
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
list_for_each_entry(entry, &dev->msi_list, list) {
write_msi_msg(entry->irq, &entry->msg);
- msi_set_mask_bit(entry->irq, entry->msi_attrib.masked);
+ msi_set_mask_bits(entry->irq, 1, entry->msi_attrib.masked);
}
BUG_ON(list_empty(&dev->msi_list));
pci_write_config_dword(dev,
msi_mask_bits_reg(pos, is_64bit_address(control)),
maskbits);
+ entry->msi_attrib.maskbits_mask = temp;
}
list_add_tail(&entry->list, &dev->msi_list);
/* Check whether driver already requested for MSI-X irqs */
if (dev->msix_enabled) {
- printk(KERN_INFO "PCI: %s: Can't enable MSI. "
- "Device already has MSI-X enabled\n",
- pci_name(dev));
+ dev_info(&dev->dev, "can't enable MSI "
+ "(MSI-X already enabled)\n");
return -EINVAL;
}
status = msi_capability_init(dev);
}
EXPORT_SYMBOL(pci_enable_msi);
-void pci_disable_msi(struct pci_dev* dev)
+void pci_msi_shutdown(struct pci_dev* dev)
{
struct msi_desc *entry;
- int default_irq;
if (!pci_msi_enable || !dev || !dev->msi_enabled)
return;
BUG_ON(list_empty(&dev->msi_list));
entry = list_entry(dev->msi_list.next, struct msi_desc, list);
- if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) {
- return;
+ /* Return the the pci reset with msi irqs unmasked */
+ if (entry->msi_attrib.maskbit) {
+ u32 mask = entry->msi_attrib.maskbits_mask;
+ msi_set_mask_bits(dev->irq, mask, ~mask);
}
-
- default_irq = entry->msi_attrib.default_irq;
- msi_free_irqs(dev);
+ if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI)
+ return;
/* Restore dev->irq to its default pin-assertion irq */
- dev->irq = default_irq;
+ dev->irq = entry->msi_attrib.default_irq;
+}
+void pci_disable_msi(struct pci_dev* dev)
+{
+ struct msi_desc *entry;
+
+ if (!pci_msi_enable || !dev || !dev->msi_enabled)
+ return;
+
+ pci_msi_shutdown(dev);
+
+ entry = list_entry(dev->msi_list.next, struct msi_desc, list);
+ if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI)
+ return;
+
+ msi_free_irqs(dev);
}
EXPORT_SYMBOL(pci_disable_msi);
/* Check whether driver already requested for MSI irq */
if (dev->msi_enabled) {
- printk(KERN_INFO "PCI: %s: Can't enable MSI-X. "
- "Device already has an MSI irq assigned\n",
- pci_name(dev));
+ dev_info(&dev->dev, "can't enable MSI-X "
+ "(MSI IRQ already assigned)\n");
return -EINVAL;
}
status = msix_capability_init(dev, entries, nvec);
msi_free_irqs(dev);
}
-void pci_disable_msix(struct pci_dev* dev)
+void pci_msix_shutdown(struct pci_dev* dev)
{
if (!pci_msi_enable || !dev || !dev->msix_enabled)
return;
msix_set_enable(dev, 0);
pci_intx_for_msi(dev, 1);
dev->msix_enabled = 0;
+}
+void pci_disable_msix(struct pci_dev* dev)
+{
+ if (!pci_msi_enable || !dev || !dev->msix_enabled)
+ return;
+
+ pci_msix_shutdown(dev);
msix_free_all_irqs(dev);
}