qualcommax: Add support for qcom remoteproc WCSS secure PIL driver

Add support for qcom remoteproc WCSS secure PIL driver.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Link: https://github.com/openwrt/openwrt/pull/20928
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
George Moussalem 2025-10-23 15:07:09 +04:00 committed by Robert Marko
parent cdf187f1e7
commit 00d61da588
12 changed files with 2096 additions and 48 deletions

View File

@ -433,6 +433,7 @@ CONFIG_QCOM_Q6V5_COMMON=y
# CONFIG_QCOM_Q6V5_MSS is not set
# CONFIG_QCOM_Q6V5_PAS is not set
CONFIG_QCOM_Q6V5_WCSS=y
# CONFIG_QCOM_Q6V5_WCSS_SEC is not set
# CONFIG_QCOM_QSEECOM is not set
# CONFIG_QCOM_RAMP_CTRL is not set
# CONFIG_QCOM_RMTFS_MEM is not set
@ -449,6 +450,7 @@ CONFIG_QCOM_SOCINFO=y
# CONFIG_QCOM_SPM is not set
# CONFIG_QCOM_STATS is not set
# CONFIG_QCOM_SYSMON is not set
# CONFIG_QCOM_TMEL_QMP_MAILBOX is not set
CONFIG_QCOM_TSENS=y
CONFIG_QCOM_TZMEM=y
CONFIG_QCOM_TZMEM_MODE_GENERIC=y

View File

@ -20,4 +20,6 @@ CONFIG_PWM_SYSFS=y
CONFIG_QCA83XX_PHY=y
CONFIG_QCOM_APM=y
CONFIG_QCOM_Q6V5_MPD=y
CONFIG_QCOM_Q6V5_WCSS_SEC=y
CONFIG_QCOM_TMEL_QMP_MAILBOX=y
CONFIG_SPI_QPIC_SNAND=y

View File

@ -0,0 +1,64 @@
From 9d67219c941458fd26eb474d25f9a6b2525876ca Mon Sep 17 00:00:00 2001
From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Date: Tue, 21 Oct 2025 15:38:28 +0400
Subject: [PATCH v6 1/8] firmware: qcom_scm: ipq5332: add support to pass metadata size
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Message-Id: <20251021-ipq5018-wifi-v6-1-c55c547df6fc@outlook.com>
IPQ5332 security software running under trustzone requires metadata size.
With new command support added in TrustZone that includes a size parameter,
pass metadata size as well.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Signed-off-by: Gokul Sriram Palanisamy <gokul.sriram.p@oss.qualcomm.com>
---
drivers/firmware/qcom/qcom_scm.c | 17 +++++++++++++----
drivers/firmware/qcom/qcom_scm.h | 1 +
2 files changed, 14 insertions(+), 4 deletions(-)
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -583,9 +583,6 @@ int qcom_scm_pas_init_image(u32 peripher
int ret;
struct qcom_scm_desc desc = {
.svc = QCOM_SCM_SVC_PIL,
- .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE,
- .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW),
- .args[0] = peripheral,
.owner = ARM_SMCCC_OWNER_SIP,
};
struct qcom_scm_res res;
@@ -617,7 +614,19 @@ int qcom_scm_pas_init_image(u32 peripher
if (ret)
goto disable_clk;
- desc.args[1] = mdata_phys;
+ if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PIL_PAS_INIT_IMAGE_V2)) {
+ desc.cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE_V2;
+ desc.arginfo = QCOM_SCM_ARGS(3, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL);
+ desc.args[0] = peripheral;
+ desc.args[1] = mdata_phys;
+ desc.args[2] = size;
+ } else {
+ desc.cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE;
+ desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW);
+ desc.args[0] = peripheral;
+ desc.args[1] = mdata_phys;
+ }
ret = qcom_scm_call(__scm->dev, &desc, &res);
qcom_scm_bw_disable();
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -101,6 +101,7 @@ struct qcom_tzmem_pool *qcom_scm_get_tzm
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+#define QCOM_SCM_PIL_PAS_INIT_IMAGE_V2 0x1a
#define QCOM_SCM_SVC_IO 0x05
#define QCOM_SCM_IO_READ 0x01

View File

@ -0,0 +1,157 @@
From db128ef0282b8cc41816caf39a03fa17d10fa865 Mon Sep 17 00:00:00 2001
From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Date: Tue, 21 Oct 2025 15:38:29 +0400
Subject: [PATCH v6 2/8] dt-bindings: remoteproc: qcom: document hexagon based WCSS secure PIL
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Message-Id: <20251021-ipq5018-wifi-v6-2-c55c547df6fc@outlook.com>
Add new binding document for hexagon based WCSS secure PIL remoteproc.
IPQ5018, IPQ5332, IPQ5424 and IPQ9574 follow secure PIL remoteproc.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Signed-off-by: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
---
.../bindings/remoteproc/qcom,wcss-sec-pil.yaml | 133 +++++++++++++++++++++
1 file changed, 133 insertions(+)
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,wcss-sec-pil.yaml
@@ -0,0 +1,135 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/remoteproc/qcom,wcss-sec-pil.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm WCSS Secure Peripheral Image Loader
+
+maintainers:
+ - Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+
+description:
+ Wireless Connectivity Subsystem (WCSS) Secure Peripheral Image Loader loads
+ firmware and power up QDSP6 remoteproc on the Qualcomm IPQ series SoC.
+
+properties:
+ compatible:
+ enum:
+ - qcom,ipq5018-wcss-sec-pil
+ - qcom,ipq5332-wcss-sec-pil
+ - qcom,ipq5424-wcss-sec-pil
+ - qcom,ipq9574-wcss-sec-pil
+
+ reg:
+ maxItems: 1
+
+ firmware-name:
+ maxItems: 1
+ description: Firmware name for the Hexagon core
+
+ interrupts:
+ items:
+ - description: Watchdog interrupt
+ - description: Fatal interrupt
+ - description: Ready interrupt
+ - description: Handover interrupt
+ - description: Stop acknowledge interrupt
+
+ interrupt-names:
+ items:
+ - const: wdog
+ - const: fatal
+ - const: ready
+ - const: handover
+ - const: stop-ack
+
+ clocks:
+ items:
+ - description: sleep clock
+ - description: AHB interconnect between system NOC and WCSS block
+
+ clock-names:
+ items:
+ - const: sleep
+ - const: interconnect
+
+ mboxes:
+ maxItems: 1
+ items:
+ - description: A phandle to the TMECom mailbox device node
+
+ qcom,smem-states:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: States used by the AP to signal the remote processor
+ items:
+ - description: Stop Q6
+ - description: Shutdown Q6
+
+ qcom,smem-state-names:
+ description:
+ Names of the states used by the AP to signal the remote processor
+ items:
+ - const: stop
+ - const: shutdown
+
+ memory-region:
+ items:
+ - description: Q6 reserved region
+
+ glink-edge:
+ $ref: /schemas/remoteproc/qcom,glink-edge.yaml#
+ description:
+ Qualcomm G-Link subnode which represents communication edge, channels
+ and devices related to the Modem.
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - firmware-name
+ - interrupts
+ - interrupt-names
+ - qcom,smem-states
+ - qcom,smem-state-names
+ - memory-region
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,ipq5424-gcc.h>
+ remoteproc@d100000 {
+ compatible = "qcom,ipq5424-wcss-sec-pil";
+ reg = <0x0d100000 0x4040>;
+ firmware-name = "ath12k/IPQ5424/hw1.0/q6_fw0.mbn";
+ interrupts-extended = <&intc GIC_SPI 508 IRQ_TYPE_EDGE_RISING>,
+ <&wcss_smp2p_in 0 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 1 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 2 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 3 IRQ_TYPE_NONE>;
+ interrupt-names = "wdog",
+ "fatal",
+ "ready",
+ "handover",
+ "stop-ack";
+
+ clocks = <&gcc GCC_IM_SLEEP_CLK>;
+ clock-names = "sleep";
+
+ mboxes = <&tmel_qmp 0>;
+ qcom,smem-states = <&wcss_smp2p_out 1>,
+ <&wcss_smp2p_out 0>;
+ qcom,smem-state-names = "stop",
+ "shutdown";
+
+ memory-region = <&q6_region>;
+
+ glink-edge {
+ interrupts = <GIC_SPI 500 IRQ_TYPE_EDGE_RISING>;
+ label = "rtr";
+ qcom,remote-pid = <1>;
+ mboxes = <&apcs_glb 8>;
+ };
+ };

