@@ -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,
};
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(-)