stm32mp1: support forced boot mode

The boot mode can be forced by key press
or by TAMP register, requested in kernel by syscon-reboot-mode

tamp: tamp@5c00a000 {
	compatible = "simple-bus", "syscon", "simple-mfd";
	reg = <0x5c00a000 0x400>;

	reboot-mode {
		compatible = "syscon-reboot-mode";
		offset = <0x150>; /* reg20 */
		mask = <0xff>;
		mode-normal = <0>;
		mode-fastboot = <0x1>;
		mode-recovery = <0x2>;
		mode-stm32cubeprogrammer = <0x3>;
		mode-ums_mmc0 = <0x10>;
		mode-ums_mmc1 = <0x11>;
		mode-ums_mmc2 = <0x12>;
	};
};

Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
diff --git a/arch/arm/dts/stm32mp157c-ed1-u-boot.dtsi b/arch/arm/dts/stm32mp157c-ed1-u-boot.dtsi
index 70bbf66..d22401c 100644
--- a/arch/arm/dts/stm32mp157c-ed1-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp157c-ed1-u-boot.dtsi
@@ -14,6 +14,11 @@
 		i2c3 = &i2c4;
 	};
 
+	config {
+		st,fastboot-gpios = <&gpioa 13 GPIO_ACTIVE_LOW>;
+		st,stm32prog-gpios = <&gpioa 14 GPIO_ACTIVE_LOW>;
+	};
+
 	led {
 		compatible = "gpio-leds";
 
diff --git a/arch/arm/mach-stm32mp/cpu.c b/arch/arm/mach-stm32mp/cpu.c
index 206b82e..305ea6d 100644
--- a/arch/arm/mach-stm32mp/cpu.c
+++ b/arch/arm/mach-stm32mp/cpu.c
@@ -359,12 +359,12 @@
 	u32 boot_mode =
 		(boot_ctx & TAMP_BOOT_MODE_MASK) >> TAMP_BOOT_MODE_SHIFT;
 	int instance = (boot_mode & TAMP_BOOT_INSTANCE_MASK) - 1;
+	u32 forced_mode = (boot_ctx & TAMP_BOOT_FORCED_MASK);
 	struct udevice *dev;
 	int alias;
 
-	pr_debug("%s: boot_ctx=0x%x => boot_mode=%x, instance=%d\n",
-		 __func__, boot_ctx, boot_mode, instance);
-
+	pr_debug("%s: boot_ctx=0x%x => boot_mode=%x, instance=%d forced=%x\n",
+		 __func__, boot_ctx, boot_mode, instance, forced_mode);
 	switch (boot_mode & TAMP_BOOT_DEVICE_MASK) {
 	case BOOT_SERIAL_UART:
 		if (instance > ARRAY_SIZE(serial_addr))
@@ -409,6 +409,36 @@
 		pr_debug("unexpected boot mode = %x\n", boot_mode);
 		break;
 	}
+
+	switch (forced_mode) {
+	case BOOT_FASTBOOT:
+		printf("Enter fastboot!\n");
+		env_set("preboot", "env set preboot; fastboot 0");
+		break;
+	case BOOT_STM32PROG:
+		env_set("boot_device", "usb");
+		env_set("boot_instance", "0");
+		break;
+	case BOOT_UMS_MMC0:
+	case BOOT_UMS_MMC1:
+	case BOOT_UMS_MMC2:
+		printf("Enter UMS!\n");
+		instance = forced_mode - BOOT_UMS_MMC0;
+		sprintf(cmd, "env set preboot; ums 0 mmc %d", instance);
+		env_set("preboot", cmd);
+		break;
+	case BOOT_RECOVERY:
+		env_set("preboot", "env set preboot; run altbootcmd");
+		break;
+	case BOOT_NORMAL:
+		break;
+	default:
+		pr_debug("unexpected forced boot mode = %x\n", forced_mode);
+		break;
+	}
+
+	/* clear TAMP for next reboot */
+	clrsetbits_le32(TAMP_BOOT_CONTEXT, TAMP_BOOT_FORCED_MASK, BOOT_NORMAL);
 }
 
 /*
diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h
index f2ab026..da23af0 100644
--- a/arch/arm/mach-stm32mp/include/mach/stm32.h
+++ b/arch/arm/mach-stm32mp/include/mach/stm32.h
@@ -92,6 +92,17 @@
 #define TAMP_BOOT_MODE_SHIFT		8
 #define TAMP_BOOT_DEVICE_MASK		GENMASK(7, 4)
 #define TAMP_BOOT_INSTANCE_MASK		GENMASK(3, 0)
+#define TAMP_BOOT_FORCED_MASK		GENMASK(7, 0)
+
+enum forced_boot_mode {
+	BOOT_NORMAL = 0x00,
+	BOOT_FASTBOOT = 0x01,
+	BOOT_RECOVERY = 0x02,
+	BOOT_STM32PROG = 0x03,
+	BOOT_UMS_MMC0 = 0x10,
+	BOOT_UMS_MMC1 = 0x11,
+	BOOT_UMS_MMC2 = 0x12,
+};
 
 /* offset used for BSEC driver: misc_read and misc_write */
 #define STM32_BSEC_SHADOW_OFFSET	0x0
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c
index 0d963c2..d13793e 100644
--- a/board/st/stm32mp1/stm32mp1.c
+++ b/board/st/stm32mp1/stm32mp1.c
@@ -67,6 +67,55 @@
 	return 0;
 }
 
