diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2010-06-17 15:16:36 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-07-30 12:29:34 -0400 |
commit | fcd097f31a6ee207cc0c3da9cccd2a86d4334785 (patch) | |
tree | 3bd2658926e691d0eab23651e45832c2714d85f6 /drivers/pci | |
parent | ea5f9fc5899660dd26c1ccf3fab183bd041140ee (diff) |
PCI: MSI: Remove unsafe and unnecessary hardware access
During suspend on an SMP system, {read,write}_msi_msg_desc() may be
called to mask and unmask interrupts on a device that is already in a
reduced power state. At this point memory-mapped registers including
MSI-X tables are not accessible, and config space may not be fully
functional either.
While a device is in a reduced power state its interrupts are
effectively masked and its MSI(-X) state will be restored when it is
brought back to D0. Therefore these functions can simply read and
write msi_desc::msg for devices not in D0.
Further, read_msi_msg_desc() should only ever be used to update a
previously written message, so it can always read msi_desc::msg
and never needs to touch the hardware.
Tested-by: "Michael Chan" <mchan@broadcom.com>
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/msi.c | 34 |
1 files changed, 11 insertions, 23 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 6a0f2f07f955..4c14f31f2b4d 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -196,30 +196,15 @@ void unmask_msi_irq(unsigned int irq) | |||
196 | void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) | 196 | void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) |
197 | { | 197 | { |
198 | struct msi_desc *entry = get_irq_desc_msi(desc); | 198 | struct msi_desc *entry = get_irq_desc_msi(desc); |
199 | if (entry->msi_attrib.is_msix) { | ||
200 | void __iomem *base = entry->mask_base + | ||
201 | entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; | ||
202 | 199 | ||
203 | msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR); | 200 | /* We do not touch the hardware (which may not even be |
204 | msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR); | 201 | * accessible at the moment) but return the last message |
205 | msg->data = readl(base + PCI_MSIX_ENTRY_DATA); | 202 | * written. Assert that this is valid, assuming that |
206 | } else { | 203 | * valid messages are not all-zeroes. */ |
207 | struct pci_dev *dev = entry->dev; | 204 | BUG_ON(!(entry->msg.address_hi | entry->msg.address_lo | |
208 | int pos = entry->msi_attrib.pos; | 205 | entry->msg.data)); |
209 | u16 data; | ||
210 | 206 | ||
211 | pci_read_config_dword(dev, msi_lower_address_reg(pos), | 207 | *msg = entry->msg; |
212 | &msg->address_lo); | ||
213 | if (entry->msi_attrib.is_64) { | ||
214 | pci_read_config_dword(dev, msi_upper_address_reg(pos), | ||
215 | &msg->address_hi); | ||
216 | pci_read_config_word(dev, msi_data_reg(pos, 1), &data); | ||
217 | } else { | ||
218 | msg->address_hi = 0; | ||
219 | pci_read_config_word(dev, msi_data_reg(pos, 0), &data); | ||
220 | } | ||
221 | msg->data = data; | ||
222 | } | ||
223 | } | 208 | } |
224 | 209 | ||
225 | void read_msi_msg(unsigned int irq, struct msi_msg *msg) | 210 | void read_msi_msg(unsigned int irq, struct msi_msg *msg) |
@@ -232,7 +217,10 @@ void read_msi_msg(unsigned int irq, struct msi_msg *msg) | |||
232 | void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) | 217 | void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) |
233 | { | 218 | { |
234 | struct msi_desc *entry = get_irq_desc_msi(desc); | 219 | struct msi_desc *entry = get_irq_desc_msi(desc); |
235 | if (entry->msi_attrib.is_msix) { | 220 | |
221 | if (entry->dev->current_state != PCI_D0) { | ||
222 | /* Don't touch the hardware now */ | ||
223 | } else if (entry->msi_attrib.is_msix) { | ||
236 | void __iomem *base; | 224 | void __iomem *base; |
237 | base = entry->mask_base + | 225 | base = entry->mask_base + |
238 | entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; | 226 | entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; |