LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [RFC 00/13] MediaTek MT8192 APU
@ 2021-10-23 11:13 Flora Fu
  2021-10-23 11:13 ` [RFC 01/13] dt-bindings: soc: mediatek: apusys: add mt8192 apu iommu bindings Flora Fu
                   ` (12 more replies)
  0 siblings, 13 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:13 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add Support for MediaTek MT8192 APU.

The MediaTek AI Processing Unit (APU) is a proprietary hardware
in the SoC to support AI functions.
The patches support the MT8192 APU running on internal microprocess.
Software packages contins power control, tinysys controller and middleware.

This series is based on drivers implemented in
MT8192 apu power domain[1] and IOMMU[2] patches.
[1] https://patchwork.kernel.org/project/linux-mediatek/list/?series=568927
[2] https://patchwork.kernel.org/project/linux-mediatek/list/?series=551641
The device tree depends on [3][4][5] which haven't yet been accepted.
[3] https://patchwork.kernel.org/project/linux-mediatek/list/?series=521655
[4] https://patchwork.kernel.org/patch/12134935
[5] https://patchwork.kernel.org/patch/12140237


Flora Fu (12):
  dt-bindings: soc: mediatek: apusys: add mt8192 apu iommu bindings
  dt-bindings: soc: mediatek: apusys: Add new document for APU power
  dt-bindings: soc: mediatek: apusys: Add new document for APU tinysys
  iommu/mediatek: Add APU iommu support
  soc: mediatek: Add command for APU SMC call
  soc: mediatek: apu: Add apu core driver
  soc: mediatek: apu: Add apu power driver
  soc: mediatek: apu: Add apusys rv driver
  soc: mediatek: apu: Add middleware driver
  arm64: dts: mt8192: Add apu power nodes
  arm64: dts: mt8192: Add apu tinysys
  arm64: dts: mt8192: Add regulator for APU

Yong Wu (1):
  arm64: dts: mt8192: Add APU-IOMMU nodes

 .../bindings/iommu/mediatek,iommu.yaml        |   2 +
 .../soc/mediatek/mediatek,apu-pwr.yaml        |  88 ++
 .../soc/mediatek/mediatek,apu-rv.yaml         | 140 +++
 arch/arm64/boot/dts/mediatek/mt8192-evb.dts   |   5 +
 arch/arm64/boot/dts/mediatek/mt8192.dtsi      | 107 ++
 drivers/iommu/mtk_iommu.c                     |  57 ++
 drivers/soc/mediatek/Kconfig                  |  18 +
 drivers/soc/mediatek/apusys/Makefile          |  22 +
 drivers/soc/mediatek/apusys/apu-config.h      | 100 ++
 drivers/soc/mediatek/apusys/apu-core.c        |  97 ++
 drivers/soc/mediatek/apusys/apu-core.h        |  18 +
 drivers/soc/mediatek/apusys/apu-device.h      |  39 +
 drivers/soc/mediatek/apusys/apu-ipi.c         | 486 +++++++++
 drivers/soc/mediatek/apusys/apu-mbox.c        |  83 ++
 drivers/soc/mediatek/apusys/apu-mbox.h        |  27 +
 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         | 599 +++++++++++
 drivers/soc/mediatek/apusys/apu-pwr.h         | 260 +++++
 drivers/soc/mediatek/apusys/apu-rproc.c       | 806 +++++++++++++++
 drivers/soc/mediatek/apusys/apu-sw-logger.c   | 429 ++++++++
 drivers/soc/mediatek/apusys/apu.h             | 256 +++++
 drivers/soc/mediatek/apusys/mdw-cmd.c         | 618 ++++++++++++
 drivers/soc/mediatek/apusys/mdw-drv.c         | 211 ++++
 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             | 208 ++++
 drivers/soc/mediatek/apusys/mt81xx-plat.c     | 320 ++++++
 include/dt-bindings/memory/mt8192-larb-port.h |   4 +
 include/linux/soc/mediatek/mtk_sip_svc.h      |   2 +
 38 files changed, 8157 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pwr.yaml
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-rv.yaml
 create mode 100644 drivers/soc/mediatek/apusys/apu-config.h
 create mode 100644 drivers/soc/mediatek/apusys/apu-core.c
 create mode 100644 drivers/soc/mediatek/apusys/apu-core.h
 create mode 100644 drivers/soc/mediatek/apusys/apu-device.h
 create mode 100644 drivers/soc/mediatek/apusys/apu-ipi.c
 create mode 100644 drivers/soc/mediatek/apusys/apu-mbox.c
 create mode 100644 drivers/soc/mediatek/apusys/apu-mbox.h
 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
 create mode 100644 drivers/soc/mediatek/apusys/apu-rproc.c
 create mode 100644 drivers/soc/mediatek/apusys/apu-sw-logger.c
 create mode 100644 drivers/soc/mediatek/apusys/apu.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
 create mode 100644 drivers/soc/mediatek/apusys/mt81xx-plat.c

-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 01/13] dt-bindings: soc: mediatek: apusys: add mt8192 apu iommu bindings
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
@ 2021-10-23 11:13 ` Flora Fu
  2021-10-23 11:13 ` [RFC 02/13] dt-bindings: soc: mediatek: apusys: Add new document for APU power Flora Fu
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:13 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add mt8192 apu iommu bindings.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
index c528a299afa9..338d245fc4af 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
 
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 02/13] dt-bindings: soc: mediatek: apusys: Add new document for APU power
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
  2021-10-23 11:13 ` [RFC 01/13] dt-bindings: soc: mediatek: apusys: add mt8192 apu iommu bindings Flora Fu
@ 2021-10-23 11:13 ` Flora Fu
  2021-10-23 11:13 ` [RFC 03/13] dt-bindings: soc: mediatek: apusys: Add new document for APU tinysys Flora Fu
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:13 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add new document for APU power controller.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 .../soc/mediatek/mediatek,apu-pwr.yaml        | 88 +++++++++++++++++++
 1 file changed, 88 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..0fd5af5138e3
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pwr.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# # Copyright 2021 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
+
+description: |
+  Mediatek AI Process Unit (APU) power driver support for subsys clock and
+  regulator controller. It will has device link to iommu-apu and apusys-rv
+  tinysys driver to ensure the power state is ready for hardware
+  in sub modules.
+
+maintainers:
+  - Flora Fu <flora.fu@mediatek.com>
+
+properties:
+  compatible:
+    oneOf:
+      - const: mediatek,apusys-power
+      - items:
+          - const: mediatek,apusys-power
+          - const: 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
+  - clocks
+  - clock-names
+  - vvpu-supply
+  - vmdla-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/mt8192-clk.h>
+    apusys_power: apusys_power@190f1000 {
+      compatible = "mediatek,apusys-power",
+                   "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";
+    };
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 03/13] dt-bindings: soc: mediatek: apusys: Add new document for APU tinysys
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
  2021-10-23 11:13 ` [RFC 01/13] dt-bindings: soc: mediatek: apusys: add mt8192 apu iommu bindings Flora Fu
  2021-10-23 11:13 ` [RFC 02/13] dt-bindings: soc: mediatek: apusys: Add new document for APU power Flora Fu
@ 2021-10-23 11:13 ` Flora Fu
  2021-10-23 11:14 ` [RFC 04/13] iommu/mediatek: Add APU iommu support Flora Fu
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:13 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add new document for APU tinysys.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 .../soc/mediatek/mediatek,apu-rv.yaml         | 140 ++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-rv.yaml

diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-rv.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-rv.yaml
new file mode 100644
index 000000000000..ee0ff5d656e9
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-rv.yaml
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# # Copyright 2021 MediaTek Inc.
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/soc/mediatek/mediatek,apu-rv.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Mediatek APU Power
+
+description: |
+  APU integrated subsystem having MD32RV33 (MD32) that runs tinysys
+  The tinsys is running on a micro processor in APU.
+  Its firmware is load and boot from Kernel side. Kernel and tinysys use
+  IPI to tx/rx messages.
+
+maintainers:
+  - Flora Fu <flora.fu@mediatek.com>
+
+properties:
+  compatible:
+    enum:
+      - mediatek,mt8192-apusys-rv
+
+  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.
+
+  mediatek,apusys-power:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the apusys-power handle.
+
+  apu_ctrl:
+    description: handle the ipi state, time sync and deep idle message.
+    type: object
+
+    properties:
+      compatible:
+        const: "mediatek,apu-ctrl-rpmsg"
+
+    required:
+      - compatible
+
+    additionalProperties: false
+
+  apu_pwr_tx:
+    description: handle the message trigger from AP side to tinysys.
+    type: object
+
+    properties:
+      compatible:
+        const: "mediatek,apupwr-tx-rpmsg"
+
+    required:
+      - compatible
+
+    additionalProperties: false
+
+  apu_pwr_rx:
+    description: handle the message trigger from tinysys to AP side.
+    type: object
+
+    properties:
+      compatible:
+        const: "mediatek,apupwr-rx-rpmsg"
+
+    required:
+      - compatible
+
+    additionalProperties: false
+
+  apu_mdw_rpmsg:
+    description: handle the middleware messages.
+    type: object
+
+    properties:
+      compatible:
+        const: "mediatek,apu-mdw-rpmsg"
+
+    required:
+      - compatible
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - power-domains
+  - interrupts
+  - mediatek,apusys-power
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/mt8192-clk.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/memory/mt8192-larb-port.h>
+
+    apusys_rv@19001000 {
+      compatible = "mediatek,mt8192-apusys-rv";
+      reg = <0x19000000 0x1000>,
+            <0x19001000 0x1000>;
+      reg-names = "apu_mbox",
+                  "md32_sysctrl";
+      mediatek,apusys-power = <&apusys_power>;
+      power-domains = <&apuspm 0>;
+      iommus = <&iommu_apu IOMMU_PORT_APU_DATA>;
+      interrupts = <GIC_SPI 393 IRQ_TYPE_LEVEL_HIGH 0>,
+                   <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH 0>;
+      interrupt-names = "apu_wdt",
+                        "mbox0_irq";
+      apu_ctrl {
+        compatible = "mediatek,apu-ctrl-rpmsg";
+      };
+      apu_pwr_tx {
+        compatible = "mediatek,apupwr-tx-rpmsg";
+      };
+      apu_pwr_rx {
+        compatible = "mediatek,apupwr-rx-rpmsg";
+      };
+      apu_mdw_rpmsg {
+        compatible = "mediatek,apu-mdw-rpmsg";
+      };
+    };
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 04/13] iommu/mediatek: Add APU iommu support
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (2 preceding siblings ...)
  2021-10-23 11:13 ` [RFC 03/13] dt-bindings: soc: mediatek: apusys: Add new document for APU tinysys Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-26 15:17   ` AngeloGioacchino Del Regno
  2021-10-23 11:14 ` [RFC 05/13] soc: mediatek: Add command for APU SMC call Flora Fu
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

APU IOMMU is a new iommu HW. it use a new pagetable.
Add support for mt8192 apu iommu.

Signed-off-by: Yong Wu <yong.wu@mediatek.com>
Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 drivers/iommu/mtk_iommu.c                     | 57 +++++++++++++++++++
 include/dt-bindings/memory/mt8192-larb-port.h |  4 ++
 2 files changed, 61 insertions(+)

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index 90be8ebbc98a..a5f8f19e053a 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)
 {
@@ -923,6 +932,37 @@ static int mtk_iommu_mm_dts_parse(struct device *dev,
 	return 0;
 }
 
+static int mtk_iommu_apu_prepare(struct device *dev)
+{
+	struct device_node *apupower_node;
+	struct platform_device *apudev;
+	struct device_link *link;
+
+	apupower_node = of_find_compatible_node(NULL, NULL, "mediatek,apusys-power");
+	if (!apupower_node) {
+		dev_warn(dev, "Can't find apu power node!\n");
+		return -EINVAL;
+	}
+
+	if (!of_device_is_available(apupower_node)) {
+		of_node_put(apupower_node);
+		return -EPERM;
+	}
+
+	apudev = of_find_device_by_node(apupower_node);
+	if (!apudev) {
+		of_node_put(apupower_node);
+		return -EPROBE_DEFER;
+	}
+
+	link = device_link_add(&apudev->dev, dev, DL_FLAG_PM_RUNTIME);
+	if (!link)
+		dev_err(dev, "Unable link %s.\n", apudev->name);
+
+	of_node_put(apupower_node);
+	return 0;
+}
+
 static int mtk_iommu_probe(struct platform_device *pdev)
 {
 	struct mtk_iommu_data   *data;
@@ -1021,6 +1061,10 @@ static int mtk_iommu_probe(struct platform_device *pdev)
 		}
 
 		data->pericfg = infracfg;
+	} else if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_APU)) {
+		ret = mtk_iommu_apu_prepare(dev);
+		if (ret)
+			goto out_runtime_disable;
 	}
 
 	platform_set_drvdata(pdev, data);
