Table of Contents
What is PCI?
– PCI is the extended version of Industry Standard Architecture(ISA). ISA is the old standard used for connecting peripherals with computer motherboard. Click here to get more information on ISA.
– Originally PCI was the replacement for the ISA(Industry Standard Architecture) to achieve the below purpose.
- For better efficiency in data transfer between the PC and its peripherals.
- Reduce platform dependency.
- Easy plug-and-play of the peripherals with the system.
- PCI is a parallel bus that can transfer 32-bit(in case of 32-bit data bus) or 64-bit(in case of 64-bit data bus) data at the same time.
- A single PCI system can have 256 buses.
- Each bus can host up to 32 devices.
- Each device may have multifunctionalities.
- Max 8- functionalities are supported.
- Ex: A device having a camera module, audio module, and video module together.
– PCI bridge is used to join two buses. It’s a special kind of peripheral device. One PCI bus is connected to the other using a PCI bridge.
– As shown in the below image, the Host bridge is the base bridge with CPU where the PCI base bus(Bus 0) is connected. Then other buses(like Bus 1) are connected to the Base PCI bus using the PCI bridge. Devices also connect with PCI buses using different kinds of bridges.
List the PCI devices in Linux.
– lspci is the command that lists all the PCI devices. Click here to see the manual page of lspci.
lspci - list all PCI devices
– In the above lspci output, it shows the bus address:device address.functionality.
– As mentioned earlier, one device can support multiple functionalities. We can see device ‘1c’ has two functionalities(0,1). Device ‘1f’ has three functionalities(0,2,3).
– lspci -tv shows tree view.
– /sys/bus/pci/devices directory shows all the PCI devices. The directory contains files that give information regarding PCI devices. e.g., vendor details, revision details, and driver-related details.
– It has files called remove and rescan. which can be used to remove the PCI device from the system and add the device again to the system.
Remove and Add PCI devices using the file.
– Removing device 16. Go to the respective device directory and write ‘1’ to remove the file.
– Rescan to add PCI device from /sys/bus/pci/. Rescanning device 16.
– Numeric details of vendor ID and product ID can be seen using “lspci -n” option.
– More details about all the PCI devices can be seen using “lspci -m” option.
– “lspci -vvv” option for detailed information about each PCI device includes the status of the device, interrupt-related info, which kernel module is used for that specific device, capabilities, and latency.
PCI configuration space
– Each PCI device has configuration space. Minimum 256 bytes.
– The first 64-bits are standard in configuration space.
– Configuration space contains VendorID, DeviceId, Class Code, etc…
– Below is the configuration space image from Intel’s PCI ethernet controller. Click here to get a detailed datasheet.
– Use sudo lspci -xxx to know the configuration space
– Let’s look at these registers in detail.
vendor ID
– This register gives details about the hardware manufacturer. PCI Special Interest Group (PCI-SIG) maintains the global registry for such numbers. – The manufacturer has to get a unique number from this group for their vendor ID.
– For more information on PC-SIG. visit wiki or PCI-SIG official website.
device ID
– This number is used to identify devices. This number is often paired with vendorID to get a unique number for a hardware device which is also called as a signature of the device. This signature is used to detect the device in the kernel as well.
Class
– All the PCI devices belong to some class. A few bits in the class are also used to identify the base-class.
– Few drivers support multiple similar devices, those devices may belong to the same class.
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 */
};
– Structure pci_device_id is used in the Linux kernel to store this vendorID, deviceID, sub vendorID, sub deviceID, and class information.
– There are different versions of macros available in the kernel to initialize this structure. Refer pci.h for more details.
#define PCI_DEVICE(vend,dev) \
.vendor = (vend), .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
#define PCI_DEVICE_SUB(vend, dev, subvend, subdev) \
.vendor = (vend), .device = (dev), \
.subvendor = (subvend), .subdevice = (subdev)
#define PCI_DEVICE_CLASS(dev_class,dev_class_mask) \
.class = (dev_class), .class_mask = (dev_class_mask), \
.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
#define PCI_VDEVICE(vend, dev) \
.vendor = PCI_VENDOR_ID_##vend, .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0
#define MODULE_DEVICE_TABLE(type, name) \
extern typeof(name) __mod_##type##__##name##_device_table \
__attribute__ ((unused, alias(__stringify(name))))
MODULE_DEVICE_TABLE macro creates __mod_pci_device_table variable which points to the list of pci_device_id. Refer module.h for more details.