summaryrefslogtreecommitdiff
path: root/src/device/pciexp_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/device/pciexp_device.c')
-rw-r--r--src/device/pciexp_device.c51
1 files changed, 51 insertions, 0 deletions
diff --git a/src/device/pciexp_device.c b/src/device/pciexp_device.c
index b03089b8f6..c9441fb22e 100644
--- a/src/device/pciexp_device.c
+++ b/src/device/pciexp_device.c
@@ -93,6 +93,57 @@ unsigned int pciexp_find_ext_vendor_cap(const struct device *dev, unsigned int c
}
}
+/**
+ * Find a PCIe device with a given serial number, and a given VID if applicable
+ *
+ * @param serial The serial number of the device.
+ * @param vid Vendor ID of the device, may be 0 if not applicable.
+ * @param from Pointer to the device structure, used as a starting point in
+ * the linked list of all_devices, which can be 0 to start at the
+ * head of the list (i.e. all_devices).
+ * @return Pointer to the device struct.
+ */
+struct device *pcie_find_dsn(const uint64_t serial, const uint16_t vid,
+ struct device *from)
+{
+ union dsn {
+ struct {
+ uint32_t dsn_low;
+ uint32_t dsn_high;
+ };
+ uint64_t dsn;
+ } dsn;
+ unsigned int cap;
+ uint16_t vendor_id;
+
+ if (!from)
+ from = all_devices;
+ else
+ from = from->next;
+
+ while (from) {
+ if (from->path.type == DEVICE_PATH_PCI) {
+ cap = pciexp_find_extended_cap(from, PCI_EXT_CAP_ID_DSN, 0);
+ /*
+ * For PCIe device, find extended capability for serial number.
+ * The capability header is 4 bytes, followed by lower 4 bytes
+ * of serial number, then higher 4 byes of serial number.
+ */
+ if (cap != 0) {
+ dsn.dsn_low = pci_read_config32(from, cap + 4);
+ dsn.dsn_high = pci_read_config32(from, cap + 8);
+ vendor_id = pci_read_config16(from, PCI_VENDOR_ID);
+ if ((dsn.dsn == serial) && (vid == 0 || vendor_id == vid))
+ return from;
+ }
+ }
+
+ from = from->next;
+ }
+
+ return from;
+}
+
/*
* Re-train a PCIe link
*/