From patchwork Fri Dec 10 17:25:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670275 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1AC2EC433EF for ; Fri, 10 Dec 2021 17:26:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244445AbhLJR36 (ORCPT ); Fri, 10 Dec 2021 12:29:58 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:42454 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244446AbhLJR35 (ORCPT ); Fri, 10 Dec 2021 12:29:57 -0500 X-UUID: 4b3bb49e7aeb4cc39fcb24e7d8ad14d8-20211211 X-UUID: 4b3bb49e7aeb4cc39fcb24e7d8ad14d8-20211211 Received: from mtkexhb02.mediatek.inc [(172.21.101.103)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 650089352; Sat, 11 Dec 2021 01:26:19 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs10n2.mediatek.inc (172.21.101.183) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.3; Sat, 11 Dec 2021 01:26:18 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:17 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 01/17] dt-bindings: mailbox: mediatek: Add APU mailbox compatible Date: Sat, 11 Dec 2021 01:25:49 +0800 Message-ID: <20211210172605.30618-2-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add the mailbox compatible for the MediaTek APU. The MT8192 and MT8195 SOC will use it. Signed-off-by: Pi-Cheng Chen Signed-off-by: Flora Fu --- .../mailbox/mediatek,apu-mailbox.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/mailbox/mediatek,apu-mailbox.yaml diff --git a/Documentation/devicetree/bindings/mailbox/mediatek,apu-mailbox.yaml b/Documentation/devicetree/bindings/mailbox/mediatek,apu-mailbox.yaml new file mode 100644 index 000000000000..ffd0e1623955 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/mediatek,apu-mailbox.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (C) 2021 MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/mediatek,apu-mailbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek AI Processing Unit mailbox controller bindings + +description: + The APU mailbox controller provides access from the + application processor to the MediaTek AI Processing Unit. + +maintainers: + - Pi-Cheng Chen + +properties: + compatible: + enum: + - mediatek,mtk-apu-mailbox + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + "#mbox-cells": + const: 1 + +required: + - compatible + - reg + - interrupts + - "#mbox-cells" + +additionalProperties: false + +examples: + - | + #include + apu_mailbox: apu_mailbox@19000000 { + compatible = "mediatek,mtk-apu-mailbox"; + reg = <0x19000000 0x100>; + interrupts = ; + #mbox-cells = <1>; + }; From patchwork Fri Dec 10 17:25:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670277 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DD32DC433EF for ; Fri, 10 Dec 2021 17:26:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240959AbhLJRaQ (ORCPT ); Fri, 10 Dec 2021 12:30:16 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:42860 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S230380AbhLJRaP (ORCPT ); Fri, 10 Dec 2021 12:30:15 -0500 X-UUID: 0a5f46073efc4afeb2408acb8cd5efd5-20211211 X-UUID: 0a5f46073efc4afeb2408acb8cd5efd5-20211211 Received: from mtkmbs10n1.mediatek.inc [(172.21.101.34)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 1998795543; Sat, 11 Dec 2021 01:26:36 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs10n1.mediatek.inc (172.21.101.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.15; Sat, 11 Dec 2021 01:26:35 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:34 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 02/17] dt-bindings: memory: mediatek: Add MT8192 apu iommu bindings Date: Sat, 11 Dec 2021 01:25:50 +0800 Message-ID: <20211210172605.30618-3-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org MT8192 has one APU iommu hardware and add apu iommu bindings. Signed-off-by: Flora Fu --- .../devicetree/bindings/iommu/mediatek,iommu.yaml | 7 +++++-- include/dt-bindings/memory/mt8192-larb-port.h | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml index c528a299afa9..14fae9642ec9 100644 --- a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml +++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml @@ -77,6 +77,7 @@ properties: - mediatek,mt8173-m4u # generation two - mediatek,mt8183-m4u # generation two - mediatek,mt8192-m4u # generation two + - mediatek,mt8192-iommu-apu # generation two - mediatek,mt8195-iommu-vdo # generation two - mediatek,mt8195-iommu-vpp # generation two - mediatek,mt8195-iommu-infra # generation two @@ -154,6 +155,7 @@ allOf: compatible: enum: - mediatek,mt8192-m4u + - mediatek,mt8192-iommu-apu - mediatek,mt8195-iommu-vdo - mediatek,mt8195-iommu-vpp @@ -165,8 +167,9 @@ allOf: not: properties: compatible: - contains: - const: mediatek,mt8195-iommu-infra + enum: + - mediatek,mt8192-iommu-apu + - mediatek,mt8195-iommu-infra then: required: diff --git a/include/dt-bindings/memory/mt8192-larb-port.h b/include/dt-bindings/memory/mt8192-larb-port.h index 23035a52c675..908d6831bf99 100644 --- a/include/dt-bindings/memory/mt8192-larb-port.h +++ b/include/dt-bindings/memory/mt8192-larb-port.h @@ -240,4 +240,8 @@ #define M4U_PORT_L20_IPE_RSC_RDMA0 MTK_M4U_ID(20, 4) #define M4U_PORT_L20_IPE_RSC_WDMA MTK_M4U_ID(20, 5) +#define IOMMU_PORT_APU_DATA MTK_M4U_ID(0, 0) +#define IOMMU_PORT_APU_VLM MTK_M4U_ID(0, 1) +#define IOMMU_PORT_APU_VPU MTK_M4U_ID(0, 2) + #endif From patchwork Fri Dec 10 17:25:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670281 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1C19DC4332F for ; Fri, 10 Dec 2021 17:26:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244474AbhLJRaV (ORCPT ); Fri, 10 Dec 2021 12:30:21 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:48784 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244464AbhLJRaU (ORCPT ); Fri, 10 Dec 2021 12:30:20 -0500 X-UUID: cb1fa888e9de4774a350af8a2122c70e-20211211 X-UUID: cb1fa888e9de4774a350af8a2122c70e-20211211 Received: from mtkexhb01.mediatek.inc [(172.21.101.102)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 814626988; Sat, 11 Dec 2021 01:26:40 +0800 Received: from mtkexhb02.mediatek.inc (172.21.101.103) by mtkmbs10n2.mediatek.inc (172.21.101.183) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.3; Sat, 11 Dec 2021 01:26:39 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkexhb02.mediatek.inc (172.21.101.103) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:39 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:38 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 03/17] dt-bindings: remoteproc: mediatek: Add APU rproc compatible Date: Sat, 11 Dec 2021 01:25:51 +0800 Message-ID: <20211210172605.30618-4-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add new binding document for the APU remote processor. The initial version is used for MT8192 SOC. Signed-off-by: Pi-Cheng Chen Signed-off-by: Flora Fu --- .../bindings/remoteproc/mediatek,apu-rv.yaml | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/remoteproc/mediatek,apu-rv.yaml diff --git a/Documentation/devicetree/bindings/remoteproc/mediatek,apu-rv.yaml b/Documentation/devicetree/bindings/remoteproc/mediatek,apu-rv.yaml new file mode 100644 index 000000000000..c390b85040eb --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/mediatek,apu-rv.yaml @@ -0,0 +1,106 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2021 (C) MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/mediatek,apu-rv.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek APU remote processor controller bindings + +description: | + APU integrated subsystem having MD32RV33 (MD32) that runs tinysys. + The tinysys runs on a microprocessor in APU. + Its firmware is loaded and booted from Kernel side. + Kernel and tinysys uses IPI to send and receive messages. + +maintainers: + - Flora Fu + - Pi-Cheng Chen + +properties: + compatible: + items: + - enum: + - mediatek,mt8192-apusys-rv + - const: simple-mfd + + reg: + minItems: 1 + + reg-names: + minItems: 1 + + power-domains: + maxItems: 1 + + iommus: + maxItems: 1 + + interrupts: + description: List of interrupts. + + interrupt-names: + description: Name list of interrupts. + + mboxes: + maxItems: 1 + +required: + - compatible + - reg + - power-domains + - interrupts + - mboxes + +additionalProperties: + type: object + description: + Represent subnodes that will register as rpmsg devices. + properties: + compatible: + enum: + - mediatek,apu-ctrl-rpmsg + - mediatek,apupwr-tx-rpmsg + - mediatek,apupwr-rx-rpmsg + - mediatek,apu-mdw-rpmsg + mediatek,rpmsg-name: + $ref: /schemas/types.yaml#/definitions/string-array + description: + Contains the name for the rpmsg device. Used to match + the subnode to rpmsg device announced by APU. + required: + - compatible + +examples: + - | + #include + #include + #include + #include + + apusys_rv@19001000 { + compatible = "mediatek,mt8192-apusys-rv", "simple-mfd"; + reg = <0x19001000 0x1000>; + reg-names = "md32_sysctrl"; + power-domains = <&apuspm 0>; + iommus = <&iommu_apu IOMMU_PORT_APU_DATA>; + interrupts = ; + interrupt-names = "apu_wdt"; + mboxes = <&apu_mailbox 0>; + apu_ctrl { + compatible = "mediatek,apu-ctrl-rpmsg"; + mediatek,rpmsg-name = "apu-ctrl-rpmsg"; + }; + apu_pwr_tx { + compatible = "mediatek,apupwr-tx-rpmsg"; + mediatek,rpmsg-name = "apupwr-tx-rpmsg"; + }; + apu_pwr_rx { + compatible = "mediatek,apupwr-rx-rpmsg"; + mediatek,rpmsg-name = "apupwr-rx-rpmsg"; + }; + apu_mdw_rpmsg { + compatible = "mediatek,apu-mdw-rpmsg"; + mediatek,rpmsg-name = "apu-mdw-rpmsg"; + }; + }; From patchwork Fri Dec 10 17:25:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670279 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D32EFC433EF for ; Fri, 10 Dec 2021 17:26:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244465AbhLJRaT (ORCPT ); Fri, 10 Dec 2021 12:30:19 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:48756 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244460AbhLJRaT (ORCPT ); Fri, 10 Dec 2021 12:30:19 -0500 X-UUID: 09cd9be715d342d2a1e6b43b85d86f22-20211211 X-UUID: 09cd9be715d342d2a1e6b43b85d86f22-20211211 Received: from mtkmbs10n2.mediatek.inc [(172.21.101.183)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 607911808; Sat, 11 Dec 2021 01:26:42 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs10n1.mediatek.inc (172.21.101.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.15; Sat, 11 Dec 2021 01:26:40 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:40 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 04/17] dt-bindings: soc: mediatek: apu: Add APU power compatible Date: Sat, 11 Dec 2021 01:25:52 +0800 Message-ID: <20211210172605.30618-5-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add new document for APU power compatible. Signed-off-by: Flora Fu --- .../soc/mediatek/mediatek,apu-pwr.yaml | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pwr.yaml diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pwr.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pwr.yaml new file mode 100644 index 000000000000..00f67dddb162 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pwr.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2021 (C) MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/mediatek/mediatek,apu-pwr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek APU Power driver bindings + +description: | + MediaTek AI Process Unit (APU) power driver supports for subsys clock and + regulator controller. + +maintainers: + - Flora Fu + +properties: + compatible: + items: + - enum: + - mediatek,mt8192-apu-power + reg: + minItems: 1 + + reg-names: + minItems: 1 + + power-domains: + maxItems: 1 + + vvpu-supply: + description: apu vpu regulator supply. + + vmdla-supply: + description: apu mdla regulator supply. + + clocks: + description: Contains module clock source and clock names + + clock-names: + description: Names of the clocks list in clocks property + +required: + - compatible + - reg + - reg-names + - vvpu-supply + - vmdla-supply + +additionalProperties: false + +examples: + - | + #include + apusys_power: apusys_power@190f1000 { + compatible = "mediatek,mt8192-apu-power"; + reg = <0x190f1000 0x1000>; + reg-names = "apu_pcu"; + power-domains = <&apuspm 0>; + vvpu-supply = <&mt6359_vproc1_buck_reg>; + vmdla-supply = <&mt6359_vproc2_buck_reg>; + clocks = <&topckgen CLK_TOP_DSP_SEL>, + <&topckgen CLK_TOP_DSP1_SEL>, + <&topckgen CLK_TOP_DSP1_NPUPLL_SEL>, + <&topckgen CLK_TOP_DSP2_SEL>, + <&topckgen CLK_TOP_DSP2_NPUPLL_SEL>, + <&topckgen CLK_TOP_DSP5_SEL>, + <&topckgen CLK_TOP_DSP5_APUPLL_SEL>, + <&topckgen CLK_TOP_IPU_IF_SEL>, + <&clk26m>; + clock-names = "clk_top_dsp_sel", + "clk_top_dsp1_sel", + "clk_top_dsp1_npupll_sel", + "clk_top_dsp2_sel", + "clk_top_dsp2_npupll_sel", + "clk_top_dsp5_sel", + "clk_top_dsp5_apupll_sel", + "clk_top_ipu_if_sel", + "clk_top_clk26m"; + }; From patchwork Fri Dec 10 17:25:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670285 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D5269C433F5 for ; Fri, 10 Dec 2021 17:26:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244490AbhLJRaX (ORCPT ); Fri, 10 Dec 2021 12:30:23 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:43004 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S230380AbhLJRaV (ORCPT ); Fri, 10 Dec 2021 12:30:21 -0500 X-UUID: d5ca78fc4c814a6da02f6846fb57defb-20211211 X-UUID: d5ca78fc4c814a6da02f6846fb57defb-20211211 Received: from mtkexhb02.mediatek.inc [(172.21.101.103)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 1565390378; Sat, 11 Dec 2021 01:26:43 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs10n2.mediatek.inc (172.21.101.183) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.3; Sat, 11 Dec 2021 01:26:41 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:41 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 05/17] dt-bindings: soc: mediatek: apu: Add apu logger compatible Date: Sat, 11 Dec 2021 01:25:53 +0800 Message-ID: <20211210172605.30618-6-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add new document for apu logger compatible. Signed-off-by: Flora Fu --- .../soc/mediatek/mediatek,apu-logger.yaml | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-logger.yaml diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-logger.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-logger.yaml new file mode 100644 index 000000000000..9e56fb3b8080 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-logger.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2021 (C) MediaTek Inc. +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/soc/mediatek/mediatek,apu-logger.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: MediaTek APU Logger for debug into microprocessor + +description: | + The APU logger is for debug usage. The APU remote microprocessor's logs + will be output to the mapped memory and application processor can read + logs from the dedicated reserved registers. + +maintainers: + - Flora Fu + +properties: + compatible: + items: + - enum: + - mediatek,apu-sw-logger + reg: + maxItems: 1 + + iommus: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + apusys_sw_logger@19000040 { + compatible = "mediatek,apu-sw-logger"; + reg = <0x19000040 0x10>; + iommus = <&iommu_apu IOMMU_PORT_APU_DATA>; + }; From patchwork Fri Dec 10 17:25:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670283 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2EE3CC433F5 for ; Fri, 10 Dec 2021 17:26:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244484AbhLJRaW (ORCPT ); Fri, 10 Dec 2021 12:30:22 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:48784 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244460AbhLJRaV (ORCPT ); Fri, 10 Dec 2021 12:30:21 -0500 X-UUID: cc5b368096fe44c9831be78be0aa153d-20211211 X-UUID: cc5b368096fe44c9831be78be0aa153d-20211211 Received: from mtkmbs10n1.mediatek.inc [(172.21.101.34)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 1342538033; Sat, 11 Dec 2021 01:26:45 +0800 Received: from mtkexhb01.mediatek.inc (172.21.101.102) by mtkmbs07n2.mediatek.inc (172.21.101.141) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:43 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkexhb01.mediatek.inc (172.21.101.102) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:43 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:43 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 06/17] mailbox: mediatek: add mtk-apu-mailbox driver Date: Sat, 11 Dec 2021 01:25:54 +0800 Message-ID: <20211210172605.30618-7-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add mtk-apu-mailbox driver to support communication with APU remote microprocessor. Signed-off-by: Pi-Cheng Chen Signed-off-by: Flora Fu --- drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 2 + drivers/mailbox/mtk-apu-mailbox.c | 162 ++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 drivers/mailbox/mtk-apu-mailbox.c diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index d9cd3606040e..aeaaadd4cb8d 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -238,6 +238,15 @@ config STM32_IPCC with hardware for Inter-Processor Communication Controller (IPCC) between processors. Say Y here if you want to have this support. +config MTK_APU_MBOX + tristate "MediaTek APU Mailbox Support" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + Say yes here to add support for the MediaTek APU Mailbox + driver. The mailbox implementation provides access from the + application processor to the MediaTek AI Processing Unit. + If unsure say N. + config MTK_CMDQ_MBOX tristate "MediaTek CMDQ Mailbox Support" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 338cc05e5431..e24ad25c3378 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -49,6 +49,8 @@ obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o +obj-$(CONFIG_MTK_APU_MBOX) += mtk-apu-mailbox.o + obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o diff --git a/drivers/mailbox/mtk-apu-mailbox.c b/drivers/mailbox/mtk-apu-mailbox.c new file mode 100644 index 000000000000..860db0504907 --- /dev/null +++ b/drivers/mailbox/mtk-apu-mailbox.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define INBOX (0x0) +#define OUTBOX (0x20) +#define INBOX_IRQ (0xc0) +#define OUTBOX_IRQ (0xc4) +#define INBOX_IRQ_MASK (0xd0) + +#define MSG_MBOX_SLOTS (4) + +struct mtk_apu_mailbox { + struct device *dev; + void __iomem *regs; + spinlock_t lock; /* register access lock */ + struct mbox_controller controller; + u32 msgs[MSG_MBOX_SLOTS]; +}; + +static irqreturn_t mtk_apu_mailbox_threaded_irq(int irq, void *dev_id) +{ + struct mtk_apu_mailbox *mbox = dev_id; + struct mbox_chan *link = &mbox->controller.chans[0]; + int i; + + for (i = 0; i < MSG_MBOX_SLOTS; i++) + mbox->msgs[i] = readl(mbox->regs + OUTBOX + i * sizeof(u32)); + + mbox_chan_received_data(link, &mbox->msgs); + writel(readl(mbox->regs + OUTBOX_IRQ), mbox->regs + OUTBOX_IRQ); + + return IRQ_HANDLED; +} + +static int mtk_apu_mailbox_send_data(struct mbox_chan *chan, void *data) +{ + struct mtk_apu_mailbox *mbox = container_of(chan->mbox, + struct mtk_apu_mailbox, + controller); + int i; + + spin_lock(&mbox->lock); + writel(~BIT(MSG_MBOX_SLOTS - 1), mbox->regs + INBOX_IRQ_MASK); + for (i = 0; i < MSG_MBOX_SLOTS; i++) + writel(((u32 *)data)[i], mbox->regs + INBOX + i * sizeof(u32)); + spin_unlock(&mbox->lock); + + return 0; +} + +static bool mtk_apu_mailbox_last_tx_done(struct mbox_chan *chan) +{ + struct mtk_apu_mailbox *mbox = container_of(chan->mbox, + struct mtk_apu_mailbox, + controller); + + return readl(mbox->regs + INBOX_IRQ) == 0; +} + +static const struct mbox_chan_ops mtk_apu_mailbox_ops = { + .send_data = mtk_apu_mailbox_send_data, + .last_tx_done = mtk_apu_mailbox_last_tx_done, +}; + +static int mtk_apu_mailbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct mtk_apu_mailbox *mbox; + int ret = 0; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + mbox->dev = dev; + platform_set_drvdata(pdev, mbox); + spin_lock_init(&mbox->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + mbox->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(mbox->regs)) + return PTR_ERR(mbox->regs); + + ret = devm_request_threaded_irq(dev, + irq_of_parse_and_map(dev->of_node, 0), + NULL, mtk_apu_mailbox_threaded_irq, + IRQF_ONESHOT, dev_name(dev), mbox); + if (ret) { + dev_err(dev, "Failed to register apu mailbox IRQ: %d\n", ret); + return -ENODEV; + } + + mbox->controller.txdone_irq = false; + mbox->controller.txdone_poll = true; + mbox->controller.txpoll_period = 1; + mbox->controller.ops = &mtk_apu_mailbox_ops; + mbox->controller.dev = dev; + mbox->controller.num_chans = 1; + mbox->controller.chans = devm_kcalloc(dev, mbox->controller.num_chans, + sizeof(*mbox->controller.chans), + GFP_KERNEL); + if (!mbox->controller.chans) + return -ENOMEM; + + ret = devm_mbox_controller_register(dev, &mbox->controller); + if (ret) + return ret; + + dev_info(dev, "registered apu mailbox\n"); + + return 0; +} + +static int mtk_apu_mailbox_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id mtk_apu_mailbox_of_match[] = { + {.compatible = "mediatek,mtk-apu-mailbox"}, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_apu_mailbox_of_match); + +static struct platform_driver mtk_apu_mailbox_driver = { + .probe = mtk_apu_mailbox_probe, + .remove = mtk_apu_mailbox_remove, + .driver = { + .name = "mtk-apu-mailbox", + .of_match_table = mtk_apu_mailbox_of_match, + }, +}; + +static int __init apu_mailbox_init(void) +{ + return platform_driver_register(&mtk_apu_mailbox_driver); +} + +static void __exit apu_mailbox_exit(void) +{ + platform_driver_unregister(&mtk_apu_mailbox_driver); +} + +module_init(apu_mailbox_init); +module_exit(apu_mailbox_exit); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("APU Mailbox Driver"); From patchwork Fri Dec 10 17:25:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670287 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 54116C4332F for ; Fri, 10 Dec 2021 17:26:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244496AbhLJRac (ORCPT ); Fri, 10 Dec 2021 12:30:32 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:43080 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244502AbhLJRaY (ORCPT ); Fri, 10 Dec 2021 12:30:24 -0500 X-UUID: f6184c85e41d4b64b8936494185a5018-20211211 X-UUID: f6184c85e41d4b64b8936494185a5018-20211211 Received: from mtkcas11.mediatek.inc [(172.21.101.40)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 26101188; Sat, 11 Dec 2021 01:26:46 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs07n1.mediatek.inc (172.21.101.16) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:45 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:44 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 07/17] iommu/mediatek: Support APU iommu and config data for mt8192 Date: Sat, 11 Dec 2021 01:25:55 +0800 Message-ID: <20211210172605.30618-8-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org APU IOMMU is a new iommu HW. it uses a new pagetable. Add support for mt8192 apu iommu. Signed-off-by: Yong Wu Signed-off-by: Flora Fu --- drivers/iommu/mtk_iommu.c | 45 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index 8377f3c37283..4bc7c76062e6 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -133,6 +133,7 @@ /* 2 bits: iommu type */ #define MTK_IOMMU_TYPE_MM (0x0 << 13) #define MTK_IOMMU_TYPE_INFRA (0x1 << 13) +#define MTK_IOMMU_TYPE_APU (0x2 << 13) #define MTK_IOMMU_TYPE_MASK (0x3 << 13) #define IFA_IOMMU_PCIe_SUPPORT BIT(15) @@ -185,6 +186,7 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data, unsigned int ban #define MTK_IOMMU_4GB_MODE_REMAP_BASE 0x140000000UL static LIST_HEAD(m4ulist); /* List all the M4U HWs */ +static LIST_HEAD(apulist); /* List the apu iommu HWs */ #define for_each_m4u(data, head) list_for_each_entry(data, head, list) @@ -209,6 +211,13 @@ static const struct mtk_iommu_iova_region mt8192_multi_dom[] = { #endif }; +static const struct mtk_iommu_iova_region mt8192_multi_dom_apu[] = { + { .iova_base = 0x0, .size = SZ_4G}, /* APU DATA */ + { .iova_base = 0x4000000ULL, .size = 0x4000000}, /* APU VLM */ + { .iova_base = 0x10000000ULL, .size = 0x10000000}, /* APU VPU */ + { .iova_base = 0x70000000ULL, .size = 0x12600000}, /* APU REG */ +}; + /* If 2 M4U share a domain(use the same hwlist), Put the corresponding info in first data.*/ static struct mtk_iommu_data *mtk_iommu_get_frst_data(struct list_head *hwlist) { @@ -638,24 +647,45 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, static struct iommu_device *mtk_iommu_probe_device(struct device *dev) { + unsigned int flag = DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS; struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - struct mtk_iommu_data *data; + struct mtk_iommu_data *data, *curdata; + struct device_link *link; if (!fwspec || fwspec->ops != &mtk_iommu_ops) return ERR_PTR(-ENODEV); /* Not a iommu client device */ data = dev_iommu_priv_get(dev); + if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_APU)) { + /* + * The APU IOMMU HWs must work together. The consumer device + * must connect with all the apu iommu HWs at the same time. + */ + for_each_m4u(curdata, data->hw_list) { + link = device_link_add(dev, curdata->dev, flag); + if (!link) + dev_err(dev, "Unable to link %s\n", dev_name(curdata->dev)); + } + } return &data->iommu; } static void mtk_iommu_release_device(struct device *dev) { struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct mtk_iommu_data *data; if (!fwspec || fwspec->ops != &mtk_iommu_ops) return; + data = dev_iommu_priv_get(dev); + if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_APU)) { + struct list_head *head = data->hw_list; + + for_each_m4u(data, head) + device_link_remove(dev, data->dev); + } iommu_fwspec_free(dev); } @@ -1270,6 +1300,18 @@ static const struct mtk_iommu_plat_data mt8192_data = { {0, 14, 16}, {0, 13, 18, 17}}, }; +static const struct mtk_iommu_plat_data mt8192_data_apu = { + .m4u_plat = M4U_MT8192, + .flags = DCM_DISABLE | MTK_IOMMU_TYPE_APU | + SHARE_PGTABLE, + .inv_sel_reg = REG_MMU_INV_SEL_GEN2, + .hw_list = &apulist, + .bank_nr = 1, + .bank_enable = {true}, + .iova_region = mt8192_multi_dom_apu, + .iova_region_nr = ARRAY_SIZE(mt8192_multi_dom_apu), +}; + static const struct mtk_iommu_plat_data mt8195_data_infra = { .m4u_plat = M4U_MT8195, .flags = WR_THROT_EN | DCM_DISABLE | @@ -1325,6 +1367,7 @@ static const struct of_device_id mtk_iommu_of_ids[] = { { .compatible = "mediatek,mt8173-m4u", .data = &mt8173_data}, { .compatible = "mediatek,mt8183-m4u", .data = &mt8183_data}, { .compatible = "mediatek,mt8192-m4u", .data = &mt8192_data}, + { .compatible = "mediatek,mt8192-iommu-apu", .data = &mt8192_data_apu}, { .compatible = "mediatek,mt8195-iommu-infra", .data = &mt8195_data_infra}, { .compatible = "mediatek,mt8195-iommu-vdo", .data = &mt8195_data_vdo}, { .compatible = "mediatek,mt8195-iommu-vpp", .data = &mt8195_data_vpp}, From patchwork Fri Dec 10 17:25:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670289 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 37AAEC433FE for ; Fri, 10 Dec 2021 17:27:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244507AbhLJRad (ORCPT ); Fri, 10 Dec 2021 12:30:33 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:48932 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244505AbhLJRaZ (ORCPT ); Fri, 10 Dec 2021 12:30:25 -0500 X-UUID: 75102da3e98349ad97736a1010d0cd6e-20211211 X-UUID: 75102da3e98349ad97736a1010d0cd6e-20211211 Received: from mtkcas10.mediatek.inc [(172.21.101.39)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 593780870; Sat, 11 Dec 2021 01:26:47 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs10n2.mediatek.inc (172.21.101.183) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.3; Sat, 11 Dec 2021 01:26:46 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:45 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 08/17] remoteproc: mediatek: Add APU remoteproc driver Date: Sat, 11 Dec 2021 01:25:56 +0800 Message-ID: <20211210172605.30618-9-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org APU integrated subsystem having MD32RV33 (MD32) that runs tinysys The tinysys is runs on a microprocessor in APU. Its firmware is loaded and booted from Kernel side. Kernel and tinysys use IPI to send and receive messages. Signed-off-by: Pi-Cheng Chen Signed-off-by: Flora Fu --- drivers/remoteproc/Kconfig | 12 + drivers/remoteproc/Makefile | 2 + drivers/remoteproc/mtk-apu-ipi.c | 474 +++++++++ drivers/remoteproc/mtk-apu-rproc.c | 1054 +++++++++++++++++++++ include/linux/remoteproc/mtk-apu-config.h | 100 ++ include/linux/remoteproc/mtk-apu.h | 217 +++++ 6 files changed, 1859 insertions(+) create mode 100644 drivers/remoteproc/mtk-apu-ipi.c create mode 100644 drivers/remoteproc/mtk-apu-rproc.c create mode 100644 include/linux/remoteproc/mtk-apu-config.h create mode 100644 include/linux/remoteproc/mtk-apu.h diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index f2e961f998ca..72fc5fe7f4b5 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -54,6 +54,18 @@ config INGENIC_VPU_RPROC This can be either built-in or a loadable module. If unsure say N. +config MTK_APU_RPROC + tristate "MediaTek APU remoteproc support" + depends on ARCH_MEDIATEK || COMPILE_TEST + select MAILBOX + select MTK_APU_MBOX + select MTK_SCP + help + Say y here to support MediaTek AI Processing Unit Subsystem + via the remote processor framework. + + It's safe to say N here. + config MTK_SCP tristate "Mediatek SCP support" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 0ac256b6c977..1c1f1e443c56 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o obj-$(CONFIG_MTK_SCP) += mtk_scp.o mtk_scp_ipi.o +obj-$(CONFIG_MTK_APU_RPROC) += apu.o +apu-objs += mtk-apu-rproc.o mtk-apu-ipi.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o diff --git a/drivers/remoteproc/mtk-apu-ipi.c b/drivers/remoteproc/mtk-apu-ipi.c new file mode 100644 index 000000000000..62197441eb0b --- /dev/null +++ b/drivers/remoteproc/mtk-apu-ipi.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct apu_mbox_hdr { + unsigned int id; + unsigned int len; + unsigned int serial_no; + unsigned int csum; +}; + +#define IPI_BUF_SIZE (round_up(sizeof(struct mtk_share_obj) * 2, PAGE_SIZE)) + +static struct lock_class_key ipi_lock_key[APU_IPI_MAX]; +static unsigned int tx_serial_no; +static unsigned int rx_serial_no; + +#if IS_ENABLED(CONFIG_MTK_APU_DEBUG) +static inline void dump_msg_buf(struct mtk_apu *apu, void *data, u32 len) +{ + struct device *dev = apu->dev; + u32 i; + int size = 64, num; + u8 buf[64], *ptr = buf; + int ret; + + dev_info(dev, "===== dump message =====\n"); + for (i = 0; i < len; i++) { + num = snprintf(ptr, size, "%02x ", ((u8 *)data)[i]); + if (num <= 0) { + dev_info(dev, "%s: snprintf return error(num = %d)\n", + __func__, num); + return; + } + size -= num; + ptr += num; + + if ((i + 1) % 4 == 0) { + ret = snprintf(ptr++, size--, " "); + if (ret <= 0) { + dev_info(dev, "%s: snprintf return error(ret = %d)\n", + __func__, ret); + return; + } + } + + if ((i + 1) % 16 == 0 || (i + 1) >= len) { + dev_info(dev, "%s\n", buf); + size = 64; + ptr = buf; + } + } + dev_info(dev, "========================\n"); +} +#endif + +static u32 calculate_csum(void *data, u32 len) +{ + u32 csum = 0, res = 0, i; + u8 *ptr; + + for (i = 0; i < (len / sizeof(csum)); i++) + csum += *(((u32 *)data) + i); + + ptr = (u8 *)data + len / sizeof(csum) * sizeof(csum); + for (i = 0; i < (len % sizeof(csum)); i++) + res |= *(ptr + i) << i * 8; + + csum += res; + + return csum; +} + +static inline bool bypass_check(u32 id) +{ + /* whitelist IPI used in power off flow */ + return id == APU_IPI_DEEP_IDLE; +} + +static void ipi_usage_cnt_update(struct mtk_apu *apu, u32 id, int diff) +{ + struct apu_ipi_desc *ipi = &apu->ipi_desc[id]; + + if (apu->platdata->ipi_attrs[id].ack != IPI_WITH_ACK) + return; + + spin_lock(&apu->usage_cnt_lock); + ipi->usage_cnt += diff; + spin_unlock(&apu->usage_cnt_lock); +} + +int apu_ipi_send(struct mtk_apu *apu, u32 id, void *data, u32 len, + u32 wait_ms) +{ + struct timespec64 ts, te; + struct apu_mbox_hdr hdr; + unsigned long timeout; + int ret = 0; + + ktime_get_ts64(&ts); + + if (!apu || + id <= APU_IPI_INIT || + id >= APU_IPI_MAX || + id == APU_IPI_NS_SERVICE || + len > APU_SHARE_BUFFER_SIZE || + !data) + return -EINVAL; + + mutex_lock(&apu->send_lock); + if (apu->platdata->ipi_attrs[id].direction == IPI_HOST_INITIATE && + apu->ipi_inbound_locked == IPI_LOCKED && !bypass_check(id)) { + dev_info(apu->dev, "%s: ipi locked, ipi=%d\n", __func__, id); + mutex_unlock(&apu->send_lock); + return -EBUSY; + } + + apu_deepidle_power_on_aputop(apu); + + /* copy message payload to share buffer, need to do cache flush if + * the buffer is cacheable. currently not + */ + memcpy_toio(apu->send_buf, data, len); + + hdr.id = id; + hdr.len = len; + hdr.csum = calculate_csum(data, len); + hdr.serial_no = tx_serial_no++; + + ret = mbox_send_message(apu->ch, &hdr); + if (ret < 0) { + dev_err(apu->dev, "%s: failed to send msg, ret=%d\n", __func__, + ret); + goto unlock_mutex; + } + ret = 0; + + apu->ipi_id_ack[id] = false; + + /* poll ack from remote processor if wait_ms specified */ + if (wait_ms) { + timeout = jiffies + msecs_to_jiffies(wait_ms); + ret = wait_event_timeout(apu->ack_wq, + &apu->ipi_id_ack[id], + timeout); + + apu->ipi_id_ack[id] = false; + + if (WARN(!ret, "apu ipi %d ack timeout!", id)) { + ret = -EIO; + goto unlock_mutex; + } else { + ret = 0; + } + } + ipi_usage_cnt_update(apu, id, 1); + +unlock_mutex: + mutex_unlock(&apu->send_lock); + ktime_get_ts64(&te); + ts = timespec64_sub(te, ts); + + return ret; +} + +int apu_ipi_lock(struct mtk_apu *apu) +{ + struct apu_ipi_desc *ipi; + int i; + bool ready_to_lock = true; + + if (mutex_trylock(&apu->send_lock) == 0) + return -EBUSY; + + if (apu->ipi_inbound_locked == IPI_LOCKED) { + dev_info(apu->dev, "%s: ipi already locked\n", __func__); + mutex_unlock(&apu->send_lock); + return 0; + } + + spin_lock(&apu->usage_cnt_lock); + for (i = 0; i < APU_IPI_MAX; i++) { + ipi = &apu->ipi_desc[i]; + + if (apu->platdata->ipi_attrs[i].ack == IPI_WITH_ACK && + ipi->usage_cnt != 0 && + !bypass_check(i)) { + dev_info(apu->dev, "%s: ipi %d is still in use %d\n", + __func__, i, ipi->usage_cnt); + ready_to_lock = false; + } + } + + if (!ready_to_lock) { + spin_unlock(&apu->usage_cnt_lock); + mutex_unlock(&apu->send_lock); + return -EBUSY; + } + + apu->ipi_inbound_locked = IPI_LOCKED; + spin_unlock(&apu->usage_cnt_lock); + mutex_unlock(&apu->send_lock); + + return 0; +} + +void apu_ipi_unlock(struct mtk_apu *apu) +{ + mutex_lock(&apu->send_lock); + if (apu->ipi_inbound_locked == IPI_UNLOCKED) + dev_info(apu->dev, "%s: ipi already unlocked\n", __func__); + + spin_lock(&apu->usage_cnt_lock); + apu->ipi_inbound_locked = IPI_UNLOCKED; + spin_unlock(&apu->usage_cnt_lock); + mutex_unlock(&apu->send_lock); +} + +int apu_ipi_register(struct mtk_apu *apu, u32 id, + ipi_handler_t handler, void *priv) +{ + if (!apu || id >= APU_IPI_MAX || WARN_ON(!handler)) { + if (apu) + dev_info(apu->dev, + "%s failed. apu=%p, id=%d, handler=%p, priv=%p\n", + __func__, apu, id, handler, priv); + return -EINVAL; + } + + mutex_lock(&apu->ipi_desc[id].lock); + apu->ipi_desc[id].handler = handler; + apu->ipi_desc[id].priv = priv; + mutex_unlock(&apu->ipi_desc[id].lock); + + return 0; +} + +void apu_ipi_unregister(struct mtk_apu *apu, u32 id) +{ + if (!apu || id >= APU_IPI_MAX) { + if (apu) + dev_info(apu->dev, "%s: invalid id=%d\n", __func__, id); + return; + } + + mutex_lock(&apu->ipi_desc[id].lock); + apu->ipi_desc[id].handler = NULL; + apu->ipi_desc[id].priv = NULL; + mutex_unlock(&apu->ipi_desc[id].lock); +} + +static void apu_init_ipi_handler(void *data, unsigned int len, void *priv) +{ + struct mtk_apu *apu = priv; + struct apu_run *run = data; + struct device *dev = apu->dev; + + strscpy(apu->run.fw_ver, data, APU_FW_VER_LEN); + apu->run.signaled = 1; + wake_up_interruptible(&apu->run.wq); + dev_info(dev, "fw_ver: %s\n", run->fw_ver); +} + +static void apu_ipi_handle_rx(struct mbox_client *cl, void *mssg) +{ + struct mtk_apu *apu = container_of(cl, struct mtk_apu, cl); + struct mtk_share_obj *recv_obj = apu->recv_buf; + struct apu_mbox_hdr *hdr = mssg; + u8 temp_buf[sizeof(struct mtk_share_obj)]; + u32 id, len, calc_csum; + + if (hdr->serial_no != rx_serial_no) { + dev_info(apu->dev, "unmatched serial_no: curr=%u, recv=%u\n", + rx_serial_no, hdr->serial_no); + } + rx_serial_no++; + + id = hdr->id; + len = hdr->len; + + if (hdr->len > APU_SHARE_BUFFER_SIZE) { + dev_info(apu->dev, "IPI message too long(len %d, max %d)", + len, APU_SHARE_BUFFER_SIZE); + return; + } + + if (id >= APU_IPI_MAX) { + dev_info(apu->dev, "no such IPI id = %d", id); + return; + } + + mutex_lock(&apu->ipi_desc[id].lock); + if (!apu->ipi_desc[id].handler) { + dev_info(apu->dev, "IPI id=%d is not registered", id); + mutex_unlock(&apu->ipi_desc[id].lock); + return; + } + + memcpy_fromio(temp_buf, &recv_obj->share_buf, len); + calc_csum = calculate_csum(temp_buf, len); + if (calc_csum != hdr->csum) { + dev_info(apu->dev, "csum error: recv=0x%08x, calc=0x%08x\n", + hdr->csum, calc_csum); +#if IS_ENABLED(CONFIG_MTK_APU_DEBUG) + dump_msg_buf(apu, temp_buf, hdr->len); +#endif + } + + apu->ipi_desc[id].handler(temp_buf, len, apu->ipi_desc[id].priv); + ipi_usage_cnt_update(apu, id, -1); + mutex_unlock(&apu->ipi_desc[id].lock); + + apu->ipi_id_ack[id] = true; + wake_up(&apu->ack_wq); +} + +static int apu_send_ipi(struct platform_device *pdev, u32 id, void *buf, + unsigned int len, unsigned int wait) +{ + struct mtk_apu *apu = platform_get_drvdata(pdev); + + return apu_ipi_send(apu, id, buf, len, wait); +} + +static int apu_register_ipi(struct platform_device *pdev, u32 id, + ipi_handler_t handler, void *priv) +{ + struct mtk_apu *apu = platform_get_drvdata(pdev); + + return apu_ipi_register(apu, id, handler, priv); +} + +static void apu_unregister_ipi(struct platform_device *pdev, u32 id) +{ + struct mtk_apu *apu = platform_get_drvdata(pdev); + + apu_ipi_unregister(apu, id); +} + +static struct mtk_rpmsg_info apu_rpmsg_info = { + .send_ipi = apu_send_ipi, + .register_ipi = apu_register_ipi, + .unregister_ipi = apu_unregister_ipi, + .ns_ipi_id = APU_IPI_NS_SERVICE, +}; + +static void apu_add_rpmsg_subdev(struct mtk_apu *apu) +{ + struct platform_device *pdev = to_platform_device(apu->dev); + + apu->rpmsg_subdev = mtk_rpmsg_create_rproc_subdev(pdev, + &apu_rpmsg_info); + if (apu->rpmsg_subdev) + rproc_add_subdev(apu->rproc, apu->rpmsg_subdev); +} + +static void apu_remove_rpmsg_subdev(struct mtk_apu *apu) +{ + if (apu->rpmsg_subdev) { + rproc_remove_subdev(apu->rproc, apu->rpmsg_subdev); + mtk_rpmsg_destroy_rproc_subdev(apu->rpmsg_subdev); + apu->rpmsg_subdev = NULL; + } +} + +void apu_ipi_config_remove(struct mtk_apu *apu) +{ + dma_free_coherent(apu->dev, IPI_BUF_SIZE, + apu->recv_buf, apu->recv_buf_da); +} + +int apu_ipi_config_init(struct mtk_apu *apu) +{ + struct device *dev = apu->dev; + struct apu_ipi_config *ipi_config; + void *ipi_buf = NULL; + dma_addr_t ipi_buf_da = 0; + + ipi_config = (struct apu_ipi_config *) + get_apu_config_user_ptr(apu->conf_buf, APU_IPI_CONFIG); + + /* initialize shared buffer */ + ipi_buf = dma_alloc_coherent(dev, IPI_BUF_SIZE, + &ipi_buf_da, GFP_KERNEL); + if (!ipi_buf || !ipi_buf_da) { + dev_info(dev, "failed to allocate ipi share memory\n"); + return -ENOMEM; + } + + memset_io(ipi_buf, 0, sizeof(struct mtk_share_obj) * 2); + apu->recv_buf = ipi_buf; + apu->recv_buf_da = ipi_buf_da; + apu->send_buf = ipi_buf + sizeof(struct mtk_share_obj); + apu->send_buf_da = ipi_buf_da + sizeof(struct mtk_share_obj); + ipi_config->in_buf_da = apu->send_buf_da; + ipi_config->out_buf_da = apu->recv_buf_da; + + return 0; +} + +void apu_ipi_remove(struct mtk_apu *apu) +{ + mbox_free_channel(apu->ch); + apu_remove_rpmsg_subdev(apu); + apu_ipi_unregister(apu, APU_IPI_INIT); +} + +int apu_ipi_init(struct platform_device *pdev, struct mtk_apu *apu) +{ + struct device *dev = apu->dev; + struct mbox_client *cl; + int i, ret; + + tx_serial_no = 0; + rx_serial_no = 0; + + mutex_init(&apu->send_lock); + spin_lock_init(&apu->usage_cnt_lock); + for (i = 0; i < APU_IPI_MAX; i++) { + mutex_init(&apu->ipi_desc[i].lock); + lockdep_set_class_and_name(&apu->ipi_desc[i].lock, + &ipi_lock_key[i], + apu->platdata->ipi_attrs[i].name); + } + + init_waitqueue_head(&apu->run.wq); + init_waitqueue_head(&apu->ack_wq); + + /* APU initialization IPI register */ + ret = apu_ipi_register(apu, APU_IPI_INIT, apu_init_ipi_handler, apu); + if (ret) { + dev_err(dev, "failed to register ipi for init, ret=%d\n", + ret); + return ret; + } + + /* add rpmsg subdev */ + apu_add_rpmsg_subdev(apu); + + cl = &apu->cl; + cl->dev = dev; + cl->tx_block = true; + cl->tx_tout = 1000; /* timeout 1000ms */ + cl->knows_txdone = false; + cl->rx_callback = apu_ipi_handle_rx; + + apu->ch = mbox_request_channel(cl, 0); + if (IS_ERR(apu->ch)) { + ret = PTR_ERR(apu->ch); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request mbox chan, ret %d\n", + ret); + + goto remove_rpmsg_subdev; + } + + return 0; + +remove_rpmsg_subdev: + apu_remove_rpmsg_subdev(apu); + apu_ipi_unregister(apu, APU_IPI_INIT); + + return ret; +} diff --git a/drivers/remoteproc/mtk-apu-rproc.c b/drivers/remoteproc/mtk-apu-rproc.c new file mode 100644 index 000000000000..40af8e88f41a --- /dev/null +++ b/drivers/remoteproc/mtk-apu-rproc.c @@ -0,0 +1,1054 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cmd */ +enum { + DPIDLE_CMD_LOCK_IPI = 0x5a00, + DPIDLE_CMD_UNLOCK_IPI = 0x5a01, + DPIDLE_CMD_PDN_UNLOCK = 0x5a02, +}; + +/* ack */ +enum { + DPIDLE_ACK_OK = 0, + DPIDLE_ACK_LOCK_BUSY, + DPIDLE_ACK_POWER_DOWN_FAIL, +}; + +static struct work_struct *apu_pwr_work; +static struct workqueue_struct *apu_pwr_wq; + +static const struct apu_ipi mt81xx_ipi_attrs[APU_IPI_MAX] = { + [APU_IPI_INIT] = { + .name = "init", + .direction = IPI_APU_INITIATE, + .ack = IPI_WITHOUT_ACK, + }, + [APU_IPI_NS_SERVICE] = { + .name = "name-service", + .direction = IPI_APU_INITIATE, + .ack = IPI_WITHOUT_ACK, + }, + [APU_IPI_DEEP_IDLE] = { + .name = "deep_idle", + .direction = IPI_APU_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_CTRL_RPMSG] = { + .name = "apu-ctrl-rpmsg", + .direction = IPI_APU_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_MIDDLEWARE] = { + .name = "apu-mdw-rpmsg", + .direction = IPI_HOST_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_REVISER_RPMSG] = { + .name = "apu-reviser-rpmsg", + .direction = IPI_HOST_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_PWR_TX] = { + .name = "apupwr-tx-rpmsg", + .direction = IPI_HOST_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_PWR_RX] = { + .name = "apupwr-rx-rpmsg", + .direction = IPI_APU_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_MDLA_TX] = { + .name = "mdla-tx-rpmsg", + .direction = IPI_HOST_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_MDLA_RX] = { + .name = "mdla-rx-rpmsg", + .direction = IPI_APU_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_TIMESYNC] = { + .name = "apu-timesync", + .direction = IPI_APU_INITIATE, + .ack = IPI_WITH_ACK, + }, + [APU_IPI_EDMA_TX] = { + .name = "apu-edma-rpmsg", + .direction = IPI_HOST_INITIATE, + .ack = IPI_WITHOUT_ACK, + }, + [APU_IPI_MNOC_TX] = { + .name = "apu-mnoc-rpmsg", + .direction = IPI_HOST_INITIATE, + .ack = IPI_WITHOUT_ACK, + }, +}; + +static void apu_reset_mcu(struct mtk_apu *apu) +{ + u32 reg; + + /* assert mcu reset */ + reg = ioread32(apu->md32_sysctrl); + iowrite32(reg & ~0x1, apu->md32_sysctrl); + mdelay(10); + iowrite32(reg | 0x1, apu->md32_sysctrl); +} + +static int apu_start_mcu(struct mtk_apu *apu) +{ + struct arm_smccc_res ares; + + iowrite32(0xEA9, apu->md32_sysctrl); + arm_smccc_smc(MTK_SIP_APUSYS_CONTROL, MTK_SIP_APU_START_MCU, + 0, 0, 0, 0, 0, 0, &ares); + if (ares.a0) + dev_err(apu->dev, "start mcu fail: %lu\n", ares.a0); + + return 0; +} + +static int apu_stop_mcu(struct mtk_apu *apu) +{ + struct arm_smccc_res ares; + + arm_smccc_smc(MTK_SIP_APUSYS_CONTROL, MTK_SIP_APU_STOP_MCU, + 0, 0, 0, 0, 0, 0, &ares); + if (ares.a0) + dev_err(apu->dev, "stop mcufail: %lu\n", ares.a0); + + return 0; +} + +static int mt81xx_rproc_start(struct mtk_apu *apu) +{ + apu_reset_mcu(apu); + apu_start_mcu(apu); + + return 0; +} + +static int mt81xx_rproc_resume(struct mtk_apu *apu) +{ + apu_start_mcu(apu); + + return 0; +} + +static int mt81xx_rproc_stop(struct mtk_apu *apu) +{ + unsigned long flags; + + spin_lock_irqsave(&apu->reg_lock, flags); + /* disable apu wdt */ + iowrite32(ioread32(apu->apu_wdt + WDT_CTRL0) & ~WDT_EN, + apu->apu_wdt + WDT_CTRL0); + /* clear wdt interrupt */ + iowrite32(0x1, apu->apu_wdt); + spin_unlock_irqrestore(&apu->reg_lock, flags); + + /* Hold runstall */ + apu_stop_mcu(apu); + return 0; +} + +static int mt81xx_apu_power_on(struct mtk_apu *apu) +{ + int ret; + + ret = pm_runtime_get_sync(apu->dev); + if (ret < 0) { + dev_info(apu->dev, + "%s: call to get_sync(dev) failed, ret=%d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int mt81xx_apu_power_off(struct mtk_apu *apu) +{ + int ret, timeout; + u32 val; + + /* wait remote power state */ + ret = readl_relaxed_poll_timeout(apu->apu_mbox + REG_MBOX_SPARE(6, 3), + val, + (val & BIT(0)), + 10, 25000); + if (ret) { + dev_err(apu->dev, "Remote WFI not ready\n"); + return ret; + } + + ret = pm_runtime_put_sync(apu->dev); + if (ret) { + dev_info(apu->dev, + "%s: call to put_sync(dev) failed, ret=%d\n", + __func__, ret); + goto error_genpd; + } + + /* polling APU TOP rpm state till suspended */ + dev_info(apu->dev, "start polling power off\n"); + timeout = 500; + while (apu->top_genpd && timeout-- > 0) + msleep(20); + if (timeout <= 0) { + dev_info(apu->dev, "%s: polling power off timeout!!\n", + __func__); + apu_ipi_unlock(apu); + WARN_ON(0); + ret = -ETIMEDOUT; + goto error_genpd; + } + + dev_info(apu->dev, "polling power done\n"); + + return 0; + +error_genpd: + pm_runtime_get_sync(apu->dev); + + return ret; +} + +static int mt81xx_apu_memmap_init(struct mtk_apu *apu) +{ + struct device *dev = apu->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apu_mbox"); + if (!res) { + dev_info(dev, "%s: apu_mbox get resource fail\n", __func__); + return -ENODEV; + } + apu->apu_mbox = ioremap(res->start, res->end - res->start + 1); + if (IS_ERR((void const *)apu->apu_mbox)) { + dev_info(dev, "%s: apu_mbox remap base fail\n", __func__); + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "md32_sysctrl"); + if (!res) { + dev_info(dev, "%s: md32_sysctrl get resource fail\n", __func__); + return -ENODEV; + } + apu->md32_sysctrl = ioremap(res->start, res->end - res->start + 1); + if (IS_ERR((void const *)apu->md32_sysctrl)) { + dev_info(dev, "%s: md32_sysctrl remap base fail\n", __func__); + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apu_wdt"); + if (!res) { + dev_info(dev, "%s: apu_wdt get resource fail\n", __func__); + return -ENODEV; + } + apu->apu_wdt = ioremap(res->start, res->end - res->start + 1); + if (IS_ERR((void const *)apu->apu_wdt)) { + dev_info(dev, "%s: apu_wdt remap base fail\n", __func__); + return -ENOMEM; + } + + return 0; +} + +static void mt81xx_apu_memmap_remove(struct mtk_apu *apu) +{ + iounmap(apu->apu_wdt); + iounmap(apu->md32_sysctrl); + iounmap(apu->apu_mbox); +} + +static void *apu_da_to_va(struct rproc *rproc, u64 da, size_t len, + bool *is_iomem) +{ + void *ptr = NULL; + struct mtk_apu *apu = (struct mtk_apu *)rproc->priv; + + if (da >= DRAM_OFFSET && da < DRAM_OFFSET + CODE_BUF_SIZE) { + ptr = apu->code_buf + (da - DRAM_OFFSET); + } else { + dev_err(apu->dev, "%s: invalid da: da = 0x%llx, len = %zu\n", + __func__, da, len); + } + return ptr; +} + +static int apu_run(struct rproc *rproc) +{ + struct mtk_apu *apu = (struct mtk_apu *)rproc->priv; + struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops; + struct device *dev = apu->dev; + struct apu_run *run = &apu->run; + struct timespec64 begin, end, delta; + int ret; + + pm_runtime_get_sync(apu->dev); + hw_ops->start(apu); + + /* check if boot success */ + ktime_get_ts64(&begin); + ret = wait_event_interruptible_timeout(run->wq, + run->signaled, + msecs_to_jiffies(10000)); + ktime_get_ts64(&end); + if (ret == 0) { + dev_info(dev, "APU initialization timeout!!\n"); + ret = -ETIME; + goto stop; + } + if (ret == -ERESTARTSYS) { + dev_info(dev, "wait APU interrupted by a signal!!\n"); + goto stop; + } + + apu->boot_done = true; + delta = timespec64_sub(end, begin); + dev_info(dev, "APU uP boot success. boot time: %llu s, %llu ns\n", + (u64)delta.tv_sec, (u64)delta.tv_nsec); + + return 0; + +stop: + hw_ops->stop(apu); + + return ret; +} + +static int apu_start(struct rproc *rproc) +{ + return apu_run(rproc); +} + +static int apu_attach(struct rproc *rproc) +{ + return apu_run(rproc); +} + +static int apu_stop(struct rproc *rproc) +{ + struct mtk_apu *apu = (struct mtk_apu *)rproc->priv; + struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops; + + hw_ops->stop(apu); + + return 0; +} + +static const struct rproc_ops apu_ops = { + .start = apu_start, + .stop = apu_stop, + .attach = apu_attach, + .da_to_va = apu_da_to_va, +}; + +#if IS_ENABLED(CONFIG_MTK_APU_DEBUG) +static void apu_deepidle_pwron_dbg_fn(struct work_struct *work) +{ + struct mtk_apu *apu = container_of(work, struct mtk_apu, pwron_dbg_wk); + struct device *dev = apu->dev; + int i; + + dev_info(dev, "mbox dummy= 0x%08x 0x%08x 0x%08x 0x%08x\n", + ioread32(apu->apu_mbox + REG_MBOX_SPARE(0, 0)), + ioread32(apu->apu_mbox + REG_MBOX_SPARE(0, 1)), + ioread32(apu->apu_mbox + REG_MBOX_SPARE(0, 2)), + ioread32(apu->apu_mbox + REG_MBOX_SPARE(0, 3))); + + usleep_range(0, 1000); + for (i = 0; i < 5; i++) { + dev_info(apu->dev, "apu boot: pc=%08x, sp=%08x\n", + ioread32(apu->md32_sysctrl + MD32_MON_PC), + ioread32(apu->md32_sysctrl + MD32_MON_SP)); + usleep_range(0, 1000); + } + + dev_info(dev, "%s: MD32_SYS_CTRL = 0x%x\n", + __func__, ioread32(apu->md32_sysctrl + MD32_SYS_CTRL)); +} +#endif + +static int apu_deepidle_send_ack(struct mtk_apu *apu, u32 cmd, u32 ack) +{ + struct dpidle_msg msg; + int ret; + + msg.cmd = cmd; + msg.ack = ack; + ret = apu_ipi_send(apu, APU_IPI_DEEP_IDLE, &msg, sizeof(msg), 0); + if (ret) + dev_info(apu->dev, + "%s: failed to send ack msg, ack=%d, ret=%d\n", + __func__, ack, ret); + + return ret; +} + +static void apu_deepidle_work_func(struct work_struct *work) +{ + struct mtk_apu *apu = container_of(work, struct mtk_apu, deepidle_work); + struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops; + struct dpidle_msg *msg = &apu->recv_msg; + int ret; + + switch (msg->cmd) { + case DPIDLE_CMD_LOCK_IPI: + ret = apu_ipi_lock(apu); + if (ret) { + dev_info(apu->dev, "%s: failed to lock IPI, ret=%d\n", + __func__, ret); + apu_deepidle_send_ack(apu, DPIDLE_CMD_LOCK_IPI, + DPIDLE_ACK_LOCK_BUSY); + return; + } + apu_deepidle_send_ack(apu, DPIDLE_CMD_LOCK_IPI, + DPIDLE_ACK_OK); + break; + + case DPIDLE_CMD_UNLOCK_IPI: + apu_ipi_unlock(apu); + apu_deepidle_send_ack(apu, DPIDLE_CMD_UNLOCK_IPI, + DPIDLE_ACK_OK); + break; + + case DPIDLE_CMD_PDN_UNLOCK: + apu_deepidle_send_ack(apu, DPIDLE_CMD_PDN_UNLOCK, + DPIDLE_ACK_OK); + ret = hw_ops->power_off(apu); + if (ret) { + dev_info(apu->dev, "failed to power off ret=%d\n", ret); + apu_ipi_unlock(apu); + WARN_ON(0); + return; + } + apu_ipi_unlock(apu); + break; + + default: + dev_info(apu->dev, "unknown cmd %x\n", msg->cmd); + break; + } +} + +static void apu_deepidle_ipi_handler(void *data, unsigned int len, void *priv) +{ + struct mtk_apu *apu = (struct mtk_apu *)priv; + + memcpy(&apu->recv_msg, data, len); + queue_work(apu->apu_deepidle_workq, &apu->deepidle_work); +} + +static int apu_deepidle_init(struct mtk_apu *apu) +{ + struct device *dev = apu->dev; + int ret; + + apu->apu_deepidle_workq = alloc_workqueue("apu_deepidle", + WQ_UNBOUND | WQ_HIGHPRI, 0); + if (!apu->apu_deepidle_workq) { + dev_info(apu->dev, "%s: failed to allocate rq for deep idle\n", + __func__); + return -ENOMEM; + } + INIT_WORK(&apu->deepidle_work, apu_deepidle_work_func); + + ret = apu_ipi_register(apu, APU_IPI_DEEP_IDLE, + apu_deepidle_ipi_handler, apu); + if (ret) { + dev_info(dev, + "%s: failed to register deepidle ipi, ret=%d\n", + __func__, ret); + } +#if IS_ENABLED(CONFIG_MTK_APU_DEBUG) + INIT_WORK(&apu->pwron_dbg_wk, apu_deepidle_pwron_dbg_fn); +#endif + + return ret; +} + +static void apu_deepidle_exit(struct mtk_apu *apu) +{ +#if IS_ENABLED(CONFIG_MTK_APU_DEBUG) + flush_work(&apu->pwron_dbg_wk); +#endif + apu_ipi_unregister(apu, APU_IPI_DEEP_IDLE); + if (apu->apu_deepidle_workq) + destroy_workqueue(apu->apu_deepidle_workq); +} + +void apu_deepidle_power_on_aputop(struct mtk_apu *apu) +{ + struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops; + + if (pm_runtime_suspended(apu->dev)) { + apu->conf_buf->time_offset = sched_clock(); + hw_ops->power_on(apu); + + #if IS_ENABLED(CONFIG_MTK_APU_DEBUG) + schedule_work(&apu->pwron_dbg_wk); + #endif + } +} + +static void apu_timesync_work_func(struct work_struct *work) +{ + struct mtk_apu *apu = container_of(work, struct mtk_apu, timesync_work); + int ret; + + apu->timesync_stamp = sched_clock(); + ret = apu_ipi_send(apu, APU_IPI_TIMESYNC, &apu->timesync_stamp, + sizeof(u64), 0); + if (ret) { + dev_err(apu->dev, "timsync ipi fail(%d)\n", ret); + return; + } +} + +static void apu_timesync_handler(void *data, u32 len, void *priv) +{ + struct mtk_apu *apu = (struct mtk_apu *)priv; + + queue_work(apu->timesync_wq, &apu->timesync_work); +} + +static int apu_timesync_init(struct mtk_apu *apu) +{ + int ret; + + apu->timesync_wq = alloc_workqueue("apu_timesync", + WQ_UNBOUND | WQ_HIGHPRI, 0); + if (!apu->timesync_wq) { + dev_info(apu->dev, "%s: failed to allocate wq for timesync\n", + __func__); + return -ENOMEM; + } + INIT_WORK(&apu->timesync_work, apu_timesync_work_func); + + ret = apu_ipi_register(apu, APU_IPI_TIMESYNC, apu_timesync_handler, + apu); + if (ret) { + dev_info(apu->dev, "%s: failed to register IPI\n", __func__); + destroy_workqueue(apu->timesync_wq); + apu->timesync_wq = NULL; + return ret; + } + + pr_info("%s %d\n", __func__, __LINE__); + return 0; +} + +static void apu_timesync_remove(struct mtk_apu *apu) +{ + apu_ipi_unregister(apu, APU_IPI_TIMESYNC); + + if (apu->timesync_wq) + destroy_workqueue(apu->timesync_wq); +} + +static irqreturn_t apu_wdt_isr(int irq, void *private_data) +{ + unsigned long flags; + struct mtk_apu *apu = (struct mtk_apu *)private_data; + struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops; + + spin_lock_irqsave(&apu->reg_lock, flags); + if (hw_ops->cg_gating) + hw_ops->cg_gating(apu); + + /* disable apu wdt */ + iowrite32(ioread32(apu->apu_wdt + WDT_CTRL0) & ~WDT_EN, + apu->apu_wdt + WDT_CTRL0); + /* clear wdt interrupt */ + iowrite32(0x1, apu->apu_wdt); + spin_unlock_irqrestore(&apu->reg_lock, flags); + disable_irq_nosync(apu->wdt_irq_number); + + return IRQ_HANDLED; +} + +static int apu_excep_init(struct mtk_apu *apu) +{ + struct device *dev = apu->dev; + struct platform_device *pdev = to_platform_device(dev); + u32 irq_type = irq_get_trigger_type(apu->wdt_irq_number); + int ret = 0; + + apu->wdt_irq_number = platform_get_irq_byname(pdev, "apu_wdt"); + ret = devm_request_irq(dev, apu->wdt_irq_number, apu_wdt_isr, + irq_type, "apusys_wdt", apu); + if (ret < 0) + dev_err(dev, "%s Failed to request irq %d: %d\n", + __func__, apu->wdt_irq_number, ret); + + return ret; +} + +static void apu_excep_remove(struct mtk_apu *apu) +{ + unsigned long flags; + + spin_lock_irqsave(&apu->reg_lock, flags); + /* disable apu wdt */ + iowrite32(ioread32(apu->apu_wdt + WDT_CTRL0) & ~WDT_EN, + apu->apu_wdt + WDT_CTRL0); + /* clear wdt interrupt */ + iowrite32(0x1, apu->apu_wdt); + spin_unlock_irqrestore(&apu->reg_lock, flags); + disable_irq(apu->wdt_irq_number); +} + +#define CONFIG_SIZE (round_up(sizeof(struct config_v1), PAGE_SIZE)) +static void apu_config_user_ptr_init(const struct mtk_apu *apu) +{ + struct config_v1 *config; + struct config_v1_entry_table *entry_table; + + if (!apu || !apu->conf_buf) { + pr_err("%s: error\n", __func__); + return; + } + + config = apu->conf_buf; + config->header_magic = 0xc0de0101; + config->header_rev = 0x1; + config->entry_offset = offsetof(struct config_v1, entry_tbl); + config->config_size = sizeof(struct config_v1); + + entry_table = (struct config_v1_entry_table *)((void *)config + + config->entry_offset); + + entry_table->user_entry[0] = offsetof(struct config_v1, user0_data); + entry_table->user_entry[1] = offsetof(struct config_v1, user1_data); + entry_table->user_entry[2] = offsetof(struct config_v1, user2_data); + entry_table->user_entry[3] = offsetof(struct config_v1, user3_data); + entry_table->user_entry[4] = offsetof(struct config_v1, user4_data); +} + +static int apu_config_setup(struct mtk_apu *apu) +{ + struct device *dev = apu->dev; + unsigned long flags; + int ret; + + apu->conf_buf = dma_alloc_coherent(apu->dev, CONFIG_SIZE, + &apu->conf_da, GFP_KERNEL); + + if (!apu->conf_buf || apu->conf_da == 0) { + dev_info(dev, "%s: dma_alloc_coherent fail\n", __func__); + return -ENOMEM; + } + memset(apu->conf_buf, 0, CONFIG_SIZE); + + apu_config_user_ptr_init(apu); + spin_lock_irqsave(&apu->reg_lock, flags); + iowrite32((u32)apu->conf_da, apu->apu_mbox + HOST_CONFIG_ADDR); + spin_unlock_irqrestore(&apu->reg_lock, flags); + + apu->conf_buf->time_offset = sched_clock(); + ret = apu_ipi_config_init(apu); + if (ret) { + dev_info(dev, "apu ipi config init failed\n"); + goto out; + } + + ret = sw_logger_config_init(apu->conf_buf); + if (ret) { + dev_err(dev, "sw logger config init failed\n"); + goto err_sw_logger; + } + + return 0; + +err_sw_logger: + apu_ipi_config_remove(apu); +out: + return ret; +} + +static void apu_config_remove(struct mtk_apu *apu) +{ + sw_logger_config_remove(); + apu_ipi_config_remove(apu); + dma_free_coherent(apu->dev, CONFIG_SIZE, + apu->conf_buf, apu->conf_da); +} + +static int apu_dram_boot_init(struct mtk_apu *apu) +{ + struct device *dev = apu->dev; + int ret = 0; + int map_sg_sz = 0; + void *domain; + struct sg_table sgt; + phys_addr_t pa; + u32 boundary; + u64 iova; + + domain = iommu_get_domain_for_dev(apu->dev); + if (!domain) { + dev_err(dev, "%s: iommu_get_domain_for_dev fail\n", + __func__); + return -ENOMEM; + } + + /* Allocate code buffer */ + apu->code_buf = dma_alloc_coherent(apu->dev, CODE_BUF_SIZE, + &apu->code_da, GFP_KERNEL); + if (!apu->code_buf || apu->code_da == 0) { + dev_err(dev, "%s: dma_alloc_coherent fail\n", __func__); + return -ENOMEM; + } + memset(apu->code_buf, 0, CODE_BUF_SIZE); + boundary = (u32)upper_32_bits(apu->code_da); + iova = CODE_BUF_DA | ((u64)boundary << 32); + + /* Convert IOVA to sgtable */ + sgt.sgl = NULL; + ret = dma_get_sgtable(apu->dev, &sgt, apu->code_buf, + apu->code_da, CODE_BUF_SIZE); + if (ret < 0 || !sgt.sgl) { + dev_err(dev, "get sgtable fail\n"); + return -EINVAL; + } + + /* Map sg_list to MD32_BOOT_ADDR */ + map_sg_sz = iommu_map_sg(domain, iova, sgt.sgl, + sgt.nents, IOMMU_READ | IOMMU_WRITE); + if (map_sg_sz != CODE_BUF_SIZE) + return -EINVAL; + + pa = iommu_iova_to_phys(domain, iova + CODE_BUF_SIZE - SZ_4K); + if (!pa) + ret = -EPERM; + + return ret; +} + +static void apu_dram_boot_remove(struct mtk_apu *apu) +{ + void *domain = iommu_get_domain_for_dev(apu->dev); + u32 boundary = (u32)upper_32_bits(apu->code_da); + u64 iova = CODE_BUF_DA | ((u64)boundary << 32); + + if (domain) + iommu_unmap(domain, iova, CODE_BUF_SIZE); + + dma_free_coherent(apu->dev, CODE_BUF_SIZE, apu->code_buf, apu->code_da); +} + +static void apu_power_work_fn(struct work_struct *work) +{ + struct mtk_apu *apu = container_of(work, struct mtk_apu, pwr_work); + struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops; + + if (!apu->boot_done) { + pr_info("%s: uP not boot yet, skip pm nfy\n", __func__); + return; + } + hw_ops->stop(apu); +} + +static int apu_power_genpd_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct mtk_apu *apu = container_of(apu_pwr_work, + struct mtk_apu, pwr_work); + + switch (event) { + case GENPD_NOTIFY_OFF: + apu->top_genpd = false; + pr_info("%s: apu top off\n", __func__); + queue_work(apu_pwr_wq, apu_pwr_work); + break; + case GENPD_NOTIFY_ON: + apu->top_genpd = true; + pr_info("%s: apu top on\n", __func__); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block apu_genpd_nb = { + .notifier_call = apu_power_genpd_notifier, +}; + +static int apu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct rproc *rproc; + struct mtk_apu *apu; + struct mtk_apu_platdata *data; + struct mtk_apu_hw_ops *hw_ops; + char *fw_name = "mrv.elf"; + int ret = 0; + + data = (struct mtk_apu_platdata *)of_device_get_match_data(dev); + if (!data) { + dev_info(dev, "%s: of_device_get_match_data fail\n", __func__); + return -EINVAL; + } + hw_ops = &data->ops; + + rproc = rproc_alloc(dev, + np->name, + &apu_ops, + fw_name, + sizeof(struct mtk_apu)); + + if (!rproc) { + dev_info(dev, "unable to allocate remoteproc\n"); + return -ENOMEM; + } + + if (data->flags & F_AUTO_BOOT) + rproc->auto_boot = true; + else + rproc->auto_boot = false; + + apu = (struct mtk_apu *)rproc->priv; + apu->rproc = rproc; + apu->dev = dev; + apu->platdata = data; + platform_set_drvdata(pdev, apu); + spin_lock_init(&apu->reg_lock); + + /* detect mandaotry platform data*/ + if (!hw_ops->apu_memmap_init || !hw_ops->apu_memmap_remove || + !hw_ops->start || !hw_ops->stop || + !hw_ops->power_on || !hw_ops->power_off || + !apu->platdata->ipi_attrs) { + WARN_ON(1); + return -EINVAL; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + ret = dma_set_mask_and_coherent(apu->dev, DMA_BIT_MASK(64)); + if (ret) { + pr_info("%s: dma_set_mask_and_coherent fail(%d)\n", + __func__, ret); + goto out_free_rproc; + } + + ret = hw_ops->apu_memmap_init(apu); + if (ret) + goto remove_apu_memmap; + + ret = apu_config_setup(apu); + if (ret) + goto remove_apu_config_setup; + + ret = apu_dram_boot_init(apu); + if (ret) + goto remove_apu_dram_boot; + + ret = apu_ipi_init(pdev, apu); + if (ret) + goto remove_apu_ipi; + + if (data->flags & F_AUTO_BOOT) { + ret = apu_deepidle_init(apu); + if (ret < 0) + goto remove_apu_deepidle; + } + + ret = apu_timesync_init(apu); + if (ret) + goto remove_apu_timesync; + + ret = apu_excep_init(apu); + if (ret < 0) + goto remove_apu_excep; + + if (data->flags & F_PRELOAD_FIRMWARE) + rproc->state = RPROC_DETACHED; + + ret = rproc_add(rproc); + if (ret < 0) { + dev_info(dev, "boot fail ret:%d\n", ret); + goto remove_apu_excep; + } + + if (hw_ops->init) { + ret = hw_ops->init(apu); + if (ret) + goto del_rproc; + } + + apu_pwr_wq = alloc_workqueue("apu_pwr_wq", + WQ_UNBOUND | WQ_HIGHPRI, 0); + if (!apu_pwr_wq) { + dev_info(dev, "%s: failed to allocate wq for rv power\n", + __func__); + goto del_rproc; + } + INIT_WORK(&apu->pwr_work, apu_power_work_fn); + apu_pwr_work = &apu->pwr_work; + + pm_runtime_put_sync(&pdev->dev); + dev_pm_genpd_add_notifier(dev, &apu_genpd_nb); + + return 0; + +del_rproc: + rproc_del(rproc); + +remove_apu_excep: + apu_excep_remove(apu); + +remove_apu_timesync: + apu_timesync_remove(apu); + +remove_apu_deepidle: + apu_deepidle_exit(apu); + +remove_apu_ipi: + apu_ipi_remove(apu); + +remove_apu_dram_boot: + apu_dram_boot_remove(apu); + +remove_apu_config_setup: + apu_config_remove(apu); + +remove_apu_memmap: + hw_ops->apu_memmap_remove(apu); + +out_free_rproc: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + rproc_free(rproc); + + return ret; +} + +static int apu_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_apu *apu = platform_get_drvdata(pdev); + struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops; + + if (hw_ops->exit) + hw_ops->exit(apu); + + dev_pm_genpd_remove_notifier(dev); + pm_runtime_put_sync(&pdev->dev); + destroy_workqueue(apu_pwr_wq); + rproc_del(apu->rproc); + apu_deepidle_exit(apu); + apu_excep_remove(apu); + apu_timesync_remove(apu); + apu_ipi_remove(apu); + apu_dram_boot_remove(apu); + apu_config_remove(apu); + hw_ops->apu_memmap_remove(apu); + pm_runtime_disable(dev); + rproc_free(apu->rproc); + + return 0; +} + +const struct mtk_apu_platdata mt8192_platdata = { + .flags = F_AUTO_BOOT, + .ipi_attrs = mt81xx_ipi_attrs, + .ops = { + .start = mt81xx_rproc_start, + .stop = mt81xx_rproc_stop, + .resume = mt81xx_rproc_resume, + .apu_memmap_init = mt81xx_apu_memmap_init, + .apu_memmap_remove = mt81xx_apu_memmap_remove, + .power_on = mt81xx_apu_power_on, + .power_off = mt81xx_apu_power_off, + }, +}; + +static const struct of_device_id apu_of_match[] = { + { .compatible = "mediatek,mt8192-apusys-rv", .data = &mt8192_platdata}, + {}, +}; +MODULE_DEVICE_TABLE(of, apu_of_match); + +static int apu_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int apu_runtime_resume(struct device *dev) +{ + struct mtk_apu *apu = dev_get_drvdata(dev); + struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops; + + if (!apu->boot_done) + return 0; + + if (hw_ops->resume) + return hw_ops->resume(apu); + + return 0; +} + +static const struct dev_pm_ops apu_pm_ops = { + SET_RUNTIME_PM_OPS(apu_runtime_suspend, apu_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver apu_driver = { + .probe = apu_probe, + .remove = apu_remove, + .driver = { + .name = "mtk-apu", + .of_match_table = of_match_ptr(apu_of_match), + .pm = &apu_pm_ops, + }, +}; + +static int __init apu_rproc_init(void) +{ + return platform_driver_register(&apu_driver); +} + +static void __exit apu_rproc_exit(void) +{ + platform_driver_unregister(&apu_driver); +} + +module_init(apu_rproc_init); +module_exit(apu_rproc_exit); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MediaTek APU control driver"); diff --git a/include/linux/remoteproc/mtk-apu-config.h b/include/linux/remoteproc/mtk-apu-config.h new file mode 100644 index 000000000000..fee3b0334502 --- /dev/null +++ b/include/linux/remoteproc/mtk-apu-config.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef APU_CONFIG_H +#define APU_CONFIG_H + +struct apu_ipi_config { + u64 in_buf_da; + u64 out_buf_da; +} __packed; + +struct vpu_init_info { + u32 vpu_num; + u32 cfg_addr; + u32 cfg_size; + u32 algo_info_ptr[3 * 2]; + u32 rst_vec[3]; + u32 dmem_addr[3]; + u32 imem_addr[3]; + u32 iram_addr[3]; + u32 cmd_addr[3]; + u32 log_addr[3]; + u32 log_size[3]; +} __packed; + +struct apusys_chip_data { + u32 s_code; + u32 b_code; + u32 r_code; + u32 a_code; +} __packed; + +struct logger_init_info { + u32 iova; +} __packed; + +struct reviser_init_info { + u32 boundary; + u32 dram[32]; +} __packed; + +enum user_config { + APU_IPI_CONFIG = 0x0, + VPU_INIT_INFO, + APUSYS_CHIP_DATA, + LOGGER_INIT_INFO, + REVISER_INIT_INFO, + USER_CONFIG_MAX +}; + +struct config_v1_entry_table { + u32 user_entry[USER_CONFIG_MAX]; +} __packed; + +struct config_v1 { + /* header begin */ + u32 header_magic; + u32 header_rev; + u32 entry_offset; + u32 config_size; + /* header end */ + /* do not add new member before this line */ + + /* system related config begin */ + u32 ramdump_offset; + u32 ramdump_type; + u64 time_offset; + /* system related config end */ + + /* entry table */ + u8 entry_tbl[sizeof(struct config_v1_entry_table)]; + + /* user data payload begin */ + u8 user0_data[sizeof(struct apu_ipi_config)]; + u8 user1_data[sizeof(struct vpu_init_info)]; + u8 user2_data[sizeof(struct apusys_chip_data)]; + u8 user3_data[sizeof(struct logger_init_info)]; + u8 user4_data[sizeof(struct reviser_init_info)]; + /* user data payload end */ +} __packed; + +static inline void *get_apu_config_user_ptr(struct config_v1 *conf, + enum user_config user_id) +{ + struct config_v1_entry_table *entry_tbl; + + if (!conf) + return NULL; + + if (user_id >= USER_CONFIG_MAX) + return NULL; + + entry_tbl = (struct config_v1_entry_table *) + ((void *)conf + conf->entry_offset); + + return (void *)conf + entry_tbl->user_entry[user_id]; +} +#endif /* APU_CONFIG_H */ diff --git a/include/linux/remoteproc/mtk-apu.h b/include/linux/remoteproc/mtk-apu.h new file mode 100644 index 000000000000..a5d3ff8a2d48 --- /dev/null +++ b/include/linux/remoteproc/mtk-apu.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef APU_H +#define APU_H + +#include +#include +#include +#include + +/* setup the SMC command ops */ +#define MTK_SIP_APU_START_MCU 0x00 +#define MTK_SIP_APU_STOP_MCU 0x01 + +/* md32_sysctrl register definition */ +#define MD32_SYS_CTRL 0x0 +#define MD32_MON_PC 0x838 +#define MD32_MON_LR 0x83c +#define MD32_MON_SP 0x840 +#define MD32_STATUS 0x844 + +/*wdt register */ +#define WDT_INT 0x0 +#define WDT_CTRL0 0x4 +#define WDT_EN BIT(31) + +/* apu_mbox spare register: mbox 0..6, spare 0..3 */ +#define REG_MBOX_SPARE(mbox, reg) \ + ((0x40 + (0x100 * (mbox))) + ((reg) * 0x4)) + +/* rv setup */ +#define HOST_CONFIG_ADDR REG_MBOX_SPARE(0, 2) +#define F_PRELOAD_FIRMWARE BIT(0) +#define F_AUTO_BOOT BIT(1) + +#define TCM_SIZE (SZ_128 * SZ_1K) +#define CODE_BUF_SIZE SZ_1M +#define DRAM_DUMP_SIZE (CODE_BUF_SIZE - TCM_SIZE) +#define REG_SIZE (4UL * 151UL) +#define TBUF_SIZE (4UL * 32UL) +#define CACHE_DUMP_SIZE (37UL * SZ_1K) +#define DRAM_OFFSET (0x0UL) +#define DRAM_DUMP_OFFSET (TCM_SIZE) +#define TCM_OFFSET (0x1d700000UL) +#define CODE_BUF_DA (DRAM_OFFSET) + +/* ipi */ +#define APU_FW_VER_LEN 32 +#define APU_SHARE_BUFFER_SIZE SZ_256 + +#define IPI_LOCKED 1 +#define IPI_UNLOCKED 0 + +#define IPI_HOST_INITIATE 0 +#define IPI_APU_INITIATE 1 +#define IPI_WITH_ACK 1 +#define IPI_WITHOUT_ACK 0 + +enum { + APU_IPI_INIT = 0, + APU_IPI_NS_SERVICE, + APU_IPI_DEEP_IDLE, + APU_IPI_CTRL_RPMSG, + APU_IPI_MIDDLEWARE, + APU_IPI_REVISER_RPMSG, + APU_IPI_PWR_TX, + APU_IPI_PWR_RX, + APU_IPI_MDLA_TX, + APU_IPI_MDLA_RX, + APU_IPI_TIMESYNC, + APU_IPI_EDMA_TX, + APU_IPI_MNOC_TX, + APU_IPI_MAX, +}; + +struct mtk_apu; + +struct mtk_apu_hw_ops { + int (*init)(struct mtk_apu *apu); + int (*exit)(struct mtk_apu *apu); + int (*start)(struct mtk_apu *apu); + int (*stop)(struct mtk_apu *apu); + int (*resume)(struct mtk_apu *apu); + int (*apu_memmap_init)(struct mtk_apu *apu); + void (*apu_memmap_remove)(struct mtk_apu *apu); + void (*cg_gating)(struct mtk_apu *apu); + void (*cg_ungating)(struct mtk_apu *apu); + void (*rv_cachedump)(struct mtk_apu *apu); + + /* power related ops */ + int (*power_init)(struct mtk_apu *apu); + int (*power_on)(struct mtk_apu *apu); + int (*power_off)(struct mtk_apu *apu); +}; + +struct apu_ipi { + char *name; + unsigned int direction:1; + unsigned int ack:1; +}; + +struct mtk_apu_platdata { + u32 flags; + struct mtk_apu_hw_ops ops; + const struct apu_ipi *ipi_attrs; +}; + +struct dpidle_msg { + u32 cmd; + u32 ack; +}; + +struct apu_run { + s8 fw_ver[APU_FW_VER_LEN]; + u32 signaled; + wait_queue_head_t wq; +}; + +struct apu_ipi_desc { + struct mutex lock; /*ipi handler mutex */ + ipi_handler_t handler; + void *priv; + /* + * positive: host-initiated ipi outstanding count + * negative: apu-initiated ipi outstanding count + */ + int usage_cnt; +}; + +struct mtk_share_obj { + u8 share_buf[APU_SHARE_BUFFER_SIZE]; +}; + +struct mtk_apu { + struct rproc *rproc; + struct device *dev; + void __iomem *apu_mbox; + void __iomem *md32_sysctrl; + void __iomem *apu_wdt; + int wdt_irq_number; + spinlock_t reg_lock; /* register r/w lock */ + + /* mailbox */ + struct mbox_client cl; + struct mbox_chan *ch; + + /* Buffer to place execution area */ + void *code_buf; + dma_addr_t code_da; + + /* Buffer to place config area */ + struct config_v1 *conf_buf; + dma_addr_t conf_da; + + /* to synchronize boot status of remote processor */ + struct apu_run run; + + /* to prevent multiple ipi_send run concurrently */ + struct mutex send_lock; + spinlock_t usage_cnt_lock; /* ipi occipued lock */ + struct apu_ipi_desc ipi_desc[APU_IPI_MAX]; + bool ipi_id_ack[APU_IPI_MAX]; /* per-ipi ack */ + bool ipi_inbound_locked; + wait_queue_head_t ack_wq; /* for waiting for ipi ack */ + + /* ipi */ + struct rproc_subdev *rpmsg_subdev; + dma_addr_t recv_buf_da; + struct mtk_share_obj *recv_buf; + dma_addr_t send_buf_da; + struct mtk_share_obj *send_buf; + + /* time sync */ + struct work_struct timesync_work; + struct workqueue_struct *timesync_wq; + u64 timesync_stamp; + + /*deep idle */ + struct dpidle_msg recv_msg; + struct work_struct deepidle_work; + struct workqueue_struct *apu_deepidle_workq; + struct work_struct pwron_dbg_wk; + + struct mtk_apu_platdata *platdata; + + /* power status */ + bool boot_done; + struct work_struct pwr_work; + bool top_genpd; +}; + +int apu_ipi_config_init(struct mtk_apu *apu); +void apu_ipi_config_remove(struct mtk_apu *apu); +void apu_ipi_remove(struct mtk_apu *apu); +int apu_ipi_init(struct platform_device *pdev, struct mtk_apu *apu); +int apu_ipi_register(struct mtk_apu *apu, u32 id, + ipi_handler_t handler, void *priv); +void apu_ipi_unregister(struct mtk_apu *apu, u32 id); +int apu_ipi_send(struct mtk_apu *apu, u32 id, void *data, u32 len, + u32 wait_ms); +int apu_ipi_lock(struct mtk_apu *apu); +void apu_ipi_unlock(struct mtk_apu *apu); + +void apu_deepidle_power_on_aputop(struct mtk_apu *apu); + +#if (IS_ENABLED(CONFIG_DEBUG_FS) && IS_ENABLED(CONFIG_MTK_APU_DEBUG)) +int sw_logger_config_init(struct config_v1 *conf); +void sw_logger_config_remove(void); +#else +static inline int sw_logger_config_init(struct config_v1 *conf) { return 0; } +static inline void sw_logger_config_remove(void) { } +#endif + +#endif /* APU_H */ From patchwork Fri Dec 10 17:25:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670305 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4BFE1C433F5 for ; Fri, 10 Dec 2021 17:27:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244552AbhLJRbC (ORCPT ); Fri, 10 Dec 2021 12:31:02 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:48998 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244514AbhLJRa2 (ORCPT ); Fri, 10 Dec 2021 12:30:28 -0500 X-UUID: 69c60ca8554f4c30a30c518e6f22846e-20211211 X-UUID: 69c60ca8554f4c30a30c518e6f22846e-20211211 Received: from mtkmbs10n1.mediatek.inc [(172.21.101.34)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 15731223; Sat, 11 Dec 2021 01:26:49 +0800 Received: from mtkexhb01.mediatek.inc (172.21.101.102) by mtkmbs07n1.mediatek.inc (172.21.101.16) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:47 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkexhb01.mediatek.inc (172.21.101.102) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:47 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:46 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 09/17] soc: mediatek: apu: Add Apu power driver Date: Sat, 11 Dec 2021 01:25:57 +0800 Message-ID: <20211210172605.30618-10-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add APU power driver to support for subsys clock and regulator controller. Add MT8192 platform APU power driver's platform data. Signed-off-by: Flora Fu --- drivers/soc/mediatek/apusys/Kconfig | 23 + drivers/soc/mediatek/apusys/Makefile | 5 + drivers/soc/mediatek/apusys/apu-pwr-dbg.c | 167 ++++++ drivers/soc/mediatek/apusys/apu-pwr-ipi.c | 377 +++++++++++++ drivers/soc/mediatek/apusys/apu-pwr.c | 613 ++++++++++++++++++++++ drivers/soc/mediatek/apusys/apu-pwr.h | 260 +++++++++ 6 files changed, 1445 insertions(+) create mode 100644 drivers/soc/mediatek/apusys/apu-pwr-dbg.c create mode 100644 drivers/soc/mediatek/apusys/apu-pwr-ipi.c create mode 100644 drivers/soc/mediatek/apusys/apu-pwr.c create mode 100644 drivers/soc/mediatek/apusys/apu-pwr.h diff --git a/drivers/soc/mediatek/apusys/Kconfig b/drivers/soc/mediatek/apusys/Kconfig index 332e323e02ae..1daf73c74c17 100644 --- a/drivers/soc/mediatek/apusys/Kconfig +++ b/drivers/soc/mediatek/apusys/Kconfig @@ -11,4 +11,27 @@ config MTK_APU_PM APU power domain shall be enabled before accessing the internal sub modules. +config MTK_APU + tristate "MediaTek APUSYS Support" + select REGMAP + select MAILBOX + select MTK_APU_MBOX + select MTK_APU_RPROC + select MTK_SCP + help + Say yes here to add support for the APU tinysys and enable + sub modules include apu power and middleware. + The tinysys firmware is loaded and booted from Kernel side through + rproc framework. The related sub modules in application processor + use IPI to communicate with APU tinysys. + +config MTK_APU_DEBUG + tristate "MediaTek APUSYS debug functions" + depends on MTK_APU + help + Say yes here to enable debug features in APU. + The debug configuration will enable kernel driver debug and register a + logger for retrieving debug logs from remote processor. + Disable it if you don't need them. + endif # MTK_APUSYS diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile index 8821c0f0b7b7..8fff18d63dc1 100644 --- a/drivers/soc/mediatek/apusys/Makefile +++ b/drivers/soc/mediatek/apusys/Makefile @@ -1,2 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o + +obj-$(CONFIG_MTK_APU) += mtk_apu_pwr.o +mtk_apu_pwr-objs += apu-pwr.o +mtk_apu_pwr-objs += apu-pwr-ipi.o +mtk_apu_pwr-$(CONFIG_MTK_APU_DEBUG) += apu-pwr-dbg.o diff --git a/drivers/soc/mediatek/apusys/apu-pwr-dbg.c b/drivers/soc/mediatek/apusys/apu-pwr-dbg.c new file mode 100644 index 000000000000..ee81271cbb2c --- /dev/null +++ b/drivers/soc/mediatek/apusys/apu-pwr-dbg.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "apu-pwr.h" + +#define DEBUG_MAX_ARGS_NUM (5) + +struct dentry *apu_power_debugfs; + +static int apu_power_set_parameter(struct apu_power *apupwr, + enum APU_POWER_PARAM param, + u32 argc, u32 *args) +{ + struct device *dev = apupwr->dev; + int ret = 0; + + switch (param) { + case POWER_PARAM_FIX_OPP: + if (args[0] == 0) { + apu_update_fixed_opp_by_reg(dev, 0xffffffff); + apu_power_notify_uP_opp_limit(apupwr, + OPP_LIMIT_FIX_OPP); + } + break; + case POWER_PARAM_DVFS_DEBUG: + if (args[0] >= 0 && args[0] <= 0xffffffff) { + apu_update_fixed_opp_by_reg(dev, args[0]); + apu_power_notify_uP_opp_limit(apupwr, + OPP_LIMIT_DVFS_DEBUG); + } + break; + case POWER_PARAM_ULOG_LEVEL: + ret = (argc == 1) ? 0 : -EINVAL; + if (ret) { + dev_err(dev, + "invalid argument, expected:1, received:%d\n", + argc); + goto out; + } + apupwr->dbg_option = POWER_PARAM_ULOG_LEVEL; + apupwr->ulog_level = args[0]; + apu_power_set_ulog_level(apupwr, args[0]); + break; + + default: + dev_err(dev, "unsupport the power parameter:%d\n", param); + break; + } + +out: + return ret; +} + +static int apu_power_dbg_show(struct seq_file *s, void *unused) +{ + struct apu_power *apupwr = (struct apu_power *)s->private; + u32 ulog_level = apupwr->ulog_level; + u32 dbg_option = apupwr->dbg_option; + + switch (dbg_option) { + case POWER_PARAM_ULOG_LEVEL: + seq_printf(s, "ulog_level = %d\n", ulog_level); + break; + default: + break; + } + + return 0; +} + +static int apu_power_dbg_open(struct inode *inode, struct file *file) +{ + return single_open(file, apu_power_dbg_show, inode->i_private); +} + +static ssize_t apu_power_dbg_write(struct file *filp, + const char __user *buffer, + size_t count, loff_t *f_pos) +{ + struct apu_power *obj = file_inode(filp)->i_private; + char *tmp, *token, *cursor; + int ret, i; + enum APU_POWER_PARAM param; + unsigned int args[DEBUG_MAX_ARGS_NUM]; + + tmp = kzalloc(count + 1, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + ret = copy_from_user(tmp, buffer, count); + if (ret) { + dev_err(obj->dev, "copy_from_user failed, ret=%d\n", ret); + goto out; + } + + tmp[count] = '\0'; + cursor = tmp; + + /* parse a command */ + token = strsep(&cursor, " "); + if (strcmp(token, "fix_opp") == 0) { + param = POWER_PARAM_FIX_OPP; + } else if (strcmp(token, "dvfs_debug") == 0) { + param = POWER_PARAM_DVFS_DEBUG; + } else if (strcmp(token, "ulog") == 0) { + param = POWER_PARAM_ULOG_LEVEL; + } else { + ret = -EINVAL; + dev_err(obj->dev, "no power param[%s]!\n", token); + goto out; + } + + /* parse arguments */ + for (i = 0; + i < DEBUG_MAX_ARGS_NUM && (token = strsep(&cursor, " ")); i++) { + ret = kstrtouint(token, 0, &args[i]); + if (ret) { + dev_err(obj->dev, "fail to parse args[%d]\n", i); + goto out; + } + } + + apu_power_set_parameter(obj, param, i, args); + ret = count; +out: + kfree(tmp); + return ret; +} + +static const struct file_operations apu_power_dbg_fops = { + .open = apu_power_dbg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, + .write = apu_power_dbg_write, +}; + +void apu_power_debugfs_init(struct apu_power *apupwr) +{ + int ret; + + apu_power_debugfs = debugfs_create_dir("apu_power", + apupwr->dbg_root); + ret = IS_ERR_OR_NULL(apu_power_debugfs); + if (ret) { + dev_err(apupwr->dev, "failed to create debug dir.\n"); + apu_power_debugfs = NULL; + } + + debugfs_create_file("power", (0644), + apu_power_debugfs, apupwr, &apu_power_dbg_fops); +} + +void apu_power_debugfs_exit(void) +{ + debugfs_remove_recursive(apu_power_debugfs); +} diff --git a/drivers/soc/mediatek/apusys/apu-pwr-ipi.c b/drivers/soc/mediatek/apusys/apu-pwr-ipi.c new file mode 100644 index 000000000000..c7bbcd1de73d --- /dev/null +++ b/drivers/soc/mediatek/apusys/apu-pwr-ipi.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apu-pwr.h" + +#define APU_TX_MSG_TIMEOUT 1000 +#define RX_MAGIC0 0xaabb +#define RX_MAGIC1 0xccdd + +#define to_rpmsg_driver(__drv) container_of(__drv, struct rpmsg_driver, drv) +#define tx_to_apu_power(__d) \ + container_of(__d, struct apu_power, tx_rpmsg_driver) +#define rx_to_apu_power(__d) \ + container_of(__d, struct apu_power, rx_rpmsg_driver) + +struct apu_power_rpmsg { + struct rpmsg_device *rpdev; + struct rpmsg_endpoint *ept; + struct mutex lock; /* wait remote msg */ + struct completion comp; + int initialized; + struct power_ipi_cmd_reply ipi_rply; +}; + +static int apu_power_tx_send(struct apu_power *apupwr, + struct power_cmd_AP *pcmd, + u32 *data0, u32 *data1) +{ + struct rpmsg_device *rpdev = apupwr->tx_rpmsg_device; + struct apu_power_rpmsg *power_rpmsg = dev_get_drvdata(&rpdev->dev); + struct power_ipi_cmd_AP ipi_cmd_send; + struct timespec64 ts64; + unsigned long timeout = msecs_to_jiffies(APU_TX_MSG_TIMEOUT); + u64 timestamp; + int ret = 0; + + if (!power_rpmsg || !power_rpmsg->initialized) { + dev_err(&rpdev->dev, "%s: rpmsg not valid\n", __func__); + ret = -EINVAL; + return ret; + } + + memset(&ipi_cmd_send, 0, sizeof(struct power_ipi_cmd_AP)); + ktime_get_real_ts64(&ts64); + timestamp = ((ts64.tv_sec & 0xFFF) * USEC_PER_SEC) + + (ts64.tv_nsec / NSEC_PER_USEC); + ipi_cmd_send.cmd_sn = (u32)timestamp; + ipi_cmd_send.pwr_cmd = *pcmd; + + /* transport normal raw data */ + ipi_cmd_send.data0 = *data0; + ipi_cmd_send.data1 = *data1; + + mutex_lock(&power_rpmsg->lock); + reinit_completion(&power_rpmsg->comp); + + ret = rpmsg_send(power_rpmsg->ept, + (void *)&ipi_cmd_send, sizeof(ipi_cmd_send)); + if (ret) { + dev_err(&rpdev->dev, + "%s: failed to send msg to remote, ret=%d\n", + __func__, ret); + goto out; + } + + ret = wait_for_completion_interruptible_timeout(&power_rpmsg->comp, + timeout); + if (ret <= 0) { + dev_err(&rpdev->dev, + "%s waiting for ack interrupted or timeout, ret=%d\n", + __func__, ret); + goto out; + } + +out: + mutex_unlock(&power_rpmsg->lock); + return ret; +} + +void apu_power_set_ulog_level(struct apu_power *apupwr, + int level) +{ + struct power_cmd_AP pwr_cmd; + u32 data0; + u32 data1; + + memset(&pwr_cmd, 0, sizeof(struct power_cmd_AP)); + memset(&data0, 0, sizeof(u32)); + memset(&data1, 0, sizeof(u32)); + + pwr_cmd.req_id = CHANGE_LOG_LEVEL; + data0 = level; + apu_power_tx_send(apupwr, &pwr_cmd, &data0, &data1); +} + +void apu_power_notify_uP_opp_limit(struct apu_power *apupwr, + enum request_id_AP req) +{ + struct power_cmd_AP pwr_cmd; + u32 data0; + u32 data1; + + memset(&pwr_cmd, 0, sizeof(struct power_cmd_AP)); + memset(&data0, 0, sizeof(u32)); + memset(&data1, 0, sizeof(u32)); + + pwr_cmd.req_id = req; + switch (pwr_cmd.req_id) { + case OPP_LIMIT_THERM: + case OPP_LIMIT_FIX_OPP: + case OPP_LIMIT_DVFS_DEBUG: + break; + default: + return; + } + + apu_power_tx_send(apupwr, &pwr_cmd, &data0, &data1); +} + +static int apu_power_rx_callback(struct rpmsg_device *rpdev, void *data, + int len, void *priv, u32 src) +{ + struct apu_power_rpmsg *power_rpmsg = dev_get_drvdata(&rpdev->dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct apu_power *apupwr = rx_to_apu_power(rpdrv); + int ret = 0; + struct device *dev = &rpdev->dev; + struct power_ipi_cmd_uP *received_data = NULL; + struct power_ipi_cmd_reply reply_data; + static u32 prev_cmd_sn; + u32 cmd_sn; + struct power_cmd_uP pwr_cmd; + u32 data0; + u32 data1; + + if (!len) { + dev_warn(dev, "apu power rpmsg received empty cmd"); + return -EINVAL; + } + + received_data = data; + cmd_sn = received_data->cmd_sn; + pwr_cmd = received_data->pwr_cmd; + data0 = received_data->data0; + data1 = received_data->data1; + + if (cmd_sn != prev_cmd_sn) + prev_cmd_sn = cmd_sn; + else + return -EINVAL; + + switch (pwr_cmd.req_id) { + case CHANGE_DEV_CLKSRC: + /* + * received_data.data0 : user + * received_data.data1 : on + */ + if (data1 == 1) + ret = apu_enable_dev_clksrc(apupwr->dev, data0); + else + apu_disable_dev_clksrc(apupwr->dev, data0); + break; + case CHANGE_DEV_CLOCK: + /* + * received_data.data0 : target_freq + * received_data.data1 : volt_domain + */ + ret = apu_set_clk_freq(apupwr->dev, data0, data1); + break; + case CHANGE_SYS_VCORE: + /* + * received_data.data0 : user + * received_data.data1 : vcore_opp + */ + ret = apu_config_vcore_volt(apupwr->dev, data1); + break; + default: + dev_err(dev, "invalid request id:%d (cmd_sn:0x%08x)\n", + pwr_cmd.req_id, + cmd_sn); + return -EINVAL; + } + + /* prepare reply data to remote */ + memset(&reply_data, 0, sizeof(struct power_ipi_cmd_reply)); + reply_data.cmd_sn = cmd_sn; + reply_data.data0 = RX_MAGIC0; + reply_data.data1 = RX_MAGIC1; + + /* send reply data to remote (no blocking) */ + ret = rpmsg_send(power_rpmsg->ept, + (void *)&reply_data, sizeof(reply_data)); + if (ret) + dev_err(dev, "%s: failed to send msg to remote, ret=%d\n", + __func__, ret); + + return ret; +} + +static int apu_power_rx_probe(struct rpmsg_device *rpdev) +{ + struct device *dev = &rpdev->dev; + struct rpmsg_channel_info chinfo = {}; + struct rpmsg_endpoint *ept; + struct apu_power_rpmsg *power_rpmsg; + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct apu_power *apupwr = rx_to_apu_power(rpdrv); + + power_rpmsg = devm_kzalloc(dev, + sizeof(struct apu_power_rpmsg), GFP_KERNEL); + if (!power_rpmsg) + return -ENOMEM; + + strscpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); + chinfo.src = rpdev->src; + chinfo.dst = RPMSG_ADDR_ANY; + ept = rpmsg_create_ept(rpdev, apu_power_rx_callback, NULL, chinfo); + if (!ept) { + dev_err(dev, "failed to create ept\n"); + return -ENODEV; + } + + init_completion(&power_rpmsg->comp); + mutex_init(&power_rpmsg->lock); + power_rpmsg->ept = ept; + power_rpmsg->rpdev = rpdev; + power_rpmsg->initialized = 1; + memset(&power_rpmsg->ipi_rply, 0x0, + sizeof(struct power_ipi_cmd_reply)); + dev_set_drvdata(dev, power_rpmsg); + apupwr->rx_rpmsg_device = rpdev; + + return 0; +} + +static void apu_power_rx_remove(struct rpmsg_device *rpdev) +{ + struct apu_power_rpmsg *power_rpmsg = dev_get_drvdata(&rpdev->dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct apu_power *apupwr = rx_to_apu_power(rpdrv); + + rpmsg_destroy_ept(power_rpmsg->ept); + apupwr->rx_rpmsg_device = NULL; +} + +static int apu_power_tx_callback(struct rpmsg_device *rpdev, void *data, + int len, void *priv, u32 src) +{ + struct device *dev = &rpdev->dev; + struct apu_power_rpmsg *power_rpmsg = dev_get_drvdata(&rpdev->dev); + + if (!len) { + dev_err(dev, "apu power rpmsg received empty rply"); + complete(&power_rpmsg->comp); + return -EINVAL; + } + + memcpy(&power_rpmsg->ipi_rply, data, + sizeof(struct power_ipi_cmd_reply)); + complete(&power_rpmsg->comp); + + return 0; +} + +static int apu_power_tx_probe(struct rpmsg_device *rpdev) +{ + struct device *dev = &rpdev->dev; + struct rpmsg_channel_info chinfo = {}; + struct rpmsg_endpoint *ept; + struct apu_power_rpmsg *power_rpmsg; + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct apu_power *apupwr = tx_to_apu_power(rpdrv); + + power_rpmsg = devm_kzalloc(dev, + sizeof(struct apu_power_rpmsg), GFP_KERNEL); + if (!power_rpmsg) + return -ENOMEM; + + strscpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); + chinfo.src = rpdev->src; + chinfo.dst = RPMSG_ADDR_ANY; + ept = rpmsg_create_ept(rpdev, apu_power_tx_callback, NULL, chinfo); + if (!ept) { + dev_err(dev, "failed to create ept\n"); + return -ENODEV; + } + + init_completion(&power_rpmsg->comp); + mutex_init(&power_rpmsg->lock); + power_rpmsg->ept = ept; + power_rpmsg->rpdev = rpdev; + power_rpmsg->initialized = 1; + memset(&power_rpmsg->ipi_rply, 0x0, + sizeof(struct power_ipi_cmd_reply)); + dev_set_drvdata(dev, power_rpmsg); + apupwr->tx_rpmsg_device = rpdev; + + return 0; +} + +static void apu_power_tx_remove(struct rpmsg_device *rpdev) +{ + struct apu_power_rpmsg *power_rpmsg = dev_get_drvdata(&rpdev->dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct apu_power *apupwr = tx_to_apu_power(rpdrv); + + rpmsg_destroy_ept(power_rpmsg->ept); + apupwr->tx_rpmsg_device = NULL; +} + +static const struct of_device_id apupwr_tx_rpmsg_of_match[] = { + { .compatible = "mediatek,apupwr-tx-rpmsg"}, + {}, +}; + +static const struct of_device_id apupwr_rx_rpmsg_of_match[] = { + { .compatible = "mediatek,apupwr-rx-rpmsg"}, + {}, +}; + +static struct rpmsg_driver pwr_tx_rpmsg_drv = { + .drv = { + .name = "apupwr-tx-rpmsg", + .owner = THIS_MODULE, + .of_match_table = apupwr_tx_rpmsg_of_match, + }, + .probe = apu_power_tx_probe, + .remove = apu_power_tx_remove, +}; + +static struct rpmsg_driver pwr_rx_rpmsg_drv = { + .drv = { + .name = "apupwr-rx-rpmsg", + .owner = THIS_MODULE, + .of_match_table = apupwr_rx_rpmsg_of_match, + }, + .probe = apu_power_rx_probe, + .remove = apu_power_rx_remove, +}; + +int apu_power_ipi_init(struct apu_power *apupwr) +{ + int ret = 0; + + apupwr->tx_rpmsg_driver = pwr_tx_rpmsg_drv; + ret = register_rpmsg_driver(&apupwr->tx_rpmsg_driver); + if (ret) + return ret; + + apupwr->rx_rpmsg_driver = pwr_rx_rpmsg_drv; + ret = register_rpmsg_driver(&apupwr->rx_rpmsg_driver); + if (ret) + goto err; + + return 0; +err: + unregister_rpmsg_driver(&apupwr->tx_rpmsg_driver); + return ret; +} + +void apu_power_ipi_exit(struct apu_power *apupwr) +{ + unregister_rpmsg_driver(&apupwr->tx_rpmsg_driver); + unregister_rpmsg_driver(&apupwr->rx_rpmsg_driver); +} diff --git a/drivers/soc/mediatek/apusys/apu-pwr.c b/drivers/soc/mediatek/apusys/apu-pwr.c new file mode 100644 index 000000000000..e8e54a767aff --- /dev/null +++ b/drivers/soc/mediatek/apusys/apu-pwr.c @@ -0,0 +1,613 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apu-pwr.h" + +static const char *mt8192_apu_clks[CLK_NUM] = { + [CLK_TOP_DSP_SEL] = "clk_top_dsp_sel", + [CLK_TOP_DSP1_SEL] = "clk_top_dsp1_sel", + [CLK_TOP_DSP1_NPUPLL_SEL] = "clk_top_dsp1_npupll_sel", + [CLK_TOP_DSP2_SEL] = "clk_top_dsp2_sel", + [CLK_TOP_DSP2_NPUPLL_SEL] = "clk_top_dsp2_npupll_sel", + [CLK_TOP_DSP5_SEL] = "clk_top_dsp5_sel", + [CLK_TOP_DSP5_APUPLL_SEL] = "clk_top_dsp5_apupll_sel", + [CLK_TOP_IPU_IF_SEL] = "clk_top_ipu_if_sel", + [CLK_CLK26M] = "clk_top_clk26m", + [CLK_TOP_MAINPLL_D4_D2] = "clk_top_mainpll_d4_d2", + [CLK_TOP_UNIVPLL_D4_D2] = "clk_top_univpll_d4_d2", + [CLK_TOP_UNIVPLL_D6_D2] = "clk_top_univpll_d6_d2", + [CLK_TOP_MMPLL_D6] = "clk_top_mmpll_d6", + [CLK_TOP_MMPLL_D5] = "clk_top_mmpll_d5", + [CLK_TOP_MMPLL_D4] = "clk_top_mmpll_d4", + [CLK_TOP_UNIVPLL_D5] = "clk_top_univpll_d5", + [CLK_TOP_UNIVPLL_D4] = "clk_top_univpll_d4", + [CLK_TOP_UNIVPLL_D3] = "clk_top_univpll_d3", + [CLK_TOP_MAINPLL_D6] = "clk_top_mainpll_d6", + [CLK_TOP_MAINPLL_D4] = "clk_top_mainpll_d4", + [CLK_TOP_MAINPLL_D3] = "clk_top_mainpll_d3", + [CLK_TOP_TVDPLL] = "clk_top_tvdpll_ck", + [CLK_TOP_APUPLL] = "clk_top_apupll_ck", + [CLK_TOP_NPUPLL] = "clk_top_npupll_ck", + [CLK_APMIXED_APUPLL] = "clk_apmixed_apupll_rate", + [CLK_APMIXED_NPUPLL] = "clk_apmixed_npupll_rate", +}; + +static void apu_power_reg_init(struct device *dev) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + const struct apupwr_plat_reg *plat_regs; + void __iomem *spare_base; + + spare_base = apupwr->spare_base; + plat_regs = apupwr->plat_data->plat_regs; + + writel(0xffffffff, spare_base + plat_regs->opp_user); + writel(0xffffffff, spare_base + plat_regs->opp_curr); + writel(0xffffffff, spare_base + plat_regs->opp_thermal); +} + +static int apu_power_regulator_init(struct device *dev) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + int ret; + + apupwr->vvpu_reg_id = devm_regulator_get_optional(dev, "vvpu"); + if (IS_ERR(apupwr->vvpu_reg_id)) { + ret = PTR_ERR(apupwr->vvpu_reg_id); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get vvpu regulator"); + + return ret; + } + + apupwr->vmdla_reg_id = devm_regulator_get_optional(dev, "vmdla"); + if (IS_ERR(apupwr->vmdla_reg_id)) { + ret = PTR_ERR(apupwr->vmdla_reg_id); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get vmdla regulator"); + + return ret; + } + + ret = regulator_enable(apupwr->vvpu_reg_id); + if (ret) + return ret; + + ret = regulator_enable(apupwr->vmdla_reg_id); + if (ret) + return ret; + + return ret; +} + +static void apu_power_regulator_exit(struct device *dev) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + + if (apupwr->vvpu_reg_id) { + if (regulator_disable(apupwr->vvpu_reg_id)) + dev_err(apupwr->dev, "disable vvpu fail\n"); + } + + if (apupwr->vmdla_reg_id) { + if (regulator_disable(apupwr->vmdla_reg_id)) + dev_err(apupwr->dev, "disable mdla fail\n"); + } +} + +static int mt8192_apu_clock_init(struct device *dev) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + int i = 0; + + apupwr->clk = devm_kcalloc(dev, CLK_NUM, + sizeof(*apupwr->clk), GFP_KERNEL); + if (!apupwr->clk) + return -ENOMEM; + + for (i = 0; i < CLK_NUM; i++) { + apupwr->clk[i] = devm_clk_get(dev, mt8192_apu_clks[i]); + if (IS_ERR(apupwr->clk[i])) { + dev_warn(dev, "%s devm_clk_get %s fail\n", + __func__, + mt8192_apu_clks[i]); + apupwr->clk[i] = NULL; + } + } + + return 0; +} + +static int mt8192_enable_vpu_clksrc(struct apu_power *apupwr) +{ + int ret; + + ret = clk_prepare_enable(apupwr->clk[CLK_TOP_DSP1_SEL]); + if (ret) + goto dsp1_sel_err; + + ret = clk_prepare_enable(apupwr->clk[CLK_TOP_DSP2_SEL]); + if (ret) + goto dsp2_sel_err; + + ret = clk_prepare_enable(apupwr->clk[CLK_TOP_NPUPLL]); + if (ret) + goto npupll_sel_err; + + ret = clk_prepare_enable(apupwr->clk[CLK_TOP_DSP1_NPUPLL_SEL]); + if (ret) + goto dsp1_npupll_sel_err; + + ret = clk_prepare_enable(apupwr->clk[CLK_TOP_DSP2_NPUPLL_SEL]); + if (ret) + goto dsp2_npupll_sel_err; + + return 0; + +dsp2_npupll_sel_err: + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP1_NPUPLL_SEL]); +dsp1_npupll_sel_err: + clk_disable_unprepare(apupwr->clk[CLK_TOP_NPUPLL]); +npupll_sel_err: + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP2_SEL]); +dsp2_sel_err: + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP1_SEL]); +dsp1_sel_err: + dev_err(apupwr->dev, "failed to enable vpu clk src\n"); + return ret; +} + +static int mt8192_enable_mdla_clksrc(struct apu_power *apupwr) +{ + int ret; + + ret = clk_prepare_enable(apupwr->clk[CLK_TOP_DSP5_SEL]); + if (ret) + goto dsp5_sel_err; + + ret = clk_prepare_enable(apupwr->clk[CLK_TOP_APUPLL]); + if (ret) + goto apupll_err; + + ret = clk_prepare_enable(apupwr->clk[CLK_TOP_DSP5_APUPLL_SEL]); + if (ret) + goto dsp5_apupll_sel_err; + + return 0; + +dsp5_apupll_sel_err: + clk_disable_unprepare(apupwr->clk[CLK_TOP_APUPLL]); +apupll_err: + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP5_APUPLL_SEL]); +dsp5_sel_err: + dev_err(apupwr->dev, "failed to enable mdla clk src\n"); + return ret; +} + +static int mt8192_apu_enable_dev_clksrc(struct device *dev, enum DVFS_USER user) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + int ret = 0; + + switch (user) { + case VPU0: + case VPU1: + ret = mt8192_enable_vpu_clksrc(apupwr); + break; + case MDLA0: + ret = mt8192_enable_mdla_clksrc(apupwr); + break; + default: + dev_err(dev, "%s illegal DVFS_USER: %d\n", __func__, user); + ret = -EINVAL; + } + + return ret; +} + +static void mt8192_apu_disable_dev_clksrc(struct device *dev, + enum DVFS_USER user) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + + switch (user) { + case VPU0: + case VPU1: + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP2_NPUPLL_SEL]); + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP1_NPUPLL_SEL]); + clk_disable_unprepare(apupwr->clk[CLK_TOP_NPUPLL]); + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP2_SEL]); + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP1_SEL]); + break; + case MDLA0: + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP5_APUPLL_SEL]); + clk_disable_unprepare(apupwr->clk[CLK_TOP_APUPLL]); + clk_disable_unprepare(apupwr->clk[CLK_TOP_DSP5_SEL]); + break; + default: + dev_err(dev, "%s illegal DVFS_USER: %d\n", __func__, user); + } +} + +static struct clk *find_clk_by_domain(struct device *dev, + enum DVFS_VOLTAGE_DOMAIN domain) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + + switch (domain) { + case V_APU_CONN: + return apupwr->clk[CLK_TOP_DSP_SEL]; + + case V_VPU0: + return apupwr->clk[CLK_TOP_DSP1_SEL]; + + case V_VPU1: + return apupwr->clk[CLK_TOP_DSP2_SEL]; + + case V_MDLA0: + return apupwr->clk[CLK_TOP_DSP5_SEL]; + + case V_VCORE: + return apupwr->clk[CLK_TOP_IPU_IF_SEL]; + + default: + dev_err(dev, "%s fail to find clk !\n", __func__); + return NULL; + } +} + +static int mt8192_apu_set_clk_freq(struct device *dev, + enum DVFS_FREQ freq, + enum DVFS_VOLTAGE_DOMAIN domain) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + int ret = 0; + struct clk *clk_src = NULL; + struct clk *clk_target = NULL; + + switch (freq) { + case DVFS_FREQ_00_026000_F: + clk_src = apupwr->clk[CLK_CLK26M]; + break; + + case DVFS_FREQ_00_208000_F: + clk_src = apupwr->clk[CLK_TOP_UNIVPLL_D6_D2]; + break; + + case DVFS_FREQ_00_273000_F: + clk_src = apupwr->clk[CLK_TOP_MAINPLL_D4_D2]; + break; + + case DVFS_FREQ_00_312000_F: + clk_src = apupwr->clk[CLK_TOP_UNIVPLL_D4_D2]; + break; + + case DVFS_FREQ_00_499200_F: + clk_src = apupwr->clk[CLK_TOP_UNIVPLL_D5]; + break; + + case DVFS_FREQ_00_546000_F: + clk_src = apupwr->clk[CLK_TOP_MAINPLL_D4]; + break; + + case DVFS_FREQ_00_624000_F: + clk_src = apupwr->clk[CLK_TOP_UNIVPLL_D4]; + break; + + case DVFS_FREQ_00_687500_F: + clk_src = apupwr->clk[CLK_TOP_MMPLL_D4]; + break; + + case DVFS_FREQ_00_728000_F: + clk_src = apupwr->clk[CLK_TOP_MAINPLL_D3]; + break; + + case DVFS_FREQ_00_832000_F: + clk_src = apupwr->clk[CLK_TOP_UNIVPLL_D3]; + break; + + case DVFS_FREQ_NOT_SUPPORT: + default: + clk_src = apupwr->clk[CLK_CLK26M]; + dev_warn(dev, "%s wrong freq : %d, force assign 26M\n", + __func__, freq); + } + + clk_target = find_clk_by_domain(dev, domain); + if (clk_target) { + ret = clk_set_parent(clk_target, clk_src); + if (ret) { + dev_err(dev, "%s fail p1 fail\n", __func__); + return ret; + } + switch (domain) { + case V_VPU0: + clk_target = apupwr->clk[CLK_TOP_DSP1_NPUPLL_SEL]; + clk_src = apupwr->clk[CLK_TOP_DSP1_SEL]; + break; + + case V_VPU1: + clk_target = apupwr->clk[CLK_TOP_DSP2_NPUPLL_SEL]; + clk_src = apupwr->clk[CLK_TOP_DSP2_SEL]; + break; + + case V_MDLA0: + clk_target = apupwr->clk[CLK_TOP_DSP5_APUPLL_SEL]; + clk_src = apupwr->clk[CLK_TOP_DSP5_SEL]; + break; + + default: + break; + } + ret = clk_set_parent(clk_target, clk_src); + if (ret) { + dev_err(dev, "%s fail p2 fail\n", __func__); + return ret; + } + } + + return ret; +} + +static int apu_clock_init(struct device *dev) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + int ret = 0; + + if (!apupwr->plat_data->ops) + return ret; + + if (apupwr->plat_data->ops->clock_init) + ret = apupwr->plat_data->ops->clock_init(dev); + + return ret; +} + +int apu_enable_dev_clksrc(struct device *dev, enum DVFS_USER user) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + int ret = 0; + + if (!apupwr->plat_data->ops) + return ret; + + if (apupwr->plat_data->ops->enable_dev_clksrc) + ret = apupwr->plat_data->ops->enable_dev_clksrc(dev, user); + + return ret; +} + +void apu_disable_dev_clksrc(struct device *dev, enum DVFS_USER user) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + + if (!apupwr->plat_data->ops) + return; + + if (apupwr->plat_data->ops->disable_dev_clksrc) + apupwr->plat_data->ops->disable_dev_clksrc(dev, user); +} + +int apu_set_clk_freq(struct device *dev, + enum DVFS_FREQ freq, + enum DVFS_VOLTAGE_DOMAIN domain) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + int ret = 0; + + if (!apupwr->plat_data->ops) + return ret; + + if (apupwr->plat_data->ops->set_clk_freq) + ret = apupwr->plat_data->ops->set_clk_freq(dev, + freq, domain); + + return ret; +} + +void apu_update_fixed_opp_by_reg(struct device *dev, + u32 opp_limit_stat) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + const struct apupwr_plat_reg *plat_regs; + void __iomem *spare_base; + + spare_base = apupwr->spare_base; + plat_regs = apupwr->plat_data->plat_regs; + writel(opp_limit_stat, spare_base + plat_regs->opp_user); +} + +void apu_check_curr_opp_by_reg(struct device *dev, + enum DVFS_USER specify_usr) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + const struct apupwr_plat_reg *plat_regs; + void __iomem *spare_base; + u32 curr_opp_stat; + + spare_base = apupwr->spare_base; + plat_regs = apupwr->plat_data->plat_regs; + + curr_opp_stat = readl(spare_base + plat_regs->opp_curr); + dev_info(dev, "%s user:%d curr opp:%d\n", + __func__, specify_usr, curr_opp_stat); +} + +void apu_update_thermal_opp_by_reg(struct device *dev, + enum DVFS_USER user, int therm_opp) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + const struct apupwr_plat_reg *plat_regs; + void __iomem *spare_base; + u32 curr_therm_stat; + + spare_base = apupwr->spare_base; + plat_regs = apupwr->plat_data->plat_regs; + + curr_therm_stat = readl(spare_base + plat_regs->opp_thermal); + curr_therm_stat &= ~(0xf << user); + curr_therm_stat |= ((therm_opp & 0xf) << user); + + writel(curr_therm_stat, spare_base + plat_regs->opp_thermal); +} + +int apu_config_vcore_volt(struct device *dev, enum DVFS_VOLTAGE volt) +{ + struct apu_power *apupwr = dev_get_drvdata(dev); + int ret = 0; + + if (apupwr->plat_data->ops->set_vcore) + ret = apupwr->plat_data->ops->set_vcore(dev, volt); + + return ret; +} + +static int apu_power_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apu_power *apupwr; + struct resource *res; + int ret = 0; + + apupwr = devm_kzalloc(dev, sizeof(struct apu_power), GFP_KERNEL); + if (!apupwr) + return -ENOMEM; + + platform_set_drvdata(pdev, apupwr); + + apupwr->dev = &pdev->dev; + apupwr->plat_data = device_get_match_data(dev); + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "apu_spare"); + apupwr->spare_base = devm_ioremap_resource(dev, res); + if (IS_ERR(apupwr->spare_base)) { + dev_err(dev, "Unable to ioremap spare_base\n"); + apupwr->spare_base = NULL; + return PTR_ERR(apupwr->spare_base); + } + + /* prepare registers */ + apu_power_reg_init(dev); + + /* prepare regulators */ + ret = apu_power_regulator_init(dev); + if (ret) + goto err_regulator; + + /* prepare clocks */ + ret = apu_clock_init(dev); + if (ret) + goto err_regulator; + + /* debugfs */ + apupwr->dbg_root = NULL; + apu_power_debugfs_init(apupwr); + + /* init remote ipi channel */ + ret = apu_power_ipi_init(apupwr); + if (ret) { + dev_err(dev, "failed to rpmsg channel\n"); + goto err_exit; + } + + ret = devm_of_platform_populate(&pdev->dev); + if (ret < 0) + dev_err(&pdev->dev, "Fail to populate child nodes: %d\n", ret); + + pm_runtime_enable(dev); + + return 0; + +err_exit: + apu_power_ipi_exit(apupwr); + apu_power_debugfs_exit(); +err_regulator: + apu_power_regulator_exit(dev); + + return ret; +} + +static int apu_power_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apu_power *apupwr = platform_get_drvdata(pdev); + + if (!apupwr) + return -ENODEV; + + pm_runtime_disable(dev); + apu_power_ipi_exit(apupwr); + apu_power_debugfs_exit(); + apu_power_regulator_exit(dev); + return 0; +} + +static const struct apupwr_plat_reg mt8192_pwr_reg = { + .opp_user = 0x40, + .opp_thermal = 0x44, + .opp_curr = 0x48, + .opp_mask = 0xF, +}; + +static struct apupwr_plat_ops mt8192_pwr_ops = { + .clock_init = mt8192_apu_clock_init, + .enable_dev_clksrc = mt8192_apu_enable_dev_clksrc, + .disable_dev_clksrc = mt8192_apu_disable_dev_clksrc, + .set_clk_freq = mt8192_apu_set_clk_freq, + .set_vcore = NULL, +}; + +static struct apupwr_plat_data mt8192_apu_power_data = { + .dvfs_user = MDLA0, + .plat_regs = &mt8192_pwr_reg, + .ops = &mt8192_pwr_ops, +}; + +static const struct of_device_id apu_power_of_match[] = { + { + .compatible = "mediatek,mt8192-apu-power", + .data = &mt8192_apu_power_data + }, { + /* Terminator */ + }, +}; +MODULE_DEVICE_TABLE(of, apu_power_of_match); + +static struct platform_driver apu_power_driver = { + .probe = apu_power_probe, + .remove = apu_power_remove, + .driver = { + .name = "apusys_power", + .of_match_table = of_match_ptr(apu_power_of_match), + }, +}; + +static int __init apu_power_drv_init(void) +{ + return platform_driver_register(&apu_power_driver); +} + +static void __exit apu_power_drv_exit(void) +{ + platform_driver_unregister(&apu_power_driver); +} + +module_init(apu_power_drv_init); +module_exit(apu_power_drv_exit); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("apu power driver"); diff --git a/drivers/soc/mediatek/apusys/apu-pwr.h b/drivers/soc/mediatek/apusys/apu-pwr.h new file mode 100644 index 000000000000..4b6d90d5f206 --- /dev/null +++ b/drivers/soc/mediatek/apusys/apu-pwr.h @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef APU_PWR_H +#define APU_PWR_H + +#include +#include + +enum DVFS_USER { + VPU0 = 0, + VPU1, + MDLA0, + MDLA1, + APUSYS_DVFS_USER_NUM, +}; + +enum DVFS_VOLTAGE_DOMAIN { + V_VPU0 = 0, + V_VPU1, + V_MDLA0, + V_APU_CONN, + V_VCORE, + APUSYS_BUCK_DOMAIN_NUM, +}; + +enum DVFS_USER_TYPE { + TYPE_VPU, + TYPE_MDLA, + DVFS_USER_TYPE_NUM, + TYPE_UNKNOWN, +}; + +/* mt8192 apu clocks*/ +enum { + CLK_TOP_DSP_SEL = 0, + CLK_TOP_DSP1_SEL, + CLK_TOP_DSP1_NPUPLL_SEL, + CLK_TOP_DSP2_SEL, + CLK_TOP_DSP2_NPUPLL_SEL, + CLK_TOP_DSP5_SEL, + CLK_TOP_DSP5_APUPLL_SEL, + CLK_TOP_IPU_IF_SEL, + CLK_CLK26M, + CLK_TOP_MAINPLL_D4_D2, + CLK_TOP_UNIVPLL_D4_D2, + CLK_TOP_UNIVPLL_D6_D2, + CLK_TOP_MMPLL_D6, + CLK_TOP_MMPLL_D5, + CLK_TOP_MMPLL_D4, + CLK_TOP_UNIVPLL_D5, + CLK_TOP_UNIVPLL_D4, + CLK_TOP_UNIVPLL_D3, + CLK_TOP_MAINPLL_D6, + CLK_TOP_MAINPLL_D4, + CLK_TOP_MAINPLL_D3, + CLK_TOP_TVDPLL, + CLK_TOP_APUPLL, + CLK_TOP_NPUPLL, + CLK_APMIXED_APUPLL, + CLK_APMIXED_NPUPLL, + CLK_NUM +}; + +enum DVFS_VOLTAGE { + DVFS_VOLT_NOT_SUPPORT = 0, + DVFS_VOLT_00_550000_V = 550000, + DVFS_VOLT_00_575000_V = 575000, + DVFS_VOLT_00_600000_V = 600000, + DVFS_VOLT_00_650000_V = 650000, + DVFS_VOLT_00_700000_V = 700000, + DVFS_VOLT_00_725000_V = 725000, + DVFS_VOLT_00_750000_V = 750000, + DVFS_VOLT_00_775000_V = 775000, + DVFS_VOLT_00_800000_V = 800000, + DVFS_VOLT_00_825000_V = 825000, + DVFS_VOLT_MAX = 825000 + 1, +}; + +enum DVFS_FREQ { + DVFS_FREQ_NOT_SUPPORT = 0, + DVFS_FREQ_00_026000_F = 26000, + DVFS_FREQ_00_208000_F = 208000, + DVFS_FREQ_00_273000_F = 273000, + DVFS_FREQ_00_312000_F = 312000, + DVFS_FREQ_00_499200_F = 499200, + DVFS_FREQ_00_525000_F = 525000, + DVFS_FREQ_00_546000_F = 546000, + DVFS_FREQ_00_594000_F = 594000, + DVFS_FREQ_00_624000_F = 624000, + DVFS_FREQ_00_688000_F = 688000, + DVFS_FREQ_00_687500_F = 687500, + DVFS_FREQ_00_728000_F = 728000, + DVFS_FREQ_00_800000_F = 800000, + DVFS_FREQ_00_832000_F = 832000, + DVFS_FREQ_00_960000_F = 960000, + DVFS_FREQ_00_1100000_F = 1100000, + DVFS_FREQ_MAX, +}; + +enum APU_POWER_PARAM { + POWER_PARAM_FIX_OPP, + POWER_PARAM_DVFS_DEBUG, + POWER_PARAM_GET_POWER_REG, + POWER_PARAM_POWER_STRESS, + POWER_PARAM_OPP_TABLE, + POWER_PARAM_CURR_STATUS, + POWER_PARAM_LOG_LEVEL, + POWER_PARAM_ULOG_LEVEL, +}; + +struct apu_dev_power_data { + int dev_type; + int dev_core; + void *pdata; +}; + +struct apupwr_plat_reg { + u32 opp_user; + u32 opp_thermal; + u32 opp_curr; + u32 opp_mask; +}; + +struct apupwr_plat_ops { + int (*clock_init)(struct device *dev); + int (*enable_dev_clksrc)(struct device *dev, enum DVFS_USER user); + void (*disable_dev_clksrc)(struct device *dev, enum DVFS_USER user); + int (*set_clk_freq)(struct device *dev, enum DVFS_FREQ freq, + enum DVFS_VOLTAGE_DOMAIN domain); + int (*set_vcore)(struct device *dev, enum DVFS_VOLTAGE volt); +}; + +struct apupwr_plat_data { + u32 flags; + int dvfs_user; + const struct apupwr_plat_reg *plat_regs; + const struct apupwr_plat_ops *ops; +}; + +struct apu_power { + struct device *dev; + void __iomem *spare_base; + const struct apupwr_plat_data *plat_data; + struct clk **clk; + struct regulator *vvpu_reg_id; + struct regulator *vmdla_reg_id; + + /* rpmsg device for power ipi */ + struct rpmsg_driver tx_rpmsg_driver; + struct rpmsg_device *tx_rpmsg_device; + struct rpmsg_driver rx_rpmsg_driver; + struct rpmsg_device *rx_rpmsg_device; + + /*debug*/ + struct dentry *dbg_root; + u32 dbg_option; + u32 ulog_level; +}; + +/* request send from AP to uP */ +enum request_id_AP { + DVFS_STAT_UPDATE = 0, + OPP_LIMIT_THERM, + OPP_LIMIT_FIX_OPP, + OPP_LIMIT_DVFS_DEBUG, + CHANGE_LOG_LEVEL, + STRESS_TEST, +}; + +/* request send from uP to AP */ +enum request_id_uP { + CHANGE_DEV_CLKSRC = 0, + CHANGE_DEV_CLOCK, + CHANGE_SYS_VCORE, + SYNC_STAT_UP_POWER, + SYNC_STAT_DEV_POWER, + DUMP_POWER_INFO, +}; + +/* power cmd format (from AP to uP) */ +struct power_cmd_AP { + u32 req_id:4, + reserved0:4, + reserved1:4, + reserved2:4, + reserved3:4, + reserved4:4, + reserved5:4, + need_handle_ack:4; +}; + +/* ipi cmd format (from AP to uP) */ +struct power_ipi_cmd_AP { + u32 cmd_sn; + struct power_cmd_AP pwr_cmd; + u32 data0; + u32 data1; +}; + +/*power cmd format (from uP to AP) */ +struct power_cmd_uP { + u32 req_id:4, + dev_id:4, + target_opp:4, + reserved0:4, + reserved1:4, + reserved2:4, + reserved3:4, + need_handle_ack:4; +}; + +/* ipi cmd format (from uP to AP) */ +struct power_ipi_cmd_uP { + u32 cmd_sn; + struct power_cmd_uP pwr_cmd; + u32 data0; + u32 data1; +}; + +/* reply to remote that we are completed */ +struct power_ipi_cmd_reply { + u32 cmd_sn; + u32 data0; + u32 data1; + u32 reserved3[61]; +}; + +int apu_enable_dev_clksrc(struct device *dev, enum DVFS_USER); +void apu_disable_dev_clksrc(struct device *dev, enum DVFS_USER); +int apu_set_clk_freq(struct device *dev, + enum DVFS_FREQ freq, + enum DVFS_VOLTAGE_DOMAIN domain); + +void apu_update_fixed_opp_by_reg(struct device *dev, + u32 opp_limit_stat); +void apu_check_curr_opp_by_reg(struct device *dev, + enum DVFS_USER specify_usr); +void apu_update_thermal_opp_by_reg(struct device *dev, + enum DVFS_USER user, int therm_opp); + +int apu_config_vcore_volt(struct device *dev, enum DVFS_VOLTAGE volt); + +int apu_power_ipi_init(struct apu_power *apupwr); +void apu_power_ipi_exit(struct apu_power *apupwr); +void apu_power_set_ulog_level(struct apu_power *apupwr, + int level); +void apu_power_notify_uP_opp_limit(struct apu_power *apu_power, + enum request_id_AP req); + +#if IS_ENABLED(CONFIG_MTK_APU_DEBUG) +void apu_power_debugfs_init(struct apu_power *apupwr); +void apu_power_debugfs_exit(void); +#else +static inline void apu_power_debugfs_init(struct apu_power *apupwr) { } +static inline void apu_power_debugfs_exit(void) { } +#endif +#endif From patchwork Fri Dec 10 17:25:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670301 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 55AEEC433F5 for ; Fri, 10 Dec 2021 17:27:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244571AbhLJRa6 (ORCPT ); Fri, 10 Dec 2021 12:30:58 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:43290 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244485AbhLJRa3 (ORCPT ); Fri, 10 Dec 2021 12:30:29 -0500 X-UUID: 5db32c13bbbb4c5eb1707471c550d1a9-20211211 X-UUID: 5db32c13bbbb4c5eb1707471c550d1a9-20211211 Received: from mtkexhb01.mediatek.inc [(172.21.101.102)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 46113754; Sat, 11 Dec 2021 01:26:50 +0800 Received: from mtkexhb02.mediatek.inc (172.21.101.103) by mtkmbs10n1.mediatek.inc (172.21.101.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.15; Sat, 11 Dec 2021 01:26:49 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkexhb02.mediatek.inc (172.21.101.103) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:48 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:47 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 10/17] soc: mediatek: apu: Add APU software logger dirver Date: Sat, 11 Dec 2021 01:25:58 +0800 Message-ID: <20211210172605.30618-11-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org The APU software logger is for debug for remote processor. The remote microprocessor's logs will be output to the mapped memory and application processor can read logs from the dedicated reserved registers Signed-off-by: Flora Fu --- drivers/soc/mediatek/apusys/Makefile | 2 + drivers/soc/mediatek/apusys/apu-sw-logger.c | 540 ++++++++++++++++++++ 2 files changed, 542 insertions(+) create mode 100644 drivers/soc/mediatek/apusys/apu-sw-logger.c diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile index 8fff18d63dc1..6fb69abcf3b9 100644 --- a/drivers/soc/mediatek/apusys/Makefile +++ b/drivers/soc/mediatek/apusys/Makefile @@ -5,3 +5,5 @@ obj-$(CONFIG_MTK_APU) += mtk_apu_pwr.o mtk_apu_pwr-objs += apu-pwr.o mtk_apu_pwr-objs += apu-pwr-ipi.o mtk_apu_pwr-$(CONFIG_MTK_APU_DEBUG) += apu-pwr-dbg.o + +obj-$(CONFIG_MTK_APU_DEBUG) += apu-sw-logger.o diff --git a/drivers/soc/mediatek/apusys/apu-sw-logger.c b/drivers/soc/mediatek/apusys/apu-sw-logger.c new file mode 100644 index 000000000000..98a9748e92a7 --- /dev/null +++ b/drivers/soc/mediatek/apusys/apu-sw-logger.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APUSYS_LOGGER_DIR "apu_logger" + +#define LOG_LINE_MAX_LENS SZ_128 +#define APU_LOG_SIZE SZ_1M +#define APU_LOG_BUF_SIZE SZ_1M + +#define LOG_W_PTR 0x0 +#define LOG_R_PTR 0x4 +#define LOG_OV_FLG 0xC + +struct sw_logger_seq_data { + u32 w_ptr; + u32 r_ptr; + u32 overflow_flg; + int i; + int is_finished; + char *data; + bool startl_first; +}; + +struct apu_sw_logger { + struct device *dev; + struct dentry *dbg_root; + dma_addr_t handle; + char *sw_log_buf; + void __iomem *apu_slog; + spinlock_t logger_spinlock; /* logger status update lock */ + struct sw_logger_seq_data pseqdata_lock; + struct sw_logger_seq_data *pseqdata; +}; + +static struct dentry *log_root; +static struct dentry *log_seqlog; +static struct dentry *log_seqlogl; +static struct device *sw_logger_dev; + +static void sw_logger_buf_invalidate(struct apu_sw_logger *logger) +{ + dma_sync_single_for_cpu(logger->dev, logger->handle, APU_LOG_SIZE, + DMA_FROM_DEVICE); +} + +static int sw_logger_buf_alloc(struct device *dev) +{ + struct apu_sw_logger *logger = dev_get_drvdata(dev); + int ret; + + ret = dma_set_coherent_mask(logger->dev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + logger->sw_log_buf = kzalloc(APU_LOG_SIZE, GFP_KERNEL); + if (!logger->sw_log_buf) + return -ENOMEM; + + logger->handle = dma_map_single(logger->dev, logger->sw_log_buf, + APU_LOG_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(logger->dev, logger->handle) != 0) { + kfree(logger->sw_log_buf); + logger->sw_log_buf = NULL; + return -EFAULT; + } + + return 0; +} + +int sw_logger_config_init(struct config_v1 *conf) +{ + int ret; + unsigned long flags; + struct logger_init_info *st_logger_init_info; + struct apu_sw_logger *logger; + + if (!conf) + return -EINVAL; + + if (!sw_logger_dev) { + pr_debug("%s: logger NA and skip logger\n", + __func__); + return 0; + } + + logger = dev_get_drvdata(sw_logger_dev); + if (!logger->sw_log_buf) { + ret = sw_logger_buf_alloc(logger->dev); + if (ret) { + dev_err(logger->dev, "%s: sw_logger_buf_alloc fail\n", + __func__); + return ret; + } + } + + spin_lock_irqsave(&logger->logger_spinlock, flags); + iowrite32(0, logger->apu_slog + LOG_W_PTR); + iowrite32(0, logger->apu_slog + LOG_R_PTR); + iowrite32(0, logger->apu_slog + LOG_OV_FLG); + spin_unlock_irqrestore(&logger->logger_spinlock, flags); + + st_logger_init_info = (struct logger_init_info *) + get_apu_config_user_ptr(conf, LOGGER_INIT_INFO); + + st_logger_init_info->iova = logger->handle; + + return 0; +} +EXPORT_SYMBOL(sw_logger_config_init); + +void sw_logger_config_remove(void) +{ + struct apu_sw_logger *logger = dev_get_drvdata(sw_logger_dev); + + if (logger->handle) + dma_unmap_single(logger->dev, logger->handle, + APU_LOG_SIZE, DMA_FROM_DEVICE); + kfree(logger->sw_log_buf); + logger->sw_log_buf = NULL; +} +EXPORT_SYMBOL(sw_logger_config_remove); + +/* + * seq_start() takes a position as an argument and returns an iterator which + * will start reading at that position. + * start->show->next->show...->next->show->next->stop->start->stop + */ +static void *seq_start(struct seq_file *s, loff_t *pos) +{ + struct apu_sw_logger *logger = (struct apu_sw_logger *)s->private; + u32 w_ptr, r_ptr, overflow_flg; + unsigned long flags; + + if (!logger->sw_log_buf) { + pr_err("%s: sw_log_buf == NULL\n", __func__); + return NULL; + } + + spin_lock_irqsave(&logger->logger_spinlock, flags); + w_ptr = ioread32(logger->apu_slog + LOG_W_PTR); + r_ptr = ioread32(logger->apu_slog + LOG_R_PTR); + overflow_flg = ioread32(logger->apu_slog + LOG_OV_FLG); + spin_unlock_irqrestore(&logger->logger_spinlock, flags); + + sw_logger_buf_invalidate(logger); + + if (w_ptr == r_ptr && overflow_flg == 0) + return NULL; + + if (!logger->pseqdata) { + logger->pseqdata = kzalloc(sizeof(*logger->pseqdata), + GFP_KERNEL); + if (logger->pseqdata) { + logger->pseqdata->w_ptr = w_ptr; + logger->pseqdata->r_ptr = r_ptr; + logger->pseqdata->overflow_flg = overflow_flg; + if (overflow_flg == 0) + logger->pseqdata->i = r_ptr; + else + logger->pseqdata->i = w_ptr; + + logger->pseqdata->is_finished = 0; + } + } + + return logger->pseqdata; +} + +/* + * seq_start() takes a position as an argument and returns an iterator which + * will start reading at that position. + */ +static void *seq_startl(struct seq_file *s, loff_t *pos) +{ + struct apu_sw_logger *logger = s->private; + u32 w_ptr, r_ptr, overflow_flg; + struct sw_logger_seq_data *pseqdata_lock = &logger->pseqdata_lock; + unsigned long flags; + + if (!logger->sw_log_buf) + return NULL; + + spin_lock_irqsave(&logger->logger_spinlock, flags); + w_ptr = ioread32(logger->apu_slog + LOG_W_PTR); + r_ptr = ioread32(logger->apu_slog + LOG_R_PTR); + overflow_flg = ioread32(logger->apu_slog + LOG_OV_FLG); + spin_unlock_irqrestore(&logger->logger_spinlock, flags); + + sw_logger_buf_invalidate(logger); + + /* for ctrl-c to force exit the loop */ + while (!signal_pending(current) && w_ptr == r_ptr) { + usleep_range(10000, 12000); + + spin_lock_irqsave(&logger->logger_spinlock, flags); + w_ptr = ioread32(logger->apu_slog + LOG_W_PTR); + r_ptr = ioread32(logger->apu_slog + LOG_R_PTR); + overflow_flg = ioread32(logger->apu_slog + LOG_OV_FLG); + spin_unlock_irqrestore(&logger->logger_spinlock, flags); + + sw_logger_buf_invalidate(logger); + + pseqdata_lock->w_ptr = w_ptr; + pseqdata_lock->r_ptr = r_ptr; + pseqdata_lock->overflow_flg = overflow_flg; + pseqdata_lock->i = r_ptr; + } + + if (pseqdata_lock->startl_first || + pseqdata_lock->i == pseqdata_lock->w_ptr) { + pseqdata_lock->startl_first = false; + pseqdata_lock->w_ptr = w_ptr; + pseqdata_lock->r_ptr = r_ptr; + pseqdata_lock->overflow_flg = overflow_flg; + pseqdata_lock->i = r_ptr; + } + + if (signal_pending(current)) + pseqdata_lock->startl_first = true; + + return pseqdata_lock; +} + +/* + * move the iterator forward to the next position in the sequence + */ +static void *seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct sw_logger_seq_data *psdata = v; + + if (!psdata) { + pr_err("%s: psdata == NULL\n", __func__); + return NULL; + } + + psdata->i = (psdata->i + LOG_LINE_MAX_LENS) % APU_LOG_SIZE; + + /* prevent kernel warning */ + *pos = psdata->i; + + if (psdata->i != psdata->w_ptr) + return v; + + psdata->is_finished = 1; + return NULL; +} + +/* + * move the iterator forward to the next position in the sequence + */ +static void *seq_next_lock(struct seq_file *s, void *v, loff_t *pos) +{ + struct apu_sw_logger *logger = s->private; + struct sw_logger_seq_data *psdata = v; + + if (!psdata) { + pr_err("%s: psdata == NULL\n", __func__); + return NULL; + } + + psdata->i = (psdata->i + LOG_LINE_MAX_LENS) % APU_LOG_SIZE; + + /* prevent kernel warning */ + *pos = psdata->i; + + if (psdata->i != psdata->w_ptr) + return v; + + iowrite32(psdata->i, logger->apu_slog + LOG_R_PTR); + return NULL; +} + +/* + * stop() is called when iteration is complete (clean up) + */ +static void seq_stop(struct seq_file *s, void *v) +{ + struct apu_sw_logger *logger = (struct apu_sw_logger *)s->private; + unsigned long flags; + + if (logger->pseqdata) { + if (logger->pseqdata->is_finished == 1) { + spin_lock_irqsave(&logger->logger_spinlock, flags); + iowrite32(logger->pseqdata->i, + logger->apu_slog + LOG_R_PTR); + /* fixme: assume next overflow won't happen + * until next seq_start + */ + iowrite32(0, logger->apu_slog + LOG_OV_FLG); + spin_unlock_irqrestore(&logger->logger_spinlock, flags); + kfree(logger->pseqdata); + logger->pseqdata = NULL; + } + } +} + +/* + * stop() is called when iteration is complete (clean up) + */ +static void seq_stopl(struct seq_file *s, void *v) +{ +} + +/* + * success return 0, otherwise return error code + */ +static int seq_show(struct seq_file *s, void *v) +{ + struct apu_sw_logger *logger = (struct apu_sw_logger *)s->private; + struct sw_logger_seq_data *psdata = v; + + seq_printf(s, "%s", logger->sw_log_buf + psdata->i); + + return 0; +} + +static int seq_showl(struct seq_file *s, void *v) +{ + struct apu_sw_logger *logger = (struct apu_sw_logger *)s->private; + struct sw_logger_seq_data *psdata = v; + + if (psdata->i != psdata->w_ptr) + seq_printf(s, "%s", logger->sw_log_buf + psdata->i); + + return 0; +} + +static const struct seq_operations seq_ops = { + .start = seq_start, + .next = seq_next, + .stop = seq_stop, + .show = seq_show +}; + +static const struct seq_operations seq_ops_lock = { + .start = seq_startl, + .next = seq_next_lock, + .stop = seq_stopl, + .show = seq_showl +}; + +static int debug_sqopen_lock(struct inode *inode, struct file *file) +{ + struct apu_sw_logger *logger = inode->i_private; + int ret; + + ret = seq_open(file, &seq_ops_lock); + if (ret) + return ret; + + ((struct seq_file *)file->private_data)->private = logger; + + return 0; +} + +static int debug_sqopen(struct inode *inode, struct file *file) +{ + struct apu_sw_logger *logger = inode->i_private; + int ret; + + ret = seq_open(file, &seq_ops); + if (ret) + return ret; + + ((struct seq_file *)file->private_data)->private = logger; + + return 0; +} + +static const struct file_operations seqlog_ops = { + .owner = THIS_MODULE, + .open = debug_sqopen, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct file_operations seqlogl_ops = { + .owner = THIS_MODULE, + .open = debug_sqopen_lock, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int sw_logger_create_debugfs(struct device *dev) +{ + struct apu_sw_logger *logger = dev_get_drvdata(dev); + int ret = 0; + + logger->dbg_root = NULL; + log_root = debugfs_create_dir(APUSYS_LOGGER_DIR, logger->dbg_root); + ret = IS_ERR_OR_NULL(log_root); + if (ret) { + dev_err(dev, "(%d)failed to create apusys_logger dir\n", ret); + goto out; + } + + log_seqlog = debugfs_create_file("seq_log", 0444, + log_root, logger, &seqlog_ops); + ret = IS_ERR_OR_NULL(log_seqlog); + if (ret) { + dev_err(dev, "(%d)failed to create apusys_logger node(seqlog)\n", + ret); + goto out; + } + + log_seqlogl = debugfs_create_file("seq_logl", 0444, + log_root, logger, &seqlogl_ops); + ret = IS_ERR_OR_NULL(log_seqlogl); + if (ret) { + dev_err(dev, "(%d)failed to create apusys_logger node(seqlogL)\n", + ret); + goto out; + } + + return 0; + +out: + debugfs_remove_recursive(log_root); + return ret; +} + +static void sw_logger_remove_debugfs(struct device *dev) +{ + debugfs_remove_recursive(log_root); +} + +static int sw_logger_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apu_sw_logger *logger; + struct resource *res; + int ret = 0; + + logger = devm_kzalloc(dev, sizeof(struct apu_sw_logger), GFP_KERNEL); + if (!logger) + return -ENOMEM; + + platform_set_drvdata(pdev, logger); + + logger->dev = dev; + sw_logger_dev = dev; + spin_lock_init(&logger->logger_spinlock); + + ret = sw_logger_create_debugfs(dev); + if (ret) { + dev_err(dev, "%s fail\n", __func__); + goto remove_debugfs; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_info(dev, "%s: apu log get resource fail\n", __func__); + ret = -ENODEV; + goto remove_ioremap; + } + logger->apu_slog = ioremap(res->start, res->end - res->start + 1); + if (IS_ERR((void const *)logger->apu_slog)) { + dev_info(dev, "%s: apu_slog remap base fail\n", __func__); + ret = -ENOMEM; + goto remove_ioremap; + } + + return 0; + +remove_ioremap: + if (logger->apu_slog) + iounmap(logger->apu_slog); + +remove_debugfs: + sw_logger_remove_debugfs(dev); + + return ret; +} + +static int sw_logger_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + sw_logger_remove_debugfs(dev); + + return 0; +} + +static const struct of_device_id sw_logger_of_match[] = { + { .compatible = "mediatek,apu-sw-logger"}, + {}, +}; +MODULE_DEVICE_TABLE(of, sw_logger_of_match); + +static struct platform_driver sw_logger_driver = { + .probe = sw_logger_probe, + .remove = sw_logger_remove, + .driver = { + .name = "apu-sw-logger", + .of_match_table = of_match_ptr(sw_logger_of_match), + } +}; + +static int __init sw_logger_init(void) +{ + int ret = 0; + + allow_signal(SIGKILL); + ret = platform_driver_register(&sw_logger_driver); + if (ret != 0) { + pr_err("failed to register sw_logger driver"); + return -ENODEV; + } + + return ret; +} + +static void __exit sw_logger_exit(void) +{ + platform_driver_unregister(&sw_logger_driver); +} + +module_init(sw_logger_init); +module_exit(sw_logger_exit); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MediaTek APU SW Logger Driver"); + From patchwork Fri Dec 10 17:25:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670297 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9A080C4332F for ; Fri, 10 Dec 2021 17:27:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244558AbhLJRae (ORCPT ); Fri, 10 Dec 2021 12:30:34 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:43338 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244519AbhLJRab (ORCPT ); Fri, 10 Dec 2021 12:30:31 -0500 X-UUID: 6a824ece93a24450aee9d5f7f71fdf79-20211211 X-UUID: 6a824ece93a24450aee9d5f7f71fdf79-20211211 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 1171561744; Sat, 11 Dec 2021 01:26:50 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs07n1.mediatek.inc (172.21.101.16) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:48 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:48 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 11/17] soc: mediatek: apu: Add middleware driver Date: Sat, 11 Dec 2021 01:25:59 +0800 Message-ID: <20211210172605.30618-12-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org The APU middleware is responsible to receive all user's requests and control command and device related flow. In Kernel side, the middleware use the IPI to send command to remote tinysys to dispatch commands to AI engines in APU. Signed-off-by: JB Tsai Signed-off-by: Flora Fu --- drivers/soc/mediatek/apusys/Makefile | 10 + drivers/soc/mediatek/apusys/apu-device.h | 39 + drivers/soc/mediatek/apusys/mdw-cmd.c | 618 +++++++++++++++ drivers/soc/mediatek/apusys/mdw-drv.c | 226 ++++++ drivers/soc/mediatek/apusys/mdw-ioctl.c | 331 ++++++++ drivers/soc/mediatek/apusys/mdw-ioctl.h | 256 +++++++ drivers/soc/mediatek/apusys/mdw-mem.c | 938 +++++++++++++++++++++++ drivers/soc/mediatek/apusys/mdw-mem.h | 23 + drivers/soc/mediatek/apusys/mdw-rv-cmd.c | 158 ++++ drivers/soc/mediatek/apusys/mdw-rv-dev.c | 386 ++++++++++ drivers/soc/mediatek/apusys/mdw-rv-msg.h | 90 +++ drivers/soc/mediatek/apusys/mdw-rv.c | 131 ++++ drivers/soc/mediatek/apusys/mdw-rv.h | 98 +++ drivers/soc/mediatek/apusys/mdw-sysfs.c | 200 +++++ drivers/soc/mediatek/apusys/mdw.h | 207 +++++ 15 files changed, 3711 insertions(+) create mode 100644 drivers/soc/mediatek/apusys/apu-device.h create mode 100644 drivers/soc/mediatek/apusys/mdw-cmd.c create mode 100644 drivers/soc/mediatek/apusys/mdw-drv.c create mode 100644 drivers/soc/mediatek/apusys/mdw-ioctl.c create mode 100644 drivers/soc/mediatek/apusys/mdw-ioctl.h create mode 100644 drivers/soc/mediatek/apusys/mdw-mem.c create mode 100644 drivers/soc/mediatek/apusys/mdw-mem.h create mode 100644 drivers/soc/mediatek/apusys/mdw-rv-cmd.c create mode 100644 drivers/soc/mediatek/apusys/mdw-rv-dev.c create mode 100644 drivers/soc/mediatek/apusys/mdw-rv-msg.h create mode 100644 drivers/soc/mediatek/apusys/mdw-rv.c create mode 100644 drivers/soc/mediatek/apusys/mdw-rv.h create mode 100644 drivers/soc/mediatek/apusys/mdw-sysfs.c create mode 100644 drivers/soc/mediatek/apusys/mdw.h diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile index 6fb69abcf3b9..9104c2979b85 100644 --- a/drivers/soc/mediatek/apusys/Makefile +++ b/drivers/soc/mediatek/apusys/Makefile @@ -7,3 +7,13 @@ mtk_apu_pwr-objs += apu-pwr-ipi.o mtk_apu_pwr-$(CONFIG_MTK_APU_DEBUG) += apu-pwr-dbg.o obj-$(CONFIG_MTK_APU_DEBUG) += apu-sw-logger.o + +obj-$(CONFIG_MTK_APU) += mtk_apu_mdw.o +mtk_apu_mdw-objs += mdw-drv.o +mtk_apu_mdw-objs += mdw-ioctl.o +mtk_apu_mdw-objs += mdw-mem.o +mtk_apu_mdw-objs += mdw-cmd.o +mtk_apu_mdw-objs += mdw-rv.o +mtk_apu_mdw-objs += mdw-rv-cmd.o +mtk_apu_mdw-objs += mdw-rv-dev.o +mtk_apu_mdw-$(CONFIG_DEBUG_FS) += mdw-sysfs.o diff --git a/drivers/soc/mediatek/apusys/apu-device.h b/drivers/soc/mediatek/apusys/apu-device.h new file mode 100644 index 000000000000..dddd8a3ddf8d --- /dev/null +++ b/drivers/soc/mediatek/apusys/apu-device.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef APUSYS_DEVICE_H +#define APUSYS_DEVICE_H + +#include + +/* device type */ +enum { + APUSYS_DEVICE_NONE, + + APUSYS_DEVICE_SAMPLE, + APUSYS_DEVICE_MDLA, + APUSYS_DEVICE_VPU, + APUSYS_DEVICE_EDMA, + APUSYS_DEVICE_RT = 32, + APUSYS_DEVICE_SAMPLE_RT, + APUSYS_DEVICE_MDLA_RT, + APUSYS_DEVICE_VPU_RT, + + APUSYS_DEVICE_MAX = 64, +}; + +/* device definition */ +#define APUSYS_DEVICE_META_SIZE (32) + +struct apusys_device { + int dev_type; + int idx; + int preempt_type; + u8 preempt_level; + char meta_data[APUSYS_DEVICE_META_SIZE]; + void *private; + int (*send_cmd)(int type, void *hnd, struct apusys_device *dev); +}; +#endif diff --git a/drivers/soc/mediatek/apusys/mdw-cmd.c b/drivers/soc/mediatek/apusys/mdw-cmd.c new file mode 100644 index 000000000000..231a82b4ba0e --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-cmd.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include + +#include "mdw.h" +#include "mdw-mem.h" + +static struct mdw_mem *mdw_cmd_get_mem(u64 handle) +{ + struct mdw_mem *m = NULL; + + m = mdw_mem_get(handle); + if (!m) + return NULL; + + mdw_mem_dma_map(m); + + return m; +} + +static int mdw_cmd_put_mem(struct mdw_mem *m) +{ + return mdw_mem_dma_unmap(m); +} + +static void mdw_cmd_put_cmdbufs(struct mdw_fpriv *mpriv, struct mdw_cmd *c) +{ + struct mdw_subcmd_kinfo *ksubcmd = NULL; + unsigned int i = 0, j = 0; + + if (!c->cmdbufs) + return; + + /* flush cmdbufs and execinfos */ + apusys_mem_invalidate_kva(c->cmdbufs->vaddr, c->cmdbufs->size); + + for (i = 0; i < c->num_subcmds; i++) { + ksubcmd = &c->ksubcmds[i]; + for (j = 0; j < ksubcmd->info->num_cmdbufs; j++) { + if (!ksubcmd->ori_cbs[j]) + continue; + + /* cmdbuf copy out */ + if (ksubcmd->cmdbufs[j].direction != MDW_CB_IN) + memcpy + (ksubcmd->ori_cbs[j]->vaddr, + (void *)ksubcmd->kvaddrs[j], + ksubcmd->ori_cbs[j]->size + ); + + mdw_cmd_put_mem(ksubcmd->ori_cbs[j]); + ksubcmd->ori_cbs[j] = NULL; + } + } + mdw_mem_unmap(c->cmdbufs); + mdw_mem_free(c->cmdbufs); + + c->cmdbufs = NULL; +} + +static int mdw_cmd_get_cmdbufs(struct mdw_fpriv *mpriv, struct mdw_cmd *c) +{ + unsigned int i = 0, j = 0, ofs = 0; + int ret = -EINVAL; + struct mdw_subcmd_kinfo *ksubcmd = NULL; + struct mdw_mem *m = NULL; + struct device *dev = mpriv->mdev->dev; + + if (!c->size_cmdbufs || c->cmdbufs) + goto out; + + /* alloc cmdbuf by dmabuf */ + c->cmdbufs = mdw_mem_alloc + (mpriv, c->size_cmdbufs, MDW_DEFAULT_ALIGN, + (1ULL << MDW_MEM_IOCTL_ALLOC_CACHEABLE | + 1ULL << MDW_MEM_IOCTL_ALLOC_32BIT), + MDW_MEM_TYPE_INTERNAL); + if (!c->cmdbufs) { + dev_err(dev, "mem alloc fail\n"); + ret = -ENOMEM; + goto out; + } + + ret = mdw_mem_map(c->cmdbufs); + if (ret) { + dev_err(dev, "mem map fail\n"); + mdw_mem_free(c->cmdbufs); + ret = -ENOMEM; + goto out; + } + + /* alloc mem for duplicated cmdbuf */ + for (i = 0; i < c->num_subcmds; i++) { + ksubcmd = &c->ksubcmds[i]; + for (j = 0; j < ksubcmd->info->num_cmdbufs; j++) { + /* calc align */ + if (ksubcmd->cmdbufs[j].align) + ofs = MDW_ALIGN(ofs, ksubcmd->cmdbufs[j].align); + else + ofs = MDW_ALIGN(ofs, MDW_DEFAULT_ALIGN); + + /* get mem from handle */ + m = mdw_cmd_get_mem(ksubcmd->cmdbufs[j].handle); + if (!m) { + dev_err(dev, "cmd get mem fail\n"); + goto free_cmdbufs; + } + /* check mem boundary */ + if (!m->vaddr || ksubcmd->cmdbufs[j].size != m->size) { + dev_err(dev, "size of cmdbuf is invalid\n"); + goto free_cmdbufs; + } + + /* cmdbuf copy in */ + if (ksubcmd->cmdbufs[j].direction != MDW_CB_OUT) + memcpy(c->cmdbufs->vaddr + ofs, + m->vaddr, + ksubcmd->cmdbufs[j].size); + + /* record buffer info */ + ksubcmd->ori_cbs[j] = m; + ksubcmd->kvaddrs[j] = + (u64)(c->cmdbufs->vaddr + ofs); + ksubcmd->daddrs[j] = + (u64)(c->cmdbufs->device_va + ofs); + ofs += ksubcmd->cmdbufs[j].size; + } + } + /* flush cmdbufs */ + apusys_mem_flush_kva(c->cmdbufs->vaddr, c->cmdbufs->size); + + ret = 0; + goto out; + +free_cmdbufs: + mdw_cmd_put_cmdbufs(mpriv, c); +out: + return ret; +} + +static unsigned int mdw_cmd_create_infos(struct mdw_fpriv *mpriv, + struct mdw_cmd *c) +{ + unsigned int i = 0, j = 0, total_size = 0; + struct mdw_subcmd_exec_info *sc_einfo = NULL; + int ret = -ENOMEM; + + c->einfos = c->exec_infos->vaddr; + if (!c->einfos) { + dev_err(mpriv->mdev->dev, "invalid exec info addr\n"); + return -EINVAL; + } + sc_einfo = &c->einfos->sc; + + for (i = 0; i < c->num_subcmds; i++) { + c->ksubcmds[i].info = &c->subcmds[i]; + + c->ksubcmds[i].ori_cbs = kvzalloc(c->subcmds[i].num_cmdbufs * + sizeof(c->ksubcmds[i].ori_cbs), GFP_KERNEL); + if (!c->ksubcmds[i].ori_cbs) + goto free_cmdbufs; + + c->ksubcmds[i].kvaddrs = kvzalloc(c->subcmds[i].num_cmdbufs * + sizeof(*c->ksubcmds[i].kvaddrs), GFP_KERNEL); + if (!c->ksubcmds[i].kvaddrs) + goto free_cmdbufs; + + c->ksubcmds[i].daddrs = kvzalloc(c->subcmds[i].num_cmdbufs * + sizeof(*c->ksubcmds[i].daddrs), GFP_KERNEL); + if (!c->ksubcmds[i].daddrs) + goto free_cmdbufs; + + c->ksubcmds[i].cmdbufs = kvzalloc(c->subcmds[i].num_cmdbufs * + sizeof(*c->ksubcmds[i].cmdbufs), GFP_KERNEL); + if (!c->ksubcmds[i].cmdbufs) + goto free_cmdbufs; + + if (copy_from_user(c->ksubcmds[i].cmdbufs, + (void __user *)c->subcmds[i].cmdbufs, + c->subcmds[i].num_cmdbufs * + sizeof(*c->ksubcmds[i].cmdbufs))) + goto free_cmdbufs; + + c->ksubcmds[i].sc_einfo = &sc_einfo[i]; + + /* accumulate cmdbuf size with alignment */ + for (j = 0; j < c->subcmds[i].num_cmdbufs; j++) { + c->num_cmdbufs++; + if (c->ksubcmds[i].cmdbufs[j].align) + total_size = + MDW_ALIGN(total_size, + c->ksubcmds[i].cmdbufs[j].align) + + c->ksubcmds[i].cmdbufs[j].size; + else + total_size += c->ksubcmds[i].cmdbufs[j].size; + } + } + c->size_cmdbufs = total_size; + + ret = mdw_cmd_get_cmdbufs(mpriv, c); + if (ret) + goto free_cmdbufs; + + goto out; + +free_cmdbufs: + for (i = 0; i < c->num_subcmds; i++) { + /* free dvaddrs */ + if (c->ksubcmds[i].daddrs) { + kvfree(c->ksubcmds[i].daddrs); + c->ksubcmds[i].daddrs = NULL; + } + /* free kvaddrs */ + if (c->ksubcmds[i].kvaddrs) { + kvfree(c->ksubcmds[i].kvaddrs); + c->ksubcmds[i].kvaddrs = NULL; + } + /* free ori kvas */ + if (c->ksubcmds[i].ori_cbs) { + kvfree(c->ksubcmds[i].ori_cbs); + c->ksubcmds[i].ori_cbs = NULL; + } + /* free cmdbufs */ + if (c->ksubcmds[i].cmdbufs) { + kvfree(c->ksubcmds[i].cmdbufs); + c->ksubcmds[i].cmdbufs = NULL; + } + } + +out: + return ret; +} + +static void mdw_cmd_delete_infos(struct mdw_fpriv *mpriv, struct mdw_cmd *c) +{ + unsigned int i = 0; + + mdw_cmd_put_cmdbufs(mpriv, c); + + for (i = 0; i < c->num_subcmds; i++) { + /* free dvaddrs */ + if (c->ksubcmds[i].daddrs) { + kvfree(c->ksubcmds[i].daddrs); + c->ksubcmds[i].daddrs = NULL; + } + /* free kvaddrs */ + if (c->ksubcmds[i].kvaddrs) { + kvfree(c->ksubcmds[i].kvaddrs); + c->ksubcmds[i].kvaddrs = NULL; + } + /* free ori kvas */ + if (c->ksubcmds[i].ori_cbs) { + kvfree(c->ksubcmds[i].ori_cbs); + c->ksubcmds[i].ori_cbs = NULL; + } + /* free cmdbufs */ + if (c->ksubcmds[i].cmdbufs) { + kvfree(c->ksubcmds[i].cmdbufs); + c->ksubcmds[i].cmdbufs = NULL; + } + } +} + +static const char *mdw_fence_get_driver_name(struct dma_fence *fence) +{ + return "apu_mdw"; +} + +static const char *mdw_fence_get_timeline_name(struct dma_fence *fence) +{ + struct mdw_fence *f = + container_of(fence, struct mdw_fence, base_fence); + + return dev_name(f->mdev->misc_dev.this_device); +} + +static bool mdw_fence_enable_signaling(struct dma_fence *fence) +{ + return true; +} + +static void mdw_fence_release(struct dma_fence *fence) +{ + struct mdw_fence *mf = + container_of(fence, struct mdw_fence, base_fence); + + kvfree(mf); +} + +static const struct dma_fence_ops mdw_fence_ops = { + .get_driver_name = mdw_fence_get_driver_name, + .get_timeline_name = mdw_fence_get_timeline_name, + .enable_signaling = mdw_fence_enable_signaling, + .wait = dma_fence_default_wait, + .release = mdw_fence_release, +}; + +static int mdw_fence_init(struct mdw_cmd *c) +{ + int ret = 0; + + c->fence = kvzalloc(sizeof(*c->fence), GFP_KERNEL); + if (!c->fence) + return -ENOMEM; + + c->fence->mdev = c->mpriv->mdev; + dma_fence_init(&c->fence->base_fence, &mdw_fence_ops, + &c->fence->lock, 0, 0); + spin_lock_init(&c->fence->lock); + + return ret; +} + +static int mdw_cmd_run(struct mdw_fpriv *mpriv, struct mdw_cmd *c) +{ + struct mdw_device *mdev = mpriv->mdev; + int ret = 0; + + ktime_get_ts64(&c->start_ts); + ret = mdev->dev_funcs->run_cmd(mpriv, c); + if (ret) { + dev_err(mpriv->mdev->dev, "run cmd(0x%llx/0x%llx) fail(%d)\n", + (u64)c->mpriv, c->kid, ret); + + dma_fence_set_error(&c->fence->base_fence, ret); + dma_fence_signal(&c->fence->base_fence); + dma_fence_put(&c->fence->base_fence); + } + + return ret; +} + +static void mdw_cmd_delete(struct mdw_cmd *c) +{ + struct mdw_fpriv *mpriv = c->mpriv; + + mdw_cmd_delete_infos(c->mpriv, c); + mdw_cmd_put_mem(c->exec_infos); + kvfree(c->adj_matrix); + kvfree(c->ksubcmds); + kvfree(c->subcmds); + mutex_lock(&mpriv->mtx); + list_del(&c->u_item); + mutex_unlock(&mpriv->mtx); + kvfree(c); + + mpriv->put(mpriv); +} + +static int mdw_cmd_complete(struct mdw_cmd *c, int ret) +{ + struct dma_fence *f = &c->fence->base_fence; + + ktime_get_ts64(&c->end_ts); + c->einfos->c.total_us = + (c->end_ts.tv_sec - c->start_ts.tv_sec) * 1000000; + c->einfos->c.total_us += + ((c->end_ts.tv_nsec - c->start_ts.tv_nsec) / 1000); + + /* check subcmds return value */ + if (c->einfos->c.sc_rets) + if (!ret) + ret = -EIO; + + c->einfos->c.ret = ret; + + if (ret) + pr_debug("cmd(%p/0x%llx) ret(%d/0x%llx) time(%llu) pid(%d/%d)\n", + c->mpriv, c->kid, ret, c->einfos->c.sc_rets, + c->einfos->c.total_us, c->pid, c->tgid); + + mdw_cmd_put_cmdbufs(c->mpriv, c); + if (ret) + dma_fence_set_error(&c->fence->base_fence, ret); + + dma_fence_signal(f); + mdw_cmd_delete(c); + dma_fence_put(f); + + return 0; +} + +static void mdw_cmd_trigger_func(struct work_struct *wk) +{ + struct mdw_cmd *c = + container_of(wk, struct mdw_cmd, t_wk); + + if (c->wait_fence) { + dma_fence_wait(c->wait_fence, false); + dma_fence_put(c->wait_fence); + } + + mdw_cmd_run(c->mpriv, c); +} + +static int mdw_cmd_sanity_check(struct mdw_cmd *c) +{ + if (c->priority >= MDW_PRIORITY_MAX || + c->num_subcmds > MDW_SUBCMD_MAX) { + pr_err("cmd invalid (0x%llx/0x%llx/0x%llx)(%u/%u)\n", + c->uid, (u64)c->mpriv, c->kid, + c->priority, c->num_subcmds); + return -EINVAL; + } + + if (c->exec_infos->size != sizeof(struct mdw_cmd_exec_info) + + c->num_subcmds * sizeof(struct mdw_subcmd_exec_info)) { + pr_err("cmd invalid (0x%llx/0x%llx/0x%llx) einfo(%u/%lu)\n", + c->uid, (u64)c->mpriv, c->kid, + c->exec_infos->size, + sizeof(struct mdw_cmd_exec_info) + + c->num_subcmds * sizeof(struct mdw_subcmd_exec_info)); + return -EINVAL; + } + + return 0; +} + +static int mdw_cmd_sc_sanity_check(struct mdw_cmd *c) +{ + unsigned int i = 0; + + for (i = 0; i < c->num_subcmds; i++) { + if (c->subcmds[i].type >= MDW_DEV_MAX || + c->subcmds[i].vlm_ctx_id >= MDW_SUBCMD_MAX || + c->subcmds[i].boost > MDW_BOOST_MAX || + c->subcmds[i].pack_id >= MDW_SUBCMD_MAX) { + pr_err("subcmd(%u) invalid (%u/%u/%u)\n", + i, c->subcmds[i].type, + c->subcmds[i].boost, + c->subcmds[i].pack_id); + return -EINVAL; + } + } + + return 0; +} + +static struct mdw_cmd *mdw_cmd_create(struct mdw_fpriv *mpriv, + union mdw_cmd_args *args) +{ + struct mdw_cmd_in *in = (struct mdw_cmd_in *)args; + struct mdw_cmd *c = NULL; + + if (in->exec.num_subcmds > MDW_SUBCMD_MAX) { + dev_err(mpriv->mdev->dev, "too much subcmds(%u)\n", + in->exec.num_subcmds); + goto out; + } + + c = kvzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + goto out; + + c->mpriv = mpriv; + c->pid = current->pid; + c->tgid = current->tgid; + c->kid = (u64)c; + c->uid = in->exec.uid; + c->usr_id = in->exec.usr_id; + c->priority = in->exec.priority; + c->hardlimit = in->exec.hardlimit; + c->softlimit = in->exec.softlimit; + c->power_save = in->exec.power_save; + c->power_plcy = in->exec.power_plcy; + c->power_dtime = in->exec.power_dtime; + c->app_type = in->exec.app_type; + c->num_subcmds = in->exec.num_subcmds; + c->exec_infos = mdw_cmd_get_mem(in->exec.exec_infos); + if (!c->exec_infos) { + dev_err(mpriv->mdev->dev, "get exec info fail\n"); + goto free_cmd; + } + + if (mdw_cmd_sanity_check(c)) { + dev_err(mpriv->mdev->dev, "cmd sanity check fail\n"); + goto free_cmd; + } + + c->subcmds = kvzalloc(c->num_subcmds * sizeof(*c->subcmds), GFP_KERNEL); + if (!c->subcmds) + goto free_cmd; + if (copy_from_user(c->subcmds, (void __user *)in->exec.subcmd_infos, + c->num_subcmds * sizeof(*c->subcmds))) { + dev_err(mpriv->mdev->dev, "copy subcmds fail\n"); + goto free_subcmds; + } + if (mdw_cmd_sc_sanity_check(c)) { + dev_err(mpriv->mdev->dev, "sc sanity check fail\n"); + goto free_subcmds; + } + + c->ksubcmds = kvzalloc(c->num_subcmds * sizeof(*c->ksubcmds), + GFP_KERNEL); + if (!c->ksubcmds) + goto free_subcmds; + + /* adj matrix */ + c->adj_matrix = kvzalloc(c->num_subcmds * + c->num_subcmds * sizeof(u8), GFP_KERNEL); + if (!c->adj_matrix) + goto free_ksubcmds; + if (copy_from_user(c->adj_matrix, (void __user *)in->exec.adj_matrix, + (c->num_subcmds * c->num_subcmds * sizeof(u8))) + ) { + dev_err(mpriv->mdev->dev, "copy adj matrix fail\n"); + goto free_adj; + } + if (mdw_cmd_create_infos(mpriv, c)) { + dev_err(mpriv->mdev->dev, "create cmd info fail\n"); + goto put_execinfo; + } + if (mdw_fence_init(c)) { + dev_err(mpriv->mdev->dev, "cmd init fence fail\n"); + goto delete_infos; + } + + c->mpriv->get(c->mpriv); + c->complete = mdw_cmd_complete; + INIT_WORK(&c->t_wk, &mdw_cmd_trigger_func); + mutex_lock(&mpriv->mtx); + list_add_tail(&c->u_item, &mpriv->cmds); + mutex_unlock(&mpriv->mtx); + + goto out; + +delete_infos: + mdw_cmd_delete_infos(mpriv, c); +put_execinfo: + mdw_cmd_put_mem(c->exec_infos); +free_adj: + kvfree(c->adj_matrix); +free_ksubcmds: + kvfree(c->ksubcmds); +free_subcmds: + kvfree(c->subcmds); +free_cmd: + kvfree(c); + c = NULL; +out: + return c; +} + +static int mdw_cmd_ioctl_run(struct mdw_fpriv *mpriv, union mdw_cmd_args *args) +{ + struct mdw_cmd_in *in = (struct mdw_cmd_in *)args; + struct mdw_cmd *c = NULL; + struct sync_file *sync_file = NULL; + int ret = 0, fd = 0, wait_fd = 0; + + wait_fd = in->exec.fence; + + c = mdw_cmd_create(mpriv, args); + if (!c) { + dev_err(mpriv->mdev->dev, "create cmd fail\n"); + ret = -EINVAL; + goto out; + } + memset(args, 0, sizeof(*args)); + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + dev_err(mpriv->mdev->dev, "get unused fd fail\n"); + ret = -EINVAL; + goto delete_cmd; + } + sync_file = sync_file_create(&c->fence->base_fence); + if (!sync_file) { + dev_err(mpriv->mdev->dev, "create sync file fail\n"); + ret = -ENOMEM; + goto put_file; + } + + /* check wait fence from other module */ + c->wait_fence = sync_file_get_fence(wait_fd); + if (!c->wait_fence) + ret = mdw_cmd_run(mpriv, c); + else + schedule_work(&c->t_wk); + + if (ret) + goto put_file; + + fd_install(fd, sync_file->file); + args->out.exec.fence = fd; + goto out; + +delete_cmd: + mdw_cmd_delete(c); +put_file: + put_unused_fd(fd); +out: + + return ret; +} + +int mdw_cmd_ioctl(struct mdw_fpriv *mpriv, void *data) +{ + union mdw_cmd_args *args = (union mdw_cmd_args *)data; + int ret = 0; + + switch (args->in.op) { + case MDW_CMD_IOCTL_RUN: + ret = mdw_cmd_ioctl_run(mpriv, args); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} diff --git a/drivers/soc/mediatek/apusys/mdw-drv.c b/drivers/soc/mediatek/apusys/mdw-drv.c new file mode 100644 index 000000000000..fdd4149f3775 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-drv.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "mdw.h" +#include "mdw-mem.h" + +int mdw_dev_init(struct mdw_device *mdev) +{ + int ret = -ENODEV; + + mdw_rv_set_func(mdev); + + if (mdev->dev_funcs) + ret = mdev->dev_funcs->late_init(mdev); + + return ret; +} + +void mdw_dev_deinit(struct mdw_device *mdev) +{ + if (mdev->dev_funcs) { + mdev->dev_funcs->late_deinit(mdev); + mdev->dev_funcs = NULL; + } +} + +static void mdw_drv_priv_delete(struct kref *ref) +{ + struct mdw_fpriv *mpriv = + container_of(ref, struct mdw_fpriv, ref); + + kfree(mpriv); +} + +static void mdw_drv_priv_get(struct mdw_fpriv *mpriv) +{ + kref_get(&mpriv->ref); +} + +static void mdw_drv_priv_put(struct mdw_fpriv *mpriv) +{ + kref_put(&mpriv->ref, mdw_drv_priv_delete); +} + +static int mdw_drv_open(struct inode *inode, struct file *filp) +{ + struct mdw_device *mdev; + struct mdw_fpriv *mpriv = NULL; + int ret = 0; + + mdev = container_of(filp->private_data, struct mdw_device, misc_dev); + if (!mdev) { + pr_warn("apusys/mdw: apu mdw no dev\n"); + return -ENODEV; + } + + if (!mdev->inited) { + dev_dbg(mdev->dev, "apu mdw dev not init"); + return -EBUSY; + } + + if (!atomic_read(&mdev->sw_inited)) { + ret = mdev->dev_funcs->sw_init(mdev); + if (ret) { + dev_err(mdev->dev, "mdw sw init fail(%d)\n", ret); + return -EFAULT; + } + atomic_inc(&mdev->sw_inited); + } + + mpriv = kzalloc(sizeof(*mpriv), GFP_KERNEL); + if (!mpriv) + return -ENOMEM; + + mpriv->mdev = mdev; + filp->private_data = mpriv; + mutex_init(&mpriv->mtx); + INIT_LIST_HEAD(&mpriv->mems); + INIT_LIST_HEAD(&mpriv->cmds); + + mpriv->get = mdw_drv_priv_get; + mpriv->put = mdw_drv_priv_put; + kref_init(&mpriv->ref); + + return ret; +} + +static int mdw_drv_close(struct inode *inode, struct file *filp) +{ + struct mdw_fpriv *mpriv = NULL; + + mpriv = filp->private_data; + mutex_lock(&mpriv->mtx); + mdw_mem_mpriv_release(mpriv); + mutex_unlock(&mpriv->mtx); + mpriv->put(mpriv); + + return 0; +} + +static const struct file_operations mdw_fops = { + .owner = THIS_MODULE, + .open = mdw_drv_open, + .release = mdw_drv_close, + .unlocked_ioctl = mdw_ioctl, + .compat_ioctl = mdw_ioctl, +}; + +static int mdw_rpmsg_probe(struct rpmsg_device *rpdev) +{ + struct device *dev = &rpdev->dev; + struct mdw_device *mdev = NULL; + int ret = 0; + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mdev->rpdev = rpdev; + mdev->dev = dev; + mdev->dma_dev = dev->parent; + mdev->misc_dev.minor = MISC_DYNAMIC_MINOR; + mdev->misc_dev.name = MDW_NAME; + mdev->misc_dev.fops = &mdw_fops; + ret = misc_register(&mdev->misc_dev); + + if (ret) + goto out; + + dev_set_drvdata(dev, mdev); + + ret = mdw_mem_init(mdev); + if (ret) + goto misc_unreg; +#ifdef CONFIG_DEBUG_FS + ret = mdw_sysfs_init(mdev); + if (ret) + goto deinit_mem; +#endif + ret = mdw_dev_init(mdev); + if (ret) + goto deinit_dbg; + + goto out; + +#ifdef CONFIG_DEBUG_FS +deinit_dbg: + mdw_sysfs_deinit(mdev); +#endif +deinit_mem: + mdw_mem_deinit(mdev); +misc_unreg: + misc_deregister(&mdev->misc_dev); +out: + return ret; +} + +static void mdw_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct mdw_device *mdev = dev_get_drvdata(&rpdev->dev); + + mdev->dev_funcs->sw_deinit(mdev); + mdw_dev_deinit(mdev); +#ifdef CONFIG_DEBUG_FS + mdw_sysfs_deinit(mdev); +#endif + mdw_mem_deinit(mdev); + misc_deregister(&mdev->misc_dev); + kfree(mdev); +} + +static const struct of_device_id mdw_rpmsg_of_match[] = { + { .compatible = "mediatek,apu-mdw-rpmsg", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mdw_rpmsg_of_match); + +static struct rpmsg_driver mdw_rpmsg_driver = { + .drv = { + .name = "apu-mdw-rpmsg", + .owner = THIS_MODULE, + .of_match_table = mdw_rpmsg_of_match, + }, + .probe = mdw_rpmsg_probe, + .remove = mdw_rpmsg_remove, +}; + +static struct platform_driver mdw_driver = { + .driver = { + .name = "mtk-mdw", + .of_match_table = of_match_ptr(mdw_rpmsg_of_match), + }, +}; + +static int __init mdw_init(void) +{ + int ret = 0; + + platform_driver_register(&mdw_driver); + ret = register_rpmsg_driver(&mdw_rpmsg_driver); + if (ret) + pr_err("Failed to register apu mdw rpmsg driver\n"); + + return ret; +} + +static void __exit mdw_exit(void) +{ + unregister_rpmsg_driver(&mdw_rpmsg_driver); + platform_driver_unregister(&mdw_driver); +} + +module_init(mdw_init); +module_exit(mdw_exit); +MODULE_DESCRIPTION("APU Middlewre Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/soc/mediatek/apusys/mdw-ioctl.c b/drivers/soc/mediatek/apusys/mdw-ioctl.c new file mode 100644 index 000000000000..4e543c257f1b --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-ioctl.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include + +#include "mdw-ioctl.h" +#include "mdw.h" +#include "mdw-mem.h" + +static int mdw_mem_ioctl_alloc(struct mdw_fpriv *mpriv, + union mdw_mem_args *args) +{ + struct mdw_mem_in *in = (struct mdw_mem_in *)args; + struct mdw_mem *m = NULL; + int ret = 0; + + if (!in->alloc.size) { + dev_err(mpriv->mdev->dev, "invalid size(%u)\n", in->alloc.size); + return -EINVAL; + } + + m = mdw_mem_alloc(mpriv, in->alloc.size, in->alloc.align, + in->alloc.flags, MDW_MEM_TYPE_ALLOC); + memset(args, 0, sizeof(*args)); + if (!m) { + dev_err(mpriv->mdev->dev, "mdw_mem_alloc fail\n"); + return -ENOMEM; + } + + args->out.alloc.handle = m->handle; + + mutex_lock(&mpriv->mtx); + list_add_tail(&m->u_item, &mpriv->mems); + mutex_unlock(&mpriv->mtx); + + return ret; +} + +static int mdw_mem_ioctl_map(struct mdw_fpriv *mpriv, + union mdw_mem_args *args) +{ + struct mdw_mem_in *in = (struct mdw_mem_in *)args; + struct mdw_mem *m = NULL; + int ret = -ENOMEM, handle = (int)in->map.handle; + u32 size = in->map.size; + + memset(args, 0, sizeof(*args)); + + mutex_lock(&mpriv->mtx); + m = mdw_mem_get(handle); + if (!m) { + /* mem not alloc from apu, import buffer */ + m = mdw_mem_import(mpriv, handle, size); + if (m) + ret = 0; + goto out; + } + + /* already exist */ + ret = mdw_mem_map(m); + if (ret) + dev_err(mpriv->mdev->dev, "map fail\n"); + +out: + if (m) + args->out.map.device_va = m->device_va; + mutex_unlock(&mpriv->mtx); + + return ret; +} + +static int mdw_mem_ioctl_unmap(struct mdw_fpriv *mpriv, + union mdw_mem_args *args) +{ + struct mdw_mem_in *in = (struct mdw_mem_in *)args; + struct mdw_mem *m = NULL; + int ret = -ENOMEM, handle = in->unmap.handle; + + memset(args, 0, sizeof(*args)); + + mutex_lock(&mpriv->mtx); + m = mdw_mem_get(handle); + if (!m) + goto out; + + ret = mdw_mem_unmap(m); + +out: + mutex_unlock(&mpriv->mtx); + + return ret; +} + +static int mdw_mem_ioctl_flush(struct mdw_fpriv *mpriv, + union mdw_mem_args *args) +{ + struct mdw_mem_in *in = (struct mdw_mem_in *)args; + struct mdw_mem *m = NULL; + int ret = -ENOMEM, handle = in->flush.handle; + + memset(args, 0, sizeof(*args)); + + mutex_lock(&mpriv->mtx); + m = mdw_mem_get(handle); + if (!m) + goto out; + + ret = mdw_mem_flush(m); +out: + mutex_unlock(&mpriv->mtx); + return ret; +} + +static int mdw_mem_ioctl_invalidate(struct mdw_fpriv *mpriv, + union mdw_mem_args *args) +{ + struct mdw_mem_in *in = (struct mdw_mem_in *)args; + struct mdw_mem *m = NULL; + int ret = -ENOMEM, handle = in->invalidate.handle; + + memset(args, 0, sizeof(*args)); + + mutex_lock(&mpriv->mtx); + m = mdw_mem_get(handle); + if (!m) + goto out; + + ret = mdw_mem_invalidate(m); +out: + mutex_unlock(&mpriv->mtx); + return ret; +} + +static int mdw_mem_ioctl(struct mdw_fpriv *mpriv, void *data) +{ + union mdw_mem_args *args = (union mdw_mem_args *)data; + int ret = 0; + + switch (args->in.op) { + case MDW_MEM_IOCTL_ALLOC: + ret = mdw_mem_ioctl_alloc(mpriv, args); + break; + + case MDW_MEM_IOCTL_MAP: + ret = mdw_mem_ioctl_map(mpriv, args); + break; + + case MDW_MEM_IOCTL_FREE: + pr_warn("not suppot free\n"); + ret = -EFAULT; + break; + + case MDW_MEM_IOCTL_UNMAP: + ret = mdw_mem_ioctl_unmap(mpriv, args); + break; + + case MDW_MEM_IOCTL_FLUSH: + ret = mdw_mem_ioctl_flush(mpriv, args); + break; + + case MDW_MEM_IOCTL_INVALIDATE: + ret = mdw_mem_ioctl_invalidate(mpriv, args); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mdw_hs_ioctl(struct mdw_fpriv *mpriv, void *data) +{ + union mdw_hs_args *args = (union mdw_hs_args *)data; + struct mdw_device *mdev = mpriv->mdev; + unsigned int type = 0; + int ret = 0; + + switch (args->in.op) { + case MDW_HS_IOCTL_OP_BASIC: + memset(args, 0, sizeof(*args)); + args->out.basic.version = mdev->uapi_ver; + memcpy(&args->out.basic.dev_bitmask, + mdev->dev_mask, sizeof(args->out.basic.dev_bitmask)); + args->out.basic.meta_size = MDW_DEV_META_SIZE; + args->out.basic.vlm_start = mdev->vlm_start; + args->out.basic.vlm_size = mdev->vlm_size; + break; + + case MDW_HS_IOCTL_OP_DEV: + type = args->in.dev.type; + if (type >= MDW_DEV_MAX) { + ret = -EINVAL; + break; + } + + if (!mdev->dinfos[type]) { + ret = -EINVAL; + break; + } + + memset(args, 0, sizeof(*args)); + args->out.dev.type = type; + args->out.dev.num = mdev->dinfos[type]->num; + memcpy(args->out.dev.meta, mdev->dinfos[type]->meta, + sizeof(args->out.dev.meta)); + break; + + default: + dev_err(mpriv->mdev->dev, + "invalid handshake op code(%d)\n", args->in.op); + ret = -EINVAL; + break; + } + + return ret; +} + +static int mdw_util_ioctl(struct mdw_fpriv *mpriv, void *data) +{ + union mdw_util_args *args = (union mdw_util_args *)data; + struct mdw_util_in *in = (struct mdw_util_in *)args; + struct mdw_device *mdev = mpriv->mdev; + void *mem_ucmd = NULL; + int ret = 0; + + switch (args->in.op) { + case MDW_UTIL_IOCTL_SETPOWER: + ret = mdev->dev_funcs->set_power(mdev, in->power.dev_type, + in->power.core_idx, in->power.boost); + break; + + case MDW_UTIL_IOCTL_UCMD: + if (!in->ucmd.size || !in->ucmd.handle) { + dev_err(mpriv->mdev->dev, "invalid ucmd param\n"); + ret = -EINVAL; + break; + } + + mem_ucmd = vzalloc(args->in.ucmd.size); + if (!mem_ucmd) { + ret = -ENOMEM; + break; + } + + if (copy_from_user(mem_ucmd, + (void __user *)in->ucmd.handle, + in->ucmd.size)) { + ret = -EFAULT; + goto free_ucmd; + } + ret = mdev->dev_funcs->ucmd(mdev, in->ucmd.dev_type, + mem_ucmd, in->ucmd.size); + if (ret) { + dev_err(mpriv->mdev->dev, "dev(%d) ucmd fail\n", + in->ucmd.dev_type); + goto free_ucmd; + } + + if (copy_to_user((void __user *)in->ucmd.handle, + mem_ucmd, in->ucmd.size)) + ret = -EFAULT; + +free_ucmd: + vfree(mem_ucmd); + break; + + default: + dev_err(mpriv->mdev->dev, "invalid util op code(%d)\n", + args->in.op); + ret = -EINVAL; + break; + } + + return ret; +} + +long mdw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + unsigned int usize = 0; + void *kdata = NULL; + struct mdw_fpriv *mpriv = filp->private_data; + + usize = _IOC_SIZE(cmd); + kdata = kzalloc(usize, GFP_KERNEL); + if (!kdata) + return -ENOMEM; + + if (cmd & IOC_IN) { + if (copy_from_user(kdata, (void __user *)arg, usize)) { + ret = -EFAULT; + goto out; + } + } + + switch (cmd) { + case APU_MDW_IOCTL_HANDSHAKE: + ret = mdw_hs_ioctl(mpriv, kdata); + break; + case APU_MDW_IOCTL_MEM: + ret = mdw_mem_ioctl(mpriv, kdata); + break; + case APU_MDW_IOCTL_CMD: + ret = mdw_cmd_ioctl(mpriv, kdata); + break; + case APU_MDW_IOCTL_UTIL: + ret = mdw_util_ioctl(mpriv, kdata); + break; + default: + ret = -EFAULT; + goto out; + } + + if (cmd & IOC_OUT) { + if (copy_to_user((void __user *)arg, kdata, usize)) { + ret = -EFAULT; + goto out; + } + } + +out: + kfree(kdata); + + return ret; +} diff --git a/drivers/soc/mediatek/apusys/mdw-ioctl.h b/drivers/soc/mediatek/apusys/mdw-ioctl.h new file mode 100644 index 000000000000..1f735b400f4b --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-ioctl.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef __MTK_APU_IOCTL_H__ +#define __MTK_APU_IOCTL_H__ + +#include +#include + +#define APUSYS_MAGICNO 'A' + +enum mdw_hs_ioctl_op { + MDW_HS_IOCTL_OP_BASIC, + MDW_HS_IOCTL_OP_DEV, +}; + +struct mdw_hs_in { + enum mdw_hs_ioctl_op op; + u64 flags; + union { + struct { + u32 type; + } dev; + }; +}; + +#define MDW_DEV_META_SIZE (32) + +struct mdw_hs_out { + union { + struct { + u64 version; + u64 dev_bitmask; + u64 flags; + u32 meta_size; + u64 vlm_start; + u32 vlm_size; + } basic; + + struct { + u32 type; + u32 num; + char meta[MDW_DEV_META_SIZE]; + } dev; + }; +}; + +union mdw_hs_args { + struct mdw_hs_in in; + struct mdw_hs_out out; +}; + +#define APU_MDW_IOCTL_HANDSHAKE \ + _IOWR(APUSYS_MAGICNO, 32, union mdw_hs_args) + +enum mdw_mem_ioctl_op { + MDW_MEM_IOCTL_ALLOC, + MDW_MEM_IOCTL_FREE, + MDW_MEM_IOCTL_MAP, + MDW_MEM_IOCTL_UNMAP, + MDW_MEM_IOCTL_FLUSH, + MDW_MEM_IOCTL_INVALIDATE, +}; + +enum MDW_MEM_IOCTL_ALLOC_BITMASK { + MDW_MEM_IOCTL_ALLOC_CACHEABLE, + MDW_MEM_IOCTL_ALLOC_32BIT, + MDW_MEM_IOCTL_ALLOC_HIGHADDR, +}; + +struct mdw_mem_in { + enum mdw_mem_ioctl_op op; + u64 flags; + union { + /* alloc */ + struct { + u32 size; + u32 align; + u64 flags; /* enum MDW_MEM_IOCTL_ALLOC_BITMASK */ + } alloc; + struct { + u64 handle; + } free; + + /* map */ + struct { + u64 handle; + u32 size; + } map; + struct { + u64 handle; + } unmap; + + /* cache operation */ + struct { + u64 handle; + } flush; + struct { + u64 handle; + } invalidate; + }; +}; + +struct mdw_mem_out { + union { + struct { + u64 handle; + } alloc; + struct { + u64 device_va; + } map; + struct { + u64 device_va; + u32 size; + } import; + }; +}; + +union mdw_mem_args { + struct mdw_mem_in in; + struct mdw_mem_out out; +}; + +#define APU_MDW_IOCTL_MEM \ + _IOWR(APUSYS_MAGICNO, 33, union mdw_mem_args) + +enum mdw_cmd_ioctl_op { + MDW_CMD_IOCTL_RUN, +}; + +enum { + /* cmdbuf copy in before execution and copy out after exection */ + MDW_CB_BIDIRECTIONAL, + /* cmdbuf copy in before execution */ + MDW_CB_IN, + /* cmdbuf copy out after execution */ + MDW_CB_OUT, +}; + +struct mdw_subcmd_exec_info { + u32 driver_time; + u32 ip_time; + u32 ip_start_ts; + u32 ip_end_ts; + u32 bw; + u32 boost; + u32 tcm_usage; + s32 ret; +}; + +struct mdw_cmd_exec_info { + u64 sc_rets; + s64 ret; + u64 total_us; + u64 reserved; +}; + +struct mdw_subcmd_cmdbuf { + u64 handle; + u32 size; + u32 align; + u32 direction; +}; + +struct mdw_subcmd_info { + u32 type; + u32 suggest_time; + u32 vlm_usage; + u32 vlm_ctx_id; + u32 vlm_force; + u32 boost; + u32 turbo_boost; + u32 min_boost; + u32 max_boost; + u32 hse_en; + u32 pack_id; + u32 driver_time; + u32 ip_time; + u32 bw; + + u32 num_cmdbufs; + u64 cmdbufs; +}; + +struct mdw_cmd_in { + enum mdw_cmd_ioctl_op op; + union { + struct { + u64 usr_id; + u64 uid; + u32 priority; + u32 hardlimit; + u32 softlimit; + u32 power_save; + u32 power_plcy; + u32 power_dtime; + u32 app_type; + u32 flags; + u32 num_subcmds; + u64 subcmd_infos; + u64 adj_matrix; + u64 fence; + u64 exec_infos; + } exec; + }; +}; + +struct mdw_cmd_out { + union { + struct { + u64 id; + u64 fence; + } exec; + }; +}; + +union mdw_cmd_args { + struct mdw_cmd_in in; + struct mdw_cmd_out out; +}; + +#define APU_MDW_IOCTL_CMD \ + _IOWR(APUSYS_MAGICNO, 34, union mdw_cmd_args) + +enum mdw_util_ioctl_op { + MDW_UTIL_IOCTL_SETPOWER, + MDW_UTIL_IOCTL_UCMD, +}; + +struct mdw_util_in { + enum mdw_util_ioctl_op op; + union { + struct { + u32 dev_type; + u32 core_idx; + u32 boost; + u64 reserve; + } power; + struct { + u32 dev_type; + u32 size; + u64 handle; + } ucmd; + }; +}; + +union mdw_util_args { + struct mdw_util_in in; +}; + +#define APU_MDW_IOCTL_UTIL \ + _IOWR(APUSYS_MAGICNO, 35, union mdw_util_args) + +#endif diff --git a/drivers/soc/mediatek/apusys/mdw-mem.c b/drivers/soc/mediatek/apusys/mdw-mem.c new file mode 100644 index 000000000000..c5aea746f6a8 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-mem.c @@ -0,0 +1,938 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include + +#include "apu-device.h" +#include "mdw.h" +#include "mdw-mem.h" + +#define APU_MEM_DMA_MASK 0x00000003ffffffff + +struct mdw_mem_dma_attachment { + struct sg_table *sgt; + struct device *dev; + struct list_head node; + bool mapped; + bool uncached; +}; + +struct mdw_mem_dma { + struct dma_buf *dbuf; + dma_addr_t dma_addr; + u32 dma_size; + u32 size; + struct { + int handle; + void *vaddr; + struct list_head attachments; + struct sg_table sgt; + void *buf; + } a; + struct { + struct dma_buf_attachment *attach; + struct sg_table *sgt; + } m; + bool uncached; + struct kref attach_ref; + + struct mutex mtx; /* protect attachments */ + + struct mdw_mem *mmem; + struct device *mem_dev; + struct list_head m_item; +}; + +struct mdw_mem_dma_mgr { + struct list_head mems; + struct mutex mtx; /* protect mems */ +}; + +static struct mdw_mem_dma_mgr mdmgr; + +static struct sg_table *mdw_mem_dma_dup_sg(struct sg_table *table) +{ + struct sg_table *new_table; + int ret, i; + struct scatterlist *sg, *new_sg; + + new_table = kzalloc(sizeof(*new_table), GFP_KERNEL); + if (!new_table) + return ERR_PTR(-ENOMEM); + + ret = sg_alloc_table(new_table, table->orig_nents, GFP_KERNEL); + if (ret) { + kfree(new_table); + return ERR_PTR(-ENOMEM); + } + + new_sg = new_table->sgl; + for_each_sgtable_sg(table, sg, i) { + sg_set_page(new_sg, sg_page(sg), sg->length, sg->offset); + new_sg = sg_next(new_sg); + } + + return new_table; +} + +static int mdw_mem_dma_allocate_sgt(const char *buf, size_t len, + struct sg_table *sgt, bool uncached, + void **vaddr) +{ + struct page **pages = NULL; + unsigned int nr_pages; + unsigned int index; + const char *p; + int ret; + pgprot_t pgprot = PAGE_KERNEL; + void *va; + + nr_pages = DIV_ROUND_UP((unsigned long)buf + len, PAGE_SIZE) + - ((unsigned long)buf / PAGE_SIZE); + pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_KERNEL); + + if (!pages) + return -ENOMEM; + + p = buf - offset_in_page(buf); + + for (index = 0; index < nr_pages; index++) { + if (is_vmalloc_addr(p)) + pages[index] = vmalloc_to_page(p); + else + pages[index] = kmap_to_page((void *)p); + if (!pages[index]) { + kfree(pages); + return -EFAULT; + } + p += PAGE_SIZE; + } + if (uncached) + pgprot = pgprot_writecombine(PAGE_KERNEL); + + va = vmap(pages, nr_pages, VM_MAP, pgprot); + ret = sg_alloc_table_from_pages(sgt, pages, index, offset_in_page(buf), + len, GFP_KERNEL); + kfree(pages); + if (ret) + return ret; + + *vaddr = va; + + return 0; +} + +static int mdw_mem_dma_free_sgt(struct sg_table *sgt) +{ + int ret = 0; + + sg_free_table(sgt); + + return ret; +} + +static int mdw_dmabuf_attach(struct dma_buf *dbuf, + struct dma_buf_attachment *attach) +{ + struct mdw_mem_dma_attachment *a = NULL; + struct mdw_mem_dma *mdbuf = dbuf->priv; + int ret = 0; + struct sg_table *table; + + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + table = mdw_mem_dma_dup_sg(&mdbuf->a.sgt); + if (IS_ERR(table)) { + kfree(a); + return -ENOMEM; + } + + a->sgt = table; + a->dev = attach->dev; + INIT_LIST_HEAD(&a->node); + a->mapped = false; + a->uncached = mdbuf->uncached; + attach->priv = a; + + mutex_lock(&mdbuf->mtx); + list_add(&a->node, &mdbuf->a.attachments); + mutex_unlock(&mdbuf->mtx); + + return ret; +} + +static void mdw_dmabuf_detach(struct dma_buf *dbuf, + struct dma_buf_attachment *attach) +{ + struct mdw_mem_dma_attachment *a = attach->priv; + struct mdw_mem_dma *mdbuf = dbuf->priv; + + mutex_lock(&mdbuf->mtx); + list_del(&a->node); + mutex_unlock(&mdbuf->mtx); + + sg_free_table(a->sgt); + kfree(a->sgt); + kfree(a); +} + +static struct sg_table *mdw_dmabuf_map_dma(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct mdw_mem_dma_attachment *a = attach->priv; + struct sg_table *table = NULL; + int attr = 0; + int ret = 0; + + table = a->sgt; + if (a->uncached) + attr |= DMA_ATTR_SKIP_CPU_SYNC; + + ret = dma_map_sgtable(attach->dev, table, dir, attr); + if (ret) + table = ERR_PTR(ret); + + a->mapped = true; + + return table; +} + +static void mdw_dmabuf_unmap_dma(struct dma_buf_attachment *attach, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + struct mdw_mem_dma_attachment *a = attach->priv; + int attr = 0; + + if (a->uncached) + attr |= DMA_ATTR_SKIP_CPU_SYNC; + + a->mapped = false; + dma_unmap_sgtable(attach->dev, sgt, dir, attr); +} + +static int mdw_dmabuf_vmap(struct dma_buf *dbuf, struct dma_buf_map *dbuf_map) +{ + struct mdw_mem_dma *mdbuf = dbuf->priv; + + dbuf_map->vaddr = mdbuf->a.vaddr; + return 0; +} + +static void mdw_dmabuf_release(struct dma_buf *dbuf) +{ + struct mdw_mem_dma *mdbuf = dbuf->priv; + struct mdw_mem *m = mdbuf->mmem; + + mutex_lock(&mdmgr.mtx); + list_del(&mdbuf->m_item); + mutex_unlock(&mdmgr.mtx); + + if (m->type != MDW_MEM_TYPE_IMPORT) { + mdw_mem_dma_free_sgt(&mdbuf->a.sgt); + vunmap(mdbuf->a.vaddr); + kvfree(mdbuf->a.buf); + } + + kfree(mdbuf); + m->release(m); +} + +static int mdw_dmabuf_mmap(struct dma_buf *dbuf, + struct vm_area_struct *vma) +{ + struct mdw_mem_dma *mdbuf = dbuf->priv; + struct sg_table *table = &mdbuf->a.sgt; + unsigned long addr = vma->vm_start; + struct sg_page_iter piter; + int ret = 0; + + if (mdbuf->uncached) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + for_each_sgtable_page(table, &piter, vma->vm_pgoff) { + struct page *page = sg_page_iter_page(&piter); + + ret = remap_pfn_range(vma, addr, page_to_pfn(page), PAGE_SIZE, + vma->vm_page_prot); + if (ret) + return ret; + addr += PAGE_SIZE; + if (addr >= vma->vm_end) + return 0; + } + return ret; +} + +static struct dma_buf_ops mdw_dmabuf_ops = { + .attach = mdw_dmabuf_attach, + .detach = mdw_dmabuf_detach, + .map_dma_buf = mdw_dmabuf_map_dma, + .unmap_dma_buf = mdw_dmabuf_unmap_dma, + .vmap = mdw_dmabuf_vmap, + .mmap = mdw_dmabuf_mmap, + .release = mdw_dmabuf_release, +}; + +static struct mdw_mem *mdw_mem_dma_get(int handle) +{ + struct dma_buf *dbuf = NULL; + struct mdw_mem_dma *m = NULL, *pos = NULL, *tmp = NULL; + + dbuf = dma_buf_get(handle); + if (IS_ERR_OR_NULL(dbuf)) + return NULL; + + mutex_lock(&mdmgr.mtx); + list_for_each_entry_safe(pos, tmp, &mdmgr.mems, m_item) { + if (pos->dbuf == dbuf) { + m = pos; + break; + } + } + mutex_unlock(&mdmgr.mtx); + + dma_buf_put(dbuf); + if (!m) { + pr_err("handle(%d) not belong to apu\n", handle); + return NULL; + } + + return m->mmem; +} + +static int mdw_mem_dma_alloc(struct mdw_mem *mem) +{ + struct mdw_mem_dma *mdbuf = NULL; + int ret = 0; + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct device *dev; + void *kva; + bool uncached = true; + + /* alloc mdw dma-buf container */ + mdbuf = kzalloc(sizeof(*mdbuf), GFP_KERNEL); + if (!mdbuf) + return -ENOMEM; + + mutex_init(&mdbuf->mtx); + INIT_LIST_HEAD(&mdbuf->a.attachments); + + /* alloc buffer by dma */ + mdbuf->dma_size = PAGE_ALIGN(mem->size); + + dev = mem->mpriv->mdev->dma_dev; + + if (!dev) { + pr_err("dev invalid\n"); + ret = -ENOMEM; + goto free_mdw_dbuf; + } + + kva = kvzalloc(mdbuf->dma_size, GFP_KERNEL); + if (!kva) { + ret = -ENOMEM; + goto free_mdw_dbuf; + } + + mdbuf->a.buf = kva; + + if (mdw_mem_dma_allocate_sgt(kva, mdbuf->dma_size, &mdbuf->a.sgt, + uncached, &mdbuf->a.vaddr)) { + dev_err(dev, "get sgt: failed\n"); + ret = -ENOMEM; + goto free_buf; + } + + exp_info.ops = &mdw_dmabuf_ops; + exp_info.size = mdbuf->dma_size; + exp_info.flags = O_RDWR | O_CLOEXEC; + exp_info.priv = mdbuf; + + mdbuf->dbuf = dma_buf_export(&exp_info); + if (IS_ERR(mdbuf->dbuf)) { + dev_err(dev, "dma_buf_export Fail\n"); + ret = -ENOMEM; + goto free_sgt; + } + + mdbuf->dbuf->priv = mdbuf; + mdbuf->mmem = mem; + mdbuf->mem_dev = dev; + mdbuf->size = mem->size; + mdbuf->uncached = uncached; + mem->device_va = mdbuf->dma_addr; + mem->vaddr = mdbuf->a.vaddr; + mem->priv = mdbuf; + mutex_lock(&mdmgr.mtx); + list_add_tail(&mdbuf->m_item, &mdmgr.mems); + mutex_unlock(&mdmgr.mtx); + + dma_sync_sgtable_for_device(mdbuf->mem_dev, &mdbuf->a.sgt, + DMA_TO_DEVICE); + + /* internal use, don't export fd */ + if (!mem->need_handle) { + mem->handle = -1; + goto out; + } + + mdbuf->a.handle = dma_buf_fd(mdbuf->dbuf, + (O_RDWR | O_CLOEXEC) & ~O_ACCMODE); + if (mdbuf->a.handle < 0) { + ret = -EINVAL; + dev_err(dev, "dma_buf_fd Fail\n"); + dma_buf_put(mdbuf->dbuf); + return ret; + } + mem->handle = mdbuf->a.handle; + +out: + return ret; + +free_sgt: + mdw_mem_dma_free_sgt(&mdbuf->a.sgt); +free_buf: + kvfree(kva); +free_mdw_dbuf: + kfree(mdbuf); + + return ret; +} + +static int mdw_mem_dma_free(struct mdw_mem *mem) +{ + struct mdw_mem_dma *mdbuf = mem->priv; + + dma_buf_put(mdbuf->dbuf); + + return 0; +} + +int mdw_mem_dma_map(struct mdw_mem *mem) +{ + struct mdw_mem_dma *mdbuf = NULL; + int ret = 0; + + mdbuf = (struct mdw_mem_dma *)mem->priv; + + if (IS_ERR_OR_NULL(mdbuf->dbuf)) + return -EINVAL; + + get_dma_buf(mdbuf->dbuf); + + /* Only Attach after First Map */ + if (kref_read(&mdbuf->attach_ref)) { + kref_get(&mdbuf->attach_ref); + goto out; + } else { + kref_init(&mdbuf->attach_ref); + } + + mdbuf->m.attach = dma_buf_attach(mdbuf->dbuf, mdbuf->mem_dev); + if (IS_ERR(mdbuf->m.attach)) { + ret = PTR_ERR(mdbuf->m.attach); + dev_err(mdbuf->mem_dev, "dma_buf_attach failed: %d\n", ret); + goto put_dbuf; + } + + mdbuf->m.sgt = dma_buf_map_attachment(mdbuf->m.attach, + DMA_BIDIRECTIONAL); + if (IS_ERR(mdbuf->m.sgt)) { + ret = PTR_ERR(mdbuf->m.sgt); + dev_err(mdbuf->mem_dev, "dma_buf_map_attachment failed: %d\n", + ret); + goto detach_dbuf; + } + + mdbuf->dma_addr = sg_dma_address(mdbuf->m.sgt->sgl); + mdbuf->dma_size = sg_dma_len(mdbuf->m.sgt->sgl); + if (!mdbuf->dma_addr || !mdbuf->dma_size) { + dev_err(mdbuf->mem_dev, "can't get mem(0x%llx) dva(0x%llx/%u)\n", + (u64)mem, mdbuf->dma_addr, mdbuf->dma_size); + ret = -ENOMEM; + goto unmap_dbuf; + } + + mem->device_va = mdbuf->dma_addr; + mem->dva_size = mdbuf->dma_size; +out: + return ret; + +unmap_dbuf: + dma_buf_unmap_attachment(mdbuf->m.attach, mdbuf->m.sgt, + DMA_BIDIRECTIONAL); +detach_dbuf: + dma_buf_detach(mdbuf->dbuf, mdbuf->m.attach); +put_dbuf: + dma_buf_put(mdbuf->dbuf); + + return ret; +} + +static void mdw_mem_dma_detach(struct kref *ref) +{ + struct mdw_mem_dma *mdbuf; + + mdbuf = container_of(ref, struct mdw_mem_dma, attach_ref); + dma_buf_unmap_attachment(mdbuf->m.attach, mdbuf->m.sgt, + DMA_BIDIRECTIONAL); + dma_buf_detach(mdbuf->dbuf, mdbuf->m.attach); +} + +int mdw_mem_dma_unmap(struct mdw_mem *mem) +{ + struct mdw_mem_dma *mdbuf = mem->priv; + int ret = 0; + + /* Only Detach after last Map */ + kref_put(&mdbuf->attach_ref, mdw_mem_dma_detach); + + dma_buf_put(mdbuf->dbuf); + + return ret; +} + +static int mdw_mem_dma_import(struct mdw_mem *mem) +{ + int ret = 0; + struct mdw_mem_dma *mdbuf = NULL; + struct dma_buf *dbuf = NULL; + struct device *dev; + + if (mem->device_va || mem->priv) + return -EINVAL; + + dbuf = dma_buf_get(mem->handle); + if (IS_ERR_OR_NULL(dbuf)) + return -ENOMEM; + + /* Import Use 32 Bit Buffer */ + dev = mem->mpriv->mdev->dma_dev; + if (!dev) { + pr_err("dev invalid\n"); + ret = -ENOMEM; + goto put_dbuf; + } + + mdbuf = kzalloc(sizeof(*mdbuf), GFP_KERNEL); + if (!mdbuf) { + ret = -ENOMEM; + goto put_dbuf; + } + + mdbuf->mmem = mem; + mdbuf->dbuf = dbuf; + mdbuf->mem_dev = dev; + mdbuf->size = mem->size; + + /* Only Attach after First Map */ + if (kref_read(&mdbuf->attach_ref)) { + kref_get(&mdbuf->attach_ref); + goto out; + } else { + kref_init(&mdbuf->attach_ref); + } + + mdbuf->m.attach = dma_buf_attach(mdbuf->dbuf, dev); + if (IS_ERR(mdbuf->m.attach)) { + ret = PTR_ERR(mdbuf->m.attach); + dev_err(dev, "dma_buf_attach failed: %d\n", ret); + goto free_mdbuf; + } + + mdbuf->m.sgt = dma_buf_map_attachment(mdbuf->m.attach, + DMA_BIDIRECTIONAL); + if (IS_ERR(mdbuf->m.sgt)) { + ret = PTR_ERR(mdbuf->m.sgt); + dev_err(dev, "dma_buf_map_attachment failed: %d\n", ret); + goto detach_dbuf; + } + + mdbuf->dma_addr = sg_dma_address(mdbuf->m.sgt->sgl); + mdbuf->dma_size = sg_dma_len(mdbuf->m.sgt->sgl); + if (!mdbuf->dma_addr || !mdbuf->dma_size) { + dev_err(dev, "can't get mem(0x%llx) dva(0x%llx/%u)\n", + (u64)mem, mdbuf->dma_addr, mdbuf->dma_size); + ret = -ENOMEM; + goto unmap_dbuf; + } + mem->device_va = mdbuf->dma_addr; + mem->priv = mdbuf; + mutex_lock(&mdmgr.mtx); + list_add_tail(&mdbuf->m_item, &mdmgr.mems); + mutex_unlock(&mdmgr.mtx); + +out: + return ret; + +unmap_dbuf: + dma_buf_unmap_attachment(mdbuf->m.attach, mdbuf->m.sgt, + DMA_BIDIRECTIONAL); +detach_dbuf: + dma_buf_detach(mdbuf->dbuf, mdbuf->m.attach); +free_mdbuf: + kfree(mdbuf); +put_dbuf: + dma_buf_put(dbuf); + + return ret; +} + +static int mdw_mem_dma_unimport(struct mdw_mem *mem) +{ + struct mdw_mem_dma *mdbuf = NULL; + int ret = 0; + + if (IS_ERR_OR_NULL(mem->priv)) + return -EINVAL; + + mdbuf = (struct mdw_mem_dma *)mem->priv; + + if (IS_ERR_OR_NULL(mdbuf->m.attach) || + IS_ERR_OR_NULL(mdbuf->m.sgt)) + return -EINVAL; + + mutex_lock(&mdmgr.mtx); + list_del(&mdbuf->m_item); + mutex_unlock(&mdmgr.mtx); + + /* Only Detach after last Map */ + kref_put(&mdbuf->attach_ref, mdw_mem_dma_detach); + + mem->device_va = 0; + mem->priv = NULL; + + dma_buf_put(mdbuf->dbuf); + kfree(mdbuf); + + return ret; +} + +static int mdw_mem_dma_flush(struct mdw_mem *mem) +{ + int ret = 0; + struct mdw_mem_dma *mdbuf = mem->priv; + + if (!mdbuf->a.vaddr) { + pr_warn("mdbuf vaddr NULL\n"); + goto out; + } + + if (!mdbuf->uncached) + dma_sync_sgtable_for_device(mdbuf->mem_dev, &mdbuf->a.sgt, + DMA_TO_DEVICE); +out: + return ret; +} + +static int mdw_mem_dma_invalidate(struct mdw_mem *mem) +{ + int ret = 0; + struct mdw_mem_dma *mdbuf = mem->priv; + + if (!mdbuf->a.vaddr) { + pr_warn("mdbuf vaddr NULL\n"); + goto out; + } + + if (!mdbuf->uncached) + dma_sync_sgtable_for_cpu(mdbuf->mem_dev, &mdbuf->a.sgt, + DMA_FROM_DEVICE); + +out: + return ret; +} + +static struct mdw_mem *mdw_mem_dma_query_mem(u64 kva) +{ + struct mdw_mem_dma *pos = NULL, *tmp = NULL; + struct mdw_mem *m = NULL; + struct mdw_mem *target = NULL; + + mutex_lock(&mdmgr.mtx); + list_for_each_entry_safe(pos, tmp, &mdmgr.mems, m_item) { + m = pos->mmem; + if (kva >= (u64)m->vaddr && + kva < (u64)m->vaddr + m->size) + target = m; + } + mutex_unlock(&mdmgr.mtx); + + return target; +} + +static int mdw_mem_dma_init(void) +{ + mutex_init(&mdmgr.mtx); + INIT_LIST_HEAD(&mdmgr.mems); + + return 0; +} + +static void mdw_mem_dma_deinit(void) +{ +} + +struct mdw_mem *mdw_mem_get(int handle) +{ + return mdw_mem_dma_get(handle); +} + +static void mdw_mem_delete(struct mdw_mem *m) +{ + struct mdw_fpriv *mpriv = m->mpriv; + + switch (m->type) { + case MDW_MEM_TYPE_ALLOC: + mutex_lock(&mpriv->mtx); + list_del(&m->u_item); + mutex_unlock(&mpriv->mtx); + break; + case MDW_MEM_TYPE_IMPORT: + list_del(&m->u_item); + break; + default: + break; + } + + vfree(m); + mpriv->put(mpriv); +} + +static struct mdw_mem *mdw_mem_create(struct mdw_fpriv *mpriv) +{ + struct mdw_mem *m = NULL; + + m = vzalloc(sizeof(*m)); + if (m) { + m->mpriv = mpriv; + m->release = mdw_mem_delete; + mpriv->get(mpriv); + } + + return m; +} + +static void mdw_mem_map_release(struct kref *ref) +{ + struct mdw_mem *m = + container_of(ref, struct mdw_mem, map_ref); + + switch (m->type) { + case MDW_MEM_TYPE_INTERNAL: + mdw_mem_dma_unmap(m); + break; + + case MDW_MEM_TYPE_ALLOC: + mdw_mem_dma_unmap(m); + break; + + case MDW_MEM_TYPE_IMPORT: + mdw_mem_dma_unimport(m); + mdw_mem_delete(m); + break; + + default: + break; + } +} + +struct mdw_mem *mdw_mem_alloc(struct mdw_fpriv *mpriv, u32 size, + u32 align, u64 flags, + enum mdw_mem_type type) +{ + struct mdw_mem *m = NULL; + int ret = 0; + + m = mdw_mem_create(mpriv); + if (!m) + goto out; + + if (type == MDW_MEM_TYPE_INTERNAL) + m->need_handle = false; + else + m->need_handle = true; + m->size = size; + m->align = align; + m->flags = flags; + ret = mdw_mem_dma_alloc(m); + if (ret) { + dev_err(mpriv->mdev->dev, "mdw_mem_dma_alloc Fail (%d)\n", ret); + goto free_mem; + } + m->type = type; + + goto out; + +free_mem: + mdw_mem_delete(m); + m = NULL; +out: + return m; +} + +int mdw_mem_free(struct mdw_mem *m) +{ + return mdw_mem_dma_free(m); +} + +int mdw_mem_map(struct mdw_mem *m) +{ + int ret = 0; + + if (kref_read(&m->map_ref)) { + kref_get(&m->map_ref); + ret = 0; + } else { + ret = mdw_mem_dma_map(m); + if (ret) { + dev_err(m->mpriv->mdev->dev, "map fail %d\n", ret); + goto out; + } + kref_init(&m->map_ref); + } +out: + return ret; +} + +int mdw_mem_unmap(struct mdw_mem *m) +{ + if (!kref_read(&m->map_ref)) { + dev_warn(m->mpriv->mdev->dev, "can't unmap mem\n"); + return -EINVAL; + } + kref_put(&m->map_ref, mdw_mem_map_release); + + return 0; +} + +int mdw_mem_flush(struct mdw_mem *m) +{ + int ret = 0; + + ret = mdw_mem_dma_flush(m); + if (ret) { + dev_err(m->mpriv->mdev->dev, "Flush Fail\n"); + ret = -EINVAL; + goto out; + } +out: + return ret; +} + +int mdw_mem_invalidate(struct mdw_mem *m) +{ + int ret = 0; + + ret = mdw_mem_dma_invalidate(m); + if (ret) { + dev_err(m->mpriv->mdev->dev, "Invalidate Fail\n"); + ret = -EINVAL; + goto out; + } +out: + return ret; +} + +struct mdw_mem *mdw_mem_import(struct mdw_fpriv *mpriv, u64 handle, u32 size) +{ + struct mdw_mem *m = NULL; + + m = mdw_mem_create(mpriv); + if (!m) + return NULL; + + m->size = size; + m->handle = handle; + if (mdw_mem_dma_import(m)) { + dev_err(mpriv->mdev->dev, "import fail\n"); + goto free_mem; + } + + m->type = MDW_MEM_TYPE_IMPORT; + kref_init(&m->map_ref); + list_add_tail(&m->u_item, &mpriv->mems); + + goto out; + +free_mem: + mdw_mem_delete(m); + m = NULL; +out: + return m; +} + +void mdw_mem_mpriv_release(struct mdw_fpriv *mpriv) +{ + struct mdw_mem *m = NULL, *tmp = NULL; + int i = 0, ref_cnt = 0; + + list_for_each_entry_safe(m, tmp, &mpriv->mems, u_item) { + ref_cnt = kref_read(&m->map_ref); + for (i = 0; i < ref_cnt; i++) + kref_put(&m->map_ref, mdw_mem_map_release); + } +} + +int mdw_mem_init(struct mdw_device *mdev) +{ + int ret = 0; + + ret = dma_set_mask_and_coherent(mdev->dma_dev, APU_MEM_DMA_MASK); + if (ret) { + dev_info(mdev->dev, "unable to set DMA mask coherent: %d\n", + ret); + return ret; + } + + return mdw_mem_dma_init(); +} + +void mdw_mem_deinit(struct mdw_device *mdev) +{ + mdw_mem_dma_deinit(); +} + +int apusys_mem_flush_kva(void *kva, u32 size) +{ + struct mdw_mem *m = NULL; + int ret = 0; + + m = mdw_mem_dma_query_mem((u64)kva); + if (!m) { + pr_err("No Mem\n"); + ret = -ENOMEM; + goto out; + } + + ret = mdw_mem_flush(m); + +out: + return ret; +} + +int apusys_mem_invalidate_kva(void *kva, u32 size) +{ + struct mdw_mem *m = NULL; + int ret = 0; + + m = mdw_mem_dma_query_mem((u64)kva); + if (!m) { + pr_err("No Mem\n"); + ret = -ENOMEM; + goto out; + } + + ret = mdw_mem_invalidate(m); +out: + return ret; +} diff --git a/drivers/soc/mediatek/apusys/mdw-mem.h b/drivers/soc/mediatek/apusys/mdw-mem.h new file mode 100644 index 000000000000..cb744f41d15e --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-mem.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef __MTK_APU_MDW_MEM_H__ +#define __MTK_APU_MDW_MEM_H__ + +struct mdw_mem *mdw_mem_alloc(struct mdw_fpriv *mpriv, u32 size, + u32 align, u64 flags, + enum mdw_mem_type type); +struct mdw_mem *mdw_mem_import(struct mdw_fpriv *mpriv, u64 handle, u32 size); +struct mdw_mem *mdw_mem_get(int handle); +int mdw_mem_map(struct mdw_mem *m); +int mdw_mem_unmap(struct mdw_mem *m); +int mdw_mem_init(struct mdw_device *mdev); +void mdw_mem_deinit(struct mdw_device *mdev); +int mdw_mem_free(struct mdw_mem *mem); +int mdw_mem_dma_map(struct mdw_mem *mem); +int mdw_mem_dma_unmap(struct mdw_mem *mem); +int apusys_mem_flush_kva(void *kva, u32 size); +int apusys_mem_invalidate_kva(void *kva, u32 size); +#endif diff --git a/drivers/soc/mediatek/apusys/mdw-rv-cmd.c b/drivers/soc/mediatek/apusys/mdw-rv-cmd.c new file mode 100644 index 000000000000..13d5febf5b61 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-rv-cmd.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include "mdw.h" +#include "mdw-mem.h" +#include "mdw-rv.h" + +#define MDW_IS_HIGHADDR(addr) (((addr) & 0xffffffff00000000) ? true : false) + +struct mdw_rv_cmd *mdw_rv_cmd_create(struct mdw_fpriv *mpriv, struct mdw_cmd *c) +{ + struct mdw_rv_cmd *rc = NULL; + u32 cb_size = 0, acc_cb = 0, i = 0, j = 0; + u32 subcmds_ofs = 0, cmdbuf_infos_ofs = 0, adj_matrix_ofs = 0; + struct mdw_rv_msg_cmd *rmc = NULL; + struct mdw_rv_msg_sc *rmsc = NULL; + struct mdw_rv_msg_cb *rmcb = NULL; + int ret = 0; + + /* check mem address for rv */ + if (MDW_IS_HIGHADDR(c->exec_infos->device_va) || + MDW_IS_HIGHADDR(c->cmdbufs->device_va)) { + dev_err(mpriv->mdev->dev, "rv dva high addr(0x%llx/0x%llx)\n", + c->cmdbufs->device_va, c->exec_infos->device_va); + goto out; + } + + rc = vzalloc(sizeof(*rc)); + if (!rc) + goto out; + + init_completion(&rc->s_msg.cmplt); + /* set start timestamp */ + rc->start_ts_ns = c->start_ts.tv_sec * 1000000000 + c->start_ts.tv_nsec; + + /* calc size and offset */ + rc->c = c; + cb_size += sizeof(struct mdw_rv_msg_cmd); + cb_size = MDW_ALIGN(cb_size, MDW_DEFAULT_ALIGN); + adj_matrix_ofs = cb_size; + cb_size += (c->num_subcmds * c->num_subcmds * sizeof(u8)); + cb_size = MDW_ALIGN(cb_size, MDW_DEFAULT_ALIGN); + subcmds_ofs = cb_size; + cb_size += (c->num_subcmds * sizeof(struct mdw_rv_msg_sc)); + cb_size = MDW_ALIGN(cb_size, MDW_DEFAULT_ALIGN); + cmdbuf_infos_ofs = cb_size; + cb_size += (c->num_cmdbufs * sizeof(struct mdw_rv_msg_cb)); + + /* allocate communicate buffer */ + rc->cb = mdw_mem_alloc(mpriv, cb_size, MDW_DEFAULT_ALIGN, + (1ULL << MDW_MEM_IOCTL_ALLOC_CACHEABLE | + 1ULL << MDW_MEM_IOCTL_ALLOC_32BIT), + MDW_MEM_TYPE_INTERNAL); + if (!rc->cb) { + dev_err(mpriv->mdev->dev, "c(0x%llx) alloc cb size(%u) fail\n", + c->kid, cb_size); + goto free_rc; + } + + ret = mdw_mem_map(rc->cb); + if (ret) { + dev_err(mpriv->mdev->dev, "c(0x%llx) map cb size(%u) fail\n", + c->kid, cb_size); + goto free_mem; + } + + /* assign cmd info */ + rmc = (struct mdw_rv_msg_cmd *)rc->cb->vaddr; + rmc->session_id = (u64)c->mpriv; + rmc->cmd_id = c->kid; + rmc->exec_infos = c->exec_infos->device_va; + rmc->exec_size = c->exec_infos->size; + rmc->priority = c->priority; + rmc->hardlimit = c->hardlimit; + rmc->softlimit = c->softlimit; + rmc->power_save = c->power_save; + rmc->power_plcy = c->power_plcy; + rmc->power_dtime = c->power_dtime; + rmc->app_type = c->app_type; + rmc->num_subcmds = c->num_subcmds; + rmc->num_cmdbufs = c->num_cmdbufs; + rmc->subcmds_offset = subcmds_ofs; + rmc->cmdbuf_infos_offset = cmdbuf_infos_ofs; + rmc->adj_matrix_offset = adj_matrix_ofs; + + memcpy((void *)rmc + rmc->adj_matrix_offset, c->adj_matrix, + c->num_subcmds * c->num_subcmds * sizeof(u8)); + + rmsc = (void *)rmc + rmc->subcmds_offset; + rmcb = (void *)rmc + rmc->cmdbuf_infos_offset; + for (i = 0; i < c->num_subcmds; i++) { + rmsc[i].type = c->subcmds[i].type; + rmsc[i].suggest_time = c->subcmds[i].suggest_time; + rmsc[i].vlm_usage = c->subcmds[i].vlm_usage; + rmsc[i].vlm_ctx_id = c->subcmds[i].vlm_ctx_id; + rmsc[i].vlm_force = c->subcmds[i].vlm_force; + rmsc[i].boost = c->subcmds[i].boost; + rmsc[i].ip_time = c->subcmds[i].ip_time; + rmsc[i].driver_time = c->subcmds[i].driver_time; + rmsc[i].bw = c->subcmds[i].bw; + rmsc[i].turbo_boost = c->subcmds[i].turbo_boost; + rmsc[i].min_boost = c->subcmds[i].min_boost; + rmsc[i].max_boost = c->subcmds[i].max_boost; + rmsc[i].hse_en = c->subcmds[i].hse_en; + rmsc[i].pack_id = c->subcmds[i].pack_id; + rmsc[i].num_cmdbufs = c->subcmds[i].num_cmdbufs; + rmsc[i].cmdbuf_start_idx = acc_cb; + + for (j = 0; j < rmsc[i].num_cmdbufs; j++) { + rmcb[acc_cb + j].size = + c->ksubcmds[i].cmdbufs[j].size; + rmcb[acc_cb + j].device_va = + c->ksubcmds[i].daddrs[j]; + } + acc_cb += c->subcmds[i].num_cmdbufs; + } + + /* clear exec ret */ + c->einfos->c.ret = 0; + c->einfos->c.sc_rets = 0; + + apusys_mem_flush_kva(rc->cb->vaddr, rc->cb->size); + apusys_mem_flush_kva(c->exec_infos->vaddr, c->exec_infos->size); + + goto out; + +free_mem: + mdw_mem_free(rc->cb); +free_rc: + vfree(rc); + rc = NULL; +out: + return rc; +} + +int mdw_rv_cmd_delete(struct mdw_rv_cmd *rc) +{ + if (!rc) + return -EINVAL; + mdw_mem_unmap(rc->cb); + mdw_mem_free(rc->cb); + vfree(rc); + + return 0; +} + +void mdw_rv_cmd_done(struct mdw_rv_cmd *rc, int ret) +{ + struct mdw_cmd *c = rc->c; + + apusys_mem_invalidate_kva(rc->cb->vaddr, rc->cb->size); + apusys_mem_invalidate_kva(c->exec_infos->vaddr, c->exec_infos->size); + + mdw_rv_cmd_delete(rc); + c->complete(c, ret); +} diff --git a/drivers/soc/mediatek/apusys/mdw-rv-dev.c b/drivers/soc/mediatek/apusys/mdw-rv-dev.c new file mode 100644 index 000000000000..3c708bda7249 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-rv-dev.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include + +#include "mdw.h" +#include "mdw-rv.h" +#include "mdw-rv-msg.h" + +#define MDW_CMD_IPI_TIMEOUT (2 * 1000) /* ms */ + +static struct mdw_ipi_msg_sync *mdw_rv_dev_get_msg(struct mdw_rv_dev *mrdev, + u64 sync_id) +{ + struct mdw_ipi_msg_sync *s_msg = NULL; + struct list_head *tmp = NULL, *list_ptr = NULL; + + mutex_lock(&mrdev->msg_mtx); + list_for_each_safe(list_ptr, tmp, &mrdev->s_list) { + s_msg = list_entry(list_ptr, struct mdw_ipi_msg_sync, ud_item); + if (s_msg->msg.sync_id == sync_id) + break; + s_msg = NULL; + } + mutex_unlock(&mrdev->msg_mtx); + + return s_msg; +} + +static int mdw_rv_dev_send_msg(struct mdw_rv_dev *mrdev, + struct mdw_ipi_msg_sync *s_msg) +{ + int ret = 0; + u32 cnt = 50, i = 0; + + s_msg->msg.sync_id = (u64)s_msg; + + mutex_lock(&mrdev->msg_mtx); + list_add_tail(&s_msg->ud_item, &mrdev->s_list); + mutex_unlock(&mrdev->msg_mtx); + + for (i = 0; i < cnt; i++) { + ret = rpmsg_send(mrdev->ept, &s_msg->msg, sizeof(s_msg->msg)); + + /* send busy, retry */ + if (ret == -EBUSY) { + msleep(20); + continue; + } + + break; + } + + if (ret) { + dev_err(mrdev->mdev->dev, "send ipi msg(0x%llx) fail(%d)\n", + s_msg->msg.sync_id, ret); + mutex_lock(&mrdev->msg_mtx); + list_del(&s_msg->ud_item); + mutex_unlock(&mrdev->msg_mtx); + } + + return ret; +} + +static void mdw_rv_ipi_cmplt_sync(struct mdw_ipi_msg_sync *s_msg) +{ + complete(&s_msg->cmplt); +} + +static int mdw_rv_dev_send_sync(struct mdw_rv_dev *mrdev, + struct mdw_ipi_msg *msg) +{ + int ret = 0; + struct mdw_ipi_msg_sync *s_msg = NULL; + unsigned long timeout = msecs_to_jiffies(MDW_CMD_IPI_TIMEOUT); + + s_msg = vzalloc(sizeof(*s_msg)); + if (!s_msg) + return -ENOMEM; + + memcpy(&s_msg->msg, msg, sizeof(*msg)); + init_completion(&s_msg->cmplt); + s_msg->complete = mdw_rv_ipi_cmplt_sync; + + mutex_lock(&mrdev->mtx); + ret = mdw_rv_dev_send_msg(mrdev, s_msg); + if (ret) { + dev_err(mrdev->mdev->dev, "send msg fail\n"); + goto fail_send_sync; + } + mutex_unlock(&mrdev->mtx); + + if (!wait_for_completion_timeout(&s_msg->cmplt, timeout)) { + dev_err(mrdev->mdev->dev, "ipi no response\n"); + mutex_lock(&mrdev->msg_mtx); + list_del(&s_msg->ud_item); + mutex_unlock(&mrdev->msg_mtx); + ret = -ETIME; + } else { + memcpy(msg, &s_msg->msg, sizeof(*msg)); + ret = msg->ret; + if (ret) + dev_err(mrdev->mdev->dev, "up return fail(%d)\n", ret); + } + + goto out; + +fail_send_sync: + mutex_unlock(&mrdev->mtx); +out: + vfree(s_msg); + + return ret; +} + +static void mdw_rv_ipi_cmplt_cmd(struct mdw_ipi_msg_sync *s_msg) +{ + int ret = 0; + struct mdw_rv_cmd *rc = + container_of(s_msg, struct mdw_rv_cmd, s_msg); + + switch (s_msg->msg.ret) { + case MDW_IPI_MSG_STATUS_BUSY: + ret = -EBUSY; + break; + + case MDW_IPI_MSG_STATUS_ERR: + ret = -EREMOTEIO; + break; + + case MDW_IPI_MSG_STATUS_TIMEOUT: + ret = -ETIME; + break; + + default: + break; + } + + mdw_rv_cmd_done(rc, ret); +} + +static int mdw_rv_dev_send_cmd(struct mdw_rv_dev *mrdev, struct mdw_rv_cmd *rc) +{ + int ret = 0; + + rc->s_msg.msg.id = MDW_IPI_APU_CMD; + rc->s_msg.msg.c.iova = rc->cb->device_va; + rc->s_msg.msg.c.size = rc->cb->size; + rc->s_msg.msg.c.start_ts_ns = rc->start_ts_ns; + rc->s_msg.complete = mdw_rv_ipi_cmplt_cmd; + + ret = mdw_rv_dev_send_msg(mrdev, &rc->s_msg); + if (ret) + dev_err(mrdev->mdev->dev, "pid(%d) send msg fail\n", + current->pid); + + return ret; +} + +int mdw_rv_dev_run_cmd(struct mdw_fpriv *mpriv, struct mdw_cmd *c) +{ + struct mdw_rv_dev *mrdev = + (struct mdw_rv_dev *)mpriv->mdev->dev_specific; + struct mdw_rv_cmd *rc = NULL; + int ret = 0; + + rc = mdw_rv_cmd_create(mpriv, c); + if (!rc) { + ret = -EINVAL; + goto out; + } + + mutex_lock(&mrdev->mtx); + ret = mdw_rv_dev_send_cmd(mrdev, rc); + if (ret) + mdw_rv_cmd_delete(rc); + mutex_unlock(&mrdev->mtx); + +out: + return ret; +} + +static int mdw_rv_callback(struct rpmsg_device *rpdev, void *data, int len, + void *priv, u32 src) +{ + struct mdw_ipi_msg *msg = (struct mdw_ipi_msg *)data; + struct mdw_ipi_msg_sync *s_msg = NULL; + struct mdw_rv_dev *mrdev = (struct mdw_rv_dev *)priv; + + s_msg = mdw_rv_dev_get_msg(mrdev, msg->sync_id); + if (s_msg) { + memcpy(&s_msg->msg, msg, sizeof(*msg)); + mutex_lock(&mrdev->msg_mtx); + list_del(&s_msg->ud_item); + mutex_unlock(&mrdev->msg_mtx); + s_msg->complete(s_msg); + } + + return 0; +} + +int mdw_rv_dev_set_param(struct mdw_rv_dev *mrdev, enum mdw_info_type type, + u32 val) +{ + struct mdw_ipi_msg msg; + int ret = 0; + + memset(&msg, 0, sizeof(msg)); + msg.id = MDW_IPI_PARAM; + memcpy(&msg.p, &mrdev->param, sizeof(msg.p)); + switch (type) { + case MDW_INFO_ULOG: + msg.p.uplog = val; + break; + case MDW_INFO_PREEMPT_POLICY: + msg.p.preempt_policy = val; + break; + case MDW_INFO_SCHED_POLICY: + msg.p.sched_policy = val; + break; + default: + ret = -EINVAL; + goto out; + } + ret = mdw_rv_dev_send_sync(mrdev, &msg); + if (!ret) + memcpy(&mrdev->param, &msg.p, sizeof(msg.p)); +out: + return ret; +} + +u32 mdw_rv_dev_get_param(struct mdw_rv_dev *mrdev, enum mdw_info_type type) +{ + u32 ret = 0; + + switch (type) { + case MDW_INFO_ULOG: + ret = (int)mrdev->param.uplog; + break; + case MDW_INFO_PREEMPT_POLICY: + ret = (int)mrdev->param.preempt_policy; + break; + case MDW_INFO_SCHED_POLICY: + ret = (int)mrdev->param.sched_policy; + break; + case MDW_INFO_NORMAL_TASK_DLA: + case MDW_INFO_NORMAL_TASK_DSP: + case MDW_INFO_NORMAL_TASK_DMA: + dev_warn(mrdev->mdev->dev, "not support(%d)\n", type); + break; + default: + dev_warn(mrdev->mdev->dev, "unknown type(%d)\n", type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int mdw_rv_dev_handshake(struct mdw_rv_dev *mrdev) +{ + struct mdw_ipi_msg msg; + int ret = 0, type = 0; + + memset(&msg, 0, sizeof(msg)); + msg.id = MDW_IPI_HANDSHAKE; + msg.h.h_id = MDW_IPI_HANDSHAKE_BASIC_INFO; + ret = mdw_rv_dev_send_sync(mrdev, &msg); + if (ret) + goto out; + + if (msg.id != MDW_IPI_HANDSHAKE || + msg.h.h_id != MDW_IPI_HANDSHAKE_BASIC_INFO) { + ret = -EINVAL; + goto out; + } + + memcpy(mrdev->dev_mask, &msg.h.basic.dev_bmp, sizeof(mrdev->dev_mask)); + mrdev->rv_version = msg.h.basic.version; + + do { + type = find_next_bit(mrdev->dev_mask, APUSYS_DEVICE_MAX, type); + if (type >= APUSYS_DEVICE_MAX) + break; + + memset(&msg, 0, sizeof(msg)); + msg.id = MDW_IPI_HANDSHAKE; + msg.h.h_id = MDW_IPI_HANDSHAKE_DEV_NUM; + msg.h.dev.type = type; + ret = mdw_rv_dev_send_sync(mrdev, &msg); + if (ret) + break; + + if (msg.id != MDW_IPI_HANDSHAKE || + msg.h.h_id != MDW_IPI_HANDSHAKE_DEV_NUM) { + ret = -EINVAL; + break; + } + + mrdev->dev_num[msg.h.dev.type] = msg.h.dev.num; + memcpy(&mrdev->meta_data[msg.h.dev.type][0], + msg.h.dev.meta, sizeof(msg.h.dev.meta)); + type++; + } while (type < APUSYS_DEVICE_MAX); + +out: + return ret; +} + +static void mdw_rv_dev_init_func(struct work_struct *wk) +{ + struct mdw_rv_dev *mrdev = container_of(wk, struct mdw_rv_dev, init_wk); + struct mdw_device *mdev = mrdev->mdev; + int ret = 0; + + ret = mdw_rv_dev_handshake(mrdev); + if (ret) { + pr_err("handshake fail(%d)\n", ret); + return; + } + + memcpy(mdev->dev_mask, mrdev->dev_mask, sizeof(mrdev->dev_mask)); + mdev->inited = true; +} + +int mdw_rv_dev_init(struct mdw_device *mdev) +{ + struct rpmsg_channel_info chinfo = {}; + struct mdw_rv_dev *mrdev = NULL; + int ret = 0; + + if (!mdev->rpdev) { + dev_err(mdev->dev, "rpdev is NULL\n"); + ret = -EINVAL; + goto out; + } + + mrdev = kvzalloc(sizeof(*mrdev), GFP_KERNEL); + if (!mrdev) + return -ENOMEM; + + mdev->dev_specific = mrdev; + mrdev->mdev = mdev; + mrdev->rpdev = mdev->rpdev; + + strscpy(chinfo.name, mrdev->rpdev->id.name, RPMSG_NAME_SIZE); + chinfo.src = mrdev->rpdev->src; + chinfo.dst = RPMSG_ADDR_ANY; + mrdev->ept = rpmsg_create_ept(mrdev->rpdev, mdw_rv_callback, mrdev, + chinfo); + if (!mrdev->ept) { + dev_err(mdev->dev, "create ept fail\n"); + ret = -ENODEV; + goto free_mrdev; + } + + /* init up dev */ + mutex_init(&mrdev->msg_mtx); + mutex_init(&mrdev->mtx); + INIT_LIST_HEAD(&mrdev->s_list); + INIT_WORK(&mrdev->init_wk, &mdw_rv_dev_init_func); + + schedule_work(&mrdev->init_wk); + + goto out; + +free_mrdev: + kvfree(mrdev); + mdev->dev_specific = NULL; +out: + return ret; +} + +void mdw_rv_dev_deinit(struct mdw_device *mdev) +{ + struct mdw_rv_dev *mrdev = (struct mdw_rv_dev *)mdev->dev_specific; + + if (!mrdev) + return; + + rpmsg_destroy_ept(mrdev->ept); + kvfree(mrdev); + mdev->dev_specific = NULL; +} diff --git a/drivers/soc/mediatek/apusys/mdw-rv-msg.h b/drivers/soc/mediatek/apusys/mdw-rv-msg.h new file mode 100644 index 000000000000..234427ceec86 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-rv-msg.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef __MTK_APU_MDW_RV_MSG__ +#define __MTK_APU_MDW_RV_MSG__ + +/* mdw queue cmd type */ +enum { + MDW_IPI_NONE, + MDW_IPI_APU_CMD, + MDW_IPI_HANDSHAKE, + MDW_IPI_PARAM, + MDW_IPI_USER, + MDW_IPI_MAX = 0x20, +}; + +enum { + MDW_IPI_HANDSHAKE_BASIC_INFO, + MDW_IPI_HANDSHAKE_DEV_NUM, + MDW_IPI_HANDSHAKE_TASK_NUM, +}; + +enum { + MDW_IPI_MSG_STATUS_OK, + MDW_IPI_MSG_STATUS_BUSY, + MDW_IPI_MSG_STATUS_ERR, + MDW_IPI_MSG_STATUS_TIMEOUT, +}; + +struct mdw_ipi_ucmd { + u32 dev_type; + u32 dev_idx; + u64 iova; + u32 size; +}; + +struct mdw_ipi_apu_cmd { + u64 start_ts_ns; + u64 iova; + u32 size; +}; + +struct mdw_ipi_handshake { + u32 h_id; + union { + struct { + u64 magic; + u32 version; + u64 dev_bmp; + } basic; + struct { + u32 type; + u32 num; + u8 meta[MDW_DEV_META_SIZE]; + } dev; + struct { + u32 type; + u32 norm_task_num; + u32 deadline_task_num; + } task; + }; +}; + +struct mdw_ipi_param { + u32 uplog; + u32 preempt_policy; + u32 sched_policy; +}; + +struct mdw_ipi_msg { + u64 sync_id; + u32 id; //ipi id + s32 ret; + union { + struct mdw_ipi_apu_cmd c; + struct mdw_ipi_handshake h; + struct mdw_ipi_param p; + struct mdw_ipi_ucmd u; + }; +} __packed; + +struct mdw_ipi_msg_sync { + struct mdw_ipi_msg msg; + struct list_head ud_item; + struct completion cmplt; + void (*complete)(struct mdw_ipi_msg_sync *s_msg); +}; +#endif diff --git a/drivers/soc/mediatek/apusys/mdw-rv.c b/drivers/soc/mediatek/apusys/mdw-rv.c new file mode 100644 index 000000000000..3c825b89b3e9 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-rv.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include "mdw.h" +#include "mdw-rv.h" + +static int mdw_rv_sw_init(struct mdw_device *mdev) +{ + int ret = 0, i = 0; + struct mdw_rv_dev *rdev = (struct mdw_rv_dev *)mdev->dev_specific; + struct mdw_dinfo *d = NULL; + + /* update device info */ + for (i = 0; i < MDW_DEV_MAX; i++) { + if (!test_bit(i, rdev->dev_mask) || mdev->dinfos[i]) + continue; + + /* setup mdev's info */ + d = kvzalloc(sizeof(*d), GFP_KERNEL); + if (!d) + goto free_dinfo; + + d->num = rdev->dev_num[i]; + d->type = i; + + memcpy(d->meta, &rdev->meta_data[i][0], sizeof(d->meta)); + + mdev->dinfos[i] = d; + bitmap_set(mdev->dev_mask, i, 1); + } + + goto out; + +free_dinfo: + for (i = 0; i < MDW_DEV_MAX; i++) { + if (mdev->dinfos[i]) { + kvfree(mdev->dinfos[i]); + mdev->dinfos[i] = NULL; + } + } + ret = -ENOMEM; +out: + return ret; +} + +static void mdw_rv_sw_deinit(struct mdw_device *mdev) +{ + unsigned int i = 0; + + for (i = 0; i < MDW_DEV_MAX; i++) { + if (mdev->dinfos[i]) { + kvfree(mdev->dinfos[i]); + mdev->dinfos[i] = NULL; + } + } +} + +static int mdw_rv_late_init(struct mdw_device *mdev) +{ + int ret = 0; + + ret = mdw_rv_dev_init(mdev); + if (ret || !mdev->dev_specific) { + dev_err(mdev->dev, "init mdw rvdev fail(%d)\n", ret); + goto dev_deinit; + } + + goto out; + +dev_deinit: + mdw_rv_dev_deinit(mdev); +out: + return ret; +} + +static void mdw_rv_late_deinit(struct mdw_device *mdev) +{ + mdw_rv_dev_deinit(mdev); +} + +static int mdw_rv_run_cmd(struct mdw_fpriv *mpriv, struct mdw_cmd *c) +{ + return mdw_rv_dev_run_cmd(mpriv, c); +} + +static int mdw_rv_set_power(struct mdw_device *mdev, u32 type, u32 idx, + u32 boost) +{ + return -EINVAL; +} + +static int mdw_rv_ucmd(struct mdw_device *mdev, u32 type, void *vaddr, + u32 size) +{ + return -EINVAL; +} + +static int mdw_rv_set_param(struct mdw_device *mdev, enum mdw_info_type type, + u32 val) +{ + struct mdw_rv_dev *mrdev = (struct mdw_rv_dev *)mdev->dev_specific; + + return mdw_rv_dev_set_param(mrdev, type, val); +} + +static u32 mdw_rv_get_info(struct mdw_device *mdev, enum mdw_info_type type) +{ + struct mdw_rv_dev *mrdev = (struct mdw_rv_dev *)mdev->dev_specific; + + return mdw_rv_dev_get_param(mrdev, type); +} + +static const struct mdw_dev_func mdw_rv_func = { + .sw_init = mdw_rv_sw_init, + .sw_deinit = mdw_rv_sw_deinit, + .late_init = mdw_rv_late_init, + .late_deinit = mdw_rv_late_deinit, + .run_cmd = mdw_rv_run_cmd, + .set_power = mdw_rv_set_power, + .ucmd = mdw_rv_ucmd, + .set_param = mdw_rv_set_param, + .get_info = mdw_rv_get_info, +}; + +void mdw_rv_set_func(struct mdw_device *mdev) +{ + mdev->dev_funcs = &mdw_rv_func; + mdev->uapi_ver = MDW_UAPI_VERSION; +} diff --git a/drivers/soc/mediatek/apusys/mdw-rv.h b/drivers/soc/mediatek/apusys/mdw-rv.h new file mode 100644 index 000000000000..dbaffc228091 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-rv.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef __MTK_APU_MDW_RV_H__ +#define __MTK_APU_MDW_RV_H__ + +#include "mdw.h" +#include "mdw-rv-msg.h" + +struct mdw_rv_dev { + struct rpmsg_device *rpdev; + struct rpmsg_endpoint *ept; + struct mdw_device *mdev; + + struct mdw_ipi_param param; + + struct list_head s_list; /* for sync msg */ + struct mutex msg_mtx; + struct mutex mtx; /* protect send cmd */ + + struct work_struct init_wk; + + /* rv information */ + u32 rv_version; + unsigned long dev_mask[BITS_TO_LONGS(MDW_DEV_MAX)]; + u8 dev_num[MDW_DEV_MAX]; + u8 meta_data[MDW_DEV_MAX][MDW_DEV_META_SIZE]; +}; + +struct mdw_rv_cmd { + struct mdw_cmd *c; + struct mdw_mem *cb; + struct list_head u_item; /* to usr list */ + struct mdw_ipi_msg_sync s_msg; /* for ipi */ + u64 start_ts_ns; /* create time at ap */ +}; + +struct mdw_rv_msg_cmd { + /* ids */ + u64 session_id; + u64 cmd_id; + /* exec infos */ + u64 exec_infos; + u32 exec_size; + /* params */ + u32 priority; + u32 hardlimit; + u32 softlimit; + u32 power_save; + u32 power_plcy; + u32 power_dtime; + u32 app_type; + u32 num_subcmds; + u32 subcmds_offset; + u32 num_cmdbufs; + u32 cmdbuf_infos_offset; + u32 adj_matrix_offset; +} __packed; + +struct mdw_rv_msg_sc { + /* params */ + u32 type; + u32 suggest_time; + u32 vlm_usage; + u32 vlm_ctx_id; + u32 vlm_force; + u32 boost; + u32 turbo_boost; + u32 min_boost; + u32 max_boost; + u32 hse_en; + u32 driver_time; + u32 ip_time; + u32 bw; + u32 pack_id; + /* cmdbufs info */ + u32 cmdbuf_start_idx; + u32 num_cmdbufs; +} __packed; + +struct mdw_rv_msg_cb { + u64 device_va; + u32 size; +} __packed; + +int mdw_rv_dev_init(struct mdw_device *mdev); +void mdw_rv_dev_deinit(struct mdw_device *mdev); +int mdw_rv_dev_run_cmd(struct mdw_fpriv *mpriv, struct mdw_cmd *c); +int mdw_rv_dev_set_param(struct mdw_rv_dev *mrdev, u32 idx, u32 val); +u32 mdw_rv_dev_get_param(struct mdw_rv_dev *mrdev, u32 idx); + +struct mdw_rv_cmd *mdw_rv_cmd_create(struct mdw_fpriv *mpriv, + struct mdw_cmd *c); +int mdw_rv_cmd_delete(struct mdw_rv_cmd *rc); +void mdw_rv_cmd_done(struct mdw_rv_cmd *rc, int ret); +#endif diff --git a/drivers/soc/mediatek/apusys/mdw-sysfs.c b/drivers/soc/mediatek/apusys/mdw-sysfs.c new file mode 100644 index 000000000000..46538ab9c196 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw-sysfs.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include + +#include "mdw.h" + +static ssize_t dsp_task_num_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdw_device *mdev = dev_get_drvdata(dev); + int ret = 0; + u32 num = 0; + + num = mdev->dev_funcs->get_info(mdev, MDW_INFO_NORMAL_TASK_DSP); + ret = sprintf(buf, "%u\n", num); + + return ret; +} +static DEVICE_ATTR_RO(dsp_task_num); + +static ssize_t dla_task_num_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdw_device *mdev = dev_get_drvdata(dev); + int ret = 0; + u32 num = 0; + + num = mdev->dev_funcs->get_info(mdev, MDW_INFO_NORMAL_TASK_DLA); + ret = sprintf(buf, "%u\n", num); + + return ret; +} +static DEVICE_ATTR_RO(dla_task_num); + +static ssize_t dma_task_num_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mdw_device *mdev = dev_get_drvdata(dev); + int ret = 0; + u32 num = 0; + + num = mdev->dev_funcs->get_info(mdev, MDW_INFO_NORMAL_TASK_DMA); + ret = sprintf(buf, "%u\n", num); + + return ret; +} +static DEVICE_ATTR_RO(dma_task_num); + +static struct attribute *mdw_task_attrs[] = { + &dev_attr_dsp_task_num.attr, + &dev_attr_dla_task_num.attr, + &dev_attr_dma_task_num.attr, + NULL, +}; + +static struct attribute_group mdw_devinfo_attr_group = { + .name = "queue", + .attrs = mdw_task_attrs, +}; + +static ssize_t policy_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return -EINVAL; +} + +static ssize_t policy_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return -EINVAL; +} +static DEVICE_ATTR_RW(policy); + +static struct attribute *mdw_sched_attrs[] = { + &dev_attr_policy.attr, + NULL, +}; + +static struct attribute_group mdw_sched_attr_group = { + .name = "sched", + .attrs = mdw_sched_attrs, +}; + +static ssize_t mem_statistics_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int n = 0; + + return n; +} +static DEVICE_ATTR_RO(mem_statistics); + +static struct attribute *mdw_mem_attrs[] = { + &dev_attr_mem_statistics.attr, + NULL, +}; + +static struct attribute_group mdw_mem_attr_group = { + .name = "memory", + .attrs = mdw_mem_attrs, +}; + +static ssize_t ulog_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mdw_device *mdev = dev_get_drvdata(dev); + int ret = 0; + u32 log_lv = 0; + + log_lv = mdev->dev_funcs->get_info(mdev, MDW_INFO_ULOG); + ret = sprintf(buf, "%u\n", log_lv); + + return ret; +} + +static ssize_t ulog_store(struct device *dev, struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct mdw_device *mdev = dev_get_drvdata(dev); + u32 val = 0; + + if (!kstrtouint(buf, 10, &val)) + mdev->dev_funcs->set_param(mdev, MDW_INFO_ULOG, val); + + return count; +} +static DEVICE_ATTR_RW(ulog); + +static ssize_t klog_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mdw_device *mdev = dev_get_drvdata(dev); + int ret = 0; + u32 log_lv = 0; + + log_lv = mdev->dev_funcs->get_info(mdev, MDW_INFO_KLOG); + ret = sprintf(buf, "%u\n", log_lv); + + return ret; +} + +static ssize_t klog_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mdw_device *mdev = dev_get_drvdata(dev); + u32 val = 0; + + if (!kstrtouint(buf, 10, &val)) + mdev->dev_funcs->set_param(mdev, MDW_INFO_KLOG, val); + + return count; +} +static DEVICE_ATTR_RW(klog); + +static struct attribute *mdw_log_attrs[] = { + &dev_attr_ulog.attr, + &dev_attr_klog.attr, + NULL, +}; + +static struct attribute_group mdw_log_attr_group = { + .name = "log", + .attrs = mdw_log_attrs, +}; + +int mdw_sysfs_init(struct mdw_device *mdev) +{ + int ret = 0; + + dev_set_drvdata(mdev->misc_dev.this_device, mdev); + + ret = sysfs_create_group(&mdev->misc_dev.this_device->kobj, + &mdw_devinfo_attr_group); + ret |= sysfs_create_group(&mdev->misc_dev.this_device->kobj, + &mdw_sched_attr_group); + ret |= sysfs_create_group(&mdev->misc_dev.this_device->kobj, + &mdw_log_attr_group); + ret |= sysfs_create_group(&mdev->misc_dev.this_device->kobj, + &mdw_mem_attr_group); + + return ret; +} + +void mdw_sysfs_deinit(struct mdw_device *mdev) +{ + struct device *dev = mdev->misc_dev.this_device; + + sysfs_remove_group(&dev->kobj, &mdw_mem_attr_group); + sysfs_remove_group(&dev->kobj, &mdw_log_attr_group); + sysfs_remove_group(&dev->kobj, &mdw_sched_attr_group); + sysfs_remove_group(&dev->kobj, &mdw_devinfo_attr_group); +} diff --git a/drivers/soc/mediatek/apusys/mdw.h b/drivers/soc/mediatek/apusys/mdw.h new file mode 100644 index 000000000000..9b4a8a308f17 --- /dev/null +++ b/drivers/soc/mediatek/apusys/mdw.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#ifndef __MTK_APU_MDW_H__ +#define __MTK_APU_MDW_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apu-device.h" +#include "mdw-ioctl.h" + +#define MDW_NAME "apusys" +#define MDW_DEV_MAX (APUSYS_DEVICE_MAX) +#define MDW_SUBCMD_MAX (63) +#define MDW_PRIORITY_MAX (32) +#define MDW_BOOST_MAX (100) +#define MDW_DEFAULT_ALIGN (16) +#define MDW_UAPI_VERSION (2) + +#define MDW_ALIGN(x, align) (((x) + (align) - 1) & (~((align) - 1))) + +enum mdw_info_type { + MDW_INFO_KLOG, + MDW_INFO_ULOG, + MDW_INFO_PREEMPT_POLICY, + MDW_INFO_SCHED_POLICY, + + MDW_INFO_NORMAL_TASK_DLA, + MDW_INFO_NORMAL_TASK_DSP, + MDW_INFO_NORMAL_TASK_DMA, + + MDW_INFO_MAX, +}; + +struct mdw_fpriv; +struct mdw_device; + +enum mdw_mem_type { + MDW_MEM_TYPE_NONE, + MDW_MEM_TYPE_INTERNAL, + MDW_MEM_TYPE_ALLOC, + MDW_MEM_TYPE_IMPORT, +}; + +struct mdw_mem { + /* in */ + unsigned int size; + unsigned int align; + u64 flags; + struct mdw_fpriv *mpriv; + bool need_handle; + + /* out */ + int handle; + void *vaddr; + u64 device_va; + u32 dva_size; + void *priv; + + /* control */ + enum mdw_mem_type type; + struct list_head u_item; + struct list_head m_item; + struct kref map_ref; + void (*release)(struct mdw_mem *m); +}; + +struct mdw_dinfo { + u32 type; + u32 num; + u8 meta[MDW_DEV_META_SIZE]; +}; + +struct mdw_device { + struct rpmsg_device *rpdev; + struct device *dev; + struct miscdevice misc_dev; + struct device *dma_dev; + + bool inited; + atomic_t sw_inited; + + u64 vlm_start; + u32 vlm_size; + + u32 uapi_ver; + + unsigned long dev_mask[BITS_TO_LONGS(MDW_DEV_MAX)]; + struct mdw_dinfo *dinfos[MDW_DEV_MAX]; + + const struct mdw_dev_func *dev_funcs; + void *dev_specific; +}; + +struct mdw_fpriv { + struct mdw_device *mdev; + + struct list_head mems; + struct list_head cmds; + struct mutex mtx; /* protect mems */ + + /* ref count for cmd/mem */ + struct kref ref; + void (*get)(struct mdw_fpriv *mpriv); + void (*put)(struct mdw_fpriv *mpriv); +}; + +struct mdw_exec_info { + struct mdw_cmd_exec_info c; + struct mdw_subcmd_exec_info sc; +}; + +struct mdw_subcmd_kinfo { + struct mdw_subcmd_info *info; /* c->subcmds */ + struct mdw_subcmd_cmdbuf *cmdbufs; /* from usr */ + struct mdw_mem **ori_cbs; /* pointer to original cmdbuf */ + struct mdw_subcmd_exec_info *sc_einfo; + u64 *kvaddrs; /* pointer to duplicated buf */ + u64 *daddrs; /* pointer to duplicated buf */ + void *priv; /* mdw_ap_sc */ +}; + +struct mdw_fence { + struct dma_fence base_fence; + struct mdw_device *mdev; + spinlock_t lock; /* used by dma_fence_init */ +}; + +struct mdw_cmd { + pid_t pid; + pid_t tgid; + u64 kid; + u64 uid; + u64 usr_id; + u32 priority; + u32 hardlimit; + u32 softlimit; + u32 power_save; + u32 power_plcy; + u32 power_dtime; + u32 app_type; + u32 num_subcmds; + struct mdw_subcmd_info *subcmds; /* from usr */ + struct mdw_subcmd_kinfo *ksubcmds; + u32 num_cmdbufs; + u32 size_cmdbufs; + struct mdw_mem *cmdbufs; + struct mdw_mem *exec_infos; + struct mdw_exec_info *einfos; + u8 *adj_matrix; + + struct list_head u_item; + + struct timespec64 start_ts; + struct timespec64 end_ts; + + struct mdw_fpriv *mpriv; + int (*complete)(struct mdw_cmd *c, int ret); + + struct mdw_fence *fence; + struct work_struct t_wk; + struct dma_fence *wait_fence; +}; + +struct mdw_dev_func { + int (*late_init)(struct mdw_device *mdev); + void (*late_deinit)(struct mdw_device *mdev); + int (*sw_init)(struct mdw_device *mdev); + void (*sw_deinit)(struct mdw_device *mdev); + + int (*run_cmd)(struct mdw_fpriv *mpriv, struct mdw_cmd *c); + int (*set_power)(struct mdw_device *mdev, u32 type, u32 idx, u32 boost); + int (*ucmd)(struct mdw_device *mdev, u32 type, void *vaddr, u32 size); + int (*set_param)(struct mdw_device *mdev, enum mdw_info_type type, + u32 val); + u32 (*get_info)(struct mdw_device *mdev, enum mdw_info_type type); +}; + +void mdw_rv_set_func(struct mdw_device *mdev); + +long mdw_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +int mdw_cmd_ioctl(struct mdw_fpriv *mpriv, void *data); + +void mdw_mem_mpriv_release(struct mdw_fpriv *mpriv); + +int mdw_mem_flush(struct mdw_mem *m); +int mdw_mem_invalidate(struct mdw_mem *m); + +#ifdef CONFIG_DEBUG_FS +int mdw_sysfs_init(struct mdw_device *mdev); +void mdw_sysfs_deinit(struct mdw_device *mdev); +#endif + +int mdw_dev_init(struct mdw_device *mdev); +void mdw_dev_deinit(struct mdw_device *mdev); +#endif From patchwork Fri Dec 10 17:26:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670307 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BEE93C4332F for ; Fri, 10 Dec 2021 17:27:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244732AbhLJRbE (ORCPT ); Fri, 10 Dec 2021 12:31:04 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:43206 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244512AbhLJRa1 (ORCPT ); Fri, 10 Dec 2021 12:30:27 -0500 X-UUID: d184caa5dd5b4c098deace206e2f0057-20211211 X-UUID: d184caa5dd5b4c098deace206e2f0057-20211211 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 1501156713; Sat, 11 Dec 2021 01:26:51 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs07n2.mediatek.inc (172.21.101.141) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:49 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:49 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 12/17] arm64: dts: mt8192: Add APU mtk-apu-mailbox node Date: Sat, 11 Dec 2021 01:26:00 +0800 Message-ID: <20211210172605.30618-13-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add mtk-apu-mailbox for mt8192 SOC. Signed-off-by: Flora Fu --- arch/arm64/boot/dts/mediatek/mt8192.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi index cb2b171e0080..5c97dc7985b4 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi @@ -906,6 +906,13 @@ #clock-cells = <1>; }; + apu_mailbox: apu_mailbox@19000000 { + compatible = "mediatek,mtk-apu-mailbox"; + reg = <0 0x19000000 0 0x100>; + interrupts = ; + #mbox-cells = <1>; + }; + apu_conn: apu_conn@19020000 { compatible = "mediatek,mt8192-apu-conn", "syscon"; reg = <0 0x19020000 0 0x1000>; From patchwork Fri Dec 10 17:26:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670303 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E6C2EC433FE for ; Fri, 10 Dec 2021 17:27:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244601AbhLJRa7 (ORCPT ); Fri, 10 Dec 2021 12:30:59 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:43360 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244522AbhLJRab (ORCPT ); Fri, 10 Dec 2021 12:30:31 -0500 X-UUID: f0bb3757669543f290d83e22d08f47c4-20211211 X-UUID: f0bb3757669543f290d83e22d08f47c4-20211211 Received: from mtkcas11.mediatek.inc [(172.21.101.40)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 900033792; Sat, 11 Dec 2021 01:26:52 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs10n2.mediatek.inc (172.21.101.183) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.3; Sat, 11 Dec 2021 01:26:50 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:50 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 13/17] arm64: dts: mt8192: Add APU-IOMMU nodes Date: Sat, 11 Dec 2021 01:26:01 +0800 Message-ID: <20211210172605.30618-14-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add APU-IOMMI nodes Signed-off-by: Yong Wu Signed-off-by: Flora Fu --- arch/arm64/boot/dts/mediatek/mt8192.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi index 5c97dc7985b4..62acaba7b033 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -913,6 +914,14 @@ #mbox-cells = <1>; }; + iommu_apu: iommu@19010000 { + compatible = "mediatek,mt8192-iommu-apu"; + reg = <0 0x19010000 0 0x1000>; + interrupts = ; + power-domains = <&apuspm 0>; + #iommu-cells = <1>; + }; + apu_conn: apu_conn@19020000 { compatible = "mediatek,mt8192-apu-conn", "syscon"; reg = <0 0x19020000 0 0x1000>; From patchwork Fri Dec 10 17:26:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670299 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DD3C1C433FE for ; Fri, 10 Dec 2021 17:27:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244616AbhLJRak (ORCPT ); Fri, 10 Dec 2021 12:30:40 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:43080 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244488AbhLJRac (ORCPT ); Fri, 10 Dec 2021 12:30:32 -0500 X-UUID: 0707d7ab912a42bb8c1c74b13a1f4f55-20211211 X-UUID: 0707d7ab912a42bb8c1c74b13a1f4f55-20211211 Received: from mtkcas11.mediatek.inc [(172.21.101.40)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 1353861907; Sat, 11 Dec 2021 01:26:52 +0800 Received: from mtkexhb01.mediatek.inc (172.21.101.102) by mtkmbs10n2.mediatek.inc (172.21.101.183) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.2.792.3; Sat, 11 Dec 2021 01:26:51 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkexhb01.mediatek.inc (172.21.101.102) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:51 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:50 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 14/17] arm64: dts: mt8192: Add apu tinysys node Date: Sat, 11 Dec 2021 01:26:02 +0800 Message-ID: <20211210172605.30618-15-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add node for APU tinysys. Signed-off-by: Flora Fu --- arch/arm64/boot/dts/mediatek/mt8192.dtsi | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi index 62acaba7b033..de73fbf0cb90 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi @@ -914,6 +914,41 @@ #mbox-cells = <1>; }; + apusys_rv@19001000 { + compatible = "mediatek,mt8192-apusys-rv", "simple-mfd"; + reg = <0 0x19000000 0 0x1000>, + <0 0x19001000 0 0x1000>, + <0 0x19002000 0 0x10>; + reg-names = "apu_mbox", + "md32_sysctrl", + "apu_wdt"; + power-domains = <&apuspm 0>; + iommus = <&iommu_apu IOMMU_PORT_APU_DATA>; + interrupts = ; + interrupt-names = "apu_wdt"; + mboxes = <&apu_mailbox 0>; + + apu_ctrl { + compatible = "mediatek,apu-ctrl-rpmsg"; + mtk,rpmsg-name = "apu-ctrl-rpmsg"; + }; + + apu_pwr_tx { + compatible = "mediatek,apupwr-tx-rpmsg"; + mtk,rpmsg-name = "apupwr-tx-rpmsg"; + }; + + apu_pwr_rx { + compatible = "mediatek,apupwr-rx-rpmsg"; + mtk,rpmsg-name = "apupwr-rx-rpmsg"; + }; + + apu_mdw_rpmsg { + compatible = "mediatek,apu-mdw-rpmsg"; + mtk,rpmsg-name = "apu-mdw-rpmsg"; + }; + }; + iommu_apu: iommu@19010000 { compatible = "mediatek,mt8192-iommu-apu"; reg = <0 0x19010000 0 0x1000>; From patchwork Fri Dec 10 17:26:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670295 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 80519C433EF for ; Fri, 10 Dec 2021 17:27:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244586AbhLJRaj (ORCPT ); Fri, 10 Dec 2021 12:30:39 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:49150 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244538AbhLJRac (ORCPT ); Fri, 10 Dec 2021 12:30:32 -0500 X-UUID: 344d5e2cb4cb422b9269a40dec9f715b-20211211 X-UUID: 344d5e2cb4cb422b9269a40dec9f715b-20211211 Received: from mtkexhb01.mediatek.inc [(172.21.101.102)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 1185051759; Sat, 11 Dec 2021 01:26:53 +0800 Received: from mtkexhb02.mediatek.inc (172.21.101.103) by mtkmbs07n2.mediatek.inc (172.21.101.141) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:52 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkexhb02.mediatek.inc (172.21.101.103) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:52 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:51 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 15/17] arm64: dts: mt8192: Add APU power nodes Date: Sat, 11 Dec 2021 01:26:03 +0800 Message-ID: <20211210172605.30618-16-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add APU power node for MT8192. Signed-off-by: Flora Fu --- arch/arm64/boot/dts/mediatek/mt8192.dtsi | 61 ++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi index de73fbf0cb90..f95d381ff1cc 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi @@ -996,6 +996,67 @@ }; }; + apusys_power: apusys_power@190f1000 { + compatible = "mediatek,mt8192-apu-power"; + reg = <0 0x190f1000 0 0x1000>, + <0 0x19000600 0 0x100>; + reg-names = "apu_pcu", + "apu_spare"; + power-domains = <&apuspm 0>; + clocks = <&topckgen CLK_TOP_DSP_SEL>, + <&topckgen CLK_TOP_DSP1_SEL>, + <&topckgen CLK_TOP_DSP1_NPUPLL_SEL>, + <&topckgen CLK_TOP_DSP2_SEL>, + <&topckgen CLK_TOP_DSP2_NPUPLL_SEL>, + <&topckgen CLK_TOP_DSP5_SEL>, + <&topckgen CLK_TOP_DSP5_APUPLL_SEL>, + <&topckgen CLK_TOP_IPU_IF_SEL>, + <&clk26m>, + <&topckgen CLK_TOP_MAINPLL_D4_D2>, + <&topckgen CLK_TOP_UNIVPLL_D4_D2>, + <&topckgen CLK_TOP_UNIVPLL_D6_D2>, + <&topckgen CLK_TOP_MMPLL_D6>, + <&topckgen CLK_TOP_MMPLL_D5>, + <&topckgen CLK_TOP_MMPLL_D4>, + <&topckgen CLK_TOP_UNIVPLL_D5>, + <&topckgen CLK_TOP_UNIVPLL_D4>, + <&topckgen CLK_TOP_UNIVPLL_D3>, + <&topckgen CLK_TOP_MAINPLL_D6>, + <&topckgen CLK_TOP_MAINPLL_D4>, + <&topckgen CLK_TOP_MAINPLL_D3>, + <&topckgen CLK_TOP_TVDPLL>, + <&topckgen CLK_TOP_APUPLL>, + <&topckgen CLK_TOP_NPUPLL>, + <&apmixedsys CLK_APMIXED_APUPLL>, + <&apmixedsys CLK_APMIXED_NPUPLL>; + clock-names = "clk_top_dsp_sel", + "clk_top_dsp1_sel", + "clk_top_dsp1_npupll_sel", + "clk_top_dsp2_sel", + "clk_top_dsp2_npupll_sel", + "clk_top_dsp5_sel", + "clk_top_dsp5_apupll_sel", + "clk_top_ipu_if_sel", + "clk_top_clk26m", + "clk_top_mainpll_d4_d2", + "clk_top_univpll_d4_d2", + "clk_top_univpll_d6_d2", + "clk_top_mmpll_d6", + "clk_top_mmpll_d5", + "clk_top_mmpll_d4", + "clk_top_univpll_d5", + "clk_top_univpll_d4", + "clk_top_univpll_d3", + "clk_top_mainpll_d6", + "clk_top_mainpll_d4", + "clk_top_mainpll_d3", + "clk_top_tvdpll_ck", + "clk_top_apupll_ck", + "clk_top_npupll_ck", + "clk_apmixed_apupll_rate", + "clk_apmixed_npupll_rate"; + }; + camsys: clock-controller@1a000000 { compatible = "mediatek,mt8192-camsys"; reg = <0 0x1a000000 0 0x1000>; From patchwork Fri Dec 10 17:26:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670291 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C145CC43219 for ; Fri, 10 Dec 2021 17:27:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244570AbhLJRag (ORCPT ); Fri, 10 Dec 2021 12:30:36 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:49128 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244478AbhLJRab (ORCPT ); Fri, 10 Dec 2021 12:30:31 -0500 X-UUID: 417f696fcbfa4e52ab9fbc41278941d0-20211211 X-UUID: 417f696fcbfa4e52ab9fbc41278941d0-20211211 Received: from mtkexhb01.mediatek.inc [(172.21.101.102)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 1392086464; Sat, 11 Dec 2021 01:26:54 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs07n1.mediatek.inc (172.21.101.16) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:52 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:52 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 16/17] arm64: dts: mt8192: Add apu-sw-logger node Date: Sat, 11 Dec 2021 01:26:04 +0800 Message-ID: <20211210172605.30618-17-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add apu-sw-logger node to enable debug into tinysys. Signed-off-by: Flora Fu --- arch/arm64/boot/dts/mediatek/mt8192.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi index f95d381ff1cc..c8cc58e74f3c 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi @@ -914,6 +914,12 @@ #mbox-cells = <1>; }; + apusys_sw_logger@19000040 { + compatible = "mediatek,apu-sw-logger"; + reg = <0 0x19000040 0 0x10>; + iommus = <&iommu_apu IOMMU_PORT_APU_DATA>; + }; + apusys_rv@19001000 { compatible = "mediatek,mt8192-apusys-rv", "simple-mfd"; reg = <0 0x19000000 0 0x1000>, From patchwork Fri Dec 10 17:26:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Flora Fu X-Patchwork-Id: 12670293 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 767E3C433EF for ; Fri, 10 Dec 2021 17:27:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244580AbhLJRai (ORCPT ); Fri, 10 Dec 2021 12:30:38 -0500 Received: from mailgw01.mediatek.com ([60.244.123.138]:43206 "EHLO mailgw01.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S244460AbhLJRad (ORCPT ); Fri, 10 Dec 2021 12:30:33 -0500 X-UUID: 8a66b5db05b54330990f2211af852f73-20211211 X-UUID: 8a66b5db05b54330990f2211af852f73-20211211 Received: from mtkexhb02.mediatek.inc [(172.21.101.103)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 1049938422; Sat, 11 Dec 2021 01:26:55 +0800 Received: from mtkcas10.mediatek.inc (172.21.101.39) by mtkmbs07n2.mediatek.inc (172.21.101.141) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Sat, 11 Dec 2021 01:26:53 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas10.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1497.2 via Frontend Transport; Sat, 11 Dec 2021 01:26:52 +0800 From: Flora Fu To: Matthias Brugger , Liam Girdwood , Mark Brown , Sumit Semwal , Yong Wu , Pi-Cheng Chen CC: , , , , , , Flora Fu , JB Tsai Subject: [PATCH 17/17] arm64: dts: mt8192: Set up regulators for APU subsys Date: Sat, 11 Dec 2021 01:26:05 +0800 Message-ID: <20211210172605.30618-18-flora.fu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20211210172605.30618-1-flora.fu@mediatek.com> References: <20211210172605.30618-1-flora.fu@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Set up APU regulators for mdla and vvpu. Signed-off-by: Flora Fu --- arch/arm64/boot/dts/mediatek/mt8192-evb.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8192-evb.dts b/arch/arm64/boot/dts/mediatek/mt8192-evb.dts index 5d9e108e41f5..431008466d77 100644 --- a/arch/arm64/boot/dts/mediatek/mt8192-evb.dts +++ b/arch/arm64/boot/dts/mediatek/mt8192-evb.dts @@ -35,3 +35,8 @@ domain-supply = <&mt6359_vproc1_buck_reg>; }; }; + +&apusys_power { + vvpu-supply = <&mt6359_vproc1_buck_reg>; + vmdla-supply = <&mt6359_vproc2_buck_reg>; +};