@@ -1268,6 +1312,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          = WR_THROT_EN | 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 |
@@ -1323,6 +1379,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},
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
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 05/13] soc: mediatek: Add command for APU SMC call
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (3 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 04/13] iommu/mediatek: Add APU iommu support Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-23 11:14 ` [RFC 06/13] soc: mediatek: apu: Add apu core driver Flora Fu
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add command for APU SMC call.
The tinyys start and stop sequence will porcess in ATF.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 include/linux/soc/mediatek/mtk_sip_svc.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/soc/mediatek/mtk_sip_svc.h b/include/linux/soc/mediatek/mtk_sip_svc.h
index 082398e0cfb1..ce44d26a3922 100644
--- a/include/linux/soc/mediatek/mtk_sip_svc.h
+++ b/include/linux/soc/mediatek/mtk_sip_svc.h
@@ -22,4 +22,6 @@
 	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, MTK_SIP_SMC_CONVENTION, \
 			   ARM_SMCCC_OWNER_SIP, fn_id)
 
+/* APUSYS SMC call */
+#define MTK_SIP_APUSYS_CONTROL MTK_SIP_SMC_CMD(0x51E)
 #endif
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 06/13] soc: mediatek: apu: Add apu core driver
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (4 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 05/13] soc: mediatek: Add command for APU SMC call Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-23 15:49   ` Randy Dunlap
  2021-10-26 15:17   ` AngeloGioacchino Del Regno
  2021-10-23 11:14 ` [RFC 07/13] soc: mediatek: apu: Add apu power driver Flora Fu
                   ` (6 subsequent siblings)
  12 siblings, 2 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add apu core driver.
The core driver will init the reset part of apu functions.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 drivers/soc/mediatek/Kconfig           | 18 +++++
 drivers/soc/mediatek/apusys/Makefile   |  3 +
 drivers/soc/mediatek/apusys/apu-core.c | 91 ++++++++++++++++++++++++++
 drivers/soc/mediatek/apusys/apu-core.h | 11 ++++
 4 files changed, 123 insertions(+)
 create mode 100644 drivers/soc/mediatek/apusys/apu-core.c
 create mode 100644 drivers/soc/mediatek/apusys/apu-core.h

diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index d9bac2710494..074b0cf24c44 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -24,6 +24,24 @@ 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 MTK_APU_PM
+	select MTK_SCP
+	help
+	  Say yes here to add support for the APU tinysys. The tinsys is
+	  running on a micro processor in APU.
+	  Its firmware is load and boot from Kernel side. Kernel and tinysys use
+	  IPI to tx/rx messages.
+
+config MTK_APU_DEBUG
+	tristate "MediaTek APUSYS debug functions"
+	depends on MTK_APU
+	help
+	  Say yes here to enalbe debug on APUSYS.
+	  Disable it if you don't need them.
+
 config MTK_CMDQ
 	tristate "MediaTek CMDQ Support"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile
index 8821c0f0b7b7..6b5249ec7064 100644
--- a/drivers/soc/mediatek/apusys/Makefile
+++ b/drivers/soc/mediatek/apusys/Makefile
@@ -1,2 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
+
+obj-$(CONFIG_MTK_APU) += apu.o
+apu-objs += apu-core.o
diff --git a/drivers/soc/mediatek/apusys/apu-core.c b/drivers/soc/mediatek/apusys/apu-core.c
new file mode 100644
index 000000000000..c1db2394307f
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu-core.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "apu-core.h"
+
+#define APUSYS_DEV_NAME "apusys"
+
+static struct apusys_core_info g_core_info;
+
+/*
+ * init function at other modulses
+ * call init function in order at apu.ko init stage
+ */
+static int (*apusys_init_func[])(struct apusys_core_info *) = {
+};
+
+/*
+ * exit function at other modulses
+ * call exit function in order at apu.ko exit stage
+ */
+static void (*apusys_exit_func[])(void) = {
+};
+
+static void create_dbg_root(void)
+{
+	g_core_info.dbg_root = debugfs_create_dir(APUSYS_DEV_NAME, NULL);
+
+	/* check dbg root create status */
+	if (IS_ERR_OR_NULL(g_core_info.dbg_root))
+		pr_info("failed to create debug dir.\n");
+}
+
+static void destroy_dbg_root(void)
+{
+	debugfs_remove_recursive(g_core_info.dbg_root);
+}
+
+static int __init apusys_init(void)
+{
+	int i, j, ret = 0;
+	int func_num = sizeof(apusys_init_func) / sizeof(int *);
+
+	/* init apusys_dev */
+	create_dbg_root();
+
+	/* call init func */
+	for (i = 0; i < func_num; i++) {
+		if (!apusys_init_func[i])
+			break;
+
+		ret = apusys_init_func[i](&g_core_info);
+		if (ret) {
+			pr_info("init function(%d) fail(%d)", i, ret);
+
+			/* exit device */
+			for (j = i - 1; j >= 0; j--)
+				apusys_exit_func[j]();
+
+			destroy_dbg_root();
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void __exit apusys_exit(void)
+{
+	int func_num = sizeof(apusys_exit_func) / sizeof(int *);
+	int i;
+
+	/* call release func */
+	for (i = 0; i < func_num; i++) {
+		if (!apusys_exit_func[i])
+			break;
+
+		apusys_exit_func[i]();
+	}
+
+	destroy_dbg_root();
+}
+
+module_init(apusys_init);
+module_exit(apusys_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/mediatek/apusys/apu-core.h b/drivers/soc/mediatek/apusys/apu-core.h
new file mode 100644
index 000000000000..bac730bbc7ea
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu-core.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+#ifndef APU_CORE_H
+#define APU_CORE_H
+
+struct apusys_core_info {
+	struct dentry *dbg_root;
+};
+#endif
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 07/13] soc: mediatek: apu: Add apu power driver
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (5 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 06/13] soc: mediatek: apu: Add apu core driver Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-23 11:14 ` [RFC 08/13] soc: mediatek: apu: Add apusys rv driver Flora Fu
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

APU power driver support for subsys clock and
regulator controller. It has device link to iommu-apu
and apusys-rv tinysys driver to ensure the power state is
ready for hardware in sub modules.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 drivers/soc/mediatek/apusys/Makefile      |   4 +
 drivers/soc/mediatek/apusys/apu-core.c    |   2 +
 drivers/soc/mediatek/apusys/apu-core.h    |   3 +
 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     | 599 ++++++++++++++++++++++
 drivers/soc/mediatek/apusys/apu-pwr.h     | 260 ++++++++++
 7 files changed, 1412 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/Makefile b/drivers/soc/mediatek/apusys/Makefile
index 6b5249ec7064..8c61ed8e2c04 100644
--- a/drivers/soc/mediatek/apusys/Makefile
+++ b/drivers/soc/mediatek/apusys/Makefile
@@ -3,3 +3,7 @@ obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
 
 obj-$(CONFIG_MTK_APU) += apu.o
 apu-objs += apu-core.o
+
+apu-objs += apu-pwr.o
+apu-objs += apu-pwr-ipi.o
+apu-$(CONFIG_MTK_APU_DEBUG) += apu-pwr-dbg.o
diff --git a/drivers/soc/mediatek/apusys/apu-core.c b/drivers/soc/mediatek/apusys/apu-core.c
index c1db2394307f..069e18af7a5b 100644
--- a/drivers/soc/mediatek/apusys/apu-core.c
+++ b/drivers/soc/mediatek/apusys/apu-core.c
@@ -18,6 +18,7 @@ static struct apusys_core_info g_core_info;
  * call init function in order at apu.ko init stage
  */
 static int (*apusys_init_func[])(struct apusys_core_info *) = {
+	apu_power_drv_init,
 };
 
 /*
@@ -25,6 +26,7 @@ static int (*apusys_init_func[])(struct apusys_core_info *) = {
  * call exit function in order at apu.ko exit stage
  */
 static void (*apusys_exit_func[])(void) = {
+	apu_power_drv_exit,
 };
 
 static void create_dbg_root(void)
diff --git a/drivers/soc/mediatek/apusys/apu-core.h b/drivers/soc/mediatek/apusys/apu-core.h
index bac730bbc7ea..77f1b39424d1 100644
--- a/drivers/soc/mediatek/apusys/apu-core.h
+++ b/drivers/soc/mediatek/apusys/apu-core.h
@@ -8,4 +8,7 @@
 struct apusys_core_info {
 	struct dentry *dbg_root;
 };
+
+int apu_power_drv_init(struct apusys_core_info *info);
+void apu_power_drv_exit(void);
 #endif
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 <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+#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 <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/rpmsg.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#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..ef0df1469a76
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu-pwr.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include "apu-core.h"
+#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 struct dentry *dbg_root;
+
+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_enalbe_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_enalbe_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_enalbe_vpu_clksrc(apupwr);
+		break;
+	case MDLA0:
+		ret = mt8192_enalbe_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->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->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->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->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 = dbg_root;
+	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;
+	}
+
+	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),
+	},
+};
+
+int apu_power_drv_init(struct apusys_core_info *info)
+{
+	dbg_root = info->dbg_root;
+	return platform_driver_register(&apu_power_driver);
+}
+
+void apu_power_drv_exit(void)
+{
+	platform_driver_unregister(&apu_power_driver);
+}
+
+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 <linux/platform_device.h>
+#include <linux/rpmsg.h>
+
+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
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 08/13] soc: mediatek: apu: Add apusys rv driver
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (6 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 07/13] soc: mediatek: apu: Add apu power driver Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-26 15:21   ` AngeloGioacchino Del Regno
  2021-10-23 11:14 ` [RFC 09/13] soc: mediatek: apu: Add middleware driver Flora Fu
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add driver for control APU tinysys

APU integrated subsystem having MD32RV33 (MD32) that runs tinysys
The tinsys is running on a micro processor in APU.
Its firmware is load and boot from Kernel side. Kernel and tinysys use
IPI to tx/rx messages.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 drivers/soc/mediatek/apusys/Makefile        |   6 +
 drivers/soc/mediatek/apusys/apu-config.h    | 100 +++
 drivers/soc/mediatek/apusys/apu-core.c      |   2 +
 drivers/soc/mediatek/apusys/apu-core.h      |   2 +
 drivers/soc/mediatek/apusys/apu-ipi.c       | 486 ++++++++++++
 drivers/soc/mediatek/apusys/apu-mbox.c      |  83 ++
 drivers/soc/mediatek/apusys/apu-mbox.h      |  27 +
 drivers/soc/mediatek/apusys/apu-rproc.c     | 806 ++++++++++++++++++++
 drivers/soc/mediatek/apusys/apu-sw-logger.c | 429 +++++++++++
 drivers/soc/mediatek/apusys/apu.h           | 256 +++++++
 drivers/soc/mediatek/apusys/mt81xx-plat.c   | 320 ++++++++
 11 files changed, 2517 insertions(+)
 create mode 100644 drivers/soc/mediatek/apusys/apu-config.h
 create mode 100644 drivers/soc/mediatek/apusys/apu-ipi.c
 create mode 100644 drivers/soc/mediatek/apusys/apu-mbox.c
 create mode 100644 drivers/soc/mediatek/apusys/apu-mbox.h
 create mode 100644 drivers/soc/mediatek/apusys/apu-rproc.c
 create mode 100644 drivers/soc/mediatek/apusys/apu-sw-logger.c
 create mode 100644 drivers/soc/mediatek/apusys/apu.h
 create mode 100644 drivers/soc/mediatek/apusys/mt81xx-plat.c

diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile
index 8c61ed8e2c04..490de0ee4727 100644
--- a/drivers/soc/mediatek/apusys/Makefile
+++ b/drivers/soc/mediatek/apusys/Makefile
@@ -7,3 +7,9 @@ apu-objs += apu-core.o
 apu-objs += apu-pwr.o
 apu-objs += apu-pwr-ipi.o
 apu-$(CONFIG_MTK_APU_DEBUG) += apu-pwr-dbg.o
+
+apu-objs += apu-rproc.o
+apu-objs += apu-ipi.o
+apu-objs += apu-mbox.o
+apu-objs += mt81xx-plat.o
+apu-$(CONFIG_DEBUG_FS) += apu-sw-logger.o
diff --git a/drivers/soc/mediatek/apusys/apu-config.h b/drivers/soc/mediatek/apusys/apu-config.h
new file mode 100644
index 000000000000..fee3b0334502
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/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/drivers/soc/mediatek/apusys/apu-core.c b/drivers/soc/mediatek/apusys/apu-core.c
index 069e18af7a5b..80652b1d056e 100644
--- a/drivers/soc/mediatek/apusys/apu-core.c
+++ b/drivers/soc/mediatek/apusys/apu-core.c
@@ -19,6 +19,7 @@ static struct apusys_core_info g_core_info;
  */
 static int (*apusys_init_func[])(struct apusys_core_info *) = {
 	apu_power_drv_init,
+	apu_rproc_init,
 };
 
 /*
@@ -26,6 +27,7 @@ static int (*apusys_init_func[])(struct apusys_core_info *) = {
  * call exit function in order at apu.ko exit stage
  */
 static void (*apusys_exit_func[])(void) = {
+	apu_rproc_exit,
 	apu_power_drv_exit,
 };
 
