diff options
author | Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com> | 2009-06-23 23:08:09 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-06-29 15:18:13 -0400 |
commit | 12abb8ba8444f7c9b355bbdd44a6d0839f7a41b6 (patch) | |
tree | e1dc48f5c1eeb91f4e5146c51f648952aea5c38a /drivers/pci/msi.c | |
parent | 7ba1930db02fc3118165338ef4e562869f575583 (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/pci/msi.c')
-rw-r--r-- | drivers/pci/msi.c | 35 |
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 | */ |
130 | static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) | 130 | static 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 | |||
144 | static 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 | */ |
150 | static void msix_mask_irq(struct msi_desc *desc, u32 flag) | 156 | static 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 | |||
168 | static void msix_mask_irq(struct msi_desc *desc, u32 flag) | ||
169 | { | ||
170 | desc->masked = __msix_mask_irq(desc, flag); | ||
159 | } | 171 | } |
160 | 172 | ||
161 | static void msi_set_mask_bit(unsigned irq, u32 flag) | 173 | static 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 | ||
747 | void pci_msix_shutdown(struct pci_dev* dev) | 760 | void 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; |