From nobody Thu Oct 2 17:00:08 2025 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 5B15D263F2D; Sun, 14 Sep 2025 12:30:32 +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=1757853036; cv=none; b=S317hYfn7UGaTReNiYZucqywcK1rkoJ87L84UV8j9ABMye+CIY1Wb3a5wzj1dl+FE7S2LhrZbiVwYAouveLuQfE4yTczigAzxJxQgndeobdwdrNWGvnvC5bOsKnuA/Juu31+xBwu0W3yoS/u6eiuc2+6G/oqubQljmBOgK+H10Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757853036; c=relaxed/simple; bh=6c+Q5WHAR7lHSD0QHQFxc9FZVeyYCOXy8sFMikW64jo=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=aJV35KSXQ2nXVdvoFnqlK76GyoMak+DysyjLbeSH6RkLMCZIVm5AGwV7J7lOjgFIpQhuKJy59rSAqHjcxlPA54rNaHMXHPX5aIQRdjClHAJUU72uRPVgIWVFjuK+YtqNA92R1N722IUf3cQrWoPQ+sYqoF9P8mU/0cz41K6f/wA= 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=Z6ZBCvan; 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="Z6ZBCvan" X-UUID: 9548ad58916611f0bd5779446731db89-20250914 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=o/LnUjycYKhLdST3uiQvKDxrDNE1WnmNJQyIxHDuaAM=; b=Z6ZBCvan1VUccTFaCV/87G2BY2/N8qKt+Y3uHObGy85QbtGJ98LqSwZ5oHbN1TA0VvPUiP8QipwDZsowdTkCyicLJAIJuLDfJR8gajbMsOGDjeDIT7KXqJGemv3FEaHjEN4eYaOjy0PscRoNDKE55cilBYrFCOu1q6UBSn1HAt4=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.4,REQID:90856e75-c08e-4d34-9097-93721d80cf82,IP:0,UR L: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:1ca6b93,CLOUDID:5a1b886c-8443-424b-b119-dc42e68239b0,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102,TC:-5,Content:0|15|50,EDM: -3,IP:nil,URL:97|99|83|106|11|1,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,CO L: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,TF_CID_SPAM_ULN X-CID-RHF: D41D8CD98F00B204E9800998ECF8427E X-UUID: 9548ad58916611f0bd5779446731db89-20250914 Received: from mtkmbs10n2.mediatek.inc [(172.21.101.183)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 508620035; Sun, 14 Sep 2025 20:30:22 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) 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.1258.39; Sun, 14 Sep 2025 20:30:18 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1258.39 via Frontend Transport; Sun, 14 Sep 2025 20:30:17 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , Jjian Zhou , Hailong Fan , Xiangzhi Tang Subject: [PATCH v2 1/4] dt-bindings: remoteproc: Add VCP support for mt8196 Date: Sun, 14 Sep 2025 20:29:24 +0800 Message-ID: <20250914122943.10412-2-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20250914122943.10412-1-xiangzhi.tang@mediatek.com> References: <20250914122943.10412-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 | 165 ++++++++++++++++++ 1 file changed, 165 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..71a55943843b --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/mediatek,mt8196-vcp.yaml @@ -0,0 +1,165 @@ +# 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 + - description: vcp rdy group IO + + reg-names: + items: + - const: sram + - const: cfg + - const: cfg_core + - const: cfg_sec + - const: vcp_vlp_ao_rsvd7 + + 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>, + <0x1c00091c 0x4>; + reg-names =3D "sram", + "cfg", + "cfg_core", + "cfg_sec", + "vcp_vlp_ao_rsvd7"; + + 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 Thu Oct 2 17:00:08 2025 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 0054618FC97; Sun, 14 Sep 2025 12:30:30 +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=1757853034; cv=none; b=aOdESZ4c731ypykHhJjhbc6wUbxJK61CKBGEmg19xAoJeDonpLVRB/yFc4AMboJrZ9Ct7ABfjz4wcQzQFhP7yxLIQz9FUvdW/y5lizCKcG9h7V5S1S4b1/IXcz9wjKmhme83h9KCiL/P6KAvIr7zuFKm/bfaZwOAVy+0SIHw9eg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757853034; c=relaxed/simple; bh=UrvuNsH3gBMtUVbJHL/HJ24Pa4HxLurnB8/QMRsPoaM=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=bSh+EuCWFmLzOTPTM5aR+h4OLxbdifIJPyVShQh+MaR4cQyImQ3PC4HNIVNGoxjwb0vhfjrh9wSMQM+G2h1+kpwqB/Yn7RY2yXZZTnez0rj4YH9YYCQuqKDOWaUWxXiA/A+4zJojXE6jznYBqJDN4CXbM6doZux2Pz/RtE1oGK4= 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=YPZpuOZJ; 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="YPZpuOZJ" X-UUID: 97047cda916611f0bd5779446731db89-20250914 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=vrdCVFghaAThoyo5lGPa7tjMtLc63Skyas4mV7BHCFQ=; b=YPZpuOZJnvrDaqk52Fo7ZyTCyHiBQoJtAjqiaYq2AG2K1PFCnOAaLZF8kShf+XSrAMZrsu/lRJYr1BlHxGrCFAxD3OZs6oSvQIn/awS38rhkItS4g7J1I9HcKu5BdZfIPvam3FaZ6o94oKXU2ta1AME01xAXVBltUwe8UXmkcZU=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.4,REQID:dee7ed55-bee7-4589-9e88-7cadebe3e4d6,IP:0,UR L:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION:r elease,TS:0 X-CID-META: VersionHash:1ca6b93,CLOUDID:dabe46f8-ebfe-43c9-88c9-80cb93f22ca4,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102,TC:-5,Content:0|15|50,EDM: -3,IP:nil,URL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,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: 97047cda916611f0bd5779446731db89-20250914 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 2028409279; Sun, 14 Sep 2025 20:30:25 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) 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.1258.39; Sun, 14 Sep 2025 20:30:21 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1258.39 via Frontend Transport; Sun, 14 Sep 2025 20:30:20 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , Jjian Zhou , Hailong Fan , Xiangzhi Tang Subject: [PATCH v2 2/4] remoterpoc: mediatek: vcp: Add vcp remoteproc driver Date: Sun, 14 Sep 2025 20:29:25 +0800 Message-ID: <20250914122943.10412-3-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20250914122943.10412-1-xiangzhi.tang@mediatek.com> References: <20250914122943.10412-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 host driver to control the mediatek Risc-V coprocessor 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 | 677 ++++++++++++++++++++++ drivers/remoteproc/mtk_vcp_common.h | 225 +++++++ drivers/remoteproc/mtk_vcp_rproc.c | 326 +++++++++++ drivers/remoteproc/mtk_vcp_rproc.h | 71 +++ include/linux/remoteproc/mtk_vcp_public.h | 78 +++ include/linux/soc/mediatek/mtk_sip_svc.h | 3 + 8 files changed, 1393 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 48a0d3a69ed0..1a9bb49a8a28 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -64,6 +64,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..d53e311c4981 --- /dev/null +++ b/drivers/remoteproc/mtk_vcp_common.c @@ -0,0 +1,677 @@ +// 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 "mtk_vcp_common.h" +#include "mtk_vcp_rproc.h" + +struct vcp_feature_tb feature_table[NUM_FEATURE_ID] =3D { + { + .feature =3D RTOS_FEATURE_ID, + .core_id =3D VCP_CORE_TOTAL, + }, + { + .feature =3D VDEC_FEATURE_ID, + .core_id =3D VCP_ID, + }, + { + .feature =3D VENC_FEATURE_ID, + .core_id =3D VCP_ID, + }, + { + .feature =3D MMDVFS_MMUP_FEATURE_ID, + .core_id =3D MMUP_ID, + }, + { + .feature =3D MMDVFS_VCP_FEATURE_ID, + .core_id =3D VCP_ID, + }, + { + .feature =3D MMDEBUG_FEATURE_ID, + .core_id =3D MMUP_ID, + }, + { + .feature =3D VMM_FEATURE_ID, + .core_id =3D MMUP_ID, + }, + { + .feature =3D VDISP_FEATURE_ID, + .core_id =3D MMUP_ID, + }, + { + .feature =3D MMQOS_FEATURE_ID, + .core_id =3D VCP_ID, + }, +}; + +static struct vcp_reserve_mblock vcp_reserve_mblock[] =3D { + { + .num =3D VCP_RTOS_MEM_ID, + .start_phys =3D 0x0, + .start_iova =3D 0x0, + .start_virt =3D 0x0, + .size =3D 0x0, + }, + { + .num =3D VDEC_MEM_ID, + .start_phys =3D 0x0, + .start_iova =3D 0x0, + .start_virt =3D 0x0, + .size =3D 0x0, + }, + { + .num =3D VENC_MEM_ID, + .start_phys =3D 0x0, + .start_iova =3D 0x0, + .start_virt =3D 0x0, + .size =3D 0x0, + }, + { + .num =3D MMDVFS_VCP_MEM_ID, + .start_phys =3D 0x0, + .start_iova =3D 0x0, + .start_virt =3D 0x0, + .size =3D 0x0, + }, + { + .num =3D MMDVFS_MMUP_MEM_ID, + .start_phys =3D 0x0, + .start_iova =3D 0x0, + .start_virt =3D 0x0, + .size =3D 0x0, + }, + { + .num =3D MMQOS_MEM_ID, + .start_phys =3D 0x0, + .start_iova =3D 0x0, + .start_virt =3D 0x0, + .size =3D 0x0, + }, +}; + +phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id) +{ + if (id >=3D 0 && id < NUMS_MEM_ID) + return vcp_reserve_mblock[id].start_phys; + + return 0; +} + +dma_addr_t vcp_get_reserve_mem_iova(enum vcp_reserve_mem_id_t id) +{ + if (id >=3D 0 && id < NUMS_MEM_ID) + return vcp_reserve_mblock[id].start_iova; + + return 0; +} + +void __iomem *vcp_get_reserve_mem_virt(enum vcp_reserve_mem_id_t id) +{ + if (id >=3D 0 && id < NUMS_MEM_ID) + return vcp_reserve_mblock[id].start_virt; + + return NULL; +} + +u32 vcp_get_reserve_mem_size(enum vcp_reserve_mem_id_t id) +{ + if (id >=3D 0 && id < NUMS_MEM_ID) + return vcp_reserve_mblock[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_ioremap(struct mtk_vcp_device *vcp) +{ +#define MEMORY_TBL_ELEM_NUM (2) + u32 num =3D (u32)(sizeof(vcp_reserve_mblock) + / sizeof(vcp_reserve_mblock[0])); + enum vcp_reserve_mem_id_t id; + u32 vcp_mem_num; + u32 i, m_idx, m_size; + u32 offset; + struct device_node *rmem_node; + struct resource res; + struct iommu_domain *domain; + void __iomem *share_memory_virt; + phys_addr_t mblock_start_phys; + dma_addr_t share_memory_iova; + size_t share_memory_size; + int ret; + + if (num !=3D NUMS_MEM_ID) { + dev_err(vcp->dev, "actual memory num(%u) is not match mem ID table (%u)\= n", + num, NUMS_MEM_ID); + WARN_ON(1); + return -EINVAL; + } + + rmem_node =3D of_parse_phandle(vcp->dev->of_node, "memory-region", 0); + if (!rmem_node) { + dev_err(vcp->dev, "No reserved memory region found.\n"); + return -EINVAL; + } + + ret =3D of_address_to_resource(rmem_node, 0, &res); + if (ret) { + dev_err(vcp->dev, "failed to parse reserved memory: %d\n", ret); + return ret; + } + + mblock_start_phys =3D (phys_addr_t)res.start; + + /* Set reserved memory table */ + vcp_mem_num =3D of_property_count_u32_elems(vcp->dev->of_node, "vcp-mem-t= bl") + / MEMORY_TBL_ELEM_NUM; + if (vcp_mem_num <=3D 0) { + dev_warn(vcp->dev, "vcp-mem-tbl not found\n"); + vcp_mem_num =3D 0; + } + + for (i =3D 0; i < vcp_mem_num; i++) { + ret =3D of_property_read_u32_index(vcp->dev->of_node, "vcp-mem-tbl", + i * MEMORY_TBL_ELEM_NUM, &m_idx); + if (ret) { + dev_err(vcp->dev, "cannot get memory index(%d)\n", i); + return -EINVAL; + } + + ret =3D of_property_read_u32_index(vcp->dev->of_node, "vcp-mem-tbl", + (i * MEMORY_TBL_ELEM_NUM) + 1, &m_size); + if (ret) { + dev_err(vcp->dev, "Cannot get memory size(%d)(%d)\n", i, m_idx); + return -EINVAL; + } + + if (m_idx >=3D NUMS_MEM_ID) { + dev_warn(vcp->dev, "skip unexpected index, %d\n", m_idx); + continue; + } + + vcp_reserve_mblock[m_idx].size =3D m_size; + } + + vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys =3D mblock_start_phys; + vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_virt =3D devm_ioremap(vcp->dev, + vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_phys, + vcp_reserve_mblock[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_reserve_mblock[VCP_RTOS_MEM_ID].start_phys, + vcp_reserve_mblock[VCP_RTOS_MEM_ID].size, + IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV, GFP_KERNEL); + if (ret) { + dev_err(vcp->dev, "%s iommu map fail, ret:%d.\n", __func__, ret); + return ret; + } + vcp_reserve_mblock[VCP_RTOS_MEM_ID].start_iova =3D vcp->platdata->rtos_st= atic_iova; + + share_memory_size =3D 0; + for (id =3D VDEC_MEM_ID; id < NUMS_MEM_ID; id++) { + if (vcp_reserve_mblock[id].size =3D=3D 0) + continue; + share_memory_size +=3D vcp_reserve_mblock[id].size; + } + + ret =3D dma_set_mask_and_coherent(vcp->dev, DMA_BIT_MASK(DMA_MAX_MASK_BIT= )); + if (ret) { + dev_err(vcp->dev, "64-bit DMA enable failed\n"); + return ret; + } + + 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 { + dev_err(vcp->dev, "Failed to set DMA parms\n"); + return -EINVAL; + } + } + share_memory_virt =3D dma_alloc_coherent(vcp->dev, + share_memory_size, + &share_memory_iova, + GFP_KERNEL); + if (!share_memory_virt) + return -ENOMEM; + offset =3D 0; + for (id =3D VDEC_MEM_ID; id < NUMS_MEM_ID; id++) { + if (vcp_reserve_mblock[id].size =3D=3D 0) + continue; + + vcp_reserve_mblock[id].start_phys =3D vcp_reserve_mblock[VCP_RTOS_MEM_ID= ].start_phys + + vcp_reserve_mblock[VCP_RTOS_MEM_ID].size + + offset; + vcp_reserve_mblock[id].start_iova =3D share_memory_iova + offset; + vcp_reserve_mblock[id].start_virt =3D share_memory_virt + offset; + offset +=3D (u32)vcp_reserve_mblock[id].size; + } + + vcp->vcp_cluster->share_mem_iova =3D share_memory_iova; + vcp->vcp_cluster->share_mem_size =3D share_memory_size; + + return 0; +} + +static bool is_vcp_ready_by_coreid(enum vcp_core_id core_id) +{ + struct device *dev; + struct mtk_vcp_device *vcp; + + dev =3D feature_table[RTOS_FEATURE_ID].priv; + if (!dev) + return false; + vcp =3D platform_get_drvdata(to_platform_device(dev)); + if (!vcp) + return false; + + 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]; + } +} + +u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp, + enum vcp_core_id core_id) +{ + u32 retry; + bool twohart_support; + u32 core_hart0; + u32 core_hart1; + + twohart_support =3D vcp->vcp_cluster->twohart[core_id]; + + for (retry =3D VCP_AWAKE_TIMEOUT; retry > 0; retry--) { + switch (core_id) { + case VCP_ID: + core_hart0 =3D readl(vcp->vcp_cluster->cfg + VCP_C0_GPR5_H0_REBOOT); + if (twohart_support) + core_hart1 =3D readl(vcp->vcp_cluster->cfg + VCP_C0_GPR6_H1_REBOOT); + break; + case MMUP_ID: + core_hart0 =3D readl(vcp->vcp_cluster->cfg + VCP_C1_GPR5_H0_REBOOT); + if (twohart_support) + core_hart1 =3D readl(vcp->vcp_cluster->cfg + VCP_C1_GPR6_H1_REBOOT); + break; + case VCP_CORE_TOTAL: + default: + break; + } + + if (twohart_support) { + if (core_hart0 =3D=3D CORE_RDY_TO_REBOOT && + core_hart1 =3D=3D CORE_RDY_TO_REBOOT) + break; + } else { + if (core_hart0 =3D=3D CORE_RDY_TO_REBOOT) + break; + } + usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX); + } + + return retry; +} + +static int reset_vcp(struct mtk_vcp_device *vcp) +{ + struct arm_smccc_res res; + + if (vcp->vcp_cluster->core_nums >=3D MMUP_ID) { + /* write vcp reserved memory address/size to GRP1/GRP2 + * to let vcp setup MPU + */ + 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); + } + + /* write vcp reserved memory address/size to GRP1/GRP2 + * to let vcp setup MPU + */ + 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 int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id i= d) +{ + mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + if (vcp->vcp_cluster->pwclkcnt =3D=3D 0) { + if (!is_vcp_ready_by_coreid(VCP_CORE_TOTAL)) { + if (reset_vcp(vcp)) { + mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + return -EINVAL; + } + } + } + vcp->vcp_cluster->pwclkcnt++; + mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + + return 0; +} + +static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id = id) +{ + mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + vcp->vcp_cluster->pwclkcnt--; + if (vcp->vcp_cluster->pwclkcnt < 0) { + for (u32 i =3D 0; i < NUM_FEATURE_ID; i++) + dev_warn(vcp->dev, "%s Check feature id %d enable cnt %d\n", + __func__, feature_table[i].feature, feature_table[i].enable); + vcp->vcp_cluster->pwclkcnt =3D 0; + return -EINVAL; + } + mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + + return 0; +} + +int vcp_A_register_feature(struct mtk_vcp_device *vcp, enum 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); + for (u32 i =3D 0; i < NUM_FEATURE_ID; i++) { + if (feature_table[i].feature =3D=3D id) + feature_table[i].enable++; + if (id =3D=3D RTOS_FEATURE_ID) + feature_table[i].priv =3D vcp->dev; + } + ret =3D vcp_enable_pm_clk(vcp, id); + mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex); + + return ret; +} + +int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, enum feature_id i= d) +{ + int ret; + + if (id >=3D NUM_FEATURE_ID) { + dev_warn(vcp->dev, "%s unsupported feature id %d\n", + __func__, id); + return -EINVAL; + } + mutex_lock(&vcp->vcp_cluster->vcp_feature_mutex); + for (u32 i =3D 0; i < NUM_FEATURE_ID; i++) { + if (feature_table[i].feature =3D=3D id) { + if (feature_table[i].enable =3D=3D 0) { + dev_warn(vcp->dev, "%s unbalanced feature id %d enable cnt %d\n", + __func__, id, feature_table[i].enable); + mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex); + return -EINVAL; + } + feature_table[i].enable--; + } + } + ret =3D vcp_disable_pm_clk(vcp, id); + mutex_unlock(&vcp->vcp_cluster->vcp_feature_mutex); + + return ret; +} + +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 mkimg_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 mkimg_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_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_RTOS_MEM_ID); + img_buf_phys =3D vcp->ops->vcp_get_mem_phys(VCP_RTOS_MEM_ID); + img_buf_va =3D vcp->ops->vcp_get_mem_virt(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(vcp->dev, "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..8340f0bd4fdc --- /dev/null +++ b/drivers/remoteproc/mtk_vcp_common.h @@ -0,0 +1,225 @@ +/* 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 VCP_AWAKE_TIMEOUT 1000 +#define USDELAY_RANGE_MIN 1000 +#define USDELAY_RANGE_MAX 2000 + +/* vcp platform define */ +#define DMA_MAX_MASK_BIT 33 + +/* vcp load image define */ +#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 define */ +#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 register define */ +#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)) + +#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_FLAG (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_FLAG (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) + +/* sec GPR */ +#define R_GPR2_SEC (0x0008) +#define MMUP_AP_SUSPEND (BIT(0)) +#define R_GPR3_SEC (0x000C) +#define VCP_AP_SUSPEND (BIT(0)) + +/* vcp rdy */ +#define VLP_AO_RSVD7 (0x0000) +#define READY_BIT (BIT(1)) + +/* vcp Core ID definition */ +enum vcp_core_id { + VCP_ID =3D 0, + MMUP_ID =3D 1, + VCP_CORE_TOTAL =3D 2, +}; + +/* vcp kernel smc server id */ +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 mkimg_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 mkimg_hdr { + u32 magic; + u32 dsz; + char name[VCM_IMAGE_NAME_MAXSZ]; +}; + +/** + * struct vcp_feature_tb - feature table structure definition. + * + * @feature: feature id + * @core_id: feature using vcp core id + * @enable: whether the feature is enabled or not + */ +struct vcp_feature_tb { + enum vcp_core_id core_id; + u32 feature; + u32 enable; + void *priv; +}; + +/** + * struct vcp_reserve_mblock - vcp reserved memory structure. + * + * @vcp_reserve_mem_id_t: reserved memory id + * @start_phys: reserved memory phy addr + * @start_iova: reserved memory dma map addr + * @start_virt: reserved memory CPU virt addr + * @size: reserved memory size + */ +struct vcp_reserve_mblock { + enum vcp_reserve_mem_id_t num; + phys_addr_t start_phys; + dma_addr_t start_iova; + void __iomem *start_virt; + size_t size; +}; + +/** + * struct vcp_region_info_st - config vcp image info sync to vcp bootloade= r. + * + * @ap_loader_start: (optional) - config vcp bootloader to copy loader sta= rt addr + * @ap_loader_size: (optional) - config vcp bootloader to copy loader size + * @ap_firmware_start: (optional) - config vcp bootloader to copy firmware= start addr + * @ap_firmware_size: (optional) - 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: (optional) - vcp task context ptr for debug + * @vcpctl: (optional) - vcp control info + * @regdump_start: (optional) - regdump start addr for debug + * @regdump_size: (optional) - regdump size for debug + * @ap_params_start: (optional) - params start addr + * @sramlog_buf_offset: (optional) - sramlog_buf_offset for debug + * @sramlog_end_idx_offset: (optional) - sramlog_end_idx_offset for debug + * @sramlog_buf_maxlen: (optional) - sramlog_buf_maxlen for debug + * @ap_loader_start_pa: (optional) - config vcp bootloader for loader star= t pa + * @coredump_offset: (optional) - coredump_offset offset for debug + * @coredump_dram_offset: (optional) - coredump_dram_offset offset for deb= ug + */ +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; +}; + +/* vcp common reserved memory APIs */ +int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp); +phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id); +dma_addr_t vcp_get_reserve_mem_iova(enum vcp_reserve_mem_id_t id); +void __iomem *vcp_get_reserve_mem_virt(enum vcp_reserve_mem_id_t id); +void __iomem *vcp_get_internal_sram_virt(struct mtk_vcp_device *vcp); +u32 vcp_get_reserve_mem_size(enum vcp_reserve_mem_id_t id); + +/* vcp common load image API */ +int mtk_vcp_load(struct rproc *rproc, const struct firmware *fw); + +/* vcp common wdt irq init API */ +int vcp_wdt_irq_init(struct mtk_vcp_device *vcp); + +/* vcp common feature register/deregister APIs */ +int vcp_A_register_feature(struct mtk_vcp_device *vcp, + enum feature_id id); +int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, + enum feature_id id); + +/* vcp common core hart shutdown API */ +u32 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..bf4736ce6795 --- /dev/null +++ b/drivers/remoteproc/mtk_vcp_rproc.c @@ -0,0 +1,326 @@ +// 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); + +/** + * vcp_put() - "free" the VCP + * + * @vcp: mtk_vcp_device structure from 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); + + if (vcp_A_register_feature(vcp, RTOS_FEATURE_ID) < 0) { + dev_err(vcp->dev, "bootup fail\n"); + vcp_A_deregister_feature(vcp, RTOS_FEATURE_ID); + } else { + dev_info(vcp->dev, "bootup successfully\n"); + } + + return 0; +} + +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) { + dev_err(&pdev->dev, "failed to twohart property\n"); + return ret; + } + ret =3D of_property_read_u32(pdev->dev.of_node, "mtk,core-sram-offset", + &vcp_cluster->sram_offset[core_id]); + if (ret) { + dev_err(&pdev->dev, "failed to sram-offset property\n"); + return ret; + } + + 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) { + dev_err(dev, "unable to allocate remoteproc\n"); + return ERR_PTR(-ENOMEM); + } + + 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; + mutex_init(&vcp->vcp_cluster->vcp_feature_mutex); + mutex_init(&vcp->vcp_cluster->vcp_pw_clk_mutex); + platform_set_drvdata(pdev, vcp); + + ret =3D vcp_reserve_memory_ioremap(vcp); + if (ret) { + dev_err(dev, "vcp_reserve_memory_ioremap failed ret =3D %d\n", ret); + return ERR_PTR(ret); + } + + 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) { + ret =3D -ENODEV; + dev_err(dev, "Not found platform device for core\n"); + return ERR_PTR(ret); + } + 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) { + dev_err(dev, "vcp_wdt_irq_init failed\n"); + return ERR_PTR(ret); + } + + 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 PTR_ERR(vcp); + + ret =3D rproc_add(vcp->rproc); + if (ret) { + dev_err(vcp->dev, "Failed to add rproc\n"); + rproc_del(vcp->rproc); + } + + 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 -ENOMEM; + + 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"); + + vcp_cluster->vcp_rdy =3D devm_platform_ioremap_resource_byname(pdev, "vcp= _vlp_ao_rsvd7"); + if (IS_ERR(vcp_cluster->vcp_rdy)) + return dev_err_probe(dev, PTR_ERR(vcp_cluster->vcp_rdy), + "Failed to parse and map vcp_rdy 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); + u32 ret; + + writel(GIPC_VCP_HART0_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + if (vcp->vcp_cluster->core_nums > VCP_ID) { + ret =3D wait_core_hart_shutdown(vcp, VCP_ID); + if (!ret) + dev_err(&pdev->dev, + "wait VCP_ID core hart shutdown timeout\n"); + else + writel(GIPC_MMUP_SHUT, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + } +} + +static const struct mtk_vcp_of_data mt8196_of_data =3D { + .ops =3D { + .vcp_register_feature =3D vcp_A_register_feature, + .vcp_deregister_feature =3D vcp_A_deregister_feature, + .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, + .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..6c7a99bf919b --- /dev/null +++ b/drivers/remoteproc/mtk_vcp_rproc.h @@ -0,0 +1,71 @@ +/* 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 + * @vcp_rdy: vlp vcp_rdy 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 + * @pwclkcnt: power and clock config count data + * @share_mem_iova: shared memory iova base + * @share_mem_size: shared memory size + * @vcp_feature_mutex: vcp feature register mutex structure + * @vcp_pw_clk_mutex: vcp feature lock pw_clk mutex structure + */ +struct mtk_vcp_of_cluster { + void __iomem *sram_base; + void __iomem *cfg; + void __iomem *cfg_sec; + void __iomem *cfg_core; + void __iomem *vcp_rdy; + u32 sram_size; + u32 core_nums; + u32 twohart[VCP_CORE_TOTAL]; + u32 sram_offset[VCP_CORE_TOTAL]; + int pwclkcnt; + dma_addr_t share_mem_iova; + size_t share_mem_size; + struct mutex vcp_feature_mutex; + struct mutex vcp_pw_clk_mutex; +}; + +/** + * 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 + * @fw_name: vcp image name and path + */ +struct mtk_vcp_platdata { + bool auto_boot; + bool sysfs_read_only; + dma_addr_t rtos_static_iova; + 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..b4de5e5d63d8 --- /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 + +/* vcp reserve memory ID definition */ +enum vcp_reserve_mem_id_t { + VCP_RTOS_MEM_ID, + VDEC_MEM_ID, + VENC_MEM_ID, + MMDVFS_VCP_MEM_ID, + MMDVFS_MMUP_MEM_ID, + MMQOS_MEM_ID, + NUMS_MEM_ID, +}; + +/* vcp feature ID list */ +enum 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 { + int (*vcp_register_feature)(struct mtk_vcp_device *vcp, enum feature_id i= d); + int (*vcp_deregister_feature)(struct mtk_vcp_device *vcp, enum feature_id= id); + phys_addr_t (*vcp_get_mem_phys)(enum vcp_reserve_mem_id_t id); + dma_addr_t (*vcp_get_mem_iova)(enum vcp_reserve_mem_id_t id); + void __iomem *(*vcp_get_mem_virt)(enum vcp_reserve_mem_id_t id); + uint32_t (*vcp_get_mem_size)(enum vcp_reserve_mem_id_t 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..d65a59752979 100644 --- a/include/linux/soc/mediatek/mtk_sip_svc.h +++ b/include/linux/soc/mediatek/mtk_sip_svc.h @@ -28,4 +28,7 @@ /* IOMMU related SMC call */ #define MTK_SIP_KERNEL_IOMMU_CONTROL MTK_SIP_SMC_CMD(0x514) =20 +/* VCP */ +#define MTK_SIP_TINYSYS_VCP_CONTROL MTK_SIP_SMC_CMD(0x52C) + #endif --=20 2.46.0 From nobody Thu Oct 2 17:00:08 2025 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 F043525FA34; Sun, 14 Sep 2025 12:30:40 +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=1757853043; cv=none; b=uhlBW3Ia4GvOquMh5Eim/ebys6iCI/dMU6CcHqB4GIULqqpi/T6e1djUeLld8oFciRMQaky9yBfHWaURTv8+iDPtYjbCQR3kDVZVTsUoqi1u0zlPpRm1qqAeZVfbcVnjyKbkIVIhubErvjYni/iR8tFbDld5A3jULFKje4lHA1Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757853043; c=relaxed/simple; bh=G79RiM9cQHNvw6IAEG+TdoM0kQVC7Qfn5vvq+qHgEH4=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=DjhLPhSpbMn7uSN1d3oBldKl6IL0HeTlp9W3pKmTitC52pd5uq2Ygg8fYf77fBd6Z7IA+SAJ15iC3ZzOatjfk5+btzdukgPgJ9i4oITSIydVCFhNk2N2goiZkfQjQKK6Zj5liCwqAljbB0qESz7Ih9Lu5Uuolmnc3ypqGHMK4XM= 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=BHMmKagL; 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="BHMmKagL" X-UUID: 987e8dbc916611f0b33aeb1e7f16c2b6-20250914 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=HetiFWZ9ZAVvgB4OozI3melU/YG2JfU1pdzrWadRXqE=; b=BHMmKagLaX1JAdtu3/3xSjNZBivKeMwfWUuEP8a6jAMxLhsEyKFd4Lx40H/CtIoOLA2Vp1dzPB1nuo6bCV66VrMiLq7iLKG9xGLdy/2TlNtofm+VzrRIAg8+I9CxpiP8KsOsyNfvH8Xsk5DYbHmmU/CZ1/SL/RA529sFMrU2+sg=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.4,REQID:bcb5d8ae-bb26-4d3a-abdb-f3819cb5014a,IP:0,UR L:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION:r elease,TS:0 X-CID-META: VersionHash:1ca6b93,CLOUDID:ecbe46f8-ebfe-43c9-88c9-80cb93f22ca4,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102,TC:-5,Content:0|15|50,EDM: -3,IP:nil,URL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,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: 987e8dbc916611f0b33aeb1e7f16c2b6-20250914 Received: from mtkmbs09n2.mediatek.inc [(172.21.101.94)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 1088861652; Sun, 14 Sep 2025 20:30:27 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) 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.1258.39; Sun, 14 Sep 2025 20:30:24 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1258.39 via Frontend Transport; Sun, 14 Sep 2025 20:30:23 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , Jjian Zhou , Hailong Fan , Xiangzhi Tang Subject: [PATCH v2 3/4] remoterpoc: mediatek: vcp: Add ipi-mbox communication Date: Sun, 14 Sep 2025 20:29:26 +0800 Message-ID: <20250914122943.10412-4-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20250914122943.10412-1-xiangzhi.tang@mediatek.com> References: <20250914122943.10412-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 communication mechanism between AP CPU with VCP mcu 1.Add VCP ipi-mbox init driver. 2.Add vcp ready ipi register driver. 3.Add vcp ready notify work mechanism. Signed-off-by: Xiangzhi Tang --- drivers/remoteproc/Kconfig | 2 + drivers/remoteproc/mtk_vcp_common.c | 163 ++++++++++++++++++++++ drivers/remoteproc/mtk_vcp_common.h | 30 ++++ drivers/remoteproc/mtk_vcp_rproc.c | 154 +++++++++++++++++++- drivers/remoteproc/mtk_vcp_rproc.h | 18 +++ include/linux/remoteproc/mtk_vcp_public.h | 62 ++++++++ 6 files changed, 428 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 1a9bb49a8a28..90fdc632ff2f 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -68,6 +68,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_common.c b/drivers/remoteproc/mtk_v= cp_common.c index d53e311c4981..9767f5ff15a0 100644 --- a/drivers/remoteproc/mtk_vcp_common.c +++ b/drivers/remoteproc/mtk_vcp_common.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -23,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); + struct vcp_feature_tb feature_table[NUM_FEATURE_ID] =3D { { .feature =3D RTOS_FEATURE_ID, @@ -302,6 +306,23 @@ static bool is_vcp_ready_by_coreid(enum vcp_core_id co= re_id) } } =20 +static enum vcp_core_id get_core_by_feature(enum feature_id id) +{ + for (u32 i =3D 0; i < NUM_FEATURE_ID; i++) { + if (feature_table[i].feature =3D=3D id) + return feature_table[i].core_id; + } + + return 0; +} + +bool is_vcp_ready(enum feature_id id) +{ + enum vcp_core_id core_id =3D get_core_by_feature(id); + + return is_vcp_ready_by_coreid(core_id); +} + u32 wait_core_hart_shutdown(struct mtk_vcp_device *vcp, enum vcp_core_id core_id) { @@ -343,9 +364,114 @@ u32 wait_core_hart_shutdown(struct mtk_vcp_device *vc= p, return retry; } =20 +void vcp_A_register_notify(enum feature_id id, + struct notifier_block *nb) +{ + enum vcp_core_id core_id =3D get_core_by_feature(id); + + switch (core_id) { + case VCP_ID: + blocking_notifier_chain_register(&vcp_notifier_list, nb); + if (is_vcp_ready_by_coreid(VCP_ID)) + nb->notifier_call(nb, VCP_EVENT_READY, NULL); + break; + case MMUP_ID: + blocking_notifier_chain_register(&mmup_notifier_list, nb); + if (is_vcp_ready_by_coreid(MMUP_ID)) + nb->notifier_call(nb, VCP_EVENT_READY, NULL); + break; + default: + break; + } +} + +void vcp_A_unregister_notify(enum feature_id id, + struct notifier_block *nb) +{ + enum vcp_core_id core_id =3D get_core_by_feature(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: + 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_warn(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 (!is_vcp_ready_by_coreid(VCP_ID)) + vcp_A_set_ready(vcp, VCP_ID); + break; + case IPI_IN_VCP_READY_1: + if (!is_vcp_ready_by_coreid(MMUP_ID)) + vcp_A_set_ready(vcp, MMUP_ID); + break; + default: + break; + } + + return 0; +} + static int reset_vcp(struct mtk_vcp_device *vcp) { struct arm_smccc_res res; + bool mmup_status, vcp_status; =20 if (vcp->vcp_cluster->core_nums >=3D MMUP_ID) { /* write vcp reserved memory address/size to GRP1/GRP2 @@ -359,6 +485,16 @@ static 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); + + read_poll_timeout_atomic(is_vcp_ready_by_coreid, + mmup_status, mmup_status, + USEC_PER_MSEC, + VCP_READY_TIMEOUT_MS * USEC_PER_MSEC, + false, MMUP_ID); + if (!mmup_status) { + dev_err(vcp->dev, "MMUP_ID bootup timeout. Stop vcp booting\n"); + return -EINVAL; + } } =20 /* write vcp reserved memory address/size to GRP1/GRP2 @@ -372,6 +508,17 @@ static int reset_vcp(struct mtk_vcp_device *vcp) arm_smccc_smc(MTK_SIP_TINYSYS_VCP_CONTROL, MTK_TINYSYS_VCP_KERNEL_OP_RESET_RELEASE, 1, 0, 0, 0, 0, 0, &res); + + read_poll_timeout_atomic(is_vcp_ready_by_coreid, + vcp_status, vcp_status, + USEC_PER_MSEC, + VCP_READY_TIMEOUT_MS * USEC_PER_MSEC, + false, VCP_ID); + if (!vcp_status) { + dev_err(vcp->dev, "VCP_ID bootup timeout. Stop vcp booting\n"); + return -EINVAL; + } + return 0; } =20 @@ -458,6 +605,22 @@ int vcp_A_deregister_feature(struct mtk_vcp_device *vc= p, enum feature_id id) return ret; } =20 +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) { + dev_err(vcp->dev, "vcp_workqueue create fail\n"); + return -EINVAL; + } + + 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; +} + static size_t load_part_binary(void __iomem *image_buf, const u8 *fw_src, size_t size, diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_v= cp_common.h index 8340f0bd4fdc..4a4393b2ae1f 100644 --- a/drivers/remoteproc/mtk_vcp_common.h +++ b/drivers/remoteproc/mtk_vcp_common.h @@ -14,6 +14,8 @@ =20 /* vcp timeout definition */ #define VCP_AWAKE_TIMEOUT 1000 +#define VCP_READY_TIMEOUT_MS 3000 +#define VCP_IPI_DEV_READY_TIMEOUT 1000 #define USDELAY_RANGE_MIN 1000 #define USDELAY_RANGE_MAX 2000 =20 @@ -153,6 +155,21 @@ struct vcp_reserve_mblock { size_t size; }; =20 +/** + * 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. * @@ -200,6 +217,19 @@ struct vcp_region_info_st { u32 coredump_dram_offset; }; =20 +/* vcp common ready signale APIs */ +int vcp_A_ready_ipi_handler(u32 id, void *prdata, + void *data, u32 len); +bool is_vcp_ready(enum feature_id id); + +/* vcp common notify extern APIs */ +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(enum feature_id id, + struct notifier_block *nb); +void vcp_A_unregister_notify(enum feature_id id, + struct notifier_block *nb); /* vcp common reserved memory APIs */ int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp); phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id); diff --git a/drivers/remoteproc/mtk_vcp_rproc.c b/drivers/remoteproc/mtk_vc= p_rproc.c index bf4736ce6795..4aa0ed47abd7 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 @@ -60,10 +61,39 @@ 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; struct arm_smccc_res res; + int ret; + + 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, @@ -82,7 +112,14 @@ static int mtk_vcp_start(struct rproc *rproc) dev_info(vcp->dev, "bootup successfully\n"); } =20 - return 0; + 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); + + return ret; } =20 static int mtk_vcp_stop(struct rproc *rproc) @@ -91,6 +128,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 @@ -100,6 +140,43 @@ 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)) { + ret =3D PTR_ERR(pdev); + dev_err(vcp->dev, "failed to create mtk-vcp-ipc device\n"); + return ret; + } + + 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) { + ret =3D -EPROBE_DEFER; + dev_err(vcp->dev, "failed to get drvdata\n"); + return ret; + } + + ret =3D mtk_vcp_ipc_device_register(vcp->ipi_dev, VCP_IPI_COUNT, vcp_ipc); + if (ret) { + dev_err(vcp->dev, "ipi_dev_register failed, ret %d\n", ret); + return 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) @@ -150,12 +227,15 @@ static struct mtk_vcp_device *vcp_rproc_init(struct p= latform_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; mutex_init(&vcp->vcp_cluster->vcp_feature_mutex); mutex_init(&vcp->vcp_cluster->vcp_pw_clk_mutex); + mutex_init(&vcp->vcp_cluster->vcp_ready_mutex); platform_set_drvdata(pdev, vcp); =20 ret =3D vcp_reserve_memory_ioremap(vcp); @@ -185,6 +265,16 @@ static struct mtk_vcp_device *vcp_rproc_init(struct pl= atform_device *pdev, return ERR_PTR(ret); } =20 + ret =3D vcp_ipi_mbox_init(vcp); + if (ret) { + dev_err(dev, "Failed to init vcp ipi-mbox\n"); + return ERR_PTR(ret); + } + + ret =3D vcp_notify_work_init(vcp); + if (ret) + dev_err(dev, "vcp_notify_work_init failed\n"); + pm_runtime_get_sync(dev); =20 return vcp; @@ -264,6 +354,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); @@ -274,6 +366,12 @@ static void vcp_device_shutdown(struct platform_device= *pdev) struct mtk_vcp_device *vcp =3D platform_get_drvdata(pdev); u32 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); if (vcp->vcp_cluster->core_nums > VCP_ID) { ret =3D wait_core_hart_shutdown(vcp, VCP_ID); @@ -285,8 +383,60 @@ static void vcp_device_shutdown(struct platform_device= *pdev) } } =20 +static struct mtk_mbox_table ipc_table =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_is_ready =3D is_vcp_ready, + .vcp_register_notify =3D vcp_A_register_notify, + .vcp_unregister_notify =3D vcp_A_unregister_notify, .vcp_register_feature =3D vcp_A_register_feature, .vcp_deregister_feature =3D vcp_A_deregister_feature, .vcp_get_mem_phys =3D vcp_get_reserve_mem_phys, @@ -299,6 +449,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 &ipc_table, + .ipi_ops =3D &mt8196_vcp_ipi_ops, .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 6c7a99bf919b..e36612256b63 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.h +++ b/drivers/remoteproc/mtk_vcp_rproc.h @@ -20,11 +20,18 @@ * @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 * @pwclkcnt: power and clock config 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_pw_clk_mutex: vcp feature lock pw_clk mutex structure + * @vcp_ready_mutex: vcp core ready mutex structure + * @vcp_ipidev: struct mtk_ipi_device + * @vcp_workqueue: ready workqueue_struct + * @vcp_ready_notify_wk: vcp_work_struct structure */ struct mtk_vcp_of_cluster { void __iomem *sram_base; @@ -36,11 +43,18 @@ 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; int pwclkcnt; + 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_pw_clk_mutex; + struct mutex vcp_ready_mutex; + struct mtk_ipi_device vcp_ipidev; + struct workqueue_struct *vcp_workqueue; + struct vcp_work_struct vcp_ready_notify_wk[VCP_CORE_TOTAL]; }; =20 /** @@ -49,12 +63,16 @@ 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 * @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_mbox_table *ipc_data; + struct mtk_vcp_ipi_ops *ipi_ops; char *fw_name; }; =20 diff --git a/include/linux/remoteproc/mtk_vcp_public.h b/include/linux/remo= teproc/mtk_vcp_public.h index b4de5e5d63d8..5bd562d1ae62 100644 --- a/include/linux/remoteproc/mtk_vcp_public.h +++ b/include/linux/remoteproc/mtk_vcp_public.h @@ -7,8 +7,19 @@ #define __MTK_VCP_PUBLIC_H__ =20 #include +#include #include =20 +#define VCP_SYNC_TIMEOUT_MS (50) + +/* vcp notify event */ +enum VCP_NOTIFY_EVENT { + VCP_EVENT_READY =3D 0, + VCP_EVENT_STOP, + VCP_EVENT_SUSPEND, + VCP_EVENT_RESUME, +}; + /* vcp reserve memory ID definition */ enum vcp_reserve_mem_id_t { VCP_RTOS_MEM_ID, @@ -38,16 +49,66 @@ enum feature_id { NUM_FEATURE_ID, }; =20 +/* vcp ipi ID list */ +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 rproc_subdev *rpmsg_subdev; + struct mtk_rpmsg_channel_info_mbox *mtk_rpchan; 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 { + bool (*vcp_is_ready)(enum feature_id id); + void (*vcp_register_notify)(enum feature_id id, struct notifier_block *nb= ); + void (*vcp_unregister_notify)(enum feature_id id, struct notifier_block *= nb); int (*vcp_register_feature)(struct mtk_vcp_device *vcp, enum feature_id i= d); int (*vcp_deregister_feature)(struct mtk_vcp_device *vcp, enum feature_id= id); phys_addr_t (*vcp_get_mem_phys)(enum vcp_reserve_mem_id_t id); @@ -59,6 +120,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 Thu Oct 2 17:00:08 2025 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 1442A25FA34; Sun, 14 Sep 2025 12:30:50 +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=1757853055; cv=none; b=o3dzEWEtQ3SN5Vl/7uitAfV3LyVeZV6wXasUNPfM2B/0spW7FrPs3M7gQleV3P3tHMSevh7AsLe87FC9Zci6EHvaZkX8cpkcRoKGIUbijPODDHDZtU8WPIvyyRKQ4pNMatMyPdSBo11B2Dj/A70FRtrqDH0pzikTkuizSdyKDkM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757853055; c=relaxed/simple; bh=+eyaWRZwnJ/LVXTl6E2ao/7V6h9VuFubdUcN+SBNyoc=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=EGLPbtSZZyhSrbYFKpEdFP3drNE0/Ibjm8vPRdA2UPvIeDkkhL4lNqcEji3VA3tIGwm77DVGEcD4nYFi1avx4LaMXLbCWFQECoiWXq6p9tbdrRhEwpa7EwobRHhaUO3a2vRKSgTYzD/iWUUCUKVxNoLeuWKdfnfh1Q2pmOs8nW4= 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=DlZ5DIbe; 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="DlZ5DIbe" X-UUID: a2a7a9ae916611f0bd5779446731db89-20250914 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=Kn7FPyCoQ97bS+YMe8FzRImsW3BTm2Hf2FJ79qnWQGQ=; b=DlZ5DIbeOeNXCdRcp3/6YgMs4moTadQrdMLs6rwQANK2MKIJM8+8T+731Rr2JMlaEg/x0ZXou5/Dt/4qjRLGC6gmHyiPdTsZQPRwBaM2FwQnOhj5aVGPViMG9Z90yovQbpLx66mHEILqWpVzJGEhKmSHK0sHyT4CPoQT0I1M2a4=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.3.4,REQID:9b6e5afa-a045-488a-9465-19c90b4c0c60,IP:0,UR L:0,TC:0,Content:0,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTION:r elease,TS:0 X-CID-META: VersionHash:1ca6b93,CLOUDID:c31b886c-8443-424b-b119-dc42e68239b0,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:81|82|102,TC:-5,Content:0|15|50,EDM: -3,IP:nil,URL:0,File:nil,RT:nil,Bulk:nil,QS:nil,BEC:nil,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: a2a7a9ae916611f0bd5779446731db89-20250914 Received: from mtkmbs13n1.mediatek.inc [(172.21.101.193)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 13609182; Sun, 14 Sep 2025 20:30:45 +0800 Received: from mtkmbs13n1.mediatek.inc (172.21.101.193) by mtkmbs13n1.mediatek.inc (172.21.101.193) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1258.39; Sun, 14 Sep 2025 20:30:38 +0800 Received: from mhfsdcap04.gcn.mediatek.inc (10.17.3.154) by mtkmbs13n1.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1258.39 via Frontend Transport; Sun, 14 Sep 2025 20:30:37 +0800 From: Xiangzhi Tang To: Bjorn Andersson , Mathieu Poirier , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Matthias Brugger , AngeloGioacchino Del Regno , Xiangzhi Tang CC: , , , , , Jjian Zhou , Hailong Fan , Xiangzhi Tang Subject: [PATCH v2 4/4] remoterpoc: mediatek: vcp: Add vcp suspned and resume feature Date: Sun, 14 Sep 2025 20:29:27 +0800 Message-ID: <20250914122943.10412-5-xiangzhi.tang@mediatek.com> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20250914122943.10412-1-xiangzhi.tang@mediatek.com> References: <20250914122943.10412-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 2.Add sleep lock ipi cmd flow for lock vcp status while feature using VCP Signed-off-by: Xiangzhi Tang --- drivers/remoteproc/mtk_vcp_common.c | 175 ++++++++++++++++++++++ drivers/remoteproc/mtk_vcp_common.h | 29 ++++ drivers/remoteproc/mtk_vcp_rproc.c | 66 ++++++++ drivers/remoteproc/mtk_vcp_rproc.h | 4 + include/linux/remoteproc/mtk_vcp_public.h | 1 + 5 files changed, 275 insertions(+) diff --git a/drivers/remoteproc/mtk_vcp_common.c b/drivers/remoteproc/mtk_v= cp_common.c index 9767f5ff15a0..0bd071f73b23 100644 --- a/drivers/remoteproc/mtk_vcp_common.c +++ b/drivers/remoteproc/mtk_vcp_common.c @@ -364,6 +364,127 @@ u32 wait_core_hart_shutdown(struct mtk_vcp_device *vc= p, return retry; } =20 +bool is_vcp_suspending(struct mtk_vcp_device *vcp) +{ + return vcp->vcp_cluster->is_suspending ? true : false; +} + +void vcp_wait_core_stop(struct mtk_vcp_device *vcp, enum vcp_core_id core_= id) +{ + u32 core_halt; + u32 core_axi; + u32 core_status; + u32 status; + + /* make sure vcp is in idle state */ + int timeout =3D SUSPEND_WAIT_TIMEOUT_MS; + + while (--timeout) { + switch (core_id) { + case VCP_ID: + core_status =3D readl(vcp->vcp_cluster->cfg + R_CORE0_STATUS); + status =3D (vcp->vcp_cluster->twohart[VCP_ID] ? + (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT) : + (B_CORE_GATED | B_HART0_HALT)); + break; + case MMUP_ID: + core_status =3D readl(vcp->vcp_cluster->cfg + R_CORE1_STATUS); + status =3D (vcp->vcp_cluster->twohart[MMUP_ID] ? + (B_CORE_GATED | B_HART0_HALT | B_HART1_HALT) : + (B_CORE_GATED | B_HART0_HALT)); + break; + case VCP_CORE_TOTAL: + default: + break; + } + + core_halt =3D ((core_status & status) =3D=3D status); + core_axi =3D core_status & (B_CORE_AXIS_BUSY); + + if (core_halt && !core_axi) { + dev_err(vcp->dev, "[%s] core status 0x%x, GPIC 0x%x flag 0x%x\n", + core_id ? "MMUP_ID" : "VCP_ID", core_status, + readl(vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET), + readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC)); + break; + } + usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX); + } + + if (timeout =3D=3D 0) { + dev_err(vcp->dev, "wait [%s] core stop timeout, current status 0x%x\n", + core_id ? "MMUP_ID" : "VCP_ID", core_status); + } +} + +void vcp_wait_rdy_signal(struct mtk_vcp_device *vcp, bool rdy) +{ + u32 rdy_signal; + int ret; + + if (!IS_ERR((void const *)vcp->vcp_cluster->vcp_rdy)) { + if (rdy) + ret =3D read_poll_timeout_atomic(readl, rdy_signal, rdy_signal & READY_= BIT, + USEC_PER_MSEC, + VCP_SYNC_TIMEOUT_MS * USEC_PER_MSEC, + false, + vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7); + else + ret =3D read_poll_timeout_atomic(readl, rdy_signal, !(rdy_signal & READ= Y_BIT), + USEC_PER_MSEC, + VCP_SYNC_TIMEOUT_MS * USEC_PER_MSEC, + false, + vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7); + if (ret < 0) + dev_err(vcp->dev, "wait vcp %s timeout 0x%x\n", + rdy ? "set rdy bit" : "clr rdy bit", + readl(vcp->vcp_cluster->vcp_rdy + VLP_AO_RSVD7)); + } else { + dev_err(vcp->dev, "illegal vcp rdy signal\n"); + } +} + +void vcp_wait_suspend_resume(struct mtk_vcp_device *vcp, bool suspend) +{ + int timeout =3D SUSPEND_WAIT_TIMEOUT_MS; + + if (suspend) { + writel(B_CORE0_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR2); + writel(B_CORE1_SUSPEND, vcp->vcp_cluster->cfg_core + AP_R_GPR3); + writel(SUSPEND_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RE= SUME_FLAG); + writel(SUSPEND_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RE= SUME_FLAG); + writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + } else { + writel(B_CORE0_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR2); + writel(B_CORE1_RESUME, vcp->vcp_cluster->cfg_core + AP_R_GPR3); + writel(RESUME_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C0_GPR0_SUSPEND_RES= UME_FLAG); + writel(RESUME_IPI_MAGIC, vcp->vcp_cluster->cfg + VCP_C1_GPR0_SUSPEND_RES= UME_FLAG); + writel(B_GIPC4_SETCLR_3, vcp->vcp_cluster->cfg_core + R_GIPC_IN_SET); + } + + while (--timeout) { + if (suspend && + (readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & (VCP_AP_SUSPEND)) && + (readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & (MMUP_AP_SUSPEND))) + break; + else if (!suspend && + !(readl(vcp->vcp_cluster->cfg_sec + R_GPR3_SEC) & (VCP_AP_SUSPEND)) && + !(readl(vcp->vcp_cluster->cfg_sec + R_GPR2_SEC) & (MMUP_AP_SUSPEND))) + break; + usleep_range(USDELAY_RANGE_MIN, USDELAY_RANGE_MAX); + } + if (timeout <=3D 0) { + 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(enum feature_id id, struct notifier_block *nb) { @@ -524,7 +645,23 @@ static int reset_vcp(struct mtk_vcp_device *vcp) =20 static int vcp_enable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id i= d) { + int ret; + bool suspend_status; + struct slp_ctrl_data ipi_data; + mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + read_poll_timeout_atomic(is_vcp_suspending, + suspend_status, !suspend_status, + USEC_PER_MSEC, + SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp); + if (suspend_status) { + dev_warn(vcp->dev, "%s blocked by vcp suspend, pwclkcnt(%d)\n", + __func__, + vcp->vcp_cluster->pwclkcnt); + return -ETIMEDOUT; + } + if (vcp->vcp_cluster->pwclkcnt =3D=3D 0) { if (!is_vcp_ready_by_coreid(VCP_CORE_TOTAL)) { if (reset_vcp(vcp)) { @@ -534,6 +671,17 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vc= p, enum feature_id id) } } vcp->vcp_cluster->pwclkcnt++; + if (id !=3D RTOS_FEATURE_ID) { + ipi_data.cmd =3D SLP_WAKE_LOCK; + ipi_data.feature =3D id; + ret =3D vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0, + &ipi_data, PIN_OUT_C_SIZE_SLEEP_0, 500); + if (ret < 0) { + dev_warn(vcp->dev, "%s ipc_send_compl failed. ret %d\n", + __func__, ret); + return ret; + } + } mutex_unlock(&vcp->vcp_cluster->vcp_pw_clk_mutex); =20 return 0; @@ -541,7 +689,34 @@ static int vcp_enable_pm_clk(struct mtk_vcp_device *vc= p, enum feature_id id) =20 static int vcp_disable_pm_clk(struct mtk_vcp_device *vcp, enum feature_id = id) { + int ret; + bool suspend_status; + struct slp_ctrl_data ipi_data; + mutex_lock(&vcp->vcp_cluster->vcp_pw_clk_mutex); + read_poll_timeout_atomic(is_vcp_suspending, + suspend_status, !suspend_status, + USEC_PER_MSEC, + SUSPEND_WAIT_TIMEOUT_MS * USEC_PER_MSEC, + false, vcp); + if (suspend_status) { + dev_warn(vcp->dev, "%s blocked by vcp suspend, pwclkcnt(%d)\n", + __func__, + vcp->vcp_cluster->pwclkcnt); + return -ETIMEDOUT; + } + + if (id !=3D RTOS_FEATURE_ID) { + ipi_data.cmd =3D SLP_WAKE_UNLOCK; + ipi_data.feature =3D id; + ret =3D vcp->ipi_ops->ipi_send_compl(vcp->ipi_dev, IPI_OUT_C_SLEEP_0, + &ipi_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; + } + } vcp->vcp_cluster->pwclkcnt--; if (vcp->vcp_cluster->pwclkcnt < 0) { for (u32 i =3D 0; i < NUM_FEATURE_ID; i++) diff --git a/drivers/remoteproc/mtk_vcp_common.h b/drivers/remoteproc/mtk_v= cp_common.h index 4a4393b2ae1f..42deda362b6c 100644 --- a/drivers/remoteproc/mtk_vcp_common.h +++ b/drivers/remoteproc/mtk_vcp_common.h @@ -18,9 +18,13 @@ #define VCP_IPI_DEV_READY_TIMEOUT 1000 #define USDELAY_RANGE_MIN 1000 #define USDELAY_RANGE_MAX 2000 +#define SUSPEND_WAIT_TIMEOUT_MS 100 =20 /* vcp platform define */ #define DMA_MAX_MASK_BIT 33 +#define RESUME_IPI_MAGIC 0x12345678 +#define SUSPEND_IPI_MAGIC 0x87654321 +#define PIN_OUT_C_SIZE_SLEEP_0 2 =20 /* vcp load image define */ #define VCM_IMAGE_MAGIC (0x58881688) @@ -98,6 +102,15 @@ enum vcp_core_id { VCP_CORE_TOTAL =3D 2, }; =20 +/* vcp sleep cmd flag sync with VCP FW */ +enum { + SLP_WAKE_LOCK =3D 0, + SLP_WAKE_UNLOCK, + SLP_STATUS_DBG, + SLP_SUSPEND, + SLP_RESUME, +}; + /* vcp kernel smc server id */ enum mtk_tinysys_vcp_kernel_op { MTK_TINYSYS_VCP_KERNEL_OP_RESET_SET =3D 0, @@ -155,6 +168,17 @@ struct vcp_reserve_mblock { size_t size; }; =20 +/** + * struct slp_ctrl_data - sleep ctrl data sync with AP and VCP + * + * @feature: Feature id + * @cmd: sleep cmd flag. + */ +struct slp_ctrl_data { + u32 feature; + u32 cmd; +}; + /** * struct vcp_work_struct - vcp notify work structure. * @@ -230,6 +254,8 @@ void vcp_A_register_notify(enum feature_id id, struct notifier_block *nb); void vcp_A_unregister_notify(enum feature_id id, struct notifier_block *nb); +bool is_vcp_suspending(struct mtk_vcp_device *vcp); + /* vcp common reserved memory APIs */ int vcp_reserve_memory_ioremap(struct mtk_vcp_device *vcp); phys_addr_t vcp_get_reserve_mem_phys(enum vcp_reserve_mem_id_t id); @@ -252,4 +278,7 @@ int vcp_A_deregister_feature(struct mtk_vcp_device *vcp, =20 /* vcp common core hart shutdown API */ u32 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_rdy_signal(struct mtk_vcp_device *vcp, bool rdy); +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 4aa0ed47abd7..133518bedd76 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" @@ -73,12 +74,68 @@ struct mtk_ipi_device *vcp_get_ipidev(struct mtk_vcp_de= vice *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); + + if (!vcp->vcp_cluster->is_suspending && + vcp->vcp_cluster->pwclkcnt) { + 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); + + /* wait vcp clr rdy bit */ + vcp_wait_rdy_signal(vcp, false); + } + 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 && + vcp->vcp_cluster->pwclkcnt) { + pm_runtime_get_sync(dev); + + /* wait vcp set rdy bit */ + vcp_wait_rdy_signal(vcp, true); + 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; struct arm_smccc_res res; int ret; =20 + 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); @@ -118,6 +175,8 @@ static int mtk_vcp_start(struct rproc *rproc) 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; } @@ -435,6 +494,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_notify =3D vcp_A_register_notify, .vcp_unregister_notify =3D vcp_A_unregister_notify, .vcp_register_feature =3D vcp_A_register_feature, @@ -455,6 +515,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}, {} @@ -468,6 +533,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 e36612256b63..3713977e4171 100644 --- a/drivers/remoteproc/mtk_vcp_rproc.h +++ b/drivers/remoteproc/mtk_vcp_rproc.h @@ -22,7 +22,9 @@ * @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 * @pwclkcnt: power and clock config 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 @@ -45,7 +47,9 @@ struct mtk_vcp_of_cluster { u32 sram_offset[VCP_CORE_TOTAL]; u32 msg_vcp_ready0; u32 msg_vcp_ready1; + u32 slp_ipi_ack_data; int pwclkcnt; + 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 5bd562d1ae62..5a859a3bc1eb 100644 --- a/include/linux/remoteproc/mtk_vcp_public.h +++ b/include/linux/remoteproc/mtk_vcp_public.h @@ -107,6 +107,7 @@ struct mtk_vcp_ipi_ops { =20 struct mtk_vcp_ops { bool (*vcp_is_ready)(enum feature_id id); + bool (*vcp_is_suspending)(struct mtk_vcp_device *vcp); void (*vcp_register_notify)(enum feature_id id, struct notifier_block *nb= ); void (*vcp_unregister_notify)(enum feature_id id, struct notifier_block *= nb); int (*vcp_register_feature)(struct mtk_vcp_device *vcp, enum feature_id i= d); --=20 2.46.0