diff mbox series

[V4,5/8] remoteproc: qcom: add support for Q6 device-tree loading

Message ID 20250403120304.2345677-6-gokul.sriram.p@oss.qualcomm.com (mailing list archive)
State New
Headers show
Series Add new driver for WCSS secure PIL loading | expand

Commit Message

Gokul Sriram P April 3, 2025, 12:03 p.m. UTC
In IPQ5332 and IPQ5424 SoCs, Q6 requires separate device-tree.

Signed-off-by: Gokul Sriram Palanisamy <gokul.sriram.p@oss.qualcomm.com>
---
 drivers/remoteproc/qcom_q6v5_wcss_sec.c | 147 ++++++++++++++++++++++--
 1 file changed, 137 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/drivers/remoteproc/qcom_q6v5_wcss_sec.c b/drivers/remoteproc/qcom_q6v5_wcss_sec.c
index 30422c6c982d..e4cc7caebd7c 100644
--- a/drivers/remoteproc/qcom_q6v5_wcss_sec.c
+++ b/drivers/remoteproc/qcom_q6v5_wcss_sec.c
@@ -10,9 +10,12 @@ 
 #include <linux/io.h>
 #include <linux/mailbox_client.h>
 #include <linux/mailbox/tmelcom-qmp.h>
+#include <linux/mfd/syscon.h>
 #include <linux/of_reserved_mem.h>
 #include <linux/platform_device.h>
+#include <linux/regmap.h>
 #include <linux/soc/qcom/mdt_loader.h>
+#include <linux/soc/qcom/smem.h>
 
 #include "qcom_common.h"
 #include "qcom_q6v5.h"
@@ -34,6 +37,18 @@  struct wcss_sec {
 	phys_addr_t mem_reloc;
 	void *mem_region;
 	size_t mem_size;
+
+	const char *dtb_firmware_name;
+	const struct firmware *dtb_firmware;
+	phys_addr_t dtb_mem_phys;
+	phys_addr_t dtb_mem_reloc;
+	void *dtb_mem_region;
+	size_t dtb_mem_size;
+	struct regmap *tcsr_map;
+	u32 dtb_low_offset;
+	u32 dtb_high_offset;
+	u32 machid_offset;
+
 	const struct wcss_data *desc;
 
 	struct mbox_client mbox_client;
@@ -46,6 +61,7 @@  struct wcss_data {
 	u32 pasid;
 	const char *ss_name;
 	bool auto_boot;
+	const char *dtb_firmware_name;
 	bool tmelcom;
 };
 
@@ -137,8 +153,45 @@  static int wcss_sec_load(struct rproc *rproc, const struct firmware *fw)
 {
 	struct wcss_sec *wcss = rproc->priv;
 	struct device *dev = wcss->dev;
+	u32 machid;
 	int ret;
 
+	if (wcss->dtb_firmware_name) {
+		ret = request_firmware(&wcss->dtb_firmware, wcss->dtb_firmware_name, wcss->dev);
+		if (ret) {
+			dev_err(wcss->dev, "request_firmware failed for %s: %d\n",
+				wcss->dtb_firmware_name, ret);
+			return ret;
+		}
+
+		ret = qcom_mdt_load_no_init(wcss->dev, wcss->dtb_firmware,
+					    wcss->dtb_firmware_name, 0, wcss->dtb_mem_region,
+					    wcss->dtb_mem_phys, wcss->dtb_mem_size,
+					    &wcss->dtb_mem_reloc);
+		if (ret)
+			goto release_dtb_firmware;
+
+		ret = qcom_smem_get_machid(&machid);
+		if (ret) {
+			pr_err("machid get failed, ret = %d\n", ret);
+			goto release_dtb_firmware;
+		}
+
+		ret = regmap_write(wcss->tcsr_map, wcss->dtb_low_offset,
+				   wcss->dtb_mem_phys & 0xFFFFFFFF);
+		if (ret)
+			goto release_dtb_firmware;
+
+		ret = regmap_write(wcss->tcsr_map, wcss->dtb_high_offset,
+				   wcss->dtb_mem_phys & (0xFFFFFFFF << 31));
+		if (ret)
+			goto release_dtb_firmware;
+
+		ret = regmap_write(wcss->tcsr_map, wcss->machid_offset, machid);
+		if (ret)
+			goto release_dtb_firmware;
+	}
+
 	if (!IS_ERR_OR_NULL(wcss->mbox_chan)) {
 		wcss->metadata = qcom_mdt_read_metadata(fw, &wcss->metadata_len,
 							rproc->firmware, wcss->dev);
@@ -146,26 +199,33 @@  static int wcss_sec_load(struct rproc *rproc, const struct firmware *fw)
 			ret = PTR_ERR(wcss->metadata);
 			dev_err(wcss->dev, "error %d reading firmware %s metadata\n",
 				ret, rproc->firmware);
-			return ret;
+			goto release_dtb_firmware;
 		}
 
 		ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, wcss->desc->pasid,
 					    wcss->mem_region, wcss->mem_phys, wcss->mem_size,
 					    &wcss->mem_reloc);
-		if (ret) {
-			kfree(wcss->metadata);
-			return ret;
-		}
+		if (ret)
+			goto release_metadata;
 	} else {
 		ret = qcom_mdt_load(dev, fw, rproc->firmware, wcss->desc->pasid, wcss->mem_region,
 				    wcss->mem_phys, wcss->mem_size, &wcss->mem_reloc);
 		if (ret)
-			return ret;
+			goto release_dtb_firmware;
 	}
 
 	qcom_pil_info_store("wcss", wcss->mem_phys, wcss->mem_size);
 
 	return 0;
