* [PATCH RFC v2 1/3] PCI: rockchip-host: Skip downstream config access while link is down
2026-11-14 0:52 [PATCH RFC v2 0/3] PCI: rockchip-host: Handle unavailable downstream config space Matt Thompson via B4 Relay
@ 2026-11-14 0:52 ` Matt Thompson via B4 Relay
2026-11-14 1:03 ` sashiko-bot
2026-11-14 0:52 ` [PATCH RFC v2 2/3] arm64: Allow synchronous abort fixups for guarded MMIO reads Matt Thompson via B4 Relay
2026-11-14 0:52 ` [PATCH RFC v2 3/3] PCI: rockchip-host: Recover downstream configuration access Matt Thompson via B4 Relay
2 siblings, 1 reply; 7+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:52 UTC (permalink / raw)
To: Shawn Lin, Bjorn Helgaas, Lorenzo Pieralisi,
Krzysztof Wilczyński, Manivannan Sadhasivam, Heiko Stuebner,
Catalin Marinas, Will Deacon
Cc: Rob Herring, Mark Rutland, linux-pci, linux-rockchip,
linux-arm-kernel, linux-kernel
From: Matt Thompson <matt@boondocker.io>
On a RockPro64, a PCI bus rescan with the downstream FPGA unavailable
faulted in rockchip_pcie_rd_conf() during a vendor ID read. The rescan
task exited with interrupts disabled and the watchdog reported a lockup.
Check the controller's link-up and link-training status before issuing
downstream config requests. Return PCIBIOS_DEVICE_NOT_FOUND while the
link is unavailable. Root port config access still uses the local
controller registers.
This check cannot prevent the link from dropping during the transaction.
Signed-off-by: Matt Thompson <matt@boondocker.io>
---
drivers/pci/controller/pcie-rockchip-host.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
index 5b071bd..35105c1 100644
--- a/drivers/pci/controller/pcie-rockchip-host.c
+++ b/drivers/pci/controller/pcie-rockchip-host.c
@@ -143,12 +143,23 @@ static int rockchip_pcie_wr_own_conf(struct rockchip_pcie *rockchip,
return PCIBIOS_SUCCESSFUL;
}
+static bool rockchip_pcie_downstream_ready(struct rockchip_pcie *rockchip)
+{
+ return PCIE_LINK_UP(rockchip_pcie_read(rockchip, PCIE_CLIENT_BASIC_STATUS1)) &&
+ PCIE_LINK_TRAINING_DONE(rockchip_pcie_read(rockchip, PCIE_CORE_CTRL));
+}
+
static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip,
struct pci_bus *bus, u32 devfn,
int where, int size, u32 *val)
{
void __iomem *addr;
+ if (!rockchip_pcie_downstream_ready(rockchip)) {
+ *val = ~0U;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
addr = rockchip->reg_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);
if (!IS_ALIGNED((uintptr_t)addr, size)) {
@@ -182,6 +193,9 @@ static int rockchip_pcie_wr_other_conf(struct rockchip_pcie *rockchip,
{
void __iomem *addr;
+ if (!rockchip_pcie_downstream_ready(rockchip))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
addr = rockchip->reg_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);
if (!IS_ALIGNED((uintptr_t)addr, size))
--
Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH RFC v2 1/3] PCI: rockchip-host: Skip downstream config access while link is down
2026-11-14 0:52 ` [PATCH RFC v2 1/3] PCI: rockchip-host: Skip downstream config access while link is down Matt Thompson via B4 Relay
@ 2026-11-14 1:03 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-11-14 1:03 UTC (permalink / raw)
To: Matt Thompson; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] PCI: rockchip-host: missing Fixes trailer for link-down config
access fix
--
commit 776ef94e954858f6a44d96ce2b3db1d4c94ca081
Author: Matt Thompson <matt@boondocker.io>
> PCI: rockchip-host: Skip downstream config access while link is down
>
> On a RockPro64, a PCI bus rescan with the downstream FPGA unavailable
> faulted in rockchip_pcie_rd_conf() during a vendor ID read. The rescan
> task exited with interrupts disabled and the watchdog reported a lockup.
>
> Check the controller's link-up and link-training status before issuing
> downstream config requests. Return PCIBIOS_DEVICE_NOT_FOUND while the
> link is unavailable. Root port config access still uses the local
> controller registers.
>
> This check cannot prevent the link from dropping during the transaction.
>
> Signed-off-by: Matt Thompson <matt@boondocker.io>
[Severity: Low]
This fixes a kernel fault when rockchip_pcie_rd_other_conf() or
rockchip_pcie_wr_other_conf() issues a downstream config access while the
link is down. Should this include:
Fixes: e77f847df54c ("PCI: rockchip: Add Rockchip PCIe controller support")
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-rfc-b4-v2-0-d6b14f95c1d6@boondocker.io?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH RFC v2 2/3] arm64: Allow synchronous abort fixups for guarded MMIO reads
2026-11-14 0:52 [PATCH RFC v2 0/3] PCI: rockchip-host: Handle unavailable downstream config space Matt Thompson via B4 Relay
2026-11-14 0:52 ` [PATCH RFC v2 1/3] PCI: rockchip-host: Skip downstream config access while link is down Matt Thompson via B4 Relay
@ 2026-11-14 0:52 ` Matt Thompson via B4 Relay
2026-11-14 1:02 ` sashiko-bot
2026-11-14 0:52 ` [PATCH RFC v2 3/3] PCI: rockchip-host: Recover downstream configuration access Matt Thompson via B4 Relay
2 siblings, 1 reply; 7+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:52 UTC (permalink / raw)
To: Shawn Lin, Bjorn Helgaas, Lorenzo Pieralisi,
Krzysztof Wilczyński, Manivannan Sadhasivam, Heiko Stuebner,
Catalin Marinas, Will Deacon
Cc: Rob Herring, Mark Rutland, linux-pci, linux-rockchip,
linux-arm-kernel, linux-kernel
From: Matt Thompson <matt@boondocker.io>
An RK3399 PCI configuration read can raise a synchronous external data
abort if it does not get a usable response. Allow an explicitly annotated
MMIO load to return an error in this case.
Match only a current-EL synchronous external abort on a read instruction
with the new exception table type. Keep existing handling for other
faults, including asynchronous SError and translation table walk aborts.
Signed-off-by: Matt Thompson <matt@boondocker.io>
---
arch/arm64/include/asm/asm-extable.h | 15 +++++++++++++++
arch/arm64/mm/extable.c | 20 ++++++++++++++++++++
arch/arm64/mm/fault.c | 4 ++++
3 files changed, 39 insertions(+)
diff --git a/arch/arm64/include/asm/asm-extable.h b/arch/arm64/include/asm/asm-extable.h
index d67e2fd..ac78167 100644
--- a/arch/arm64/include/asm/asm-extable.h
+++ b/arch/arm64/include/asm/asm-extable.h
@@ -11,6 +11,7 @@
#define EX_TYPE_KACCESS_ERR_ZERO 3
#define EX_TYPE_UACCESS_CPY 4
#define EX_TYPE_LOAD_UNALIGNED_ZEROPAD 5
+#define EX_TYPE_MMIO_READ_ERR_ZERO 6
/* Data fields for EX_TYPE_UACCESS_ERR_ZERO */
#define EX_DATA_REG_ERR_SHIFT 0
@@ -80,6 +81,11 @@
#else /* __ASSEMBLER__ */
#include <linux/stringify.h>
+#include <linux/types.h>
+
+struct pt_regs;
+
+bool fixup_sea_exception(struct pt_regs *regs, unsigned long esr);
#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
".pushsection __ex_table, \"a\"\n" \
@@ -111,6 +117,15 @@
EX_DATA_REG(ZERO, zero) \
")")
+#define _ASM_EXTABLE_MMIO_READ_ERR_ZERO(insn, fixup, err, zero) \
+ __DEFINE_ASM_GPR_NUMS \
+ __ASM_EXTABLE_RAW(#insn, #fixup, \
+ __stringify(EX_TYPE_MMIO_READ_ERR_ZERO), \
+ "(" \
+ EX_DATA_REG(ERR, err) " | " \
+ EX_DATA_REG(ZERO, zero) \
+ ")")
+
#define _ASM_EXTABLE_UACCESS_ERR(insn, fixup, err) \
_ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, wzr)
diff --git a/arch/arm64/mm/extable.c b/arch/arm64/mm/extable.c
index 299a244..dcb84d8 100644
--- a/arch/arm64/mm/extable.c
+++ b/arch/arm64/mm/extable.c
@@ -28,6 +28,8 @@ bool insn_may_access_user(unsigned long addr, unsigned long esr)
return false;
switch (ex->type) {
+ case EX_TYPE_MMIO_READ_ERR_ZERO:
+ return false;
case EX_TYPE_UACCESS_CPY:
return cpy_faulted_on_uaccess(ex, esr);
default:
@@ -101,6 +103,8 @@ bool fixup_exception(struct pt_regs *regs, unsigned long esr)
case EX_TYPE_UACCESS_ERR_ZERO:
case EX_TYPE_KACCESS_ERR_ZERO:
return ex_handler_uaccess_err_zero(ex, regs);
+ case EX_TYPE_MMIO_READ_ERR_ZERO:
+ return false;
case EX_TYPE_UACCESS_CPY:
return ex_handler_uaccess_cpy(ex, regs, esr);
case EX_TYPE_LOAD_UNALIGNED_ZEROPAD:
@@ -109,3 +113,19 @@ bool fixup_exception(struct pt_regs *regs, unsigned long esr)
BUG();
}
+
+bool fixup_sea_exception(struct pt_regs *regs, unsigned long esr)
+{
+ const struct exception_table_entry *ex;
+
+ if (ESR_ELx_EC(esr) != ESR_ELx_EC_DABT_CUR ||
+ (esr & ESR_ELx_FSC) != ESR_ELx_FSC_EXTABT ||
+ (esr & ESR_ELx_WNR))
+ return false;
+
+ ex = search_exception_tables(instruction_pointer(regs));
+ if (!ex || ex->type != EX_TYPE_MMIO_READ_ERR_ZERO)
+ return false;
+
+ return ex_handler_uaccess_err_zero(ex, regs);
+}
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index b227368..ebe8054 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -30,6 +30,7 @@
#include <linux/hugetlb.h>
#include <asm/acpi.h>
+#include <asm/asm-extable.h>
#include <asm/bug.h>
#include <asm/cmpxchg.h>
#include <asm/cpufeature.h>
@@ -879,6 +880,9 @@ static int do_sea(unsigned long far, unsigned long esr, struct pt_regs *regs)
const struct fault_info *inf;
unsigned long siaddr;
+ if (fixup_sea_exception(regs, esr))
+ return 0;
+
inf = esr_to_fault_info(esr);
if (user_mode(regs) && apei_claim_sea(regs) == 0) {
--
Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH RFC v2 3/3] PCI: rockchip-host: Recover downstream configuration access
2026-11-14 0:52 [PATCH RFC v2 0/3] PCI: rockchip-host: Handle unavailable downstream config space Matt Thompson via B4 Relay
2026-11-14 0:52 ` [PATCH RFC v2 1/3] PCI: rockchip-host: Skip downstream config access while link is down Matt Thompson via B4 Relay
2026-11-14 0:52 ` [PATCH RFC v2 2/3] arm64: Allow synchronous abort fixups for guarded MMIO reads Matt Thompson via B4 Relay
@ 2026-11-14 0:52 ` Matt Thompson via B4 Relay
2026-11-14 1:01 ` sashiko-bot
2 siblings, 1 reply; 7+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:52 UTC (permalink / raw)
To: Shawn Lin, Bjorn Helgaas, Lorenzo Pieralisi,
Krzysztof Wilczyński, Manivannan Sadhasivam, Heiko Stuebner,
Catalin Marinas, Will Deacon
Cc: Rob Herring, Mark Rutland, linux-pci, linux-rockchip,
linux-arm-kernel, linux-kernel
From: Matt Thompson <matt@boondocker.io>
The link status can change between the readiness check and a downstream
PCI configuration read. A link that reports up can also fail to return
a usable completion.
On arm64, annotate downstream loads for all three access widths so a
synchronous external abort returns PCIBIOS_DEVICE_NOT_FOUND with all-ones
data. Keep little-endian conversion and I/O read ordering on success.
After retraining, the controller can continue to block configuration
traffic until its AXI link-down latch is cleared. Clear the latch before
downstream config reads and writes once the link readiness checks pass.
Link: https://lore.kernel.org/linux-pci/20261024-rockchip-pcie-resume-v1-2-58e54a5cf5f1@iav.lv/
Signed-off-by: Matt Thompson <matt@boondocker.io>
---
drivers/pci/controller/pcie-rockchip-host.c | 62 +++++++++++++++++++++++++++--
drivers/pci/controller/pcie-rockchip.h | 1 +
2 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
index 35105c1..64ac6f4 100644
--- a/drivers/pci/controller/pcie-rockchip-host.c
+++ b/drivers/pci/controller/pcie-rockchip-host.c
@@ -29,6 +29,10 @@
#include "../pci.h"
#include "pcie-rockchip.h"
+#ifdef CONFIG_ARM64
+#include <asm/asm-extable.h>
+#endif
+
static void rockchip_pcie_enable_bw_int(struct rockchip_pcie *rockchip)
{
u32 status;
@@ -149,16 +153,36 @@ static bool rockchip_pcie_downstream_ready(struct rockchip_pcie *rockchip)
PCIE_LINK_TRAINING_DONE(rockchip_pcie_read(rockchip, PCIE_CORE_CTRL));
}
+#ifdef CONFIG_ARM64
+/*
+ * An absent completion can raise a synchronous external abort even when
+ * the link status indicated up immediately before the config transaction.
+ */
+#define rockchip_pcie_cfg_load(insn, addr, val) \
+ ({ \
+ int err = 0; \
+ asm volatile("1: " #insn " %w0, [%2]\n" \
+ "2:\n" \
+ _ASM_EXTABLE_MMIO_READ_ERR_ZERO(1b, 2b, \
+ %w1, %w0) \
+ : "=&r" (val), "+r" (err) \
+ : "r" (addr) : "memory"); \
+ err; \
+ })
+#endif
+
static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip,
struct pci_bus *bus, u32 devfn,
int where, int size, u32 *val)
{
void __iomem *addr;
+#ifdef CONFIG_ARM64
+ u32 data;
+ int err;
+#endif
- if (!rockchip_pcie_downstream_ready(rockchip)) {
- *val = ~0U;
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
+ if (!rockchip_pcie_downstream_ready(rockchip))
+ goto absent;
addr = rockchip->reg_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);
@@ -174,6 +198,28 @@ static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip,
rockchip_pcie_cfg_configuration_accesses(rockchip,
AXI_WRAPPER_TYPE1_CFG);
+ /* Clear the AXI link-down latch before downstream config traffic. */
+ rockchip_pcie_write(rockchip, 0, PCIE_CORE_AXI_LINK_DOWN);
+
+#ifdef CONFIG_ARM64
+ if (size == 4) {
+ err = rockchip_pcie_cfg_load(ldr, addr, data);
+ *val = le32_to_cpu((__force __le32)data);
+ } else if (size == 2) {
+ err = rockchip_pcie_cfg_load(ldrh, addr, data);
+ *val = le16_to_cpu((__force __le16)data);
+ } else if (size == 1) {
+ err = rockchip_pcie_cfg_load(ldrb, addr, data);
+ *val = data;
+ } else {
+ *val = 0;
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+ if (err)
+ goto absent;
+ /* Preserve the I/O read ordering of readl/readw/readb. */
+ __io_ar(*val);
+#else
if (size == 4) {
*val = readl(addr);
} else if (size == 2) {
@@ -184,7 +230,12 @@ static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip,
*val = 0;
return PCIBIOS_BAD_REGISTER_NUMBER;
}
+#endif
return PCIBIOS_SUCCESSFUL;
+
+absent:
+ *val = ~0U;
+ return PCIBIOS_DEVICE_NOT_FOUND;
}
static int rockchip_pcie_wr_other_conf(struct rockchip_pcie *rockchip,
@@ -208,6 +259,9 @@ static int rockchip_pcie_wr_other_conf(struct rockchip_pcie *rockchip,
rockchip_pcie_cfg_configuration_accesses(rockchip,
AXI_WRAPPER_TYPE1_CFG);
+ /* Clear the AXI link-down latch before downstream config traffic. */
+ rockchip_pcie_write(rockchip, 0, PCIE_CORE_AXI_LINK_DOWN);
+
if (size == 4)
writel(val, addr);
else if (size == 2)
diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h
index b5da156..1cc3621 100644
--- a/drivers/pci/controller/pcie-rockchip.h
+++ b/drivers/pci/controller/pcie-rockchip.h
@@ -164,6 +164,7 @@
#define MIN_AXI_ADDR_BITS_PASSED 8
#define PCIE_ADDR_MASK GENMASK_ULL(63, MIN_AXI_ADDR_BITS_PASSED)
#define PCIE_CORE_AXI_CONF_BASE 0xc00000
+#define PCIE_CORE_AXI_LINK_DOWN (PCIE_CORE_AXI_CONF_BASE + 0x824)
#define PCIE_CORE_OB_REGION_ADDR0 (PCIE_CORE_AXI_CONF_BASE + 0x0)
#define PCIE_CORE_OB_REGION_ADDR0_NUM_BITS 0x3f
#define PCIE_CORE_OB_REGION_ADDR0_LO_ADDR PCIE_ADDR_MASK
--
Git-155)
^ permalink raw reply related [flat|nested] 7+ messages in thread