diff --git a/drivers/soc/mediatek/apusys/apu-core.h b/drivers/soc/mediatek/apusys/apu-core.h
index 77f1b39424d1..b47d95f0a1ae 100644
--- a/drivers/soc/mediatek/apusys/apu-core.h
+++ b/drivers/soc/mediatek/apusys/apu-core.h
@@ -11,4 +11,6 @@ struct apusys_core_info {
 
 int apu_power_drv_init(struct apusys_core_info *info);
 void apu_power_drv_exit(void);
+int apu_rproc_init(struct apusys_core_info *info);
+void apu_rproc_exit(void);
 #endif
diff --git a/drivers/soc/mediatek/apusys/apu-ipi.c b/drivers/soc/mediatek/apusys/apu-ipi.c
new file mode 100644
index 000000000000..547e034b3620
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu-ipi.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/lockdep.h>
+#include <linux/time64.h>
+
+#include "apu.h"
+#include "apu-config.h"
+#include "apu-mbox.h"
+
+#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;
+	}
+
+	/* re-init inbox mask everytime for aoc */
+	apu_mbox_inbox_init(apu);
+	apu_deepidle_power_on_aputop(apu);
+	ret = apu_mbox_wait_inbox(apu);
+	if (ret) {
+		dev_info(apu->dev, "wait inbox fail, ret=%d\n", ret);
+		goto unlock_mutex;
+	}
+
+	/* 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++;
+
+	apu_mbox_write_inbox(apu, &hdr);
+	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 irqreturn_t apu_ipi_handler(int irq, void *priv)
+{
+	struct timespec64 ts, te;
+	struct mtk_apu *apu = priv;
+	struct apu_mbox_hdr hdr;
+	struct mtk_share_obj *recv_obj = apu->recv_buf;
+	ipi_handler_t handler;
+	u32 id, len, calc_csum;
+	u32 temp_buf[APU_SHARE_BUFFER_SIZE / 4] = {0};
+
+	hdr.id = 0;
+	hdr.len = 0;
+	hdr.serial_no = 0;
+	hdr.csum = 0;
+
+	ktime_get_ts64(&ts);
+	apu_mbox_read_outbox(apu, &hdr);
+	id = hdr.id;
+	len = hdr.len;
+	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);
+		dev_info(apu->dev, "outbox irq=%x\n",
+			 ioread32(apu->apu_mbox + 0xc4));
+		if (ioread32(apu->apu_mbox + 0xc4) == 0) {
+			dev_info(apu->dev, "abnormal isr call, skip\n");
+			goto ack_irq;
+		}
+	}
+	rx_serial_no++;
+
+	if (len > APU_SHARE_BUFFER_SIZE) {
+		dev_info(apu->dev, "IPI message too long(len %d, max %d)",
+			 len, APU_SHARE_BUFFER_SIZE);
+		goto ack_irq;
+	}
+
+	if (id >= APU_IPI_MAX) {
+		dev_info(apu->dev, "no such IPI id = %d", id);
+		goto ack_irq;
+	}
+
+	mutex_lock(&apu->ipi_desc[id].lock);
+	handler = apu->ipi_desc[id].handler;
+	if (!handler) {
+		dev_info(apu->dev, "IPI id=%d is not registered", id);
+		mutex_unlock(&apu->ipi_desc[id].lock);
+		goto ack_irq;
+	}
+
+	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
+	}
+
+	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);
+
+ack_irq:
+	apu_mbox_ack_outbox(apu);
+	ktime_get_ts64(&te);
+	ts = timespec64_sub(te, ts);
+
+	return IRQ_HANDLED;
+}
+
+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)
+{
+	apu_mbox_hw_exit(apu);
+	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;
+	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);
+
+	/* register mailbox IRQ */
+	apu->mbox0_irq_number = platform_get_irq_byname(pdev, "mbox0_irq");
+	dev_info(&pdev->dev, "%s: mbox0_irq = %d\n", __func__,
+		 apu->mbox0_irq_number);
+
+	ret = devm_request_threaded_irq(&pdev->dev, apu->mbox0_irq_number,
+					NULL, apu_ipi_handler, IRQF_ONESHOT,
+					"apu_ipi", apu);
+	if (ret < 0)
+		goto remove_rpmsg_subdev;
+
+	apu_mbox_hw_init(apu);
+
+	return 0;
+
+remove_rpmsg_subdev:
+	apu_remove_rpmsg_subdev(apu);
+	apu_ipi_unregister(apu, APU_IPI_INIT);
+
+	return ret;
+}
diff --git a/drivers/soc/mediatek/apusys/apu-mbox.c b/drivers/soc/mediatek/apusys/apu-mbox.c
new file mode 100644
index 000000000000..dfd41f7c2640
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu-mbox.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/jiffies.h>
+
+#include "apu.h"
+#include "apu-mbox.h"
+
+#define _INBOX(_m)		((_m)->apu_mbox + 0x0)
+#define _OUTBOX(_m)		((_m)->apu_mbox + 0x20)
+#define _DUMMY(_m)		((_m)->apu_mbox + 0x40)
+#define _INBOX_IRQ(_m)		((_m)->apu_mbox + 0xc0)
+#define _OUTBOX_IRQ(_m)		((_m)->apu_mbox + 0xc4)
+#define _INBOX_IRQ_MASK(_m)	((_m)->apu_mbox + 0xd0)
+#define _OUTBOX_IRQ_MASK(_m)	((_m)->apu_mbox + 0xd8)
+
+void apu_mbox_ack_outbox(struct mtk_apu *apu)
+{
+	iowrite32(ioread32(_OUTBOX_IRQ(apu)),
+		  _OUTBOX_IRQ(apu));
+}
+
+void apu_mbox_read_outbox(struct mtk_apu *apu, struct apu_mbox_hdr *hdr)
+{
+	unsigned int i, val;
+
+	for (i = 0; i < APU_MBOX_HDR_SLOTS; i++) {
+		val = ioread32(_OUTBOX(apu) + i * APU_MBOX_SLOT_SIZE);
+		((unsigned int *)hdr)[i] = val;
+	}
+}
+
+int apu_mbox_wait_inbox(struct mtk_apu *apu)
+{
+	unsigned long timeout;
+	unsigned char irq, mask;
+
+	timeout = jiffies + msecs_to_jiffies(1000);
+	do {
+		if (time_after(jiffies, timeout)) {
+			dev_info(apu->dev, "timeout.\n");
+			return -ETIMEDOUT;
+		}
+
+		irq = ioread32(_INBOX_IRQ(apu));
+		mask = ioread32(_INBOX_IRQ_MASK(apu));
+
+	} while (irq & ~mask);
+
+	return 0;
+}
+
+void apu_mbox_write_inbox(struct mtk_apu *apu, struct apu_mbox_hdr *hdr)
+{
+	unsigned int i, val;
+
+	for (i = 0; i < APU_MBOX_HDR_SLOTS; i++) {
+		val = ((unsigned int *)hdr)[i];
+		iowrite32(val, _INBOX(apu) +  i * APU_MBOX_SLOT_SIZE);
+	}
+}
+
+void apu_mbox_inbox_init(struct mtk_apu *apu)
+{
+	iowrite32(~(1 << (APU_MBOX_HDR_SLOTS - 1)),
+		  _INBOX_IRQ_MASK(apu));
+}
+
+void apu_mbox_hw_init(struct mtk_apu *apu)
+{
+	apu_mbox_inbox_init(apu);
+
+	/* clear outbox IRQ */
+	apu_mbox_ack_outbox(apu);
+}
+
+void apu_mbox_hw_exit(struct mtk_apu *apu)
+{
+	/* mask inbox IRQ */
+	iowrite32(0xff, _INBOX_IRQ_MASK(apu));
+}
diff --git a/drivers/soc/mediatek/apusys/apu-mbox.h b/drivers/soc/mediatek/apusys/apu-mbox.h
new file mode 100644
index 000000000000..47c48d2a1c25
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu-mbox.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#ifndef APU_MBOX_H
+#define APU_MBOX_H
+
+struct apu_mbox_hdr {
+	unsigned int id;
+	unsigned int len;
+	unsigned int serial_no;
+	unsigned int csum;
+};
+
+#define APU_MBOX_SLOT_SIZE	(4)
+#define APU_MBOX_HDR_SLOTS \
+		(sizeof(struct apu_mbox_hdr) / APU_MBOX_SLOT_SIZE)
+
+void apu_mbox_ack_outbox(struct mtk_apu *apu);
+void apu_mbox_read_outbox(struct mtk_apu *apu, struct apu_mbox_hdr *hdr);
+int apu_mbox_wait_inbox(struct mtk_apu *apu);
+void apu_mbox_write_inbox(struct mtk_apu *apu, struct apu_mbox_hdr *hdr);
+void apu_mbox_inbox_init(struct mtk_apu *apu);
+void apu_mbox_hw_init(struct mtk_apu *apu);
+void apu_mbox_hw_exit(struct mtk_apu *apu);
+#endif /* APU_MBOX_H */
diff --git a/drivers/soc/mediatek/apusys/apu-rproc.c b/drivers/soc/mediatek/apusys/apu-rproc.c
new file mode 100644
index 000000000000..e2fe63dd6cc1
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu-rproc.c
@@ -0,0 +1,806 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/remoteproc.h>
+#include <linux/time64.h>
+#include <linux/of_platform.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched/clock.h>
+#include <linux/time64.h>
+#include <linux/workqueue.h>
+
+#include "apu.h"
+#include "apu-config.h"
+#include "apu-core.h"
+
+/* 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 struct dentry *dbg_root;
+
+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 + MBOX0_SPARE0),
+		 ioread32(apu->apu_mbox + MBOX0_SPARE1),
+		 ioread32(apu->apu_mbox + MBOX0_SPARE2),
+		 ioread32(apu->apu_mbox + MBOX0_SPARE3));
+
+	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);
+	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);
+	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)
+{
+	switch (event) {
+	case GENPD_NOTIFY_OFF:
+		pr_info("%s: apu top off\n", __func__);
+		queue_work(apu_pwr_wq, apu_pwr_work);
+		break;
+	case GENPD_NOTIFY_ON:
+		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;
+	struct device_link *link;
+
+	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_init ||
+	    !hw_ops->power_on || !hw_ops->power_off ||
+	    !apu->platdata->ipi_attrs) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	if (data->flags & F_AUTO_BOOT) {
+		ret = hw_ops->power_init(apu);
+		if (ret)
+			goto out_free_rproc;
+
+		link = device_link_add(dev, apu->power_dev, DL_FLAG_PM_RUNTIME);
+		if (!link)
+			dev_err(dev, "unable to link\n");
+	}
+	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;
+
+	apu->dbg_root = dbg_root;
+	ret = apu_sw_logger_init(apu);
+	if (ret)
+		goto remove_apu_sw_logger;
+
+	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_sw_logger:
+	apu_sw_logger_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);
+	apu_sw_logger_remove(apu);
+	hw_ops->apu_memmap_remove(apu);
+	pm_runtime_disable(dev);
+	rproc_free(apu->rproc);
+
+	return 0;
+}
+
+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,
+	},
+};
+
+int apu_rproc_init(struct apusys_core_info *info)
+{
+	dbg_root = info->dbg_root;
+	return platform_driver_register(&apu_driver);
+}
+
+void apu_rproc_exit(void)
+{
+	platform_driver_unregister(&apu_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek APU control driver");
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..818b11cbaa29
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu-sw-logger.c
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/sched/signal.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+
+#include "apu.h"
+#include "apu-core.h"
+
+#define SW_LOGGER_DEV_NAME "apu_sw_logger"
+#define APUSYS_LOGGER_DIR "logger"
+
+#define LOG_LINE_MAX_LENS 128
+#define APU_LOG_SIZE (1024 * 1024)
+#define APU_LOG_BUF_SIZE (1024 * 1024)
+
+static struct dentry *log_root;
+static struct dentry *log_seqlog;
+static struct dentry *log_seqlogl;
+
+static void sw_logger_buf_invalidate(struct mtk_apu *apu)
+{
+	dma_sync_single_for_cpu(apu->dev, apu->handle, APU_LOG_SIZE,
+				DMA_FROM_DEVICE);
+}
+
+static int sw_logger_buf_alloc(struct device *dev)
+{
+	struct mtk_apu *apu = dev_get_drvdata(dev);
+	int ret;
+
+	ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
+	if (ret)
+		return ret;
+
+	apu->sw_log_buf = kzalloc(APU_LOG_SIZE, GFP_KERNEL);
+	if (!apu->sw_log_buf)
+		return -ENOMEM;
+
+	apu->handle = dma_map_single(dev, apu->sw_log_buf, APU_LOG_SIZE,
+				     DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev, apu->handle) != 0) {
+		kfree(apu->sw_log_buf);
+		apu->sw_log_buf = NULL;
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int sw_logger_config_init(struct mtk_apu *apu)
+{
+	int ret;
+	unsigned long flags;
+	struct logger_init_info *st_logger_init_info;
+
+	if (!apu || !apu->conf_buf)
+		return -EINVAL;
+
+	if (!apu->sw_log_buf) {
+		ret = sw_logger_buf_alloc(apu->dev);
+		if (ret) {
+			dev_err(apu->dev, "%s: sw_logger_buf_alloc fail\n",
+				__func__);
+			return ret;
+		}
+	}
+
+	spin_lock_irqsave(&apu->sw_logger_spinlock, flags);
+	iowrite32(0, apu->apu_mbox + LOG_W_PTR);
+	iowrite32(0, apu->apu_mbox + LOG_R_PTR);
+	iowrite32(0, apu->apu_mbox + LOG_OV_FLG);
+	spin_unlock_irqrestore(&apu->sw_logger_spinlock, flags);
+
+	st_logger_init_info = (struct logger_init_info *)
+		get_apu_config_user_ptr(apu->conf_buf, LOGGER_INIT_INFO);
+
+	st_logger_init_info->iova = apu->handle;
+
+	return 0;
+}
+
+void sw_logger_config_remove(struct mtk_apu *apu)
+{
+	if (apu->handle)
+		dma_unmap_single(apu->dev, apu->handle,
+				 APU_LOG_SIZE, DMA_FROM_DEVICE);
+	kfree(apu->sw_log_buf);
+}
+
+/*
+ * 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 mtk_apu *apu = (struct mtk_apu *)s->private;
+	u32 w_ptr, r_ptr, overflow_flg;
+	unsigned long flags;
+
+	if (!apu->sw_log_buf) {
+		pr_err("%s: sw_log_buf == NULL\n", __func__);
+		return NULL;
+	}
+
+	spin_lock_irqsave(&apu->sw_logger_spinlock, flags);
+	w_ptr = ioread32(apu->apu_mbox + LOG_W_PTR);
+	r_ptr = ioread32(apu->apu_mbox + LOG_R_PTR);
+	overflow_flg = ioread32(apu->apu_mbox + LOG_OV_FLG);
+	spin_unlock_irqrestore(&apu->sw_logger_spinlock, flags);
+
+	sw_logger_buf_invalidate(apu);
+
+	if (w_ptr == r_ptr && overflow_flg == 0)
+		return NULL;
+
+	if (!apu->pseqdata) {
+		apu->pseqdata = kzalloc(sizeof(*apu->pseqdata), GFP_KERNEL);
+		if (apu->pseqdata) {
+			apu->pseqdata->w_ptr = w_ptr;
+			apu->pseqdata->r_ptr = r_ptr;
+			apu->pseqdata->overflow_flg = overflow_flg;
+			if (overflow_flg == 0)
+				apu->pseqdata->i = r_ptr;
+			else
+				apu->pseqdata->i = w_ptr;
+
+			apu->pseqdata->is_finished = 0;
+		}
+	}
+
+	return apu->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 mtk_apu *apu = s->private;
+	u32 w_ptr, r_ptr, overflow_flg;
+	struct sw_logger_seq_data *pseqdata_lock = &apu->pseqdata_lock;
+	unsigned long flags;
+
+	if (!apu->sw_log_buf)
+		return NULL;
+
+	spin_lock_irqsave(&apu->sw_logger_spinlock, flags);
+	w_ptr = ioread32(apu->apu_mbox + LOG_W_PTR);
+	r_ptr = ioread32(apu->apu_mbox + LOG_R_PTR);
+	overflow_flg = ioread32(apu->apu_mbox + LOG_OV_FLG);
+	spin_unlock_irqrestore(&apu->sw_logger_spinlock, flags);
+
+	sw_logger_buf_invalidate(apu);
+
+	/* for ctrl-c to force exit the loop */
+	while (!signal_pending(current) && w_ptr == r_ptr) {
+		usleep_range(10000, 12000);
+
+		spin_lock_irqsave(&apu->sw_logger_spinlock, flags);
+		w_ptr = ioread32(apu->apu_mbox + LOG_W_PTR);
+		r_ptr = ioread32(apu->apu_mbox + LOG_R_PTR);
+		overflow_flg = ioread32(apu->apu_mbox + LOG_OV_FLG);
+		spin_unlock_irqrestore(&apu->sw_logger_spinlock, flags);
+
+		sw_logger_buf_invalidate(apu);
+
+		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 mtk_apu *apu = 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, apu->apu_mbox + 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 mtk_apu *apu = (struct mtk_apu *)s->private;
+	unsigned long flags;
+
+	if (apu->pseqdata) {
+		if (apu->pseqdata->is_finished == 1) {
+			spin_lock_irqsave(&apu->sw_logger_spinlock, flags);
+			iowrite32(apu->pseqdata->i, apu->apu_mbox + LOG_R_PTR);
+			/* fixme: assume next overflow won't happen
+			 * until next seq_start
+			 */
+			iowrite32(0, apu->apu_mbox + LOG_OV_FLG);
+			spin_unlock_irqrestore(&apu->sw_logger_spinlock, flags);
+			kfree(apu->pseqdata);
+			apu->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 mtk_apu *apu = (struct mtk_apu *)s->private;
+	struct sw_logger_seq_data *psdata = v;
+
+	seq_printf(s, "%s", apu->sw_log_buf + psdata->i);
+
+	return 0;
+}
+
+static int seq_showl(struct seq_file *s, void *v)
+{
+	struct mtk_apu *apu = (struct mtk_apu *)s->private;
+	struct sw_logger_seq_data *psdata = v;
+
+	if (psdata->i != psdata->w_ptr)
+		seq_printf(s, "%s", apu->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 mtk_apu *apu = inode->i_private;
+	int ret;
+
+	ret = seq_open(file, &seq_ops_lock);
+	if (ret)
+		return ret;
+
+	((struct seq_file *)file->private_data)->private = apu;
+
+	return 0;
+}
+
+static int debug_sqopen(struct inode *inode, struct file *file)
+{
+	struct mtk_apu *apu = inode->i_private;
+	int ret;
+
+	ret = seq_open(file, &seq_ops);
+	if (ret)
+		return ret;
+
+	((struct seq_file *)file->private_data)->private = apu;
+
+	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 mtk_apu *apu = dev_get_drvdata(dev);
+	int ret = 0;
+
+	log_root = debugfs_create_dir(APUSYS_LOGGER_DIR, apu->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, apu, &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, apu, &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);
+}
+
+int apu_sw_logger_init(struct mtk_apu *apu)
+{
+	struct device *dev = apu->dev;
+	int ret = 0;
+
+	spin_lock_init(&apu->sw_logger_spinlock);
+	ret = sw_logger_create_debugfs(dev);
+	if (ret) {
+		dev_err(dev, "%s fail\n", __func__);
+		goto remove_debugfs;
+	}
+
+	return 0;
+
+remove_debugfs:
+	sw_logger_remove_debugfs(dev);
+	dev_err(dev, "%s error!!\n", __func__);
+
+	return ret;
+}
+
+void apu_sw_logger_remove(struct mtk_apu *apu)
+{
+	struct device *dev = apu->dev;
+
+	sw_logger_remove_debugfs(dev);
+}
diff --git a/drivers/soc/mediatek/apusys/apu.h b/drivers/soc/mediatek/apusys/apu.h
new file mode 100644
index 000000000000..5bbc46416a19
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/apu.h
@@ -0,0 +1,256 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#ifndef APU_H
+#define APU_H
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/rpmsg/mtk_rpmsg.h>
+
+#include "apu-config.h"
+
+/* 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 regiter */
+#define MBOX0_SPARE0 0x40
+#define MBOX0_SPARE1 0x44
+#define MBOX0_SPARE2 0x48
+#define MBOX0_SPARE3 0x4C
+#define MBOX6_SPARE0 0x640
+#define MBOX6_SPARE1 0x644
+#define MBOX6_SPARE2 0x648
+#define MBOX6_SPARE3 0x64C
+
+#define HOST_CONFIG_ADDR MBOX0_SPARE2
+
+#define LOG_W_PTR (MBOX0_SPARE0)
+#define LOG_R_PTR (MBOX0_SPARE1)
+#define LOG_OV_FLG (MBOX0_SPARE3)
+
+/* rv setup  */
+#define F_PRELOAD_FIRMWARE	BIT(0)
+#define F_AUTO_BOOT		BIT(1)
+
+#define TCM_SIZE (128UL * 1024UL)
+#define CODE_BUF_SIZE (1024UL * 1024UL)
+#define DRAM_DUMP_SIZE (CODE_BUF_SIZE - TCM_SIZE)
+#define REG_SIZE (4UL * 151UL)
+#define TBUF_SIZE (4UL * 32UL)
+#define CACHE_DUMP_SIZE (37UL * 1024UL)
+#define DRAM_OFFSET (0x00000UL)
+#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  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 hanlder 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 sw_logger_seq_data {
+	u32 w_ptr;
+	u32 r_ptr;
+	u32 overflow_flg;
+	int i;
+	int is_finished;
+	char *data;
+	bool startl_first;
+};
+
+struct mtk_apu {
+	struct rproc *rproc;
+	struct device *dev;
+	void __iomem *apu_mbox;
+	void __iomem *md32_sysctrl;
+	void __iomem *apu_wdt;
+	int mbox0_irq_number;
+	int wdt_irq_number;
+	spinlock_t reg_lock; /* register r/w lock */
+
+	/* 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;
+
+	/* link power deive */
+	struct device *power_dev;
+	bool boot_done;
+	struct work_struct pwr_work;
+
+	/* logger and debug */
+	struct dentry *dbg_root;
+	dma_addr_t handle;
+	char *sw_log_buf;
+	spinlock_t sw_logger_spinlock; /* logger status update lock */
+	struct sw_logger_seq_data pseqdata_lock;
+	struct sw_logger_seq_data *pseqdata;
+};
+
+struct apu_coredump {
+	char tcmdump[TCM_SIZE];
+	char ramdump[DRAM_DUMP_SIZE];
+	char regdump[REG_SIZE];
+	char tbufdump[TBUF_SIZE];
+	u32 cachedump[CACHE_DUMP_SIZE / sizeof(u32)];
+} __packed;
+
+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)
+int sw_logger_config_init(struct mtk_apu *apu);
+void sw_logger_config_remove(struct mtk_apu *apu);
+int apu_sw_logger_init(struct mtk_apu *apu);
+void apu_sw_logger_remove(struct mtk_apu *apu);
+#else
+static inline int sw_logger_config_init(struct mtk_apu *apu) { return 0; }
+static inline void sw_logger_config_remove(struct mtk_apu *apu) { }
+static inline int apu_sw_logger_init(struct mtk_apu *apu) { return 0; }
+static inline void apu_sw_logger_remove(struct mtk_apu *apu) { }
+#endif
+
+extern const struct mtk_apu_platdata mt8192_platdata;
+#endif /* APU_H */
diff --git a/drivers/soc/mediatek/apusys/mt81xx-plat.c b/drivers/soc/mediatek/apusys/mt81xx-plat.c
new file mode 100644
index 000000000000..54f75c8d07c3
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/mt81xx-plat.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched/clock.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+
+#include "apu.h"
+
+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;
+
+	/* initialize IOMMU and ACP config (iommu_tr_en=1, acp_en=0) */
+	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 mt8192_rproc_start(struct mtk_apu *apu)
+{
+	apu_reset_mcu(apu);
+	apu_start_mcu(apu);
+
+	return 0;
+}
+
+static int mt8192_rproc_resume(struct mtk_apu *apu)
+{
+	apu_start_mcu(apu);
+
+	return 0;
+}
+
+static int mt8192_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_init(struct mtk_apu *apu)
+{
+	struct device *dev = apu->dev;
+	struct device_node *np;
+	struct platform_device *pdev;
+
+	/* power dev */
+	np = of_parse_phandle(dev->of_node, "mediatek,apusys-power", 0);
+	if (!np) {
+		dev_info(dev, "failed to parse apusys_power node\n");
+		return -EINVAL;
+	}
+
+	if (!of_device_is_available(np)) {
+		dev_info(dev, "unable to find apusys_power node\n");
+		of_node_put(np);
+		return -ENODEV;
+	}
+
+	pdev = of_find_device_by_node(np);
+	if (!pdev) {
+		dev_info(dev, "apusys_power is not ready yet\n");
+		of_node_put(np);
+		return -EPROBE_DEFER;
+	}
+
+	dev_info(dev, "%s: get power_dev, name=%s\n", __func__, pdev->name);
+
+	apu->power_dev = &pdev->dev;
+	of_node_put(np);
+
+	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 + MBOX6_SPARE3,
+					 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);
+		return ret;
+	}
+
+	/* polling APU TOP rpm state till suspended */
+	dev_info(apu->dev, "start polling power off\n");
+	timeout = 500;
+	while (!pm_runtime_suspended(apu->power_dev) && 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_get_power_dev;
+	}
+
+	dev_info(apu->dev, "polling power done\n");
+
+	return 0;
+
+error_get_power_dev:
+	pm_runtime_get_sync(apu->power_dev);
+
+	return ret;
+}
+
+static int mt8192_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 mt8192_apu_memmap_remove(struct mtk_apu *apu)
+{
+	iounmap(apu->apu_wdt);
+	iounmap(apu->md32_sysctrl);
+	iounmap(apu->apu_mbox);
+}
+
+const struct mtk_apu_platdata mt8192_platdata = {
+	.flags		= F_AUTO_BOOT,
+	.ipi_attrs = mt81xx_ipi_attrs,
+	.ops		= {
+		.init	= NULL,
+		.exit	= NULL,
+		.start	= mt8192_rproc_start,
+		.stop	= mt8192_rproc_stop,
+		.resume	= mt8192_rproc_resume,
+		.apu_memmap_init = mt8192_apu_memmap_init,
+		.apu_memmap_remove = mt8192_apu_memmap_remove,
+		.cg_gating = NULL,
+		.cg_ungating = NULL,
+		.rv_cachedump = NULL,
+		.power_init = mt81xx_apu_power_init,
+		.power_on = mt81xx_apu_power_on,
+		.power_off = mt81xx_apu_power_off,
+	},
+};
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 09/13] soc: mediatek: apu: Add middleware driver
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (7 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 08/13] soc: mediatek: apu: Add apusys rv driver Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-23 11:14 ` [RFC 10/13] arm64: dts: mt8192: Add APU-IOMMU nodes Flora Fu
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen,
	JB Tsai

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 <jb.tsai@mediatek.com>
Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 drivers/soc/mediatek/apusys/Makefile     |   9 +
 drivers/soc/mediatek/apusys/apu-core.c   |   2 +
 drivers/soc/mediatek/apusys/apu-core.h   |   2 +
 drivers/soc/mediatek/apusys/apu-device.h |  39 +
 drivers/soc/mediatek/apusys/mdw-cmd.c    | 618 +++++++++++++++
 drivers/soc/mediatek/apusys/mdw-drv.c    | 211 +++++
 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        | 208 +++++
 17 files changed, 3700 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 490de0ee4727..dee3b1f0a1e7 100644
--- a/drivers/soc/mediatek/apusys/Makefile
+++ b/drivers/soc/mediatek/apusys/Makefile
@@ -13,3 +13,12 @@ apu-objs += apu-ipi.o
 apu-objs += apu-mbox.o
 apu-objs += mt81xx-plat.o
 apu-$(CONFIG_DEBUG_FS) += apu-sw-logger.o
+
+apu-objs += mdw-drv.o
+apu-objs += mdw-ioctl.o
+apu-objs += mdw-mem.o
+apu-objs += mdw-cmd.o
+apu-objs += mdw-rv.o
+apu-objs += mdw-rv-cmd.o
+apu-objs += mdw-rv-dev.o
+apu-$(CONFIG_DEBUG_FS) += mdw-sysfs.o
diff --git a/drivers/soc/mediatek/apusys/apu-core.c b/drivers/soc/mediatek/apusys/apu-core.c
index 80652b1d056e..ecad5660ee18 100644
--- a/drivers/soc/mediatek/apusys/apu-core.c
+++ b/drivers/soc/mediatek/apusys/apu-core.c
@@ -19,6 +19,7 @@ static struct apusys_core_info g_core_info;
  */
 static int (*apusys_init_func[])(struct apusys_core_info *) = {
 	apu_power_drv_init,
+	mdw_init,
 	apu_rproc_init,
 };
 
@@ -28,6 +29,7 @@ static int (*apusys_init_func[])(struct apusys_core_info *) = {
  */
 static void (*apusys_exit_func[])(void) = {
 	apu_rproc_exit,
+	mdw_exit,
 	apu_power_drv_exit,
 };
 
diff --git a/drivers/soc/mediatek/apusys/apu-core.h b/drivers/soc/mediatek/apusys/apu-core.h
index b47d95f0a1ae..7d8d6033ab07 100644
--- a/drivers/soc/mediatek/apusys/apu-core.h
+++ b/drivers/soc/mediatek/apusys/apu-core.h
@@ -11,6 +11,8 @@ struct apusys_core_info {
 
 int apu_power_drv_init(struct apusys_core_info *info);
 void apu_power_drv_exit(void);
+int mdw_init(struct apusys_core_info *info);
+void mdw_exit(void);
 int apu_rproc_init(struct apusys_core_info *info);
 void apu_rproc_exit(void);
 #endif
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 <linux/dma-buf.h>
+
+/* 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 <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/sync_file.h>
+
+#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..9c01a383a080
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/mdw-drv.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/dma-direct.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/rpmsg.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "apu-core.h"
+#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", },
+	{ },
+};
+
+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,
+};
+
+int mdw_init(struct apusys_core_info *info)
+{
+	int ret = 0;
+
+	ret = register_rpmsg_driver(&mdw_rpmsg_driver);
+	if (ret)
+		pr_err("Failed to register apu mdw rpmsg driver\n");
+
+	return ret;
+}
+
+void mdw_exit(void)
+{
+	unregister_rpmsg_driver(&mdw_rpmsg_driver);
+}
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 <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#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 <linux/ioctl.h>
+#include <linux/types.h>
+
+#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 <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+#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 <linux/rpmsg.h>
+
+#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..b0a21bd50060
--- /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; // cmd time
+	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 <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/uaccess.h>
+
+#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..e65e246f6494
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/mdw.h
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#ifndef __MTK_APU_MDW_H__
+#define __MTK_APU_MDW_H__
+
+#include <linux/dma-direction.h>
+#include <linux/dma-fence.h>
+#include <linux/hashtable.h>
+#include <linux/iopoll.h>
+#include <linux/irqreturn.h>
+#include <linux/miscdevice.h>
+#include <linux/of_device.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "apu-core.h"
+#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
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 10/13] arm64: dts: mt8192: Add APU-IOMMU nodes
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (8 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 09/13] soc: mediatek: apu: Add middleware driver Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-23 11:14 ` [RFC 11/13] arm64: dts: mt8192: Add apu power nodes Flora Fu
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

