Merge branch 'pci/controller/dwc-rcar-gen4'

- When MSI is enabled but iMSI-RX is not used, configure AXIINTC to allow
  GIT ITS to handle MSI (Marek Vasut)

- Refactor GIC600 implementation to make it easier to add platforms that
  only support 32-bit addressing (Marek Vasut)

- Add Renesas R-Car Gen4 S4/V4H/V4M to the list of GIC600 integrations that
  only support 32-bit addressing (Marek Vasut)

* pci/controller/dwc-rcar-gen4:
  irqchip/gic-v3: Add Renesas R-Car Gen4 erratum workaround
  irqchip/gic-v3: Refactor GIC600 limited to 32bit PA erratum handling
  PCI: rcar-gen4: Configure AXIINTC if iMSI-RX is not used
  PCI: dwc: Move iMSI-RX check before calling 'pp->ops->init()'
This commit is contained in:
Bjorn Helgaas 2026-08-21 16:40:43 -05:00
commit 45b3d1baf4
6 changed files with 148 additions and 18 deletions

View File

@ -352,6 +352,7 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| Qualcomm Tech. | Kryo4xx Gold | N/A | ARM64_ERRATUM_1286807 |
+----------------+-----------------+-----------------+-----------------------------+
| Renesas | S4/V4H/V4M | N/A | RENESAS_ERRATUM_GEN4GICITS1 |
+----------------+-----------------+-----------------+-----------------------------+
| Rockchip | RK3588 | #3588001 | ROCKCHIP_ERRATUM_3588001 |
+----------------+-----------------+-----------------+-----------------------------+

View File

@ -1382,6 +1382,15 @@ config NVIDIA_CARMEL_CNP_ERRATUM
If unsure, say Y.
config RENESAS_ERRATUM_GEN4GICITS1
bool "Renesas R-Car Gen4: GIC600 can not access physical addresses above 4 GiB"
default y
help
The Renesas R-Car Gen4 S4/V4H/V4M GIC600 SoC integrations have AXI
addressing limited to the first 32-bit of physical address space.
If unsure, say Y.
config ROCKCHIP_ERRATUM_3568002
bool "Rockchip 3568002: GIC600 can not access physical addresses higher than 4GB"
default y

View File

@ -4890,10 +4890,22 @@ static bool __maybe_unused its_enable_quirk_hip09_162100801(void *data)
return true;
}
static bool __maybe_unused its_enable_rk3568002(void *data)
static const char * const dma_32bit_impaired_platforms[] = {
#ifdef CONFIG_RENESAS_ERRATUM_GEN4GICITS1
"renesas,r8a779f0",
"renesas,r8a779g0",
"renesas,r8a779h0",
#endif
#ifdef CONFIG_ROCKCHIP_ERRATUM_3568002
"rockchip,rk3566",
"rockchip,rk3568",
#endif
NULL,
};
static bool its_enable_dma32(void *data)
{
if (!of_machine_is_compatible("rockchip,rk3566") &&
!of_machine_is_compatible("rockchip,rk3568"))
if (!of_machine_compatible_match(dma_32bit_impaired_platforms))
return false;
gfp_flags_quirk |= GFP_DMA32;
@ -4968,14 +4980,12 @@ static const struct gic_quirk its_quirks[] = {
.property = "dma-noncoherent",
.init = its_set_non_coherent,
},
#ifdef CONFIG_ROCKCHIP_ERRATUM_3568002
{
.desc = "ITS: Rockchip erratum RK3568002",
.desc = "ITS: Broken GIC600 integration limited to 32bit PA",
.iidr = 0x0201743b,
.mask = 0xffffffff,
.init = its_enable_rk3568002,
.init = its_enable_dma32,
},
#endif
{
}
};

View File

@ -346,7 +346,7 @@ config PCIE_RCAR_GEN4
config PCIE_RCAR_GEN4_HOST
tristate "Renesas R-Car Gen4 PCIe controller (host mode)"
depends on ARCH_RENESAS || COMPILE_TEST
depends on ARM64 && (ARCH_RENESAS || COMPILE_TEST)
depends on PCI_MSI
select PCIE_DW_HOST
select PCIE_RCAR_GEN4
@ -357,7 +357,7 @@ config PCIE_RCAR_GEN4_HOST
config PCIE_RCAR_GEN4_EP
tristate "Renesas R-Car Gen4 PCIe controller (endpoint mode)"
depends on ARCH_RENESAS || COMPILE_TEST
depends on ARM64 && (ARCH_RENESAS || COMPILE_TEST)
depends on PCI_ENDPOINT
select PCIE_DW_EP
select PCIE_RCAR_GEN4

View File

