remoteproc: k3-dsp: Add support for L2RAM loading on C66x DSPs

The resets for the DSP processors on K3 SoCs are managed through the
Power and Sleep Controller (PSC) module. Each DSP typically has two
resets - a global module reset for powering on the device, and a local
reset that affects only the CPU while allowing access to the other
sub-modules within the DSP processor sub-systems.

The C66x DSPs have two levels of internal RAMs that can be used to
boot from, and the firmware loading into these RAMs require the
local reset to be asserted with the device powered on/enabled using
the module reset. Enhance the K3 DSP remoteproc driver to add support
for loading into the internal RAMs. The local reset is deasserted on
SoC power-on-reset, so logic has to be added in probe in remoteproc
mode to balance the remoteproc state-machine.

Note that the local resets are a no-op on C71x cores, and the hardware
does not supporting loading into its internal RAMs.

Signed-off-by: Suman Anna <s-anna@ti.com>
diff --git a/drivers/remoteproc/ti_k3_dsp_rproc.c b/drivers/remoteproc/ti_k3_dsp_rproc.c
index 4937fdd..1fc8193 100644
--- a/drivers/remoteproc/ti_k3_dsp_rproc.c
+++ b/drivers/remoteproc/ti_k3_dsp_rproc.c
@@ -4,7 +4,7 @@
  *
  * Copyright (C) 2018-2020 Texas Instruments Incorporated - http://www.ti.com/
  *	Lokesh Vutla <lokeshvutla@ti.com>
- *
+ *	Suman Anna <s-anna@ti.com>
  */
 
 #include <common.h>
@@ -41,9 +41,11 @@
 /**
  * struct k3_dsp_boot_data - internal data structure used for boot
  * @boot_align_addr: Boot vector address alignment granularity
+ * @uses_lreset: Flag to denote the need for local reset management
  */
 struct k3_dsp_boot_data {
 	u32 boot_align_addr;
+	bool uses_lreset;
 };
 
 /**
@@ -62,6 +64,54 @@
 	int num_mems;
 };
 
+/*
+ * The C66x DSP cores have a local reset that affects only the CPU, and a
+ * generic module reset that powers on the device and allows the DSP internal
+ * memories to be accessed while the local reset is asserted. This function is
+ * used to release the global reset on C66x DSPs to allow loading into the DSP
+ * internal RAMs. This helper function is invoked in k3_dsp_load() before any
+ * actual firmware loading and is undone only in k3_dsp_stop(). The local reset
+ * on C71x cores is a no-op and the global reset cannot be released on C71x
+ * cores until after the firmware images are loaded, so this function does
+ * nothing for C71x cores.
+ */
+static int k3_dsp_prepare(struct udevice *dev)
+{
+	struct k3_dsp_privdata *dsp = dev_get_priv(dev);
+	struct k3_dsp_boot_data *data = dsp->data;
+	int ret;
+
+	/* local reset is no-op on C71x processors */
+	if (!data->uses_lreset)
+		return 0;
+
+	ret = ti_sci_proc_power_domain_on(&dsp->tsp);
+	if (ret)
+		dev_err(dev, "cannot enable internal RAM loading, ret = %d\n",
+			ret);
+
+	return ret;
+}
+
+/*
+ * This function is the counterpart to k3_dsp_prepare() and is used to assert
+ * the global reset on C66x DSP cores (no-op for C71x DSP cores). This completes
+ * the second step of powering down the C66x DSP cores. The cores themselves
+ * are halted through the local reset in first step. This function is invoked
+ * in k3_dsp_stop() after the local reset is asserted.
+ */
+static int k3_dsp_unprepare(struct udevice *dev)
+{
+	struct k3_dsp_privdata *dsp = dev_get_priv(dev);
+	struct k3_dsp_boot_data *data = dsp->data;
+
+	/* local reset is no-op on C71x processors */
+	if (!data->uses_lreset)
+		return 0;
+
+	return ti_sci_proc_power_domain_off(&dsp->tsp);
+}
+
 /**
  * k3_dsp_load() - Load up the Remote processor image
  * @dev:	rproc device pointer
@@ -82,10 +132,17 @@
 	if (ret)
 		return ret;
 
+	ret = k3_dsp_prepare(dev);
+	if (ret) {
+		dev_err(dev, "DSP prepare failed for core %d\n",
+			dsp->tsp.proc_id);
+		goto proc_release;
+	}
+
 	ret = rproc_elf_load_image(dev, addr, size);
 	if (ret < 0) {
 		dev_err(dev, "Loading elf failed %d\n", ret);
-		goto proc_release;
+		goto unprepare;
 	}
 
 	boot_vector = rproc_elf_get_boot_addr(dev, addr);
@@ -99,6 +156,9 @@
 	dev_dbg(dev, "%s: Boot vector = 0x%x\n", __func__, boot_vector);
 
 	ret = ti_sci_proc_set_config(&dsp->tsp, boot_vector, 0, 0);
+unprepare:
+	if (ret)
+		k3_dsp_unprepare(dev);
 proc_release:
 	ti_sci_proc_release(&dsp->tsp);
 	return ret;
@@ -113,6 +173,7 @@
 static int k3_dsp_start(struct udevice *dev)
 {
 	struct k3_dsp_privdata *dsp = dev_get_priv(dev);
+	struct k3_dsp_boot_data *data = dsp->data;
 	int ret;
 
 	dev_dbg(dev, "%s\n", __func__);
@@ -121,13 +182,17 @@
 	if (ret)
 		return ret;
 
-	ret = ti_sci_proc_power_domain_on(&dsp->tsp);
-	if (ret)
-		goto proc_release;
+	if (!data->uses_lreset) {
+		ret = ti_sci_proc_power_domain_on(&dsp->tsp);
+		if (ret)
+			goto proc_release;
+	}
 
 	ret = reset_deassert(&dsp->dsp_rst);
-	if (ret)
-		ti_sci_proc_power_domain_off(&dsp->tsp);
+	if (ret) {
+		if (!data->uses_lreset)
+			ti_sci_proc_power_domain_off(&dsp->tsp);
+	}
 
 proc_release:
 	ti_sci_proc_release(&dsp->tsp);
@@ -344,6 +409,15 @@
 		return ret;
 	}
 
+	/*
+	 * The DSP local resets are deasserted by default on Power-On-Reset.
+	 * Assert the local resets to ensure the DSPs don't execute bogus code
+	 * in .load() callback when the module reset is released to support
+	 * internal memory loading. This is needed for C66x DSPs, and is a
+	 * no-op on C71x DSPs.
+	 */
+	reset_assert(&dsp->dsp_rst);
+
 	dev_dbg(dev, "Remoteproc successfully probed\n");
 
 	return 0;
@@ -360,10 +434,12 @@
 
 static const struct k3_dsp_boot_data c66_data = {
 	.boot_align_addr = SZ_1K,
+	.uses_lreset = true,
 };
 
 static const struct k3_dsp_boot_data c71_data = {
 	.boot_align_addr = SZ_2M,
+	.uses_lreset = false,
 };
 
 static const struct udevice_id k3_dsp_ids[] = {