PCI: keystone: Add support to build as a loadable module

The 'pci-keystone.c' driver is the application/glue/wrapper driver for the
Designware PCIe Controllers on TI SoCs. Now that all of the helper APIs
that the 'pci-keystone.c' driver depends upon have been exported for use,
enable support to build the driver as a loadable module.

When building the driver as a module, the functions marked by the '__init'
keyword may be invoked after the init memory has been freed by the kernel.
This results will result in an exception of the form:

  Unable to handle kernel paging request at virtual address ...
  Mem abort info:
  ...
  pc : ks_pcie_host_init+0x0/0x540
  lr : dw_pcie_host_init+0x170/0x498
  ...
  ks_pcie_host_init+0x0/0x540 (P)
  ks_pcie_probe+0x728/0x84c
  platform_probe+0x5c/0x98
  really_probe+0xbc/0x29c
  __driver_probe_device+0x78/0x12c
  driver_probe_device+0xd8/0x15c

To address this, introduce a new function namely 'ks_pcie_init()' to
register the 'fault handler' while removing the '__init' keyword from
existing functions.

Note that hook_fault_code() is defined as '__init' function. Since the init
functions should never be called during runtime (after init memory freeing
stage), the driver is made as a built-in if CONFIG_ARM (where
hook_fault_code() is used) is selected.

Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
[mani: added a note about hook_fault_code()]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Link: https://patch.msgid.link/20251029080547.1253757-5-s-vadapalli@ti.com
This commit is contained in:
Siddharth Vadapalli 2025-10-29 13:34:52 +05:30 committed by Bjorn Helgaas
parent 5aa84c034a
commit bc10d0ad54
3 changed files with 59 additions and 37 deletions

View File

@ -482,15 +482,21 @@ config PCI_DRA7XX_EP
to enable device-specific features PCI_DRA7XX_EP must be selected. to enable device-specific features PCI_DRA7XX_EP must be selected.
This uses the DesignWare core. This uses the DesignWare core.
# ARM32 platforms use hook_fault_code() and cannot support loadable module.
config PCI_KEYSTONE config PCI_KEYSTONE
bool bool
# On non-ARM32 platforms, loadable module can be supported.
config PCI_KEYSTONE_TRISTATE
tristate
config PCI_KEYSTONE_HOST config PCI_KEYSTONE_HOST
bool "TI Keystone PCIe controller (host mode)" tristate "TI Keystone PCIe controller (host mode)"
depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
depends on PCI_MSI depends on PCI_MSI
select PCIE_DW_HOST select PCIE_DW_HOST
select PCI_KEYSTONE select PCI_KEYSTONE if ARM
select PCI_KEYSTONE_TRISTATE if !ARM
help help
Enables support for the PCIe controller in the Keystone SoC to Enables support for the PCIe controller in the Keystone SoC to
work in host mode. The PCI controller on Keystone is based on work in host mode. The PCI controller on Keystone is based on
@ -498,11 +504,12 @@ config PCI_KEYSTONE_HOST
DesignWare core functions to implement the driver. DesignWare core functions to implement the driver.
config PCI_KEYSTONE_EP config PCI_KEYSTONE_EP
bool "TI Keystone PCIe controller (endpoint mode)" tristate "TI Keystone PCIe controller (endpoint mode)"
depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
depends on PCI_ENDPOINT depends on PCI_ENDPOINT
select PCIE_DW_EP select PCIE_DW_EP
select PCI_KEYSTONE select PCI_KEYSTONE if ARM
select PCI_KEYSTONE_TRISTATE if !ARM
help help
Enables support for the PCIe controller in the Keystone SoC to Enables support for the PCIe controller in the Keystone SoC to
work in endpoint mode. The PCI controller on Keystone is based work in endpoint mode. The PCI controller on Keystone is based

View File

@ -11,7 +11,10 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
# ARM32 platforms use hook_fault_code() and cannot support loadable module.
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
# On non-ARM32 platforms, loadable module can be supported.
obj-$(CONFIG_PCI_KEYSTONE_TRISTATE) += pci-keystone.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o
obj-$(CONFIG_PCIE_QCOM_COMMON) += pcie-qcom-common.o obj-$(CONFIG_PCIE_QCOM_COMMON) += pcie-qcom-common.o

View File

@ -17,6 +17,7 @@
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
@ -777,29 +778,7 @@ static int ks_pcie_config_intx_irq(struct keystone_pcie *ks_pcie)
return ret; return ret;
} }
#ifdef CONFIG_ARM static int ks_pcie_init_id(struct keystone_pcie *ks_pcie)
/*
* When a PCI device does not exist during config cycles, keystone host
* gets a bus error instead of returning 0xffffffff (PCI_ERROR_RESPONSE).
* This handler always returns 0 for this kind of fault.
*/
static int ks_pcie_fault(unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
{
unsigned long instr = *(unsigned long *) instruction_pointer(regs);
if ((instr & 0x0e100090) == 0x00100090) {
int reg = (instr >> 12) & 15;
regs->uregs[reg] = -1;
regs->ARM_pc += 4;
}
return 0;
}
#endif
static int __init ks_pcie_init_id(struct keystone_pcie *ks_pcie)
{ {
int ret; int ret;
unsigned int id; unsigned int id;
@ -831,7 +810,7 @@ static int __init ks_pcie_init_id(struct keystone_pcie *ks_pcie)
return 0; return 0;
} }
static int __init ks_pcie_host_init(struct dw_pcie_rp *pp) static int ks_pcie_host_init(struct dw_pcie_rp *pp)
{ {
struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
@ -861,15 +840,6 @@ static int __init ks_pcie_host_init(struct dw_pcie_rp *pp)
if (ret < 0) if (ret < 0)
return ret; return ret;
#ifdef CONFIG_ARM
/*
* PCIe access errors that result into OCP errors are caught by ARM as
* "External aborts"
*/
hook_fault_code(17, ks_pcie_fault, SIGBUS, 0,
"Asynchronous external abort");
#endif
return 0; return 0;
} }
@ -1134,6 +1104,7 @@ static const struct of_device_id ks_pcie_of_match[] = {
}, },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, ks_pcie_of_match);
static int ks_pcie_probe(struct platform_device *pdev) static int ks_pcie_probe(struct platform_device *pdev)
{ {
@ -1381,4 +1352,45 @@ static struct platform_driver ks_pcie_driver = {
.of_match_table = ks_pcie_of_match, .of_match_table = ks_pcie_of_match,
}, },
}; };
#ifdef CONFIG_ARM
/*
* When a PCI device does not exist during config cycles, keystone host
* gets a bus error instead of returning 0xffffffff (PCI_ERROR_RESPONSE).
* This handler always returns 0 for this kind of fault.
*/
static int ks_pcie_fault(unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
{
unsigned long instr = *(unsigned long *)instruction_pointer(regs);
if ((instr & 0x0e100090) == 0x00100090) {
int reg = (instr >> 12) & 15;
regs->uregs[reg] = -1;
regs->ARM_pc += 4;
}
return 0;
}
static int __init ks_pcie_init(void)
{
/*
* PCIe access errors that result into OCP errors are caught by ARM as
* "External aborts"
*/
if (of_find_matching_node(NULL, ks_pcie_of_match))
hook_fault_code(17, ks_pcie_fault, SIGBUS, 0,
"Asynchronous external abort");
return platform_driver_register(&ks_pcie_driver);
}
device_initcall(ks_pcie_init);
#else
builtin_platform_driver(ks_pcie_driver); builtin_platform_driver(ks_pcie_driver);
#endif
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PCIe controller driver for Texas Instruments Keystone SoCs");
MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>");