From: Yong Wu <yong.wu@mediatek.com>

Add APU-IOMMI nodes

Signed-off-by: Yong Wu <yong.wu@mediatek.com>
Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 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 7014082637b0..d5e417a512a7 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -8,6 +8,7 @@
 #include <dt-bindings/clock/mt8192-clk.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/memory/mt8192-larb-port.h>
 #include <dt-bindings/pinctrl/mt8192-pinfunc.h>
 #include <dt-bindings/power/mt8192-power.h>
 
@@ -916,6 +917,14 @@
 			#clock-cells = <1>;
 		};
 
+		iommu_apu: m4u@19010000 {
+			compatible = "mediatek,mt8192-iommu-apu";
+			reg = <0 0x19010000 0 0x1000>;
+			interrupts = <GIC_SPI 394 IRQ_TYPE_LEVEL_HIGH 0>;
+			power-domains = <&apuspm 0>;
+			#iommu-cells = <1>;
+		};
+
 		apu_conn: apu_conn@19020000 {
 			compatible = "mediatek,mt8192-apu-conn", "syscon";
 			reg = <0 0x19020000 0 0x1000>;
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 11/13] arm64: dts: mt8192: Add apu power nodes
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (9 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 10/13] arm64: dts: mt8192: Add APU-IOMMU nodes Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-23 11:14 ` [RFC 12/13] arm64: dts: mt8192: Add apu tinysys Flora Fu
  2021-10-23 11:14 ` [RFC 13/13] arm64: dts: mt8192: Add regulator for APU Flora Fu
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add apu power node.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8192.dtsi | 62 ++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
index d5e417a512a7..c505c6926839 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -964,6 +964,68 @@
 			};
 		};
 
