The PCI bus architecture is mainly divided into three parts:
1.PCI equipment.
A device that complies with the PCI bus standard is called a PCI device and can contain multiple PCI devices in the PCI bus architecture. Audio, LAN are a PCI device. PCI devices are also divided into two kinds of primary and target devices, the main device is the initiator of an access operation, and the target device is the visitor.
2.PCI Bus.
The PCI bus can be extended in a system similar to a tree structure, and each PCI bus can connect multiple PCI devices/bridges.
3.PCI Bridge. When a PCI bus has not enough load, it can be extended with the new PCI bus, while the PCI bridge is the link between the PCI bus.
PCI-related structures in the kernel probably have pci_driver, pci_device_id, Pci_dev, PCI_BUS,PCI buses that refer to the Pci_bus
struct Pci_bus {struct list_head node; /* node in List of buses */struct Pci_bus *parent; /* Parent Bus This bridge are on */struct list_head children; /* List of child buses/struct list_head devices; /* List of devices on this bus/struct Pci_dev *self; * Bridge device as seen by parent */struct list_head slots;
/* List of slots on this bus;
protected by Pci_slot_mutex * * struct resource *resource[pci_bridge_resource_num]; struct List_head resources; /* Address spaces routed to the bus * * struct resource busn_res; * * Bus numbers routed to the bus * * struct pci_ops *ops; /* Configuration Access functions * * struct msi_controller *msi; /* MSI controller */void *sysdata; /* Hook for sys-specific extension * * struct proc_dir_entry *procdir; /* Directory Entry IN/PROC/BUS/PCI * * unsigned char number; /* Bus number */unsigned char primary; /*Number of Primary bridge * * unsigned char max_bus_speed; /* Enum Pci_bus_speed */unsigned char cur_bus_speed;
/* Enum Pci_bus_speed * * #ifdef config_pci_domains_generic int domain_nr;
#endif Char name[48]; unsigned short bridge_ctl; /* Manage No_isa/fbb/et Al behaviors * * pci_bus_flags_t bus_flags;
/* Inherited by Child buses * * struct device *bridge;
struct device dev; struct Bin_attribute *legacy_io; /* Legacy I/O for this bus/struct Bin_attribute *legacy_mem;
/* Legacy MEM */unsigned int is_added:1;
};
Several important members:
CHILDREN:PCI Bridge can be extended to the current bus, the current bus has several PCI bridge, then the current bus will have several sub bus, the bus will be connected to the parent bus children linked list.
Devices: A list of devices connected to this bus.
OPS: The read, write method for the device configuration space on the current bus access bus. The operating structure of the PCI bus is pci_ops as follows:
/* Low-level architecture-dependent routines * *
struct Pci_ops {
int (*add_bus) (struct pci_bus);
void (*remove_bus) (struct Pci_bus *bus);
void __iomem * (*map_bus) (struct Pci_bus *bus, unsigned int devfn, int where);
Int (*read) (struct Pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
Int (*write) (struct Pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
In the process of booting the kernel, first, the level 0 bus is created and the devices on the level 0 bus are enumerated, and if it is a PCI bridge, then the next level of the bus is added, and eventually all the connected PCI devices will be detected, the detailed probe process, which we analyze behind.
Each PCI device is uniquely identified by a set of parameters, which are stored in the pci_device_id of the struct body, defined in include/linux/mod_devicetable.h:
struct PCI_DEVICE_ID {
__u32 vendor, device; /* Vendor and Device ID or pci_any_id*/
__u32 Subvendor, subdevice;/* Subsystem ID ' s or pci_any_id/
__u32 class , Class_mask; /* (CLASS,SUBCLASS,PROG-IF) triplet * *
kernel_ulong_t driver_data;/* Data Private to the driver * *
;
The pci_driver structure is primarily used to identify the id_table structure of the device, as well as functions to detect the device probe () and unload the device remove ():
struct pci_driver {struct node;
const char *name; const struct PCI_DEVICE_ID *id_table; /* must is non-null for probe to is called/int (*probe) (struct Pci_dev *dev, const struct pci_device_id); /* New Device inserted */void (*remove) (struct Pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver)/int (*suspend) (struct Pci_dev, *dev State) ;
/* Device suspended */int (*suspend_late) (struct Pci_dev *dev, pm_message_t State);
Int (*resume_early) (struct Pci_dev *dev); Int (*resume) (struct Pci_dev *dev);
/* Device woken up/Void (*shutdown) (struct Pci_dev *dev); Int (*sriov_configure) (struct pci_dev *dev, int num_vfs);
/* PF Pdev/const struct pci_error_handlers *err_handler;
struct Device_driver driver;
struct Pci_dynids dynids;
};
The
Pci_dev structure, which is also in file include/linux/pci.h, details almost all hardware information for a PCI device, including vendor ID, device ID, various resources, and so on:
* * The PCI_DEV structure is used to describe PCI devices. * * struct Pci_dev {struct list_head bus_list; /* node in per-bus list */struct Pci_bus *bus; /* Bus This device are on */struct Pci_bus *subordinate; * Device bridges to/void *sysdata; /* Hook for sys-specific extension * * struct proc_dir_entry *procent; /* Device Entry IN/PROC/BUS/PCI * * struct pci_slot *slot; /* Physical slot this device was in * unsigned int devfn;
/* encoded device & Function Index */unsigned short vendor;
unsigned short device;
unsigned short Subsystem_vendor;
unsigned short subsystem_device; unsigned int class; * * 3 bytes: (base,sub,prog-if) * * U8 revision; /* PCI revision, low byte of class word */U8 Hdr_type; /* PCI Header type (' Multi ' flag masked out) * * U8 Pcie_cap; /* PCIE Capability offset * * U8 Msi_cap;
/* MSI Capability offset * *U8 Msix_cap; /* MSI-X Capability offset * * U8 pcie_mpss:3; /* PCIe Max Payload Size Supported * * U8 Rom_base_reg; /* which config register controls the ROM */U8 pin; /* which interrupt pin this device uses * * U16 Pcie_flags_reg; /* Cached PCIe capabilities Register/unsigned long *dma_alias_mask;/* mask of enabled DEVFN aliases * * Struc T Pci_driver *driver; /* Which driver has allocated this device * U64 dma_mask; /* Mask of the "bits of bus" this device implements. Normally this is 0xFFFFFFFF. You are need to the change this if your device has broken DMA or supports 64-BI T transfers.
* * struct device_dma_parameters dma_parms; pci_power_t current_state; /* Current operating state. In Acpi-speak, this is D0-D3, D0 being fully functional, and D3 being Off. */U8 Pm_cap; /* PM capability offset */unsigned int pme_support:5;
/* Bitmask of states from which pme# can be generated/unsigned int pme_interrupt:1; unsigned int pme_poll:1; /* Poll device ' s PME status bit */unsigned int d1_support:1; /* Low power state D1 is supported */unsigned int d2_support:1; /* Low power state D2 is supported */unsigned int no_d1d2:1; /* D1 and D2 are forbidden */unsigned int no_d3cold:1; /* D3cold is forbidden */unsigned int d3cold_allowed:1; /* D3cold is allowed by user */unsigned int mmio_always_on:1; /* Disallow turning off Io/mem decoding during bar sizing/unsigned int wakeup_prepare
D:1; unsigned int runtime_d3cold:1; /* Whether go through runtime d3cold, not set for devices powered on /off by the correspondING bridge */unsigned int ignore_hotplug:1; /* Ignore hotplug Events * * unsigned int d3_delay; /* d3->d0 Transition Time in MS */unsigned int d3cold_delay; /* d3cold->d0 Transition Time in MS */#ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state; /* ASPM Link State * * * #endif pci_channel_state_t error_state; /* Current Connectivity state */struct device dev; /* Generic Device interface */int cfg_size; /* Size of configuration Space/* * Instead of touching interrupt line and base address registers * dire ctly, use the values stored here.
They might be different!
* * unsigned int IRQ; struct resource Resource[device_count_resource]; */I/O and memory regions + expansion ROMs * * BOOL match_driver; /* Skip Attaching driver * * * fields are used by common fixups/unsigned int transparent:1; /* subtractive decode PCI Bridge */unsigned intmultifunction:1;/* part of multi-function device/* Keep track of device state */unsigned int is_added:1; unsigned int is_busmaster:1; /* Device is busmaster */unsigned int no_msi:1; /* Device may isn't use MSI */unsigned int no_64bit_msi:1; /* Device May 32-bit MSIs * * unsigned int block_cfg_access:1; /* Config space access is blocked * * unsigned int broken_parity_status:1; /* Device generates false positive parity * * unsigned int irq_reroute_variant:2;
/* Device needs IRQ rerouting variant */unsigned int msi_enabled:1;
unsigned int msix_enabled:1; unsigned int ari_enabled:1; /* ARI forwarding */unsigned int ats_enabled:1;
/* Address Translation Service */unsigned int is_managed:1; unsigned int needs_freset:1;
* Dev requires fundamental reset/unsigned int state_saved:1;
unsigned int is_physfn:1;
unsigned int is_virtfn:1; unsigned int reset_fn:1;
unsigned int is_hotplug_bridge:1;
unsigned int __aer_firmware_first_valid:1;
unsigned int __aer_firmware_first:1;
unsigned int broken_intx_masking:1; unsigned int io_window_1k:1;
/* Intel Peer-to-peer bridge 1K I/o windows/unsigned int irq_managed:1;
unsigned int has_secondary_link:1; unsigned int non_compliant_bars:1; /* Broken BARs;
Ignore them * * pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* Pci_enable_device has been called * * u32 saved_config_space[16];
/* Config space saved at suspend time/struct hlist_head saved_cap_space; struct Bin_attribute *rom_attr; /* Attribute descriptor for SYSFS ROM entry */int rom_attr_enabled; /* has display of the ROM attribute been enabled? * * struct bin_attribute *res_attr[device_count_resource]; /* SYSFS file for resources */struct bin_attribute *res_attr_wc[device_count_resource]; /* Sysfs file for WC mapping-*/#ifdef COnfig_pci_msi const struct Attribute_group **msi_irq_groups;
#endif struct PCI_VPD *vpd; #ifdef config_pci_ats Union {struct Pci_sriov *sriov; /* SR-IOV Capability related * * struct pci_dev *PHYSFN;
/* The PF this VF are associated with */}; U16 Ats_cap; /* ATS Capability Offset * * U8 Ats_stu; /* ATS Smallest translation Unit * * atomic_t ats_ref_cnt; /* Number of VFs with ATS enabled */#endif phys_addr_t rom; /* Physical Address of ROM if it isn't from the BAR/size_t Romlen; /* Length of ROM if it ' s not from the BAR/char *driver_override;
/* Driver name to force a match */};
The definition of a PCI bus is Pci_bus_type, which is defined in Drivers/pci/pci-driver.c, as follows:
struct Bus_type pci_bus_type = {
. Name = "PCI",
. Match = Pci_bus_match,
. uevent = pci_uevent,
. Probe = Pci_device_probe,
. Remove = Pci_device_remove,
. Shutdown = Pci_device_ Shutdown,
. dev_groups = pci_dev_groups,
. bus_groups = pci_bus_groups,
. drv_groups = pci_drv_groups,
. PM = pci_pm_ops_ptr,
Where Pci_driver and Pci_dev are registered to the Pci_bus_type drive and equipment, where Pci_bus_match are as follows:
/**
* Pci_bus_match-tell If a PCI device structure has a matching PCI device ID structure
* @dev: the PCI device Structure to match against
* @drv: The device driver to search for matching PCI device ID Structures
*
* Used By a driver to check whether a PCI device present in the
* system be in it list of supported devices. Returns the matching
* pci_device_id structure or%null if there is no match.
*
/static int pci_bus_match (struct device *dev struct device_driver) {*drv struct pci_dev
= *pci_dev Ci_dev (dev);
struct Pci_driver *pci_drv;
const struct PCI_DEVICE_ID *found_id;
if (!pci_dev->match_driver) return
0;
Pci_drv = To_pci_driver (DRV);
found_id = Pci_match_device (Pci_drv, Pci_dev);
if (found_id) return
1;
return 0;
}
Where registered PCI drivers and devices call the Pci_match_device function to implement:
/** * Pci_match_device-tell If a PCI device structure has a matching PCI device ID structure * @drv: the PCI driver T o match against * @dev: The PCI device structure to match against * * Used by a driver to check whether a PCI device PR Esent in the * system was in its list of supported devices.
Returns the matching * pci_device_id structure or%null if there is no match. * * Static const struct PCI_DEVICE_ID *pci_match_device (struct pci_driver *drv, struct PCI_DEV
*dev) {struct Pci_dynid *dynid;
const struct PCI_DEVICE_ID *found_id = NULL; /* When driver_override are set, only bind to the matching driver/if (Dev->driver_override && strcmp (dev-
>driver_override, Drv->name)) return NULL;
/* Look at the dynamic IDs, before the static ones * * Spin_lock (&drv->dynids.lock); List_for_each_entry (Dynid, &drv->dynids.list, node) {if (Pci_match_one_device &dynid->id,Dev)) {found_id = &dynid->id;
Break
} spin_unlock (&drv->dynids.lock);
if (!found_id) found_id = pci_match_id (drv->id_table, Dev); /* Driver_override would always match, send a dummy ID */if (!found_id && dev->driver_override) fou
nd_id = &pci_device_id_any;
return found_id;
}
Pci_match_one_device was invoked to match the device ID for PCI,
/**
* Pci_match_one_device-tell If a PCI device structure has a matching
* PCI Device ID structure
* @id : Single PCI Device ID structure to match
* @dev: The PCI device structure to match against
*
* Returns the MA Tching pci_device_id structure or%null if there is no match.
* *
static inline const struct PCI_DEVICE_ID *
pci_match_one_device (const struct PCI_DEVICE_ID, const *ID Pci_dev *dev)
{
if (Id->vendor = = pci_any_id | | id->vendor = = dev->vendor) &&
(id-> device = = PCI_ANY_ID | | Id->device = dev->device) &&
(Id->subvendor = = pci_any_id | | id->subvendor = = dev-> Subsystem_vendor) &&
(Id->subdevice = = pci_any_id | | | id->subdevice = = dev->subsystem_device) &&
! ( (id->class ^ dev->class) & Id->class_mask)) return
ID;
return NULL;
}
A pci_match_id PCI device was invoked to match the given pci_id table:
/** * Pci_match_id-see if a PCI device matches a given pci_id table * @ids: array of PCI Dev ICE ID structures to search in</