board: phytec: imx93: Add eeprom-based hardware introspection

The phyCORE-i.MX 93 is available in various variants. Relevant variant
options for the spl/u-boot are:
- with or without HS400 support for the eMMC
- with 1GB ram chip, or 2GB ram chip

The phyCORE's eeprom contains all information about the existing variant
options. Add evaluation of the eeprom data to the spl/u-boot to
enable/disable HS400 and to select the appropriate ram configuration at
startup.

Signed-off-by: Christoph Stoidner <c.stoidner@phytec.de>
Reviewed-by: Wadim Egorov <w.egorov@phytec.de>
Reviewed-by: Yannic Moog <y.moog@phytec.de>
Tested-by: Primoz Fiser <primoz.fiser@norik.com>
diff --git a/board/phytec/common/Kconfig b/board/phytec/common/Kconfig
index f394ace..bc55117 100644
--- a/board/phytec/common/Kconfig
+++ b/board/phytec/common/Kconfig
@@ -19,6 +19,14 @@
 	  Support of I2C EEPROM based SoM detection. Supported
 	  for PHYTEC i.MX8MM/i.MX8MP boards
 
+config PHYTEC_IMX93_SOM_DETECTION
+	bool "Support SoM detection for i.MX93 PHYTEC platforms"
+	depends on ARCH_IMX9 && PHYTEC_SOM_DETECTION
+	default y
+	help
+	  Support of I2C EEPROM based SoM detection. Supported
+	  for PHYTEC i.MX93 based boards
+
 config PHYTEC_AM62_SOM_DETECTION
 	bool "Support SoM detection for AM62x PHYTEC platforms"
 	depends on (TARGET_PHYCORE_AM62X_A53 || TARGET_PHYCORE_AM62X_R5) && \
diff --git a/board/phytec/common/Makefile b/board/phytec/common/Makefile
index cd78f76..8126f73 100644
--- a/board/phytec/common/Makefile
+++ b/board/phytec/common/Makefile
@@ -10,3 +10,4 @@
 obj-y += phytec_som_detection.o phytec_som_detection_blocks.o
 obj-$(CONFIG_ARCH_K3) += am6_som_detection.o k3/
 obj-$(CONFIG_ARCH_IMX8M) += imx8m_som_detection.o
