mach-snapdragon: handle platforms without PSCI support

Most MSM8916 devices shipped without PSCI support. The history is quite
nuanced (a good overview can be found in [1]), but the end result is
that the upstream DTs for this SoC pretend that PSCI exists, and it's
expected that the bootloader handles the case where it doesn't. This is
codified by the de-facto bootloader for MSM8916 devices, lk2nd [2].

So we handle it here by deleting the /psci node if we detect the absence
of PSCI. We need to do this early to ensure sysreset works correctly,
since the PSCI firmware driver is PRE_RELOC and binds the PSCI sysreset
driver.

Additionally, show_psci_version is updated to check that PSCI exists.
Currently this banner outputs "PSCI: 65535.65535" on devices without
PSCI support, which isn't very useful :)

[1]: https://github.com/msm8916-mainline/linux/issues/388
[2]: https://github.com/msm8916-mainline/lk2nd/blob/8183ea2/lk2nd/smp/spin-table/spin-table.c#L237

Signed-off-by: Sam Day <me@samcday.com>
Link: https://lore.kernel.org/r/20250127-qcom-handle-absent-psci-v1-1-e762f2db938c@samcday.com
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
index e875517..75b9cf1 100644
--- a/arch/arm/mach-snapdragon/board.c
+++ b/arch/arm/mach-snapdragon/board.c
@@ -162,11 +162,42 @@
 
 	arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
 
+	/* Some older SoCs like MSM8916 don't always support PSCI */
+	if ((int)res.a0 == PSCI_RET_NOT_SUPPORTED)
+		return;
+
 	debug("PSCI:  v%ld.%ld\n",
 	      PSCI_VERSION_MAJOR(res.a0),
 	      PSCI_VERSION_MINOR(res.a0));
 }
 
+/**
+ * Most MSM8916 devices in the wild shipped without PSCI support, but the
+ * upstream DTs pretend that PSCI exists. If that situation is detected here,
+ * the /psci node is deleted. This is done very early to ensure the PSCI
+ * firmware driver doesn't bind (which then binds a sysreset driver that won't
+ * work).
+ */
+static void qcom_psci_fixup(void *fdt)
+{
+	int offset, ret;
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
+
+	if ((int)res.a0 != PSCI_RET_NOT_SUPPORTED)
+		return;
+
+	offset = fdt_path_offset(fdt, "/psci");
+	if (offset < 0)
+		return;
+
+	debug("Found /psci DT node on device with no PSCI. Deleting.\n");
+	ret = fdt_del_node(fdt, offset);
+	if (ret)
+		log_err("Failed to delete /psci node: %d\n", ret);
+}
+
 /* We support booting U-Boot with an internal DT when running as a first-stage bootloader
  * or for supporting quirky devices where it's easier to leave the downstream DT in place
  * to improve ABL compatibility. Otherwise, we use the DT provided by ABL.
@@ -212,12 +243,16 @@
 
 	if (internal_valid) {
 		debug("Using built in FDT\n");
-		return -EEXIST;
+		ret = -EEXIST;
+	} else {
+		debug("Using external FDT\n");
+		*fdtp = external_fdt;
+		ret = 0;
 	}
 
-	debug("Using external FDT\n");
-	*fdtp = external_fdt;
-	return 0;
+	qcom_psci_fixup(*fdtp);
+
+	return ret;
 }
 
 void reset_cpu(void)