+		apusys_power: apusys_power@190f1000 {
+			compatible = "mediatek,apusys-power",
+				     "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>;
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 12/13] arm64: dts: mt8192: Add apu tinysys
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (10 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 11/13] arm64: dts: mt8192: Add apu power nodes Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  2021-10-26 15:18   ` AngeloGioacchino Del Regno
  2021-10-23 11:14 ` [RFC 13/13] arm64: dts: mt8192: Add regulator for APU Flora Fu
  12 siblings, 1 reply; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add node for APU tinysys.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8192.dtsi | 36 ++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
index c505c6926839..8108084a3f6f 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -925,6 +925,42 @@
 			#iommu-cells = <1>;
 		};
 
+		apusys_rv@19001000 {
+			compatible = "mediatek,mt8192-apusys-rv";
+			reg = <0 0x19000000 0 0x1000>,
+			      <0 0x19001000 0 0x1000>,
+			      <0 0x19002000 0 0x10>;
+			reg-names = "apu_mbox",
+				    "md32_sysctrl",
+				    "apu_wdt";
+			mediatek,apusys-power = <&apusys_power>;
+			power-domains = <&apuspm 0>;
+			iommus = <&iommu_apu IOMMU_PORT_APU_DATA>;
+			interrupts = <GIC_SPI 393 IRQ_TYPE_LEVEL_HIGH 0>,
+				     <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH 0>;
+			interrupt-names = "apu_wdt",
+					  "mbox0_irq";
+			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";
+			};
+		};
+
 		apu_conn: apu_conn@19020000 {
 			compatible = "mediatek,mt8192-apu-conn", "syscon";
 			reg = <0 0x19020000 0 0x1000>;
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC 13/13] arm64: dts: mt8192: Add regulator for APU
  2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
                   ` (11 preceding siblings ...)
  2021-10-23 11:14 ` [RFC 12/13] arm64: dts: mt8192: Add apu tinysys Flora Fu
@ 2021-10-23 11:14 ` Flora Fu
  12 siblings, 0 replies; 20+ messages in thread
From: Flora Fu @ 2021-10-23 11:14 UTC (permalink / raw)
  To: Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Flora Fu, Yong Wu, Pi-Cheng Chen

Add regulator for mt8192 evb board.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 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>;
+};
-- 
2.18.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC 06/13] soc: mediatek: apu: Add apu core driver
  2021-10-23 11:14 ` [RFC 06/13] soc: mediatek: apu: Add apu core driver Flora Fu
@ 2021-10-23 15:49   ` Randy Dunlap
  2021-10-26 15:17   ` AngeloGioacchino Del Regno
  1 sibling, 0 replies; 20+ messages in thread
From: Randy Dunlap @ 2021-10-23 15:49 UTC (permalink / raw)
  To: Flora Fu, Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Yong Wu, Pi-Cheng Chen

Hi,

On 10/23/21 4:14 AM, Flora Fu wrote:
> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> index d9bac2710494..074b0cf24c44 100644
> --- a/drivers/soc/mediatek/Kconfig
> +++ b/drivers/soc/mediatek/Kconfig
> @@ -24,6 +24,24 @@ 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 MTK_APU_PM
> +	select MTK_SCP
> +	help
> +	  Say yes here to add support for the APU tinysys. The tinsys is

	                                                       tinysys runs on

> +	  running on a micro processor in APU.

	  a microprocessor in the APU.

> +	  Its firmware is load and boot from Kernel side. Kernel and tinysys use

	               is loaded and booted

> +	  IPI to tx/rx messages.

	      to send/receive messages.

> +
> +config MTK_APU_DEBUG
> +	tristate "MediaTek APUSYS debug functions"
> +	depends on MTK_APU
> +	help
> +	  Say yes here to enalbe debug on APUSYS.

	                  enable

> +	  Disable it if you don't need them.


-- 
~Randy

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC 06/13] soc: mediatek: apu: Add apu core driver
  2021-10-23 11:14 ` [RFC 06/13] soc: mediatek: apu: Add apu core driver Flora Fu
  2021-10-23 15:49   ` Randy Dunlap