+
+release_metadata:
+	if (!IS_ERR_OR_NULL(wcss->mbox_chan))
+		kfree(wcss->metadata);
+release_dtb_firmware:
+	if (wcss->dtb_firmware_name)
+		release_firmware(wcss->dtb_firmware);
+
+	return ret;
 }
 
 static unsigned long wcss_sec_panic(struct rproc *rproc)
@@ -282,6 +342,33 @@  static int wcss_sec_alloc_memory_region(struct wcss_sec *wcss)
 		return -ENOMEM;
 	}
 
+	if (!wcss->dtb_firmware_name)
+		return 0;
+
+	node = of_parse_phandle(dev->of_node, "memory-region", 1);
+	if (!node) {
+		dev_err(dev, "can't find dtb memory-region\n");
+		return -EINVAL;
+	}
+
+	rmem = of_reserved_mem_lookup(node);
+	of_node_put(node);
+
+	if (!rmem) {
+		dev_err(dev, "unable to resolve dtb memory-region\n");
+		return -EINVAL;
+	}
+
+	wcss->dtb_mem_phys = rmem->base;
+	wcss->dtb_mem_reloc = rmem->base;
+	wcss->dtb_mem_size = rmem->size;
+	wcss->dtb_mem_region = devm_ioremap_wc(dev, wcss->dtb_mem_phys, wcss->dtb_mem_size);
+	if (!wcss->dtb_mem_region) {
+		dev_err(dev, "unable to map dtb memory region: %pa+%pa\n",
+			&rmem->base, &rmem->size);
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 
@@ -291,6 +378,9 @@  static int wcss_sec_probe(struct platform_device *pdev)
 	struct wcss_sec *wcss;
 	struct clk *sleep_clk;
 	const char *fw_name = NULL;
+	const char *dtb_fw_name = NULL;
+	struct of_phandle_args args;
+	int args_cell_cnt = 3;
 	const struct wcss_data *desc = of_device_get_match_data(&pdev->dev);
 	int ret;
 
@@ -310,6 +400,33 @@  static int wcss_sec_probe(struct platform_device *pdev)
 	wcss->dev = &pdev->dev;
 	wcss->desc = desc;
 
+	if (desc->dtb_firmware_name) {
+		ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name",
+						    1, &dtb_fw_name);
+		if (ret < 0)
+			return ret;
+
+		if (dtb_fw_name)
+			wcss->dtb_firmware_name = dtb_fw_name;
+
+		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, "qcom,q6-dtb-info",
+						       args_cell_cnt, 0, &args);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to parse qcom,q6-dtb-info\n");
+			return ret;
+		}
+
+		wcss->tcsr_map = syscon_node_to_regmap(args.np);
+		of_node_put(args.np);
+		if (IS_ERR(wcss->tcsr_map))
+			return dev_err_probe(&pdev->dev, PTR_ERR(wcss->tcsr_map),
+					     "unable to get syscon regmap\n");
+
+		wcss->dtb_low_offset = args.args[0];
+		wcss->dtb_high_offset = args.args[1];
+		wcss->machid_offset = args.args[2];
+	}
+
 	ret = wcss_sec_alloc_memory_region(wcss);
 	if (ret)
 		return ret;
@@ -336,18 +453,26 @@  static int wcss_sec_probe(struct platform_device *pdev)
 		wcss->mbox_client.knows_txdone = true;
 		wcss->mbox_client.tx_block = true;
 		wcss->mbox_chan = mbox_request_channel(&wcss->mbox_client, 0);
-		if (IS_ERR(wcss->mbox_chan))
-			return dev_err_probe(wcss->dev, PTR_ERR(wcss->mbox_chan),
-					     "mbox chan for IPC is missing\n");
+		if (IS_ERR(wcss->mbox_chan)) {
+			ret = dev_err_probe(&pdev->dev, PTR_ERR(wcss->mbox_chan),
+					    "mbox chan for IPC is missing\n");
+			goto remove_subdevs;
+		}
 	}
 
 	ret = devm_rproc_add(&pdev->dev, rproc);
 	if (ret)
-		return ret;
+		goto remove_subdevs;
 
 	platform_set_drvdata(pdev, rproc);
 
 	return 0;
+
+remove_subdevs:
+	qcom_remove_glink_subdev(rproc, &wcss->glink_subdev);
+	qcom_remove_ssr_subdev(rproc, &wcss->ssr_subdev);
+
+	return ret;
 }
 
 static void wcss_sec_remove(struct platform_device *pdev)
@@ -364,11 +489,13 @@  static void wcss_sec_remove(struct platform_device *pdev)
 static const struct wcss_data wcss_sec_ipq5332_res_init = {
 	.pasid = MPD_WCSS_PAS_ID,
 	.ss_name = "q6wcss",
+	.dtb_firmware_name = "qdsp6sw_dtb.mbn",
 };
 
 static const struct wcss_data wcss_sec_ipq5424_res_init = {
 	.pasid = MPD_WCSS_PAS_ID,
 	.ss_name = "q6wcss",
+	.dtb_firmware_name = "qdsp6sw_dtb.mbn",
 	.tmelcom = true,
 };