There are various way to do function granularity reset. PCI-e and PCI Advanced Feature have Function Level Reset(FLR) support. Introduce __pci_check_reset_function_cap() to find out which method device supported to reset the function. Signed-off-by: Sheng Yang <sheng@xxxxxxxxxxxxxxx> --- drivers/pci/pci.c | 76 ++++++++++++++++++++++++++++++++--------------------- 1 files changed, 46 insertions(+), 30 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 28af496..2077aee 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1751,6 +1751,22 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) EXPORT_SYMBOL(pci_set_dma_seg_boundary); #endif +#define PCI_RESET_FUNC_CAP_PCIE_FLR 0 +static int __pci_check_reset_function_cap(struct pci_dev *dev) +{ + int cappos; + u32 cap; + + cappos = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (cappos) { + pci_read_config_dword(dev, cappos + PCI_EXP_DEVCAP, &cap); + if (cap & PCI_EXP_DEVCAP_FLR) + return PCI_RESET_FUNC_CAP_PCIE_FLR; + } + + return -ENOTTY; +} + /** * pci_execute_reset_function() - Reset a PCI device function * @dev: Device function to reset @@ -1771,36 +1787,40 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary); int pci_execute_reset_function(struct pci_dev *dev) { u16 status; - u32 cap; - int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); + int cappos; + int reset_cap; + int r; - if (!exppos) - return -ENOTTY; - pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap); - if (!(cap & PCI_EXP_DEVCAP_FLR)) - return -ENOTTY; + reset_cap = __pci_check_reset_function_cap(dev); + if (reset_cap < 0) + return reset_cap; + r = 0; pci_block_user_cfg_access(dev); - /* Wait for Transaction Pending bit clean */ - msleep(100); - pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); - if (status & PCI_EXP_DEVSTA_TRPND) { - dev_info(&dev->dev, "Busy after 100ms while trying to reset; " - "sleeping for 1 second\n"); - ssleep(1); - pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); - if (status & PCI_EXP_DEVSTA_TRPND) - dev_info(&dev->dev, "Still busy after 1s; " - "proceeding with reset anyway\n"); - } - - pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL, + if (reset_cap == PCI_RESET_FUNC_CAP_PCIE_FLR) { + /* Wait for Transaction Pending bit clean */ + msleep(100); + cappos = pci_find_capability(dev, PCI_CAP_ID_EXP); + pci_read_config_word(dev, cappos + PCI_EXP_DEVSTA, &status); + if (status & PCI_EXP_DEVSTA_TRPND) { + dev_info(&dev->dev, "Busy after 100ms while trying to" + " reset; sleeping for 1 second\n"); + ssleep(1); + pci_read_config_word(dev, + cappos + PCI_EXP_DEVSTA, &status); + if (status & PCI_EXP_DEVSTA_TRPND) + dev_info(&dev->dev, "Still busy after 1s; " + "proceeding with reset anyway\n"); + } + pci_write_config_word(dev, cappos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); - mdelay(100); + mdelay(100); + } else + r = -ENOTTY; pci_unblock_user_cfg_access(dev); - return 0; + return r; } EXPORT_SYMBOL_GPL(pci_execute_reset_function); @@ -1822,15 +1842,11 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function); */ int pci_reset_function(struct pci_dev *dev) { - u32 cap; - int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); int r; - if (!exppos) - return -ENOTTY; - pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap); - if (!(cap & PCI_EXP_DEVCAP_FLR)) - return -ENOTTY; + r = __pci_check_reset_function_cap(dev); + if (r < 0) + return r; if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0) disable_irq(dev->irq); -- 1.5.4.5 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html