@ -587,6 +587,12 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
return ret;
if (pci_msi_enabled()) {
pp->use_imsi_rx = !(pp->ops->msi_init ||
of_property_present(np, "msi-parent") ||
of_property_present(np, "msi-map"));
}
if (pp->ops->init) {
ret = pp->ops->init(pp);
if (ret)
@ -594,10 +600,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
}
if (pci_msi_enabled()) {
pp->use_imsi_rx = !(pp->ops->msi_init ||
of_property_present(np, "msi-parent") ||
of_property_present(np, "msi-map"));
/*
* For the use_imsi_rx case the default assignment is handled
* in the dw_pcie_msi_host_init().

View File

@ -13,8 +13,11 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/irqchip/arm-gic-v3.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@ -31,6 +34,10 @@
#define DEVICE_TYPE_RC BIT(4)
#define BIFUR_MOD_SET_ON BIT(0)
/* MSI Capability */
#define MSICAP0 0x0050
#define MSICAP0_MSIE BIT(16)
/* PCIe Interrupt Status 0 */
#define PCIEINTSTS0 0x0084
@ -55,6 +62,14 @@
#define APP_HOLD_PHY_RST BIT(16)
#define APP_LTSSM_ENABLE BIT(0)
/* INTC address */
#define AXIINTCADDR 0x0a00
/* INTC control & mask */
#define AXIINTCCONT 0x0a04
#define INTC_EN BIT(31)
#define INTC_MASK GENMASK(11, 3)
/* PCIe Power Management Control */
#define PCIEPWRMNGCTRL 0x0070
#define APP_CLK_REQ_N BIT(11)
@ -305,13 +320,103 @@ static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev)
return rcar;
}
static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct device_node *msi_node = NULL;
struct device *dev = dw->dev;
struct resource res;
u64 addr;
int ret;
/*
* Either the "msi-parent" or the "msi-map" phandle needs to exist
* to obtain the MSI node.
*/
of_msi_xlate(dev, &msi_node, 0);
if (!msi_node)
return -ENODEV;
/* Check if "msi-parent" or the "msi-map" points to ARM GICv3 ITS. */
if (!of_device_is_compatible(msi_node, "arm,gic-v3-its"))
return dev_err_probe(dev, -ENODEV, "Compatible MSI controller not found\n");
/* Derive GITS_TRANSLATER address from GICv3 */
ret = of_address_to_resource(msi_node, 0, &res);
if (ret < 0)
return dev_err_probe(dev, ret, "MSI controller resources not obtained\n");
addr = res.start + GITS_TRANSLATER;
if (addr >= SZ_4G)
return dev_err_probe(dev, -EINVAL, "MSI controller address above 32bit range\n");
*msi_addr = addr;
return 0;
}
static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
u32 val;
int ret;
/* Make sure MSICAP0 MSIE is configured. */
val = dw_pcie_readl_dbi(dw, MSICAP0);
if (pci_msi_enabled())
val |= MSICAP0_MSIE;
else
val &= ~MSICAP0_MSIE;
dw_pcie_writel_dbi(dw, MSICAP0, val);
if (!pci_msi_enabled() || pp->use_imsi_rx) {
/* Clear AXIINTC mapping. */
writel(0, rcar->base + AXIINTCADDR);
writel(0, rcar->base + AXIINTCCONT);
} else {
ret = rcar_gen4_pcie_host_msi_addr(pp, &val);
if (ret)
goto err;
/* Point AXIINTC to GIC ITS and enable. */
writel(val, rcar->base + AXIINTCADDR);
writel(INTC_EN | INTC_MASK, rcar->base + AXIINTCCONT);
}
/* Configure MSI interrupt signal */
val = readl(rcar->base + PCIEINTSTS0EN);
if (pci_msi_enabled())
val |= MSI_CTRL_INT;
else
val &= ~MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
return 0;
err:
/* Deconfigure MSICAP0 MSIE. */
val = dw_pcie_readl_dbi(dw, MSICAP0);
val &= ~MSICAP0_MSIE;
dw_pcie_writel_dbi(dw, MSICAP0, val);
/* Clear AXIINTC mapping. */
writel(0, rcar->base + AXIINTCADDR);
writel(0, rcar->base + AXIINTCCONT);
/* Deconfigure MSI interrupt signal */
val = readl(rcar->base + PCIEINTSTS0EN);
val &= ~MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
return ret;
}
/* Host mode */
static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
u32 val;
gpiod_set_value_cansleep(dw->pe_rst, 1);
@ -328,16 +433,19 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_0, 0x0);
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_1, 0x0);
/* Enable MSI interrupt signal */
val = readl(rcar->base + PCIEINTSTS0EN);
val |= MSI_CTRL_INT;
writel(val, rcar->base + PCIEINTSTS0EN);
ret = rcar_gen4_pcie_host_msi_init(pp);
if (ret)
goto err;
msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
gpiod_set_value_cansleep(dw->pe_rst, 0);
return 0;
err:
rcar_gen4_pcie_common_deinit(rcar);
return ret;
}
static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)