diff options
-rw-r--r-- | drivers/pci/access.c | 2 | ||||
-rw-r--r-- | drivers/pci/pci.c | 110 | ||||
-rw-r--r-- | drivers/pci/pci.h | 2 | ||||
-rw-r--r-- | include/linux/pci.h | 3 |
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 | ||
16 | static DEFINE_RAW_SPINLOCK(pci_lock); | 16 | DEFINE_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 | */ | ||
2777 | bool 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 | } | ||
2805 | EXPORT_SYMBOL_GPL(pci_intx_mask_supported); | ||
2806 | |||
2807 | static 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 | |||
2846 | done: | ||
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 | */ | ||
2860 | bool pci_check_and_mask_intx(struct pci_dev *dev) | ||
2861 | { | ||
2862 | return pci_check_and_set_intx_mask(dev, true); | ||
2863 | } | ||
2864 | EXPORT_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 | */ | ||
2874 | bool pci_check_and_unmask_intx(struct pci_dev *dev) | ||
2875 | { | ||
2876 | return pci_check_and_set_intx_mask(dev, false); | ||
2877 | } | ||
2878 | EXPORT_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 */ |
137 | extern struct rw_semaphore pci_bus_sem; | 137 | extern struct rw_semaphore pci_bus_sem; |
138 | 138 | ||
139 | extern raw_spinlock_t pci_lock; | ||
140 | |||
139 | extern unsigned int pci_pm_d3_delay; | 141 | extern 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); | |||
804 | int pci_try_set_mwi(struct pci_dev *dev); | 804 | int pci_try_set_mwi(struct pci_dev *dev); |
805 | void pci_clear_mwi(struct pci_dev *dev); | 805 | void pci_clear_mwi(struct pci_dev *dev); |
806 | void pci_intx(struct pci_dev *dev, int enable); | 806 | void pci_intx(struct pci_dev *dev, int enable); |
807 | bool pci_intx_mask_supported(struct pci_dev *dev); | ||
808 | bool pci_check_and_mask_intx(struct pci_dev *dev); | ||
809 | bool pci_check_and_unmask_intx(struct pci_dev *dev); | ||
807 | void pci_msi_off(struct pci_dev *dev); | 810 | void pci_msi_off(struct pci_dev *dev); |
808 | int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size); | 811 | int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size); |
809 | int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask); | 812 | int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask); |