@ 2021-10-26 15:17   ` AngeloGioacchino Del Regno
  1 sibling, 0 replies; 20+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-10-26 15:17 UTC (permalink / raw)
  To: Flora Fu, Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Yong Wu, Pi-Cheng Chen

Il 23/10/21 13:14, Flora Fu ha scritto:
> Add apu core driver.
> The core driver will init the reset part of apu functions.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>   drivers/soc/mediatek/Kconfig           | 18 +++++
>   drivers/soc/mediatek/apusys/Makefile   |  3 +
>   drivers/soc/mediatek/apusys/apu-core.c | 91 ++++++++++++++++++++++++++
>   drivers/soc/mediatek/apusys/apu-core.h | 11 ++++
>   4 files changed, 123 insertions(+)
>   create mode 100644 drivers/soc/mediatek/apusys/apu-core.c
>   create mode 100644 drivers/soc/mediatek/apusys/apu-core.h
> 

Hello Flora,

I don't think that this custom probe/init mechanism through apu-core.c
can ever be a thing: you should simply register a number of platform
drivers (likely modules) and let the kernel decide what to probe
first, what to probe last, as it's done for every kernel driver.

I understand that this may reduce probe deferrals, as it's a controlled
probe sequence, made specifically for apusys, but it's anyway something
that won't give you big gains (and if it was, then you should still let
the kernel decide and eventually optimize that mechanism somehow).


I want to note that, since this series is rather huge, I will probably
do more than one incremental review of it... and for how things look,
this will most probably be split to more than one series, to allow getting
reviews from specific subsystem maintainers, leading to better code quality
and robustness in the end.

Some more details are coming in reply of other patches in this series.

Thanks,
- Angelo

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC 04/13] iommu/mediatek: Add APU iommu support
  2021-10-23 11:14 ` [RFC 04/13] iommu/mediatek: Add APU iommu support Flora Fu
@ 2021-10-26 15:17   ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 20+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-10-26 15:17 UTC (permalink / raw)
  To: Flora Fu, Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Yong Wu, Pi-Cheng Chen

Il 23/10/21 13:14, Flora Fu ha scritto:
> APU IOMMU is a new iommu HW. it use a new pagetable.
> Add support for mt8192 apu iommu.
> 
> Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>   drivers/iommu/mtk_iommu.c                     | 57 +++++++++++++++++++
>   include/dt-bindings/memory/mt8192-larb-port.h |  4 ++
>   2 files changed, 61 insertions(+)
> 
> diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
> index 90be8ebbc98a..a5f8f19e053a 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)
>   {
> @@ -923,6 +932,37 @@ static int mtk_iommu_mm_dts_parse(struct device *dev,
>   	return 0;
>   }
>   
> +static int mtk_iommu_apu_prepare(struct device *dev)
> +{
> +	struct device_node *apupower_node;
> +	struct platform_device *apudev;
> +	struct device_link *link;
> +
> +	apupower_node = of_find_compatible_node(NULL, NULL, "mediatek,apusys-power");

Is it expected to have PM ops in the apusys-power driver? Currently, I can't see
any, but maybe it's because of how complex is this entire implementation.

In any case, the name suggests that this controls power for the entire APU... so
it would be more appropriate if apusys-power would expose a power domain (since it
also has some sort of OPP, from what I can see), in which case you wouldn't be
retrieving it here like you're doing right now... but simply as a power domain,
simplifying the entire handling of that in here.


> +	if (!apupower_node) {
> +		dev_warn(dev, "Can't find apu power node!\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!of_device_is_available(apupower_node)) {
> +		of_node_put(apupower_node);
> +		return -EPERM;
> +	}
> +
> +	apudev = of_find_device_by_node(apupower_node);
> +	if (!apudev) {
> +		of_node_put(apupower_node);
> +		return -EPROBE_DEFER;
> +	}
> +
> +	link = device_link_add(&apudev->dev, dev, DL_FLAG_PM_RUNTIME);
> +	if (!link)
> +		dev_err(dev, "Unable link %s.\n", apudev->name);
> +
> +	of_node_put(apupower_node);
> +	return 0;
> +}
> +
>   static int mtk_iommu_probe(struct platform_device *pdev)
>   {
>   	struct mtk_iommu_data   *data;
> @@ -1021,6 +1061,10 @@ static int mtk_iommu_probe(struct platform_device *pdev)
>   		}
>   
>   		data->pericfg = infracfg;
> +	} else if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_APU)) {
> +		ret = mtk_iommu_apu_prepare(dev);
> +		if (ret)
> +			goto out_runtime_disable;
>   	}
>   
>   	platform_set_drvdata(pdev, data);
> @@ -1268,6 +1312,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          = WR_THROT_EN | 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 |
> @@ -1323,6 +1379,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},
> 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
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC 12/13] arm64: dts: mt8192: Add apu tinysys
  2021-10-23 11:14 ` [RFC 12/13] arm64: dts: mt8192: Add apu tinysys Flora Fu
@ 2021-10-26 15:18   ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 20+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-10-26 15:18 UTC (permalink / raw)
  To: Flora Fu, Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Yong Wu, Pi-Cheng Chen

Il 23/10/21 13:14, Flora Fu ha scritto:
> Add node for APU tinysys.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>   arch/arm64/boot/dts/mediatek/mt8192.dtsi | 36 ++++++++++++++++++++++++
>   1 file changed, 36 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
> index c505c6926839..8108084a3f6f 100644
> --- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
> @@ -925,6 +925,42 @@
>   			#iommu-cells = <1>;
>   		};
>   
> +		apusys_rv@19001000 {
> +			compatible = "mediatek,mt8192-apusys-rv";
> +			reg = <0 0x19000000 0 0x1000>,
> +			      <0 0x19001000 0 0x1000>,
> +			      <0 0x19002000 0 0x10>;
> +			reg-names = "apu_mbox",
> +				    "md32_sysctrl",
> +				    "apu_wdt";
> +			mediatek,apusys-power = <&apusys_power>;

As said on the IOMMU commit, I think that apusys-power can be passed as a
power domain here as well... also keeping in mind that the apuspm power
domain is being used in both the IOMMU and in apusys-power already, so you
could even pass only the apusys-power pd here, because apuspm would be
already being turned on by the former...

...unless there's any possibility to have some functionality with apuspm up
but apusys-power down? From how it looks right now, that's not a thing.

> +			power-domains = <&apuspm 0>;
> +			iommus = <&iommu_apu IOMMU_PORT_APU_DATA>;
> +			interrupts = <GIC_SPI 393 IRQ_TYPE_LEVEL_HIGH 0>,
> +				     <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH 0>;
> +			interrupt-names = "apu_wdt",
> +					  "mbox0_irq";
> +			apu_ctrl {
> +				compatible = "mediatek,apu-ctrl-rpmsg";
> +				mtk,rpmsg-name = "apu-ctrl-rpmsg";

This is supposed to be "mediatek,rpmsg-name" instead... or it won't work.

> +			};
> +
> +			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";
> +			};
> +		};
> +
>   		apu_conn: apu_conn@19020000 {
>   			compatible = "mediatek,mt8192-apu-conn", "syscon";
>   			reg = <0 0x19020000 0 0x1000>;
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC 08/13] soc: mediatek: apu: Add apusys rv driver
  2021-10-23 11:14 ` [RFC 08/13] soc: mediatek: apu: Add apusys rv driver Flora Fu
@ 2021-10-26 15:21   ` AngeloGioacchino Del Regno
       [not found]     ` <0248142e6e0b55fe12f179e74b77375b386082ee.camel@mediatek.com>
  0 siblings, 1 reply; 20+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-10-26 15:21 UTC (permalink / raw)
  To: Flora Fu, Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Yong Wu, Pi-Cheng Chen

Il 23/10/21 13:14, Flora Fu ha scritto:
> Add driver for control APU tinysys
> 
> APU integrated subsystem having MD32RV33 (MD32) that runs tinysys
> The tinsys is running on a micro processor in APU.
> Its firmware is load and boot from Kernel side. Kernel and tinysys use
> IPI to tx/rx messages.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>   drivers/soc/mediatek/apusys/Makefile        |   6 +
>   drivers/soc/mediatek/apusys/apu-config.h    | 100 +++
>   drivers/soc/mediatek/apusys/apu-core.c      |   2 +
>   drivers/soc/mediatek/apusys/apu-core.h      |   2 +
>   drivers/soc/mediatek/apusys/apu-ipi.c       | 486 ++++++++++++

I'm not sure of that, but your apu-ipi.c may be more suited to be in
drivers/remoteproc instead.

>   drivers/soc/mediatek/apusys/apu-mbox.c      |  83 ++

apu-mbox.c should go to drivers/mailbox/ and you should register it with
the mailbox API as a mailbox controller instead of what you're currently
doing...

 From what I see, you have functions in there that can be indeed mapped
to struct mbox_chan_ops .send_data and .peek_data... also your function
apu_mbox_wait_inbox seems to be waiting on an interrupt, and such irq is
apparently your "mbox0_irq" (as you named it in the dt).
In that case, you can also manage that in your drivers/mailbox/ driver.
+
>   drivers/soc/mediatek/apusys/apu-mbox.h      |  27 +
>   drivers/soc/mediatek/apusys/apu-rproc.c     | 806 ++++++++++++++++++++

The apu-rproc.c driver seems to be a good candidate to be moved away from

drivers/soc/mediatek/apusys/ - as this is indeed a remoteproc driver.

Having it as drivers/remoteproc/mtk_apu.c seems to be a good option.



>   drivers/soc/mediatek/apusys/apu-sw-logger.c | 429 +++++++++++

This one definitely belongs here in drivers/soc/mediatek, and it's a consumer
of the mailbox driver.

>   drivers/soc/mediatek/apusys/apu.h           | 256 +++++++
>   drivers/soc/mediatek/apusys/mt81xx-plat.c   | 320 ++++++++

If we end up keeping to be in need to have a separate mt81xx-plat.c file,
then I believe this should have another name, so that it becomes one that
aggregates all of the very-platform-specific functions in one, instead of
having one file for each platform.

Though, it may also be possible that this file will disappear entirely:
since most of the things here will be moved around, it may become mostly
empty... but it's probably too soon to judge.

>   11 files changed, 2517 insertions(+)
>   create mode 100644 drivers/soc/mediatek/apusys/apu-config.h
>   create mode 100644 drivers/soc/mediatek/apusys/apu-ipi.c
>   create mode 100644 drivers/soc/mediatek/apusys/apu-mbox.c
>   create mode 100644 drivers/soc/mediatek/apusys/apu-mbox.h
>   create mode 100644 drivers/soc/mediatek/apusys/apu-rproc.c
>   create mode 100644 drivers/soc/mediatek/apusys/apu-sw-logger.c
>   create mode 100644 drivers/soc/mediatek/apusys/apu.h
>   create mode 100644 drivers/soc/mediatek/apusys/mt81xx-plat.c
> 

snip...

> diff --git a/drivers/soc/mediatek/apusys/apu-ipi.c b/drivers/soc/mediatek/apusys/apu-ipi.c
> new file mode 100644
> index 000000000000..547e034b3620
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/apu-ipi.c

snip...

> +int apu_ipi_init(struct platform_device *pdev, struct mtk_apu *apu)
> +{
> +	struct device *dev = apu->dev;
> +	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);
> +
> +	/* register mailbox IRQ */
> +	apu->mbox0_irq_number = platform_get_irq_byname(pdev, "mbox0_irq");
> +	dev_info(&pdev->dev, "%s: mbox0_irq = %d\n", __func__,
> +		 apu->mbox0_irq_number);
> +
> +	ret = devm_request_threaded_irq(&pdev->dev, apu->mbox0_irq_number,
> +					NULL, apu_ipi_handler, IRQF_ONESHOT,
> +					"apu_ipi", apu);

This is the mailbox interrupt... but it's handled in this driver instead of
being handler in the mailbox driver... it's a bit confusing.

Is this interrupt supposed to fire as a mailbox doorbell or..?
In that case, you should request it in the mailbox driver and register an
interrupt controller (still, in the mailbox driver) so that you can export
a sw interrupt to this one.

Or, maybe you can use notifiers to catch the mailbox message in this driver?

> +	if (ret < 0)
> +		goto remove_rpmsg_subdev;
> +
> +	apu_mbox_hw_init(apu);
> +
> +	return 0;
> +
> +remove_rpmsg_subdev:
> +	apu_remove_rpmsg_subdev(apu);
> +	apu_ipi_unregister(apu, APU_IPI_INIT);
> +
> +	return ret;
> +}

snip...

> diff --git a/drivers/soc/mediatek/apusys/apu-rproc.c b/drivers/soc/mediatek/apusys/apu-rproc.c
> new file mode 100644
> index 000000000000..e2fe63dd6cc1
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/apu-rproc.c
> @@ -0,0 +1,806 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/iommu.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/remoteproc.h>
> +#include <linux/time64.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_domain.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/sched/clock.h>
> +#include <linux/time64.h>
> +#include <linux/workqueue.h>
> +
> +#include "apu.h"
> +#include "apu-config.h"
> +#include "apu-core.h"
> +
> +/* 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 struct dentry *dbg_root;
> +
> +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);

Don't forget to always check return values.....

> +
> +	/* check if boot success */
> +	ktime_get_ts64(&begin);
> +	ret = wait_event_interruptible_timeout(run->wq,
> +					       run->signaled,
> +					       msecs_to_jiffies(10000));

