aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kiszka <jan.kiszka@siemens.com>2011-11-04 04:46:00 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2012-01-06 15:10:34 -0500
commita2e27787f893621c5a6b865acf6b7766f8671328 (patch)
treeb7398b80a56b1e25e4758dfc9ed2af2df27458f3
parentfb51ccbf217c1c994607b6519c7d85250928553d (diff)
PCI: Introduce INTx check & mask API
These new PCI services allow to probe for 2.3-compliant INTx masking support and then use the feature from PCI interrupt handlers. The services are properly synchronized with concurrent config space access via sysfs or on device reset. This enables generic PCI device drivers like uio_pci_generic or KVM's device assignment to implement the necessary kernel-side IRQ handling without any knowledge about device-specific interrupt status and control registers. Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r--drivers/pci/access.c2
-rw-r--r--drivers/pci/pci.c110
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--include/linux/pci.h3
4 files changed, 116 insertions, 1 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 0c4c71712dfc..2a581642c237 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -13,7 +13,7 @@
13 * configuration space. 13 * configuration space.
14 */ 14 */
15 15
16static DEFINE_RAW_SPINLOCK(pci_lock); 16DEFINE_RAW_SPINLOCK(pci_lock);
17 17
18/* 18/*
19 * Wrappers for all PCI configuration access functions. They just check 19 * Wrappers for all PCI configuration access functions. They just check
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index c3cca7cdc6e5..924193ef4fe1 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2768,6 +2768,116 @@ pci_intx(struct pci_dev *pdev, int enable)
2768} 2768}
2769 2769
2770/** 2770/**
2771 * pci_intx_mask_supported - probe for INTx masking support
2772 * @pdev: the PCI device to operate on
2773 *
2774 * Check if the device dev support INTx masking via the config space
2775 * command word.
2776 */
2777bool pci_intx_mask_supported(struct pci_dev *dev)
2778{
2779 bool mask_supported = false;
2780 u16 orig, new;
2781
2782 pci_cfg_access_lock(dev);
2783
2784 pci_read_config_word(dev, PCI_COMMAND, &orig);
2785 pci_write_config_word(dev, PCI_COMMAND,
2786 orig ^ PCI_COMMAND_INTX_DISABLE);
2787 pci_read_config_word(dev, PCI_COMMAND, &new);
2788
2789 /*
2790 * There's no way to protect against hardware bugs or detect them
2791 * reliably, but as long as we know what the value should be, let's
2792 * go ahead and check it.
2793 */
2794 if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) {
2795 dev_err(&dev->dev, "Command register changed from "
2796 "0x%x to 0x%x: driver or hardware bug?\n", orig, new);
2797 } else if ((new ^ orig) & PCI_COMMAND_INTX_DISABLE) {
2798 mask_supported = true;
2799 pci_write_config_word(dev, PCI_COMMAND, orig);
2800 }
2801
2802 pci_cfg_access_unlock(dev);
2803 return mask_supported;
2804}
2805EXPORT_SYMBOL_GPL(pci_intx_mask_supported);
2806
2807static bool pci_check_and_set_intx_mask(struct pci_dev *dev, bool mask)
2808{
2809 struct pci_bus *bus = dev->bus;
2810 bool mask_updated = true;
2811 u32 cmd_status_dword;
2812 u16 origcmd, newcmd;
2813 unsigned long flags;
2814 bool irq_pending;
2815
2816 /*
2817 * We do a single dword read to retrieve both command and status.
2818 * Document assumptions that make this possible.
2819 */
2820 BUILD_BUG_ON(PCI_COMMAND % 4);
2821 BUILD_BUG_ON(PCI_COMMAND + 2 != PCI_STATUS);
2822
2823 raw_spin_lock_irqsave(&pci_lock, flags);
2824
2825 bus->ops->read(bus, dev->devfn, PCI_COMMAND, 4, &cmd_status_dword);
2826
2827 irq_pending = (cmd_status_dword >> 16) & PCI_STATUS_INTERRUPT;
2828
2829 /*
2830 * Check interrupt status register to see whether our device
2831 * triggered the interrupt (when masking) or the next IRQ is
2832 * already pending (when unmasking).
2833 */
2834 if (mask != irq_pending) {
2835 mask_updated = false;
2836 goto done;
2837 }
2838
2839 origcmd = cmd_status_dword;
2840 newcmd = origcmd & ~PCI_COMMAND_INTX_DISABLE;
2841 if (mask)
2842 newcmd |= PCI_COMMAND_INTX_DISABLE;
2843 if (newcmd != origcmd)
2844 bus->ops->write(bus, dev->devfn, PCI_COMMAND, 2, newcmd);
2845
2846done:
2847 raw_spin_unlock_irqrestore(&pci_lock, flags);
2848
2849 return mask_updated;
2850}
2851
2852/**
2853 * pci_check_and_mask_intx - mask INTx on pending interrupt
2854 * @pdev: the PCI device to operate on
2855 *
2856 * Check if the device dev has its INTx line asserted, mask it and
2857 * return true in that case. False is returned if not interrupt was
2858 * pending.
2859 */
2860bool pci_check_and_mask_intx(struct pci_dev *dev)
2861{
2862 return pci_check_and_set_intx_mask(dev, true);
2863}
2864EXPORT_SYMBOL_GPL(pci_check_and_mask_intx);
2865
2866/**
2867 * pci_check_and_mask_intx - unmask INTx of no interrupt is pending
2868 * @pdev: the PCI device to operate on
2869 *
2870 * Check if the device dev has its INTx line asserted, unmask it if not
2871 * and return true. False is returned and the mask remains active if
2872 * there was still an interrupt pending.
2873 */
2874bool pci_check_and_unmask_intx(struct pci_dev *dev)
2875{
2876 return pci_check_and_set_intx_mask(dev, false);
2877}
2878EXPORT_SYMBOL_GPL(pci_check_and_unmask_intx);
2879
2880/**
2771 * pci_msi_off - disables any msi or msix capabilities 2881 * pci_msi_off - disables any msi or msix capabilities
2772 * @dev: the PCI device to operate on 2882 * @dev: the PCI device to operate on
2773 * 2883 *
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index b74084e9ca12..3b6e4ed306b6 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -136,6 +136,8 @@ static inline void pci_remove_legacy_files(struct pci_bus *bus) { return; }
136/* Lock for read/write access to pci device and bus lists */ 136/* Lock for read/write access to pci device and bus lists */
137extern struct rw_semaphore pci_bus_sem; 137extern struct rw_semaphore pci_bus_sem;
138 138
139extern raw_spinlock_t pci_lock;
140
139extern unsigned int pci_pm_d3_delay; 141extern unsigned int pci_pm_d3_delay;
140 142
141#ifdef CONFIG_PCI_MSI 143#ifdef CONFIG_PCI_MSI
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 72401596b2a8..4286b853956e 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -804,6 +804,9 @@ int __must_check pci_set_mwi(struct pci_dev *dev);
804int pci_try_set_mwi(struct pci_dev *dev); 804int pci_try_set_mwi(struct pci_dev *dev);
805void pci_clear_mwi(struct pci_dev *dev); 805void pci_clear_mwi(struct pci_dev *dev);
806void pci_intx(struct pci_dev *dev, int enable); 806void pci_intx(struct pci_dev *dev, int enable);
807bool pci_intx_mask_supported(struct pci_dev *dev);
808bool pci_check_and_mask_intx(struct pci_dev *dev);
809bool pci_check_and_unmask_intx(struct pci_dev *dev);
807void pci_msi_off(struct pci_dev *dev); 810void pci_msi_off(struct pci_dev *dev);
808int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size); 811int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size);
809int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask); 812int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask);