mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Bjorn Helgaas <helgaas@kernel.org>
To: Nirmoy Das <nirmoyd@nvidia.com>
Cc: "Bjorn Helgaas" <bhelgaas@google.com>,
	linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org,
	"JC Chen" <jc_chen@diodes.com>,
	"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Subject: Re: [RFC PATCH] PCI: Work around Pericom PI7C9X3G606GPC Port 4 BAR erratum
Date: Thu, 23 Jul 2026 15:54:13 -0500	[thread overview]
Message-ID: <20260723205413.GA852929@bhelgaas> (raw)
In-Reply-To: <20260723161010.2638956-1-nirmoyd@nvidia.com>

[+cc Ilpo, this sounds really weird from a resource perspective]

On Thu, Jul 23, 2026 at 09:10:00AM -0700, Nirmoy Das wrote:
> The Pericom PI7C9X3G606GPC PCIe switch has an erratum where downstream
> Port 4 retains a default MSI-X table and PBA decode when BAR 0 is zero.
> Memory reads that match this window are dropped with an Unsupported
> Request completion, which may cause the SoC to report a timeout.

Help me understand what's going on here.  Can you share the dmesg log
of enumeration and resource assignment and the "lspci -v" output for
the whole switch (both upstream and downstream ports) without this
patch?

I guess without this patch, MSI-X from the downstream port doesn't
work correctly?

> The workaround is to make Port 4 BAR 0 mirror BAR 0 of the immediate
> upstream port. Firmware may establish this at boot, but PCI resource
> assignment can move upstream BAR 0 without updating Port 4.

By "mirror", I guess you mean you want the same value in BAR 0 of both
the switch upstream port and the downstream port?  I don't know what
that even means, because the upstream port BAR 0 can't be inside its
memory window, so reads should never reach the downstream port.

> For a 64-bit BAR, also mirror BAR 1 while memory decoding is disabled,
> matching the PCI core update sequence. Port 4 BAR 0 may read back as zero
> after a successful write, so do not use readback to validate the update.
> 
> Signed-off-by: Nirmoy Das <nirmoyd@nvidia.com>
> ---
>  drivers/pci/quirks.c | 92 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 92 insertions(+)
> 
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index b09f27f7846fc..fa8098d77adb5 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -6264,6 +6264,98 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb404,
>  DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404,
>  			 pci_fixup_pericom_acs_store_forward);
>  
> +#define PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC 0xc008
> +
> +/*
> + * Pericom PI7C9X3G606GPC switch erratum E15 -
> + * Downstream Port 4 BAR 0 must mirror the immediate upstream port BAR 0
> + *
> + * Port 4 uses BAR 0 for its MSI-X table and PBA. Firmware may program this
> + * mirror at boot, but Linux resource assignment can move the upstream BAR
> + * and leave Port 4 with a stale mirror.
> + *
> + * Diodes confirmed Tile0/P4 appears to Linux as device 4, function 0 on the
> + * bus below the upstream port. Match that downstream function and re-apply
> + * the mirror after resource assignment and early resume.
> + */
> +static void pci_fixup_pericom_pi7c9x3g606gpc_bar0_mirror(struct pci_dev *pdev)
> +{
> +	struct pci_dev *upstream;
> +	bool bar0_64, disable_mem;
> +	u16 cmd = 0;
> +	u32 bar = 0, bar1 = 0, upstream_bar = 0, upstream_bar1 = 0;
> +
> +	if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)
> +		return;
> +
> +	if (PCI_SLOT(pdev->devfn) != 4 || PCI_FUNC(pdev->devfn))
> +		return;
> +
> +	upstream = pci_upstream_bridge(pdev);
> +	if (!upstream || upstream->vendor != PCI_VENDOR_ID_PERICOM ||
> +	    upstream->device != PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC ||
> +	    pci_pcie_type(upstream) != PCI_EXP_TYPE_UPSTREAM)
> +		return;
> +
> +	pci_read_config_dword(upstream, PCI_BASE_ADDRESS_0, &upstream_bar);
> +	if (upstream_bar & PCI_BASE_ADDRESS_SPACE_IO)
> +		return;
> +
> +	bar0_64 = (upstream_bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
> +		  PCI_BASE_ADDRESS_MEM_TYPE_64;
> +	if (bar0_64)
> +		pci_read_config_dword(upstream, PCI_BASE_ADDRESS_1,
> +				      &upstream_bar1);
> +
> +	if (!(upstream_bar & PCI_BASE_ADDRESS_MEM_MASK) &&
> +	    (!bar0_64 || !upstream_bar1)) {
> +		pci_warn(pdev, "skipping PI7C9X3G606GPC BAR 0 mirror workaround because upstream BAR 0 is unassigned\n");
> +		return;
> +	}
> +
> +	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar);
> +	if (bar0_64) {
> +		pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, &bar1);
> +		if (bar == upstream_bar && bar1 == upstream_bar1)
> +			return;
> +	} else {
> +		if (bar == upstream_bar)
> +			return;
> +	}
> +
> +	/*
> +	 * Port 4 BAR 0 may read back as zero even after a successful write.
> +	 * If BAR 0 is configured as 64-bit, BAR 1 is the upper half.
> +	 * Disable memory decoding while updating both dwords, matching PCI
> +	 * core's 64-bit BAR update sequence.
> +	 */
> +	disable_mem = bar0_64 && !pdev->mmio_always_on;
> +	if (disable_mem) {
> +		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
> +		pci_write_config_word(pdev, PCI_COMMAND,
> +				      cmd & ~PCI_COMMAND_MEMORY);
> +	}
> +
> +	pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, upstream_bar);
> +	if (bar0_64)
> +		pci_write_config_dword(pdev, PCI_BASE_ADDRESS_1, upstream_bar1);
> +	if (disable_mem)
> +		pci_write_config_word(pdev, PCI_COMMAND, cmd);
> +
> +	if (bar0_64)
> +		pci_info(pdev, "wrote upstream BAR 0/1 %#x/%#x to Port 4 BAR 0/1 for PI7C9X3G606GPC BAR 0 mirror workaround\n",
> +			 upstream_bar, upstream_bar1);
> +	else
> +		pci_info(pdev, "wrote upstream BAR 0 %#x to Port 4 BAR 0 for PI7C9X3G606GPC BAR 0 mirror workaround\n",
> +			 upstream_bar);
> +}
> +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PERICOM,
> +			PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC,
> +			pci_fixup_pericom_pi7c9x3g606gpc_bar0_mirror);
> +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_PERICOM,
> +			       PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC,
> +			       pci_fixup_pericom_pi7c9x3g606gpc_bar0_mirror);
> +
>  static void nvidia_ion_ahci_fixup(struct pci_dev *pdev)
>  {
>  	pdev->dev_flags |= PCI_DEV_FLAGS_HAS_MSI_MASKING;
> 
> base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
> -- 
> 2.43.0
> 

  reply	other threads:[~2026-07-23 20:54 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-23 16:10 Nirmoy Das
2026-07-23 20:54 ` Bjorn Helgaas [this message]
2026-07-24  8:36   ` JC Chen[陳饒靜]
2026-08-26 14:28     ` Ilpo Järvinen
2026-08-27  0:13       ` JC Chen[陳饒靜]
2026-08-27  6:45         ` JC Chen[陳饒靜]

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=20260723205413.GA852929@bhelgaas \
    --to=helgaas@kernel.org \
    --cc=bhelgaas@google.com \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=jc_chen@diodes.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=nirmoyd@nvidia.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®