PCI: rockchip: dw-ep: Hide broken ATS cap

From chip design point of view, ATS support wasn't implemented in EP mode,
but leaving ATS cap available for both of EP and RC mode is totally broken
if servers active IOMMU and ATS support.

Reports state the problem are:

(1)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

(2)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]

Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Change-Id: I7763f304bb3b71c11a67579803b2531ab7538133
This commit is contained in:
Shawn Lin
2025-02-24 09:08:43 +08:00
committed by Tao Huang
parent fa77b3acb0
commit 8668d80571

View File

@@ -700,6 +700,45 @@ static int rockchip_pcie_deinit_host(struct rockchip_pcie *rockchip)
return 0;
}
/*
* ATS does not work on platform like rk3588 when running in EP mode.
* After a host has enabled ATS on the EP side, it will send an IOTLB
* invalidation request to the EP side. 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 cap, things work as expected.
*/
static void rockchip_pcie_hide_broken_ats_cap(struct dw_pcie *pci)
{
struct device *dev = pci->dev;
unsigned int spcie_cap_offset, next_cap_offset;
u32 spcie_cap_header, next_cap_header;
/* only hide the ATS cap for rk3588 running in EP mode */
if (!of_device_is_compatible(dev->of_node, "rockchip,rk3588-pcie-std-ep"))
return;
spcie_cap_offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_SECPCI);
if (!spcie_cap_offset)
return;
spcie_cap_header = dw_pcie_readl_dbi(pci, spcie_cap_offset);
next_cap_offset = PCI_EXT_CAP_NEXT(spcie_cap_header);
next_cap_header = dw_pcie_readl_dbi(pci, next_cap_offset);
if (PCI_EXT_CAP_ID(next_cap_header) != PCI_EXT_CAP_ID_ATS)
return;
/* clear next ptr */
spcie_cap_header &= ~GENMASK(31, 20);
/* set next ptr to next ptr of ATS_CAP */
spcie_cap_header |= next_cap_header & GENMASK(31, 20);
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi(pci, spcie_cap_offset, spcie_cap_header);
dw_pcie_dbi_ro_wr_dis(pci);
}
static int rockchip_pcie_config_host(struct rockchip_pcie *rockchip)
{
struct device *dev = rockchip->pci.dev;
@@ -717,6 +756,8 @@ static int rockchip_pcie_config_host(struct rockchip_pcie *rockchip)
dw_pcie_setup(&rockchip->pci);
rockchip_pcie_hide_broken_ats_cap(pci);
dw_pcie_dbi_ro_wr_en(&rockchip->pci);
/* Enable bus master and memory space */
dw_pcie_writel_dbi(pci, PCIE_TYPE0_STATUS_COMMAND_REG, 0x6);