device/pci_device: Add generic subsystem programming logic

This patch adds generic log to perform subsystem programming
based on header type.

Type 0: subsystem offset 0x2C
Type 2: subsystem offset 0x40
Type 1: Read CAP ID 0xD to know cap offset start, offset 4 to locate
subsystem vendor id.

Change-Id: Id8aed6dac24517e93cd55d6bb3b254b7b4d950d3
Signed-off-by: Subrata Banik <subrata.banik@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/31983
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-by: David Guckian
Reviewed-by: Furquan Shaikh <furquan@google.com>
diff --git a/src/device/pci_device.c b/src/device/pci_device.c
index fbe9d96..5f908d2 100644
--- a/src/device/pci_device.c
+++ b/src/device/pci_device.c
@@ -734,11 +734,31 @@
 void pci_dev_set_subsystem(struct device *dev, unsigned int vendor,
 			   unsigned int device)
 {
+	uint8_t offset;
+
+	/* Header type */
+	switch (dev->hdr_type & 0x7f) {
+	case PCI_HEADER_TYPE_NORMAL:
+		offset = PCI_SUBSYSTEM_VENDOR_ID;
+		break;
+	case PCI_HEADER_TYPE_BRIDGE:
+		offset = pci_find_capability(dev, PCI_CAP_ID_SSVID);
+		if (!offset)
+			return;
+		offset += 4; /* Vendor ID at offset 4 */
+		break;
+	case PCI_HEADER_TYPE_CARDBUS:
+		offset = PCI_CB_SUBSYSTEM_VENDOR_ID;
+		break;
+	default:
+		return;
+	}
+
 	if (!vendor || !device) {
-		pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+		pci_write_config32(dev, offset,
 			pci_read_config32(dev, PCI_VENDOR_ID));
 	} else {
-		pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+		pci_write_config32(dev, offset,
 			((device & 0xffff) << 16) | (vendor & 0xffff));
 	}
 }