Commit 8c1c699f authored by Yu Zhao's avatar Yu Zhao Committed by Jesse Barnes

PCI: cleanup Function Level Reset

This patch enhances the FLR functions:
  1) remove disable_irq() so the shared IRQ won't be disabled.
  2) replace the 1s wait with 100, 200 and 400ms wait intervals
     for the Pending Transaction.
  3) replace mdelay() with msleep().
  4) add might_sleep().
  5) lock the device to prevent PM suspend from accessing the CSRs
     during the reset.
  6) coding style fixes.
Reviewed-by: default avatarKenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: default avatarYu Zhao <yu.zhao@intel.com>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent c465def6
...@@ -110,7 +110,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) ...@@ -110,7 +110,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset)
} }
if (reset) if (reset)
pci_execute_reset_function(virtfn); __pci_reset_function(virtfn);
pci_device_add(virtfn, virtfn->bus); pci_device_add(virtfn, virtfn->bus);
mutex_unlock(&iov->dev->sriov->lock); mutex_unlock(&iov->dev->sriov->lock);
...@@ -164,7 +164,7 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) ...@@ -164,7 +164,7 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
if (reset) { if (reset) {
device_release_driver(&virtfn->dev); device_release_driver(&virtfn->dev);
pci_execute_reset_function(virtfn); __pci_reset_function(virtfn);
} }
sprintf(buf, "virtfn%u", id); sprintf(buf, "virtfn%u", id);
......
...@@ -2055,111 +2055,112 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) ...@@ -2055,111 +2055,112 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask)
EXPORT_SYMBOL(pci_set_dma_seg_boundary); EXPORT_SYMBOL(pci_set_dma_seg_boundary);
#endif #endif
static int __pcie_flr(struct pci_dev *dev, int probe) static int pcie_flr(struct pci_dev *dev, int probe)
{ {
u16 status; int i;
int pos;
u32 cap; u32 cap;
int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); u16 status;
if (!exppos) pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
if (!pos)
return -ENOTTY; return -ENOTTY;
pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap);
pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP, &cap);
if (!(cap & PCI_EXP_DEVCAP_FLR)) if (!(cap & PCI_EXP_DEVCAP_FLR))
return -ENOTTY; return -ENOTTY;
if (probe) if (probe)
return 0; return 0;
pci_block_user_cfg_access(dev);
/* Wait for Transaction Pending bit clean */ /* Wait for Transaction Pending bit clean */
pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); for (i = 0; i < 4; i++) {
if (!(status & PCI_EXP_DEVSTA_TRPND)) if (i)
goto transaction_done; msleep((1 << (i - 1)) * 100);
msleep(100); pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); if (!(status & PCI_EXP_DEVSTA_TRPND))
if (!(status & PCI_EXP_DEVSTA_TRPND)) goto clear;
goto transaction_done; }
dev_info(&dev->dev, "Busy after 100ms while trying to reset; " dev_err(&dev->dev, "transaction is not cleared; "
"sleeping for 1 second\n"); "proceeding with reset anyway\n");
ssleep(1);
pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); clear:
if (status & PCI_EXP_DEVSTA_TRPND) pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
dev_info(&dev->dev, "Still busy after 1s; "
"proceeding with reset anyway\n");
transaction_done:
pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_BCR_FLR); PCI_EXP_DEVCTL_BCR_FLR);
mdelay(100); msleep(100);
pci_unblock_user_cfg_access(dev);
return 0; return 0;
} }
static int __pci_af_flr(struct pci_dev *dev, int probe) static int pci_af_flr(struct pci_dev *dev, int probe)
{ {
int cappos = pci_find_capability(dev, PCI_CAP_ID_AF); int i;
u8 status; int pos;
u8 cap; u8 cap;
u8 status;
if (!cappos) pos = pci_find_capability(dev, PCI_CAP_ID_AF);
if (!pos)
return -ENOTTY; return -ENOTTY;
pci_read_config_byte(dev, cappos + PCI_AF_CAP, &cap);
pci_read_config_byte(dev, pos + PCI_AF_CAP, &cap);
if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR)) if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR))
return -ENOTTY; return -ENOTTY;
if (probe) if (probe)
return 0; return 0;
pci_block_user_cfg_access(dev);
/* Wait for Transaction Pending bit clean */ /* Wait for Transaction Pending bit clean */
pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status); for (i = 0; i < 4; i++) {
if (!(status & PCI_AF_STATUS_TP)) if (i)
goto transaction_done; msleep((1 << (i - 1)) * 100);
pci_read_config_byte(dev, pos + PCI_AF_STATUS, &status);
if (!(status & PCI_AF_STATUS_TP))
goto clear;
}
dev_err(&dev->dev, "transaction is not cleared; "
"proceeding with reset anyway\n");
clear:
pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
msleep(100); msleep(100);
pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status);
if (!(status & PCI_AF_STATUS_TP))
goto transaction_done;
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, &status);
if (status & PCI_AF_STATUS_TP)
dev_info(&dev->dev, "Still busy after 1s; "
"proceeding with reset anyway\n");
transaction_done:
pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
mdelay(100);
pci_unblock_user_cfg_access(dev);
return 0; return 0;
} }
static int __pci_reset_function(struct pci_dev *pdev, int probe) static int pci_dev_reset(struct pci_dev *dev, int probe)
{ {
int res; int rc;
might_sleep();
if (!probe) {
pci_block_user_cfg_access(dev);
/* block PM suspend, driver probe, etc. */
down(&dev->dev.sem);
}
res = __pcie_flr(pdev, probe); rc = pcie_flr(dev, probe);
if (res != -ENOTTY) if (rc != -ENOTTY)
return res; goto done;
res = __pci_af_flr(pdev, probe); rc = pci_af_flr(dev, probe);
if (res != -ENOTTY) done:
return res; if (!probe) {
up(&dev->dev.sem);
pci_unblock_user_cfg_access(dev);
}
return res; return rc;
} }
/** /**
* pci_execute_reset_function() - Reset a PCI device function * __pci_reset_function - reset a PCI device function
* @dev: Device function to reset * @dev: PCI device to reset
* *
* Some devices allow an individual function to be reset without affecting * Some devices allow an individual function to be reset without affecting
* other functions in the same device. The PCI device must be responsive * other functions in the same device. The PCI device must be responsive
...@@ -2171,18 +2172,18 @@ static int __pci_reset_function(struct pci_dev *pdev, int probe) ...@@ -2171,18 +2172,18 @@ static int __pci_reset_function(struct pci_dev *pdev, int probe)
* device including MSI, bus mastering, BARs, decoding IO and memory spaces, * device including MSI, bus mastering, BARs, decoding IO and memory spaces,
* etc. * etc.
* *
* Returns 0 if the device function was successfully reset or -ENOTTY if the * Returns 0 if the device function was successfully reset or negative if the
* device doesn't support resetting a single function. * device doesn't support resetting a single function.
*/ */
int pci_execute_reset_function(struct pci_dev *dev) int __pci_reset_function(struct pci_dev *dev)
{ {
return __pci_reset_function(dev, 0); return pci_dev_reset(dev, 0);
} }
EXPORT_SYMBOL_GPL(pci_execute_reset_function); EXPORT_SYMBOL_GPL(__pci_reset_function);
/** /**
* pci_reset_function() - quiesce and reset a PCI device function * pci_reset_function - quiesce and reset a PCI device function
* @dev: Device function to reset * @dev: PCI device to reset
* *
* Some devices allow an individual function to be reset without affecting * Some devices allow an individual function to be reset without affecting
* other functions in the same device. The PCI device must be responsive * other functions in the same device. The PCI device must be responsive
...@@ -2190,32 +2191,33 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function); ...@@ -2190,32 +2191,33 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function);
* *
* This function does not just reset the PCI portion of a device, but * This function does not just reset the PCI portion of a device, but
* clears all the state associated with the device. This function differs * clears all the state associated with the device. This function differs
* from pci_execute_reset_function in that it saves and restores device state * from __pci_reset_function in that it saves and restores device state
* over the reset. * over the reset.
* *
* Returns 0 if the device function was successfully reset or -ENOTTY if the * Returns 0 if the device function was successfully reset or negative if the
* device doesn't support resetting a single function. * device doesn't support resetting a single function.
*/ */
int pci_reset_function(struct pci_dev *dev) int pci_reset_function(struct pci_dev *dev)
{ {
int r = __pci_reset_function(dev, 1); int rc;
if (r < 0) rc = pci_dev_reset(dev, 1);
return r; if (rc)
return rc;
if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0)
disable_irq(dev->irq);
pci_save_state(dev); pci_save_state(dev);
/*
* both INTx and MSI are disabled after the Interrupt Disable bit
* is set and the Bus Master bit is cleared.
*/
pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
r = pci_execute_reset_function(dev); rc = pci_dev_reset(dev, 0);
pci_restore_state(dev); pci_restore_state(dev);
if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0)
enable_irq(dev->irq);
return r; return rc;
} }
EXPORT_SYMBOL_GPL(pci_reset_function); EXPORT_SYMBOL_GPL(pci_reset_function);
......
...@@ -702,8 +702,8 @@ int pcix_get_mmrbc(struct pci_dev *dev); ...@@ -702,8 +702,8 @@ int pcix_get_mmrbc(struct pci_dev *dev);
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc); int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
int pcie_get_readrq(struct pci_dev *dev); int pcie_get_readrq(struct pci_dev *dev);
int pcie_set_readrq(struct pci_dev *dev, int rq); int pcie_set_readrq(struct pci_dev *dev, int rq);
int __pci_reset_function(struct pci_dev *dev);
int pci_reset_function(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev);
int pci_execute_reset_function(struct pci_dev *dev);
void pci_update_resource(struct pci_dev *dev, int resno); void pci_update_resource(struct pci_dev *dev, int resno);
int __must_check pci_assign_resource(struct pci_dev *dev, int i); int __must_check pci_assign_resource(struct pci_dev *dev, int i);
int pci_select_bars(struct pci_dev *dev, unsigned long flags); int pci_select_bars(struct pci_dev *dev, unsigned long flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment