Some PCI device implemented PCI Advanced Feature, which is also support Function Level Reset(FLR). Signed-off-by: Sheng Yang <sheng@xxxxxxxxxxxxxxx> --- drivers/pci/pci.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ad89c74..36421ee 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1752,6 +1752,7 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary); #endif #define PCI_RESET_FUNC_CAP_PCIE_FLR 0 +#define PCI_RESET_FUNC_CAP_PCI_AF_FLR 1 static int __pci_check_reset_function_cap(struct pci_dev *dev) { int cappos; @@ -1764,6 +1765,13 @@ static int __pci_check_reset_function_cap(struct pci_dev *dev) return PCI_RESET_FUNC_CAP_PCIE_FLR; } + cappos = pci_find_capability(dev, PCI_CAP_ID_AF); + if (cappos) { + pci_read_config_byte(dev, cappos + PCI_AF_CAP, (u8 *)&cap); + if ((cap & PCI_AF_CAP_TP) && (cap & PCI_AF_CAP_FLR)) + return PCI_RESET_FUNC_CAP_PCI_AF_FLR; + } + return -ENOTTY; } @@ -1798,6 +1806,36 @@ static int __pcie_flr(struct pci_dev *dev) return 0; } +static int __pci_af_flr(struct pci_dev *dev) +{ + int cappos = pci_find_capability(dev, PCI_CAP_ID_AF); + u16 status; + u32 cap; + + if (!cappos) + return -ENOTTY; + pci_read_config_byte(dev, cappos + PCI_AF_CAP, (u8 *)&cap); + if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR)) + return -ENOTTY; + + /* Wait for Transaction Pending bit clean */ + msleep(100); + pci_read_config_byte(dev, cappos + PCI_AF_STATUS, (u8 *)&status); + if (status & PCI_AF_STATUS_TP) { + dev_info(&dev->dev, "Busy after 100ms while trying to" + " reset; sleeping for 1 second\n"); + ssleep(1); + pci_read_config_byte(dev, + cappos + PCI_AF_STATUS, (u8 *)&status); + if (status & PCI_AF_STATUS_TP) + dev_info(&dev->dev, "Still busy after 1s; " + "proceeding with reset anyway\n"); + } + pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); + mdelay(100); + return 0; +} + /** * pci_execute_reset_function() - Reset a PCI device function * @dev: Device function to reset @@ -1827,10 +1865,17 @@ int pci_execute_reset_function(struct pci_dev *dev) r = 0; pci_block_user_cfg_access(dev); - if (reset_cap == PCI_RESET_FUNC_CAP_PCIE_FLR) + switch (reset_cap) { + case PCI_RESET_FUNC_CAP_PCIE_FLR: r = __pcie_flr(dev); - else + break; + case PCI_RESET_FUNC_CAP_PCI_AF_FLR: + r = __pci_af_flr(dev); + break; + default: r = -ENOTTY; + break; + } pci_unblock_user_cfg_access(dev); return r; -- 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