+obj-$(CONFIG_ARCH_IMX9) += imx93_som_detection.o
diff --git a/board/phytec/common/imx93_som_detection.c b/board/phytec/common/imx93_som_detection.c
new file mode 100644
index 0000000..eb9574d
--- /dev/null
+++ b/board/phytec/common/imx93_som_detection.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 PHYTEC Messtechnik GmbH
+ * Author: Primoz Fiser <primoz.fiser@norik.com>
+ */
+
+#include <asm/arch/sys_proto.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <i2c.h>
+#include <u-boot/crc.h>
+
+#include "imx93_som_detection.h"
+
+extern struct phytec_eeprom_data eeprom_data;
+
+#if IS_ENABLED(CONFIG_PHYTEC_IMX93_SOM_DETECTION)
+
+/* Check if the SoM is actually one of the following products:
+ * - i.MX93
+ *
+ * Returns 0 in case it's a known SoM. Otherwise, returns 1.
+ */
+u8 __maybe_unused phytec_imx93_detect(struct phytec_eeprom_data *data)
+{
+	u8 som;
+
+	if (!data)
+		data = &eeprom_data;
+
+	/* Early API revisions are not supported */
+	if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
+		return 1;
+
+	som = data->payload.data.data_api2.som_no;
+	debug("%s: som id: %u\n", __func__, som);
+
+	if (som == PHYTEC_IMX93_SOM && is_imx93())
+		return 0;
+
+	pr_err("%s: SoM ID does not match. Wrong EEPROM data?\n", __func__);
+	return 1;
+}
+
+/*
+ * Filter PHYTEC i.MX93 SoM options by option index
+ *
+ * Returns:
+ *  - option value
+ *  - PHYTEC_EEPROM_INVAL when the data is invalid
+ *
+ */
+u8 __maybe_unused phytec_imx93_get_opt(struct phytec_eeprom_data *data,
+				       enum phytec_imx93_option_index idx)
+{
+	char *opt;
+	u8 opt_id;
+
+	if (!data)
+		data = &eeprom_data;
+
+	if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
+		return PHYTEC_EEPROM_INVAL;
+
+	opt = phytec_get_opt(data);
+	if (opt)
+		opt_id = PHYTEC_GET_OPTION(opt[idx]);
+	else
+		opt_id = PHYTEC_EEPROM_INVAL;
+
+	debug("%s: opt[%d] id: %u\n", __func__, idx, opt_id);
+	return opt_id;
+}
+
+/*
+ * Filter PHYTEC i.MX93 SoM voltage
+ *
+ * Returns:
+ *  - PHYTEC_IMX93_VOLTAGE_1V8 or PHYTEC_IMX93_VOLTAGE_3V3
+ *  - PHYTEC_EEPROM_INVAL when the data is invalid
+ *
+ */
+enum phytec_imx93_voltage __maybe_unused phytec_imx93_get_voltage(struct phytec_eeprom_data *data)
+{
+	u8 option = phytec_imx93_get_opt(data, PHYTEC_IMX93_OPT_FEAT);
+
+	if (option == PHYTEC_EEPROM_INVAL)
+		return PHYTEC_IMX93_VOLTAGE_INVALID;
+	return (option & 0x01) ? PHYTEC_IMX93_VOLTAGE_1V8 : PHYTEC_IMX93_VOLTAGE_3V3;
+}
+
+#else
+
+inline u8 __maybe_unused phytec_imx93_detect(struct phytec_eeprom_data *data)
+{
+	return 1;
+}
+
+inline u8 __maybe_unused phytec_imx93_get_opt(struct phytec_eeprom_data *data,
+					      enum phytec_imx93_option_index idx)
+{
+	return PHYTEC_EEPROM_INVAL;
+}
+
+inline enum phytec_imx93_voltage __maybe_unused phytec_imx93_get_voltage
+	(struct phytec_eeprom_data *data)
+{
+	return PHYTEC_EEPROM_INVAL;
+}
+
+#endif /* IS_ENABLED(CONFIG_PHYTEC_IMX93_SOM_DETECTION) */
diff --git a/board/phytec/common/imx93_som_detection.h b/board/phytec/common/imx93_som_detection.h
new file mode 100644
index 0000000..a0803b4
--- /dev/null
+++ b/board/phytec/common/imx93_som_detection.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024 PHYTEC Messtechnik GmbH
+ * Author: Primoz Fiser <primoz.fiser@norik.com>
+ */
+
+#ifndef _PHYTEC_IMX93_SOM_DETECTION_H
+#define _PHYTEC_IMX93_SOM_DETECTION_H
+
+#include "phytec_som_detection.h"
+
+#define PHYTEC_IMX93_SOM	77
+
+enum phytec_imx93_option_index {
+	PHYTEC_IMX93_OPT_DDR = 0,
+	PHYTEC_IMX93_OPT_EMMC = 1,
+	PHYTEC_IMX93_OPT_CPU = 2,
+	PHYTEC_IMX93_OPT_FREQ = 3,
+	PHYTEC_IMX93_OPT_NPU = 4,
+	PHYTEC_IMX93_OPT_DISP = 5,
+	PHYTEC_IMX93_OPT_ETH = 6,
+	PHYTEC_IMX93_OPT_FEAT = 7,
+	PHYTEC_IMX93_OPT_TEMP = 8,
+	PHYTEC_IMX93_OPT_BOOT = 9,
+	PHYTEC_IMX93_OPT_LED = 10,
+	PHYTEC_IMX93_OPT_EEPROM = 11,
+};
+
+enum phytec_imx93_voltage {
+	PHYTEC_IMX93_VOLTAGE_INVALID = PHYTEC_EEPROM_INVAL,
+	PHYTEC_IMX93_VOLTAGE_3V3 = 0,
+	PHYTEC_IMX93_VOLTAGE_1V8 = 1,
+};
+
+enum phytec_imx93_ddr_eeprom_code {
+	PHYTEC_IMX93_DDR_INVALID = PHYTEC_EEPROM_INVAL,
+	PHYTEC_IMX93_LPDDR4X_512MB = 0,
+	PHYTEC_IMX93_LPDDR4X_1GB = 1,
+	PHYTEC_IMX93_LPDDR4X_2GB = 2,
+	PHYTEC_IMX93_LPDDR4_512MB = 3,
+	PHYTEC_IMX93_LPDDR4_1GB = 4,
+	PHYTEC_IMX93_LPDDR4_2GB = 5,
+};
+
+u8 __maybe_unused phytec_imx93_detect(struct phytec_eeprom_data *data);
+u8 __maybe_unused phytec_imx93_get_opt(struct phytec_eeprom_data *data,
+				       enum phytec_imx93_option_index idx);
+enum phytec_imx93_voltage __maybe_unused phytec_imx93_get_voltage
+	(struct phytec_eeprom_data *data);
+
+#endif /* _PHYTEC_IMX93_SOM_DETECTION_H */