#define APU_INIT_TIMEOUT_MS	10000

...but then, does it really need 10 *seconds* for that?! That's a lot of time...

> +	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;
> +}
> +


snip...


> +
> +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);

 From what I understand, the sw logger is not critical for functionality... so
it should probably be a "pluggable" instead.
Also, since that sw logger seems to be "simply" reading from a mailbox, it
should be pretty straightforward to make it so, in which case, you wouldn't
be initializing it here, but as a platform driver instead (if debugging enabled?)

> +	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;
> +}
> +

snip...

> diff --git a/drivers/soc/mediatek/apusys/apu.h b/drivers/soc/mediatek/apusys/apu.h
> new file mode 100644
> index 000000000000..5bbc46416a19
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/apu.h
> @@ -0,0 +1,256 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#ifndef APU_H
> +#define APU_H
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/rpmsg/mtk_rpmsg.h>
> +
> +#include "apu-config.h"
> +
> +/* 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 regiter */

/* apu_mbox spare register: mbox 0..6, spare 0..3 */
#define REG_MBOX_SPARE(mbox, reg) 	((0x40 + (0x100 * mbox)) + (reg * 0x4))
#define REG_MBOX0_SPARE(n)		REG_MBOX_SPARE(0, n)
#define REG_MBOX6_SPARE(n)		REG_MBOX_SPARE(6, n)

#define HOST_CONFIG_ADDR		REG_MBOX_SPARE(0, 2)

Would that be better? Granted, mbox1-5 are also accessible and perhaps used
in the future.

> +#define MBOX0_SPARE0 0x40
> +#define MBOX0_SPARE1 0x44
> +#define MBOX0_SPARE2 0x48
> +#define MBOX0_SPARE3 0x4C
> +#define MBOX6_SPARE0 0x640
> +#define MBOX6_SPARE1 0x644
> +#define MBOX6_SPARE2 0x648
> +#define MBOX6_SPARE3 0x64C
> +
> +#define HOST_CONFIG_ADDR MBOX0_SPARE2
> +
> +#define LOG_W_PTR (MBOX0_SPARE0)
> +#define LOG_R_PTR (MBOX0_SPARE1)
> +#define LOG_OV_FLG (MBOX0_SPARE3)
> +
> +/* rv setup  */
> +#define F_PRELOAD_FIRMWARE	BIT(0)
> +#define F_AUTO_BOOT		BIT(1)
> +

Use SZ_* macros where possible

> +#define TCM_SIZE (128UL * 1024UL)
> +#define CODE_BUF_SIZE (1024UL * 1024UL)
> +#define DRAM_DUMP_SIZE (CODE_BUF_SIZE - TCM_SIZE)
> +#define REG_SIZE (4UL * 151UL)
> +#define TBUF_SIZE (4UL * 32UL)
> +#define CACHE_DUMP_SIZE (37UL * 1024UL)
> +#define DRAM_OFFSET (0x00000UL)
> +#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  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 hanlder mutex */

typo

