Existing controller drivers (e.g., DWC, custom out-of-tree drivers)
duplicate logic for scanning PCI capability lists. This creates
maintenance burdens and risks inconsistencies.
To resolve this:
1. Add pci_generic_find_capability() and pci_generic_find_ext_capability()
in drivers/pci/pci.c, accepting controller-specific read functions
and device data as parameters.
2. Refactor dwc_pcie_find_capability() and similar functions to utilize
these new generic interfaces.
3. Update out-of-tree drivers to leverage the common implementation,
eliminating code duplication.
This approach:
- Centralizes critical PCI capability scanning logic
- Allows flexible adaptation to varied hardware access methods
- Reduces future maintenance overhead
- Aligns with kernel code reuse best practices
Tested with DWC PCIe controller and CDNS PCIe drivers.
Signed-off-by: Hans Zhang <18255117159@163.com>
---
drivers/pci/pci.c | 83 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci.h | 13 ++++++-
2 files changed, 95 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 869d204a70a3..d686d89d211e 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -612,6 +612,89 @@ u16 pci_find_ext_capability(struct pci_dev *dev, int cap)
}
EXPORT_SYMBOL_GPL(pci_find_ext_capability);
+/*
+ * These interfaces resemble the pci_find_*capability() interfaces, but these
+ * are for configuring host controllers, which are bridges *to* PCI devices but
+ * are not PCI devices themselves.
+ */
+static u8 __pci_generic_find_next_cap(void *priv, pci_generic_read_cfg read_cfg,
+ u8 cap_ptr, u8 cap)
+{
+ u8 cap_id, next_cap_ptr;
+ u16 reg;
+
+ if (!cap_ptr)
+ return 0;
+
+ reg = read_cfg(priv, cap_ptr, 2);
+ cap_id = (reg & 0x00ff);
+
+ if (cap_id > PCI_CAP_ID_MAX)
+ return 0;
+
+ if (cap_id == cap)
+ return cap_ptr;
+
+ next_cap_ptr = (reg & 0xff00) >> 8;
+ return __pci_generic_find_next_cap(priv, read_cfg, next_cap_ptr, cap);
+}
+
+u8 pci_generic_find_capability(void *priv, pci_generic_read_cfg read_cfg,
+ u8 cap)
+{
+ u8 next_cap_ptr;
+ u16 reg;
+
+ reg = read_cfg(priv, PCI_CAPABILITY_LIST, 2);
+ next_cap_ptr = (reg & 0x00ff);
+
+ return __pci_generic_find_next_cap(priv, read_cfg, next_cap_ptr, cap);
+}
+EXPORT_SYMBOL_GPL(pci_generic_find_capability);
+
+static u16 pci_generic_find_next_ext_capability(void *priv,
+ pci_generic_read_cfg read_cfg,
+ u16 start, u8 cap)
+{
+ u32 header;
+ int ttl;
+ int pos = PCI_CFG_SPACE_SIZE;
+
+ /* minimum 8 bytes per capability */
+ ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
+
+ if (start)
+ pos = start;
+
+ header = read_cfg(priv, pos, 4);
+ /*
+ * If we have no capabilities, this is indicated by cap ID,
+ * cap version and next pointer all being 0.
+ */
+ if (header == 0)
+ return 0;
+
+ while (ttl-- > 0) {
+ if (PCI_EXT_CAP_ID(header) == cap && pos != start)
+ return pos;
+
+ pos = PCI_EXT_CAP_NEXT(header);
+ if (pos < PCI_CFG_SPACE_SIZE)
+ break;
+
+ header = read_cfg(priv, pos, 4);
+ }
+
+ return 0;
+}
+
+u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
+ int cap)
+{
+ return pci_generic_find_next_ext_capability(priv, read_cfg, 0, cap);
+}
+EXPORT_SYMBOL_GPL(pci_generic_find_ext_capability);
+
/**
* pci_get_dsn - Read and return the 8-byte Device Serial Number
* @dev: PCI device to query
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 47b31ad724fa..48c2b45a4d04 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1205,6 +1205,11 @@ u8 pci_find_ht_capability(struct pci_dev *dev, int ht_cap);
u8 pci_find_next_ht_capability(struct pci_dev *dev, u8 pos, int ht_cap);
u16 pci_find_ext_capability(struct pci_dev *dev, int cap);
u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 pos, int cap);
+typedef u32 (*pci_generic_read_cfg)(void *priv, int where, int size);
+u8 pci_generic_find_capability(void *priv, pci_generic_read_cfg read_cfg,
+ u8 cap);
+u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
+ u8 cap);
struct pci_bus *pci_find_next_bus(const struct pci_bus *from);
u16 pci_find_vsec_capability(struct pci_dev *dev, u16 vendor, int cap);
u16 pci_find_dvsec_capability(struct pci_dev *dev, u16 vendor, u16 dvsec);
@@ -2012,7 +2017,13 @@ static inline u8 pci_find_next_capability(struct pci_dev *dev, u8 post, int cap)
{ return 0; }
static inline u16 pci_find_ext_capability(struct pci_dev *dev, int cap)
{ return 0; }
-
+typedef u32 (*pci_generic_read_cfg)(void *priv, int where, int size);
+u8 pci_generic_find_capability(void *priv, pci_generic_read_cfg read_cfg,
+ u8 cap)
+{ return 0; }
+u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
+ u8 cap)
+{ return 0; }
static inline u64 pci_get_dsn(struct pci_dev *dev)
{ return 0; }
--
2.25.1
Hi Hans,
kernel test robot noticed the following build errors:
[auto build test ERROR on a1cffe8cc8aef85f1b07c4464f0998b9785b795a]
url: https://github.com/intel-lab-lkp/linux/commits/Hans-Zhang/PCI-Introduce-generic-capability-search-functions/20250321-120748
base: a1cffe8cc8aef85f1b07c4464f0998b9785b795a
patch link: https://lore.kernel.org/r/20250321040358.360755-2-18255117159%40163.com
patch subject: [v3 1/4] PCI: Introduce generic capability search functions
config: arc-randconfig-001-20250321 (https://download.01.org/0day-ci/archive/20250321/202503211714.tv7E2QkK-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 13.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250321/202503211714.tv7E2QkK-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503211714.tv7E2QkK-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/pci/pci.c:691:5: error: conflicting types for 'pci_generic_find_ext_capability'; have 'u16(void *, u32 (*)(void *, int, int), int)' {aka 'short unsigned int(void *, unsigned int (*)(void *, int, int), int)'}
691 | u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/pci/pci.c:18:
include/linux/pci.h:1211:5: note: previous declaration of 'pci_generic_find_ext_capability' with type 'u16(void *, u32 (*)(void *, int, int), u8)' {aka 'short unsigned int(void *, unsigned int (*)(void *, int, int), unsigned char)'}
1211 | u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from include/linux/linkage.h:7,
from include/linux/preempt.h:10,
from include/linux/spinlock.h:56,
from include/linux/mmzone.h:8,
from include/linux/gfp.h:7,
from include/linux/slab.h:16,
from include/linux/resource_ext.h:11,
from include/linux/acpi.h:13,
from drivers/pci/pci.c:11:
drivers/pci/pci.c:696:19: error: conflicting types for 'pci_generic_find_ext_capability'; have 'u16(void *, u32 (*)(void *, int, int), int)' {aka 'short unsigned int(void *, unsigned int (*)(void *, int, int), int)'}
696 | EXPORT_SYMBOL_GPL(pci_generic_find_ext_capability);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/export.h:70:28: note: in definition of macro '__EXPORT_SYMBOL'
70 | extern typeof(sym) sym; \
| ^~~
include/linux/export.h:84:41: note: in expansion of macro '_EXPORT_SYMBOL'
84 | #define EXPORT_SYMBOL_GPL(sym) _EXPORT_SYMBOL(sym, "GPL")
| ^~~~~~~~~~~~~~
drivers/pci/pci.c:696:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
696 | EXPORT_SYMBOL_GPL(pci_generic_find_ext_capability);
| ^~~~~~~~~~~~~~~~~
include/linux/pci.h:1211:5: note: previous declaration of 'pci_generic_find_ext_capability' with type 'u16(void *, u32 (*)(void *, int, int), u8)' {aka 'short unsigned int(void *, unsigned int (*)(void *, int, int), unsigned char)'}
1211 | u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vim +691 drivers/pci/pci.c
690
> 691 u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
692 int cap)
693 {
694 return pci_generic_find_next_ext_capability(priv, read_cfg, 0, cap);
695 }
696 EXPORT_SYMBOL_GPL(pci_generic_find_ext_capability);
697
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Hans,
kernel test robot noticed the following build errors:
[auto build test ERROR on a1cffe8cc8aef85f1b07c4464f0998b9785b795a]
url: https://github.com/intel-lab-lkp/linux/commits/Hans-Zhang/PCI-Introduce-generic-capability-search-functions/20250321-120748
base: a1cffe8cc8aef85f1b07c4464f0998b9785b795a
patch link: https://lore.kernel.org/r/20250321040358.360755-2-18255117159%40163.com
patch subject: [v3 1/4] PCI: Introduce generic capability search functions
config: arc-randconfig-002-20250321 (https://download.01.org/0day-ci/archive/20250321/202503211726.DEvDBGk8-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250321/202503211726.DEvDBGk8-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503211726.DEvDBGk8-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
In file included from drivers/nvme/host/nvme.h:11,
from drivers/nvme/host/fc.c:13:
>> include/linux/pci.h:2021:4: warning: no previous prototype for 'pci_generic_find_capability' [-Wmissing-prototypes]
2021 | u8 pci_generic_find_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
>> include/linux/pci.h:2024:5: warning: no previous prototype for 'pci_generic_find_ext_capability' [-Wmissing-prototypes]
2024 | u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
arc-linux-ld: drivers/scsi/libsas/sas_phy.o: in function `pci_generic_find_capability':
>> sas_phy.c:(.text+0x148): multiple definition of `pci_generic_find_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd18): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_phy.o: in function `pci_generic_find_ext_capability':
>> sas_phy.c:(.text+0x150): multiple definition of `pci_generic_find_ext_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd20): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_port.o: in function `pci_generic_find_capability':
sas_port.c:(.text+0x1c8): multiple definition of `pci_generic_find_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd18): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_port.o: in function `pci_generic_find_ext_capability':
sas_port.c:(.text+0x1d0): multiple definition of `pci_generic_find_ext_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd20): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_event.o: in function `pci_generic_find_capability':
sas_event.c:(.text+0xf8): multiple definition of `pci_generic_find_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd18): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_event.o: in function `pci_generic_find_ext_capability':
sas_event.c:(.text+0x100): multiple definition of `pci_generic_find_ext_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd20): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_discover.o: in function `pci_generic_find_capability':
sas_discover.c:(.text+0x874): multiple definition of `pci_generic_find_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd18): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_discover.o: in function `pci_generic_find_ext_capability':
sas_discover.c:(.text+0x87c): multiple definition of `pci_generic_find_ext_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd20): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_expander.o: in function `pci_generic_find_capability':
sas_expander.c:(.text+0x2378): multiple definition of `pci_generic_find_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd18): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_expander.o: in function `pci_generic_find_ext_capability':
sas_expander.c:(.text+0x2380): multiple definition of `pci_generic_find_ext_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd20): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_scsi_host.o: in function `pci_generic_find_capability':
sas_scsi_host.c:(.text+0x17c0): multiple definition of `pci_generic_find_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd18): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_scsi_host.o: in function `pci_generic_find_ext_capability':
sas_scsi_host.c:(.text+0x17c8): multiple definition of `pci_generic_find_ext_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd20): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_task.o: in function `pci_generic_find_capability':
sas_task.c:(.text+0xd4): multiple definition of `pci_generic_find_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd18): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_task.o: in function `pci_generic_find_ext_capability':
sas_task.c:(.text+0xdc): multiple definition of `pci_generic_find_ext_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd20): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_host_smp.o: in function `pci_generic_find_capability':
sas_host_smp.c:(.text+0x48): multiple definition of `pci_generic_find_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd18): first defined here
arc-linux-ld: drivers/scsi/libsas/sas_host_smp.o: in function `pci_generic_find_ext_capability':
sas_host_smp.c:(.text+0x50): multiple definition of `pci_generic_find_ext_capability'; drivers/scsi/libsas/sas_init.o:sas_init.c:(.text+0xd20): first defined here
--
arc-linux-ld: drivers/pcmcia/cistpl.o: in function `pci_generic_find_capability':
>> cistpl.c:(.text+0xfac): multiple definition of `pci_generic_find_capability'; drivers/pcmcia/pcmcia_resource.o:pcmcia_resource.c:(.text+0x1244): first defined here
arc-linux-ld: drivers/pcmcia/cistpl.o: in function `pci_generic_find_ext_capability':
>> cistpl.c:(.text+0xfb4): multiple definition of `pci_generic_find_ext_capability'; drivers/pcmcia/pcmcia_resource.o:pcmcia_resource.c:(.text+0x124c): first defined here
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Hans,
kernel test robot noticed the following build warnings:
[auto build test WARNING on a1cffe8cc8aef85f1b07c4464f0998b9785b795a]
url: https://github.com/intel-lab-lkp/linux/commits/Hans-Zhang/PCI-Introduce-generic-capability-search-functions/20250321-120748
base: a1cffe8cc8aef85f1b07c4464f0998b9785b795a
patch link: https://lore.kernel.org/r/20250321040358.360755-2-18255117159%40163.com
patch subject: [v3 1/4] PCI: Introduce generic capability search functions
config: arm-randconfig-004-20250321 (https://download.01.org/0day-ci/archive/20250321/202503211730.XKhqW2Mw-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project c2692afc0a92cd5da140dfcdfff7818a5b8ce997)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250321/202503211730.XKhqW2Mw-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503211730.XKhqW2Mw-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from arch/arm/mm/iomap.c:9:
>> include/linux/pci.h:2021:4: warning: no previous prototype for function 'pci_generic_find_capability' [-Wmissing-prototypes]
2021 | u8 pci_generic_find_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^
include/linux/pci.h:2021:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
2021 | u8 pci_generic_find_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^
| static
>> include/linux/pci.h:2024:5: warning: no previous prototype for function 'pci_generic_find_ext_capability' [-Wmissing-prototypes]
2024 | u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^
include/linux/pci.h:2024:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
2024 | u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
| ^
| static
2 warnings generated.
vim +/pci_generic_find_capability +2021 include/linux/pci.h
1999
2000 static inline void pci_set_master(struct pci_dev *dev) { }
2001 static inline void pci_clear_master(struct pci_dev *dev) { }
2002 static inline int pci_enable_device(struct pci_dev *dev) { return -EIO; }
2003 static inline void pci_disable_device(struct pci_dev *dev) { }
2004 static inline int pcim_enable_device(struct pci_dev *pdev) { return -EIO; }
2005 static inline int pci_assign_resource(struct pci_dev *dev, int i)
2006 { return -EBUSY; }
2007 static inline int __must_check __pci_register_driver(struct pci_driver *drv,
2008 struct module *owner,
2009 const char *mod_name)
2010 { return 0; }
2011 static inline int pci_register_driver(struct pci_driver *drv)
2012 { return 0; }
2013 static inline void pci_unregister_driver(struct pci_driver *drv) { }
2014 static inline u8 pci_find_capability(struct pci_dev *dev, int cap)
2015 { return 0; }
2016 static inline u8 pci_find_next_capability(struct pci_dev *dev, u8 post, int cap)
2017 { return 0; }
2018 static inline u16 pci_find_ext_capability(struct pci_dev *dev, int cap)
2019 { return 0; }
2020 typedef u32 (*pci_generic_read_cfg)(void *priv, int where, int size);
> 2021 u8 pci_generic_find_capability(void *priv, pci_generic_read_cfg read_cfg,
2022 u8 cap)
2023 { return 0; }
> 2024 u16 pci_generic_find_ext_capability(void *priv, pci_generic_read_cfg read_cfg,
2025 u8 cap)
2026 { return 0; }
2027 static inline u64 pci_get_dsn(struct pci_dev *dev)
2028 { return 0; }
2029
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.