summaryrefslogtreecommitdiff
path: root/drivers/pci/pci.c
diff options
context:
space:
mode:
authorYu Zhao <yu.zhao@intel.com>2009-06-13 07:52:14 (GMT)
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-06-16 21:30:16 (GMT)
commitf85876ba82281f15bc4da11e41b94243a8b2b5b4 (patch)
tree8a2b9143123a8aa319bbe2e3078d8ea65aeb77da /drivers/pci/pci.c
parent8c1c699fec9e9021bf6ff0285dee086bb27aec90 (diff)
downloadlinux-fsl-qoriq-f85876ba82281f15bc4da11e41b94243a8b2b5b4.tar.xz
PCI: support PM D0hot->D3 transition reset
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. Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> Signed-off-by: Yu Zhao <yu.zhao@intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r--drivers/pci/pci.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 6a052ad..2e58acc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2132,6 +2132,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;
@@ -2149,6 +2179,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:
if (!probe) {
up(&dev->dev.sem);