[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [linux-2.6.18-xen] Save/restore PCI MSI state across S3.
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1227536158 0 # Node ID 1b68d09b868fdcd724c6b86eb2790748a9fd0bb7 # Parent 163a3807cb1fb4f35304a99c63f4deac322df2da Save/restore PCI MSI state across S3. Signed-off-by: Haitao Shan <haitao.shan@xxxxxxxxx> --- drivers/pci/msi-xen.c | 177 ++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 128 insertions(+), 49 deletions(-) diff -r 163a3807cb1f -r 1b68d09b868f drivers/pci/msi-xen.c --- a/drivers/pci/msi-xen.c Mon Nov 24 11:13:20 2008 +0000 +++ b/drivers/pci/msi-xen.c Mon Nov 24 14:15:58 2008 +0000 @@ -48,6 +48,13 @@ struct msi_pirq_entry { struct list_head list; int pirq; int entry_nr; +#ifdef CONFIG_PM + /* PM save area for MSIX address/data */ + void __iomem *mask_base; + u32 address_hi_save; + u32 address_lo_save; + u32 data_save; +#endif }; static struct msi_dev_list *get_msi_dev_pirq_list(struct pci_dev *dev) @@ -83,7 +90,7 @@ static struct msi_dev_list *get_msi_dev_ return ret; } -static int attach_pirq_entry(int pirq, int entry_nr, +static int attach_pirq_entry(int pirq, int entry_nr, u64 table_base, struct msi_dev_list *msi_dev_entry) { struct msi_pirq_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC); @@ -93,6 +100,9 @@ static int attach_pirq_entry(int pirq, i return -ENOMEM; entry->pirq = pirq; entry->entry_nr = entry_nr; +#ifdef COMFIG_PM + entry->mask_base = table_base; +#endif spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); list_add_tail(&entry->list, &msi_dev_entry->pirq_list_head); spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); @@ -299,104 +309,173 @@ static void enable_msi_mode(struct pci_d #ifdef CONFIG_PM int pci_save_msi_state(struct pci_dev *dev) { - int pos; + int pos, i = 0; + u16 control; + struct pci_cap_saved_state *save_state; + u32 *cap; pos = pci_find_capability(dev, PCI_CAP_ID_MSI); if (pos <= 0 || dev->no_msi) return 0; - if (!dev->msi_enabled) + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (!(control & PCI_MSI_FLAGS_ENABLE)) return 0; - /* Restore dev->irq to its default pin-assertion vector */ - msi_unmap_pirq(dev, dev->irq); - /* Disable MSI mode */ - disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); - /* Set the flags for use of restore */ - dev->msi_enabled = 1; + save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u32) * 5, + GFP_KERNEL); + if (!save_state) { + printk(KERN_ERR "Out of memory in pci_save_msi_state\n"); + return -ENOMEM; + } + cap = &save_state->data[0]; + + pci_read_config_dword(dev, pos, &cap[i++]); + control = cap[0] >> 16; + pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, &cap[i++]); + if (control & PCI_MSI_FLAGS_64BIT) { + pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, &cap[i++]); + pci_read_config_dword(dev, pos + PCI_MSI_DATA_64, &cap[i++]); + } else + pci_read_config_dword(dev, pos + PCI_MSI_DATA_32, &cap[i++]); + if (control & PCI_MSI_FLAGS_MASKBIT) + pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT, &cap[i++]); + save_state->cap_nr = PCI_CAP_ID_MSI; + pci_add_saved_cap(dev, save_state); return 0; } void pci_restore_msi_state(struct pci_dev *dev) { - int pos, pirq; - + int i = 0, pos; + u16 control; + struct pci_cap_saved_state *save_state; + u32 *cap; + + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSI); pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (pos <= 0) - return; - - if (!dev->msi_enabled) - return; - - pirq = msi_map_pirq_to_vector(dev, dev->irq, 0, 0); - if (pirq < 0) - return; + if (!save_state || pos <= 0) + return; + cap = &save_state->data[0]; + + control = cap[i++] >> 16; + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, cap[i++]); + if (control & PCI_MSI_FLAGS_64BIT) { + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, cap[i++]); + pci_write_config_dword(dev, pos + PCI_MSI_DATA_64, cap[i++]); + } else + pci_write_config_dword(dev, pos + PCI_MSI_DATA_32, cap[i++]); + if (control & PCI_MSI_FLAGS_MASKBIT) + pci_write_config_dword(dev, pos + PCI_MSI_MASK_BIT, cap[i++]); + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + pci_remove_saved_cap(save_state); + kfree(save_state); } int pci_save_msix_state(struct pci_dev *dev) { int pos; + u16 control; + struct pci_cap_saved_state *save_state; unsigned long flags; struct msi_dev_list *msi_dev_entry; - struct msi_pirq_entry *pirq_entry, *tmp; + struct msi_pirq_entry *pirq_entry; pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); if (pos <= 0 || dev->no_msi) return 0; + printk(KERN_CRIT "Saving MSIX cap\n"); + /* save the capability */ - if (!dev->msix_enabled) + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (!(control & PCI_MSIX_FLAGS_ENABLE)) return 0; + save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u16), + GFP_KERNEL); + if (!save_state) { + printk(KERN_ERR "Out of memory in pci_save_msix_state\n"); + return -ENOMEM; + } + *((u16 *)&save_state->data[0]) = control; msi_dev_entry = get_msi_dev_pirq_list(dev); spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); - list_for_each_entry_safe(pirq_entry, tmp, - &msi_dev_entry->pirq_list_head, list) - msi_unmap_pirq(dev, pirq_entry->pirq); + list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) { + int j; + void __iomem *base; + + /* save the table */ + base = pirq_entry->mask_base; + j = pirq_entry->entry_nr; + printk(KERN_CRIT "Save msix table entry %d pirq %x base %p\n", + j, pirq_entry->pirq, base); + + pirq_entry->address_lo_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + pirq_entry->address_hi_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + pirq_entry->data_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + } spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); - disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); - /* Set the flags for use of restore */ - dev->msix_enabled = 1; - + save_state->cap_nr = PCI_CAP_ID_MSIX; + pci_add_saved_cap(dev, save_state); return 0; } void pci_restore_msix_state(struct pci_dev *dev) { - int pos; + u16 save; + int pos, j; + void __iomem *base; + struct pci_cap_saved_state *save_state; unsigned long flags; - u64 table_base; struct msi_dev_list *msi_dev_entry; - struct msi_pirq_entry *pirq_entry, *tmp; + struct msi_pirq_entry *pirq_entry; + + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX); + if (!save_state) + return; + printk(KERN_CRIT "Restoring MSIX cap\n"); + + save = *((u16 *)&save_state->data[0]); + pci_remove_saved_cap(save_state); + kfree(save_state); pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); if (pos <= 0) return; - if (!dev->msix_enabled) - return; - msi_dev_entry = get_msi_dev_pirq_list(dev); - table_base = find_table_base(dev, pos); - if (!table_base) - return; spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); - list_for_each_entry_safe(pirq_entry, tmp, - &msi_dev_entry->pirq_list_head, list) { - int rc = msi_map_pirq_to_vector(dev, pirq_entry->pirq, - pirq_entry->entry_nr, table_base); - if (rc < 0) - printk(KERN_WARNING - "%s: re-mapping irq #%d (pirq%d) failed: %d\n", - pci_name(dev), pirq_entry->entry_nr, - pirq_entry->pirq, rc); + list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) { + /* route the table */ + base = pirq_entry->mask_base; + j = pirq_entry->entry_nr; + + printk(KERN_CRIT "Restore msix table entry %d pirq %x base %p\n", + j, pirq_entry->pirq, base); + writel(pirq_entry->address_lo_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + writel(pirq_entry->address_hi_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + writel(pirq_entry->data_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); } spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + pci_write_config_word(dev, msi_control_reg(pos), save); enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); } #endif @@ -475,7 +554,7 @@ static int msix_capability_init(struct p pirq = msi_map_vector(dev, entries[i].entry, table_base); if (pirq < 0) break; - attach_pirq_entry(pirq, entries[i].entry, msi_dev_entry); + attach_pirq_entry(pirq, entries[i].entry, table_base, msi_dev_entry); (entries + i)->vector = pirq; } @@ -660,7 +739,7 @@ int pci_enable_msix(struct pci_dev* dev, if (mapped) continue; irq = evtchn_map_pirq(-1, entries[i].vector); - attach_pirq_entry(irq, entries[i].entry, msi_dev_entry); + attach_pirq_entry(irq, entries[i].entry, 0, msi_dev_entry); entries[i].vector = irq; } return 0; _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |