All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stanislav Spassov <stanspas@amazon.com>
To: <linux-pci@vger.kernel.org>
Cc: "Stanislav Spassov" <stanspas@amazon.de>,
	"Bjorn Helgaas" <bhelgaas@google.com>,
	"Thomas Gleixner" <tglx@linutronix.de>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Jan H . Schönherr" <jschoenh@amazon.de>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Ashok Raj" <ashok.raj@intel.com>,
	"Alex Williamson" <alex.williamson@redhat.com>,
	"Sinan Kaya" <okaya@kernel.org>,
	"Rajat Jain" <rajatja@google.com>
Subject: [PATCH v2 08/17] PCI: Add more delay overrides to struct pci_dev
Date: Mon, 2 Mar 2020 19:44:20 +0100	[thread overview]
Message-ID: <20200302184429.12880-9-stanspas@amazon.com> (raw)
In-Reply-To: <20200302184429.12880-1-stanspas@amazon.com>

From: Stanislav Spassov <stanspas@amazon.de>

Almost all of the PCI_*_DELAY constants declared in drivers/pci/pci.h
can be overridden on a per-device basis, as explained in the constants'
comments. To allow storing per-device values, this patch introduces
a type to describe the various "initialization events":
    enum pci_init_event
and an array in struct pci_dev, with an override for each event.

This array is initially populated with the PCI_*_DELAY constants,
but can be later overridden by pci_acpi_optimize_delay. Quirks and
drivers may also end up tweaking the values.

The new array replaces two previous members: d3_delay and d3cold_delay
Direct mentions of PCI_*_DELAY are replaced with the per-device
override where applicable.

Signed-off-by: Stanislav Spassov <stanspas@amazon.de>
---
 Documentation/power/pci.rst         |  4 ++-
 arch/x86/pci/intel_mid_pci.c        |  2 +-
 drivers/hid/intel-ish-hid/ipc/ipc.c |  2 +-
 drivers/mfd/intel-lpss-pci.c        |  2 +-
 drivers/net/ethernet/marvell/sky2.c |  2 +-
 drivers/pci/iov.c                   |  4 +--
 drivers/pci/pci-acpi.c              | 42 ++++++++++++++++-------------
 drivers/pci/pci.c                   | 36 ++++++++++++++++++-------
 drivers/pci/quirks.c                |  9 ++++---
 include/linux/pci.h                 | 35 ++++++++++++++++++++++--
 10 files changed, 97 insertions(+), 41 deletions(-)

diff --git a/Documentation/power/pci.rst b/Documentation/power/pci.rst
index 0924d29636ad..8136c8a4b150 100644
--- a/Documentation/power/pci.rst
+++ b/Documentation/power/pci.rst
@@ -320,7 +320,9 @@ that these callbacks operate on::
 	unsigned int	d2_support:1;	/* Low power state D2 is supported */
 	unsigned int	no_d1d2:1;	/* D1 and D2 are forbidden */
 	unsigned int	wakeup_prepared:1;  /* Device prepared for wake up */
-	unsigned int	d3_delay;	/* D3->D0 transition time in ms */
+	unsigned int    delay[PCI_INIT_EVENT_COUNT]; /* minimum waiting time
+							after various events
+							in ms */
 	...
   };
 
diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c
index 00c62115f39c..b42b14cd0e00 100644
--- a/arch/x86/pci/intel_mid_pci.c
+++ b/arch/x86/pci/intel_mid_pci.c
@@ -322,7 +322,7 @@ static void pci_d3delay_fixup(struct pci_dev *dev)
 	 */
 	if (type1_access_ok(dev->bus->number, dev->devfn, PCI_DEVICE_ID))
 		return;
-	dev->d3_delay = 0;
+	dev->delay[PCI_INIT_EVENT_D3HOT_TO_D0] = 0;
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_d3delay_fixup);
 
diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c
index 8f8dfdf64833..a209b954fbe8 100644
--- a/drivers/hid/intel-ish-hid/ipc/ipc.c
+++ b/drivers/hid/intel-ish-hid/ipc/ipc.c
@@ -755,7 +755,7 @@ static int _ish_hw_reset(struct ishtp_device *dev)
 	csr |= PCI_D3hot;
 	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr);
 
-	mdelay(pdev->d3_delay);
+	mdelay(pdev->delay[PCI_INIT_EVENT_D3HOT_TO_D0]);
 
 	csr &= ~PCI_PM_CTRL_STATE_MASK;
 	csr |= PCI_D0;
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index c40a6c7d0cf8..5b9e458d0ab1 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -35,7 +35,7 @@ static int intel_lpss_pci_probe(struct pci_dev *pdev,
 	info->mem = &pdev->resource[0];
 	info->irq = pdev->irq;
 
-	pdev->d3cold_delay = 0;
+	pdev->delay[PCI_INIT_EVENT_RESET] = 0;
 
 	/* Probably it is enough to set this for iDMA capable devices only */
 	pci_set_master(pdev);
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index ebfd0ceac884..f808562e389d 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -5100,7 +5100,7 @@ static int sky2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	INIT_WORK(&hw->restart_work, sky2_restart);
 
 	pci_set_drvdata(pdev, hw);
-	pdev->d3_delay = 300;
+	pdev->delay[PCI_INIT_EVENT_D3HOT_TO_D0] = 300;
 
 	return 0;
 
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index d4e4a0c0a97f..f71fc28b69e6 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -524,7 +524,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
 	iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
 	pci_cfg_access_lock(dev);
 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
-	msleep(PCI_VF_ENABLE_DELAY);
+	msleep(dev->delay[PCI_INIT_EVENT_VF_ENABLE]);
 	pci_cfg_access_unlock(dev);
 
 	rc = sriov_add_vfs(dev, initial);
@@ -735,7 +735,7 @@ static void sriov_restore_state(struct pci_dev *dev)
 	pci_iov_set_numvfs(dev, iov->num_VFs);
 	pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
 	if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
-		msleep(PCI_VF_ENABLE_DELAY);
+		msleep(dev->delay[PCI_INIT_EVENT_VF_ENABLE]);
 }
 
 /**
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 66e8f8199ce0..4b5c29d2a5ab 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -1177,11 +1177,11 @@ static struct acpi_device *acpi_pci_find_companion(struct device *dev)
 }
 
 /**
- * pci_acpi_optimize_delay - optimize PCI D3 and D3cold delay from ACPI
- * @pdev: the PCI device whose delay is to be updated
+ * pci_acpi_optimize_delay - optimize PCI readiness delays from ACPI
+ * @pdev: the PCI device whose delays are to be updated
  * @handle: ACPI handle of this device
  *
- * Update the d3_delay and d3cold_delay of a PCI device from the ACPI _DSM
+ * Update the readiness delays of a PCI device from the ACPI _DSM
  * Function 9 of the device, and cache the parent host bridge's flag for
  * ignoring reset delay upon Sx Resume (the flag is originally set in
  * acpi_pci_add_bus through _DSM Function 8).
@@ -1226,6 +1226,7 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
 	u64 value_us;
 	int value;
 	union acpi_object *obj, *elements;
+	int i;
 
 	pdev->ignore_reset_delay_on_sx_resume =
 		bridge->ignore_reset_delay_on_sx_resume;
@@ -1237,21 +1238,26 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
 
 	if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 5) {
 		elements = obj->package.elements;
-		if (elements[0].type == ACPI_TYPE_INTEGER) {
-			value_us = elements[0].integer.value;
-			value = (int)(value_us / 1000);
-			if (value_us % 1000 > 0)
-				value++;
-			if (value < PCI_RESET_DELAY)
-				pdev->d3cold_delay = value;
-		}
-		if (elements[3].type == ACPI_TYPE_INTEGER) {
-			value_us = elements[3].integer.value;
-			value = (int)(value_us / 1000);
-			if (value_us % 1000 > 0)
-				value++;
-			if (value < PCI_PM_D3HOT_DELAY)
-				pdev->d3_delay = value;
+		for (i = 0; i < 5; i++) {
+			if (elements[i].type == ACPI_TYPE_INTEGER) {
+				value_us = elements[i].integer.value;
+				value = (int)(value_us / 1000);
+				if (value_us % 1000 > 0)
+					value++;
+				/*
+				 * XXX This relies on the initial values in the
+				 * delay array being set using the PCI_*_DELAY
+				 * macros in drivers/pci/pci.h
+				 * Once the kernel has support for Readiness
+				 * Time Reporting Extended Capability, this
+				 * needs fixing to honor prioritization of
+				 * overrides. Also, a flag would need to be
+				 * set to disable the use of Readiness
+				 * Notifications at some point.
+				 */
+				if (value < pdev->delay[i])
+					pdev->delay[i] = value;
+			}
 		}
 	}
 	ACPI_FREE(obj);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index aaef00578487..ba54164652cc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -43,6 +43,15 @@ const char *pci_power_names[] = {
 };
 EXPORT_SYMBOL_GPL(pci_power_names);
 
+const char *pci_init_event_names[] = {
+	[PCI_INIT_EVENT_RESET] = "conventional reset",
+	[PCI_INIT_EVENT_DL_UP] = "DL Up",
+	[PCI_INIT_EVENT_FLR] = "FLR",
+	[PCI_INIT_EVENT_D3HOT_TO_D0] = "PM D3hot->D0",
+	[PCI_INIT_EVENT_VF_ENABLE] = "VF Enable",
+};
+EXPORT_SYMBOL_GPL(pci_init_event_names);
+
 int isa_dma_bridge_buggy;
 EXPORT_SYMBOL(isa_dma_bridge_buggy);
 
@@ -66,7 +75,7 @@ struct pci_pme_device {
 
 static void pci_dev_d3_sleep(struct pci_dev *dev)
 {
-	unsigned int delay = dev->d3_delay;
+	unsigned int delay = dev->delay[PCI_INIT_EVENT_D3HOT_TO_D0];
 
 	if (delay < pci_pm_d3_delay)
 		delay = pci_pm_d3_delay;
@@ -2844,8 +2853,11 @@ void pci_pm_init(struct pci_dev *dev)
 
 	dev->pm_cap = pm;
 	dev->ignore_reset_delay_on_sx_resume = 0;
-	dev->d3_delay = PCI_PM_D3HOT_DELAY;
-	dev->d3cold_delay = PCI_RESET_DELAY;
+	dev->delay[PCI_INIT_EVENT_RESET] = PCI_RESET_DELAY;
+	dev->delay[PCI_INIT_EVENT_DL_UP] = PCI_DL_UP_DELAY;
+	dev->delay[PCI_INIT_EVENT_FLR] = PCI_FLR_DELAY;
+	dev->delay[PCI_INIT_EVENT_D3HOT_TO_D0] = PCI_PM_D3HOT_DELAY;
+	dev->delay[PCI_INIT_EVENT_VF_ENABLE] = PCI_VF_ENABLE_DELAY;
 	dev->bridge_d3 = pci_bridge_d3_possible(dev);
 	dev->d3cold_allowed = true;
 
@@ -4500,7 +4512,7 @@ int pcie_flr(struct pci_dev *dev)
 	if (dev->imm_ready)
 		return 0;
 
-	msleep(PCI_FLR_DELAY);
+	msleep(dev->delay[PCI_INIT_EVENT_FLR]);
 
 	return pci_dev_wait(dev, "FLR", PCIE_RESET_READY_POLL_MS);
 }
@@ -4539,7 +4551,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
 	if (dev->imm_ready)
 		return 0;
 
-	msleep(PCI_FLR_DELAY);
+	msleep(dev->delay[PCI_INIT_EVENT_FLR]);
 
 	return pci_dev_wait(dev, "AF_FLR", PCIE_RESET_READY_POLL_MS);
 }
@@ -4556,7 +4568,9 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
  *
  * NOTE: This causes the caller to sleep for twice the device power transition
  * cooldown period, which for the D0->D3hot and D3hot->D0 transitions is 10 ms
- * by default (i.e. unless the @dev's d3_delay field has a different value).
+ * by default (i.e. unless the @dev's delay[PCI_INIT_EVENT_D3HOT_TO_D0] field
+ * has a different value).
+ *
  * Moreover, only devices in D0 can be reset by this function.
  */
 static int pci_pm_reset(struct pci_dev *dev, int probe)
@@ -4666,12 +4680,14 @@ static int pci_bus_max_d3cold_delay(const struct pci_bus *bus)
 	const struct pci_dev *pdev;
 	int min_delay = 100;
 	int max_delay = 0;
+	int delay;
 
 	list_for_each_entry(pdev, &bus->devices, bus_list) {
-		if (pdev->d3cold_delay < min_delay)
-			min_delay = pdev->d3cold_delay;
-		if (pdev->d3cold_delay > max_delay)
-			max_delay = pdev->d3cold_delay;
+		delay = pdev->delay[PCI_INIT_EVENT_RESET];
+		if (delay < min_delay)
+			min_delay = delay;
+		if (delay > max_delay)
+			max_delay = delay;
 	}
 
 	return max(min_delay, max_delay);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 29f473ebf20f..12f22af0cbef 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1873,12 +1873,13 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,	0x260b, quirk_intel_pcie_pm);
 
 static void quirk_d3hot_delay(struct pci_dev *dev, unsigned int delay)
 {
-	if (dev->d3_delay >= delay)
+
+	if (dev->delay[PCI_INIT_EVENT_D3HOT_TO_D0] >= delay)
 		return;
 
-	dev->d3_delay = delay;
+	dev->delay[PCI_INIT_EVENT_D3HOT_TO_D0] = delay;
 	pci_info(dev, "extending delay after power-on from D3hot to %d msec\n",
-		 dev->d3_delay);
+		 dev->delay[PCI_INIT_EVENT_D3HOT_TO_D0]);
 }
 
 static void quirk_radeon_pm(struct pci_dev *dev)
@@ -3310,7 +3311,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
  */
 static void quirk_remove_d3_delay(struct pci_dev *dev)
 {
-	dev->d3_delay = 0;
+	dev->delay[PCI_INIT_EVENT_D3HOT_TO_D0] = 0;
 }
 /* C600 Series devices do not need 10ms d3_delay */
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0412, quirk_remove_d3_delay);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 22a5b7164c32..16dbfff2092e 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -268,6 +268,36 @@ enum pci_bus_speed {
 enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev);
 enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev);
 
