diff mbox series

[RFC,08/13] soc: mediatek: apu: Add apusys rv driver

Message ID 20211023111409.30463-9-flora.fu@mediatek.com (mailing list archive)
State New, archived
Headers show
Series MediaTek MT8192 APU | expand

Commit Message

Flora Fu Oct. 23, 2021, 11:14 a.m. UTC
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

Comments

AngeloGioacchino Del Regno Oct. 26, 2021, 3:21 p.m. UTC | #1
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
Flora Fu Nov. 26, 2021, 9:25 a.m. UTC | #2
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] = {
> 
> 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.
> 
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,
	},
};

> > +		   [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
AngeloGioacchino Del Regno Nov. 26, 2021, 10:07 a.m. UTC | #3
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
diff mbox series

Patch

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