ec/starlabs/merlin: Add support for the ITE mirror flag

When enabled, the EC will mirror the firmware contained inside the
coreboot ROM. This allows it to be updated at the same time as
coreboot.

Enable the mirror flag if the installed EC firmware does not match
the target version or if a CMOS option, "manual_mirror_flag" is
set.

Signed-off-by: Sean Rhodes <sean@starlabs.systems>
Change-Id: I377abbb37dc4d3e535e518a73e73969b25967daa
Reviewed-on: https://review.coreboot.org/c/coreboot/+/73044
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
diff --git a/src/ec/starlabs/merlin/ite.c b/src/ec/starlabs/merlin/ite.c
index 357c67c..9730647 100644
--- a/src/ec/starlabs/merlin/ite.c
+++ b/src/ec/starlabs/merlin/ite.c
@@ -6,6 +6,7 @@
 #include <ec/acpi/ec.h>
 #include <option.h>
 #include <pc80/keyboard.h>
+#include <halt.h>
 
 #include "ec.h"
 #include "ecdefs.h"
@@ -26,6 +27,66 @@
 	return lut[index];
 }
 
+static void ec_mirror_with_count(void)
+{
+	unsigned int cmos_mirror_flag_counter = get_uint_option("mirror_flag_counter", UINT_MAX);
+
+	if (cmos_mirror_flag_counter != UINT_MAX) {
+		printk(BIOS_DEBUG, "ITE: mirror_flag_counter = %u\n", cmos_mirror_flag_counter);
+
+		/* Avoid boot loops by only trying a state change once */
+		if (cmos_mirror_flag_counter < MIRROR_ATTEMPTS) {
+			cmos_mirror_flag_counter++;
+			set_uint_option("mirror_flag_counter", cmos_mirror_flag_counter);
+			printk(BIOS_DEBUG, "ITE: Mirror attempt %u/%u.\n", cmos_mirror_flag_counter,
+				MIRROR_ATTEMPTS);
+
+			/* Write the EC mirror flag */
+			ec_write(ECRAM_MIRROR_FLAG, MIRROR_ENABLED);
+
+			/* Check what has been written */
+			if (ec_read(ECRAM_MIRROR_FLAG) == MIRROR_ENABLED)
+				poweroff();
+		} else {
+			/*
+			 * If the mirror flags fails after 1 attempt, it will
+			 * likely need a cold boot, or recovering.
+			 */
+			printk(BIOS_ERR, "ITE: Failed to mirror the EC in %u attempts!\n",
+				MIRROR_ATTEMPTS);
+		}
+	} else {
+		printk(BIOS_DEBUG, "ITE: Powering Off");
+		/* Write the EC mirror flag */
+		ec_write(ECRAM_MIRROR_FLAG, MIRROR_ENABLED);
+
+		/* Check what has been written */
+		if (ec_read(ECRAM_MIRROR_FLAG) == MIRROR_ENABLED)
+			poweroff();
+	}
+}
+
+
+void ec_mirror_flag(void)
+{
+	/*
+	 * For the mirror flag to work, the status of the EC pin must be known
+	 * at all times, which means external power. This can be either a DC
+	 * charger, or PD with CCG6. PD with an ANX7447 requires configuration
+	 * from the EC, so the update will interrupt this.
+	 *
+	 * This means we can unconditionally apply the mirror flag to devices
+	 * that have CCG6, present on devices with TBT, but have a manual
+	 * flag for devices without it.
+	 */
+	if (CONFIG(EC_STARLABS_MIRROR_SUPPORT) &&
+		(CONFIG(SOC_INTEL_COMMON_BLOCK_TCSS) || get_uint_option("mirror_flag", 0)) &&
+		(ec_get_version() != CONFIG_EC_STARLABS_MIRROR_VERSION))	{
+		printk(BIOS_ERR, "ITE: System and EC ROM version mismatch.\n");
+		ec_mirror_with_count();
+	}
+}
+
 static uint16_t ec_get_chip_id(unsigned int port)
 {
 	return (pnp_read_index(port, ITE_CHIPID1) << 8) |
@@ -57,6 +118,8 @@
 		return;
 	}
 
+	ec_mirror_flag();
+
 	pc_keyboard_init(NO_AUX_DEVICE);
 
 	/*