+static void board_key_check(void)
+{
+#if defined(CONFIG_FASTBOOT) || defined(CONFIG_CMD_STM32PROG)
+	ofnode node;
+	struct gpio_desc gpio;
+	enum forced_boot_mode boot_mode = BOOT_NORMAL;
+
+	node = ofnode_path("/config");
+	if (!ofnode_valid(node)) {
+		debug("%s: no /config node?\n", __func__);
+		return;
+	}
+#ifdef CONFIG_FASTBOOT
+	if (gpio_request_by_name_nodev(node, "st,fastboot-gpios", 0,
+				       &gpio, GPIOD_IS_IN)) {
+		debug("%s: could not find a /config/st,fastboot-gpios\n",
+		      __func__);
+	} else {
+		if (dm_gpio_get_value(&gpio)) {
+			puts("Fastboot key pressed, ");
+			boot_mode = BOOT_FASTBOOT;
+		}
+
+		dm_gpio_free(NULL, &gpio);
+	}
+#endif
+#ifdef CONFIG_CMD_STM32PROG
+	if (gpio_request_by_name_nodev(node, "st,stm32prog-gpios", 0,
+				       &gpio, GPIOD_IS_IN)) {
+		debug("%s: could not find a /config/st,stm32prog-gpios\n",
+		      __func__);
+	} else {
+		if (dm_gpio_get_value(&gpio)) {
+			puts("STM32Programmer key pressed, ");
+			boot_mode = BOOT_STM32PROG;
+		}
+		dm_gpio_free(NULL, &gpio);
+	}
+#endif
+
+	if (boot_mode != BOOT_NORMAL) {
+		puts("entering download mode...\n");
+		clrsetbits_le32(TAMP_BOOT_CONTEXT,
+				TAMP_BOOT_FORCED_MASK,
+				boot_mode);
+	}
+#endif
+}
+
 static struct dwc2_plat_otg_data stm32mp_otg_data = {
 	.usb_gusbcfg = STM32MP_GUSBCFG,
 };
@@ -227,6 +276,8 @@
 	/* address of boot parameters */
 	gd->bd->bi_boot_params = STM32_DDR_BASE + 0x100;
 
+	board_key_check();
+
 	if (IS_ENABLED(CONFIG_LED))
 		led_default_state();