diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-03-30 23:21:28 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-03-30 23:21:28 -0800 |
commit | 7446377126dc0e69c4d60dd3edf557efdd150337 (patch) | |
tree | 9299f91597ae116a6ffb52fee657d92d48d353a6 /pci | |
parent | b2dfc44b83efab534b5a72a9ee8afe628b1a4f43 (diff) | |
download | patches-7446377126dc0e69c4d60dd3edf557efdd150337.tar.gz |
add msi save patch back
Diffstat (limited to 'pci')
-rw-r--r-- | pci/msi-save-restore-for-suspend-resume.patch | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/pci/msi-save-restore-for-suspend-resume.patch b/pci/msi-save-restore-for-suspend-resume.patch new file mode 100644 index 0000000000000..7e8a36c7f5f40 --- /dev/null +++ b/pci/msi-save-restore-for-suspend-resume.patch @@ -0,0 +1,372 @@ +From owner-linux-pci@atrey.karlin.mff.cuni.cz Wed Feb 8 01:12:18 2006 +Subject: PCI: MSI(X) save/restore for suspend/resume +From: Shaohua Li <shaohua.li@intel.com> +Cc: Andrew Morton <akpm@osdl.org>, Greg <greg@kroah.com> +Date: Wed, 08 Feb 2006 17:11:38 +0800 +Message-Id: <1139389898.14976.11.camel@sli10-desk.sh.intel.com> + +Add MSI(X) configure sapce save/restore in generic PCI helper. + +Signed-off-by: Shaohua Li <shaohua.li@intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/pci/msi.c | 227 +++++++++++++++++++++++++++++++++++++++++++++------- + drivers/pci/pci.c | 6 + + drivers/pci/pci.h | 11 ++ + include/linux/pci.h | 31 +++++++ + 4 files changed, 246 insertions(+), 29 deletions(-) + +--- gregkh-2.6.orig/drivers/pci/msi.c ++++ gregkh-2.6/drivers/pci/msi.c +@@ -504,6 +504,201 @@ void pci_scan_msi_device(struct pci_dev + nr_reserved_vectors++; + } + ++#ifdef CONFIG_PM ++int pci_save_msi_state(struct pci_dev *dev) ++{ ++ 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; ++ ++ pci_read_config_word(dev, msi_control_reg(pos), &control); ++ if (!(control & PCI_MSI_FLAGS_ENABLE)) ++ return 0; ++ ++ 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++]); ++ disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); ++ 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 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 (!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; ++ ++ pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); ++ if (pos <= 0 || dev->no_msi) ++ return 0; ++ ++ 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; ++ ++ disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); ++ 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) ++{ ++ u16 save; ++ int pos; ++ int vector, head, tail = 0; ++ void __iomem *base; ++ int j; ++ struct msg_address address; ++ struct msg_data data; ++ struct msi_desc *entry; ++ int temp; ++ struct pci_cap_saved_state *save_state; ++ ++ save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX); ++ if (!save_state) ++ return; ++ 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; ++ ++ /* route the table */ ++ temp = dev->irq; ++ if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) ++ return; ++ vector = head = dev->irq; ++ while (head != tail) { ++ entry = msi_desc[vector]; ++ base = entry->mask_base; ++ j = entry->msi_attrib.entry_nr; ++ ++ msi_address_init(&address); ++ msi_data_init(&data, vector); ++ ++ address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; ++ address.lo_address.value |= entry->msi_attrib.current_cpu << ++ MSI_TARGET_CPU_SHIFT; ++ ++ writel(address.lo_address.value, ++ base + j * PCI_MSIX_ENTRY_SIZE + ++ PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); ++ writel(address.hi_address, ++ base + j * PCI_MSIX_ENTRY_SIZE + ++ PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); ++ writel(*(u32*)&data, ++ base + j * PCI_MSIX_ENTRY_SIZE + ++ PCI_MSIX_ENTRY_DATA_OFFSET); ++ ++ tail = msi_desc[vector]->link.tail; ++ vector = tail; ++ } ++ dev->irq = temp; ++ ++ pci_write_config_word(dev, msi_control_reg(pos), save); ++ enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); ++} ++#endif ++ ++static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) ++{ ++ struct msg_address address; ++ struct msg_data data; ++ int pos, vector = dev->irq; ++ u16 control; ++ ++ pos = pci_find_capability(dev, PCI_CAP_ID_MSI); ++ pci_read_config_word(dev, msi_control_reg(pos), &control); ++ /* Configure MSI capability structure */ ++ msi_address_init(&address); ++ msi_data_init(&data, vector); ++ entry->msi_attrib.current_cpu = ((address.lo_address.u.dest_id >> ++ MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); ++ pci_write_config_dword(dev, msi_lower_address_reg(pos), ++ address.lo_address.value); ++ if (is_64bit_address(control)) { ++ pci_write_config_dword(dev, ++ msi_upper_address_reg(pos), address.hi_address); ++ pci_write_config_word(dev, ++ msi_data_reg(pos, 1), *((u32*)&data)); ++ } else ++ pci_write_config_word(dev, ++ msi_data_reg(pos, 0), *((u32*)&data)); ++ if (entry->msi_attrib.maskbit) { ++ unsigned int maskbits, temp; ++ /* All MSIs are unmasked by default, Mask them all */ ++ pci_read_config_dword(dev, ++ msi_mask_bits_reg(pos, is_64bit_address(control)), ++ &maskbits); ++ temp = (1 << multi_msi_capable(control)); ++ temp = ((temp - 1) & ~temp); ++ maskbits |= temp; ++ pci_write_config_dword(dev, ++ msi_mask_bits_reg(pos, is_64bit_address(control)), ++ maskbits); ++ } ++} ++ + /** + * msi_capability_init - configure device's MSI capability structure + * @dev: pointer to the pci_dev data structure of MSI device function +@@ -516,8 +711,6 @@ void pci_scan_msi_device(struct pci_dev + static int msi_capability_init(struct pci_dev *dev) + { + struct msi_desc *entry; +- struct msg_address address; +- struct msg_data data; + int pos, vector; + u16 control; + +@@ -549,33 +742,8 @@ static int msi_capability_init(struct pc + /* Replace with MSI handler */ + irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); + /* Configure MSI capability structure */ +- msi_address_init(&address); +- msi_data_init(&data, vector); +- entry->msi_attrib.current_cpu = ((address.lo_address.u.dest_id >> +- MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); +- pci_write_config_dword(dev, msi_lower_address_reg(pos), +- address.lo_address.value); +- if (is_64bit_address(control)) { +- pci_write_config_dword(dev, +- msi_upper_address_reg(pos), address.hi_address); +- pci_write_config_word(dev, +- msi_data_reg(pos, 1), *((u32*)&data)); +- } else +- pci_write_config_word(dev, +- msi_data_reg(pos, 0), *((u32*)&data)); +- if (entry->msi_attrib.maskbit) { +- unsigned int maskbits, temp; +- /* All MSIs are unmasked by default, Mask them all */ +- pci_read_config_dword(dev, +- msi_mask_bits_reg(pos, is_64bit_address(control)), +- &maskbits); +- temp = (1 << multi_msi_capable(control)); +- temp = ((temp - 1) & ~temp); +- maskbits |= temp; +- pci_write_config_dword(dev, +- msi_mask_bits_reg(pos, is_64bit_address(control)), +- maskbits); +- } ++ msi_register_init(dev, entry); ++ + attach_msi_entry(entry, vector); + /* Set MSI enabled bits */ + enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); +@@ -731,6 +899,7 @@ int pci_enable_msi(struct pci_dev* dev) + vector_irq[dev->irq] = -1; + nr_released_vectors--; + spin_unlock_irqrestore(&msi_lock, flags); ++ msi_register_init(dev, msi_desc[dev->irq]); + enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + return 0; + } +--- gregkh-2.6.orig/drivers/pci/pci.c ++++ gregkh-2.6/drivers/pci/pci.c +@@ -444,6 +444,10 @@ pci_save_state(struct pci_dev *dev) + /* XXX: 100% dword access ok here? */ + for (i = 0; i < 16; i++) + pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]); ++ if ((i = pci_save_msi_state(dev)) != 0) ++ return i; ++ if ((i = pci_save_msix_state(dev)) != 0) ++ return i; + return 0; + } + +@@ -458,6 +462,8 @@ pci_restore_state(struct pci_dev *dev) + + for (i = 0; i < 16; i++) + pci_write_config_dword(dev,i * 4, dev->saved_config_space[i]); ++ pci_restore_msi_state(dev); ++ pci_restore_msix_state(dev); + return 0; + } + +--- gregkh-2.6.orig/drivers/pci/pci.h ++++ gregkh-2.6/drivers/pci/pci.h +@@ -55,6 +55,17 @@ void pci_no_msi(void); + static inline void disable_msi_mode(struct pci_dev *dev, int pos, int type) { } + static inline void pci_no_msi(void) { } + #endif ++#if defined(CONFIG_PCI_MSI) && defined(CONFIG_PM) ++int pci_save_msi_state(struct pci_dev *dev); ++int pci_save_msix_state(struct pci_dev *dev); ++void pci_restore_msi_state(struct pci_dev *dev); ++void pci_restore_msix_state(struct pci_dev *dev); ++#else ++static inline int pci_save_msi_state(struct pci_dev *dev) { return 0; } ++static inline int pci_save_msix_state(struct pci_dev *dev) { return 0; } ++static inline void pci_restore_msi_state(struct pci_dev *dev) {} ++static inline void pci_restore_msix_state(struct pci_dev *dev) {} ++#endif + + extern int pcie_mch_quirk; + extern struct device_attribute pci_dev_attrs[]; +--- gregkh-2.6.orig/include/linux/pci.h ++++ gregkh-2.6/include/linux/pci.h +@@ -100,6 +100,12 @@ enum pci_bus_flags { + PCI_BUS_FLAGS_NO_MSI = (pci_bus_flags_t) 1, + }; + ++struct pci_cap_saved_state { ++ struct hlist_node next; ++ char cap_nr; ++ u32 data[0]; ++}; ++ + /* + * The pci_dev structure is used to describe PCI devices. + */ +@@ -159,6 +165,7 @@ struct pci_dev { + unsigned int block_ucfg_access:1; /* userspace config space access is blocked */ + + u32 saved_config_space[16]; /* config space saved at suspend time */ ++ struct hlist_head saved_cap_space; + struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */ + int rom_attr_enabled; /* has display of the rom attribute been enabled? */ + struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ +@@ -169,6 +176,30 @@ struct pci_dev { + #define to_pci_dev(n) container_of(n, struct pci_dev, dev) + #define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) + ++static inline struct pci_cap_saved_state *pci_find_saved_cap( ++ struct pci_dev *pci_dev,char cap) ++{ ++ struct pci_cap_saved_state *tmp; ++ struct hlist_node *pos; ++ ++ hlist_for_each_entry(tmp, pos, &pci_dev->saved_cap_space, next) { ++ if (tmp->cap_nr == cap) ++ return tmp; ++ } ++ return NULL; ++} ++ ++static inline void pci_add_saved_cap(struct pci_dev *pci_dev, ++ struct pci_cap_saved_state *new_cap) ++{ ++ hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space); ++} ++ ++static inline void pci_remove_saved_cap(struct pci_cap_saved_state *cap) ++{ ++ hlist_del(&cap->next); ++} ++ + /* + * For PCI devices, the region numbers are assigned this way: + * |