> +	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 sw_logger_seq_data {
> +	u32 w_ptr;
> +	u32 r_ptr;
> +	u32 overflow_flg;
> +	int i;
> +	int is_finished;
> +	char *data;
> +	bool startl_first;
> +};
> +
> +struct mtk_apu {
> +	struct rproc *rproc;
> +	struct device *dev;
> +	void __iomem *apu_mbox;
> +	void __iomem *md32_sysctrl;
> +	void __iomem *apu_wdt;
> +	int mbox0_irq_number;
> +	int wdt_irq_number;
> +	spinlock_t reg_lock; /* register r/w lock */
> +
> +	/* 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;
> +
> +	/* link power deive */
> +	struct device *power_dev;
> +	bool boot_done;
> +	struct work_struct pwr_work;
> +
> +	/* logger and debug */
> +	struct dentry *dbg_root;
> +	dma_addr_t handle;
> +	char *sw_log_buf;
> +	spinlock_t sw_logger_spinlock; /* logger status update lock */
> +	struct sw_logger_seq_data pseqdata_lock;
> +	struct sw_logger_seq_data *pseqdata;
> +};
> +
> +struct apu_coredump {
> +	char tcmdump[TCM_SIZE];
> +	char ramdump[DRAM_DUMP_SIZE];
> +	char regdump[REG_SIZE];
> +	char tbufdump[TBUF_SIZE];
> +	u32 cachedump[CACHE_DUMP_SIZE / sizeof(u32)];
> +} __packed;
> +
> +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)
> +int sw_logger_config_init(struct mtk_apu *apu);
> +void sw_logger_config_remove(struct mtk_apu *apu);
> +int apu_sw_logger_init(struct mtk_apu *apu);
> +void apu_sw_logger_remove(struct mtk_apu *apu);
> +#else
> +static inline int sw_logger_config_init(struct mtk_apu *apu) { return 0; }
> +static inline void sw_logger_config_remove(struct mtk_apu *apu) { }
> +static inline int apu_sw_logger_init(struct mtk_apu *apu) { return 0; }
> +static inline void apu_sw_logger_remove(struct mtk_apu *apu) { }
> +#endif
> +
> +extern const struct mtk_apu_platdata mt8192_platdata;
> +#endif /* APU_H */
> diff --git a/drivers/soc/mediatek/apusys/mt81xx-plat.c b/drivers/soc/mediatek/apusys/mt81xx-plat.c
> new file mode 100644
> index 000000000000..54f75c8d07c3
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/mt81xx-plat.c
> @@ -0,0 +1,320 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/arm-smccc.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/sched/clock.h>
> +#include <linux/soc/mediatek/mtk_sip_svc.h>
> +
> +#include "apu.h"
> +
> +static const struct apu_ipi mt81xx_ipi_attrs[APU_IPI_MAX] = {

In here, this is used only to pass it to the remoteproc "ipi" driver: it would make
sense if this was in the ipi driver, associated to a compatible like
mediatek,mt8192-ipi.

> +		   [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;
> +
> +	/* initialize IOMMU and ACP config (iommu_tr_en=1, acp_en=0) */
> +	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;
> +}

Is it expected for other SoCs to have different (or more) secure world calls?
If it is, then it may be worth it to move this to a different driver in
drivers/firmware, so that you will be able to map different/more values to
different compatibles.

Otherwise, keep it here.

> +

Thanks,
- Angelo

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC 08/13] soc: mediatek: apu: Add apusys rv driver
       [not found]     ` <0248142e6e0b55fe12f179e74b77375b386082ee.camel@mediatek.com>
@ 2021-11-26 10:07       ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 20+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-11-26 10:07 UTC (permalink / raw)
  To: Flora.Fu, Matthias Brugger, Mark Brown, Sumit Semwal
  Cc: linux-kernel, linux-arm-kernel, linux-mediatek, linux-media,
	dri-devel, linaro-mm-sig, Yong Wu, Pi-Cheng Chen

Il 26/11/21 10:25, Flora.Fu ha scritto:
> On Tue, 2021-10-26 at 17:21 +0200, AngeloGioacchino Del Regno wrote:
>> Il 23/10/21 13:14, Flora Fu ha scritto:
>>> Add driver for control APU tinysys
>>>
>>> APU integrated subsystem having MD32RV33 (MD32) that runs tinysys
>>> The tinsys is running on a micro processor in APU.
>>> Its firmware is load and boot from Kernel side. Kernel and tinysys
>>> use
>>> IPI to tx/rx messages.
>>>
>>> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
>>> ---
>>>    drivers/soc/mediatek/apusys/Makefile        |   6 +
>>>    drivers/soc/mediatek/apusys/apu-config.h    | 100 +++
>>>    drivers/soc/mediatek/apusys/apu-core.c      |   2 +
>>>    drivers/soc/mediatek/apusys/apu-core.h      |   2 +
>>>    drivers/soc/mediatek/apusys/apu-ipi.c       | 486 ++++++++++++
>>
>> I'm not sure of that, but your apu-ipi.c may be more suited to be in
>> drivers/remoteproc instead.
>>
>>>    drivers/soc/mediatek/apusys/apu-mbox.c      |  83 ++
>>
>> apu-mbox.c should go to drivers/mailbox/ and you should register it
>> with
>> the mailbox API as a mailbox controller instead of what you're
>> currently
>> doing...
>>
>>   From what I see, you have functions in there that can be indeed
>> mapped
>> to struct mbox_chan_ops .send_data and .peek_data... also your
>> function
>> apu_mbox_wait_inbox seems to be waiting on an interrupt, and such irq
>> is
>> apparently your "mbox0_irq" (as you named it in the dt).
>> In that case, you can also manage that in your drivers/mailbox/
>> driver.
>> +
>>>    drivers/soc/mediatek/apusys/apu-mbox.h      |  27 +
>>>    drivers/soc/mediatek/apusys/apu-rproc.c     | 806
>>> ++++++++++++++++++++
>>
>> The apu-rproc.c driver seems to be a good candidate to be moved away
>> from
>>
>> drivers/soc/mediatek/apusys/ - as this is indeed a remoteproc driver.
>>
>> Having it as drivers/remoteproc/mtk_apu.c seems to be a good option.
>>
> 
> For mbox/ipi/apu-rproc, we will check the kernel framework and make
> more fix on it.
> 
>>
>>>    drivers/soc/mediatek/apusys/apu-sw-logger.c | 429 +++++++++++
>>
>> This one definitely belongs here in drivers/soc/mediatek, and it's a
>> consumer
>> of the mailbox driver.
>>
>>>    drivers/soc/mediatek/apusys/apu.h           | 256 +++++++
>>>    drivers/soc/mediatek/apusys/mt81xx-plat.c   | 320 ++++++++
>>
>> If we end up keeping to be in need to have a separate mt81xx-plat.c
>> file,
>> then I believe this should have another name, so that it becomes one
>> that
>> aggregates all of the very-platform-specific functions in one,
>> instead of
>> having one file for each platform.
>>
>> Though, it may also be possible that this file will disappear
>> entirely:
>> since most of the things here will be moved around, it may become
>> mostly
>> empty... but it's probably too soon to judge.
> 
> The mt81xx-platform.c is prepared for the similiar apu desing platforms
> but some of them will have differnt operations in the functions. I will
> just keep it and we can reivew it again in the feature versions.
> 
>>
>>>    11 files changed, 2517 insertions(+)
>>>    create mode 100644 drivers/soc/mediatek/apusys/apu-config.h
>>>    create mode 100644 drivers/soc/mediatek/apusys/apu-ipi.c
>>>    create mode 100644 drivers/soc/mediatek/apusys/apu-mbox.c
>>>    create mode 100644 drivers/soc/mediatek/apusys/apu-mbox.h
>>>    create mode 100644 drivers/soc/mediatek/apusys/apu-rproc.c
>>>    create mode 100644 drivers/soc/mediatek/apusys/apu-sw-logger.c
>>>    create mode 100644 drivers/soc/mediatek/apusys/apu.h
>>>    create mode 100644 drivers/soc/mediatek/apusys/mt81xx-plat.c
>>>
>>
>> snip...
>>
>>> diff --git a/drivers/soc/mediatek/apusys/apu-ipi.c
>>> b/drivers/soc/mediatek/apusys/apu-ipi.c
>>> new file mode 100644
>>> index 000000000000..547e034b3620
>>> --- /dev/null
>>> +++ b/drivers/soc/mediatek/apusys/apu-ipi.c
>>
>> snip...
>>
>>> +int apu_ipi_init(struct platform_device *pdev, struct mtk_apu
>>> *apu)
>>> +{
>>> +	struct device *dev = apu->dev;
>>> +	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);
>>> +
>>> +	/* register mailbox IRQ */
>>> +	apu->mbox0_irq_number = platform_get_irq_byname(pdev,
>>> "mbox0_irq");
>>> +	dev_info(&pdev->dev, "%s: mbox0_irq = %d\n", __func__,
>>> +		 apu->mbox0_irq_number);
>>> +
>>> +	ret = devm_request_threaded_irq(&pdev->dev, apu-
>>>> mbox0_irq_number,
>>> +					NULL, apu_ipi_handler,
>>> IRQF_ONESHOT,
>>> +					"apu_ipi", apu);
>>
>> This is the mailbox interrupt... but it's handled in this driver
>> instead of
>> being handler in the mailbox driver... it's a bit confusing.
>>
>> Is this interrupt supposed to fire as a mailbox doorbell or..?
>> In that case, you should request it in the mailbox driver and
>> register an
>> interrupt controller (still, in the mailbox driver) so that you can
>> export
>> a sw interrupt to this one.
>>
>> Or, maybe you can use notifiers to catch the mailbox message in this
>> driver?
> 
> Ack.
> 
>>> +	if (ret < 0)
>>> +		goto remove_rpmsg_subdev;
>>> +
>>> +	apu_mbox_hw_init(apu);
>>> +
>>> +	return 0;
>>> +
>>> +remove_rpmsg_subdev:
>>> +	apu_remove_rpmsg_subdev(apu);
>>> +	apu_ipi_unregister(apu, APU_IPI_INIT);
>>> +
>>> +	return ret;
>>> +}
>>
>> snip...
>>
>>> diff --git a/drivers/soc/mediatek/apusys/apu-rproc.c
>>> b/drivers/soc/mediatek/apusys/apu-rproc.c
>>> new file mode 100644
>>> index 000000000000..e2fe63dd6cc1
>>> --- /dev/null
>>> +++ b/drivers/soc/mediatek/apusys/apu-rproc.c
>>> @@ -0,0 +1,806 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (c) 2021 MediaTek Inc.
>>> + */
>>> +
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/iommu.h>
>>> +#include <linux/irq.h>
>>> +#include <linux/module.h>
>>> +#include <linux/remoteproc.h>
>>> +#include <linux/time64.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/pm_domain.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/sched/clock.h>
>>> +#include <linux/time64.h>
>>> +#include <linux/workqueue.h>
>>> +
>>> +#include "apu.h"
>>> +#include "apu-config.h"
>>> +#include "apu-core.h"
>>> +
>>> +/* 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 struct dentry *dbg_root;
>>> +
>>> +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);
>>
>> Don't forget to always check return values.....
> 
> Ack.
>>
>>> +
>>> +	/* check if boot success */
>>> +	ktime_get_ts64(&begin);
>>> +	ret = wait_event_interruptible_timeout(run->wq,
>>> +					       run->signaled,
>>> +					       msecs_to_jiffies(10000))
>>> ;
>>
>> #define APU_INIT_TIMEOUT_MS	10000
>>
>> ...but then, does it really need 10 *seconds* for that?! That's a lot
>> of time...
>>
>>> +	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;
>>> +}
>>> +
>>
>>
>> snip...
>>
>>
>>> +
>>> +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);
>>
>>   From what I understand, the sw logger is not critical for
>> functionality... so
>> it should probably be a "pluggable" instead.
>> Also, since that sw logger seems to be "simply" reading from a
>> mailbox, it
>> should be pretty straightforward to make it so, in which case, you
>> wouldn't
>> be initializing it here, but as a platform driver instead (if
>> debugging enabled?)
> 
> It is for debug usage and I think it can be a pluggable module. But it
> still needs configurations in the boot stage to make micorprocessor
> enable the logger. I will update this part.
> 
>>
>>> +	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;
>>> +}
>>> +
>>
>> snip...
>>
>>> diff --git a/drivers/soc/mediatek/apusys/apu.h
>>> b/drivers/soc/mediatek/apusys/apu.h
>>> new file mode 100644
>>> index 000000000000..5bbc46416a19
>>> --- /dev/null
>>> +++ b/drivers/soc/mediatek/apusys/apu.h
>>> @@ -0,0 +1,256 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (c) 2021 MediaTek Inc.
>>> + */
>>> +
>>> +#ifndef APU_H
>>> +#define APU_H
>>> +#include <linux/platform_device.h>
>>> +#include <linux/spinlock.h>
>>> +#include <linux/rpmsg/mtk_rpmsg.h>
>>> +
>>> +#include "apu-config.h"
>>> +
>>> +/* 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 regiter */
>>
>> /* apu_mbox spare register: mbox 0..6, spare 0..3 */
>> #define REG_MBOX_SPARE(mbox, reg) 	((0x40 + (0x100 * mbox)) + (reg
>> * 0x4))
>> #define REG_MBOX0_SPARE(n)		REG_MBOX_SPARE(0, n)
>> #define REG_MBOX6_SPARE(n)		REG_MBOX_SPARE(6, n)
>>
>> #define HOST_CONFIG_ADDR		REG_MBOX_SPARE(0, 2)
>>
>> Would that be better? Granted, mbox1-5 are also accessible and
>> perhaps used
>> in the future.
> 
> Ack.
> 
>>> +#define MBOX0_SPARE0 0x40
>>> +#define MBOX0_SPARE1 0x44
>>> +#define MBOX0_SPARE2 0x48
>>> +#define MBOX0_SPARE3 0x4C
>>> +#define MBOX6_SPARE0 0x640
>>> +#define MBOX6_SPARE1 0x644
>>> +#define MBOX6_SPARE2 0x648
>>> +#define MBOX6_SPARE3 0x64C
>>> +
>>> +#define HOST_CONFIG_ADDR MBOX0_SPARE2
>>> +
>>> +#define LOG_W_PTR (MBOX0_SPARE0)
>>> +#define LOG_R_PTR (MBOX0_SPARE1)
>>> +#define LOG_OV_FLG (MBOX0_SPARE3)
>>> +
>>> +/* rv setup  */
>>> +#define F_PRELOAD_FIRMWARE	BIT(0)
>>> +#define F_AUTO_BOOT		BIT(1)
>>> +
>>
>> Use SZ_* macros where possible
> 
> Ack.
>>
>>> +#define TCM_SIZE (128UL * 1024UL)
>>> +#define CODE_BUF_SIZE (1024UL * 1024UL)
>>> +#define DRAM_DUMP_SIZE (CODE_BUF_SIZE - TCM_SIZE)
>>> +#define REG_SIZE (4UL * 151UL)
>>> +#define TBUF_SIZE (4UL * 32UL)
>>> +#define CACHE_DUMP_SIZE (37UL * 1024UL)
>>> +#define DRAM_OFFSET (0x00000UL)
>>> +#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  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 hanlder mutex */
>>
>> typo
>>
> Ack.
>>> +	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 sw_logger_seq_data {
>>> +	u32 w_ptr;
>>> +	u32 r_ptr;
>>> +	u32 overflow_flg;
>>> +	int i;
>>> +	int is_finished;
>>> +	char *data;
>>> +	bool startl_first;
>>> +};
>>> +
>>> +struct mtk_apu {
>>> +	struct rproc *rproc;
>>> +	struct device *dev;
>>> +	void __iomem *apu_mbox;
>>> +	void __iomem *md32_sysctrl;
>>> +	void __iomem *apu_wdt;
>>> +	int mbox0_irq_number;
>>> +	int wdt_irq_number;
>>> +	spinlock_t reg_lock; /* register r/w lock */
>>> +
>>> +	/* 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;
>>> +
>>> +	/* link power deive */
>>> +	struct device *power_dev;
>>> +	bool boot_done;
>>> +	struct work_struct pwr_work;
>>> +
>>> +	/* logger and debug */
>>> +	struct dentry *dbg_root;
>>> +	dma_addr_t handle;
>>> +	char *sw_log_buf;
>>> +	spinlock_t sw_logger_spinlock; /* logger status update lock */
>>> +	struct sw_logger_seq_data pseqdata_lock;
>>> +	struct sw_logger_seq_data *pseqdata;
>>> +};
>>> +
>>> +struct apu_coredump {
>>> +	char tcmdump[TCM_SIZE];
>>> +	char ramdump[DRAM_DUMP_SIZE];
>>> +	char regdump[REG_SIZE];
>>> +	char tbufdump[TBUF_SIZE];
>>> +	u32 cachedump[CACHE_DUMP_SIZE / sizeof(u32)];
>>> +} __packed;
>>> +
>>> +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)
>>> +int sw_logger_config_init(struct mtk_apu *apu);
>>> +void sw_logger_config_remove(struct mtk_apu *apu);
>>> +int apu_sw_logger_init(struct mtk_apu *apu);
>>> +void apu_sw_logger_remove(struct mtk_apu *apu);
>>> +#else
>>> +static inline int sw_logger_config_init(struct mtk_apu *apu) {
>>> return 0; }
>>> +static inline void sw_logger_config_remove(struct mtk_apu *apu) {
>>> }
>>> +static inline int apu_sw_logger_init(struct mtk_apu *apu) { return
>>> 0; }
>>> +static inline void apu_sw_logger_remove(struct mtk_apu *apu) { }
>>> +#endif
>>> +
>>> +extern const struct mtk_apu_platdata mt8192_platdata;
>>> +#endif /* APU_H */
>>> diff --git a/drivers/soc/mediatek/apusys/mt81xx-plat.c
>>> b/drivers/soc/mediatek/apusys/mt81xx-plat.c
>>> new file mode 100644
>>> index 000000000000..54f75c8d07c3
>>> --- /dev/null
>>> +++ b/drivers/soc/mediatek/apusys/mt81xx-plat.c
>>> @@ -0,0 +1,320 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (c) 2021 MediaTek Inc.
>>> + */
>>> +
>>> +#include <linux/arm-smccc.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/io.h>
>>> +#include <linux/iopoll.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/sched/clock.h>
>>> +#include <linux/soc/mediatek/mtk_sip_svc.h>
>>> +
>>> +#include "apu.h"
>>> +
>>> +static const struct apu_ipi mt81xx_ipi_attrs[APU_IPI_MAX] = {
>>



Hello Flora,

>> In here, this is used only to pass it to the remoteproc "ipi" driver:
>> it would make
>> sense if this was in the ipi driver, associated to a compatible like
                            ^^^^^ Sorry, I was meaning the -> rproc driver <-

>> mediatek,mt8192-ipi.
>>
> Hi, Algelo,
> I am not quite sure about the comments. Could you explain more details?
> 
> The list is used in kernel as ipi channe attributes by
> platforms(different platfrom will has differnt settings) to create ipi
> channesl by mtk-rpmsg. The ipi-attrs is add into platform data.
> 
> const struct mtk_apu_platdata mt8192_platdata = {
> 	.flags		= F_AUTO_BOOT,
> 	.ipi_attrs = mt81xx_ipi_attrs,
> 	.ops		= {
> 		.init	= NULL,
> snip.
> 		.power_init = mt81xx_apu_power_init,
> 		.power_on = mt81xx_apu_power_on,
> 		.power_off = mt81xx_apu_power_off,
> 	},
> };
> 

Reiterating: this file is very short and also it looks like the code
in here will be common (or commonizable) between multiple SoCs,
so please move everything that is in here to apu-rproc.c.

Regards,
- Angelo

>>> +		   [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;
>>> +
>>> +	/* initialize IOMMU and ACP config (iommu_tr_en=1, acp_en=0) */
>>> +	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;
>>> +}
>>
>> Is it expected for other SoCs to have different (or more) secure
>> world calls?
>> If it is, then it may be worth it to move this to a different driver
>> in
>> drivers/firmware, so that you will be able to map different/more
>> values to
>> different compatibles.
>>
>> Otherwise, keep it here.
> 
> This is for apu usage and only for apu. I will keep it here. The SMC
> receivers are also found in ATF upstream.
> https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/9709/11.
> 
> Thanks for your comments.
> 
>> - Angelo

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2021-11-26 10:09 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-23 11:13 [RFC 00/13] MediaTek MT8192 APU Flora Fu
2021-10-23 11:13 ` [RFC 01/13] dt-bindings: soc: mediatek: apusys: add mt8192 apu iommu bindings Flora Fu
2021-10-23 11:13 ` [RFC 02/13] dt-bindings: soc: mediatek: apusys: Add new document for APU power Flora Fu
2021-10-23 11:13 ` [RFC 03/13] dt-bindings: soc: mediatek: apusys: Add new document for APU tinysys Flora Fu
2021-10-23 11:14 ` [RFC 04/13] iommu/mediatek: Add APU iommu support Flora Fu
2021-10-26 15:17   ` AngeloGioacchino Del Regno
2021-10-23 11:14 ` [RFC 05/13] soc: mediatek: Add command for APU SMC call Flora Fu
2021-10-23 11:14 ` [RFC 06/13] soc: mediatek: apu: Add apu core driver Flora Fu
2021-10-23 15:49   ` Randy Dunlap
2021-10-26 15:17   ` AngeloGioacchino Del Regno
2021-10-23 11:14 ` [RFC 07/13] soc: mediatek: apu: Add apu power driver Flora Fu
2021-10-23 11:14 ` [RFC 08/13] soc: mediatek: apu: Add apusys rv driver Flora Fu
2021-10-26 15:21   ` AngeloGioacchino Del Regno
     [not found]     ` <0248142e6e0b55fe12f179e74b77375b386082ee.camel@mediatek.com>
2021-11-26 10:07       ` AngeloGioacchino Del Regno
2021-10-23 11:14 ` [RFC 09/13] soc: mediatek: apu: Add middleware driver Flora Fu
2021-10-23 11:14 ` [RFC 10/13] arm64: dts: mt8192: Add APU-IOMMU nodes Flora Fu
2021-10-23 11:14 ` [RFC 11/13] arm64: dts: mt8192: Add apu power nodes Flora Fu
2021-10-23 11:14 ` [RFC 12/13] arm64: dts: mt8192: Add apu tinysys Flora Fu
2021-10-26 15:18   ` AngeloGioacchino Del Regno
2021-10-23 11:14 ` [RFC 13/13] arm64: dts: mt8192: Add regulator for APU Flora Fu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).