Add imx93-var-som support

Add support for the Variscite VAR-SOM-IMX93 evaluation kit. The SoM
consists of an NXP iMX93 dual A55 CPU. The SoM is mounted on a Variscite
Symphony SBC.

Signed-off-by: Mathieu Othacehe <m.othacehe@gmail.com>
diff --git a/board/variscite/common/eth.c b/board/variscite/common/eth.c
new file mode 100644
index 0000000..a794533
--- /dev/null
+++ b/board/variscite/common/eth.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 Variscite Ltd.
+ */
+#include <net.h>
+#include <miiphy.h>
+#include <env.h>
+#include "../common/imx9_eeprom.h"
+
+#define CHAR_BIT 8
+
+static u64 mac2int(const u8 hwaddr[])
+{
+	u8 i;
+	u64 ret = 0;
+	const u8 *p = hwaddr;
+
+	for (i = 6; i > 0; i--)
+		ret |= (u64)*p++ << (CHAR_BIT * (i - 1));
+
+	return ret;
+}
+
+static void int2mac(const u64 mac, u8 *hwaddr)
+{
+	u8 i;
+	u8 *p = hwaddr;
+
+	for (i = 6; i > 0; i--)
+		*p++ = mac >> (CHAR_BIT * (i - 1));
+}
+
+int var_setup_mac(struct var_eeprom *eeprom)
+{
+	int ret;
+	unsigned char enetaddr[6];
+	u64 addr;
+	unsigned char enet1addr[6];
+
+	ret = eth_env_get_enetaddr("ethaddr", enetaddr);
+	if (ret)
+		return 0;
+
+	ret = var_eeprom_get_mac(eeprom, enetaddr);
+	if (ret)
+		return ret;
+
+	if (!is_valid_ethaddr(enetaddr))
+		return -EINVAL;
+
+	eth_env_set_enetaddr("ethaddr", enetaddr);
+
+	addr = mac2int(enetaddr);
+	int2mac(addr + 1, enet1addr);
+	eth_env_set_enetaddr("eth1addr", enet1addr);
+
+	return 0;
+}
diff --git a/board/variscite/common/eth.h b/board/variscite/common/eth.h
new file mode 100644
index 0000000..a335c08
--- /dev/null
+++ b/board/variscite/common/eth.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023 Variscite Ltd.
+ *
+ */
+
+#ifndef _MX9_ETH_H_
+#define _MX9_ETH_H_
+
+int var_setup_mac(struct var_eeprom *eeprom);
+
+#endif /* _MX9_ETH_H_ */
diff --git a/board/variscite/common/imx9_eeprom.c b/board/variscite/common/imx9_eeprom.c
new file mode 100644
index 0000000..32551af
--- /dev/null
+++ b/board/variscite/common/imx9_eeprom.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Variscite Ltd.
+ *
+ */
+#include <command.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/io.h>
+#include <cpu_func.h>
+#include <u-boot/crc.h>
+#include <asm/arch-imx9/ddr.h>
+
+#include "imx9_eeprom.h"
+
+static int var_eeprom_get_dev(struct udevice **devp)
+{
+	int ret;
+	struct udevice *bus;
+
+	ret = uclass_get_device_by_name(UCLASS_I2C, VAR_SOM_EEPROM_I2C_NAME, &bus);
+	if (ret) {
+		printf("%s: No EEPROM I2C bus '%s'\n", __func__,
+		       VAR_SOM_EEPROM_I2C_NAME);
+		return ret;
+	}
+
+	ret = dm_i2c_probe(bus, VAR_SOM_EEPROM_I2C_ADDR, 0, devp);
+	if (ret) {
+		printf("%s: I2C EEPROM probe failed\n", __func__);
+		return ret;
+	}
+
+	i2c_set_chip_offset_len(*devp, 1);
+	i2c_set_chip_addr_offset_mask(*devp, 1);
+
+	return 0;
+}
+
+int var_eeprom_read_header(struct var_eeprom *e)
+{
+	int ret;
+	struct udevice *dev;
+
+	ret = var_eeprom_get_dev(&dev);
+	if (ret) {
+		printf("%s: Failed to detect I2C EEPROM\n", __func__);
+		return ret;
+	}
+
+	/* Read EEPROM header to memory */
+	ret = dm_i2c_read(dev, 0, (void *)e, sizeof(*e));
+	if (ret) {
+		printf("%s: EEPROM read failed, ret=%d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int var_eeprom_get_mac(struct var_eeprom *ep, u8 *mac)
+{
+	flush_dcache_all();
+	if (!var_eeprom_is_valid(ep))
+		return -1;
+
+	memcpy(mac, ep->mac, sizeof(ep->mac));
+
+	return 0;
+}
+
+int var_eeprom_get_dram_size(struct var_eeprom *ep, phys_size_t *size)
+{
+	/* No data in EEPROM - return default DRAM size */
+	if (!var_eeprom_is_valid(ep)) {
+		*size = DEFAULT_SDRAM_SIZE;
+		return 0;
+	}
+
+	*size = (ep->dramsize * 128UL) << 20;
+	return 0;
+}
+
+void var_eeprom_print_prod_info(struct var_eeprom *ep)
+{
+	if (IS_ENABLED(CONFIG_SPL_BUILD))
+		return;
+
+	flush_dcache_all();
+
+	if (!var_eeprom_is_valid(ep))
+		return;
+
+	if (IS_ENABLED(CONFIG_TARGET_IMX93_VAR_SOM))
+		printf("\nPart number: VSM-MX93-%.*s\n",
+		       (int)sizeof(ep->partnum), ep->partnum);
+
+	printf("Assembly: AS%.*s\n", (int)sizeof(ep->assembly), (char *)ep->assembly);
+
+	printf("Production date: %.*s %.*s %.*s\n",
+	       4, /* YYYY */
+	       (char *)ep->date,
+	       3, /* MMM */
+	       ((char *)ep->date) + 4,
+	       2, /* DD */
+	       ((char *)ep->date) + 4 + 3);
+
+	printf("Serial Number: %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       ep->mac[0], ep->mac[1], ep->mac[2], ep->mac[3], ep->mac[4], ep->mac[5]);
+
+	debug("EEPROM version: 0x%x\n", ep->version);
+	debug("SOM features: 0x%x\n", ep->features);
+	printf("SOM revision: 0x%x\n", ep->somrev);
+	printf("DRAM PN: VIC-%04d\n", ep->ddr_vic);
+	debug("DRAM size: %d GiB\n\n", (ep->dramsize * 128) / 1024);
+}
+
+int var_carrier_eeprom_read(const char *bus_name, int addr, struct var_carrier_eeprom *ep)
+{
+	int ret;
+	struct udevice *bus;
+	struct udevice *dev;
+
+	ret = uclass_get_device_by_name(UCLASS_I2C, bus_name, &bus);
+	if (ret) {
+		printf("%s: No bus '%s'\n", __func__, bus_name);
+		return ret;
+	}
+
+	ret = dm_i2c_probe(bus, addr, 0, &dev);
+	if (ret) {
+		printf("%s: Carrier EEPROM I2C probe failed\n", __func__);
+		return ret;
+	}
+
+	/* Read EEPROM to memory */
+	ret = dm_i2c_read(dev, 0, (void *)ep, sizeof(*ep));
+	if (ret) {
+		printf("%s: Carrier EEPROM read failed, ret=%d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int var_carrier_eeprom_is_valid(struct var_carrier_eeprom *ep)
+{
+	u32 crc, crc_offset = offsetof(struct var_carrier_eeprom, crc);
+
+	if (htons(ep->magic) != VAR_CARRIER_EEPROM_MAGIC) {
+		printf("Invalid carrier EEPROM magic 0x%x, expected 0x%x\n",
+		       htons(ep->magic), VAR_CARRIER_EEPROM_MAGIC);
+		return 0;
+	}
+
+	if (ep->struct_ver < 1) {
+		printf("Invalid carrier EEPROM version 0x%x\n", ep->struct_ver);
+		return 0;
+	}
+
+	if (ep->struct_ver == 1)
+		return 1;
+
+	/* Only EEPROM structure above version 1 has CRC field */
+	crc = crc32(0, (void *)ep, crc_offset);
+
+	if (crc != ep->crc) {
+		printf("Carrier EEPROM CRC mismatch (%08x != %08x)\n",
+		       crc, be32_to_cpu(ep->crc));
+		return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Returns carrier board revision string via 'rev' argument.  For legacy
+ * carrier board revisions the "legacy" string is returned.  For new carrier
+ * board revisions the actual carrier revision is returned.  Symphony-Board
+ * 1.4 and below are legacy, 1.4a and above are new.  DT8MCustomBoard 1.4 and
+ * below are legacy, 2.0 and above are new.
+ *
+ */
+void var_carrier_eeprom_get_revision(struct var_carrier_eeprom *ep, char *rev, size_t size)
+{
+	if (var_carrier_eeprom_is_valid(ep))
+		strlcpy(rev, (const char *)ep->carrier_rev, size);
+	else
+		strlcpy(rev, "legacy", size);
+}
diff --git a/board/variscite/common/imx9_eeprom.h b/board/variscite/common/imx9_eeprom.h
new file mode 100644
index 0000000..ed33368
--- /dev/null
+++ b/board/variscite/common/imx9_eeprom.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023 Variscite Ltd.
+ *
+ */
+
+#ifndef _MX9_VAR_EEPROM_H_
+#define _MX9_VAR_EEPROM_H_
+
+#ifdef CONFIG_ARCH_IMX9
+#include <asm/arch-imx9/ddr.h>
+#endif
+
+#define VAR_SOM_EEPROM_MAGIC	0x4D58 /* == HEX("MX") */
+
+#define VAR_SOM_EEPROM_I2C_ADDR	0x52
+
+/* Optional SOM features */
+#define VAR_EEPROM_F_WIFI		BIT(0)
+#define VAR_EEPROM_F_ETH		BIT(1)
+#define VAR_EEPROM_F_AUDIO		BIT(2)
+
+/* SOM storage types */
+enum som_storage {
+	SOM_STORAGE_EMMC,
+	SOM_STORAGE_NAND,
+	SOM_STORAGE_UNDEFINED,
+};
+
+/* Number of DRAM adjustment tables */
+#define DRAM_TABLE_NUM 7
+
+struct __packed var_eeprom
+{
+	u16 magic;			/* 00-0x00 - magic number       */
+	u8 partnum[8];			/* 02-0x02 - part number        */
+	u8 assembly[10];		/* 10-0x0a - assembly number    */
+	u8 date[9];			/* 20-0x14 - build date         */
+	u8 mac[6];			/* 29-0x1d - MAC address        */
+	u8 somrev;			/* 35-0x23 - SOM revision       */
+	u8 version;			/* 36-0x24 - EEPROM version     */
+	u8 features;			/* 37-0x25 - SOM features       */
+	u8 dramsize;			/* 38-0x26 - DRAM size          */
+	u8 reserved[5];			/* 39 0x27 - reserved           */
+	u32 ddr_crc32;			/* 44-0x2c - CRC32 of DDR DATAi */
+	u16 ddr_vic;			/* 48-0x30 - DDR VIC PN         */
+	u16 off[DRAM_TABLE_NUM + 1];	/* 50-0x32 - DRAM table offsets */
+};
+
+#define VAR_EEPROM_DATA ((struct var_eeprom *)VAR_EEPROM_DRAM_START)
+
+#define VAR_CARRIER_EEPROM_MAGIC	0x5643 /* == HEX("VC") */
+
+#define CARRIER_REV_LEN 16
+struct __packed var_carrier_eeprom
+{
+	u16 magic;                          /* 00-0x00 - magic number		*/
+	u8 struct_ver;                      /* 01-0x01 - EEPROM structure version	*/
+	u8 carrier_rev[CARRIER_REV_LEN];    /* 02-0x02 - carrier board revision	*/
+	u32 crc;                            /* 10-0x0a - checksum			*/
+};
+
+static inline int var_eeprom_is_valid(struct var_eeprom *ep)
+{
+	if (htons(ep->magic) != VAR_SOM_EEPROM_MAGIC) {
+		debug("Invalid EEPROM magic 0x%x, expected 0x%x\n",
+		      htons(ep->magic), VAR_SOM_EEPROM_MAGIC);
+		return 0;
+	}
+
+	return 1;
+}
+
+int var_eeprom_read_header(struct var_eeprom *e);
+int var_eeprom_get_dram_size(struct var_eeprom *e, phys_size_t *size);
+int var_eeprom_get_mac(struct var_eeprom *e, u8 *mac);
+void var_eeprom_print_prod_info(struct var_eeprom *e);
+
+int var_carrier_eeprom_read(const char *bus_name, int addr, struct var_carrier_eeprom *ep);
+int var_carrier_eeprom_is_valid(struct var_carrier_eeprom *ep);
+void var_carrier_eeprom_get_revision(struct var_carrier_eeprom *ep, char *rev, size_t size);
+
+#endif /* _MX9_VAR_EEPROM_H_ */
diff --git a/board/variscite/common/mmc.c b/board/variscite/common/mmc.c
new file mode 100644
index 0000000..0db416d
--- /dev/null
+++ b/board/variscite/common/mmc.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
+ *
+ */
+#include <command.h>
+#include <asm/arch/sys_proto.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <stdbool.h>
+#include <mmc.h>
+#include <vsprintf.h>
+#include <env.h>
+
+static int check_mmc_autodetect(void)
+{
+	char *autodetect_str = env_get("mmcautodetect");
+
+	if (autodetect_str && (strcmp(autodetect_str, "yes") == 0))
+		return 1;
+
+	return 0;
+}
+
+/* This should be defined for each board */
+__weak int mmc_map_to_kernel_blk(int dev_no)
+{
+	return dev_no;
+}
+
+void board_late_mmc_env_init(void)
+{
+	char cmd[32];
+	u32 dev_no = mmc_get_env_dev();
+
+	if (!check_mmc_autodetect())
+		return;
+
+	env_set_ulong("mmcdev", dev_no);
+
+	/* Set mmcblk env */
+	env_set_ulong("mmcblk", mmc_map_to_kernel_blk(dev_no));
+
+	sprintf(cmd, "mmc dev %d", dev_no);
+	run_command(cmd, 0);
+}