aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2010-06-17 15:16:36 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2010-07-30 12:29:34 -0400
commitfcd097f31a6ee207cc0c3da9cccd2a86d4334785 (patch)
tree3bd2658926e691d0eab23651e45832c2714d85f6
parentea5f9fc5899660dd26c1ccf3fab183bd041140ee (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>
-rw-r--r--drivers/pci/msi.c34
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)
196void read_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) 196void 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
225void read_msi_msg(unsigned int irq, struct msi_msg *msg) 210void 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)
232void write_msi_msg_desc(struct irq_desc *desc, struct msi_msg *msg) 217void 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;