]> www.infradead.org Git - nvme.git/commitdiff
PCI: dw-rockchip: Hide broken ATS capability for RK3588 running in EP mode
authorNiklas Cassel <cassel@kernel.org>
Mon, 10 Mar 2025 09:48:28 +0000 (10:48 +0100)
committerKrzysztof Wilczyński <kwilczynski@kernel.org>
Fri, 14 Mar 2025 16:13:19 +0000 (16:13 +0000)
When running the RK3588 in Endpoint mode, with an Intel host with IOMMU
enabled, the host side prints:

  DMAR: VT-d detected Invalidation Time-out Error: SID 0

When running the RK3588 in Endpoint mode, with an AMD host with IOMMU
enabled, the host side prints:

  iommu ivhd0: AMD-Vi: Event logged [IOTLB_INV_TIMEOUT device=63:00.0 address=0x42b5b01a0]

Rockchip has confirmed that the ATS support for RK3588 only works when
running the PCIe controller in Root Complex (RC) mode, see:

  https://lore.kernel.org/linux-pci/93cdce39-1ae6-4939-a3fc-db10be7564e5@rock-chips.com

Usually, to handle these issues, we add a quirk for the PCI vendor and
device ID in drivers/pci/quirks.c with quirk_no_ats(). That is because
we cannot usually modify the capabilities on the EP side. In this case,
we can modify the capabilities on the EP side.

Thus, hide the broken ATS capability on RK3588 when running in EP mode.

That way, we don't need any quirk on the host side, and we see no errors
on the host side, and we can run pci_endpoint_test successfully, with
the IOMMU enabled on the host side.

Acked-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
[kwilczynski: commit log, tidy up code comments and error message]
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Link: https://lore.kernel.org/r/20250310094826.842681-6-cassel@kernel.org
drivers/pci/controller/dwc/pcie-dw-rockchip.c

index 93698abff4d9c7694822f33acb5f598240918ac5..3aca62f3a864ec5c21d4ad8a309629eb3aa8609d 100644 (file)
@@ -240,6 +240,34 @@ static const struct dw_pcie_host_ops rockchip_pcie_host_ops = {
        .init = rockchip_pcie_host_init,
 };
 
+/*
+ * ATS does not work on RK3588 when running in EP mode.
+ *
+ * After the host has enabled ATS on the EP side, it will send an IOTLB
+ * invalidation request to the EP side. However, the RK3588 will never send
+ * a completion back and eventually the host will print an IOTLB_INV_TIMEOUT
+ * error, and the EP will not be operational. If we hide the ATS capability,
+ * things work as expected.
+ */
+static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       struct device *dev = pci->dev;
+
+       /* Only hide the ATS capability for RK3588 running in EP mode. */
+       if (!of_device_is_compatible(dev->of_node, "rockchip,rk3588-pcie-ep"))
+               return;
+
+       if (dw_pcie_ep_hide_ext_capability(pci, PCI_EXT_CAP_ID_SECPCI,
+                                          PCI_EXT_CAP_ID_ATS))
+               dev_err(dev, "failed to hide ATS capability\n");
+}
+
+static void rockchip_pcie_ep_pre_init(struct dw_pcie_ep *ep)
+{
+       rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep);
+}
+
 static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep)
 {
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
@@ -312,6 +340,7 @@ rockchip_pcie_get_features(struct dw_pcie_ep *ep)
 
 static const struct dw_pcie_ep_ops rockchip_pcie_ep_ops = {
        .init = rockchip_pcie_ep_init,
+       .pre_init = rockchip_pcie_ep_pre_init,
        .raise_irq = rockchip_pcie_raise_irq,
        .get_features = rockchip_pcie_get_features,
 };