* [RFC PATCH 1/3] PCI: rewrite Function Level Reset
@ 2009-04-07 1:33 Yu Zhao
2009-04-07 1:33 ` [RFC PATCH 2/3] PCI: support PM D0hot->D3 transition reset Yu Zhao
2009-04-07 1:33 ` [RFC PATCH 3/3] PCI: support Secondary Bus Reset Yu Zhao
0 siblings, 2 replies; 3+ messages in thread
From: Yu Zhao @ 2009-04-07 1:33 UTC (permalink / raw)
To: linux-pci; +Cc: kvm, matthew, Yu Zhao
Changes:
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.
Signed-off-by: Yu Zhao <yu.zhao@intel.com>
---
drivers/pci/pci.c | 166 ++++++++++++++++++++++++++-------------------------
include/linux/pci.h | 2 +-
2 files changed, 85 insertions(+), 83 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index af4db4e..46ae997 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2008,111 +2008,112 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask)
EXPORT_SYMBOL(pci_set_dma_seg_boundary);
#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;
- 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;
- 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))
return -ENOTTY;
if (probe)
return 0;
- pci_block_user_cfg_access(dev);
-
/* Wait for Transaction Pending bit clean */
- pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
- if (!(status & PCI_EXP_DEVSTA_TRPND))
- goto transaction_done;
+ for (i = 0; i < 4; i++) {
+ if (i)
+ msleep((1 << (i - 1)) * 100);
- msleep(100);
- pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
- if (!(status & PCI_EXP_DEVSTA_TRPND))
- goto transaction_done;
-
- 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");
-
-transaction_done:
- pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL,
+ pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
+ if (!(status & PCI_EXP_DEVSTA_TRPND))
+ goto clear;
+ }
+
+ dev_err(&dev->dev, "transaction is not cleared; "
+ "proceeding with reset anyway\n");
+
+clear:
+ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_BCR_FLR);
- mdelay(100);
+ msleep(100);
- pci_unblock_user_cfg_access(dev);
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);
- u8 status;
+ int i;
+ int pos;
u8 cap;
+ u8 status;
- if (!cappos)
+ pos = pci_find_capability(dev, PCI_CAP_ID_AF);
+ if (!pos)
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))
return -ENOTTY;
if (probe)
return 0;
- pci_block_user_cfg_access(dev);
-
/* Wait for Transaction Pending bit clean */
- pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status);
- if (!(status & PCI_AF_STATUS_TP))
- goto transaction_done;
+ for (i = 0; i < 4; i++) {
+ if (i)
+ 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);
- 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;
}
-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);
- res = __pcie_flr(pdev, probe);
- if (res != -ENOTTY)
- return res;
+ /* block PM suspend, driver probe, etc. */
+ down(&dev->dev.sem);
- res = __pci_af_flr(pdev, probe);
- if (res != -ENOTTY)
- return res;
+ rc = pcie_flr(dev, probe);
+ if (rc != -ENOTTY)
+ goto done;
- return res;
+ rc = pci_af_flr(dev, probe);
+done:
+ up(&dev->dev.sem);
+
+ if (!probe)
+ pci_unblock_user_cfg_access(dev);
+
+ return rc;
}
/**
- * pci_execute_reset_function() - Reset a PCI device function
- * @dev: Device function to reset
+ * __pci_reset_function - reset a PCI device function
+ * @dev: PCI device to reset
*
* Some devices allow an individual function to be reset without affecting
* other functions in the same device. The PCI device must be responsive
@@ -2124,18 +2125,18 @@ static int __pci_reset_function(struct pci_dev *pdev, int probe)
* device including MSI, bus mastering, BARs, decoding IO and memory spaces,
* 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.
*/
-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
- * @dev: Device function to reset
+ * pci_reset_function - quiesce and reset a PCI device function
+ * @dev: PCI device to reset
*
* Some devices allow an individual function to be reset without affecting
* other functions in the same device. The PCI device must be responsive
@@ -2143,32 +2144,33 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function);
*
* This function does not just reset the PCI portion of a device, but
* 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.
*
- * 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.
*/
int pci_reset_function(struct pci_dev *dev)
{
- int r = __pci_reset_function(dev, 1);
+ int rc;
- if (r < 0)
- return r;
+ rc = pci_dev_reset(dev, 1);
+ if (rc)
+ return rc;
- if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0)
- disable_irq(dev->irq);
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);
- r = pci_execute_reset_function(dev);
+ rc = pci_dev_reset(dev, 0);
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);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index a7fe4bb..597391a 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -698,8 +698,8 @@ int pcix_get_mmrbc(struct pci_dev *dev);
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
int pcie_get_readrq(struct pci_dev *dev);
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_execute_reset_function(struct pci_dev *dev);
void pci_update_resource(struct pci_dev *dev, int resno);
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
--
1.5.6.4
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH 2/3] PCI: support PM D0hot->D3 transition reset
2009-04-07 1:33 [RFC PATCH 1/3] PCI: rewrite Function Level Reset Yu Zhao
@ 2009-04-07 1:33 ` Yu Zhao
2009-04-07 1:33 ` [RFC PATCH 3/3] PCI: support Secondary Bus Reset Yu Zhao
1 sibling, 0 replies; 3+ messages in thread
From: Yu Zhao @ 2009-04-07 1:33 UTC (permalink / raw)
To: linux-pci; +Cc: kvm, matthew, Yu Zhao
PCI PM 1.2 specifies that the device will perform an internal reset upon
transitioning from D3hot to D0 when the NO_SOFT_RESET bit is clear. This
method can be used to reset a function if neither PCIe FLR nor PCI AF FLR
are supported.
Signed-off-by: Yu Zhao <yu.zhao@intel.com>
---
drivers/pci/pci.c | 34 ++++++++++++++++++++++++++++++++++
1 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 46ae997..e459a0b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2085,6 +2085,36 @@ clear:
return 0;
}
+static int pci_pm_reset(struct pci_dev *dev, int probe)
+{
+ u16 csr;
+
+ if (!dev->pm_cap)
+ return -ENOTTY;
+
+ pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &csr);
+ if (csr & PCI_PM_CTRL_NO_SOFT_RESET)
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
+
+ if (dev->current_state != PCI_D0)
+ return -EINVAL;
+
+ csr &= ~PCI_PM_CTRL_STATE_MASK;
+ csr |= PCI_D3hot;
+ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
+ msleep(pci_pm_d3_delay);
+
+ csr &= ~PCI_PM_CTRL_STATE_MASK;
+ csr |= PCI_D0;
+ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
+ msleep(pci_pm_d3_delay);
+
+ return 0;
+}
+
static int pci_dev_reset(struct pci_dev *dev, int probe)
{
int rc;
@@ -2102,6 +2132,10 @@ static int pci_dev_reset(struct pci_dev *dev, int probe)
goto done;
rc = pci_af_flr(dev, probe);
+ if (rc != -ENOTTY)
+ goto done;
+
+ rc = pci_pm_reset(dev, probe);
done:
up(&dev->dev.sem);
--
1.5.6.4
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH 3/3] PCI: support Secondary Bus Reset
2009-04-07 1:33 [RFC PATCH 1/3] PCI: rewrite Function Level Reset Yu Zhao
2009-04-07 1:33 ` [RFC PATCH 2/3] PCI: support PM D0hot->D3 transition reset Yu Zhao
@ 2009-04-07 1:33 ` Yu Zhao
1 sibling, 0 replies; 3+ messages in thread
From: Yu Zhao @ 2009-04-07 1:33 UTC (permalink / raw)
To: linux-pci; +Cc: kvm, matthew, Yu Zhao
PCI-to-PCI Bridge 1.2 specifies that the Secondary Bus Reset bit can
force the assertion of RST# on the secondary interface, which can be
used to reset all devices including subordinates under this bus. This
can be used to reset a function if this function is the only device
under this bus.
Signed-off-by: Yu Zhao <yu.zhao@intel.com>
---
drivers/pci/pci.c | 31 +++++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e459a0b..a77c33a 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2115,6 +2115,33 @@ static int pci_pm_flr(struct pci_dev *dev, int probe)
return 0;
}
+static int pci_secondary_bus_reset(struct pci_dev *dev, int probe)
+{
+ u16 ctrl;
+ struct pci_dev *pdev;
+
+ if (dev->subordinate)
+ return -ENOTTY;
+
+ list_for_each_entry(pdev, &dev->bus->devices, bus_list)
+ if (pdev != dev)
+ return -ENOTTY;
+
+ if (probe)
+ return 0;
+
+ pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl);
+ ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
+ pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
+ msleep(100);
+
+ ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+ pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
+ msleep(100);
+
+ return 0;
+}
+
static int pci_dev_reset(struct pci_dev *dev, int probe)
{
int rc;
@@ -2136,6 +2163,10 @@ static int pci_dev_reset(struct pci_dev *dev, int probe)
goto done;
rc = pci_pm_flr(dev, probe);
+ if (rc != -ENOTTY)
+ goto done;
+
+ rc = pci_secondary_bus_reset(dev, probe);
done:
up(&dev->dev.sem);
--
1.5.6.4
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2009-04-07 1:32 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-07 1:33 [RFC PATCH 1/3] PCI: rewrite Function Level Reset Yu Zhao
2009-04-07 1:33 ` [RFC PATCH 2/3] PCI: support PM D0hot->D3 transition reset Yu Zhao
2009-04-07 1:33 ` [RFC PATCH 3/3] PCI: support Secondary Bus Reset Yu Zhao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).