From nobody Mon Apr 6 09:19:41 2026 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) (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 556A82E11A6; Fri, 20 Mar 2026 03:20:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.244.123.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976860; cv=none; b=kWhmwSPgoFiFapU0Sb9HwfR7/a8zwsRqnPs6r/tLFt/tR3sHt6UJZZOpZxf4jGIgEymex8NwEVsE5lP5CTMWuU1uEQPpa6/46FbHpUTejw4nO1MceWLVoqSwr3gPQS0OqHkVMc+RXe4b5tw/hfCctoJgn4haMz6Oy1jw6kRFPPE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976860; c=relaxed/simple; bh=iHKxvtz2PgFYLrcWYXN0oarWIty7O/8RDbR4hpCvHDs=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=PuseVppzTcdeFOTYIQGhx9Ua0O2KowJmNA7cLAythGyNj9mzgWRE0T9yMM1AAofrDbTMPb7xn9WacMlX4V6Gp++0SZmpBs/sr8OMgQPViXkadIdsfBNe7UKnc4+doMRXm5nf69WclF4H3QbChfQaolORUYZ0Oc14kqsqnXJ+1h8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=dT7GIKyl; arc=none smtp.client-ip=60.244.123.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="dT7GIKyl" X-UUID: cc07a6ea240b11f1a02d4725871ece0b-20260320 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=PhHH1eFF51u/zNrMzfxhIKmtLMJpZ4bBYigWRekSPi0=; b=dT7GIKylHdOx8YTiaA/gc4j1FU8Bq2RwWiK6fXEus/Q1WTzARqwiUYNQqpfnI3P+sbXAUcDBsugtLb7hU/bzs5nHF0JYx6vuNDWtVibhftO+gWIPqqDeWnQu96w/3Sk13bdEUTdLMmOziuksrSPLcGFjB3842MAfwcnw6CLs33w=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:111a8d34-7714-4c32-af33-f409be553f99,IP:0,U RL:25,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:25 X-CID-META: VersionHash:e7bac3a,CLOUDID:c81ab54c-9183-487b-8624-e74f2dd98990,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|836|888|898,TC:-5,Content: 0|15|50,EDM:-3,IP:nil,URL:11|97|99|83|106|1,File:130,RT:0,Bulk:nil,QS:nil, BEC:-1,COL:0,OSI:0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_ULN,TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: cc07a6ea240b11f1a02d4725871ece0b-20260320 Received: from mtkmbs09n2.mediatek.inc [(172.21.101.94)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 422781593; Fri, 20 Mar 2026 11:20:51 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by mtkmbs11n1.mediatek.inc (172.21.101.185) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.29; Fri, 20 Mar 2026 11:20:49 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.2562.29 via Frontend Transport; Fri, 20 Mar 2026 11:20:48 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , , Hailong Fan , Huayu Zong , Xiangzhi Tang Subject: [PATCH v3 1/6] dt-bindings: remoteproc: Add VCP support for mt8196 Date: Fri, 20 Mar 2026 11:18:03 +0800 Message-ID: <20260320032014.13608-2-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> References: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the new binding document for MediaTek Video Companion Processor(VCP) on MediaTek mt8196. Signed-off-by: Xiangzhi Tang --- .../remoteproc/mediatek,mt8196-vcp.yaml | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 Documentation/devicetree/bindings/remoteproc/mediatek,m= t8196-vcp.yaml diff --git a/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-v= cp.yaml b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.= yaml new file mode 100644 index 000000000000..7ec1ec69537a --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/mediatek,mt8196-vcp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Video Companion Processor (VCP) + +maintainers: + - Xiangzhi Tang + +description: + The MediaTek VCP enables the SoC control the MediaTek Video Companion Ri= sc-V coprocessor. + +properties: + compatible: + enum: + - mediatek,mt8196-vcp + + reg: + items: + - description: sram base + - description: cfg group IO + - description: cfg core group IO + - description: cfg sec group IO + + reg-names: + items: + - const: sram + - const: cfg + - const: cfg_core + - const: cfg_sec + + interrupts: + maxItems: 1 + + mboxes: + maxItems: 5 + + mbox-names: + maxItems: 5 + + power-domains: + maxItems: 1 + + iommus: + description: + Using MediaTek iommu to apply larb ports for Multimedia Memory + Management Unit and address translation + Documentation/devicetree/bindings/iommu/arm,smmu-v3.yaml + maxItems: 1 + + memory-region: + maxItems: 1 + +patternProperties: + "^vcp@[a-f0-9]+$": + type: object + description: + The MediaTek VCP integrated to SoC might be a multi-core version. + The other cores are represented as child nodes of the boot core. + There are some integration differences for the IP like the usage of + address translator for translating SoC bus addresses into address + space for the processor. + + The SRAM are shared by all cores, each VCP core only using a piece + SRAM memory. The power of SRAM should be enabled before booting VCP = cores. + The size of SRAM are varied on differnt SoCs. + + The VCP cores has differences on different SoCs to support for + Hart. + + properties: + compatible: + enum: + - mediatek,vcp-core + + reg: + description: The base address and size of SRAM. + maxItems: 1 + + reg-names: + const: sram + + mtk,vcp-core-twohart: + enum: [0, 1] + $ref: /schemas/types.yaml#/definitions/uint32 + + mtk,vcp-sram-offset: + description: + Allocated SRAM memory for each VCP core used. + $ref: /schemas/types.yaml#/definitions/uint32 + + required: + - compatible + - reg + - reg-names + - mtk,vcp-core-twohart + - mtk,vcp-sram-offset + + additionalProperties: false + +required: + - compatible + - reg + - reg-names + - interrupts + - mboxes + - mbox-names + - power-domains + - iommus + - memory-region + +additionalProperties: false + +examples: + - | + #include + #include + #include + + vcp: vcp@31800000 { + compatible =3D "mediatek,mt8196-vcp"; + reg =3D <0x31800000 0x60000>, + <0x31a04000 0xa000>, + <0x31bd0000 0x1000>, + <0x31a70020 0x100>, + reg-names =3D "sram", + "cfg", + "cfg_core", + "cfg_sec"; + + interrupts =3D ; + + mboxes =3D <&vcp_mailbox0>, + <&vcp_mailbox1>, + <&vcp_mailbox2>, + <&vcp_mailbox3>, + <&vcp_mailbox4>; + mbox-names =3D "mbox0", "mbox1", "mbox2", "mbox3", "mbox4"; + + power-domains =3D <&scpsys MT8196_POWER_DOMAIN_MM_PROC_DORMANT>; + iommus =3D <&mm_smmu 160>; + memory-region =3D <&vcp_resv_mem>; + + vcp@0 { + compatible =3D "mediatek,vcp-core"; + reg =3D <0x0 0x31000>; + reg-names =3D "sram"; + mtk,vcp-core-twohart =3D <1>; + mtk,vcp-sram-offset =3D <0x0>; + }; + + vcp@31000 { + compatible =3D "mediatek,vcp-core"; + reg =3D <0x31000 0x60000>; + reg-names =3D "sram"; + mtk,vcp-core-twohart =3D <0>; + mtk,vcp-sram-offset =3D <0x31000>; + }; + }; --=20 2.46.0 From nobody Mon Apr 6 09:19:41 2026 Received: from mailgw02.mediatek.com (unknown [210.61.82.184]) (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 5C85C1C84A0; Fri, 20 Mar 2026 03:21:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.61.82.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976867; cv=none; b=jUNiAwiGvmDfThN23BtsMHhaJl/KEeaJ8WW3+OzbljouslBH/OnNRnS+hB6YaAQAKcA14cvKdSMeN2nlw7k9rQkCScDN+YRvwstw+4d/gxGF+uQNM6D/KmnvJ7qYYxLwsTCMwp1MtQ4Ua3uZ8jNzjnIfOqv4T4lJb9yEc8RQQ5A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976867; c=relaxed/simple; bh=V08inFFHA8uZ8eEuFlhUbysgLe6O9Pnp8wrbNm7DdPk=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=V5npAqNiwlVBWLdE/eERf7CeyZBV544Doxc8Yr1COI4fm882+tsPtQqDqUShMzCmdOWqGiq4lLyvnXWe39YqVt2+gbyOwyH0PT/FOI9HEoOmEWlpvElJEjZ2nCJnzjGmauzhV7fY2yx0U1da6I/u6ftbLT34jiqLhc73ylrVekA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=GDqx7vXc; arc=none smtp.client-ip=210.61.82.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="GDqx7vXc" X-UUID: ce8ab286240b11f1a39cd589f645bc18-20260320 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=WnALF2XoA7CLU3lEShYH0fiVckTzWi6axE6z0Ueuug4=; b=GDqx7vXceTKWCt7xDOhTq4XEVAmCwZ4yr6LThtWjoFwQGcdELgAnRZK2WeMqPFSQq0BS4GkT7jU7Vt/E4mZjM+arGKJBvhxD6AqWTW9by8Ol5s0F110MGaou/l3z/SsQKwN+tqXfb3h0+QvFL4ggKvXdZ9eZeKKAcgzzbSGLO8E=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:a2e03005-6532-42ab-b347-1940d3998c54,IP:0,U RL:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION: release,TS:0 X-CID-META: VersionHash:e7bac3a,CLOUDID:b05aeed4-060f-4ecc-9ee0-121eeeb4a682,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|836|888|898,TC:-5,Content: 0|15|50,EDM:-3,IP:nil,URL:0,File:130,RT:0,Bulk:nil,QS:nil,BEC:-1,COL:0,OSI :0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: ce8ab286240b11f1a39cd589f645bc18-20260320 Received: from mtkmbs10n2.mediatek.inc [(172.21.101.183)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 304706205; Fri, 20 Mar 2026 11:20:55 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by mtkmbs10n1.mediatek.inc (172.21.101.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.29; Fri, 20 Mar 2026 11:20:53 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.2562.29 via Frontend Transport; Fri, 20 Mar 2026 11:20:52 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Matthias Brugger" , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , , Hailong Fan , Huayu Zong , "Xiangzhi Tang" Subject: [PATCH v3 2/6] remoteproc: Mediatek: Add VCP remoteproc driver Date: Fri, 20 Mar 2026 11:18:04 +0800 Message-ID: <20260320032014.13608-3-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> References: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 1.Support rproc mechanism to load vcm firmware from filesystem 2.Support SMC services to request ATF to setting vcp boot sequence Signed-off-by: Xiangzhi Tang --- drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 3 + drivers/remoteproc/mtk_vcp_common.c | 461 ++++++++++++++++++++++ drivers/remoteproc/mtk_vcp_common.h | 217 ++++++++++ drivers/remoteproc/mtk_vcp_rproc.c | 325 +++++++++++++++ drivers/remoteproc/mtk_vcp_rproc.h | 69 ++++ include/linux/remoteproc/mtk_vcp_public.h | 78 ++++ include/linux/soc/mediatek/mtk_sip_svc.h | 2 + 8 files changed, 1165 insertions(+) create mode 100644 drivers/remoteproc/mtk_vcp_common.c create mode 100644 drivers/remoteproc/mtk_vcp_common.h create mode 100644 drivers/remoteproc/mtk_vcp_rproc.c create mode 100644 drivers/remoteproc/mtk_vcp_rproc.h create mode 100644 include/linux/remoteproc/mtk_vcp_public.h diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index ee54436fea5a..93827f6fd3c5 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -66,6 +66,16 @@ config MTK_SCP =20 It's safe to say N here. =20 +config MTK_VCP_RPROC + tristate "MediaTek VCP support" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on ARCH_DMA_ADDR_T_64BIT + help + Say y here to support MediaTek's Video Companion Processor (VCP) via + the remote processor framework. + + It's safe to say N here. + config OMAP_REMOTEPROC tristate "OMAP remoteproc support" depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 1c7598b8475d..ad48d85c5019 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -15,6 +15,9 @@ obj-$(CONFIG_IMX_REMOTEPROC) +=3D imx_rproc.o obj-$(CONFIG_IMX_DSP_REMOTEPROC) +=3D imx_dsp_rproc.o obj-$(CONFIG_INGENIC_VPU_RPROC) +=3D ingenic_rproc.o obj-$(CONFIG_MTK_SCP) +=3D mtk_scp.o mtk_scp_ipi.o +obj-$(CONFIG_MTK_VCP_RPROC) +=3D mtk_vcp.o +mtk_vcp-$(CONFIG_MTK_VCP_RPROC) +=3D mtk_vcp_rproc.o +mtk_vcp-$(CONFIG_MTK_VCP_RPROC) +=3D mtk_vcp_common.o obj-$(CONFIG_OMAP_REMOTEPROC) +=3D omap_remoteproc.o obj-$(CONFIG_WKUP_M3_RPROC) +=3D wkup_m3_rproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) +=3D da8xx_remoteproc.o diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_v= cp_common.c new file mode 100644 index 000000000000..97ea8099912d --- /dev/null +++ b/drivers/remoteproc/mtk_vcp_common.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 MediaTek Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_vcp_common.h" +#include "mtk_vcp_rproc.h" + +phys_addr_t vcp_get_reserve_mem_phys(struct mtk_vcp_device *vcp, + enum vcp_reserve_mem_id id) +{ + if (id >=3D 0 && id < NUMS_MEM_ID) + return vcp->vcp_cluster->vcp_memory_tb[id].phys; + + return 0; +} + +dma_addr_t vcp_get_reserve_mem_iova(struct mtk_vcp_device *vcp, + enum vcp_reserve_mem_id id) +{ + if (id >=3D 0 && id < NUMS_MEM_ID) + return vcp->vcp_cluster->vcp_memory_tb[id].iova; + + return 0; +} + +void __iomem *vcp_get_reserve_mem_virt(struct mtk_vcp_device *vcp, + enum vcp_reserve_mem_id id) +{ + if (id >=3D 0 && id < NUMS_MEM_ID) + return vcp->vcp_cluster->vcp_memory_tb[id].virt; + + return NULL; +} + +size_t vcp_get_reserve_mem_size(struct mtk_vcp_device *vcp, + enum vcp_reserve_mem_id id) +{ + if (id >=3D 0 && id < NUMS_MEM_ID) + return vcp->vcp_cluster->vcp_memory_tb[id].size; + + return 0; +} + +void __iomem *vcp_get_internal_sram_virt(struct mtk_vcp_device *vcp) +{ + return vcp->vcp_cluster->sram_base; +} + +int vcp_reserve_memory_init(struct mtk_vcp_device *vcp) +{ + struct device_node *rmem_node; + struct resource res; + struct iommu_domain *domain; + void __iomem *share_memory_virt; + void __iomem *rtos_memory_virt; + phys_addr_t mblock_phys; + phys_addr_t share_memory_phys; + dma_addr_t share_memory_iova; + size_t mblock_size; + size_t share_memory_size; + enum vcp_reserve_mem_id id; + u32 offset; + int ret; + + rmem_node =3D of_parse_phandle(vcp->dev->of_node, "memory-region", 0); + if (!rmem_node) + return dev_err_probe(vcp->dev, -ENODEV, "No reserved memory-region found= .\n"); + + ret =3D of_address_to_resource(rmem_node, 0, &res); + if (ret) + return dev_err_probe(vcp->dev, ret, "failed to parse reserved memory\n"); + + mblock_phys =3D (phys_addr_t)res.start; + mblock_size =3D (size_t)resource_size(&res); + + offset =3D 0; + for (id =3D 0; id < NUMS_MEM_ID; id++) { + vcp->vcp_cluster->vcp_memory_tb[id].phys =3D mblock_phys + offset; + vcp->vcp_cluster->vcp_memory_tb[id].size =3D vcp->platdata->memory_tb[id= ].size; + offset +=3D vcp->vcp_cluster->vcp_memory_tb[id].size; + } + if (offset > mblock_size) + return dev_err_probe(vcp->dev, -EINVAL, "No enough reserved memory to al= loc"); + + share_memory_size =3D offset - vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_M= EM_ID].size; + share_memory_phys =3D vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].ph= ys + + vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].size; + + rtos_memory_virt =3D devm_ioremap(vcp->dev, + vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].phys, + vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].size); + domain =3D iommu_get_domain_for_dev(vcp->dev); + ret =3D iommu_map(domain, vcp->platdata->rtos_static_iova, + vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].phys, + vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].size, + IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL); + if (ret) + return dev_err_probe(vcp->dev, ret, "iommu map failed\n"); + + vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].virt =3D rtos_memory_vir= t; + vcp->vcp_cluster->vcp_memory_tb[VCP_RTOS_MEM_ID].iova =3D vcp->platdata->= rtos_static_iova; + + ret =3D dma_set_mask_and_coherent(vcp->dev, DMA_BIT_MASK(DMA_MAX_MASK_BIT= )); + if (ret) + return dev_err_probe(vcp->dev, ret, "64-bit DMA enable failed\n"); + + if (!vcp->dev->dma_parms) { + vcp->dev->dma_parms =3D devm_kzalloc(vcp->dev, + sizeof(*vcp->dev->dma_parms), + GFP_KERNEL); + if (vcp->dev->dma_parms) + dma_set_max_seg_size(vcp->dev, (u32)DMA_BIT_MASK(33)); + else + return dev_err_probe(vcp->dev, -EINVAL, "Failed to set DMA parms\n"); + } + share_memory_virt =3D dma_alloc_coherent(vcp->dev, + share_memory_size, + &share_memory_iova, + GFP_KERNEL); + if (!share_memory_virt) + return dev_err_probe(vcp->dev, -EINVAL, "dma_alloc_coherent failed\n"); + + offset =3D 0; + for (id =3D VCP_RTOS_MEM_ID + 1; id < NUMS_MEM_ID; id++) { + vcp->vcp_cluster->vcp_memory_tb[id].phys =3D share_memory_phys + offset; + vcp->vcp_cluster->vcp_memory_tb[id].iova =3D share_memory_iova + offset; + vcp->vcp_cluster->vcp_memory_tb[id].virt =3D share_memory_virt + offset; + offset +=3D (u32)vcp->vcp_cluster->vcp_memory_tb[id].size; + } + + vcp->vcp_cluster->share_mem_iova =3D share_memory_iova; + vcp->vcp_cluster->share_mem_size =3D share_memory_size; + + return 0; +} + +int wait_core_hart_shutdown(struct mtk_vcp_device *vcp, + enum vcp_core_id core_id) +{ + u32 hart0; + u32 hart1; + bool twohart_support; + int ret; + + twohart_support =3D vcp->vcp_cluster->twohart[core_id]; + + switch (core_id) { + case VCP_ID: + ret =3D readl_poll_timeout(vcp->vcp_cluster->cfg + VCP_C0_GPR5_H0_REBOOT, + hart0, (hart0 & CORE_RDY_TO_REBOOT), + USEC_PER_MSEC, + CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC); + if (ret) { + dev_err(vcp->dev, "VCP_ID hart0 shutdown timeout\n"); + return ret; + } + + if (twohart_support) { + ret =3D readl_poll_timeout(vcp->vcp_cluster->cfg + VCP_C0_GPR6_H1_REBOO= T, + hart1, (hart1 & CORE_RDY_TO_REBOOT), + USEC_PER_MSEC, + CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC); + if (ret) { + dev_err(vcp->dev, "VCP_ID hart1 shutdown timeout\n"); + return ret; + } + } + break; + case MMUP_ID: + ret =3D readl_poll_timeout(vcp->vcp_cluster->cfg + VCP_C1_GPR5_H0_REBOOT, + hart0, (hart0 & CORE_RDY_TO_REBOOT), + USEC_PER_MSEC, + CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC); + if (ret) { + dev_err(vcp->dev, "MMUP_ID hart0 shutdown timeout\n"); + return ret; + } + + if (twohart_support) { + ret =3D readl_poll_timeout(vcp->vcp_cluster->cfg + VCP_C1_GPR6_H1_REBOO= T, + hart1, (hart1 & CORE_RDY_TO_REBOOT), + USEC_PER_MSEC, + CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC); + if (ret) { + dev_err(vcp->dev, "MMUP_ID hart1 shutdown timeout\n"); + return ret; + } + } + break; + default: + ret =3D -EINVAL; + dev_err(vcp->dev, "%s(), No Support core id\n", __func__); + break; + } + + return ret; +} + +int reset_vcp(struct mtk_vcp_device *vcp) +{ + struct arm_smccc_res res; + + if (vcp->vcp_cluster->core_nums > MMUP_ID) { + writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova), + vcp->vcp_cluster->cfg + VCP_C1_GPR1_DRAM_RESV_ADDR); + writel((u32)vcp->vcp_cluster->share_mem_size, + vcp->vcp_cluster->cfg + VCP_C1_GPR2_DRAM_RESV_SIZE); + + arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, + MTK_TINYSYS_MMUP_KERNEL_OP_RESET_RELEASE, + 1, 0, 0, 0, 0, 0, &res); + } + + writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova), + vcp->vcp_cluster->cfg + VCP_C0_GPR1_DRAM_RESV_ADDR); + writel((u32)vcp->vcp_cluster->share_mem_size, + vcp->vcp_cluster->cfg + VCP_C0_GPR2_DRAM_RESV_SIZE); + + arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, + MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE, + 1, 0, 0, 0, 0, 0, &res); + + return 0; +} + +static size_t load_part_binary(void __iomem *image_buf, + const u8 *fw_src, + size_t size, + const char *part_bin_name) +{ + const u8 *fw_ptr =3D fw_src; + u32 offset; + u32 align_size; + const struct mtk_vcp_img_hdr *img_hdr_info; + + if (!fw_src || !image_buf || size < VCP_IMAGE_HEADER_SIZE) + return 0; + + offset =3D 0; + while (offset < size) { + img_hdr_info =3D (const struct mtk_vcp_img_hdr *)(fw_ptr + offset); + align_size =3D round_up(img_hdr_info->dsz, ALIGN_16); + offset +=3D VCP_IMAGE_HEADER_SIZE; + if (img_hdr_info->magic !=3D VCM_IMAGE_MAGIC || + strncmp(img_hdr_info->name, part_bin_name, VCM_IMAGE_NAME_MAXSZ - 1)= ) { + offset +=3D align_size; + } else { + memcpy_toio(image_buf, fw_ptr + offset, img_hdr_info->dsz); + offset +=3D align_size; + return img_hdr_info->dsz; + } + } + + return 0; +} + +static int load_vcp_bin(const u8 *fw_src, + size_t size, + void __iomem *img_buf_va, + phys_addr_t img_buf_pa, + dma_addr_t img_buf_iova, + struct mtk_vcp_device *vcp) +{ + u32 fw_size; + u32 dram_img_size; + u32 dram_backup_img_offset; + struct vcp_region_info_st vcp_region_info =3D {}; + struct arm_smccc_res res; + + fw_size =3D load_part_binary(vcp->vcp_cluster->sram_base + + vcp->vcp_cluster->sram_offset[VCP_ID], + fw_src, size, VCP_HFRP_PART_NAME); + if (!fw_size) { + dev_err(vcp->dev, "load %s failed\n", VCP_HFRP_PART_NAME); + return -EINVAL; + } + + dram_img_size =3D load_part_binary(img_buf_va + VCP_DRAM_IMG_OFFSET, + fw_src, size, VCP_HFRP_DRAM_PART_NAME); + if (!dram_img_size) { + dev_err(vcp->dev, "load %s failed\n", VCP_HFRP_DRAM_PART_NAME); + return -EINVAL; + } + + vcp_region_info.struct_size =3D sizeof(struct vcp_region_info_st); + + dram_backup_img_offset =3D VCP_DRAM_IMG_OFFSET + round_up(dram_img_size, = ALIGN_1024); + + vcp_region_info.ap_dram_start =3D VCP_PACK_IOVA(img_buf_iova + VCP_DRAM_I= MG_OFFSET); + vcp_region_info.ap_dram_backup_start =3D VCP_PACK_IOVA(img_buf_iova + dra= m_backup_img_offset); + vcp_region_info.ap_dram_size =3D dram_img_size; + + vcp_region_info.l2tcm_offset =3D vcp->vcp_cluster->sram_offset[MMUP_ID]; + + memcpy_toio(vcp->vcp_cluster->sram_base + + vcp->vcp_cluster->sram_offset[VCP_ID] + REGION_OFFSET, + &vcp_region_info, sizeof(vcp_region_info)); + + arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, + MTK_TINYSYS_MMUP_KERNEL_OP_SET_L2TCM_OFFSET, + vcp->vcp_cluster->sram_offset[MMUP_ID], + 0, 0, 0, 0, 0, &res); + + return 0; +} + +static int load_mmup_bin(const u8 *fw_src, + size_t size, + void __iomem *img_buf_va, + phys_addr_t img_buf_pa, + dma_addr_t img_buf_iova, + struct mtk_vcp_device *vcp) +{ + u32 fw_size; + u32 dram_img_size; + u32 dram_backup_img_offset; + struct vcp_region_info_st vcp_region_info =3D {}; + struct arm_smccc_res res; + + fw_size =3D load_part_binary(vcp->vcp_cluster->sram_base + + vcp->vcp_cluster->sram_offset[MMUP_ID], + fw_src, size, VCP_MMUP_PART_NAME); + if (!fw_size) { + dev_err(vcp->dev, "load %s failed\n", VCP_MMUP_PART_NAME); + return -EINVAL; + } + + dram_img_size =3D load_part_binary(img_buf_va + MMUP_DRAM_IMG_OFFSET, fw_= src, size, + VCP_MMUP_DRAM_PART_NAME); + if (!dram_img_size) { + dev_err(vcp->dev, "load %s failed\n", VCP_MMUP_DRAM_PART_NAME); + return -EINVAL; + } + + vcp_region_info.struct_size =3D sizeof(struct vcp_region_info_st); + + dram_backup_img_offset =3D MMUP_DRAM_IMG_OFFSET + round_up(dram_img_size,= ALIGN_1024); + vcp_region_info.ap_dram_start =3D VCP_PACK_IOVA(img_buf_iova + MMUP_DRAM_= IMG_OFFSET); + vcp_region_info.ap_dram_backup_start =3D VCP_PACK_IOVA(img_buf_iova + dra= m_backup_img_offset); + vcp_region_info.ap_dram_size =3D dram_img_size; + + memcpy_toio(vcp->vcp_cluster->sram_base + + vcp->vcp_cluster->sram_offset[MMUP_ID] + REGION_OFFSET, + &vcp_region_info, sizeof(vcp_region_info)); + + arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, + MTK_TINYSYS_MMUP_KERNEL_OP_SET_FW_SIZE, + fw_size, 0, 0, 0, 0, 0, &res); + + return 0; +} + +int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw) +{ + struct arm_smccc_res res; + struct mtk_vcp_device *vcp =3D (struct mtk_vcp_device *)rproc->priv; + dma_addr_t img_buf_iova; + phys_addr_t img_buf_phys; + void __iomem *img_buf_va; + int ret; + + if (!vcp) { + dev_err(vcp->dev, "vcp device is no exist!\n"); + return -EINVAL; + } + + if (fw->size < VCP_IMAGE_HEADER_SIZE || + fw->size > vcp->ops->vcp_get_mem_size(vcp, VCP_RTOS_MEM_ID)) { + dev_err(vcp->dev, "firmware is oversize/undersize\n"); + return -EINVAL; + } + + writel(0x1, vcp->vcp_cluster->cfg_core + VCP_R_CORE0_SW_RSTN_SET); + writel(0x1, vcp->vcp_cluster->cfg_core + VCP_R_CORE1_SW_RSTN_SET); + + memset_io(vcp->vcp_cluster->sram_base, 0, vcp->vcp_cluster->sram_size); + + img_buf_iova =3D vcp->ops->vcp_get_mem_iova(vcp, VCP_RTOS_MEM_ID); + img_buf_phys =3D vcp->ops->vcp_get_mem_phys(vcp, VCP_RTOS_MEM_ID); + img_buf_va =3D vcp->ops->vcp_get_mem_virt(vcp, VCP_RTOS_MEM_ID); + + arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, + MTK_TINYSYS_VCP_KERNEL_OP_COLD_BOOT_VCP, + 0, 0, 0, 0, 0, 0, &res); + + ret =3D load_vcp_bin(fw->data, fw->size, + img_buf_va, img_buf_phys, + img_buf_iova, vcp); + if (ret) { + dev_err(vcp->dev, "load vcp bin failed\n"); + return -EINVAL; + } + + ret =3D load_mmup_bin(fw->data, fw->size, + img_buf_va, img_buf_phys, + img_buf_iova, vcp); + if (ret) { + dev_err(vcp->dev, "load mmup bin failed\n"); + return -EINVAL; + } + + return ret; +} + +static irqreturn_t vcp_irq_handler(int irq, void *priv) +{ + u32 reg0, reg1; + struct mtk_vcp_device *vcp =3D priv; + + disable_irq_nosync(irq); + + reg0 =3D readl(vcp->vcp_cluster->cfg_core + R_CORE0_WDT_IRQ); + reg1 =3D vcp->vcp_cluster->core_nums > VCP_ID ? + readl(vcp->vcp_cluster->cfg_core + R_CORE1_WDT_IRQ) : 0; + + if (reg0) + writel(B_WDT_IRQ, vcp->vcp_cluster->cfg_core + R_CORE0_WDT_IRQ); + if (reg1) + writel(B_WDT_IRQ, vcp->vcp_cluster->cfg_core + R_CORE1_WDT_IRQ); + + if (reg0 || reg1) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +int vcp_wdt_irq_init(struct mtk_vcp_device *vcp) +{ + int ret; + + ret =3D devm_request_irq(vcp->dev, platform_get_irq(vcp->pdev, 0), + vcp_irq_handler, IRQF_ONESHOT, + vcp->pdev->name, vcp); + if (ret) + dev_err_probe(vcp->dev, ret, "failed to request wdt irq\n"); + + return ret; +} + +MODULE_AUTHOR("Xiangzhi Tang "); +MODULE_DESCRIPTION("MTK VCP Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_v= cp_common.h new file mode 100644 index 000000000000..d048757c955a --- /dev/null +++ b/drivers/remoteproc/mtk_vcp_common.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2025 MediaTek Inc. + */ + +#ifndef __MTK_VCP_COMMON_H +#define __MTK_VCP_COMMON_H + +#include +#include +#include +#include +#include + +/* VCP timeout definition */ +#define CORE_HART_SHUTDOWN_TIMEOUT_MS 10 + +/* VCP platform definition */ +#define DMA_MAX_MASK_BIT 33 + +/* VCP load image definition */ +#define VCM_IMAGE_MAGIC (0x58881688) +#define VCM_IMAGE_NAME_MAXSZ (32) +#define VCP_IMAGE_HEADER_SIZE (0x200) + +#define VCP_DRAM_IMG_OFFSET (0x200000) +#define MMUP_DRAM_IMG_OFFSET (0x1200000) + +#define REGION_OFFSET (0x4) +#define ALIGN_1024 (1024) +#define ALIGN_16 (16) +#define VCP_HFRP_PART_NAME "tinysys-vcp-RV55_A" +#define VCP_MMUP_PART_NAME "tinysys-mmup-RV33_A" +#define VCP_HFRP_DRAM_PART_NAME "tinysys-vcp-RV55_A_dram" +#define VCP_MMUP_DRAM_PART_NAME "tinysys-mmup-RV33_A_dram" + +/* VCP memory iove pack convert definition */ +#define VCP_PACK_IOVA(addr) ((uint32_t)((addr) | (((uint64_t)(addr) >>= 32) & 0xF))) +#define VCP_UNPACK_IOVA(addr) \ + ((uint64_t)(addr & 0xFFFFFFF0) | (((uint64_t)(addr) & 0xF) << 32)) + +/* VCP cfg_core register offset definition */ +#define VCP_R_CORE0_SW_RSTN_SET (0x0004) +#define VCP_R_CORE1_SW_RSTN_SET (0x000C) +#define R_GIPC_IN_SET (0x0028) +#define R_GIPC_IN_CLR (0x002C) +#define GIPC_MMUP_SHUT BIT(10) +#define GIPC_VCP_HART0_SHUT BIT(14) +#define B_GIPC4_SETCLR_3 BIT(19) +#define R_CORE0_WDT_IRQ (0x0050) +#define R_CORE1_WDT_IRQ (0x0054) +#define B_WDT_IRQ BIT(0) +#define AP_R_GPR2 (0x0068) +#define B_CORE0_SUSPEND BIT(0) +#define B_CORE0_RESUME BIT(1) +#define AP_R_GPR3 (0x006C) +#define B_CORE1_SUSPEND BIT(0) +#define B_CORE1_RESUME BIT(1) + +/* VCP cfg register offset definition */ +#define R_CORE0_STATUS (0x6070) +#define B_CORE_GATED BIT(0) +#define B_HART0_HALT BIT(1) +#define B_HART1_HALT BIT(2) +#define B_CORE_AXIS_BUSY BIT(4) +#define R_CORE1_STATUS (0x9070) +#define VCP_C0_GPR0_SUSPEND_RESUME (0x6040) +#define VCP_C0_GPR1_DRAM_RESV_ADDR (0x6044) +#define VCP_C0_GPR2_DRAM_RESV_SIZE (0x6048) +#define VCP_C0_GPR3_DRAM_RESV_LOGGER (0x604C) +#define VCP_C0_GPR5_H0_REBOOT (0x6054) +#define CORE_RDY_TO_REBOOT (0x0034) +#define VCP_C0_GPR6_H1_REBOOT (0x6058) +#define VCP_C1_GPR0_SUSPEND_RESUME (0x9040) +#define VCP_C1_GPR1_DRAM_RESV_ADDR (0x9044) +#define VCP_C1_GPR2_DRAM_RESV_SIZE (0x9048) +#define VCP_C1_GPR3_DRAM_RESV_LOGGER (0x904C) +#define VCP_C1_GPR5_H0_REBOOT (0x9054) +#define VCP_C1_GPR6_H1_REBOOT (0x9058) + +/* VCP cfg_sec register offset definition */ +#define R_GPR2_SEC (0x0008) +#define MMUP_AP_SUSPEND BIT(0) +#define R_GPR3_SEC (0x000C) +#define VCP_AP_SUSPEND BIT(0) + +enum vcp_core_id { + VCP_ID =3D 0, + MMUP_ID, + VCP_CORE_TOTAL, +}; + +enum mtk_tinysys_vcp_kernel_op { + MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET =3D 0, + MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE, + MTK_TINYSYS_VCP_KERNEL_OP_COLD_BOOT_VCP, + MTK_TINYSYS_MMUP_KERNEL_OP_RESET_SET, + MTK_TINYSYS_MMUP_KERNEL_OP_RESET_RELEASE, + MTK_TINYSYS_MMUP_KERNEL_OP_SET_L2TCM_OFFSET, + MTK_TINYSYS_MMUP_KERNEL_OP_SET_FW_SIZE, + MTK_TINYSYS_MMUP_KERNEL_OP_COLD_BOOT_MMUP, + MTK_TINYSYS_VCP_KERNEL_OP_NUM, +}; + +/** + * struct mtk_vcp_img_hdr - mtk image header format. + * + * @magic: mtk vcp image magic id + * @dsz: mtk vcp image part binary size + * @name: mtk vcp image part binary parttion name + */ +struct mtk_vcp_img_hdr { + u32 magic; + u32 dsz; + char name[VCM_IMAGE_NAME_MAXSZ]; +}; + +/** + * struct mtk_vcp_feature_table - feature table structure definition. + * + * @feature_id: feature id + * @core_id: feature using vcp core id + */ +struct mtk_vcp_feature_table { + enum vcp_feature_id feature_id; + enum vcp_core_id core_id; +}; + +/** + * struct mtk_vcp_reserved_mem_table - memory table structure definition. + * + * @memory_id: memory_id id + * @size: predistribution memory size + */ +struct mtk_vcp_reserved_mem_table { + enum vcp_reserve_mem_id memory_id; + size_t size; +}; + +/** + * struct vcp_reserve_mblock - vcp reserved memory structure. + * + * @vcp_reserve_mem_id: reserved memory id + * @phys: reserved memory phy addr + * @iova: reserved memory dma map addr + * @virt: reserved memory CPU virt addr + * @size: reserved memory size + */ +struct vcp_reserve_mblock { + enum vcp_reserve_mem_id num; + phys_addr_t phys; + dma_addr_t iova; + void __iomem *virt; + size_t size; +}; + +/** + * struct vcp_region_info_st - config vcp image info sync to vcp bootloade= r. + * + * @ap_loader_start: config vcp bootloader to copy loader start addr + * @ap_loader_size: config vcp bootloader to copy loader size + * @ap_firmware_start: config vcp bootloader to copy firmware start addr + * @ap_firmware_size: config vcp bootloader to copy firmware size + * @ap_dram_start: config vcp run dram binary start addr + * @ap_dram_size: config vcp run dram binary size + * @ap_dram_backup_start: config vcp backup dram binary start addr + * @struct_size: vcp image region info structure size + * @l2tcm_offset: vcp two core using l2sram layout + * @TaskContext_ptr: vcp task context ptr for debug + * @vcpctl: - vcp control info + * @regdump_start: regdump start addr for debug + * @regdump_size: regdump size for debug + * @ap_params_start: params start addr + * @sramlog_buf_offset: sramlog_buf_offset for debug + * @sramlog_end_idx_offset: sramlog_end_idx_offset for debug + * @sramlog_buf_maxlen: sramlog_buf_maxlen for debug + * @ap_loader_start_pa: config vcp bootloader for loader start pa + * @coredump_offset: coredump_offset offset for debug + * @coredump_dram_offset: coredump_dram_offset offset for debug + */ +struct vcp_region_info_st { + u32 ap_loader_start; + u32 ap_loader_size; + u32 ap_firmware_start; + u32 ap_firmware_size; + u32 ap_dram_start; + u32 ap_dram_size; + u32 ap_dram_backup_start; + u32 struct_size; + u32 l2tcm_offset; + u32 TaskContext_ptr; + u32 vcpctl; + u32 regdump_start; + u32 regdump_size; + u32 ap_params_start; + u32 sramlog_buf_offset; + u32 sramlog_end_idx_offset; + u32 sramlog_buf_maxlen; + u32 ap_loader_start_pa; + u32 coredump_offset; + u32 coredump_dram_offset; +}; + +int vcp_reserve_memory_init(struct mtk_vcp_device *vcp); +phys_addr_t vcp_get_reserve_mem_phys(struct mtk_vcp_device *vcp, enum vcp_= reserve_mem_id id); +dma_addr_t vcp_get_reserve_mem_iova(struct mtk_vcp_device *vcp, enum vcp_r= eserve_mem_id id); +size_t vcp_get_reserve_mem_size(struct mtk_vcp_device *vcp, enum vcp_reser= ve_mem_id id); +void __iomem *vcp_get_reserve_mem_virt(struct mtk_vcp_device *vcp, enum vc= p_reserve_mem_id id); +void __iomem *vcp_get_internal_sram_virt(struct mtk_vcp_device *vcp); + +int reset_vcp(struct mtk_vcp_device *vcp); +int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw); + +int vcp_wdt_irq_init(struct mtk_vcp_device *vcp); + +int wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id c= ore_id); +#endif diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vc= p_rproc.c new file mode 100644 index 000000000000..bcb5f3786cf7 --- /dev/null +++ b/drivers/remoteproc/mtk_vcp_rproc.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 MediaTek Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_vcp_common.h" +#include "mtk_vcp_rproc.h" +#include "remoteproc_internal.h" + +/** + * vcp_get() - get a reference to VCP. + * + * @pdev: the platform device of the module requesting VCP platform + * device for using VCP API. + * + * Return: Return NULL if failed. otherwise reference to VCP. + **/ +struct mtk_vcp_device *vcp_get(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct device_node *vcp_node; + struct platform_device *vcp_pdev; + + vcp_node =3D of_parse_phandle(dev->of_node, "mediatek,vcp", 0); + if (!vcp_node) { + dev_err(dev, "can't get VCP node\n"); + return NULL; + } + + vcp_pdev =3D of_find_device_by_node(vcp_node); + of_node_put(vcp_node); + + if (WARN_ON(!vcp_pdev)) { + dev_err(dev, "VCP pdev failed\n"); + return NULL; + } + + return platform_get_drvdata(vcp_pdev); +} +EXPORT_SYMBOL_GPL(vcp_get); + +void vcp_put(struct mtk_vcp_device *vcp) +{ + put_device(vcp->dev); +} +EXPORT_SYMBOL_GPL(vcp_put); + +static int mtk_vcp_start(struct rproc *rproc) +{ + struct mtk_vcp_device *vcp =3D (struct mtk_vcp_device *)rproc->priv; + struct arm_smccc_res res; + + /* core 0 */ + arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, + MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET, + 1, 0, 0, 0, 0, 0, &res); + + /* core 1 */ + arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, + MTK_TINYSYS_MMUP_KERNEL_OP_RESET_SET, + 1, 0, 0, 0, 0, 0, &res); + + ret =3D reset_vcp(vcp); + if (ret) + dev_err(vcp->dev, "bootup fail\n"); + else + dev_info(vcp->dev, "bootup successfully\n"); + + return ret; +} + +static int mtk_vcp_stop(struct rproc *rproc) +{ + struct mtk_vcp_device *vcp =3D (struct mtk_vcp_device *)rproc->priv; + + vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID); + + return 0; +} + +static const struct rproc_ops mtk_vcp_ops =3D { + .load =3D mtk_vcp_load, + .start =3D mtk_vcp_start, + .stop =3D mtk_vcp_stop, +}; + +static int vcp_multi_core_init(struct platform_device *pdev, + struct mtk_vcp_of_cluster *vcp_cluster, + enum vcp_core_id core_id) +{ + int ret; + + ret =3D of_property_read_u32(pdev->dev.of_node, "mtk,vcp-core-twohart", + &vcp_cluster->twohart[core_id]); + if (ret) + return dev_err_probe(&pdev->dev, ret, "get twohart property failed\n"); + + ret =3D of_property_read_u32(pdev->dev.of_node, "mtk,core-sram-offset", + &vcp_cluster->sram_offset[core_id]); + if (ret) + return dev_err_probe(&pdev->dev, ret, "get sram-offset property failed\n= "); + + return ret; +} + +static struct mtk_vcp_device *vcp_rproc_init(struct platform_device *pdev, + struct mtk_vcp_of_cluster *vcp_cluster) +{ + struct device *dev =3D &pdev->dev; + struct device_node *np =3D dev_of_node(dev); + struct device_node *child; + struct platform_device *cpdev; + struct mtk_vcp_device *vcp; + struct rproc *rproc; + const struct mtk_vcp_of_data *vcp_of_data; + u32 core_id; + int ret; + + vcp_of_data =3D of_device_get_match_data(dev); + rproc =3D devm_rproc_alloc(dev, np->name, &mtk_vcp_ops, + vcp_of_data->platdata.fw_name, + sizeof(struct mtk_vcp_device)); + if (!rproc) + return ERR_PTR(dev_err_probe(dev, -ENOMEM, "allocate remoteproc failed\n= ")); + + vcp =3D rproc->priv; + vcp->rproc =3D rproc; + vcp->pdev =3D pdev; + vcp->dev =3D dev; + vcp->ops =3D &vcp_of_data->ops; + vcp->platdata =3D &vcp_of_data->platdata; + vcp->vcp_cluster =3D vcp_cluster; + + rproc->auto_boot =3D vcp_of_data->platdata.auto_boot; + rproc->sysfs_read_only =3D vcp_of_data->platdata.sysfs_read_only; + platform_set_drvdata(pdev, vcp); + + ret =3D vcp_reserve_memory_init(vcp); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "reserve memory failed\n")); + + core_id =3D 0; + for_each_available_child_of_node(np, child) { + if (of_device_is_compatible(child, "mediatek,vcp-core")) { + cpdev =3D of_find_device_by_node(child); + if (!cpdev) { + of_node_put(child); + return ERR_PTR(dev_err_probe(dev, -ENODEV, "Not child node\n")); + } + ret =3D vcp_multi_core_init(cpdev, vcp_cluster, core_id); + core_id++; + } + } + vcp->vcp_cluster->core_nums =3D core_id; + + ret =3D vcp_wdt_irq_init(vcp); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "vcp_wdt_irq_init failed\n")); + + pm_runtime_get_sync(dev); + + return vcp; +} + +static int vcp_cluster_init(struct platform_device *pdev, + struct mtk_vcp_of_cluster *vcp_cluster) +{ + struct mtk_vcp_device *vcp; + int ret; + + vcp =3D vcp_rproc_init(pdev, vcp_cluster); + if (IS_ERR(vcp)) + return dev_err_probe(vcp->dev, PTR_ERR(vcp), "vcp_rproc_init failed\n"); + + ret =3D rproc_add(vcp->rproc); + if (ret) { + rproc_del(vcp->rproc); + return dev_err_probe(vcp->dev, ret, "Failed to add rproc\n"); + } + + return ret; +} + +static int vcp_device_probe(struct platform_device *pdev) +{ + struct resource *res; + struct device *dev =3D &pdev->dev; + struct mtk_vcp_of_cluster *vcp_cluster; + int ret; + + pm_runtime_enable(dev); + + vcp_cluster =3D devm_kzalloc(dev, sizeof(*vcp_cluster), GFP_KERNEL); + if (!vcp_cluster) + return dev_err_probe(dev, -ENOMEM, "allocate resource failed\n"); + + vcp_cluster->cfg =3D devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(vcp_cluster->cfg)) + return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg), + "Failed to parse and map cfg memory\n"); + + vcp_cluster->cfg_sec =3D devm_platform_ioremap_resource_byname(pdev, "cfg= _sec"); + if (IS_ERR(vcp_cluster->cfg_sec)) + return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg_sec), + "Failed to parse and map cfg_sec memory\n"); + + vcp_cluster->cfg_core =3D devm_platform_ioremap_resource_byname(pdev, "cf= g_core"); + if (IS_ERR(vcp_cluster->cfg_core)) + return dev_err_probe(dev, PTR_ERR(vcp_cluster->cfg_core), + "Failed to parse and map cfg_core memory\n"); + + res =3D platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + vcp_cluster->sram_base =3D devm_ioremap_resource(dev, res); + if (IS_ERR(vcp_cluster->sram_base)) + return dev_err_probe(dev, PTR_ERR(vcp_cluster->sram_base), + "Failed to parse and map sram memory\n"); + vcp_cluster->sram_size =3D (u32)resource_size(res); + + ret =3D devm_of_platform_populate(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to populate platform devices\n"); + + ret =3D vcp_cluster_init(pdev, vcp_cluster); + if (ret) + return ret; + + return 0; +} + +static void vcp_device_remove(struct platform_device *pdev) +{ + struct mtk_vcp_device *vcp =3D platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + rproc_del(vcp->rproc); +} + +static void vcp_device_shutdown(struct platform_device *pdev) +{ + struct mtk_vcp_device *vcp =3D platform_get_drvdata(pdev); + int ret; + + writel(GIPC_VCP_HART0_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + ret =3D wait_core_hart_shutdown(vcp, VCP_ID); + if (ret) + dev_err(&pdev->dev, "wait VCP_ID core hart shutdown timeout\n"); + + if (vcp->vcp_cluster->core_nums > MMUP_ID) { + writel(GIPC_MMUP_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + ret =3D wait_core_hart_shutdown(vcp, MMUP_ID); + if (ret) + dev_err(&pdev->dev, "wait MMUP_ID core hart shutdown timeout\n"); + } +} + +static struct mtk_vcp_feature_table mt8196_feature_tb[NUM_FEATURE_ID] =3D { + { .feature_id =3D RTOS_FEATURE_ID, .core_id =3D VCP_CORE_TOTAL }, + { .feature_id =3D VDEC_FEATURE_ID, .core_id =3D VCP_ID }, + { .feature_id =3D VENC_FEATURE_ID, .core_id =3D VCP_ID }, + { .feature_id =3D MMDVFS_MMUP_FEATURE_ID, .core_id =3D MMUP_ID }, + { .feature_id =3D MMDVFS_VCP_FEATURE_ID, .core_id =3D VCP_ID }, + { .feature_id =3D MMDEBUG_FEATURE_ID, .core_id =3D MMUP_ID }, + { .feature_id =3D VMM_FEATURE_ID, .core_id =3D MMUP_ID }, + { .feature_id =3D VDISP_FEATURE_ID, .core_id =3D MMUP_ID }, + { .feature_id =3D MMQOS_FEATURE_ID, .core_id =3D VCP_ID }, +}; + +static struct mtk_vcp_reserved_mem_table mt8196_memory_tb[NUMS_MEM_ID] =3D= { + { .memory_id =3D VCP_RTOS_MEM_ID, .size =3D 0x1a00000 }, + { .memory_id =3D VDEC_MEM_ID, .size =3D 0x30000 }, + { .memory_id =3D VENC_MEM_ID, .size =3D 0x12000 }, + { .memory_id =3D MMDVFS_VCP_MEM_ID, .size =3D 0x1000 }, + { .memory_id =3D MMDVFS_MMUP_MEM_ID, .size =3D 0x1000 }, + { .memory_id =3D MMQOS_MEM_ID, .size =3D 0x1000 }, +}; + +static const struct mtk_vcp_of_data mt8196_of_data =3D { + .ops =3D { + .vcp_get_mem_phys =3D vcp_get_reserve_mem_phys, + .vcp_get_mem_iova =3D vcp_get_reserve_mem_iova, + .vcp_get_mem_virt =3D vcp_get_reserve_mem_virt, + .vcp_get_mem_size =3D vcp_get_reserve_mem_size, + .vcp_get_sram_virt =3D vcp_get_internal_sram_virt, + }, + .platdata =3D { + .auto_boot =3D true, + .sysfs_read_only =3D true, + .rtos_static_iova =3D 0x180600000, + .feature_tb =3D mt8196_feature_tb, + .memory_tb =3D mt8196_memory_tb, + .fw_name =3D "mediatek/mt8196/vcp.img", + }, +}; + +static const struct of_device_id mtk_vcp_of_match[] =3D { + { .compatible =3D "mediatek,mt8196-vcp", .data =3D &mt8196_of_data}, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_vcp_of_match); + +static struct platform_driver mtk_vcp_device =3D { + .probe =3D vcp_device_probe, + .remove_new =3D vcp_device_remove, + .shutdown =3D vcp_device_shutdown, + .driver =3D { + .name =3D "mtk-vcp", + .of_match_table =3D mtk_vcp_of_match, + }, +}; + +module_platform_driver(mtk_vcp_device); + +MODULE_AUTHOR("Xiangzhi Tang "); +MODULE_DESCRIPTION("MTK VCP Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vc= p_rproc.h new file mode 100644 index 000000000000..11ce0f6d562b --- /dev/null +++ b/drivers/remoteproc/mtk_vcp_rproc.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2025 MediaTek Inc. + */ + +#ifndef __MTK_VCP_RPROC_H__ +#define __MTK_VCP_RPROC_H__ + +#include + +/* + * struct mtk_vcp_of_cluster - vcp cluster priv data. + * + * @sram_base: sram_base get from dtb + * @cfg: cfg register get from dtb + * @cfg_sec: cfg_sec register get from dtb + * @cfg_core: cfg_core register get from dtb + * @sram_size: total sram size get from dtb + * @core_nums: total core numbers get from dtb + * @twohart: core weo hart support flag + * @sram_offset: core sram memory layout + * @share_mem_iova: shared memory iova base + * @share_mem_size: shared memory size + * @vcp_memory_tb: vcp memory allocated table + */ +struct mtk_vcp_of_cluster { + void __iomem *sram_base; + void __iomem *cfg; + void __iomem *cfg_sec; + void __iomem *cfg_core; + u32 sram_size; + u32 core_nums; + u32 twohart[VCP_CORE_TOTAL]; + u32 sram_offset[VCP_CORE_TOTAL]; + dma_addr_t share_mem_iova; + size_t share_mem_size; + struct vcp_reserve_mblock vcp_memory_tb[NUMS_MEM_ID]; +}; + +/** + * struct mtk_vcp_platdata - vcp platform priv data. + * + * @auto_boot: rproc auto_boot flag + * @sysfs_read_only: rproc sysfs_read_only flag + * @rtos_static_iova: vcp dram binary static map iova + * @feature_tb: vcp feature table structure + * @memory_tb: vcp memory table structure + * @fw_name: vcp image name and path + */ +struct mtk_vcp_platdata { + bool auto_boot; + bool sysfs_read_only; + dma_addr_t rtos_static_iova; + struct mtk_vcp_feature_table *feature_tb; + struct mtk_vcp_reserved_mem_table *memory_tb; + char *fw_name; +}; + +/** + * struct mtk_vcp_of_data - const vcp device data. + * + * @mtk_vcp_ops: mtk_vcp_ops structure + * @mtk_vcp_platdata: mtk_vcp_platdata structure + */ +struct mtk_vcp_of_data { + const struct mtk_vcp_ops ops; + const struct mtk_vcp_platdata platdata; +}; +#endif diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remo= teproc/mtk_vcp_public.h new file mode 100644 index 000000000000..3bbc2055f9f8 --- /dev/null +++ b/include/linux/remoteproc/mtk_vcp_public.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2025 MediaTek Inc. + */ + +#ifndef __MTK_VCP_PUBLIC_H__ +#define __MTK_VCP_PUBLIC_H__ + +#include +#include + +enum vcp_reserve_mem_id { + VCP_RTOS_MEM_ID, + VDEC_MEM_ID, + VENC_MEM_ID, + MMDVFS_VCP_MEM_ID, + MMDVFS_MMUP_MEM_ID, + MMQOS_MEM_ID, + NUMS_MEM_ID, +}; + +enum vcp_feature_id { + RTOS_FEATURE_ID, + VDEC_FEATURE_ID, + VENC_FEATURE_ID, + GCE_FEATURE_ID, + MMDVFS_MMUP_FEATURE_ID, + MMDVFS_VCP_FEATURE_ID, + MMQOS_FEATURE_ID, + MMDEBUG_FEATURE_ID, + HWCCF_FEATURE_ID, + HWCCF_DEBUG_FEATURE_ID, + IMGSYS_FEATURE_ID, + VDISP_FEATURE_ID, + VMM_FEATURE_ID, + NUM_FEATURE_ID, +}; + +struct mtk_vcp_device { + struct platform_device *pdev; + struct device *dev; + struct rproc *rproc; + struct mtk_vcp_of_cluster *vcp_cluster; + const struct mtk_vcp_ops *ops; + const struct mtk_vcp_platdata *platdata; +}; + +struct mtk_vcp_ops { + phys_addr_t (*vcp_get_mem_phys)(struct mtk_vcp_device *vcp, + enum vcp_reserve_mem_id id); + dma_addr_t (*vcp_get_mem_iova)(struct mtk_vcp_device *vcp, + enum vcp_reserve_mem_id id); + void __iomem *(*vcp_get_mem_virt)(struct mtk_vcp_device *vcp, + enum vcp_reserve_mem_id id); + size_t (*vcp_get_mem_size)(struct mtk_vcp_device *vcp, + enum vcp_reserve_mem_id id); + void __iomem *(*vcp_get_sram_virt)(struct mtk_vcp_device *vcp); +}; + +struct mtk_vcp_device *vcp_get(struct platform_device *pdev); +void vcp_put(struct mtk_vcp_device *vcp); + +/* + * These inline functions are intended for user drivers that are loaded + * earlier than the VCP driver, or for built-in drivers that cannot access + * the symbols of VCP module. + */ +static inline struct mtk_vcp_device *mtk_vcp_get_by_phandle(phandle phandl= e) +{ + struct rproc *rproc =3D NULL; + + rproc =3D rproc_get_by_phandle(phandle); + if (IS_ERR_OR_NULL(rproc)) + return NULL; + + return rproc->priv; +} +#endif diff --git a/include/linux/soc/mediatek/mtk_sip_svc.h b/include/linux/soc/m= ediatek/mtk_sip_svc.h index abe24a73ee19..e59fadc8cbee 100644 --- a/include/linux/soc/mediatek/mtk_sip_svc.h +++ b/include/linux/soc/mediatek/mtk_sip_svc.h @@ -28,4 +28,6 @@ /* IOMMU related SMC call */ #define MTK_SIP_KERNEL_IOMMU_CONTROL MTK_SIP_SMC_CMD(0x514) =20 +#define MTK_SIP_TINYSYS_VCP_CONTROL MTK_SIP_SMC_CMD(0x52C) + #endif --=20 2.46.0 From nobody Mon Apr 6 09:19:41 2026 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) (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 6E55F26056C; Fri, 20 Mar 2026 03:21:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.244.123.138 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976869; cv=none; b=s+QfI2gMw+U9pyUsWdstZcSVCFhdtsZ+e0OCCyM0XEwKi0SXNr5CwV/sg9AQ6TZp6kYvu5saMhux0YufLWDX8IwnysN3ZHPYnlfx1SmzW8Ct8C23UyZzMRed2BVip3UBJ47fYGi7hoP8abEJ3MgTAadfsljDCPQijmiylIeaaaI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976869; c=relaxed/simple; bh=PvMnOs+Up6+CK+HdoWOFFsiriiOGzPnI0aijxrTMdqs=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=D1OUYvOzQjje4VzkalRHiXIoG0989UApBFLcoccGwq//Tfn2/lmNz3mddIfZU5bYWVSYe4bhxfna8bMrApMGHsqMkOb0QBgr9Jt6yEDwUhKJIG3wgv6/6W7ZD5KFS8BKTuZL4+3ubk8U9ziNMS60ujMau7SmqcivlW9KuDWcK34= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=rzgRozvI; arc=none smtp.client-ip=60.244.123.138 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="rzgRozvI" X-UUID: cf1e6a12240b11f1a02d4725871ece0b-20260320 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=awBjXHDwgHsayY+N80DkWBPnwpc3L4jdYL92DUFuYUs=; b=rzgRozvIkxvAiq/9r8/LVpX1IOATxkWIGRHmoHgcXjfy/lPfhLNwWwCQd3XzCnTbSdMQBxElzXB9z0XOf/sYIvljkQwlX1/lRKScYgw0WYGBjBWAqEI50i8IwUlmtVmOte1j/LJcatuv+TmepkfXJ/WcFfKIOV1H/KKomq908dE=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:5619d408-6c92-45ce-8f61-aa7a23acccf1,IP:0,U RL:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION: release,TS:0 X-CID-META: VersionHash:e7bac3a,CLOUDID:62c20194-f8ef-4ca8-bea0-143568f9ca1d,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|836|888|898,TC:-5,Content: 0|15|50,EDM:-3,IP:nil,URL:0,File:130,RT:0,Bulk:nil,QS:nil,BEC:-1,COL:0,OSI :0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: cf1e6a12240b11f1a02d4725871ece0b-20260320 Received: from mtkmbs13n2.mediatek.inc [(172.21.101.108)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 549832523; Fri, 20 Mar 2026 11:20:56 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by mtkmbs13n2.mediatek.inc (172.21.101.108) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.29; Fri, 20 Mar 2026 11:20:54 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.2562.29 via Frontend Transport; Fri, 20 Mar 2026 11:20:53 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , , Hailong Fan , Huayu Zong , Xiangzhi Tang , Jjian Zhou Subject: [PATCH v3 3/6] firmware: mediatek: Add VCP IPC protocol interfaces driver Date: Fri, 20 Mar 2026 11:18:05 +0800 Message-ID: <20260320032014.13608-4-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> References: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Some of mediatek processors contain the Risc-V coprocessor, The communication between Host CPU and vcp firmware is taking place using a shared memory area for message passing. VCP IPC protocol offers (send/recv) interfaces using mediatek-mailbox APIs. Signed-off-by: Jjian Zhou Signed-off-by: Xiangzhi Tang --- drivers/firmware/Kconfig | 9 + drivers/firmware/Makefile | 1 + drivers/firmware/mtk-vcp-ipc.c | 481 ++++++++++++++++++ include/linux/firmware/mediatek/mtk-vcp-ipc.h | 151 ++++++ 4 files changed, 642 insertions(+) create mode 100644 drivers/firmware/mtk-vcp-ipc.c create mode 100644 include/linux/firmware/mediatek/mtk-vcp-ipc.h diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index bbd2155d8483..80f63b733820 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -178,6 +178,15 @@ config MTK_ADSP_IPC ADSP exists on some mtk processors. Client might use shared memory to exchange information with ADSP. =20 +config MTK_VCP_IPC + tristate "MTK VCP IPC Protocol driver" + depends on MTK_VCP_MBOX + help + Say yes here to add support for the MediaTek VCP IPC + between host AP (Linux) and the firmware running on VCP. + VCP exists on some mtk processors. + Client might use shared memory to exchange information with VCP. + config SYSFB bool select BOOT_VESA_SUPPORT diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 4ddec2820c96..d6b6197cb54c 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_ISCSI_IBFT_FIND) +=3D iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT) +=3D iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP) +=3D memmap.o obj-$(CONFIG_MTK_ADSP_IPC) +=3D mtk-adsp-ipc.o +obj-$(CONFIG_MTK_VCP_IPC) +=3D mtk-vcp-ipc.o obj-$(CONFIG_RASPBERRYPI_FIRMWARE) +=3D raspberrypi.o obj-$(CONFIG_FW_CFG_SYSFS) +=3D qemu_fw_cfg.o obj-$(CONFIG_SYSFB) +=3D sysfb.o diff --git a/drivers/firmware/mtk-vcp-ipc.c b/drivers/firmware/mtk-vcp-ipc.c new file mode 100644 index 000000000000..c25615cf1def --- /dev/null +++ b/drivers/firmware/mtk-vcp-ipc.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2024 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct mtk_ipi_chan_table - channel table that belong to mtk_ipi_device + * @mbox: the mbox channel number + * @mbox_pin_cb: callback function + * @holder: keep 1 if there are ipi waiters (to wait the reply) + * @ipi_record: timestamp of each ipi transmission stage + * @pin_buf: buffer point + * @prdata: private data + * @recv_opt: recv option, 0:receive ,1: response + * @notify: completion notify process + * @send_ofs: message offset in the slots of a mbox + * @send_index: bit offset in the mbox + * @msg_zie: slot size of the ipi message + * + * All of these data should be initialized by mtk_ipi_device_register() + */ +struct mtk_ipi_chan_table { + u32 mbox; + mbox_pin_cb_t mbox_pin_cb; + atomic_t holder; + void *pin_buf; + void *prdata; + u32 recv_opt; + struct completion notify; + /* define a mutex for remote response */ + struct mutex mutex_send; + u32 send_ofs; + u32 send_index; + u32 msg_size; +}; + +/** + * mbox information + * + * @mbdev: mbox device + * @mbox_id: mbox id + * @slot: how many slots that mbox used + * @opt: option for tx mode, 0:mbox, 1:share memory 2:queue + * @base: mbox base address + * @mbox_client: mbox client + * @mbox_chan: mbox channel + */ +struct mtk_mbox_info { + struct mtk_vcp_ipc *vcp_ipc; + u32 mbox_id; + u32 slot; + u32 opt; + /* lock of mbox */ + spinlock_t mbox_lock; + struct mbox_client cl; + struct mbox_chan *ch; + struct mtk_ipi_info ipi_info; +}; + +static const char * const mbox_names[VCP_MBOX_NUM] =3D { + "mbox0", "mbox1", "mbox2", "mbox3", "mbox4" +}; + +/** + * mtk_vcp_ipc_recv - recv callback used by MTK VCP mailbox + * + * @c: mbox client + * @msg: message received + * + * Users of VCP IPC will need to provide handle_reply and handle_request + * callbacks. + */ +static void mtk_vcp_ipc_recv(struct mbox_client *c, void *msg) +{ + struct mtk_mbox_info *minfo =3D container_of(c, struct mtk_mbox_info, cl); + struct mtk_vcp_ipc *vcp_ipc =3D minfo->vcp_ipc; + struct mtk_ipi_info *ipi_info =3D msg; + struct mtk_ipi_device *ipidev =3D vcp_ipc->ipi_priv; + struct mtk_ipi_chan_table *table; + struct mtk_mbox_recv_table *mbox_recv; + u32 id; + + /* execute all receive pin handler */ + for (id =3D 0; id < vcp_ipc->mbdev->recv_count; id++) { + mbox_recv =3D &vcp_ipc->mbdev->recv_table[id]; + if (mbox_recv->mbox_id !=3D minfo->mbox_id) + continue; + + if (!(BIT(mbox_recv->pin_index) & ipi_info->irq_status)) + continue; + + table =3D &ipidev->table[mbox_recv->ipi_id]; + if (!table->pin_buf) { + dev_err(vcp_ipc->dev, "IPI%d buf is null.\n", + mbox_recv->ipi_id); + continue; + } + + memcpy(table->pin_buf, + ipi_info->msg + mbox_recv->offset * MBOX_SLOT_SIZE, + mbox_recv->msg_size * MBOX_SLOT_SIZE); + + if (!mbox_recv->recv_opt && table->mbox_pin_cb) + table->mbox_pin_cb(mbox_recv->ipi_id, + table->prdata, + table->pin_buf, + mbox_recv->msg_size * MBOX_SLOT_SIZE); + + /* notify task */ + if (table->recv_opt =3D=3D MBOX_RECV_MESSAGE || + atomic_read(&table->holder)) + complete(&table->notify); + } +} + +/* + * mtk_vcp_ipc_send - send ipc command to MTK VCP + * + * @ipidev: VCP struct mtk_ipi_device handle + * @id: id of the feature IPI + * @data: message address + * @len: message length + * + * Return: Zero for success from mbox_send_message + * negative value for error + */ +int mtk_vcp_ipc_send(struct mtk_ipi_device *ipidev, u32 id, void *data, u3= 2 len) +{ + struct device *dev; + struct mtk_mbox_info *minfo; + struct mtk_ipi_chan_table *table; + struct mtk_vcp_ipc *vcp_ipc; + int ret; + + if (!ipidev || !ipidev->ipi_inited || !data) + return IPI_UNAVAILABLE; + vcp_ipc =3D ipidev->vcp_ipc; + if (!vcp_ipc) + return IPI_UNAVAILABLE; + + table =3D ipidev->table; + dev =3D ipidev->vcp_ipc->dev; + minfo =3D &ipidev->vcp_ipc->info_table[table[id].mbox]; + if (!minfo) { + dev_err(dev, "%s IPI%d minfo is invalid.\n", ipidev->name, id); + return IPI_UNAVAILABLE; + } + + if (len > table[id].msg_size) + return IPI_MSG_TOO_BIG; + else if (!len) + len =3D table[id].msg_size; + + mutex_lock(&table[id].mutex_send); + + minfo->ipi_info.msg =3D data; + minfo->ipi_info.len =3D len; + minfo->ipi_info.id =3D id; + minfo->ipi_info.index =3D table[id].send_index; + minfo->ipi_info.slot_ofs =3D table[id].send_ofs * MBOX_SLOT_SIZE; + + ret =3D mbox_send_message(minfo->ch, &minfo->ipi_info); + mutex_unlock(&table[id].mutex_send); + if (ret < 0) { + dev_err(dev, "%s IPI%d send failed.\n", ipidev->name, id); + return IPI_MBOX_ERR; + } + + return IPI_ACTION_DONE; +} +EXPORT_SYMBOL(mtk_vcp_ipc_send); + +/* + * mtk_vcp_ipc_send_compl - send ipc command to MTK VCP + * + * @ipidev: VCP struct mtk_ipi_device handle + * @id: id of the feature IPI + * @data: message address + * @len: message length + * @timeout_ms: + * + * Return: Zero for success from mbox_send_message + * negative value for error + */ +int mtk_vcp_ipc_send_compl(struct mtk_ipi_device *ipidev, u32 id, + void *data, u32 len, u32 timeout_ms) +{ + struct device *dev; + struct mtk_mbox_info *minfo; + struct mtk_ipi_chan_table *table; + struct mtk_vcp_ipc *vcp_ipc; + int ret; + + if (!ipidev || !ipidev->ipi_inited || !data) + return IPI_UNAVAILABLE; + vcp_ipc =3D ipidev->vcp_ipc; + if (!vcp_ipc) + return IPI_UNAVAILABLE; + + table =3D ipidev->table; + dev =3D ipidev->vcp_ipc->dev; + minfo =3D &ipidev->vcp_ipc->info_table[table[id].mbox]; + if (!minfo) { + dev_err(dev, "%s IPI%d minfo is invalid.\n", ipidev->name, id); + return IPI_UNAVAILABLE; + } + + if (len > table[id].msg_size) + return IPI_MSG_TOO_BIG; + else if (!len) + len =3D table[id].msg_size; + + mutex_lock(&table[id].mutex_send); + + minfo->ipi_info.msg =3D data; + minfo->ipi_info.len =3D len; + minfo->ipi_info.id =3D id; + minfo->ipi_info.index =3D table[id].send_index; + minfo->ipi_info.slot_ofs =3D table[id].send_ofs * MBOX_SLOT_SIZE; + + atomic_inc(&table[id].holder); + + ret =3D mbox_send_message(minfo->ch, &minfo->ipi_info); + if (ret < 0) { + atomic_set(&table[id].holder, 0); + mutex_unlock(&table[id].mutex_send); + dev_err(dev, "%s IPI%d send failed.\n", ipidev->name, id); + return IPI_MBOX_ERR; + } + + /* wait for completion */ + ret =3D wait_for_completion_timeout(&table[id].notify, + msecs_to_jiffies(timeout_ms)); + atomic_set(&table[id].holder, 0); + if (ret > 0) + ret =3D IPI_ACTION_DONE; + + mutex_unlock(&table[id].mutex_send); + + return ret; +} +EXPORT_SYMBOL(mtk_vcp_ipc_send_compl); + +int mtk_vcp_mbox_ipc_register(struct mtk_ipi_device *ipidev, int id, + mbox_pin_cb_t cb, void *prdata, void *msg) +{ + if (!ipidev || !ipidev->ipi_inited) + return IPI_DEV_ILLEGAL; + if (!msg) + return IPI_NO_MSGBUF; + + if (ipidev->table[id].pin_buf) + return IPI_ALREADY_USED; + ipidev->table[id].mbox_pin_cb =3D cb; + ipidev->table[id].pin_buf =3D msg; + ipidev->table[id].prdata =3D prdata; + + return IPI_ACTION_DONE; +} +EXPORT_SYMBOL(mtk_vcp_mbox_ipc_register); + +int mtk_vcp_mbox_ipc_unregister(struct mtk_ipi_device *ipidev, int id) +{ + if (!ipidev || !ipidev->ipi_inited) + return IPI_DEV_ILLEGAL; + + /* Drop the ipi and reset the record */ + complete(&ipidev->table[id].notify); + + ipidev->table[id].mbox_pin_cb =3D NULL; + ipidev->table[id].pin_buf =3D NULL; + ipidev->table[id].prdata =3D NULL; + + return IPI_ACTION_DONE; +} +EXPORT_SYMBOL(mtk_vcp_mbox_ipc_unregister); + +static void mtk_fill_in_entry(struct mtk_ipi_chan_table *entry, const u32 = ipi_id, + const struct mtk_mbox_table *mbdev) +{ + const struct mtk_mbox_send_table *mbox_send =3D mbdev->send_table; + u32 index; + + for (index =3D 0; index < mbdev->send_count; index++) { + if (ipi_id !=3D mbox_send[index].ipi_id) + continue; + + entry->send_ofs =3D mbox_send[index].offset; + entry->send_index =3D mbox_send[index].pin_index; + entry->msg_size =3D mbox_send[index].msg_size; + entry->mbox =3D mbox_send[index].mbox_id; + return; + } + + entry->mbox =3D -ENOENT; +} + +int mtk_vcp_ipc_device_register(struct mtk_ipi_device *ipidev, + u32 ipi_chan_count, struct mtk_vcp_ipc *vcp_ipc) +{ + struct mtk_ipi_chan_table *ipi_chan_table; + struct mtk_mbox_table *mbdev; + u32 index; + + if (!vcp_ipc || !ipidev) + return -EINVAL; + + ipi_chan_table =3D kcalloc(ipi_chan_count, + sizeof(struct mtk_ipi_chan_table), GFP_KERNEL); + if (!ipi_chan_table) + return -ENOMEM; + + mbdev =3D vcp_ipc->mbdev; + vcp_ipc->ipi_priv =3D (void *)ipidev; + ipidev->table =3D ipi_chan_table; + ipidev->vcp_ipc =3D vcp_ipc; + + for (index =3D 0; index < ipi_chan_count; index++) { + atomic_set(&ipi_chan_table[index].holder, 0); + mutex_init(&ipi_chan_table[index].mutex_send); + init_completion(&ipi_chan_table[index].notify); + mtk_fill_in_entry(&ipi_chan_table[index], index, mbdev); + } + + ipidev->ipi_inited =3D 1; + + dev_dbg(vcp_ipc->dev, "%s (with %d IPI) has registered.\n", + ipidev->name, ipi_chan_count); + + return IPI_ACTION_DONE; +} +EXPORT_SYMBOL(mtk_vcp_ipc_device_register); + +static int setup_mbox_table(struct mtk_mbox_table *mbdev, u32 mbox) +{ + struct mtk_mbox_send_table *mbox_send =3D &mbdev->send_table[0]; + struct mtk_mbox_recv_table *mbox_recv =3D &mbdev->recv_table[0]; + u32 i, last_ofs =3D 0, last_idx =3D 0, last_slot =3D 0, last_sz =3D 0; + + for (i =3D 0; i < mbdev->send_count; i++) { + if (mbox =3D=3D mbox_send[i].mbox_id) { + mbox_send[i].offset =3D last_ofs + last_slot; + mbox_send[i].pin_index =3D last_idx + last_sz; + last_idx =3D mbox_send[i].pin_index; + last_sz =3D DIV_ROUND_UP(mbox_send[i].msg_size, MBOX_SLOT_ALIGN); + last_ofs =3D last_sz * MBOX_SLOT_ALIGN; + last_slot =3D last_idx * MBOX_SLOT_ALIGN; + } else if (mbox < mbox_send[i].mbox_id) { + /* no need to search the rest id */ + break; + } + } + + for (i =3D 0; i < mbdev->recv_count; i++) { + if (mbox =3D=3D mbox_recv[i].mbox_id) { + mbox_recv[i].offset =3D last_ofs + last_slot; + mbox_recv[i].pin_index =3D last_idx + last_sz; + last_idx =3D mbox_recv[i].pin_index; + last_sz =3D DIV_ROUND_UP(mbox_recv[i].msg_size, MBOX_SLOT_ALIGN); + last_ofs =3D last_sz * MBOX_SLOT_ALIGN; + last_slot =3D last_idx * MBOX_SLOT_ALIGN; + } else if (mbox < mbox_recv[i].mbox_id) { + /* no need to search the rest id */ + break; + } + } + + if (last_idx > MBOX_MAX_PIN || (last_ofs + last_slot) > MTK_VCP_MBOX_SLOT= _MAX_SIZE / 4) + return -EINVAL; + + return 0; +} + +static int mtk_vcp_ipc_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct mtk_vcp_ipc *vcp_ipc; + struct mbox_client *cl; + struct mtk_mbox_info *minfo; + int ret; + u32 mbox, i; + struct mtk_mbox_table *mbox_data =3D dev_get_platdata(dev); + + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); + + vcp_ipc =3D devm_kzalloc(dev, sizeof(*vcp_ipc), GFP_KERNEL); + if (!vcp_ipc) + return -ENOMEM; + + if (!mbox_data) { + dev_err(dev, "No platform data available\n"); + return -EINVAL; + } + vcp_ipc->mbdev =3D mbox_data; + + /* alloc and init mmup_mbox_info */ + vcp_ipc->info_table =3D vzalloc(sizeof(*vcp_ipc->info_table) * VCP_MBOX_N= UM); + if (!vcp_ipc->info_table) + return -ENOMEM; + + /* create mbox dev */ + for (mbox =3D 0; mbox < VCP_MBOX_NUM; mbox++) { + minfo =3D &vcp_ipc->info_table[mbox]; + minfo->mbox_id =3D mbox; + minfo->vcp_ipc =3D vcp_ipc; + spin_lock_init(&minfo->mbox_lock); + + ret =3D setup_mbox_table(vcp_ipc->mbdev, mbox); + if (ret) + return ret; + + cl =3D &minfo->cl; + cl->dev =3D &pdev->dev; + cl->tx_block =3D false; + cl->knows_txdone =3D false; + cl->tx_prepare =3D NULL; + cl->rx_callback =3D mtk_vcp_ipc_recv; + minfo->ch =3D mbox_request_channel_byname(cl, mbox_names[mbox]); + if (IS_ERR(minfo->ch)) { + ret =3D PTR_ERR(minfo->ch); + if (ret !=3D -EPROBE_DEFER) + dev_err(dev, "Failed to request mbox channel %s ret %d\n", + mbox_names[mbox], ret); + + for (i =3D 0; i < mbox; i++) { + minfo =3D &vcp_ipc->info_table[i]; + mbox_free_channel(minfo->ch); + } + + vfree(vcp_ipc->info_table); + return ret; + } + } + + vcp_ipc->dev =3D dev; + dev_set_drvdata(dev, vcp_ipc); + dev_dbg(dev, "MTK VCP IPC initialized\n"); + + return 0; +} + +static void mtk_vcp_ipc_remove(struct platform_device *pdev) +{ + struct mtk_vcp_ipc *vcp_ipc =3D dev_get_drvdata(&pdev->dev); + struct mtk_mbox_info *minfo; + int i; + + for (i =3D 0; i < VCP_MBOX_NUM; i++) { + minfo =3D &vcp_ipc->info_table[i]; + mbox_free_channel(minfo->ch); + } + + vfree(vcp_ipc->info_table); +} + +static struct platform_driver mtk_vcp_ipc_driver =3D { + .probe =3D mtk_vcp_ipc_probe, + .remove =3D mtk_vcp_ipc_remove, + .driver =3D { + .name =3D "mtk-vcp-ipc", + }, +}; +builtin_platform_driver(mtk_vcp_ipc_driver); + +MODULE_AUTHOR("Jjian Zhou "); +MODULE_DESCRIPTION("MediaTek VCP IPC Controller"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/firmware/mediatek/mtk-vcp-ipc.h b/include/linux/= firmware/mediatek/mtk-vcp-ipc.h new file mode 100644 index 000000000000..dc34b0ba9dd8 --- /dev/null +++ b/include/linux/firmware/mediatek/mtk-vcp-ipc.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2024 MediaTek Inc. + */ + +#ifndef __MTK_VCP_IPC_H__ +#define __MTK_VCP_IPC_H__ + +#include +#include +#include +#include +#include +#include + +/* IPI result definition */ +#define IPI_ACTION_DONE 0 +#define IPI_DEV_ILLEGAL -1 /* ipi device is not initialized */ +#define IPI_ALREADY_USED -2 /* the ipi has be registered */ +#define IPI_UNAVAILABLE -3 /* the ipi can't be found */ +#define IPI_NO_MSGBUF -4 /* receiver doesn't have message buffer */ +#define IPI_MSG_TOO_BIG -5 /* message length is larger than defined */ +#define IPI_MBOX_ERR -99 /* some error from rpmsg layer */ + +/* mbox recv action definition */ +enum mtk_ipi_recv_opt { + MBOX_RECV_MESSAGE =3D 0, + MBOX_RECV_ACK =3D 1, +}; + +/* mbox table item number definition */ +#define send_item_num 3 +#define recv_item_num 4 +#define VCP_MBOX_NUM 5 + +/* mbox slot size definition: 1 slot for 4 bytes */ +#define MBOX_SLOT_SIZE 0x4 +#define MBOX_MAX_PIN 32 +#define VCP_MBOX_NUM 5 +#define MBOX_SLOT_ALIGN 2 + +struct mtk_vcp_ipc; +struct mtk_ipi_chan_table; + +typedef int (*mbox_pin_cb_t)(u32 ipi_id, void *prdata, void *data, u32 len= ); + +/** + * mbox pin structure, this is for send definition, + * @offset: message offset in the slots of a mbox + * @msg_size: message used slots in the mbox, 4 bytes alignment + * @pin_index: bit offset in the mbox + * @ipi_id: ipi enum number + * @mbox_id: mbox number id + */ +struct mtk_mbox_send_table { + u32 offset; + u32 msg_size; + u32 pin_index; + u32 ipi_id; + u32 mbox_id; +}; + +/** + * mbox pin structure, this is for receive definition, + * @offset: message offset in the slots of a mbox + * @recv_opt: recv option, 0:receive ,1: response + * @msg_size: message used slots in the mbox, 4 bytes alignment + * @pin_index: bit offset in the mbox + * @ipi_id: ipi enum number + * @mbox_id: mbox number id + */ +struct mtk_mbox_recv_table { + u32 offset; + u32 recv_opt; + u32 msg_size; + u32 pin_index; + u32 ipi_id; + u32 mbox_id; +}; + +/** + * struct mtk_ipi_device - device for represent the tinysys using mtk ipi + * @name: name of tinysys device + * @id: device id (used to match between rpmsg drivers and devices) + * @vcp_ipc: vcp ipc structure for tinysys device + * @table: channel table with endpoint & channel_info & mbox_pin info + * @prdata: private data for the callback use + * @ipi_inited: set when vcp_ipi_device_register() done + */ +struct mtk_ipi_device { + const char *name; + struct mtk_vcp_ipc *vcp_ipc; + struct mtk_ipi_chan_table *table; + void *prdata; + int ipi_inited; +}; + +/** + * The mtk_mbox_table is a structure used to record the send + * table and recv table. The send table is used to record + * the feature ID and size of the sent data. The recv table + * is used to record the feature ID and size of the received + * data, and whether a callback needs to be invoked. + * + * Following are platform specific interfacer + * @recv_table: structure mtk_mbox_recv_table + * @send_table: structure mtk_mbox_send_table + * @recv_count: receive feature number in this channel + * @send_count: send feature number in this channel + */ +struct mtk_mbox_table { + struct mtk_mbox_recv_table recv_table[32]; + struct mtk_mbox_send_table send_table[32]; + u32 recv_count; + u32 send_count; +}; + +/** + * Mbox is a dedicate hardware of a tinysys consists of: + * 1) a share memory tightly coupled to the tinysys + * 2) several IRQs + * + * Following are platform specific interface + * @dev: vcp device + * @name: identity of the device + * @info_table: mbox info structure + * @ipi_priv: private data for synchronization layer + * @mbox_id: mbox number + * @mbdev: mtk_mbox_table structure + */ +struct mtk_vcp_ipc { + struct device *dev; + const char *name; + struct mtk_mbox_info *info_table; + void *ipi_priv; + void *mbox_id; + struct mtk_mbox_table *mbdev; +}; + +int mtk_vcp_ipc_device_register(struct mtk_ipi_device *ipidev, + u32 ipi_chan_count, + struct mtk_vcp_ipc *vcp_ipc); +int mtk_vcp_ipc_send(struct mtk_ipi_device *ipidev, u32 ipi_id, + void *data, u32 len); +int mtk_vcp_ipc_send_compl(struct mtk_ipi_device *ipidev, u32 ipi_id, + void *data, u32 len, u32 timeout_ms); +int mtk_vcp_mbox_ipc_register(struct mtk_ipi_device *ipidev, int ipi_id, + mbox_pin_cb_t cb, void *prdata, void *msg); +int mtk_vcp_mbox_ipc_unregister(struct mtk_ipi_device *ipidev, int ipi_id); + +#endif --=20 2.46.0 From nobody Mon Apr 6 09:19:41 2026 Received: from mailgw02.mediatek.com (unknown [210.61.82.184]) (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 5FF1513959D; Fri, 20 Mar 2026 03:21:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.61.82.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976868; cv=none; b=QgG6peadzuMNeOaLuiI8zibEOf+EI+iUFr2NzlCPZ1vXztOfEOulSv42p4AWYI9f/L0Zl2QxmllDHLmryIb4i2By6kRyBcK+rkOfR2b1Ip6Ofc8pj4CANf28KFO0wFNKuUFvQbMXUIJ4+JmjkTmwR/DOfR6WzoiFJwH9dLrn54o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976868; c=relaxed/simple; bh=QWOcooYasNMwDAjywY9jRXm2iK0qZzp8rfIkeW06C+w=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Cw4ricr+qEzQazCkfLbJdqyNNtleNCr6phM5RYQM0+neV0njZEnGkimr6FqEHOPERDsY6XcC/FWJ+lpxuVHPd62IgIjkSFTWccpVfrpwDrJwygV1OnSJsXt3J3iEMDk2A1MXpe+52Igx1kDDZP2fPr4Q2+7uoRVYs8YaOJrJfWI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=gXcUR/1+; arc=none smtp.client-ip=210.61.82.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="gXcUR/1+" X-UUID: cfa565f8240b11f1a39cd589f645bc18-20260320 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=vmgQjpi/wNR8vyPtSzI1GJMpPUcUdQy/qqk6xGPKFFE=; b=gXcUR/1+5lS76oySxPkgX7hmrOBwLQp5sQ80hvlY7euT1q6AeqiGmCROw9tKhVdtZTaN6J9T8XmRpMa9HLLbGq9wHHfoXhx/smqmmYrHMIQYDxDiCXeuAjebiFlQRNoXupftu86AFoM8u59Xzek4T6oz7B0vLiKkxCeAa8faArk=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:cb142db5-e248-4fa5-b2b1-6e47f2d47695,IP:0,U RL:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION: release,TS:0 X-CID-META: VersionHash:e7bac3a,CLOUDID:1c8fe916-aa6b-4b2e-be76-373ef1a42b04,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|836|888|898,TC:-5,Content: 0|15|50,EDM:-3,IP:nil,URL:0,File:130,RT:0,Bulk:nil,QS:nil,BEC:-1,COL:0,OSI :0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: cfa565f8240b11f1a39cd589f645bc18-20260320 Received: from mtkmbs14n2.mediatek.inc [(172.21.101.76)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 163218207; Fri, 20 Mar 2026 11:20:57 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by MTKMBS14N1.mediatek.inc (172.21.101.75) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.29; Fri, 20 Mar 2026 11:20:55 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.2562.29 via Frontend Transport; Fri, 20 Mar 2026 11:20:54 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , , Hailong Fan , Huayu Zong , Xiangzhi Tang Subject: [PATCH v3 4/6] remoteproc: mediatek: Add VCP ipi-mbox init driver Date: Fri, 20 Mar 2026 11:18:06 +0800 Message-ID: <20260320032014.13608-5-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> References: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add VCP ipi-mbox init driver. Signed-off-by: Xiangzhi Tang --- drivers/remoteproc/Kconfig | 2 + drivers/remoteproc/mtk_vcp_rproc.c | 98 +++++++++++++++++++++++ drivers/remoteproc/mtk_vcp_rproc.h | 6 ++ include/linux/remoteproc/mtk_vcp_public.h | 55 +++++++++++++ 4 files changed, 161 insertions(+) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 93827f6fd3c5..54b416db0c0b 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -70,6 +70,8 @@ config MTK_VCP_RPROC tristate "MediaTek VCP support" depends on ARCH_MEDIATEK || COMPILE_TEST depends on ARCH_DMA_ADDR_T_64BIT + select MTK_VCP_IPC + select MTK_VCP_MBOX help Say y here to support MediaTek's Video Companion Processor (VCP) via the remote processor framework. diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vc= p_rproc.c index bcb5f3786cf7..6e0fecef72ce 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.c +++ b/drivers/remoteproc/mtk_vcp_rproc.c @@ -5,6 +5,7 @@ =20 #include #include +#include #include #include #include @@ -54,6 +55,18 @@ void vcp_put(struct mtk_vcp_device *vcp) } EXPORT_SYMBOL_GPL(vcp_put); =20 +/** + * vcp_get_ipidev() - get a vcp ipi device struct to reference vcp ipi. + * + * @vcp: mtk_vcp_device structure from vcp_get(). + * + **/ +struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_device *vcp) +{ + return vcp->ipi_dev; +} +EXPORT_SYMBOL_GPL(vcp_get_ipidev); + static int mtk_vcp_start(struct rproc *rproc) { struct mtk_vcp_device *vcp =3D (struct mtk_vcp_device *)rproc->priv; @@ -93,6 +106,34 @@ static const struct rproc_ops mtk_vcp_ops =3D { .stop =3D mtk_vcp_stop, }; =20 +static int vcp_ipi_mbox_init(struct mtk_vcp_device *vcp) +{ + struct mtk_vcp_ipc *vcp_ipc; + struct platform_device *pdev; + int ret; + + pdev =3D platform_device_register_data(vcp->dev, "mtk-vcp-ipc", + PLATFORM_DEVID_NONE, + vcp->platdata->ipc_data, + sizeof(struct mtk_mbox_table)); + if (IS_ERR(pdev)) + return dev_err_probe(vcp->dev, PTR_ERR(pdev), "ipc_data register failed\= n"); + + ret =3D read_poll_timeout_atomic(dev_get_drvdata, + vcp_ipc, vcp_ipc, + USEC_PER_MSEC, + VCP_IPI_DEV_READY_TIMEOUT * USEC_PER_MSEC, + false, &pdev->dev); + if (ret) + return dev_err_probe(vcp->dev, -EPROBE_DEFER, "get vcp_ipc drvdata faile= d\n"); + + ret =3D mtk_vcp_ipc_device_register(vcp->ipi_dev, VCP_IPI_COUNT, vcp_ipc); + if (ret) + dev_err_probe(vcp->dev, ret, "ipi_dev register failed, ret %d\n", ret); + + return ret; +} + static int vcp_multi_core_init(struct platform_device *pdev, struct mtk_vcp_of_cluster *vcp_cluster, enum vcp_core_id core_id) @@ -138,7 +179,9 @@ static struct mtk_vcp_device *vcp_rproc_init(struct pla= tform_device *pdev, vcp->dev =3D dev; vcp->ops =3D &vcp_of_data->ops; vcp->platdata =3D &vcp_of_data->platdata; + vcp->ipi_ops =3D vcp_of_data->platdata.ipi_ops; vcp->vcp_cluster =3D vcp_cluster; + vcp->ipi_dev =3D &vcp_cluster->vcp_ipidev; =20 rproc->auto_boot =3D vcp_of_data->platdata.auto_boot; rproc->sysfs_read_only =3D vcp_of_data->platdata.sysfs_read_only; @@ -166,6 +209,10 @@ static struct mtk_vcp_device *vcp_rproc_init(struct pl= atform_device *pdev, if (ret) return ERR_PTR(dev_err_probe(dev, ret, "vcp_wdt_irq_init failed\n")); =20 + ret =3D vcp_ipi_mbox_init(vcp); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "vcp_ipi_mbox_init failed\n")); + pm_runtime_get_sync(dev); =20 return vcp; @@ -284,6 +331,55 @@ static struct mtk_vcp_reserved_mem_table mt8196_memory= _tb[NUMS_MEM_ID] =3D { { .memory_id =3D MMQOS_MEM_ID, .size =3D 0x1000 }, }; =20 +static struct mtk_mbox_table mt8196_ipc_tb =3D { + .send_table =3D { + { .msg_size =3D 18, .ipi_id =3D 0, .mbox_id =3D 0 }, + + { .msg_size =3D 8, .ipi_id =3D 15, .mbox_id =3D 1 }, + { .msg_size =3D 18, .ipi_id =3D 16, .mbox_id =3D 1 }, + { .msg_size =3D 2, .ipi_id =3D 9, .mbox_id =3D 1 }, + + { .msg_size =3D 18, .ipi_id =3D 11, .mbox_id =3D 2 }, + { .msg_size =3D 2, .ipi_id =3D 2, .mbox_id =3D 2 }, + { .msg_size =3D 3, .ipi_id =3D 3, .mbox_id =3D 2 }, + { .msg_size =3D 2, .ipi_id =3D 32, .mbox_id =3D 2 }, + + { .msg_size =3D 2, .ipi_id =3D 33, .mbox_id =3D 3 }, + { .msg_size =3D 2, .ipi_id =3D 13, .mbox_id =3D 3 }, + { .msg_size =3D 2, .ipi_id =3D 35, .mbox_id =3D 3 }, + + { .msg_size =3D 2, .ipi_id =3D 20, .mbox_id =3D 4 }, + { .msg_size =3D 3, .ipi_id =3D 21, .mbox_id =3D 4 }, + { .msg_size =3D 2, .ipi_id =3D 23, .mbox_id =3D 4 } + }, + .recv_table =3D { + { .recv_opt =3D 0, .msg_size =3D 18, .ipi_id =3D 1, .mbox_id =3D 0 }, + + { .recv_opt =3D 1, .msg_size =3D 8, .ipi_id =3D 15, .mbox_id =3D 1 }, + { .recv_opt =3D 0, .msg_size =3D 18, .ipi_id =3D 17, .mbox_id =3D 1 }, + { .recv_opt =3D 0, .msg_size =3D 2, .ipi_id =3D 10, .mbox_id =3D 1 }, + + { .recv_opt =3D 0, .msg_size =3D 18, .ipi_id =3D 12, .mbox_id =3D 2 }, + { .recv_opt =3D 0, .msg_size =3D 1, .ipi_id =3D 5, .mbox_id =3D 2 }, + { .recv_opt =3D 1, .msg_size =3D 1, .ipi_id =3D 2, .mbox_id =3D 2 }, + + { .recv_opt =3D 0, .msg_size =3D 2, .ipi_id =3D 34, .mbox_id =3D 3 }, + { .recv_opt =3D 0, .msg_size =3D 2, .ipi_id =3D 14, .mbox_id =3D 3 }, + + { .recv_opt =3D 0, .msg_size =3D 1, .ipi_id =3D 26, .mbox_id =3D 4 }, + { .recv_opt =3D 1, .msg_size =3D 1, .ipi_id =3D 20, .mbox_id =3D 4 } + }, + .recv_count =3D 11, + .send_count =3D 14, +}; + +static struct mtk_vcp_ipi_ops mt8196_vcp_ipi_ops =3D { + .ipi_send =3D mtk_vcp_ipc_send, + .ipi_send_compl =3D mtk_vcp_ipc_send_compl, + .ipi_register =3D mtk_vcp_mbox_ipc_register, + .ipi_unregister =3D mtk_vcp_mbox_ipc_unregister, +}; + static const struct mtk_vcp_of_data mt8196_of_data =3D { .ops =3D { .vcp_get_mem_phys =3D vcp_get_reserve_mem_phys, @@ -296,6 +392,8 @@ static const struct mtk_vcp_of_data mt8196_of_data =3D { .auto_boot =3D true, .sysfs_read_only =3D true, .rtos_static_iova =3D 0x180600000, + .ipc_data =3D &mt8196_ipc_tb, + .ipi_ops =3D &mt8196_vcp_ipi_ops, .feature_tb =3D mt8196_feature_tb, .memory_tb =3D mt8196_memory_tb, .fw_name =3D "mediatek/mt8196/vcp.img", diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vc= p_rproc.h index 11ce0f6d562b..ff3e67fc2611 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.h +++ b/drivers/remoteproc/mtk_vcp_rproc.h @@ -21,6 +21,7 @@ * @sram_offset: core sram memory layout * @share_mem_iova: shared memory iova base * @share_mem_size: shared memory size + * @vcp_ipidev: struct mtk_ipi_device * @vcp_memory_tb: vcp memory allocated table */ struct mtk_vcp_of_cluster { @@ -34,6 +35,7 @@ struct mtk_vcp_of_cluster { u32 sram_offset[VCP_CORE_TOTAL]; dma_addr_t share_mem_iova; size_t share_mem_size; + struct mtk_ipi_device vcp_ipidev; struct vcp_reserve_mblock vcp_memory_tb[NUMS_MEM_ID]; }; =20 @@ -43,6 +45,8 @@ struct mtk_vcp_of_cluster { * @auto_boot: rproc auto_boot flag * @sysfs_read_only: rproc sysfs_read_only flag * @rtos_static_iova: vcp dram binary static map iova + * @mtk_mbox_table: mtk_mbox_table structure + * @mtk_vcp_ipi_ops: vcp ipi api ops structure * @feature_tb: vcp feature table structure * @memory_tb: vcp memory table structure * @fw_name: vcp image name and path @@ -51,6 +55,8 @@ struct mtk_vcp_platdata { bool auto_boot; bool sysfs_read_only; dma_addr_t rtos_static_iova; + struct mtk_mbox_table *ipc_data; + struct mtk_vcp_ipi_ops *ipi_ops; struct mtk_vcp_feature_table *feature_tb; struct mtk_vcp_reserved_mem_table *memory_tb; char *fw_name; diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remo= teproc/mtk_vcp_public.h index 3bbc2055f9f8..58ba4b8bb023 100644 --- a/include/linux/remoteproc/mtk_vcp_public.h +++ b/include/linux/remoteproc/mtk_vcp_public.h @@ -7,8 +7,18 @@ #define __MTK_VCP_PUBLIC_H__ =20 #include +#include #include =20 +#define VCP_SYNC_TIMEOUT_MS (50) + +enum vcp_notify_event { + VCP_EVENT_READY =3D 0, + VCP_EVENT_STOP, + VCP_EVENT_SUSPEND, + VCP_EVENT_RESUME, +}; + enum vcp_reserve_mem_id { VCP_RTOS_MEM_ID, VDEC_MEM_ID, @@ -36,15 +46,59 @@ enum vcp_feature_id { NUM_FEATURE_ID, }; =20 +enum { + IPI_OUT_VDEC_1 =3D 0, + IPI_IN_VDEC_1 =3D 1, + IPI_OUT_C_SLEEP_0 =3D 2, + IPI_OUT_TEST_0 =3D 3, + IPI_IN_VCP_READY_0 =3D 5, + IPI_OUT_MMDVFS_VCP =3D 9, + IPI_IN_MMDVFS_VCP =3D 10, + IPI_OUT_MMQOS =3D 11, + IPI_IN_MMQOS =3D 12, + IPI_OUT_MMDEBUG =3D 13, + IPI_IN_MMDEBUG =3D 14, + IPI_OUT_C_VCP_HWVOTER_DEBUG =3D 15, + IPI_OUT_VENC_0 =3D 16, + IPI_IN_VENC_0 =3D 17, + IPI_OUT_C_SLEEP_1 =3D 20, + IPI_OUT_TEST_1 =3D 21, + IPI_OUT_LOGGER_CTRL_0 =3D 22, + IPI_OUT_VCPCTL_1 =3D 23, + IPI_IN_LOGGER_CTRL_0 =3D 25, + IPI_IN_VCP_READY_1 =3D 26, + IPI_OUT_LOGGER_CTRL_1 =3D 30, + IPI_IN_LOGGER_CTRL_1 =3D 31, + IPI_OUT_VCPCTL_0 =3D 32, + IPI_OUT_MMDVFS_MMUP =3D 33, + IPI_IN_MMDVFS_MMUP =3D 34, + IPI_OUT_VDISP =3D 35, + VCP_IPI_COUNT, + VCP_IPI_NS_SERVICE =3D 0xff, + VCP_IPI_NS_SERVICE_COUNT =3D 0x100, +}; + struct mtk_vcp_device { struct platform_device *pdev; struct device *dev; struct rproc *rproc; + struct mtk_ipi_device *ipi_dev; struct mtk_vcp_of_cluster *vcp_cluster; + const struct mtk_vcp_ipi_ops *ipi_ops; const struct mtk_vcp_ops *ops; const struct mtk_vcp_platdata *platdata; }; =20 +struct mtk_vcp_ipi_ops { + int (*ipi_send)(struct mtk_ipi_device *ipidev, u32 ipi_id, + void *data, u32 len); + int (*ipi_send_compl)(struct mtk_ipi_device *ipidev, u32 ipi_id, + void *data, u32 len, u32 timeout_ms); + int (*ipi_register)(struct mtk_ipi_device *ipidev, int ipi_id, + mbox_pin_cb_t cb, void *prdata, void *msg); + int (*ipi_unregister)(struct mtk_ipi_device *ipidev, int ipi_id); +}; + struct mtk_vcp_ops { phys_addr_t (*vcp_get_mem_phys)(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id); @@ -59,6 +113,7 @@ struct mtk_vcp_ops { =20 struct mtk_vcp_device *vcp_get(struct platform_device *pdev); void vcp_put(struct mtk_vcp_device *vcp); +struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_device *vcp); =20 /* * These inline functions are intended for user drivers that are loaded --=20 2.46.0 From nobody Mon Apr 6 09:19:41 2026 Received: from mailgw02.mediatek.com (unknown [210.61.82.184]) (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 6EBA3280CFC; Fri, 20 Mar 2026 03:21:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.61.82.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976867; cv=none; b=UxlDh7NzPL2X7i+jLU03ZW12KfTVmGqsPmuoKi+Wnw9c5x6vSFUbQLIgthG7oYSWBXg+mVa8KFK2vFQTZhZjx0tPFxWewiqxtJrEPbdxrPoDM3Z1ISG4omZdd3g6pYK/CGDQ1gjQj/lZMDyePQH+TvLrzLawXPIO6dEAk9bRFBU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976867; c=relaxed/simple; bh=lYT7o1JBgTwBelKiM2eaFw+md6vclbJ/6in3UMPjpwo=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=uFhTclNq2ZUDLIjZYFcjD8wz5U+4h4GNyof+5jYdnf0dFyaMi8WskvWlr1VSfOv/nws6MXIKC4yePDOkMNY/G6+QFK1bWFOdn7VqxqKci02g/1X9SB2ZGuBjtXuaLu8+RJWZ9SKbz/0RBlgaTt1f/XVgCfOvPZL78ZgzkOVmLXQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=T993Wtdu; arc=none smtp.client-ip=210.61.82.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="T993Wtdu" X-UUID: d02f8d5a240b11f1a39cd589f645bc18-20260320 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=bfHyWawRl4vYE7iTZb8yAakfsESJYG+ItEreEJ/CV5E=; b=T993WtduQxD/y4wnOBGYiKaetg+aF9Hjq5Qy7ovxZzf7Pe3gs/f5rsDZ+p8vG5SWKJHoBeXTEi2nUwz3RlL5PSVVkofNReKQC5KYE//EAHusRn4tA+jynnHJsk7aU/9OgrEXnWGHJ2eKxRg6sHRGM5JDk9mSghFJrqanS0J7rkw=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:79e55bb2-d12b-4849-acdb-0643351cc315,IP:0,U RL:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION: release,TS:0 X-CID-META: VersionHash:e7bac3a,CLOUDID:1e8fe916-aa6b-4b2e-be76-373ef1a42b04,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|836|888|898,TC:-5,Content: 0|15|50,EDM:-3,IP:nil,URL:0,File:130,RT:0,Bulk:nil,QS:nil,BEC:-1,COL:0,OSI :0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: d02f8d5a240b11f1a39cd589f645bc18-20260320 Received: from mtkmbs13n1.mediatek.inc [(172.21.101.193)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 1962490241; Fri, 20 Mar 2026 11:20:58 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by mtkmbs10n2.mediatek.inc (172.21.101.183) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.29; Fri, 20 Mar 2026 11:20:56 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.2562.29 via Frontend Transport; Fri, 20 Mar 2026 11:20:55 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Matthias Brugger" , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , , Hailong Fan , Huayu Zong , "Xiangzhi Tang" Subject: [PATCH v3 5/6] remoteproc: mediatek: Add VCP ipi communication sync mechanism Date: Fri, 20 Mar 2026 11:18:07 +0800 Message-ID: <20260320032014.13608-6-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> References: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 1.Add vcp ready ipi register driver. 2.Add vcp ready notify work mechanism. 3.Add vcp feature resgiter mechanism. Signed-off-by: Xiangzhi Tang --- drivers/remoteproc/mtk_vcp_common.c | 279 ++++++++++++++++++++++ drivers/remoteproc/mtk_vcp_common.h | 56 +++++ drivers/remoteproc/mtk_vcp_rproc.c | 62 ++++- drivers/remoteproc/mtk_vcp_rproc.h | 18 ++ include/linux/remoteproc/mtk_vcp_public.h | 12 + 5 files changed, 425 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_v= cp_common.c index 97ea8099912d..f3b506034e95 100644 --- a/drivers/remoteproc/mtk_vcp_common.c +++ b/drivers/remoteproc/mtk_vcp_common.c @@ -24,6 +24,9 @@ #include "mtk_vcp_common.h" #include "mtk_vcp_rproc.h" =20 +static BLOCKING_NOTIFIER_HEAD(mmup_notifier_list); +static BLOCKING_NOTIFIER_HEAD(vcp_notifier_list); + phys_addr_t vcp_get_reserve_mem_phys(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id) { @@ -153,6 +156,40 @@ int vcp_reserve_memory_init(struct mtk_vcp_device *vcp) return 0; } =20 +static bool vcp_is_core_ready(struct mtk_vcp_device *vcp, + enum vcp_core_id core_id) +{ + switch (core_id) { + case VCP_ID: + return vcp->vcp_cluster->vcp_ready[VCP_ID]; + case MMUP_ID: + return vcp->vcp_cluster->vcp_ready[MMUP_ID]; + case VCP_CORE_TOTAL: + default: + return vcp->vcp_cluster->vcp_ready[VCP_ID] & + vcp->vcp_cluster->vcp_ready[MMUP_ID]; + } +} + +static enum vcp_core_id get_core_by_feature(struct mtk_vcp_device *vcp, + enum vcp_feature_id id) +{ + for (u32 i =3D 0; i < NUM_FEATURE_ID; i++) { + if (vcp->platdata->feature_tb[i].feature_id =3D=3D id) + return vcp->platdata->feature_tb[i].core_id; + } + + return 0; +} + +bool is_vcp_ready(struct mtk_vcp_device *vcp, + enum vcp_feature_id id) +{ + enum vcp_core_id core_id =3D get_core_by_feature(vcp, id); + + return vcp_is_core_ready(vcp, core_id); +} + int wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id core_id) { @@ -215,9 +252,120 @@ int wait_core_hart_shutdown(struct mtk_vcp_device *vc= p, return ret; } =20 +void vcp_A_register_notify(struct mtk_vcp_device *vcp, + enum vcp_feature_id id, + struct notifier_block *nb) +{ + enum vcp_core_id core_id =3D get_core_by_feature(vcp, id); + + switch (core_id) { + case VCP_ID: + blocking_notifier_chain_register(&vcp_notifier_list, nb); + if (vcp_is_core_ready(vcp, VCP_ID)) + nb->notifier_call(nb, VCP_EVENT_READY, NULL); + break; + case MMUP_ID: + blocking_notifier_chain_register(&mmup_notifier_list, nb); + if (vcp_is_core_ready(vcp, MMUP_ID)) + nb->notifier_call(nb, VCP_EVENT_READY, NULL); + break; + default: + dev_err(vcp->dev, "%s(), No Support core id\n", __func__); + break; + } +} + +void vcp_A_unregister_notify(struct mtk_vcp_device *vcp, + enum vcp_feature_id id, + struct notifier_block *nb) +{ + enum vcp_core_id core_id =3D get_core_by_feature(vcp, id); + + switch (core_id) { + case VCP_ID: + blocking_notifier_chain_unregister(&vcp_notifier_list, nb); + break; + case MMUP_ID: + blocking_notifier_chain_unregister(&mmup_notifier_list, nb); + break; + default: + dev_err(vcp->dev, "%s(), No Support core id\n", __func__); + break; + } +} + +void vcp_extern_notify(enum vcp_core_id core_id, + enum vcp_notify_event notify_status) +{ + switch (core_id) { + case VCP_ID: + blocking_notifier_call_chain(&vcp_notifier_list, notify_status, NULL); + break; + case MMUP_ID: + blocking_notifier_call_chain(&mmup_notifier_list, notify_status, NULL); + break; + default: + break; + } +} + +static void vcp_A_notify_ws(struct work_struct *ws) +{ + struct vcp_work_struct *sws =3D + container_of(ws, struct vcp_work_struct, work); + struct mtk_vcp_device *vcp =3D platform_get_drvdata(to_platform_device(sw= s->dev)); + enum vcp_core_id core_id =3D sws->flags; + + if (core_id < VCP_CORE_TOTAL) { + mutex_lock(&vcp->vcp_cluster->vcp_ready_mutex); + vcp->vcp_cluster->vcp_ready[core_id] =3D true; + mutex_unlock(&vcp->vcp_cluster->vcp_ready_mutex); + + vcp_extern_notify(core_id, VCP_EVENT_READY); + + /*clear reset status and unlock wake lock*/ + dev_info(sws->dev, "%s core id %u ready\n", __func__, core_id); + } else { + dev_err(sws->dev, "%s wrong core id %u\n", __func__, core_id); + } +} + +static void vcp_A_set_ready(struct mtk_vcp_device *vcp, + enum vcp_core_id core_id) +{ + if (core_id < VCP_CORE_TOTAL) { + vcp->vcp_cluster->vcp_ready_notify_wk[core_id].flags =3D core_id; + queue_work(vcp->vcp_cluster->vcp_workqueue, + &vcp->vcp_cluster->vcp_ready_notify_wk[core_id].work); + } +} + +int vcp_A_ready_ipi_handler(u32 id, void *prdata, void *data, u32 len) +{ + struct mtk_vcp_device *vcp =3D (struct mtk_vcp_device *)prdata; + + switch (id) { + case IPI_IN_VCP_READY_0: + if (!vcp_is_core_ready(vcp, VCP_ID)) + vcp_A_set_ready(vcp, VCP_ID); + break; + case IPI_IN_VCP_READY_1: + if (!vcp_is_core_ready(vcp, MMUP_ID)) + vcp_A_set_ready(vcp, MMUP_ID); + break; + default: + dev_err(vcp->dev, "%s(), No Support ipi id\n", __func__); + break; + } + + return 0; +} + int reset_vcp(struct mtk_vcp_device *vcp) { struct arm_smccc_res res; + bool mmup_status, vcp_status; + int ret; =20 if (vcp->vcp_cluster->core_nums > MMUP_ID) { writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova), @@ -228,6 +376,16 @@ int reset_vcp(struct mtk_vcp_device *vcp) arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, MTK_TINYSYS_MMUP_KERNEL_OP_RESET_RELEASE, 1, 0, 0, 0, 0, 0, &res); + + ret =3D read_poll_timeout(vcp_is_core_ready, + mmup_status, mmup_status, + USEC_PER_MSEC, + VCP_READY_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp, MMUP_ID); + if (ret) { + dev_err(vcp->dev, "MMUP_ID bootup timeout. Stop vcp booting\n"); + return ret; + } } =20 writel((u32)VCP_PACK_IOVA(vcp->vcp_cluster->share_mem_iova), @@ -239,6 +397,127 @@ int reset_vcp(struct mtk_vcp_device *vcp) MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE, 1, 0, 0, 0, 0, 0, &res); =20 + ret =3D read_poll_timeout(vcp_is_core_ready, + vcp_status, vcp_status, + USEC_PER_MSEC, + VCP_READY_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp, VCP_ID); + if (ret) { + dev_err(vcp->dev, "VCP_ID bootup timeout. Stop vcp booting\n"); + return ret; + } + + return ret; +} + +static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum vcp_feature_= id id) +{ + struct vcp_slp_ctrl slp_data; + bool suspend_status; + int ret; + + if (vcp->vcp_cluster->feature_enable[id]) { + dev_err(vcp->dev, "%s feature(id=3D%d) already enabled\n", + __func__, id); + return -EINVAL; + } + + if (id !=3D RTOS_FEATURE_ID) { + slp_data.cmd =3D SLP_WAKE_LOCK; + slp_data.feature =3D id; + ret =3D vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0, + &slp_data, PIN_OUT_C_SIZE_SLEEP_0, 500); + if (ret < 0) { + dev_err(vcp->dev, "%s ipc_send_compl failed. ret %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum vcp_feature= _id id) +{ + struct vcp_slp_ctrl slp_data; + bool suspend_status; + int ret; + + if (!vcp->vcp_cluster->feature_enable[id]) { + dev_err(vcp->dev, "%s feature(id=3D%d) already disabled\n", + __func__, id); + return -EINVAL; + } + + if (id !=3D RTOS_FEATURE_ID) { + slp_data.cmd =3D SLP_WAKE_UNLOCK; + slp_data.feature =3D id; + ret =3D vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0, + &slp_data, PIN_OUT_C_SIZE_SLEEP_0, 500); + if (ret < 0) { + dev_err(vcp->dev, "%s ipc_send_compl failed. ret %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +int vcp_A_register_feature(struct mtk_vcp_device *vcp, enum vcp_feature_id= id) +{ + int ret; + + if (id >=3D NUM_FEATURE_ID) { + dev_err(vcp->dev, "%s unsupported feature id %d\n", + __func__, id); + return -EINVAL; + } + + mutex_lock(&vcp->vcp_cluster->vcp_feature_mutex); + ret =3D vcp_enable_pm_clk(vcp, id); + if (ret) + dev_err(vcp->dev, "%s feature(id=3D%d) register failed\n", + __func__, id); + else + vcp->vcp_cluster->feature_enable[id] =3D true; + mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex); + + return ret; +} + +int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, enum vcp_feature_= id id) +{ + int ret; + + if (id >=3D NUM_FEATURE_ID) { + dev_err(vcp->dev, "%s unsupported feature id %d\n", __func__, id); + return -EINVAL; + } + + mutex_lock(&vcp->vcp_cluster->vcp_feature_mutex); + ret =3D vcp_disable_pm_clk(vcp, id); + if (ret) + dev_err(vcp->dev, "%s feature(id=3D%d) deregister failed\n", + __func__, id); + else + vcp->vcp_cluster->feature_enable[id] =3D false; + mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex); + + return ret; +} + +int vcp_notify_work_init(struct mtk_vcp_device *vcp) +{ + vcp->vcp_cluster->vcp_workqueue =3D create_singlethread_workqueue("VCP_WQ= "); + if (!vcp->vcp_cluster->vcp_workqueue) + return dev_err_probe(vcp->dev, -EINVAL, "vcp_workqueue create fail\n"); + + for (u32 core_id =3D 0; core_id < VCP_CORE_TOTAL; core_id++) { + vcp->vcp_cluster->vcp_ready_notify_wk[core_id].dev =3D vcp->dev; + INIT_WORK(&vcp->vcp_cluster->vcp_ready_notify_wk[core_id].work, vcp_A_no= tify_ws); + } + return 0; } =20 diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_v= cp_common.h index d048757c955a..8b19fcb78a79 100644 --- a/drivers/remoteproc/mtk_vcp_common.h +++ b/drivers/remoteproc/mtk_vcp_common.h @@ -13,10 +13,13 @@ #include =20 /* VCP timeout definition */ +#define VCP_READY_TIMEOUT_MS 3000 +#define VCP_IPI_DEV_READY_TIMEOUT 1000 #define CORE_HART_SHUTDOWN_TIMEOUT_MS 10 =20 /* VCP platform definition */ #define DMA_MAX_MASK_BIT 33 +#define PIN_OUT_C_SIZE_SLEEP_0 2 =20 /* VCP load image definition */ #define VCM_IMAGE_MAGIC (0x58881688) @@ -90,6 +93,14 @@ enum vcp_core_id { VCP_CORE_TOTAL, }; =20 +enum vcp_slp_cmd { + SLP_WAKE_LOCK =3D 0, + SLP_WAKE_UNLOCK, + SLP_STATUS_DBG, + SLP_SUSPEND, + SLP_RESUME, +}; + enum mtk_tinysys_vcp_kernel_op { MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET =3D 0, MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE, @@ -154,6 +165,32 @@ struct vcp_reserve_mblock { size_t size; }; =20 +/** + * struct vcp_slp_ctrl - sleep ctrl data sync with AP and VCP + * + * @feature: Feature id + * @cmd: sleep cmd flag. + */ +struct vcp_slp_ctrl { + u32 feature; + u32 cmd; +}; + +/** + * struct vcp_work_struct - vcp notify work structure. + * + * @work: struct work_struct member + * @dev: struct device member + * @u32 flags: vcp notify work flag + * @id: vcp core id + */ +struct vcp_work_struct { + struct work_struct work; + struct device *dev; + u32 flags; + u32 id; +}; + /** * struct vcp_region_info_st - config vcp image info sync to vcp bootloade= r. * @@ -201,6 +238,20 @@ struct vcp_region_info_st { u32 coredump_dram_offset; }; =20 +int vcp_A_ready_ipi_handler(u32 id, void *prdata, + void *data, u32 len); +bool is_vcp_ready(struct mtk_vcp_device *vcp, + enum vcp_feature_id id); +int vcp_notify_work_init(struct mtk_vcp_device *vcp); +void vcp_extern_notify(enum vcp_core_id core_id, + enum vcp_notify_event notify_status); +void vcp_A_register_notify(struct mtk_vcp_device *vcp, + enum vcp_feature_id id, + struct notifier_block *nb); +void vcp_A_unregister_notify(struct mtk_vcp_device *vcp, + enum vcp_feature_id id, + struct notifier_block *nb); + int vcp_reserve_memory_init(struct mtk_vcp_device *vcp); phys_addr_t vcp_get_reserve_mem_phys(struct mtk_vcp_device *vcp, enum vcp_= reserve_mem_id id); dma_addr_t vcp_get_reserve_mem_iova(struct mtk_vcp_device *vcp, enum vcp_r= eserve_mem_id id); @@ -213,5 +264,10 @@ int mtk_vcp_load(struct rproc *rproc, const struct fir= mware *fw); =20 int vcp_wdt_irq_init(struct mtk_vcp_device *vcp); =20 +int vcp_A_register_feature(struct mtk_vcp_device *vcp, + enum vcp_feature_id id); +int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, + enum vcp_feature_id id); + int wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id c= ore_id); #endif diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vc= p_rproc.c index 6e0fecef72ce..833a0dc69d9c 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.c +++ b/drivers/remoteproc/mtk_vcp_rproc.c @@ -71,6 +71,30 @@ static int mtk_vcp_start(struct rproc *rproc) { struct mtk_vcp_device *vcp =3D (struct mtk_vcp_device *)rproc->priv; struct arm_smccc_res res; + int ret; + + ret =3D vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_OUT_C_SLEEP_0, + NULL, NULL, &vcp->vcp_cluster->slp_ipi_ack_data); + if (ret) { + dev_err(vcp->dev, "Failed to register IPI_OUT_C_SLEEP_0\n"); + goto slp_ipi_unregister; + } + + ret =3D vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_IN_VCP_READY_0, + (void *)vcp_A_ready_ipi_handler, + vcp, &vcp->vcp_cluster->msg_vcp_ready0); + if (ret) { + dev_err(vcp->dev, "Failed to register IPI_IN_VCP_READY_0\n"); + goto vcp0_ready_ipi_unregister; + } + + ret =3D vcp->ipi_ops->ipi_register(vcp->ipi_dev, IPI_IN_VCP_READY_1, + (void *)vcp_A_ready_ipi_handler, + vcp, &vcp->vcp_cluster->msg_vcp_ready1); + if (ret) { + dev_err(vcp->dev, "Failed to register IPI_IN_VCP_READY_1\n"); + goto vcp1_ready_ipi_unregister; + } =20 /* core 0 */ arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, @@ -83,10 +107,22 @@ static int mtk_vcp_start(struct rproc *rproc) 1, 0, 0, 0, 0, 0, &res); =20 ret =3D reset_vcp(vcp); - if (ret) + if (ret) { dev_err(vcp->dev, "bootup fail\n"); - else + } else { dev_info(vcp->dev, "bootup successfully\n"); + if (vcp_A_register_feature(vcp, RTOS_FEATURE_ID) < 0) + vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID); + } + + return ret; + +vcp1_ready_ipi_unregister: + vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_IN_VCP_READY_1); +vcp0_ready_ipi_unregister: + vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_IN_VCP_READY_0); +slp_ipi_unregister: + vcp->ipi_ops->ipi_unregister(vcp->ipi_dev, IPI_OUT_C_SLEEP_0); =20 return ret; } @@ -97,6 +133,9 @@ static int mtk_vcp_stop(struct rproc *rproc) =20 vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID); =20 + vcp_extern_notify(VCP_ID, VCP_EVENT_STOP); + vcp_extern_notify(MMUP_ID, VCP_EVENT_STOP); + return 0; } =20 @@ -185,6 +224,8 @@ static struct mtk_vcp_device *vcp_rproc_init(struct pla= tform_device *pdev, =20 rproc->auto_boot =3D vcp_of_data->platdata.auto_boot; rproc->sysfs_read_only =3D vcp_of_data->platdata.sysfs_read_only; + mutex_init(&vcp->vcp_cluster->vcp_feature_mutex); + mutex_init(&vcp->vcp_cluster->vcp_ready_mutex); platform_set_drvdata(pdev, vcp); =20 ret =3D vcp_reserve_memory_init(vcp); @@ -213,6 +254,10 @@ static struct mtk_vcp_device *vcp_rproc_init(struct pl= atform_device *pdev, if (ret) return ERR_PTR(dev_err_probe(dev, ret, "vcp_ipi_mbox_init failed\n")); =20 + ret =3D vcp_notify_work_init(vcp); + if (ret) + return ERR_PTR(dev_err_probe(dev, ret, "vcp_notify_work_init failed\n")); + pm_runtime_get_sync(dev); =20 return vcp; @@ -287,6 +332,8 @@ static void vcp_device_remove(struct platform_device *p= dev) { struct mtk_vcp_device *vcp =3D platform_get_drvdata(pdev); =20 + flush_workqueue(vcp->vcp_cluster->vcp_workqueue); + destroy_workqueue(vcp->vcp_cluster->vcp_workqueue); pm_runtime_disable(&pdev->dev); =20 rproc_del(vcp->rproc); @@ -297,6 +344,12 @@ static void vcp_device_shutdown(struct platform_device= *pdev) struct mtk_vcp_device *vcp =3D platform_get_drvdata(pdev); int ret; =20 + vcp->vcp_cluster->vcp_ready[VCP_ID] =3D false; + vcp->vcp_cluster->vcp_ready[MMUP_ID] =3D false; + + vcp_extern_notify(VCP_ID, VCP_EVENT_STOP); + vcp_extern_notify(MMUP_ID, VCP_EVENT_STOP); + writel(GIPC_VCP_HART0_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); ret =3D wait_core_hart_shutdown(vcp, VCP_ID); if (ret) @@ -382,6 +435,11 @@ static struct mtk_vcp_ipi_ops mt8196_vcp_ipi_ops =3D { =20 static const struct mtk_vcp_of_data mt8196_of_data =3D { .ops =3D { + .vcp_is_ready =3D is_vcp_ready, + .vcp_register_feature =3D vcp_A_register_feature, + .vcp_deregister_feature =3D vcp_A_deregister_feature, + .vcp_register_notify =3D vcp_A_register_notify, + .vcp_unregister_notify =3D vcp_A_unregister_notify, .vcp_get_mem_phys =3D vcp_get_reserve_mem_phys, .vcp_get_mem_iova =3D vcp_get_reserve_mem_iova, .vcp_get_mem_virt =3D vcp_get_reserve_mem_virt, diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vc= p_rproc.h index ff3e67fc2611..600715b77124 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.h +++ b/drivers/remoteproc/mtk_vcp_rproc.h @@ -19,10 +19,19 @@ * @core_nums: total core numbers get from dtb * @twohart: core weo hart support flag * @sram_offset: core sram memory layout + * @msg_vcp_ready0: core0 ready ipi msg data + * @msg_vcp_ready1: core1 ready ipi msg data + * @slp_ipi_ack_data: sleep ipi msg data + * @feature_enable: feature status count data + * @vcp_ready: vcp core status flag * @share_mem_iova: shared memory iova base * @share_mem_size: shared memory size + * @vcp_feature_mutex: vcp feature register mutex structure + * @vcp_ready_mutex: vcp core ready mutex structure * @vcp_ipidev: struct mtk_ipi_device + * @vcp_workqueue: ready workqueue_struct * @vcp_memory_tb: vcp memory allocated table + * @vcp_ready_notify_wk: vcp_work_struct structure */ struct mtk_vcp_of_cluster { void __iomem *sram_base; @@ -33,10 +42,19 @@ struct mtk_vcp_of_cluster { u32 core_nums; u32 twohart[VCP_CORE_TOTAL]; u32 sram_offset[VCP_CORE_TOTAL]; + u32 msg_vcp_ready0; + u32 msg_vcp_ready1; + u32 slp_ipi_ack_data; + bool feature_enable[NUM_FEATURE_ID]; + bool vcp_ready[VCP_CORE_TOTAL]; dma_addr_t share_mem_iova; size_t share_mem_size; + struct mutex vcp_feature_mutex; + struct mutex vcp_ready_mutex; struct mtk_ipi_device vcp_ipidev; + struct workqueue_struct *vcp_workqueue; struct vcp_reserve_mblock vcp_memory_tb[NUMS_MEM_ID]; + struct vcp_work_struct vcp_ready_notify_wk[VCP_CORE_TOTAL]; }; =20 /** diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remo= teproc/mtk_vcp_public.h index 58ba4b8bb023..b9e1d86685fd 100644 --- a/include/linux/remoteproc/mtk_vcp_public.h +++ b/include/linux/remoteproc/mtk_vcp_public.h @@ -100,6 +100,18 @@ struct mtk_vcp_ipi_ops { }; =20 struct mtk_vcp_ops { + bool (*vcp_is_ready)(struct mtk_vcp_device *vcp, + enum vcp_feature_id id); + int (*vcp_register_feature)(struct mtk_vcp_device *vcp, + enum vcp_feature_id id); + int (*vcp_deregister_feature)(struct mtk_vcp_device *vcp, + enum vcp_feature_id id); + void (*vcp_register_notify)(struct mtk_vcp_device *vcp, + enum vcp_feature_id id, + struct notifier_block *nb); + void (*vcp_unregister_notify)(struct mtk_vcp_device *vcp, + enum vcp_feature_id id, + struct notifier_block *nb); phys_addr_t (*vcp_get_mem_phys)(struct mtk_vcp_device *vcp, enum vcp_reserve_mem_id id); dma_addr_t (*vcp_get_mem_iova)(struct mtk_vcp_device *vcp, --=20 2.46.0 From nobody Mon Apr 6 09:19:41 2026 Received: from mailgw02.mediatek.com (unknown [210.61.82.184]) (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 4D1232D7DDF; Fri, 20 Mar 2026 03:21:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.61.82.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976869; cv=none; b=hIIg0hxi7PcxEZI8VuIEztiSXT/wagUO1MS07xh1d9q6bkqF6RjzRB806tBhfTqp2ELPli3WfzNamQZdwcVR0dSXmxYKHZJD3T/nY1aq3mq1s+3rVPMwDF95JC/5cBAjSHKsIYPzHRYbgvhUMYaagQymguu2RKsRwhbtYarzqik= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773976869; c=relaxed/simple; bh=5Kjv1xZrlCL2RSuD9we39UBT+jWIgSkOV2REQ/wxDos=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=t3jXGgmnKHxjv0BeYkJWSSWh+EYRkcb0knZEz+WsMHiommIzgCluNDT7X8uFx6PGpIi+39gWNwJicGjco7jqKZoJplHOZV5cPFFeFN2LeyOd23WrqpFo95ca4ABydMDbdXo3uKqaN7/GsgVNC78u2WvtTtOxyW/aerriYAZj84Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com; spf=pass smtp.mailfrom=mediatek.com; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b=C1F5f+F0; arc=none smtp.client-ip=210.61.82.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mediatek.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mediatek.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="C1F5f+F0" X-UUID: d0c0c7ac240b11f1a39cd589f645bc18-20260320 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=NT4VoMEkut88E7L5xM3Nk57RkG041neRyZ4yAKpbst8=; b=C1F5f+F0KrYlp/bLJrHyq9W41F6j9AM+EuBOEOr1sSpvWKUZqtmQ8aEb+dIoLe+d0u39ZqTAbC6Dk33Q1Us8p+p0SkhyYntMlh+ysU3YWniZdns/WLx0kPrMtAJVNyXGGVefD9tPWJNO8aw/3MiAxN5rShajikYOIi0oONVbFko=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.12,REQID:f885be67-a248-4928-94eb-07142eced660,IP:0,U RL:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION: release,TS:0 X-CID-META: VersionHash:e7bac3a,CLOUDID:1b8fe916-aa6b-4b2e-be76-373ef1a42b04,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102|836|888|898,TC:-5,Content: 0|15|50,EDM:-3,IP:nil,URL:0,File:130,RT:0,Bulk:nil,QS:nil,BEC:-1,COL:0,OSI :0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0 X-CID-BVR: 2,SSN|SDN X-CID-BAS: 2,SSN|SDN,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: d0c0c7ac240b11f1a39cd589f645bc18-20260320 Received: from mtkmbs11n1.mediatek.inc [(172.21.101.185)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 1301549450; Fri, 20 Mar 2026 11:20:59 +0800 Received: from mtkmbs11n1.mediatek.inc (172.21.101.185) by mtkmbs13n2.mediatek.inc (172.21.101.108) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.29; Fri, 20 Mar 2026 11:20:57 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs11n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.2562.29 via Frontend Transport; Fri, 20 Mar 2026 11:20:56 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , , Hailong Fan , Huayu Zong , Xiangzhi Tang Subject: [PATCH v3 6/6] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature Date: Fri, 20 Mar 2026 11:18:08 +0800 Message-ID: <20260320032014.13608-7-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> References: <20260320032014.13608-1-xiangzhi.tang@mediatek.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 1.Add vcp suspend and resume callback Signed-off-by: Xiangzhi Tang --- drivers/remoteproc/mtk_vcp_common.c | 114 ++++++++++++++++++++++ drivers/remoteproc/mtk_vcp_common.h | 6 ++ drivers/remoteproc/mtk_vcp_rproc.c | 59 +++++++++++ drivers/remoteproc/mtk_vcp_rproc.h | 2 + include/linux/remoteproc/mtk_vcp_public.h | 1 + 5 files changed, 182 insertions(+) diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_v= cp_common.c index f3b506034e95..f4952fbace3c 100644 --- a/drivers/remoteproc/mtk_vcp_common.c +++ b/drivers/remoteproc/mtk_vcp_common.c @@ -190,6 +190,11 @@ bool is_vcp_ready(struct mtk_vcp_device *vcp, return vcp_is_core_ready(vcp, core_id); } =20 +bool is_vcp_suspending(struct mtk_vcp_device *vcp) +{ + return vcp->vcp_cluster->is_suspending ? true : false; +} + int wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id core_id) { @@ -252,6 +257,95 @@ int wait_core_hart_shutdown(struct mtk_vcp_device *vcp, return ret; } =20 +void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_= id) +{ + u32 status; + u32 stop_ctrl; + int ret; + + stop_ctrl =3D (vcp->vcp_cluster->twohart[core_id] ? + (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT | B_CORE_AXIS_BUSY) : + (B_CORE_GATED | B_HART0_HALT | B_CORE_AXIS_BUSY)); + + switch (core_id) { + case VCP_ID: + ret =3D readl_poll_timeout(vcp->vcp_cluster->cfg + R_CORE0_STATUS, + status, + (status & stop_ctrl) =3D=3D (stop_ctrl & ~B_CORE_AXIS_BUSY), + USEC_PER_MSEC, + CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC); + if (ret) { + dev_err(vcp->dev, "wait [%s] core stop timeout, current status 0x%x\n", + core_id ? "MMUP_ID" : "VCP_ID", status); + return; + } + break; + case MMUP_ID: + ret =3D readl_poll_timeout(vcp->vcp_cluster->cfg + R_CORE1_STATUS, + status, + (status & stop_ctrl) =3D=3D (stop_ctrl & ~B_CORE_AXIS_BUSY), + USEC_PER_MSEC, + CORE_HART_SHUTDOWN_TIMEOUT_MS * USEC_PER_MSEC); + if (ret) { + dev_err(vcp->dev, "wait [%s] core stop timeout, current status 0x%x\n", + core_id ? "MMUP_ID" : "VCP_ID", status); + return; + } + break; + default: + dev_err(vcp->dev, "%s(), No Support core id\n", __func__); + break; + } +} + +static bool vcp_get_suspend_resume_status(struct mtk_vcp_device *vcp) +{ + if (vcp->vcp_cluster->core_nums > MMUP_ID) + return !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & VCP_AP_SUSPEND= ) && + !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & MMUP_AP_SUSPEN= D); + + return !!(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & VCP_AP_SUSPEND); +} + +void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend) +{ + bool status; + int ret; + + if (suspend) { + writel(B_CORE0_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR2); + writel(SUSPEND_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RESUME= ); + if (vcp->vcp_cluster->core_nums > MMUP_ID) { + writel(B_CORE1_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR3); + writel(SUSPEND_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RESUM= E); + } + } else { + writel(B_CORE0_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR2); + writel(RESUME_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RESUME); + if (vcp->vcp_cluster->core_nums > MMUP_ID) { + writel(B_CORE1_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR3); + writel(RESUME_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RESUME= ); + } + } + + writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + + ret =3D read_poll_timeout(vcp_get_suspend_resume_status, + status, (status =3D=3D suspend), + USEC_PER_MSEC, + SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp); + if (ret) + dev_err(vcp->dev, "vcp %s timeout GPIC 0x%x 0x%x 0x%x 0x%x flag 0x%x 0x%= x\n", + suspend ? "suspend" : "resume", + readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET), + readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_CLR), + readl(vcp->vcp_cluster->cfg_core + AP_R_GPR2), + readl(vcp->vcp_cluster->cfg_core + AP_R_GPR3), + readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC), + readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC)); +} + void vcp_A_register_notify(struct mtk_vcp_device *vcp, enum vcp_feature_id id, struct notifier_block *nb) @@ -416,6 +510,16 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vc= p, enum vcp_feature_id id) bool suspend_status; int ret; =20 + ret =3D read_poll_timeout(is_vcp_suspending, + suspend_status, !suspend_status, + USEC_PER_MSEC, + SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp); + if (ret) { + dev_err(vcp->dev, "%s blocked by vcp suspend\n", __func__); + return ret; + } + if (vcp->vcp_cluster->feature_enable[id]) { dev_err(vcp->dev, "%s feature(id=3D%d) already enabled\n", __func__, id); @@ -443,6 +547,16 @@ static int vcp_disable_pm_clk(struct mtk_vcp_device *v= cp, enum vcp_feature_id id bool suspend_status; int ret; =20 + ret =3D read_poll_timeout(is_vcp_suspending, + suspend_status, !suspend_status, + USEC_PER_MSEC, + SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp); + if (ret) { + dev_err(vcp->dev, "%s blocked by vcp suspend\n", __func__); + return ret; + } + if (!vcp->vcp_cluster->feature_enable[id]) { dev_err(vcp->dev, "%s feature(id=3D%d) already disabled\n", __func__, id); diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_v= cp_common.h index 8b19fcb78a79..eff2199c7610 100644 --- a/drivers/remoteproc/mtk_vcp_common.h +++ b/drivers/remoteproc/mtk_vcp_common.h @@ -16,9 +16,12 @@ #define VCP_READY_TIMEOUT_MS 3000 #define VCP_IPI_DEV_READY_TIMEOUT 1000 #define CORE_HART_SHUTDOWN_TIMEOUT_MS 10 +#define SUSPEND_WAIT_TIMEOUT_MS 100 =20 /* VCP platform definition */ #define DMA_MAX_MASK_BIT 33 +#define RESUME_MAGIC 0x12345678 +#define SUSPEND_MAGIC 0x87654321 #define PIN_OUT_C_SIZE_SLEEP_0 2 =20 /* VCP load image definition */ @@ -269,5 +272,8 @@ int vcp_A_register_feature(struct mtk_vcp_device *vcp, int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, enum vcp_feature_id id); =20 +bool is_vcp_suspending(struct mtk_vcp_device *vcp); int wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id c= ore_id); +void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_= id); +void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend); #endif diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vc= p_rproc.c index 833a0dc69d9c..a44a8081e7a3 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.c +++ b/drivers/remoteproc/mtk_vcp_rproc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include =20 #include "mtk_vcp_common.h" @@ -67,6 +68,57 @@ struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_dev= ice *vcp) } EXPORT_SYMBOL_GPL(vcp_get_ipidev); =20 +static int mtk_vcp_suspend(struct device *dev) +{ + struct mtk_vcp_device *vcp =3D platform_get_drvdata(to_platform_device(de= v)); + + vcp_extern_notify(VCP_ID, VCP_EVENT_SUSPEND); + vcp_extern_notify(MMUP_ID, VCP_EVENT_SUSPEND); + + for (u32 id =3D RTOS_FEATURE_ID + 1; id < NUM_FEATURE_ID; id++) { + if (vcp->vcp_cluster->feature_enable[id]) { + dev_err(vcp->dev, "%s Check feature(id=3D%d) statue(%d)\n", + __func__, id, + vcp->vcp_cluster->feature_enable[id]); + return -EINVAL; + } + } + + if (!vcp->vcp_cluster->is_suspending) { + vcp->vcp_cluster->is_suspending =3D true; + vcp->vcp_cluster->vcp_ready[VCP_ID] =3D false; + vcp->vcp_cluster->vcp_ready[MMUP_ID] =3D false; + + flush_workqueue(vcp->vcp_cluster->vcp_workqueue); + + vcp_wait_suspend_resume(vcp, true); + vcp_wait_core_stop(vcp, VCP_ID); + vcp_wait_core_stop(vcp, MMUP_ID); + + pm_runtime_put_sync(dev); + } + vcp->vcp_cluster->is_suspending =3D true; + + return 0; +} + +static int mtk_vcp_resume(struct device *dev) +{ + struct mtk_vcp_device *vcp =3D platform_get_drvdata(to_platform_device(de= v)); + + if (vcp->vcp_cluster->is_suspending) { + pm_runtime_get_sync(dev); + + vcp_wait_suspend_resume(vcp, false); + } + vcp->vcp_cluster->is_suspending =3D false; + + vcp_extern_notify(MMUP_ID, VCP_EVENT_RESUME); + vcp_extern_notify(VCP_ID, VCP_EVENT_RESUME); + + return 0; +} + static int mtk_vcp_start(struct rproc *rproc) { struct mtk_vcp_device *vcp =3D (struct mtk_vcp_device *)rproc->priv; @@ -436,6 +488,7 @@ static struct mtk_vcp_ipi_ops mt8196_vcp_ipi_ops =3D { static const struct mtk_vcp_of_data mt8196_of_data =3D { .ops =3D { .vcp_is_ready =3D is_vcp_ready, + .vcp_is_suspending =3D is_vcp_suspending, .vcp_register_feature =3D vcp_A_register_feature, .vcp_deregister_feature =3D vcp_A_deregister_feature, .vcp_register_notify =3D vcp_A_register_notify, @@ -458,6 +511,11 @@ static const struct mtk_vcp_of_data mt8196_of_data =3D= { }, }; =20 +static const struct dev_pm_ops mtk_vcp_rproc_pm_ops =3D { + .suspend_noirq =3D mtk_vcp_suspend, + .resume_noirq =3D mtk_vcp_resume, +}; + static const struct of_device_id mtk_vcp_of_match[] =3D { { .compatible =3D "mediatek,mt8196-vcp", .data =3D &mt8196_of_data}, {} @@ -471,6 +529,7 @@ static struct platform_driver mtk_vcp_device =3D { .driver =3D { .name =3D "mtk-vcp", .of_match_table =3D mtk_vcp_of_match, + .pm =3D pm_ptr(&mtk_vcp_rproc_pm_ops), }, }; =20 diff --git a/drivers/remoteproc/mtk_vcp_rproc.h b/drivers/remoteproc/mtk_vc= p_rproc.h index 600715b77124..e3a76e368fd7 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.h +++ b/drivers/remoteproc/mtk_vcp_rproc.h @@ -23,6 +23,7 @@ * @msg_vcp_ready1: core1 ready ipi msg data * @slp_ipi_ack_data: sleep ipi msg data * @feature_enable: feature status count data + * @is_suspending: suspend status flag * @vcp_ready: vcp core status flag * @share_mem_iova: shared memory iova base * @share_mem_size: shared memory size @@ -46,6 +47,7 @@ struct mtk_vcp_of_cluster { u32 msg_vcp_ready1; u32 slp_ipi_ack_data; bool feature_enable[NUM_FEATURE_ID]; + bool is_suspending; bool vcp_ready[VCP_CORE_TOTAL]; dma_addr_t share_mem_iova; size_t share_mem_size; diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remo= teproc/mtk_vcp_public.h index b9e1d86685fd..dbdcf5aa0f99 100644 --- a/include/linux/remoteproc/mtk_vcp_public.h +++ b/include/linux/remoteproc/mtk_vcp_public.h @@ -100,6 +100,7 @@ struct mtk_vcp_ipi_ops { }; =20 struct mtk_vcp_ops { + bool (*vcp_is_suspending)(struct mtk_vcp_device *vcp); bool (*vcp_is_ready)(struct mtk_vcp_device *vcp, enum vcp_feature_id id); int (*vcp_register_feature)(struct mtk_vcp_device *vcp, --=20 2.46.0