* [PATCH RFC 0/3] PCI: rockchip-host: Handle unavailable downstream config space
@ 2026-11-14 0:28 ` Matt Thompson via B4 Relay
0 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:28 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
On a RockPro64 v2.1 with a Kintex-7 FPGA endpoint, a downstream
vendor-ID read during a PCI rescan raised a synchronous external
abort (ESR 0x96000210) in rockchip_pcie_rd_conf(). The rescan task
exited with IRQs disabled and a watchdog lockup followed.
The first patch skips downstream config accesses while the controller
reports the link unavailable. The remaining patches handle synchronous
external aborts on explicitly annotated arm64 config reads, including
when the link changes after the status check. The final patch also
clears the AXI link-down latch before downstream config accesses
once the controller reports the link ready.
The arm64 change is limited to marked load instructions. Asynchronous
SError, BAR accesses, and config writes that race a link change are
outside that recovery path. Feedback on the exception-table interface
and whether the controller can handle absent completions directly is
welcome.
A related discussion covers an RK3399 endpoint that trains but fails
to answer config requests:
https://lore.kernel.org/linux-pci/20261024-rockchip-pcie-resume-v1-0-58e54a5cf5f1@iav.lv/
Validation: applied these three RFC patches to upstream commit
97d73c8251226cf27cc6656b4c542869bb8c69fa and booted the resulting
7.4.0-rc2 kernel on a RockPro64 v2.1. The test config enabled Rust
and built in the required board drivers.
Three test cycles completed on that kernel. Each removed the FPGA
from Linux, disabled the link and rescanned, then re-enabled the link,
requested retraining and rescanned. The FPGA stayed absent in each
disabled scan and returned at 01:00.0 after each retrain without a host
driver rebind. The log showed no synchronous external abort or
watchdog lockup. The test did not establish whether the arm64 fixup ran.
---
Matt Thompson (3):
PCI: rockchip-host: Skip downstream config access while link is down
arm64: Allow synchronous abort fixups for guarded MMIO reads
PCI: rockchip-host: Recover downstream configuration access
arch/arm64/include/asm/asm-extable.h | 9 ++++
arch/arm64/mm/extable.c | 1 +
arch/arm64/mm/fault.c | 11 +++++
drivers/pci/controller/pcie-rockchip-host.c | 69 +++++++++++++++++++++++++++++
drivers/pci/controller/pcie-rockchip.h | 1 +
5 files changed, 91 insertions(+)
---
base-commit: a72c40386d63746568fb027900faaa8ec0054127
change-id: 20261113-rfc-b4-6b795b536d9a
Best regards,
--
Matt Thompson <matt@boondocker.io>
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH RFC 0/3] PCI: rockchip-host: Handle unavailable downstream config space
@ 2026-11-14 0:28 ` Matt Thompson via B4 Relay
0 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:28 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
On a RockPro64 v2.1 with a Kintex-7 FPGA endpoint, a downstream
vendor-ID read during a PCI rescan raised a synchronous external
abort (ESR 0x96000210) in rockchip_pcie_rd_conf(). The rescan task
exited with IRQs disabled and a watchdog lockup followed.
The first patch skips downstream config accesses while the controller
reports the link unavailable. The remaining patches handle synchronous
external aborts on explicitly annotated arm64 config reads, including
when the link changes after the status check. The final patch also
clears the AXI link-down latch before downstream config accesses
once the controller reports the link ready.
The arm64 change is limited to marked load instructions. Asynchronous
SError, BAR accesses, and config writes that race a link change are
outside that recovery path. Feedback on the exception-table interface
and whether the controller can handle absent completions directly is
welcome.
A related discussion covers an RK3399 endpoint that trains but fails
to answer config requests:
https://lore.kernel.org/linux-pci/20261024-rockchip-pcie-resume-v1-0-58e54a5cf5f1@iav.lv/
Validation: applied these three RFC patches to upstream commit
97d73c8251226cf27cc6656b4c542869bb8c69fa and booted the resulting
7.4.0-rc2 kernel on a RockPro64 v2.1. The test config enabled Rust
and built in the required board drivers.
Three test cycles completed on that kernel. Each removed the FPGA
from Linux, disabled the link and rescanned, then re-enabled the link,
requested retraining and rescanned. The FPGA stayed absent in each
disabled scan and returned at 01:00.0 after each retrain without a host
driver rebind. The log showed no synchronous external abort or
watchdog lockup. The test did not establish whether the arm64 fixup ran.
---
Matt Thompson (3):
PCI: rockchip-host: Skip downstream config access while link is down
arm64: Allow synchronous abort fixups for guarded MMIO reads
PCI: rockchip-host: Recover downstream configuration access
arch/arm64/include/asm/asm-extable.h | 9 ++++
arch/arm64/mm/extable.c | 1 +
arch/arm64/mm/fault.c | 11 +++++
drivers/pci/controller/pcie-rockchip-host.c | 69 +++++++++++++++++++++++++++++
drivers/pci/controller/pcie-rockchip.h | 1 +
5 files changed, 91 insertions(+)
---
base-commit: a72c40386d63746568fb027900faaa8ec0054127
change-id: 20261113-rfc-b4-6b795b536d9a
Best regards,
--
Matt Thompson <matt@boondocker.io>
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply [flat|nested] 15+ messages in thread* [PATCH RFC 1/3] PCI: rockchip-host: Skip downstream config access while link is down
2026-11-14 0:28 ` Matt Thompson via B4 Relay
(?)
@ 2026-11-14 0:28 ` Matt Thompson
-1 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:28 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] 15+ messages in thread* [PATCH RFC 1/3] PCI: rockchip-host: Skip downstream config access while link is down
@ 2026-11-14 0:28 ` Matt Thompson
0 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:28 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)
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC 1/3] PCI: rockchip-host: Skip downstream config access while link is down
@ 2026-11-14 0:28 ` Matt Thompson
0 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson @ 2026-11-14 0:28 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
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] 15+ messages in thread* Re: [PATCH RFC 1/3] PCI: rockchip-host: Skip downstream config access while link is down
2026-11-14 0:28 ` Matt Thompson
(?)
(?)
@ 2026-11-14 0:38 ` sashiko-bot
-1 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-11-14 0:38 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 d2a13e40a12b2c5046df485daa4e6e09d60bef0c
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]
Since this fixes a fault and lockup when rockchip_pcie_rd_other_conf() or
rockchip_pcie_wr_other_conf() accesses config space while the PCIe link is
down, should it include a Fixes: tag?
Fixes: e77f847df54c ("PCI: rockchip: Add Rockchip PCIe controller support")
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-rfc-b4-v1-0-b91dcb502250@boondocker.io?part=1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH RFC 2/3] arm64: Allow synchronous abort fixups for guarded MMIO reads
2026-11-14 0:28 ` Matt Thompson via B4 Relay
(?)
@ 2026-11-14 0:28 ` Matt Thompson
-1 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:28 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 | 9 +++++++++
arch/arm64/mm/extable.c | 1 +
arch/arm64/mm/fault.c | 11 +++++++++++
3 files changed, 21 insertions(+)
diff --git a/arch/arm64/include/asm/asm-extable.h b/arch/arm64/include/asm/asm-extable.h
index d67e2fd..ed80aec 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
@@ -123,6 +124,14 @@
#define _ASM_EXTABLE_KACCESS(insn, fixup) \
_ASM_EXTABLE_KACCESS_ERR_ZERO(insn, fixup, wzr, wzr)
+#define _ASM_EXTABLE_MMIO_READ_ERR_ZERO(insn, fixup, err, zero) \
+ __ASM_EXTABLE_RAW(insn, fixup, \
+ __stringify(EX_TYPE_MMIO_READ_ERR_ZERO), \
+ "((.L__gpr_num_" err " << " \
+ __stringify(EX_DATA_REG_ERR_SHIFT) ") | " \
+ "(.L__gpr_num_" zero " << " \
+ __stringify(EX_DATA_REG_ZERO_SHIFT) "))")
+
#define _ASM_EXTABLE_LOAD_UNALIGNED_ZEROPAD(insn, fixup, data, addr) \
__DEFINE_ASM_GPR_NUMS \
__ASM_EXTABLE_RAW(#insn, #fixup, \
diff --git a/arch/arm64/mm/extable.c b/arch/arm64/mm/extable.c
index 299a244..c9b5944 100644
--- a/arch/arm64/mm/extable.c
+++ b/arch/arm64/mm/extable.c
@@ -100,6 +100,7 @@ bool fixup_exception(struct pt_regs *regs, unsigned long esr)
return ex_handler_bpf(ex, regs);
case EX_TYPE_UACCESS_ERR_ZERO:
case EX_TYPE_KACCESS_ERR_ZERO:
+ case EX_TYPE_MMIO_READ_ERR_ZERO:
return ex_handler_uaccess_err_zero(ex, regs);
case EX_TYPE_UACCESS_CPY:
return ex_handler_uaccess_cpy(ex, regs, esr);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index b227368..148f7c9 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -877,8 +877,19 @@ static int do_bad(unsigned long far, unsigned long esr, struct pt_regs *regs)
static int do_sea(unsigned long far, unsigned long esr, struct pt_regs *regs)
{
const struct fault_info *inf;
+ const struct exception_table_entry *ex;
unsigned long siaddr;
+ /* Only a synchronous data abort on an explicitly guarded MMIO read. */
+ if (ESR_ELx_EC(esr) == ESR_ELx_EC_DABT_CUR &&
+ (esr & ESR_ELx_FSC) == ESR_ELx_FSC_EXTABT &&
+ !(esr & ESR_ELx_WNR)) {
+ ex = search_exception_tables(instruction_pointer(regs));
+ if (ex && ex->type == EX_TYPE_MMIO_READ_ERR_ZERO &&
+ fixup_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] 15+ messages in thread* [PATCH RFC 2/3] arm64: Allow synchronous abort fixups for guarded MMIO reads
@ 2026-11-14 0:28 ` Matt Thompson
0 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:28 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 | 9 +++++++++
arch/arm64/mm/extable.c | 1 +
arch/arm64/mm/fault.c | 11 +++++++++++
3 files changed, 21 insertions(+)
diff --git a/arch/arm64/include/asm/asm-extable.h b/arch/arm64/include/asm/asm-extable.h
index d67e2fd..ed80aec 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
@@ -123,6 +124,14 @@
#define _ASM_EXTABLE_KACCESS(insn, fixup) \
_ASM_EXTABLE_KACCESS_ERR_ZERO(insn, fixup, wzr, wzr)
+#define _ASM_EXTABLE_MMIO_READ_ERR_ZERO(insn, fixup, err, zero) \
+ __ASM_EXTABLE_RAW(insn, fixup, \
+ __stringify(EX_TYPE_MMIO_READ_ERR_ZERO), \
+ "((.L__gpr_num_" err " << " \
+ __stringify(EX_DATA_REG_ERR_SHIFT) ") | " \
+ "(.L__gpr_num_" zero " << " \
+ __stringify(EX_DATA_REG_ZERO_SHIFT) "))")
+
#define _ASM_EXTABLE_LOAD_UNALIGNED_ZEROPAD(insn, fixup, data, addr) \
__DEFINE_ASM_GPR_NUMS \
__ASM_EXTABLE_RAW(#insn, #fixup, \
diff --git a/arch/arm64/mm/extable.c b/arch/arm64/mm/extable.c
index 299a244..c9b5944 100644
--- a/arch/arm64/mm/extable.c
+++ b/arch/arm64/mm/extable.c
@@ -100,6 +100,7 @@ bool fixup_exception(struct pt_regs *regs, unsigned long esr)
return ex_handler_bpf(ex, regs);
case EX_TYPE_UACCESS_ERR_ZERO:
case EX_TYPE_KACCESS_ERR_ZERO:
+ case EX_TYPE_MMIO_READ_ERR_ZERO:
return ex_handler_uaccess_err_zero(ex, regs);
case EX_TYPE_UACCESS_CPY:
return ex_handler_uaccess_cpy(ex, regs, esr);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index b227368..148f7c9 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -877,8 +877,19 @@ static int do_bad(unsigned long far, unsigned long esr, struct pt_regs *regs)
static int do_sea(unsigned long far, unsigned long esr, struct pt_regs *regs)
{
const struct fault_info *inf;
+ const struct exception_table_entry *ex;
unsigned long siaddr;
+ /* Only a synchronous data abort on an explicitly guarded MMIO read. */
+ if (ESR_ELx_EC(esr) == ESR_ELx_EC_DABT_CUR &&
+ (esr & ESR_ELx_FSC) == ESR_ELx_FSC_EXTABT &&
+ !(esr & ESR_ELx_WNR)) {
+ ex = search_exception_tables(instruction_pointer(regs));
+ if (ex && ex->type == EX_TYPE_MMIO_READ_ERR_ZERO &&
+ fixup_exception(regs, esr))
+ return 0;
+ }
+
inf = esr_to_fault_info(esr);
if (user_mode(regs) && apei_claim_sea(regs) == 0) {
--
Git-155)
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC 2/3] arm64: Allow synchronous abort fixups for guarded MMIO reads
@ 2026-11-14 0:28 ` Matt Thompson
0 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson @ 2026-11-14 0:28 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
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 | 9 +++++++++
arch/arm64/mm/extable.c | 1 +
arch/arm64/mm/fault.c | 11 +++++++++++
3 files changed, 21 insertions(+)
diff --git a/arch/arm64/include/asm/asm-extable.h b/arch/arm64/include/asm/asm-extable.h
index d67e2fd..ed80aec 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
@@ -123,6 +124,14 @@
#define _ASM_EXTABLE_KACCESS(insn, fixup) \
_ASM_EXTABLE_KACCESS_ERR_ZERO(insn, fixup, wzr, wzr)
+#define _ASM_EXTABLE_MMIO_READ_ERR_ZERO(insn, fixup, err, zero) \
+ __ASM_EXTABLE_RAW(insn, fixup, \
+ __stringify(EX_TYPE_MMIO_READ_ERR_ZERO), \
+ "((.L__gpr_num_" err " << " \
+ __stringify(EX_DATA_REG_ERR_SHIFT) ") | " \
+ "(.L__gpr_num_" zero " << " \
+ __stringify(EX_DATA_REG_ZERO_SHIFT) "))")
+
#define _ASM_EXTABLE_LOAD_UNALIGNED_ZEROPAD(insn, fixup, data, addr) \
__DEFINE_ASM_GPR_NUMS \
__ASM_EXTABLE_RAW(#insn, #fixup, \
diff --git a/arch/arm64/mm/extable.c b/arch/arm64/mm/extable.c
index 299a244..c9b5944 100644
--- a/arch/arm64/mm/extable.c
+++ b/arch/arm64/mm/extable.c
@@ -100,6 +100,7 @@ bool fixup_exception(struct pt_regs *regs, unsigned long esr)
return ex_handler_bpf(ex, regs);
case EX_TYPE_UACCESS_ERR_ZERO:
case EX_TYPE_KACCESS_ERR_ZERO:
+ case EX_TYPE_MMIO_READ_ERR_ZERO:
return ex_handler_uaccess_err_zero(ex, regs);
case EX_TYPE_UACCESS_CPY:
return ex_handler_uaccess_cpy(ex, regs, esr);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index b227368..148f7c9 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -877,8 +877,19 @@ static int do_bad(unsigned long far, unsigned long esr, struct pt_regs *regs)
static int do_sea(unsigned long far, unsigned long esr, struct pt_regs *regs)
{
const struct fault_info *inf;
+ const struct exception_table_entry *ex;
unsigned long siaddr;
+ /* Only a synchronous data abort on an explicitly guarded MMIO read. */
+ if (ESR_ELx_EC(esr) == ESR_ELx_EC_DABT_CUR &&
+ (esr & ESR_ELx_FSC) == ESR_ELx_FSC_EXTABT &&
+ !(esr & ESR_ELx_WNR)) {
+ ex = search_exception_tables(instruction_pointer(regs));
+ if (ex && ex->type == EX_TYPE_MMIO_READ_ERR_ZERO &&
+ fixup_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] 15+ messages in thread* Re: [PATCH RFC 2/3] arm64: Allow synchronous abort fixups for guarded MMIO reads
2026-11-14 0:28 ` Matt Thompson
(?)
(?)
@ 2026-11-14 0:41 ` sashiko-bot
-1 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-11-14 0:41 UTC (permalink / raw)
To: Matt Thompson; +Cc: linux-pci
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] arm64: extable: fixup_exception() and insn_may_access_user()
match non-SEA faults
- [Low] arm64: extable: missing __DEFINE_ASM_GPR_NUMS in
_ASM_EXTABLE_MMIO_READ_ERR_ZERO()
--
commit 1c63c0d5426ef0604198833160287cf9aa8cf068
Author: Matt Thompson <matt@boondocker.io>
arm64: Allow synchronous abort fixups for guarded MMIO reads
This commit adds the EX_TYPE_MMIO_READ_ERR_ZERO exception table type and
hooks it into do_sea() and fixup_exception() to fix up synchronous
external data aborts on guarded MMIO read instructions.
> diff --git a/arch/arm64/include/asm/asm-extable.h b/arch/arm64/include/asm/asm-extable.h
> index d67e2fdd1aee5..ed80aec162798 100644
> --- a/arch/arm64/include/asm/asm-extable.h
> +++ b/arch/arm64/include/asm/asm-extable.h
[ ... ]
> @@ -123,6 +124,14 @@
> #define _ASM_EXTABLE_KACCESS(insn, fixup) \
> _ASM_EXTABLE_KACCESS_ERR_ZERO(insn, fixup, wzr, wzr)
>
> +#define _ASM_EXTABLE_MMIO_READ_ERR_ZERO(insn, fixup, err, zero) \
> + __ASM_EXTABLE_RAW(insn, fixup, \
> + __stringify(EX_TYPE_MMIO_READ_ERR_ZERO), \
> + "((.L__gpr_num_" err " << " \
> + __stringify(EX_DATA_REG_ERR_SHIFT) ") | " \
> + "(.L__gpr_num_" zero " << " \
> + __stringify(EX_DATA_REG_ZERO_SHIFT) "))")
> +
[Severity: Low]
Should _ASM_EXTABLE_MMIO_READ_ERR_ZERO() emit __DEFINE_ASM_GPR_NUMS and
use #insn, #fixup and EX_DATA_REG() like _ASM_EXTABLE_KACCESS_ERR_ZERO()?
Without __DEFINE_ASM_GPR_NUMS, any C file using this macro fails to
assemble unless it defines the .L__gpr_num_* symbols itself, and callers
have to pass pre-quoted string literals for the labels and register
names.
> diff --git a/arch/arm64/mm/extable.c b/arch/arm64/mm/extable.c
> index 299a244f68e60..c9b59449fbb24 100644
> --- a/arch/arm64/mm/extable.c
> +++ b/arch/arm64/mm/extable.c
> @@ -100,6 +100,7 @@ bool fixup_exception(struct pt_regs *regs, unsigned long esr)
> return ex_handler_bpf(ex, regs);
> case EX_TYPE_UACCESS_ERR_ZERO:
> case EX_TYPE_KACCESS_ERR_ZERO:
> + case EX_TYPE_MMIO_READ_ERR_ZERO:
> return ex_handler_uaccess_err_zero(ex, regs);
> case EX_TYPE_UACCESS_CPY:
> return ex_handler_uaccess_cpy(ex, regs, esr);
> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
> index b22736853fcd8..148f7c98ab444 100644
> --- a/arch/arm64/mm/fault.c
> +++ b/arch/arm64/mm/fault.c
> @@ -877,8 +877,19 @@ static int do_bad(unsigned long far, unsigned long esr, struct pt_regs *regs)
> static int do_sea(unsigned long far, unsigned long esr, struct pt_regs *regs)
> {
> const struct fault_info *inf;
> + const struct exception_table_entry *ex;
> unsigned long siaddr;
>
> + /* Only a synchronous data abort on an explicitly guarded MMIO read. */
> + if (ESR_ELx_EC(esr) == ESR_ELx_EC_DABT_CUR &&
> + (esr & ESR_ELx_FSC) == ESR_ELx_FSC_EXTABT &&
> + !(esr & ESR_ELx_WNR)) {
> + ex = search_exception_tables(instruction_pointer(regs));
> + if (ex && ex->type == EX_TYPE_MMIO_READ_ERR_ZERO &&
> + fixup_exception(regs, esr))
> + return 0;
> + }
[Severity: Medium]
Can fixup_exception() and insn_may_access_user() still match
EX_TYPE_MMIO_READ_ERR_ZERO on non-SEA faults?
The ESR check is in do_sea(), but fixup_exception() handles
EX_TYPE_MMIO_READ_ERR_ZERO unconditionally and is also called from
__do_kernel_fault() and do_gpf(). A translation or alignment fault on a
guarded MMIO read (such as a NULL or unmapped iomem pointer) will be
silently fixed up to -EFAULT instead of oopsing.
In addition, insn_may_access_user() returns true in its default case for
EX_TYPE_MMIO_READ_ERR_ZERO:
arch/arm64/mm/extable.c:insn_may_access_user() {
...
switch (ex->type) {
case EX_TYPE_UACCESS_CPY:
return cpy_faulted_on_uaccess(ex, esr);
default:
return true;
}
}
If a guarded MMIO read in process context faults on a mapped readable
TTBR0 address while PAN is active, do_page_fault() treats it as a valid
uaccess instead of calling die_kernel_fault(), and handle_mm_fault()
returns 0 without advancing regs->pc, causing an infinite fault loop.
do_sea() also calls search_exception_tables() before calling
fixup_exception(), which searches the exception tables a second time.
Checking esr in a dedicated handler in arch/arm64/mm/extable.c and
returning false for EX_TYPE_MMIO_READ_ERR_ZERO in insn_may_access_user()
would restrict the fixup to synchronous external read aborts across all
callers and avoid the duplicate lookup.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-rfc-b4-v1-0-b91dcb502250@boondocker.io?part=2
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH RFC 3/3] PCI: rockchip-host: Recover downstream configuration access
2026-11-14 0:28 ` Matt Thompson via B4 Relay
(?)
@ 2026-11-14 0:28 ` Matt Thompson
-1 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:28 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 | 63 +++++++++++++++++++++++++++--
drivers/pci/controller/pcie-rockchip.h | 1 +
2 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
index 35105c1..64c7da5 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,37 @@ 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(__DEFINE_ASM_GPR_NUMS \
+ "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 +199,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 +231,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 +260,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] 15+ messages in thread* [PATCH RFC 3/3] PCI: rockchip-host: Recover downstream configuration access
@ 2026-11-14 0:28 ` Matt Thompson
0 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson via B4 Relay @ 2026-11-14 0:28 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 | 63 +++++++++++++++++++++++++++--
drivers/pci/controller/pcie-rockchip.h | 1 +
2 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
index 35105c1..64c7da5 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,37 @@ 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(__DEFINE_ASM_GPR_NUMS \
+ "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 +199,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 +231,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 +260,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)
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC 3/3] PCI: rockchip-host: Recover downstream configuration access
@ 2026-11-14 0:28 ` Matt Thompson
0 siblings, 0 replies; 15+ messages in thread
From: Matt Thompson @ 2026-11-14 0:28 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
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 | 63 +++++++++++++++++++++++++++--
drivers/pci/controller/pcie-rockchip.h | 1 +
2 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
index 35105c1..64c7da5 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,37 @@ 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(__DEFINE_ASM_GPR_NUMS \
+ "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 +199,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 +231,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 +260,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] 15+ messages in thread* Re: [PATCH RFC 3/3] PCI: rockchip-host: Recover downstream configuration access
2026-11-14 0:28 ` Matt Thompson
(?)
(?)
@ 2026-11-14 0:36 ` sashiko-bot
-1 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-11-14 0:36 UTC (permalink / raw)
To: Matt Thompson; +Cc: linux-pci
> 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>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-rfc-b4-v1-0-b91dcb502250@boondocker.io?part=3
^ permalink raw reply [flat|nested] 15+ messages in thread