aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorHidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>2009-06-23 23:08:09 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-06-29 15:18:13 -0400
commit12abb8ba8444f7c9b355bbdd44a6d0839f7a41b6 (patch)
treee1dc48f5c1eeb91f4e5146c51f648952aea5c38a /drivers
parent7ba1930db02fc3118165338ef4e562869f575583 (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/msi.c35
1 files changed, 28 insertions, 7 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 9ab4fe8f20af..d986afb7032b 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -127,17 +127,23 @@ static inline __attribute_const__ u32 msi_enabled_mask(u16 control)
127 * reliably as devices without an INTx disable bit will then generate a 127 * reliably as devices without an INTx disable bit will then generate a
128 * level IRQ which will never be cleared. 128 * level IRQ which will never be cleared.
129 */ 129 */
130static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) 130static u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
131{ 131{
132 u32 mask_bits = desc->masked; 132 u32 mask_bits = desc->masked;
133 133
134 if (!desc->msi_attrib.maskbit) 134 if (!desc->msi_attrib.maskbit)
135 return; 135 return 0;
136 136
137 mask_bits &= ~mask; 137 mask_bits &= ~mask;
138 mask_bits |= flag; 138 mask_bits |= flag;
139 pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits); 139 pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits);
140 desc->masked = mask_bits; 140
141 return mask_bits;
142}
143
144static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
145{
146 desc->masked = __msi_mask_irq(desc, mask, flag);
141} 147}
142 148
143/* 149/*
@@ -147,7 +153,7 @@ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
147 * file. This saves a few milliseconds when initialising devices with lots 153 * file. This saves a few milliseconds when initialising devices with lots
148 * of MSI-X interrupts. 154 * of MSI-X interrupts.
149 */ 155 */
150static void msix_mask_irq(struct msi_desc *desc, u32 flag) 156static u32 __msix_mask_irq(struct msi_desc *desc, u32 flag)
151{ 157{
152 u32 mask_bits = desc->masked; 158 u32 mask_bits = desc->masked;
153 unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + 159 unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
@@ -155,7 +161,13 @@ static void msix_mask_irq(struct msi_desc *desc, u32 flag)
155 mask_bits &= ~1; 161 mask_bits &= ~1;
156 mask_bits |= flag; 162 mask_bits |= flag;
157 writel(mask_bits, desc->mask_base + offset); 163 writel(mask_bits, desc->mask_base + offset);
158 desc->masked = mask_bits; 164
165 return mask_bits;
166}
167
168static void msix_mask_irq(struct msi_desc *desc, u32 flag)
169{
170 desc->masked = __msix_mask_irq(desc, flag);
159} 171}
160 172
161static void msi_set_mask_bit(unsigned irq, u32 flag) 173static void msi_set_mask_bit(unsigned irq, u32 flag)
@@ -616,9 +628,11 @@ void pci_msi_shutdown(struct pci_dev *dev)
616 pci_intx_for_msi(dev, 1); 628 pci_intx_for_msi(dev, 1);
617 dev->msi_enabled = 0; 629 dev->msi_enabled = 0;
618 630
631 /* Return the device with MSI unmasked as initial states */
619 pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &ctrl); 632 pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &ctrl);
620 mask = msi_capable_mask(ctrl); 633 mask = msi_capable_mask(ctrl);
621 msi_mask_irq(desc, mask, ~mask); 634 /* Keep cached state to be restored */
635 __msi_mask_irq(desc, mask, ~mask);
622 636
623 /* Restore dev->irq to its default pin-assertion irq */ 637 /* Restore dev->irq to its default pin-assertion irq */
624 dev->irq = desc->msi_attrib.default_irq; 638 dev->irq = desc->msi_attrib.default_irq;
@@ -658,7 +672,6 @@ static int msi_free_irqs(struct pci_dev* dev)
658 672
659 list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { 673 list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
660 if (entry->msi_attrib.is_msix) { 674 if (entry->msi_attrib.is_msix) {
661 msix_mask_irq(entry, 1);
662 if (list_is_last(&entry->list, &dev->msi_list)) 675 if (list_is_last(&entry->list, &dev->msi_list))
663 iounmap(entry->mask_base); 676 iounmap(entry->mask_base);
664 } 677 }
@@ -746,9 +759,17 @@ static void msix_free_all_irqs(struct pci_dev *dev)
746 759
747void pci_msix_shutdown(struct pci_dev* dev) 760void pci_msix_shutdown(struct pci_dev* dev)
748{ 761{
762 struct msi_desc *entry;
763
749 if (!pci_msi_enable || !dev || !dev->msix_enabled) 764 if (!pci_msi_enable || !dev || !dev->msix_enabled)
750 return; 765 return;
751 766
767 /* Return the device with MSI-X masked as initial states */
768 list_for_each_entry(entry, &dev->msi_list, list) {
769 /* Keep cached states to be restored */
770 __msix_mask_irq(entry, 1);
771 }
772
752 msix_set_enable(dev, 0); 773 msix_set_enable(dev, 0);
753 pci_intx_for_msi(dev, 1); 774 pci_intx_for_msi(dev, 1);
754 dev->msix_enabled = 0; 775 dev->msix_enabled = 0;