View File

@ -0,0 +1,19 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Tue, 21 Oct 2025 15:15:23 +0400
Subject: [PATCH] clk: qcom: gcc-ipq5018: flag sleep clock as critical
The sleep clock never be disabled. To avoid the kernel trying to disable
it and keep it always on, flag it as critical.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/drivers/clk/qcom/gcc-ipq5018.c
+++ b/drivers/clk/qcom/gcc-ipq5018.c
@@ -1340,6 +1340,7 @@ static struct clk_branch gcc_sleep_clk_s
.name = "gcc_sleep_clk_src",
.parent_data = gcc_sleep_clk_data,
.num_parents = ARRAY_SIZE(gcc_sleep_clk_data),
+ .flags = CLK_IS_CRITICAL,
.ops = &clk_branch2_ops,
},
},

View File

@ -0,0 +1,500 @@
From 47d46157200bb5f67e540a87c67aab9cde62f8a4 Mon Sep 17 00:00:00 2001
From: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com>
Date: Tue, 21 Oct 2025 15:38:30 +0400
Subject: [PATCH v6 3/8] remoteproc: qcom: add hexagon based WCSS secure PIL driver
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Message-Id: <20251021-ipq5018-wifi-v6-3-c55c547df6fc@outlook.com>
Add support to bring up hexagon based WCSS using secure PIL. All IPQxxxx
SoCs support secure Peripheral Image Loading (PIL).
Secure PIL image is signed firmware image which only trusted software such
as TrustZone (TZ) can authenticate and load. Linux kernel will send a
Peripheral Authentication Service (PAS) request to TZ to authenticate and
load the PIL images. This change also introduces secure firmware
authentication using Trusted Management Engine-Lite (TME-L) which is
supported on IPQ5424 SoC. This driver uses mailbox based PAS request to
TME-L for image authentication if supported, else it will fallback to use
SCM call based PAS request to TZ.
In order to avoid overloading the existing WCSS driver or PAS driver, we
came up with this new PAS based IPQ WCSS driver.
Signed-off-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com>
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Signed-off-by: Gokul Sriram Palanisamy <gokul.sriram.p@oss.qualcomm.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/remoteproc/Kconfig | 19 ++
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/qcom_q6v5_wcss_sec.c | 390 ++++++++++++++++++++++++++++++++
include/linux/remoteproc.h | 2 +
4 files changed, 412 insertions(+)
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -255,6 +255,25 @@ config QCOM_Q6V5_WCSS
Hexagon V5 based WCSS remote processors on e.g. IPQ8074. This is
a non-TrustZone wireless subsystem.
+config QCOM_Q6V5_WCSS_SEC
+ tristate "Qualcomm Hexagon based WCSS Secure Peripheral Image Loader"
+ depends on OF && ARCH_QCOM
+ depends on QCOM_SMEM
+ depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
+ depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n
+ select QCOM_MDT_LOADER
+ select QCOM_PIL_INFO
+ select QCOM_Q6V5_COMMON
+ select QCOM_RPROC_COMMON
+ select QCOM_SCM
+ help
+ Say y here to support the Qualcomm Secure Peripheral Image Loader
+ for the Hexagon based remote processors on e.g. IPQ5332.
+
+ This is TrustZone wireless subsystem. The firmware is
+ verified and booted with the help of the Peripheral Authentication
+ System (PAS) in TrustZone.
+
config QCOM_SYSMON
tristate "Qualcomm sysmon driver"
depends on RPMSG
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6
obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o
obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o
obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o
+obj-$(CONFIG_QCOM_Q6V5_WCSS_SEC) += qcom_q6v5_wcss_sec.o
obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o
obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
qcom_wcnss_pil-y += qcom_wcnss.o
--- /dev/null
+++ b/drivers/remoteproc/qcom_q6v5_wcss_sec.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016-2018 Linaro Ltd.
+ * Copyright (C) 2014 Sony Mobile Communications AB
+ * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <linux/clk.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/io.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox/tmelcom-qmp.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/mdt_loader.h>
+
+#include "qcom_common.h"
+#include "qcom_q6v5.h"
+#include "qcom_pil_info.h"
+
+#define WCSS_CRASH_REASON 421
+
+#define WCSS_PAS_ID 0x6
+#define MPD_WCSS_PAS_ID 0xd
+
+#define Q6_WAIT_TIMEOUT (5 * HZ)
+
+struct wcss_sec {
+ struct device *dev;
+ struct qcom_rproc_glink glink_subdev;
+ struct qcom_rproc_ssr ssr_subdev;
+ struct qcom_q6v5 q6;
+ phys_addr_t mem_phys;
+ phys_addr_t mem_reloc;
+ void *mem_region;
+ size_t mem_size;
+ const struct wcss_data *desc;
+
+ struct mbox_client mbox_client;
+ struct mbox_chan *mbox_chan;
+ void *metadata;
+ size_t metadata_len;
+};
+
+struct wcss_data {
+ u32 pasid;
+ const char *ss_name;
+ bool auto_boot;
+ bool use_tmelcom;
+};
+
+static int wcss_sec_start(struct rproc *rproc)
+{
+ struct wcss_sec *wcss = rproc->priv;
+ struct device *dev = wcss->dev;
+ int ret;
+
+ ret = qcom_q6v5_prepare(&wcss->q6);
+ if (ret)
+ return ret;
+
+ if (wcss->desc->use_tmelcom) {
+ struct tmel_sec_auth tsa;
+ struct tmel_qmp_msg tqm;
+
+ tsa.data = wcss->metadata;
+ tsa.size = wcss->metadata_len;
+ tsa.pas_id = wcss->desc->pasid;
+ tqm.msg = &tsa;
+ tqm.msg_id = TMEL_MSG_UID_SECBOOT_SEC_AUTH;
+
+ ret = mbox_send_message(wcss->mbox_chan, (void *)&tqm);
+ if (ret < 0) {
+ dev_err(dev, "Failed to send message via mailbox\n");
+ goto unprepare;
+ }
+ } else {
+ ret = qcom_scm_pas_auth_and_reset(wcss->desc->pasid);
+ if (ret) {
+ dev_err(dev, "wcss_reset failed\n");
+ goto unprepare;
+ }
+ }
+
+ ret = qcom_q6v5_wait_for_start(&wcss->q6, Q6_WAIT_TIMEOUT);
+ if (ret == -ETIMEDOUT)
+ dev_err(dev, "start timed out\n");
+
+unprepare:
+ qcom_q6v5_unprepare(&wcss->q6);
+
+ return ret;
+}
+
+static int wcss_sec_stop(struct rproc *rproc)
+{
+ struct wcss_sec *wcss = rproc->priv;
+ struct device *dev = wcss->dev;
+ int ret;
+
+ if (wcss->desc->use_tmelcom) {
+ struct tmel_sec_auth tsa = {0};
+ struct tmel_qmp_msg tqm;
+
+ tsa.pas_id = wcss->desc->pasid;
+ tqm.msg = &tsa;
+ tqm.msg_id = TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN;
+
+ mbox_send_message(wcss->mbox_chan, (void *)&tqm);
+ } else {
+ ret = qcom_scm_pas_shutdown(wcss->desc->pasid);
+ if (ret) {
+ dev_err(dev, "not able to shutdown\n");
+ return ret;
+ }
+ }
+
+ qcom_q6v5_unprepare(&wcss->q6);
+
+ return 0;
+}
+
+static void *wcss_sec_da_to_va(struct rproc *rproc, u64 da, size_t len,
+ bool *is_iomem)
+{
+ struct wcss_sec *wcss = rproc->priv;
+ int offset;
+
+ offset = da - wcss->mem_reloc;
+ if (offset < 0 || offset + len > wcss->mem_size)
+ return NULL;
+
+ return wcss->mem_region + offset;
+}
+
+static int wcss_sec_load(struct rproc *rproc, const struct firmware *fw)
+{
+ struct wcss_sec *wcss = rproc->priv;
+ struct device *dev = wcss->dev;
+ int ret;
+
+ if (wcss->desc->use_tmelcom) {
+ wcss->metadata = qcom_mdt_read_metadata(fw, &wcss->metadata_len,
+ rproc->firmware, wcss->dev);
+ if (IS_ERR(wcss->metadata)) {
+ ret = PTR_ERR(wcss->metadata);
+ dev_err(wcss->dev, "error %d reading firmware %s metadata\n",
+ ret, rproc->firmware);
+ return ret;
+ }
+
+ 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;
+ }
+ } 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;
+ }
+
+ qcom_pil_info_store("wcss", wcss->mem_phys, wcss->mem_size);
+
+ return 0;
+}
+
+static unsigned long wcss_sec_panic(struct rproc *rproc)
+{
+ struct wcss_sec *wcss = rproc->priv;
+
+ return qcom_q6v5_panic(&wcss->q6);
+}
+
+static void wcss_sec_copy_segment(struct rproc *rproc,
+ struct rproc_dump_segment *segment,
+ void *dest, size_t offset, size_t size)
+{
+ struct wcss_sec *wcss = rproc->priv;
+ struct device *dev = wcss->dev;
+
+ if (!segment->io_ptr)
+ segment->io_ptr = ioremap_wc(segment->da, segment->size);
+
+ if (!segment->io_ptr) {
+ dev_err(dev, "Failed to ioremap segment %pad size 0x%zx\n",
+ &segment->da, segment->size);
+ return;
+ }
+
+ if (offset + size < segment->size) {
+ memcpy(dest, segment->io_ptr + offset, size);
+ } else {
+ iounmap(segment->io_ptr);
+ segment->io_ptr = NULL;
+ }
+}
+
+static int wcss_sec_dump_segments(struct rproc *rproc,
+ const struct firmware *fw)
+{
+ struct device *dev = rproc->dev.parent;
+ struct reserved_mem *rmem = NULL;
+ struct device_node *node;
+ int num_segs, index;
+ int ret;
+
+ /*
+ * Parse through additional reserved memory regions for the rproc
+ * and add them to the coredump segments
+ */
+ num_segs = of_count_phandle_with_args(dev->of_node,
+ "memory-region", NULL);
+ for (index = 0; index < num_segs; index++) {
+ node = of_parse_phandle(dev->of_node,
+ "memory-region", index);
+ if (!node)
+ return -EINVAL;
+
+ rmem = of_reserved_mem_lookup(node);
+ of_node_put(node);
+ if (!rmem) {
+ dev_err(dev, "unable to acquire memory-region index %d num_segs %d\n",
+ index, num_segs);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Adding segment 0x%pa size 0x%pa",
+ &rmem->base, &rmem->size);
+ ret = rproc_coredump_add_custom_segment(rproc,
+ rmem->base,
+ rmem->size,
+ wcss_sec_copy_segment,
+ NULL);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct rproc_ops wcss_sec_ops = {
+ .start = wcss_sec_start,
+ .stop = wcss_sec_stop,
+ .da_to_va = wcss_sec_da_to_va,
+ .load = wcss_sec_load,
+ .get_boot_addr = rproc_elf_get_boot_addr,
+ .panic = wcss_sec_panic,
+ .parse_fw = wcss_sec_dump_segments,
+};
+
+static int wcss_sec_alloc_memory_region(struct wcss_sec *wcss)
+{
+ struct reserved_mem *rmem = NULL;
+ struct device_node *node;
+ struct device *dev = wcss->dev;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!node) {
+ dev_err(dev, "can't find phandle memory-region\n");
+ return -EINVAL;
+ }
+
+ rmem = of_reserved_mem_lookup(node);
+ of_node_put(node);
+
+ if (!rmem) {
+ dev_err(dev, "unable to acquire memory-region\n");
+ return -EINVAL;
+ }
+
+ wcss->mem_phys = rmem->base;
+ wcss->mem_reloc = rmem->base;
+ wcss->mem_size = rmem->size;
+ wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
+ if (!wcss->mem_region) {
+ dev_err(dev, "unable to map memory region: %pa+%pa\n",
+ &rmem->base, &rmem->size);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int wcss_sec_probe(struct platform_device *pdev)
+{
+ struct rproc *rproc;
+ struct wcss_sec *wcss;
+ struct clk *sleep_clk;
+ struct clk *int_clk;
+ const char *fw_name = NULL;
+ const struct wcss_data *desc = of_device_get_match_data(&pdev->dev);
+ int ret;
+
+ ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
+ &fw_name);
+ if (ret < 0)
+ return ret;
+
+ rproc = devm_rproc_alloc(&pdev->dev, desc->ss_name, &wcss_sec_ops,
+ fw_name, sizeof(*wcss));
+ if (!rproc) {
+ dev_err(&pdev->dev, "failed to allocate rproc\n");
+ return -ENOMEM;
+ }
+
+ wcss = rproc->priv;
+ wcss->dev = &pdev->dev;
+ wcss->desc = desc;
+
+ ret = wcss_sec_alloc_memory_region(wcss);
+ if (ret)
+ return ret;
+
+ sleep_clk = devm_clk_get_optional_enabled(&pdev->dev, "sleep");
+ if (IS_ERR(sleep_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sleep_clk),
+ "Failed to get sleep clock\n");
+
+ int_clk = devm_clk_get_optional_enabled(&pdev->dev, "interconnect");
+ if (IS_ERR(int_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(int_clk),
+ "Failed to get interconnect clock\n");
+
+ ret = qcom_q6v5_init(&wcss->q6, pdev, rproc,
+ WCSS_CRASH_REASON, NULL, NULL);
+ if (ret)
+ return ret;
+
+ qcom_add_glink_subdev(rproc, &wcss->glink_subdev, desc->ss_name);
+ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, desc->ss_name);
+
+ rproc->auto_boot = false;
+ rproc->dump_conf = RPROC_COREDUMP_INLINE;
+ rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
+
+ if (desc->use_tmelcom) {
+ wcss->mbox_client.dev = wcss->dev;
+ 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_OR_NULL(wcss->mbox_chan))
+ return dev_err_probe(wcss->dev, PTR_ERR(wcss->mbox_chan),
+ "mbox chan for IPC is missing\n");
+ }
+
+ ret = devm_rproc_add(&pdev->dev, rproc);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, rproc);
+
+ return 0;
+}
+
+static void wcss_sec_remove(struct platform_device *pdev)
+{
+ struct rproc *rproc = platform_get_drvdata(pdev);
+ struct wcss_sec *wcss = rproc->priv;
+
+ mbox_free_channel(wcss->mbox_chan);
+ qcom_remove_glink_subdev(rproc, &wcss->glink_subdev);
+ qcom_remove_ssr_subdev(rproc, &wcss->ssr_subdev);
+ qcom_q6v5_deinit(&wcss->q6);
+}
+
+static const struct wcss_data wcss_sec_ipq5332_res_init = {
+ .pasid = MPD_WCSS_PAS_ID,
+ .ss_name = "q6wcss",
+};
+
+static const struct wcss_data wcss_sec_ipq5424_res_init = {
+ .pasid = MPD_WCSS_PAS_ID,
+ .ss_name = "q6wcss",
+ .use_tmelcom = true,
+};
+
+static const struct wcss_data wcss_sec_ipq9574_res_init = {
+ .pasid = WCSS_PAS_ID,
+ .ss_name = "q6wcss",
+};
+
+static const struct of_device_id wcss_sec_of_match[] = {
+ { .compatible = "qcom,ipq5018-wcss-sec-pil", .data = &wcss_sec_ipq5332_res_init },
+ { .compatible = "qcom,ipq5332-wcss-sec-pil", .data = &wcss_sec_ipq5332_res_init },
+ { .compatible = "qcom,ipq5424-wcss-sec-pil", .data = &wcss_sec_ipq5424_res_init },
+ { .compatible = "qcom,ipq9574-wcss-sec-pil", .data = &wcss_sec_ipq9574_res_init },
+ { },
+};
+MODULE_DEVICE_TABLE(of, wcss_sec_of_match);
+
+static struct platform_driver wcss_sec_driver = {
+ .probe = wcss_sec_probe,
+ .remove = wcss_sec_remove,
+ .driver = {
+ .name = "qcom-wcss-secure-pil",
+ .of_match_table = wcss_sec_of_match,
+ },
+};
+module_platform_driver(wcss_sec_driver);
+
+MODULE_DESCRIPTION("Hexagon WCSS Secure Peripheral Image Loader");
+MODULE_LICENSE("GPL");
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -472,6 +472,7 @@ enum rproc_dump_mechanism {
* @node: list node related to the rproc segment list
* @da: device address of the segment
* @size: size of the segment
+ * @io_ptr: ptr to store the ioremapped dump segment
* @priv: private data associated with the dump_segment
* @dump: custom dump function to fill device memory segment associated
* with coredump
@@ -483,6 +484,7 @@ struct rproc_dump_segment {
dma_addr_t da;
size_t size;
+ void *io_ptr;
void *priv;
void (*dump)(struct rproc *rproc, struct rproc_dump_segment *segment,
void *dest, size_t offset, size_t size);

View File

@ -11,7 +11,7 @@ Link: https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/commit/036b
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
--- a/drivers/clk/qcom/gcc-ipq5018.c
+++ b/drivers/clk/qcom/gcc-ipq5018.c
@@ -3647,7 +3647,7 @@ static const struct qcom_reset_map gcc_i
@@ -3648,7 +3648,7 @@ static const struct qcom_reset_map gcc_i
[GCC_UNIPHY_SYS_ARES] = { 0x56104, 1 },
[GCC_UNIPHY_RX_ARES] = { 0x56104, 4 },
[GCC_UNIPHY_TX_ARES] = { 0x56104, 5 },

View File

@ -1,43 +0,0 @@
From 50799703c6c8ec0860e19b102dd7cca3d29028e1 Mon Sep 17 00:00:00 2001
From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Date: Fri, 10 Nov 2023 14:49:34 +0530
Subject: [PATCH] firmware: qcom_scm: ipq5332: add support to pass
metadata size
IPQ5332 security software running under trustzone
requires metadata size. With V2 cmd, pass metadata
size as well.
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
---
drivers/firmware/qcom//qcom_scm.c | 8 ++++++++
drivers/firmware/qcom//qcom_scm.h | 1 +
2 files changed, 9 insertions(+)
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -686,6 +686,14 @@ int qcom_scm_pas_mem_setup(u32 periphera
if (ret)
goto disable_clk;
+ if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_INIT_IMAGE_V2)) {
+ desc.cmd = QCOM_SCM_PAS_INIT_IMAGE_V2;
+ desc.arginfo =
+ QCOM_SCM_ARGS(3, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL);
+ desc.args[2] = size;
+ }
+
ret = qcom_scm_call(__scm->dev, &desc, &res);
qcom_scm_bw_disable();
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -96,6 +96,7 @@ struct qcom_tzmem_pool *qcom_scm_get_tzm
#define QCOM_SCM_SVC_PIL 0x02
#define QCOM_SCM_PIL_PAS_INIT_IMAGE 0x01
+#define QCOM_SCM_PAS_INIT_IMAGE_V2 0x1a
#define QCOM_SCM_PIL_PAS_MEM_SETUP 0x02
#define QCOM_SCM_PIL_PAS_AUTH_AND_RESET 0x05
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06

View File

@ -28,7 +28,7 @@ Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -806,6 +806,84 @@ bool qcom_scm_pas_supported(u32 peripher
@@ -807,6 +807,84 @@ bool qcom_scm_pas_supported(u32 peripher
}
EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
@ -116,9 +116,9 @@ Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -102,6 +102,8 @@ struct qcom_tzmem_pool *qcom_scm_get_tzm
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
#define QCOM_SCM_PIL_PAS_INIT_IMAGE_V2 0x1a
+#define QCOM_SCM_MSA_LOCK 0x24
+#define QCOM_SCM_MSA_UNLOCK 0x25

View File

@ -0,0 +1,102 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Tue, 21 Oct 2025 15:26:31 +0400
Subject: [PATCH] remoteproc: qcom: wcss-sec: add split firmware support
The driver currently expects only one firmware file to be loaded as part
of bringing up WCSS. For IPQ5018 and QCN6122, multiple firmware files are
required, both the q6 and m3 firmware files. As such, add support for
loading up to 3 firmware files.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/drivers/remoteproc/qcom_q6v5_wcss_sec.c
+++ b/drivers/remoteproc/qcom_q6v5_wcss_sec.c
@@ -25,6 +25,8 @@
#define Q6_WAIT_TIMEOUT (5 * HZ)
+#define MAX_FIRMWARE 3
+
struct wcss_sec {
struct device *dev;
struct qcom_rproc_glink glink_subdev;
@@ -35,6 +37,7 @@ struct wcss_sec {
void *mem_region;
size_t mem_size;
const struct wcss_data *desc;
+ const char **firmware;
struct mbox_client mbox_client;
struct mbox_chan *mbox_chan;
@@ -137,7 +140,8 @@ static int wcss_sec_load(struct rproc *r
{
struct wcss_sec *wcss = rproc->priv;
struct device *dev = wcss->dev;
- int ret;
+ const struct firmware *fw_hdl;
+ int i, ret;
if (wcss->desc->use_tmelcom) {
wcss->metadata = qcom_mdt_read_metadata(fw, &wcss->metadata_len,
@@ -161,6 +165,28 @@ static int wcss_sec_load(struct rproc *r
wcss->mem_phys, wcss->mem_size, &wcss->mem_reloc);
if (ret)
return ret;
+
+ for (i = 1; i < MAX_FIRMWARE; i++) {
+ if (!wcss->firmware[i])
+ continue;
+
+ ret = request_firmware(&fw_hdl, wcss->firmware[i], dev);
+
+ if (ret)
+ continue;
+
+ ret = qcom_mdt_load_no_init(dev, fw_hdl, wcss->firmware[i], 0,
+ wcss->mem_region, wcss->mem_phys,
+ wcss->mem_size, &wcss->mem_reloc);
+
+ release_firmware(fw_hdl);
+
+ if (ret) {
+ dev_err(dev, "error %d loading firmware %s\n",
+ ret, wcss->firmware[i]);
+ return ret;
+ }
+ }
}
qcom_pil_info_store("wcss", wcss->mem_phys, wcss->mem_size);
@@ -291,17 +317,20 @@ static int wcss_sec_probe(struct platfor
struct wcss_sec *wcss;
struct clk *sleep_clk;
struct clk *int_clk;
- const char *fw_name = NULL;
+ const char **firmware = NULL;
const struct wcss_data *desc = of_device_get_match_data(&pdev->dev);
int ret;
- ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
- &fw_name);
+ firmware = devm_kcalloc(&pdev->dev, MAX_FIRMWARE,
+ sizeof(*firmware), GFP_KERNEL);
+
+ ret = of_property_read_string_array(pdev->dev.of_node, "firmware-name",
+ firmware, MAX_FIRMWARE);
if (ret < 0)
return ret;
rproc = devm_rproc_alloc(&pdev->dev, desc->ss_name, &wcss_sec_ops,
- fw_name, sizeof(*wcss));
+ firmware[0], sizeof(*wcss));
if (!rproc) {
dev_err(&pdev->dev, "failed to allocate rproc\n");
return -ENOMEM;
@@ -310,6 +339,7 @@ static int wcss_sec_probe(struct platfor
wcss = rproc->priv;
wcss->dev = &pdev->dev;
wcss->desc = desc;
+ wcss->firmware = firmware;
ret = wcss_sec_alloc_memory_region(wcss);
if (ret)

View File

@ -28,7 +28,7 @@ Signed-off-by: George Moussalem <george.moussalem@outlook.com>
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -807,6 +807,85 @@ bool qcom_scm_pas_supported(u32 peripher
@@ -808,6 +808,85 @@ bool qcom_scm_pas_supported(u32 peripher
EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
/**
@ -117,9 +117,9 @@ Signed-off-by: George Moussalem <george.moussalem@outlook.com>
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -102,6 +102,9 @@ struct qcom_tzmem_pool *qcom_scm_get_tzm
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
#define QCOM_SCM_PIL_PAS_INIT_IMAGE_V2 0x1a
+#define QCOM_SCM_INTERNAL_WIFI_POWERUP 0x17
+#define QCOM_SCM_INTERNAL_WIFI_SHUTDOWN 0x18
+#define QCOM_SCM_PIL_PAS_LOAD_SEG 0x19