SMBIOS/SCONFIG: Allow devtree-defined Type 41 entries

Introduce the `smbios_dev_info` devicetree keyword to specify the
instance ID and RefDes (Reference Designation) of onboard devices.

Example syntax:

 device pci 1c.0 on	# PCIe Port #1
 	device pci 00.0 on
 		smbios_dev_info 6
 	end
 end
 device pci 1c.1 on	# PCIe Port #2
 	device pci 00.0 on
 		smbios_dev_info 42 "PCIe-PCI Time Machine"
 	end
 end

The `SMBIOS_TYPE41_PROVIDED_BY_DEVTREE` Kconfig option enables using
this syntax to control the generated Type 41 entries. When this option
is enabled, Type 41 entries are only autogenerated for devices with a
defined instance ID. This avoids having to keep track of which instance
IDs have been used for every device class.

Using `smbios_dev_info` when `SMBIOS_TYPE41_PROVIDED_BY_DEVTREE` is not
enabled will result in a build-time error, as the syntax is meaningless
in this case. This is done with preprocessor guards around the Type 41
members in `struct device` and the code which uses the guarded members.
Although the preprocessor usage isn't particularly elegant, adjusting
the devicetree syntax and/or grammar depending on a Kconfig option is
probably even worse.

Change-Id: Iecca9ada6ee1000674cb5dd7afd5c309d8e1a64b
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/57370
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/src/Kconfig b/src/Kconfig
index be269b6..e4ddf0c 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -785,6 +785,21 @@
 
 	  If unsure, say Y.
 
+config SMBIOS_TYPE41_PROVIDED_BY_DEVTREE
+	bool
+	depends on ARCH_X86
+	help
+	  If enabled, only generate SMBIOS Type 41 entries for PCI devices in
+	  the devicetree for which Type 41 information is provided, e.g. with
+	  the `smbios_dev_info` devicetree syntax. This is useful to manually
+	  assign specific instance IDs to onboard devices irrespective of the
+	  device traversal order. It is assumed that instance IDs for devices
+	  of the same class are unique.
+	  When disabled, coreboot autogenerates SMBIOS Type 41 entries for all
+	  appropriate PCI devices in the devicetree. Instance IDs are assigned
+	  successive numbers from a monotonically increasing counter, with one
+	  counter for each device class.
+
 config SMBIOS_PROVIDED_BY_MOBO
 	bool
 	default n
diff --git a/src/arch/x86/smbios.c b/src/arch/x86/smbios.c
index ea42971..c05f905 100644
--- a/src/arch/x86/smbios.c
+++ b/src/arch/x86/smbios.c
@@ -1177,30 +1177,55 @@
 	}
 }
 
+static bool smbios_get_type41_instance_id(struct device *dev, u8 device_type, u8 *instance_id)
+{
+#if CONFIG(SMBIOS_TYPE41_PROVIDED_BY_DEVTREE)
+	*instance_id = dev->smbios_instance_id;
+	return dev->smbios_instance_id_valid;
+#else
+	static u8 type41_inst_cnt[SMBIOS_DEVICE_TYPE_COUNT + 1] = {};
+
+	if (device_type == SMBIOS_DEVICE_TYPE_OTHER ||
+	    device_type == SMBIOS_DEVICE_TYPE_UNKNOWN)
+		return false;
+
+	if (device_type > SMBIOS_DEVICE_TYPE_COUNT)
+		return false;
+
+	*instance_id = type41_inst_cnt[device_type]++;
+	return true;
+#endif
+}
+
+static const char *smbios_get_type41_refdes(struct device *dev)
+{
+#if CONFIG(SMBIOS_TYPE41_PROVIDED_BY_DEVTREE)
+	if (dev->smbios_refdes)
+		return dev->smbios_refdes;
+#endif
+	return get_pci_subclass_name(dev);
+}
+
 static int smbios_generate_type41_from_devtree(struct device *dev, int *handle,
 					       unsigned long *current)
 {
-	static u8 type41_inst_cnt[SMBIOS_DEVICE_TYPE_COUNT + 1] = {};
-
 	if (dev->path.type != DEVICE_PATH_PCI)
 		return 0;
 	if (!dev->on_mainboard)
 		return 0;
 
-	u8 device_type = smbios_get_device_type_from_dev(dev);
+	const u8 device_type = smbios_get_device_type_from_dev(dev);
 
-	if (device_type == SMBIOS_DEVICE_TYPE_OTHER ||
-	    device_type == SMBIOS_DEVICE_TYPE_UNKNOWN)
+	u8 instance_id;
+
+	if (!smbios_get_type41_instance_id(dev, device_type, &instance_id))
 		return 0;
 
-	if (device_type > SMBIOS_DEVICE_TYPE_COUNT)
-		return 0;
-
-	const char *name = get_pci_subclass_name(dev);
+	const char *name = smbios_get_type41_refdes(dev);
 
 	return smbios_write_type41(current, handle,
 					name, // name
-					type41_inst_cnt[device_type]++, // inst
+					instance_id, // inst
 					0, // segment
 					dev->bus->secondary, //bus
 					PCI_SLOT(dev->path.pci.devfn), // device
diff --git a/src/include/device/device.h b/src/include/device/device.h
index 8610e0a..237d836 100644
--- a/src/include/device/device.h
+++ b/src/include/device/device.h
@@ -148,6 +148,17 @@
 	u8 smbios_slot_data_width;
 	u8 smbios_slot_length;
 	const char *smbios_slot_designation;
+
+#if CONFIG(SMBIOS_TYPE41_PROVIDED_BY_DEVTREE)
+	/*
+	 * These fields are intentionally guarded so that attempts to use
+	 * the corresponding devicetree syntax without selecting the Kconfig
+	 * option result in build-time errors. Smaller size is a side effect.
+	 */
+	bool smbios_instance_id_valid;
+	u8 smbios_instance_id;
+	const char *smbios_refdes;
+#endif
 #endif
 #endif
 	DEVTREE_CONST void *chip_info;