+/*
+ * The first five constants correspond to delays specified in both:
+ * PCI Firmware Specification Rev. 3.2 (January 26, 2015),
+ * Section 4.6.9. "_DSM for Specifying Device Readiness Durations", and
+ * PCI Express Base Specification  Revision 5.0 Version 1.0 (May 22, 2019)
+ * Section 7.9.17 "Readiness Time Reporting Extended Capability"
+ *
+ * The code assumes these constants are in the same order as in the
+ * PCI Firmware Specification.
+ */
+enum pci_init_event {
+	PCI_INIT_EVENT_RESET		= 0, /* D3cold->D0, SBR */
+	PCI_INIT_EVENT_DL_UP		= 1,
+	PCI_INIT_EVENT_FLR		= 2,
+	PCI_INIT_EVENT_D3HOT_TO_D0	= 3,
+	PCI_INIT_EVENT_VF_ENABLE	= 4,
+	PCI_INIT_EVENT_COUNT  /* Keep this as last element */
+};
+
+/* Remember to update this when the list above changes! */
+extern const char *pci_init_event_names[];
+
+static inline const char *pci_init_event_name(enum pci_init_event event)
+{
+	if (event >= PCI_INIT_EVENT_COUNT)
+		return "<unknown>";
+	else
+		return pci_init_event_names[event];
+}
+
 struct pci_cap_saved_data {
 	u16		cap_nr;
 	bool		cap_extended;
@@ -356,8 +386,9 @@ struct pci_dev {
 						   bit manually */
 	unsigned int    ignore_reset_delay_on_sx_resume:1; /* Cached value from
 							      pci_host_bridge */
-	unsigned int	d3_delay;	/* D3->D0 transition time in ms */
-	unsigned int	d3cold_delay;	/* D3cold->D0 transition time in ms */
+	unsigned int    delay[PCI_INIT_EVENT_COUNT]; /* minimum waiting time
+							after various events
+							in ms */
 
 #ifdef CONFIG_PCIEASPM
 	struct pcie_link_state	*link_state;	/* ASPM link state */
-- 
2.25.1




Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879




  parent reply	other threads:[~2020-03-02 18:46 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-02 18:44 [PATCH v2 00/17] Improve PCI device post-reset readiness polling Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 01/17] PCI: Fall back to slot/bus reset if softer methods timeout Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 02/17] PCI: Remove unused PCI_PM_BUS_WAIT Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 03/17] PCI: Use pci_bridge_wait_for_secondary_bus after SBR Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 04/17] PCI: Do not override delay for D0->D3hot transition Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 05/17] PCI: Fix handling of _DSM 8 (avoiding reset delays) Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 06/17] PCI: Fix us->ms conversion in pci_acpi_optimize_delay Stanislav Spassov
2020-03-03  4:19   ` kbuild test robot
2020-03-03  4:19     ` kbuild test robot
2020-03-03  5:54   ` kbuild test robot
2020-03-03  5:54     ` kbuild test robot
2020-03-02 18:44 ` [PATCH v2 07/17] PCI: Clean up and document PM/reset delays Stanislav Spassov
2020-03-03  1:51   ` kbuild test robot
2020-03-03  1:51     ` kbuild test robot
2020-03-03  2:54   ` kbuild test robot
2020-03-03  2:54     ` kbuild test robot
2020-03-02 18:44 ` Stanislav Spassov [this message]
2020-03-02 18:44 ` [PATCH v2 09/17] PCI: Generalize pci_bus_max_d3cold_delay to pci_bus_max_delay Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 10/17] PCI: Use correct delay in pci_bridge_wait_for_secondary_bus Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 11/17] PCI: Refactor pci_dev_wait to remove timeout parameter Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 12/17] PCI: Refactor pci_dev_wait to take pci_init_event Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 13/17] PCI: Cache CRS Software Visibiliy in struct pci_dev Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 14/17] PCI: Introduce per-device reset_ready_poll override Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 15/17] PCI: Refactor polling loop out of pci_dev_wait Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 16/17] PCI: Add CRS handling to pci_dev_wait() Stanislav Spassov
2020-03-02 18:44 ` [PATCH v2 17/17] PCI: Lower PCIE_RESET_READY_POLL_MS from 1m to 1s Stanislav Spassov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200302184429.12880-9-stanspas@amazon.com \
    --to=stanspas@amazon.com \
    --cc=akpm@linux-foundation.org \
    --cc=alex.williamson@redhat.com \
    --cc=ashok.raj@intel.com \
    --cc=bhelgaas@google.com \
    --cc=corbet@lwn.net \
    --cc=jschoenh@amazon.de \
    --cc=linux-pci@vger.kernel.org \
    --cc=okaya@kernel.org \
    --cc=rajatja@google.com \
    --cc=stanspas@amazon.de \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.