PCI: Refactor capability search into PCI_FIND_NEXT_CAP()

The PCI Capability search functionality is duplicated across the PCI core
and several controller drivers. The core's current implementation requires
fully initialized PCI device and bus structures, which prevents controller
drivers from using it during early initialization phases before these
structures are available.

Move the Capability search logic into a PCI_FIND_NEXT_CAP() macro that
accepts a config space accessor function as an argument. This enables
controller drivers to perform Capability discovery using their early
access mechanisms prior to full device initialization while sharing the
Capability search code.

Convert the existing PCI core Capability search implementation to use
PCI_FIND_NEXT_CAP().  Controller drivers can later use this with their
early access mechanisms while maintaining the existing protection against
infinite loops through preserved TTL checks.

Signed-off-by: Hans Zhang <18255117159@163.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Niklas Schnelle <schnelle@linux.ibm.com>
Link: https://patch.msgid.link/20250813144529.303548-3-18255117159@163.com
This commit is contained in:
Hans Zhang 2025-08-13 22:45:25 +08:00 committed by Bjorn Helgaas
parent 37d1ade896
commit 3c02084d8f
2 changed files with 53 additions and 34 deletions

View File

@ -9,7 +9,6 @@
*/
#include <linux/acpi.h>
#include <linux/align.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/dmi.h>
@ -424,36 +423,10 @@ static int pci_dev_str_match(struct pci_dev *dev, const char *p,
return 1;
}
static u8 __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
u8 pos, int cap, int *ttl)
{
u8 id;
u16 ent;
pci_bus_read_config_byte(bus, devfn, pos, &pos);
while ((*ttl)--) {
if (pos < PCI_STD_HEADER_SIZEOF)
break;
pos = ALIGN_DOWN(pos, 4);
pci_bus_read_config_word(bus, devfn, pos, &ent);
id = FIELD_GET(PCI_CAP_ID_MASK, ent);
if (id == 0xff)
break;
if (id == cap)
return pos;
pos = FIELD_GET(PCI_CAP_LIST_NEXT_MASK, ent);
}
return 0;
}
static u8 __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn,
u8 pos, int cap)
{
int ttl = PCI_FIND_CAP_TTL;
return __pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl);
return PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, cap, bus, devfn);
}
u8 pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap)
@ -649,7 +622,7 @@ EXPORT_SYMBOL_GPL(pci_get_dsn);
static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap)
{
int rc, ttl = PCI_FIND_CAP_TTL;
int rc;
u8 cap, mask;
if (ht_cap == HT_CAPTYPE_SLAVE || ht_cap == HT_CAPTYPE_HOST)
@ -657,8 +630,8 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap)
else
mask = HT_5BIT_CAP_MASK;
pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn, pos,
PCI_CAP_ID_HT, &ttl);
pos = PCI_FIND_NEXT_CAP(pci_bus_read_config, pos,
PCI_CAP_ID_HT, dev->bus, dev->devfn);
while (pos) {
rc = pci_read_config_byte(dev, pos + 3, &cap);
if (rc != PCIBIOS_SUCCESSFUL)
@ -667,9 +640,10 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap)
if ((cap & mask) == ht_cap)
return pos;
pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn,
pos + PCI_CAP_LIST_NEXT,
PCI_CAP_ID_HT, &ttl);
pos = PCI_FIND_NEXT_CAP(pci_bus_read_config,
pos + PCI_CAP_LIST_NEXT,
PCI_CAP_ID_HT, dev->bus,
dev->devfn);
}
return 0;

View File

@ -2,6 +2,8 @@
#ifndef DRIVERS_PCI_H
#define DRIVERS_PCI_H
#include <linux/align.h>
#include <linux/bitfield.h>
#include <linux/pci.h>
struct pcie_tlp_log;
@ -88,6 +90,49 @@ bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
bool pcie_cap_has_lnkctl2(const struct pci_dev *dev);
bool pcie_cap_has_rtctl(const struct pci_dev *dev);
/* Standard Capability finder */
/**
* PCI_FIND_NEXT_CAP - Find a PCI standard capability
* @read_cfg: Function pointer for reading PCI config space
* @start: Starting position to begin search
* @cap: Capability ID to find
* @args: Arguments to pass to read_cfg function
*
* Search the capability list in PCI config space to find @cap.
* Implements TTL (time-to-live) protection against infinite loops.
*
* Return: Position of the capability if found, 0 otherwise.
*/
#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, args...) \
({ \
int __ttl = PCI_FIND_CAP_TTL; \
u8 __id, __found_pos = 0; \
u8 __pos = (start); \
u16 __ent; \
\
read_cfg##_byte(args, __pos, &__pos); \
\
while (__ttl--) { \
if (__pos < PCI_STD_HEADER_SIZEOF) \
break; \
\
__pos = ALIGN_DOWN(__pos, 4); \
read_cfg##_word(args, __pos, &__ent); \
\
__id = FIELD_GET(PCI_CAP_ID_MASK, __ent); \
if (__id == 0xff) \
break; \
\
if (__id == (cap)) { \
__found_pos = __pos; \
break; \
} \
\
__pos = FIELD_GET(PCI_CAP_LIST_NEXT_MASK, __ent); \
} \
__found_pos; \
})
/* Functions internal to the PCI core code */
#ifdef CONFIG_DMI