From 00d61da5880005df53b862593b51d95bfe8b2d1b Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Thu, 23 Oct 2025 15:07:09 +0400 Subject: [PATCH] qualcommax: Add support for qcom remoteproc WCSS secure PIL driver Add support for qcom remoteproc WCSS secure PIL driver. Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/20928 Signed-off-by: Robert Marko --- target/linux/qualcommax/config-6.12 | 2 + .../linux/qualcommax/ipq50xx/config-default | 2 + ...mp-Introduce-TMEL-QMP-mailbox-driver.patch | 1245 +++++++++++++++++ ...32-add-support-to-pass-metadata-size.patch | 64 + ...cument-hexagon-based-wcss-secure-pil.patch | 157 +++ ...ipq5018-flag-sleep-clock-as-critical.patch | 19 + ...hexagon-based-wcss-secure-pil-driver.patch | 500 +++++++ ...-ipq5018-fix-uniphy-soft-reset-issue.patch | 2 +- ...m-ipq5332-add-support-to-pass-metada.patch | 43 - ...m-ipq5332-add-msa-lock-unlock-suppor.patch | 4 +- ...-wcss-sec-add-split-firmware-support.patch | 102 ++ .../0811-firmware-qcom_scm-support-MPD.patch | 4 +- 12 files changed, 2096 insertions(+), 48 deletions(-) create mode 100644 target/linux/qualcommax/patches-6.12/0184-mailbox-tmelite-qmp-Introduce-TMEL-QMP-mailbox-driver.patch create mode 100644 target/linux/qualcommax/patches-6.12/0185-firmware-qcom_scm-ipq5332-add-support-to-pass-metadata-size.patch create mode 100644 target/linux/qualcommax/patches-6.12/0186-dt-bindings-remoteproc-qcom-document-hexagon-based-wcss-secure-pil.patch create mode 100644 target/linux/qualcommax/patches-6.12/0187-clk-qcom-gcc-ipq5018-flag-sleep-clock-as-critical.patch create mode 100644 target/linux/qualcommax/patches-6.12/0188-remoteproc-qcom-add-hexagon-based-wcss-secure-pil-driver.patch delete mode 100644 target/linux/qualcommax/patches-6.12/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch create mode 100644 target/linux/qualcommax/patches-6.12/0808-remoteproc-qcom-wcss-sec-add-split-firmware-support.patch diff --git a/target/linux/qualcommax/config-6.12 b/target/linux/qualcommax/config-6.12 index 40f2f3172c..cc9079707b 100644 --- a/target/linux/qualcommax/config-6.12 +++ b/target/linux/qualcommax/config-6.12 @@ -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 diff --git a/target/linux/qualcommax/ipq50xx/config-default b/target/linux/qualcommax/ipq50xx/config-default index ac32263637..97a342776a 100644 --- a/target/linux/qualcommax/ipq50xx/config-default +++ b/target/linux/qualcommax/ipq50xx/config-default @@ -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 diff --git a/target/linux/qualcommax/patches-6.12/0184-mailbox-tmelite-qmp-Introduce-TMEL-QMP-mailbox-driver.patch b/target/linux/qualcommax/patches-6.12/0184-mailbox-tmelite-qmp-Introduce-TMEL-QMP-mailbox-driver.patch new file mode 100644 index 0000000000..22016b474c --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0184-mailbox-tmelite-qmp-Introduce-TMEL-QMP-mailbox-driver.patch @@ -0,0 +1,1245 @@ +From patchwork Thu Mar 27 18:17:50 2025 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Sricharan Ramabadhran +X-Patchwork-Id: 14031335 +Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com + [205.220.180.131]) + (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) + (No client certificate requested) + by smtp.subspace.kernel.org (Postfix) with ESMTPS id B6FC4218EB3; + Thu, 27 Mar 2025 18:18:22 +0000 (UTC) +Authentication-Results: smtp.subspace.kernel.org; + arc=none smtp.client-ip=205.220.180.131 +ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; + t=1743099505; cv=none; + b=EIGqvUFVdX2yAV1X5BSy4Vd6avjUSpDkQyVdP3vFK5RB8T9Eoxto8HY1cWSbjWATbbmUQdxpIqDsTwpW93ZxX2u+xR66TEfrq53Mz7WXezAMNj+c7sxKOvD1LuN/SwZlpLeymS3aYCTXQF9tlaX4Kchd/XG9PHCNNlj4J/HMLEU= +ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; + s=arc-20240116; t=1743099505; c=relaxed/simple; + bh=1bC94iLOVn4uSZE28DCVE/Z1lxJpVgGYizWuGyj1fB4=; + h=From:To:Subject:Date:Message-ID:In-Reply-To:References: + MIME-Version:Content-Type; + b=Vd0k6dRvTlGuhPjBJy4f26hetu50FFAxHBHHvpt01PASSbUWn5QdLpfIBDf4hhOejN+OP/t5Y4zRcQ8ls9btkI0XMU7nqZw/h8kFFPlXO+VWPWCIVQo6DeM4rgWKPIskkdmi3YCmekJQ8mOvrnj0Z1B7840MW6GkQowDA1ijXyQ= +ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; + dmarc=pass (p=none dis=none) header.from=quicinc.com; + spf=pass smtp.mailfrom=quicinc.com; + dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com + header.b=nWsnWiiP; arc=none smtp.client-ip=205.220.180.131 +Authentication-Results: smtp.subspace.kernel.org; + dmarc=pass (p=none dis=none) header.from=quicinc.com +Authentication-Results: smtp.subspace.kernel.org; + spf=pass smtp.mailfrom=quicinc.com +Authentication-Results: smtp.subspace.kernel.org; + dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com + header.b="nWsnWiiP" +Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) + by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id + 52RElAqC023840; + Thu, 27 Mar 2025 18:18:16 GMT +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= + content-transfer-encoding:content-type:date:from:in-reply-to + :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= + QkU6cfXx/Dr1uYdS2bfC4yKOb27hRI4xX4Mjt2GlxX0=; b=nWsnWiiPd2BhTQo2 + 8bvwRq7Eyr3yMh8LCCDfuk/SXkQbpNukzvr4A4R66oU3PmGnp5Y+Yeo0w09T3vue + 4JYS0hV8suACJbj1eqqWEu+sLl1Uns0BXlWaWmOCnRVIwh/bpA3XIfCi9Fy+rANI + aXzrjtC81dBCEuqIj+0J97gY2Tt/kGays9pGkqXp64pNSHDoVQ7ijZgQlosIAg4b + qnNh7nFqwkbdlodmMjTuAQk0RBEAf1C5Wbyu2SP+jmUdu1Ign7AZHlK8OQtHCeR5 + W1EPvlsTzXeaqCfYpgYZWI/kcO/WfBgjLFlrn4EG660ta9NShqlsNyHbuG0R5u8P + GiugpQ== +Received: from nasanppmta03.qualcomm.com (i-global254.qualcomm.com + [199.106.103.254]) + by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 45kyr9qahq-1 + (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); + Thu, 27 Mar 2025 18:18:16 +0000 (GMT) +Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com + [10.46.141.250]) + by NASANPPMTA03.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id + 52RIIFs2028891 + (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); + Thu, 27 Mar 2025 18:18:15 GMT +Received: from hu-srichara-blr.qualcomm.com (10.80.80.8) by + nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server + (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id + 15.2.1544.9; Thu, 27 Mar 2025 11:18:11 -0700 +From: Sricharan R +To: , , , + , , + , , + , , + , , + +Subject: [PATCH V4 2/2] mailbox: tmelite-qmp: Introduce TMEL QMP mailbox + driver +Date: Thu, 27 Mar 2025 23:47:50 +0530 +Message-ID: <20250327181750.3733881-3-quic_srichara@quicinc.com> +X-Mailer: git-send-email 2.34.1 +In-Reply-To: <20250327181750.3733881-1-quic_srichara@quicinc.com> +References: <20250327181750.3733881-1-quic_srichara@quicinc.com> +Precedence: bulk +X-Mailing-List: linux-arm-msm@vger.kernel.org +List-Id: +List-Subscribe: +List-Unsubscribe: +MIME-Version: 1.0 +X-ClientProxiedBy: nasanex01b.na.qualcomm.com (10.46.141.250) To + nasanex01b.na.qualcomm.com (10.46.141.250) +X-QCInternal: smtphost +X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 + signatures=585085 +X-Proofpoint-GUID: 0rO4P6gnHKFRp6byxTJdsYHwAuRh45Fp +X-Authority-Analysis: v=2.4 cv=UblRSLSN c=1 sm=1 tr=0 ts=67e59668 cx=c_pps + a=JYp8KDb2vCoCEuGobkYCKw==:117 a=JYp8KDb2vCoCEuGobkYCKw==:17 + a=GEpy-HfZoHoA:10 a=Vs1iUdzkB0EA:10 a=COk6AnOGAAAA:8 a=hOkhvlhF9aMxycy05ToA:9 + a=RVmHIydaz68A:10 + a=TjNXssC_j7lpFel5tvFf:22 +X-Proofpoint-ORIG-GUID: 0rO4P6gnHKFRp6byxTJdsYHwAuRh45Fp +X-Proofpoint-Virus-Version: vendor=baseguard + engine=ICAP:2.0.293,Aquarius:18.0.1095,Hydra:6.0.680,FMLib:17.12.68.34 + definitions=2025-03-27_03,2025-03-26_02,2024-11-22_01 +X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 + lowpriorityscore=0 + suspectscore=0 priorityscore=1501 phishscore=0 mlxscore=0 impostorscore=0 + adultscore=0 clxscore=1015 mlxlogscore=999 bulkscore=0 malwarescore=0 + spamscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound + adjust=0 reason=mlx scancount=1 engine=8.19.0-2502280000 + definitions=main-2503270125 + +From: Sricharan Ramabadhran + +This mailbox facilitates the communication between the TMEL server +subsystem (Trust Management Engine Lite) and the TMEL client +(APPSS/BTSS/AUDIOSS), used for secure services like secure image +authentication, enable/disable efuses, crypto services etc. Each client in +the SoC has its own block of message RAM and IRQ for communication with the +TMEL SS. The protocol used to communicate in the message RAM is known as +Qualcomm Messaging Protocol (QMP). + +Remote proc driver subscribes to this mailbox and uses the +mbox_send_message to use TMEL to securely authenticate/teardown the images. + +Signed-off-by: Sricharan Ramabadhran +--- +[V4] + Fixed TME-L naming in all places and expanded it. + Folded tmel_work in tmel. + Added more kernel doc as relevant. + Removed __packed in all places, as not required. + Renamed all functions to have tmel_ prefixes. + Used readl/writel in all places. + Added Inline for all required functions. + Removed redundant type conversions. + Removed redundant 'goto's + Added __free macro + Fixed Linux std errno in tmel_sec_boot_auth/teardown + Added spinlock in qmp_startup + Used of_mbox_index_xlate and dropped the tmel_qmp_mbox_xlate + Updated header file to have only mbox consumer required and moved rest to .c file + Fixed the TMEL_MSG macros to use standard GENMASK + Moved the irq registration to end of probe + + Following tests were done and no issues. + + *) Checkpatch + *) Codespell + *) Sparse + *) kerneldoc check + *) Kernel lock debugging + *) dt_binding_check and dtbs_check + +[V3] + + Fixed wrong code/comments wrappings. + Fixed Kconfig and Makefile entries to right place. + Removed unused headers inclusion. + Fixed locking, removed the mutexes and having only tx spinlock. + Removed the use of global ptr for tmel, made it as device specific. + Replaced pr_err/pr_debug with dev_err/dev_dbg in all places. + Fixed usage of dev_err_probe. + Fixed xlate callback as per comments. + Used devm equivalents and kcalloc version as per comments. + Removed all un-nessecary wrapper macros for register access, inlined it + as per comments. + Re-organised the function layout as per comments and make it more readable. + Removed the pictures in headers files as per comments. + Used Field_prep/get as per comments. + Fixed Kernel test reported issues. + Fixed all other comments as well. + + Following tests were done and no issues. + + *) Checkpatch + *) Codespell + *) Sparse + *) kerneldoc check + *) Kernel lock debugging + *) dt_binding_check and dtbs_check + +[v2] + Added HW description in the bindings patch. + Fixed review comments for bindings from Krzysztof and Dmitry + Changed patch#2 driver to add work for mailbox tx processing + Cleaned up patch#2 for some checkpatch warnings. + There are some checkpatch [CHECK] like below, which looks like false positive. + + CHECK: Macro argument 'm' may be better as '(m)' to avoid precedence issues + #1072: FILE: include/linux/mailbox/tmelcom-qmp.h:40: + +#define TMEL_MSG_UID_CREATE(m, a) ((u32)(((m & 0xff) << 8) | (a & 0xff))) + +[v1] + RFC Post + + drivers/mailbox/Kconfig | 10 + + drivers/mailbox/Makefile | 2 + + drivers/mailbox/qcom-tmel-qmp.c | 947 ++++++++++++++++++++++++++++ + include/linux/mailbox/tmelcom-qmp.h | 65 ++ + 4 files changed, 1024 insertions(+) + create mode 100644 drivers/mailbox/qcom-tmel-qmp.c + create mode 100644 include/linux/mailbox/tmelcom-qmp.h + +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -295,4 +295,14 @@ config QCOM_IPCC + acts as an interrupt controller for receiving interrupts from clients. + Say Y here if you want to build this driver. + ++config QCOM_TMEL_QMP_MAILBOX ++ tristate "QCOM Mailbox Protocol(QMP) for TMEL SS" ++ depends on ARCH_QCOM || COMPILE_TEST ++ help ++ Say yes to add support for the QMP Mailbox Protocol driver for ++ Trust Management Engine Lite Sub System (TMEL SS). ++ QMP is a lightweight communication protocol for sending messages to ++ TMEL. This protocol fits into the Generic Mailbox Framework. ++ QMP uses a mailbox registers. ++ + endif +--- a/drivers/mailbox/Makefile ++++ b/drivers/mailbox/Makefile +@@ -47,6 +47,8 @@ obj-$(CONFIG_POLARFIRE_SOC_MAILBOX) += m + + obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o + ++obj-$(CONFIG_QCOM_TMEL_QMP_MAILBOX) += qcom-tmel-qmp.o ++ + obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o + + obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o +--- /dev/null ++++ b/drivers/mailbox/qcom-tmel-qmp.c +@@ -0,0 +1,947 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define QMP_NUM_CHANS 0x1 ++#define QMP_TOUT_MS 1000 ++#define QMP_CTRL_DATA_SIZE 4 ++#define QMP_MAX_PKT_SIZE 0x18 ++#define QMP_UCORE_DESC_OFFSET 0x1000 ++#define QMP_SEND_TIMEOUT 30000 ++ ++#define QMP_HW_MBOX_SIZE 32 ++#define QMP_MBOX_RSV_SIZE 4 ++#define QMP_MBOX_IPC_PACKET_SIZE (QMP_HW_MBOX_SIZE - QMP_CTRL_DATA_SIZE - QMP_MBOX_RSV_SIZE) ++#define QMP_MBOX_IPC_MAX_PARAMS 5 ++ ++#define QMP_MAX_PARAM_IN_PARAM_ID 14 ++#define QMP_PARAM_CNT_FOR_OUTBUF 3 ++#define QMP_SRAM_IPC_MAX_PARAMS (QMP_MAX_PARAM_IN_PARAM_ID * QMP_PARAM_CNT_FOR_OUTBUF) ++#define QMP_SRAM_IPC_MAX_BUF_SIZE (QMP_SRAM_IPC_MAX_PARAMS * sizeof(u32)) ++ ++#define TMEL_ERROR_GENERIC (0x1u) ++#define TMEL_ERROR_NOT_SUPPORTED (0x2u) ++#define TMEL_ERROR_BAD_PARAMETER (0x3u) ++#define TMEL_ERROR_BAD_MESSAGE (0x4u) ++#define TMEL_ERROR_BAD_ADDRESS (0x5u) ++#define TMEL_ERROR_TMELCOM_FAILURE (0x6u) ++#define TMEL_ERROR_TMEL_BUSY (0x7u) ++ ++/* ++ * mbox data can be shared over mem or sram ++ */ ++enum ipc_type { ++ IPC_MBOX_MEM, ++ IPC_MBOX_SRAM, ++}; ++ ++/* ++ * mbox header indicates the type of payload and action required. ++ */ ++struct ipc_header { ++ u8 ipc_type:1; ++ u8 msg_len:7; ++ u8 msg_type; ++ u8 action_id; ++ s8 response; ++}; ++ ++struct mbox_payload { ++ u32 param[QMP_MBOX_IPC_MAX_PARAMS]; ++}; ++ ++struct sram_payload { ++ u32 payload_ptr; ++ u32 payload_len; ++}; ++ ++union ipc_payload { ++ struct mbox_payload mbox_payload; ++ struct sram_payload sram_payload; ++}; ++ ++struct tmel_ipc_pkt { ++ struct ipc_header msg_hdr; ++ union ipc_payload payload; ++}; ++ ++/** ++ * enum qmp_local_state - definition of the local state machine ++ * @LINK_DISCONNECTED: Init state, waiting for ucore to start ++ * @LINK_NEGOTIATION: Set local link state to up, wait for ucore ack ++ * @LINK_CONNECTED: Link state up, channel not connected ++ * @LOCAL_CONNECTING: Channel opening locally, wait for ucore ack ++ * @CHANNEL_CONNECTED: Channel fully opened ++ * @LOCAL_DISCONNECTING: Channel disconnected locally, wait for ucore ack ++ */ ++enum qmp_local_state { ++ LINK_DISCONNECTED, ++ LINK_NEGOTIATION, ++ LINK_CONNECTED, ++ LOCAL_CONNECTING, ++ CHANNEL_CONNECTED, ++ LOCAL_DISCONNECTING, ++}; ++ ++/** ++ * struct qmp_channel_desc - IPC bits ++ * @bits: Var to access each member ++ * @val: u32 representation of above ++ */ ++union qmp_channel_desc { ++ struct { ++ u32 link_state:1; ++ u32 link_state_ack:1; ++ u32 ch_state:1; ++ u32 ch_state_ack:1; ++ u32 tx:1; ++ u32 tx_ack:1; ++ u32 rx_done:1; ++ u32 rx_done_ack:1; ++ u32 reserved:8; ++ u32 frag_size:8; ++ u32 rem_frag_count:8; ++ } bits; ++ unsigned int val; ++}; ++ ++/** ++ * struct qmp_device - local information for managing a single mailbox ++ * @dev: The device that corresponds to this mailbox ++ * @mcore_desc: Local core (APSS) mailbox descriptor ++ * @ucore_desc: Remote core (TME-L) mailbox descriptor ++ * @mcore: Local core (APSS) channel descriptor ++ * @ucore: Remote core (TME-L) channel descriptor ++ * @rx_pkt: Buffer to pass to client, holds received data from mailbox ++ * @mbox_client: Mailbox client for the IPC interrupt ++ * @mbox_chan: Mailbox client chan for the IPC interrupt ++ * @local_state: Current state of mailbox protocol ++ * @tx_lock: Serialize access for writes to mailbox ++ * @link_complete: Use to block until link negotiation with remote proc ++ * @ch_complete: Use to block until the channel is fully opened ++ * @tx_sent: True if tx is sent and remote proc has not sent ack ++ */ ++struct qmp_device { ++ struct device *dev; ++ ++ void __iomem *mcore_desc; ++ void __iomem *ucore_desc; ++ union qmp_channel_desc mcore; ++ union qmp_channel_desc ucore; ++ ++ struct kvec rx_pkt; ++ ++ struct mbox_client mbox_client; ++ struct mbox_chan *mbox_chan; ++ ++ enum qmp_local_state local_state; ++ ++ /* ++ * Serialize access to mcore IPC descriptors. ++ * mcore refers to the IPC request descriptors sent to TMEL, ++ * protecting it from various SM transitions using this. ++ */ ++ spinlock_t tx_lock; ++ ++ struct completion link_complete; ++ struct completion ch_complete; ++ ++ atomic_t tx_sent; ++}; ++ ++/** ++ * struct tmel - tmel controller instance ++ * @dev: The device that corresponds to this mailbox ++ * @ctrl: Mailbox controller for use by tmel clients ++ * @mdev: qmp_device associated with this tmel instance ++ * @pkt: Buffer from client, to be sent over mailbox ++ * @ipc_pkt: wrapper used for prepare/un_prepare ++ * @sram_dma_addr: mailbox sram address to copy the data ++ * @waitq: Use to wait for posted messages completion ++ * @rx_done: Use to indicate receive completion from remote ++ * @twork: worker for posting the client req to tmel ctrl ++ * @data: client data to be sent for the current request ++ */ ++struct tmel { ++ struct device *dev; ++ struct mbox_controller ctrl; ++ struct qmp_device *mdev; ++ struct kvec pkt; ++ struct tmel_ipc_pkt *ipc_pkt; ++ dma_addr_t sram_dma_addr; ++ wait_queue_head_t waitq; ++ bool rx_done; ++ struct work_struct twork; ++ void *data; ++}; ++ ++struct tmel_msg_param_type_buf_in { ++ u32 buf; ++ u32 buf_len; ++}; ++ ++struct tmel_secboot_sec_auth_req { ++ u32 sw_id; ++ struct tmel_msg_param_type_buf_in elf_buf; ++ struct tmel_msg_param_type_buf_in region_list; ++ u32 relocate; ++}; ++ ++struct tmel_secboot_sec_auth_resp { ++ u32 first_seg_addr; ++ u32 first_seg_len; ++ u32 entry_addr; ++ u32 extended_error; ++ u32 status; ++}; ++ ++struct tmel_secboot_sec_auth { ++ struct tmel_secboot_sec_auth_req req; ++ struct tmel_secboot_sec_auth_resp resp; ++}; ++ ++struct tmel_secboot_sec { ++ struct device *dev; ++ void *elf_buf; ++ struct tmel_secboot_sec_auth msg; ++}; ++ ++struct tmel_secboot_teardown_req { ++ u32 sw_id; ++ u32 secondary_sw_id; ++}; ++ ++struct tmel_secboot_teardown_resp { ++ u32 status; ++}; ++ ++struct tmel_secboot_teardown { ++ struct tmel_secboot_teardown_req req; ++ struct tmel_secboot_teardown_resp resp; ++}; ++ ++/** ++ * tmel_qmp_send_irq() - send an irq to a remote entity as an event signal. ++ * @mdev: Which remote entity that should receive the irq. ++ */ ++static inline void tmel_qmp_send_irq(struct qmp_device *mdev) ++{ ++ writel(mdev->mcore.val, mdev->mcore_desc); ++ /* Ensure desc update is visible before IPC */ ++ wmb(); ++ ++ dev_dbg(mdev->dev, "%s: mcore 0x%x ucore 0x%x", __func__, ++ mdev->mcore.val, mdev->ucore.val); ++ ++ mbox_send_message(mdev->mbox_chan, NULL); ++ mbox_client_txdone(mdev->mbox_chan, 0); ++} ++ ++/** ++ * tmel_qmp_send_data() - Send the data to remote and notify. ++ * @mdev: qmp_device to send the data to. ++ * @data: Data to be sent to remote processor, should be in the format of ++ * a kvec. ++ * ++ * Copy the data to the channel's mailbox and notify remote subsystem of new ++ * data. This function will return an error if the previous message sent has ++ * not been read. ++ */ ++static inline int tmel_qmp_send_data(struct qmp_device *mdev, void *data) ++{ ++ struct kvec *pkt = (struct kvec *)data; ++ void __iomem *addr; ++ unsigned long flags; ++ ++ if (pkt->iov_len > QMP_MAX_PKT_SIZE) { ++ dev_err(mdev->dev, "Unsupported packet size"); ++ return -EINVAL; ++ } ++ ++ if (atomic_read(&mdev->tx_sent)) ++ return -EAGAIN; ++ ++ dev_dbg(mdev->dev, "%s: mcore 0x%x ucore 0x%x", __func__, ++ mdev->mcore.val, mdev->ucore.val); ++ ++ addr = mdev->mcore_desc + QMP_CTRL_DATA_SIZE; ++ memcpy_toio(addr, pkt->iov_base, pkt->iov_len); ++ ++ mdev->mcore.bits.frag_size = pkt->iov_len; ++ mdev->mcore.bits.rem_frag_count = 0; ++ ++ dev_dbg(mdev->dev, "Copied buffer to mbox, sz: %d", ++ mdev->mcore.bits.frag_size); ++ ++ atomic_set(&mdev->tx_sent, 1); ++ ++ spin_lock_irqsave(&mdev->tx_lock, flags); ++ mdev->mcore.bits.tx = !(mdev->mcore.bits.tx); ++ tmel_qmp_send_irq(mdev); ++ spin_unlock_irqrestore(&mdev->tx_lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * tmel_qmp_notify_client() - Notify the tmel client about remote data. ++ * @tdev: tmel device to notify. ++ * @message: response pkt from remote processor, should be in format of kvec. ++ * ++ * Wakeup the clients after receiving data from the remote. ++ */ ++static inline void tmel_qmp_notify_client(struct tmel *tdev, void *message) ++{ ++ struct kvec *pkt = NULL; ++ ++ if (!message) { ++ dev_err(tdev->dev, "spurious message received\n"); ++ goto notify_fail; ++ } ++ ++ if (tdev->rx_done) { ++ dev_err(tdev->dev, "tmel response pending\n"); ++ goto notify_fail; ++ } ++ ++ pkt = (struct kvec *)message; ++ tdev->pkt.iov_len = pkt->iov_len; ++ tdev->pkt.iov_base = pkt->iov_base; ++ tdev->rx_done = true; ++ ++notify_fail: ++ wake_up_interruptible(&tdev->waitq); ++} ++ ++/** ++ * tmel_qmp_recv_data() - Receive data and send ack. ++ * @tdev: tmel device that received the notification. ++ * @mbox_of: offset of mailbox after QMP Control data. ++ * ++ * Copies data from mailbox and passes to the client upon receiving data ++ * available notification. Also acknowledges the read completion. ++ */ ++static inline void tmel_qmp_recv_data(struct tmel *tdev, u32 mbox_of) ++{ ++ struct qmp_device *mdev = tdev->mdev; ++ void __iomem *addr; ++ struct kvec *pkt; ++ ++ addr = mdev->ucore_desc + mbox_of; ++ pkt = &mdev->rx_pkt; ++ pkt->iov_len = mdev->ucore.bits.frag_size; ++ ++ memcpy_fromio(pkt->iov_base, addr, pkt->iov_len); ++ mdev->mcore.bits.tx_ack = mdev->ucore.bits.tx; ++ dev_dbg(mdev->dev, "%s: Send RX data to TMEL Client", __func__); ++ tmel_qmp_notify_client(tdev, pkt); ++ ++ mdev->mcore.bits.rx_done = !(mdev->mcore.bits.rx_done); ++ tmel_qmp_send_irq(mdev); ++} ++ ++/** ++ * tmel_qmp_clr_mcore_ch_state() - Clear the mcore state of a mailbox. ++ * @mdev: mailbox device to be initialized. ++ */ ++static inline void tmel_qmp_clr_mcore_ch_state(struct qmp_device *mdev) ++{ ++ /* Clear all fields except link_state */ ++ mdev->mcore.bits.ch_state = 0; ++ mdev->mcore.bits.ch_state_ack = 0; ++ mdev->mcore.bits.tx = 0; ++ mdev->mcore.bits.tx_ack = 0; ++ mdev->mcore.bits.rx_done = 0; ++ mdev->mcore.bits.rx_done_ack = 0; ++ mdev->mcore.bits.frag_size = 0; ++ mdev->mcore.bits.rem_frag_count = 0; ++} ++ ++/** ++ * tmel_qmp_rx() - Handle incoming messages from remote processor. ++ * @tdev: tmel device to send the event to. ++ */ ++static inline void tmel_qmp_rx(struct tmel *tdev) ++{ ++ struct qmp_device *mdev = tdev->mdev; ++ unsigned long flags; ++ ++ /* read remote_desc from mailbox register */ ++ mdev->ucore.val = readl(mdev->ucore_desc); ++ ++ dev_dbg(mdev->dev, "%s: mcore 0x%x ucore 0x%x", __func__, ++ mdev->mcore.val, mdev->ucore.val); ++ ++ spin_lock_irqsave(&mdev->tx_lock, flags); ++ ++ /* Check if remote link down */ ++ if (mdev->local_state >= LINK_CONNECTED && ++ !(mdev->ucore.bits.link_state)) { ++ mdev->local_state = LINK_NEGOTIATION; ++ mdev->mcore.bits.link_state_ack = mdev->ucore.bits.link_state; ++ tmel_qmp_send_irq(mdev); ++ spin_unlock_irqrestore(&mdev->tx_lock, flags); ++ return; ++ } ++ ++ switch (mdev->local_state) { ++ case LINK_NEGOTIATION: ++ if (!(mdev->mcore.bits.link_state) || ++ !(mdev->ucore.bits.link_state)) { ++ dev_err(mdev->dev, "rx irq:link down state\n"); ++ break; ++ } ++ tmel_qmp_clr_mcore_ch_state(mdev); ++ mdev->mcore.bits.link_state_ack = mdev->ucore.bits.link_state; ++ mdev->local_state = LINK_CONNECTED; ++ complete_all(&mdev->link_complete); ++ dev_dbg(mdev->dev, "Set to link connected"); ++ break; ++ case LINK_CONNECTED: ++ /* No need to handle until local opens */ ++ break; ++ case LOCAL_CONNECTING: ++ /* Ack to remote ch_state change */ ++ mdev->mcore.bits.ch_state_ack = mdev->ucore.bits.ch_state; ++ mdev->local_state = CHANNEL_CONNECTED; ++ complete_all(&mdev->ch_complete); ++ dev_dbg(mdev->dev, "Set to channel connected"); ++ tmel_qmp_send_irq(mdev); ++ break; ++ case CHANNEL_CONNECTED: ++ /* Check for remote channel down */ ++ if (!(mdev->ucore.bits.ch_state)) { ++ mdev->local_state = LOCAL_CONNECTING; ++ mdev->mcore.bits.ch_state_ack = mdev->ucore.bits.ch_state; ++ dev_dbg(mdev->dev, "Remote Disconnect"); ++ tmel_qmp_send_irq(mdev); ++ } ++ ++ /* Check TX done */ ++ if (atomic_read(&mdev->tx_sent) && ++ mdev->ucore.bits.rx_done != mdev->mcore.bits.rx_done_ack) { ++ /* Ack to remote */ ++ mdev->mcore.bits.rx_done_ack = mdev->ucore.bits.rx_done; ++ atomic_set(&mdev->tx_sent, 0); ++ dev_dbg(mdev->dev, "TX flag cleared"); ++ } ++ ++ /* Check if remote is Transmitting */ ++ if (!(mdev->ucore.bits.tx != mdev->mcore.bits.tx_ack)) ++ break; ++ if (mdev->ucore.bits.frag_size == 0 || ++ mdev->ucore.bits.frag_size > QMP_MAX_PKT_SIZE) { ++ dev_err(mdev->dev, "Rx frag size error %d\n", ++ mdev->ucore.bits.frag_size); ++ break; ++ } ++ tmel_qmp_recv_data(tdev, QMP_CTRL_DATA_SIZE); ++ break; ++ case LOCAL_DISCONNECTING: ++ if (!(mdev->mcore.bits.ch_state)) { ++ tmel_qmp_clr_mcore_ch_state(mdev); ++ mdev->local_state = LINK_CONNECTED; ++ dev_dbg(mdev->dev, "Channel closed"); ++ reinit_completion(&mdev->ch_complete); ++ } ++ ++ break; ++ default: ++ dev_err(mdev->dev, "Local Channel State corrupted\n"); ++ } ++ spin_unlock_irqrestore(&mdev->tx_lock, flags); ++} ++ ++/** ++ * tmel_qmp_irq_handler() - Handle incoming messages from remote processor. ++ * @irq: ipc interrupt from remote ++ * @priv: ptr to the corresponding tmel device. ++ */ ++static inline irqreturn_t tmel_qmp_irq_handler(int irq, void *priv) ++{ ++ struct tmel *tdev = (struct tmel *)priv; ++ ++ tmel_qmp_rx(tdev); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * tmel_prepare_msg() - copies the payload to the mbox destination ++ * @tdev: the tmel device ++ * @msg_uid: msg_type/action_id combo ++ * @msg_buf: payload to be sent ++ * @msg_size: size of the payload ++ */ ++static inline int tmel_prepare_msg(struct tmel *tdev, u32 msg_uid, ++ void *msg_buf, size_t msg_size) ++{ ++ struct tmel_ipc_pkt *ipc_pkt = tdev->ipc_pkt; ++ struct ipc_header *msg_hdr = &ipc_pkt->msg_hdr; ++ struct mbox_payload *mbox_payload = &ipc_pkt->payload.mbox_payload; ++ struct sram_payload *sram_payload = &ipc_pkt->payload.sram_payload; ++ int ret; ++ ++ memset(ipc_pkt, 0, sizeof(struct tmel_ipc_pkt)); ++ ++ msg_hdr->msg_type = TMEL_MSG_UID_MSG_TYPE(msg_uid); ++ msg_hdr->action_id = TMEL_MSG_UID_ACTION_ID(msg_uid); ++ ++ dev_dbg(tdev->dev, "uid: %d, msg_size: %zu msg_type:%d, action_id:%d\n", ++ msg_uid, msg_size, msg_hdr->msg_type, msg_hdr->action_id); ++ ++ if (sizeof(struct ipc_header) + msg_size <= QMP_MBOX_IPC_PACKET_SIZE) { ++ /* Mbox only */ ++ msg_hdr->ipc_type = IPC_MBOX_MEM; ++ msg_hdr->msg_len = msg_size; ++ memcpy((void *)mbox_payload, msg_buf, msg_size); ++ } else if (msg_size <= QMP_SRAM_IPC_MAX_BUF_SIZE) { ++ /* SRAM */ ++ msg_hdr->ipc_type = IPC_MBOX_SRAM; ++ msg_hdr->msg_len = 8; ++ ++ tdev->sram_dma_addr = dma_map_single(tdev->dev, msg_buf, ++ msg_size, ++ DMA_BIDIRECTIONAL); ++ ret = dma_mapping_error(tdev->dev, tdev->sram_dma_addr); ++ if (ret) { ++ dev_err(tdev->dev, "SRAM DMA mapping error: %d\n", ret); ++ return ret; ++ } ++ ++ sram_payload->payload_ptr = tdev->sram_dma_addr; ++ sram_payload->payload_len = msg_size; ++ } else { ++ dev_err(tdev->dev, "Invalid payload length: %zu\n", msg_size); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * tmel_unprepare_message() - Get the response data back for client ++ * @tdev: the tmel device ++ * @msg_buf: payload to be sent ++ * @msg_size: size of the payload ++ */ ++static inline void tmel_unprepare_message(struct tmel *tdev, void *msg_buf, size_t msg_size) ++{ ++ struct tmel_ipc_pkt *ipc_pkt = (struct tmel_ipc_pkt *)tdev->pkt.iov_base; ++ struct mbox_payload *mbox_payload = &ipc_pkt->payload.mbox_payload; ++ ++ if (ipc_pkt->msg_hdr.ipc_type == IPC_MBOX_MEM) { ++ memcpy(msg_buf, mbox_payload, msg_size); ++ } else if (ipc_pkt->msg_hdr.ipc_type == IPC_MBOX_SRAM) { ++ dma_unmap_single(tdev->dev, tdev->sram_dma_addr, msg_size, DMA_BIDIRECTIONAL); ++ tdev->sram_dma_addr = 0; ++ } ++} ++ ++static inline bool tmel_rx_done(struct tmel *tdev) ++{ ++ return tdev->rx_done; ++} ++ ++/** ++ * tmel_process_request() - process client msg and wait for response ++ * @tdev: the tmel device ++ * @msg_uid: msg_type/action_id combo ++ * @msg_buf: payload to be sent ++ * @msg_size: size of the payload ++ */ ++static inline int tmel_process_request(struct tmel *tdev, u32 msg_uid, ++ void *msg_buf, size_t msg_size) ++{ ++ struct qmp_device *mdev = tdev->mdev; ++ struct tmel_ipc_pkt *resp_ipc_pkt; ++ struct mbox_chan *chan; ++ unsigned long jiffies; ++ long time_left = 0; ++ int ret = 0; ++ ++ chan = &tdev->ctrl.chans[0]; ++ ++ if (!msg_buf || !msg_size) { ++ dev_err(tdev->dev, "Invalid msg_buf or msg_size\n"); ++ return -EINVAL; ++ } ++ ++ tdev->rx_done = false; ++ ++ ret = tmel_prepare_msg(tdev, msg_uid, msg_buf, msg_size); ++ if (ret) ++ return ret; ++ ++ tdev->pkt.iov_len = sizeof(struct tmel_ipc_pkt); ++ tdev->pkt.iov_base = (void *)tdev->ipc_pkt; ++ ++ tmel_qmp_send_data(mdev, &tdev->pkt); ++ jiffies = msecs_to_jiffies(QMP_SEND_TIMEOUT); ++ ++ time_left = wait_event_interruptible_timeout(tdev->waitq, ++ tmel_rx_done(tdev), ++ jiffies); ++ ++ if (!time_left) { ++ dev_err(tdev->dev, "Request timed out\n"); ++ atomic_set(&mdev->tx_sent, 0); ++ mbox_chan_txdone(chan, ret); ++ return -ETIMEDOUT; ++ } ++ ++ if (tdev->pkt.iov_len != sizeof(struct tmel_ipc_pkt)) ++ return -EPROTO; ++ ++ resp_ipc_pkt = (struct tmel_ipc_pkt *)tdev->pkt.iov_base; ++ tmel_unprepare_message(tdev, msg_buf, msg_size); ++ tdev->rx_done = false; ++ ++ return resp_ipc_pkt->msg_hdr.response; ++} ++ ++/** ++ * tmel_secboot_sec_free() - Release the dma alloc and kmalloc'ed memory ++ * @ptr: Address of the tmel_secboot_sec wrapper for dma and kmalloc region. ++ */ ++void tmel_secboot_sec_free(void *ptr) ++{ ++ struct tmel_secboot_sec *smsg = ptr; ++ void *elf_buf = smsg->elf_buf; ++ dma_addr_t elf_buf_phys; ++ u32 size; ++ ++ elf_buf_phys = smsg->msg.req.elf_buf.buf; ++ size = smsg->msg.req.elf_buf.buf_len; ++ dma_free_coherent(smsg->dev, size, elf_buf, elf_buf_phys); ++ kfree(smsg); ++} ++ ++/** ++ * tmel_secboot_sec_auth() - authenticate the remote subsys image ++ * @tdev: the tmel device ++ * @sw_id: pas_id of the remote ++ * @metadata: payload to be sent ++ * @size: size of the payload ++ */ ++static inline int tmel_secboot_sec_auth(struct tmel *tdev, u32 sw_id, ++ void *metadata, size_t size) ++{ ++ struct tmel_secboot_sec *smsg __free(tmel_secboot_sec_f) = NULL; ++ struct device *dev = tdev->dev; ++ dma_addr_t elf_buf_phys; ++ void *elf_buf; ++ int ret; ++ ++ if (!dev || !metadata) ++ return -EINVAL; ++ ++ smsg = kzalloc(sizeof(*smsg), GFP_KERNEL); ++ ++ elf_buf = dma_alloc_coherent(dev, size, &elf_buf_phys, GFP_KERNEL); ++ if (!elf_buf) ++ return -ENOMEM; ++ ++ memcpy(elf_buf, metadata, size); ++ ++ smsg->dev = dev; ++ smsg->elf_buf = elf_buf; ++ ++ smsg->msg.req.sw_id = sw_id; ++ smsg->msg.req.elf_buf.buf = (u32)elf_buf_phys; ++ smsg->msg.req.elf_buf.buf_len = (u32)size; ++ ++ ret = tmel_process_request(tdev, TMEL_MSG_UID_SECBOOT_SEC_AUTH, ++ &smsg->msg, ++ sizeof(struct tmel_secboot_sec_auth)); ++ if (ret) { ++ dev_err(dev, "Failed to send IPC: %d\n", ret); ++ } else if (smsg->msg.resp.status) { ++ dev_err(dev, "Failed with status: %d", smsg->msg.resp.status); ++ ret = smsg->msg.resp.status ? -EINVAL : 0; ++ } else if (smsg->msg.resp.extended_error) { ++ dev_err(dev, "Failed with error: %d", smsg->msg.resp.extended_error); ++ ret = smsg->msg.resp.extended_error ? -EINVAL : 0; ++ } ++ ++ return ret; ++} ++ ++/** ++ * tmel_secboot_teardown() - teardown the remote subsys ++ * @tdev: tmel device ++ * @sw_id: pas_id of the remote ++ * @secondary_sw_id: extra argument for the pas_id ++ */ ++static inline int tmel_secboot_teardown(struct tmel *tdev, u32 sw_id, ++ u32 secondary_sw_id) ++{ ++ struct tmel_secboot_teardown msg = {0}; ++ struct device *dev = tdev->dev; ++ int ret; ++ ++ if (!dev) ++ return -EINVAL; ++ ++ msg.req.sw_id = sw_id; ++ msg.req.secondary_sw_id = secondary_sw_id; ++ msg.resp.status = TMEL_ERROR_GENERIC; ++ ++ ret = tmel_process_request(tdev, TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN, ++ &msg, sizeof(msg)); ++ if (ret) { ++ dev_err(dev, "Failed to send IPC: %d\n", ret); ++ } else if (msg.resp.status) { ++ dev_err(dev, "Failed with status: %d\n", msg.resp.status); ++ ret = msg.resp.status ? -EINVAL : 0; ++ } ++ ++ return ret; ++} ++ ++static inline void tmel_qmp_send_work(struct work_struct *work) ++{ ++ struct tmel *tdev = container_of(work, struct tmel, twork); ++ struct tmel_qmp_msg *tmsg = tdev->data; ++ struct tmel_sec_auth *smsg = tmsg->msg; ++ struct mbox_chan *chan; ++ int ret; ++ ++ chan = &tdev->ctrl.chans[0]; ++ ++ switch (tmsg->msg_id) { ++ case TMEL_MSG_UID_SECBOOT_SEC_AUTH: ++ ret = tmel_secboot_sec_auth(tdev, smsg->pas_id, smsg->data, smsg->size); ++ break; ++ case TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN: ++ ret = tmel_secboot_teardown(tdev, smsg->pas_id, 0); ++ break; ++ } ++ ++ mbox_chan_txdone(chan, ret); ++} ++ ++/** ++ * tmel_qmp_startup() - Start qmp mailbox channel for communication. ++ * @chan: mailbox channel that is being opened. ++ * Waits for remote subsystem to open channel if link is not ++ * initiated or until timeout. ++ */ ++static inline int tmel_qmp_startup(struct mbox_chan *chan) ++{ ++ struct tmel *tdev = chan->con_priv; ++ struct qmp_device *mdev = tdev->mdev; ++ unsigned long flags; ++ int ret; ++ ++ /* ++ * Kick start the SM from the negotiation phase ++ * Rest of the link changes would follow when remote responds. ++ */ ++ spin_lock_irqsave(&mdev->tx_lock, flags); ++ mdev->mcore.bits.link_state = 1; ++ mdev->local_state = LINK_NEGOTIATION; ++ spin_unlock_irqrestore(&mdev->tx_lock, flags); ++ ++ mdev->rx_pkt.iov_base = devm_kcalloc(mdev->dev, 1, QMP_MAX_PKT_SIZE, ++ GFP_KERNEL); ++ if (!mdev->rx_pkt.iov_base) ++ return -ENOMEM; ++ ++ tmel_qmp_send_irq(mdev); ++ ++ ret = wait_for_completion_timeout(&mdev->link_complete, ++ msecs_to_jiffies(QMP_TOUT_MS)); ++ if (!ret) ++ return -EAGAIN; ++ ++ spin_lock_irqsave(&mdev->tx_lock, flags); ++ if (mdev->local_state == LINK_CONNECTED) { ++ mdev->mcore.bits.ch_state = 1; ++ mdev->local_state = LOCAL_CONNECTING; ++ dev_dbg(mdev->dev, "link complete, local connecting"); ++ tmel_qmp_send_irq(mdev); ++ } ++ spin_unlock_irqrestore(&mdev->tx_lock, flags); ++ ++ ret = wait_for_completion_timeout(&mdev->ch_complete, ++ msecs_to_jiffies(QMP_TOUT_MS)); ++ if (!ret) ++ return -ETIMEDOUT; ++ ++ return 0; ++} ++ ++/** ++ * tmel_qmp_shutdown() - Shutdown this mailbox channel. ++ * @chan: mailbox channel to be shutdown. ++ * Disconnect this mailbox channel so the client does not receive anymore ++ * data and can reliquish control of the channel. ++ */ ++static inline void tmel_qmp_shutdown(struct mbox_chan *chan) ++{ ++ struct qmp_device *mdev = chan->con_priv; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&mdev->tx_lock, flags); ++ if (mdev->local_state != LINK_DISCONNECTED) { ++ mdev->local_state = LOCAL_DISCONNECTING; ++ mdev->mcore.bits.ch_state = 0; ++ tmel_qmp_send_irq(mdev); ++ } ++ spin_unlock_irqrestore(&mdev->tx_lock, flags); ++} ++ ++static inline int tmel_qmp_send(struct mbox_chan *chan, void *data) ++{ ++ struct tmel *tdev = chan->con_priv; ++ ++ tdev->data = data; ++ queue_work(system_wq, &tdev->twork); ++ ++ return 0; ++} ++ ++static struct mbox_chan_ops tmel_qmp_ops = { ++ .startup = tmel_qmp_startup, ++ .shutdown = tmel_qmp_shutdown, ++ .send_data = tmel_qmp_send, ++}; ++ ++static inline struct tmel *tmel_init(struct platform_device *pdev) ++{ ++ struct tmel *tdev; ++ struct mbox_chan *chans; ++ ++ tdev = devm_kcalloc(&pdev->dev, 1, sizeof(*tdev), GFP_KERNEL); ++ if (!tdev) ++ return ERR_PTR(-ENOMEM); ++ ++ tdev->ipc_pkt = devm_kcalloc(&pdev->dev, 1, sizeof(struct tmel_ipc_pkt), GFP_KERNEL); ++ if (!tdev->ipc_pkt) ++ return ERR_PTR(-ENOMEM); ++ ++ init_waitqueue_head(&tdev->waitq); ++ ++ tdev->rx_done = false; ++ tdev->dev = &pdev->dev; ++ platform_set_drvdata(pdev, tdev); ++ ++ chans = devm_kcalloc(&pdev->dev, QMP_NUM_CHANS, sizeof(*chans), GFP_KERNEL); ++ if (!chans) ++ return ERR_PTR(-ENOMEM); ++ ++ tdev->ctrl.chans = chans; ++ INIT_WORK(&tdev->twork, tmel_qmp_send_work); ++ ++ return tdev; ++} ++ ++static inline struct qmp_device *qmp_init(struct platform_device *pdev) ++{ ++ struct qmp_device *mdev; ++ ++ mdev = devm_kcalloc(&pdev->dev, 1, sizeof(*mdev), GFP_KERNEL); ++ if (!mdev) ++ return ERR_PTR(-ENOMEM); ++ ++ mdev->dev = &pdev->dev; ++ mdev->mcore_desc = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mdev->mcore_desc)) ++ return ERR_PTR(-EIO); ++ ++ mdev->ucore_desc = mdev->mcore_desc + QMP_UCORE_DESC_OFFSET; ++ ++ spin_lock_init(&mdev->tx_lock); ++ mdev->local_state = LINK_DISCONNECTED; ++ init_completion(&mdev->link_complete); ++ init_completion(&mdev->ch_complete); ++ ++ return mdev; ++} ++ ++static inline int qmp_mbox_client_init(struct qmp_device *mdev) ++{ ++ int ret = 0; ++ ++ mdev->mbox_client.dev = mdev->dev; ++ mdev->mbox_client.knows_txdone = false; ++ mdev->mbox_chan = mbox_request_channel(&mdev->mbox_client, 0); ++ if (IS_ERR(mdev->mbox_chan)) ++ ret = PTR_ERR(mdev->mbox_chan); ++ ++ return ret; ++} ++ ++static inline int tmel_mbox_ctrl_init(struct tmel *tdev) ++{ ++ tdev->ctrl.dev = tdev->dev; ++ tdev->ctrl.ops = &tmel_qmp_ops; ++ tdev->ctrl.chans[0].con_priv = tdev; ++ tdev->ctrl.num_chans = QMP_NUM_CHANS; ++ tdev->ctrl.txdone_irq = true; ++ ++ return devm_mbox_controller_register(tdev->dev, &tdev->ctrl); ++} ++ ++static inline int tmel_qmp_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct qmp_device *mdev; ++ struct tmel *tdev; ++ int ret = 0; ++ ++ tdev = tmel_init(pdev); ++ if (IS_ERR(tdev)) ++ return dev_err_probe(tdev->dev, ret, "tmel device init failed\n"); ++ ++ mdev = qmp_init(pdev); ++ if (IS_ERR(mdev)) ++ return dev_err_probe(tdev->dev, ret, "qmp device init failed\n"); ++ ++ tdev->mdev = mdev; ++ ++ ret = qmp_mbox_client_init(mdev); ++ if (ret) ++ return dev_err_probe(mdev->dev, ret, "IPC chan missing, client init failed"); ++ ++ ret = tmel_mbox_ctrl_init(tdev); ++ if (ret) ++ return dev_err_probe(tdev->dev, ret, "failed to register mbox controller"); ++ ++ ret = platform_get_irq(pdev, 0); ++ ret = devm_request_threaded_irq(tdev->dev, ret, NULL, tmel_qmp_irq_handler, ++ IRQF_TRIGGER_RISING | IRQF_ONESHOT, ++ node->name, (void *)tdev); ++ if (ret < 0) ++ return dev_err_probe(tdev->dev, ret, "request threaded irq failed\n"); ++ ++ return ret; ++} ++ ++static const struct of_device_id tmel_qmp_dt_match[] = { ++ { .compatible = "qcom,ipq5424-tmel" }, ++ {}, ++}; ++ ++static struct platform_driver tmel_qmp_driver = { ++ .driver = { ++ .name = "tmel_qmp_mbox", ++ .of_match_table = tmel_qmp_dt_match, ++ }, ++ .probe = tmel_qmp_probe, ++}; ++module_platform_driver(tmel_qmp_driver); ++ ++MODULE_DESCRIPTION("QCOM TMEL QMP driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/linux/mailbox/tmelcom-qmp.h +@@ -0,0 +1,65 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++#ifndef _TMELCOM_H_ ++#define _TMELCOM_H_ ++ ++#include ++ ++/* ++ * Macro used to define unique TMEL Message Identifier based on ++ * message type and action identifier. ++ */ ++#define MSGTYPE_MASK GENMASK(15, 8) ++#define ACTIONID_MASK GENMASK(7, 0) ++ ++#define TMEL_MSG_UID_CREATE(msg_type, action_id) \ ++ (FIELD_PREP_CONST(MSGTYPE_MASK, msg_type) | \ ++ FIELD_PREP_CONST(ACTIONID_MASK, action_id)) ++ ++/* ++ * Helper macro to extract the messageType from TMEL_MSG_UID ++ */ ++#define TMEL_MSG_UID_MSG_TYPE(v) FIELD_GET(MSGTYPE_MASK, v) ++ ++/* ++ * Helper macro to extract the actionID from TMEL_MSG_UID ++ */ ++#define TMEL_MSG_UID_ACTION_ID(v) FIELD_GET(ACTIONID_MASK, v) ++ ++/* ++ * All definitions of supported messageTypes. ++ */ ++#define TMEL_MSG_SECBOOT 0x00 ++ ++/* ++ * Action IDs for TMEL_MSG_SECBOOT ++ */ ++#define TMEL_ACTION_SECBOOT_SEC_AUTH 0x04 ++#define TMEL_ACTION_SECBOOT_SS_TEAR_DOWN 0x0a ++ ++/* ++ * UIDs for TMEL_MSG_SECBOOT ++ */ ++#define TMEL_MSG_UID_SECBOOT_SEC_AUTH TMEL_MSG_UID_CREATE(TMEL_MSG_SECBOOT,\ ++ TMEL_ACTION_SECBOOT_SEC_AUTH) ++ ++#define TMEL_MSG_UID_SECBOOT_SS_TEAR_DOWN TMEL_MSG_UID_CREATE(TMEL_MSG_SECBOOT,\ ++ TMEL_ACTION_SECBOOT_SS_TEAR_DOWN) ++ ++struct tmel_qmp_msg { ++ void *msg; ++ u32 msg_id; ++}; ++ ++struct tmel_sec_auth { ++ void *data; ++ u32 size; ++ u32 pas_id; ++}; ++ ++void tmel_secboot_sec_free(void *ptr); ++ ++DEFINE_FREE(tmel_secboot_sec_f, void *, if (_T) tmel_secboot_sec_free(_T)) ++#endif /* _TMELCOM_H_ */ diff --git a/target/linux/qualcommax/patches-6.12/0185-firmware-qcom_scm-ipq5332-add-support-to-pass-metadata-size.patch b/target/linux/qualcommax/patches-6.12/0185-firmware-qcom_scm-ipq5332-add-support-to-pass-metadata-size.patch new file mode 100644 index 0000000000..103c4434d7 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0185-firmware-qcom_scm-ipq5332-add-support-to-pass-metadata-size.patch @@ -0,0 +1,64 @@ +From 9d67219c941458fd26eb474d25f9a6b2525876ca Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +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 +Signed-off-by: Manikanta Mylavarapu +Signed-off-by: Gokul Sriram Palanisamy +--- + 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 diff --git a/target/linux/qualcommax/patches-6.12/0186-dt-bindings-remoteproc-qcom-document-hexagon-based-wcss-secure-pil.patch b/target/linux/qualcommax/patches-6.12/0186-dt-bindings-remoteproc-qcom-document-hexagon-based-wcss-secure-pil.patch new file mode 100644 index 0000000000..ff74f44bdd --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0186-dt-bindings-remoteproc-qcom-document-hexagon-based-wcss-secure-pil.patch @@ -0,0 +1,157 @@ +From db128ef0282b8cc41816caf39a03fa17d10fa865 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +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 +Signed-off-by: Manikanta Mylavarapu +Signed-off-by: Gokul Sriram Palanisamy +--- + .../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 ++ ++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 ++ #include ++ 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 = ; ++ label = "rtr"; ++ qcom,remote-pid = <1>; ++ mboxes = <&apcs_glb 8>; ++ }; ++ }; diff --git a/target/linux/qualcommax/patches-6.12/0187-clk-qcom-gcc-ipq5018-flag-sleep-clock-as-critical.patch b/target/linux/qualcommax/patches-6.12/0187-clk-qcom-gcc-ipq5018-flag-sleep-clock-as-critical.patch new file mode 100644 index 0000000000..2b51938c62 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0187-clk-qcom-gcc-ipq5018-flag-sleep-clock-as-critical.patch @@ -0,0 +1,19 @@ +From: George Moussalem +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 +--- +--- 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, + }, + }, diff --git a/target/linux/qualcommax/patches-6.12/0188-remoteproc-qcom-add-hexagon-based-wcss-secure-pil-driver.patch b/target/linux/qualcommax/patches-6.12/0188-remoteproc-qcom-add-hexagon-based-wcss-secure-pil-driver.patch new file mode 100644 index 0000000000..d999e5563d --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0188-remoteproc-qcom-add-hexagon-based-wcss-secure-pil-driver.patch @@ -0,0 +1,500 @@ +From 47d46157200bb5f67e540a87c67aab9cde62f8a4 Mon Sep 17 00:00:00 2001 +From: Vignesh Viswanathan +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 +Signed-off-by: Manikanta Mylavarapu +Signed-off-by: Gokul Sriram Palanisamy +Signed-off-by: George Moussalem +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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); diff --git a/target/linux/qualcommax/patches-6.12/0723-clk-qcom-gcc-ipq5018-fix-uniphy-soft-reset-issue.patch b/target/linux/qualcommax/patches-6.12/0723-clk-qcom-gcc-ipq5018-fix-uniphy-soft-reset-issue.patch index f4d7742722..f670ef442b 100644 --- a/target/linux/qualcommax/patches-6.12/0723-clk-qcom-gcc-ipq5018-fix-uniphy-soft-reset-issue.patch +++ b/target/linux/qualcommax/patches-6.12/0723-clk-qcom-gcc-ipq5018-fix-uniphy-soft-reset-issue.patch @@ -11,7 +11,7 @@ Link: https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/commit/036b Signed-off-by: George Moussalem --- 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 }, diff --git a/target/linux/qualcommax/patches-6.12/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch b/target/linux/qualcommax/patches-6.12/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch deleted file mode 100644 index c12e60f3d4..0000000000 --- a/target/linux/qualcommax/patches-6.12/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 50799703c6c8ec0860e19b102dd7cca3d29028e1 Mon Sep 17 00:00:00 2001 -From: Manikanta Mylavarapu -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 ---- - 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 diff --git a/target/linux/qualcommax/patches-6.12/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch b/target/linux/qualcommax/patches-6.12/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch index 0d62ece676..438e553e2e 100644 --- a/target/linux/qualcommax/patches-6.12/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch +++ b/target/linux/qualcommax/patches-6.12/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch @@ -28,7 +28,7 @@ Signed-off-by: Manikanta Mylavarapu 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 --- 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 diff --git a/target/linux/qualcommax/patches-6.12/0808-remoteproc-qcom-wcss-sec-add-split-firmware-support.patch b/target/linux/qualcommax/patches-6.12/0808-remoteproc-qcom-wcss-sec-add-split-firmware-support.patch new file mode 100644 index 0000000000..29644e3fdb --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0808-remoteproc-qcom-wcss-sec-add-split-firmware-support.patch @@ -0,0 +1,102 @@ +From: George Moussalem +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 +--- +--- 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) diff --git a/target/linux/qualcommax/patches-6.12/0811-firmware-qcom_scm-support-MPD.patch b/target/linux/qualcommax/patches-6.12/0811-firmware-qcom_scm-support-MPD.patch index d2150ef5fb..760f76cd71 100644 --- a/target/linux/qualcommax/patches-6.12/0811-firmware-qcom_scm-support-MPD.patch +++ b/target/linux/qualcommax/patches-6.12/0811-firmware-qcom_scm-support-MPD.patch @@ -28,7 +28,7 @@ Signed-off-by: George Moussalem --- 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 --- 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 -- 2.30.2