18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (c) 2018 MediaTek Inc.
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/bitops.h>
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h>
178c2ecf20Sopenharmony_ci#include <linux/mailbox/mtk-cmdq-mailbox.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define CMDQ_OP_CODE_MASK		(0xff << CMDQ_OP_CODE_SHIFT)
218c2ecf20Sopenharmony_ci#define CMDQ_NUM_CMD(t)			(t->cmd_buf_size / CMDQ_INST_SIZE)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define CMDQ_CURR_IRQ_STATUS		0x10
248c2ecf20Sopenharmony_ci#define CMDQ_SYNC_TOKEN_UPDATE		0x68
258c2ecf20Sopenharmony_ci#define CMDQ_THR_SLOT_CYCLES		0x30
268c2ecf20Sopenharmony_ci#define CMDQ_THR_BASE			0x100
278c2ecf20Sopenharmony_ci#define CMDQ_THR_SIZE			0x80
288c2ecf20Sopenharmony_ci#define CMDQ_THR_WARM_RESET		0x00
298c2ecf20Sopenharmony_ci#define CMDQ_THR_ENABLE_TASK		0x04
308c2ecf20Sopenharmony_ci#define CMDQ_THR_SUSPEND_TASK		0x08
318c2ecf20Sopenharmony_ci#define CMDQ_THR_CURR_STATUS		0x0c
328c2ecf20Sopenharmony_ci#define CMDQ_THR_IRQ_STATUS		0x10
338c2ecf20Sopenharmony_ci#define CMDQ_THR_IRQ_ENABLE		0x14
348c2ecf20Sopenharmony_ci#define CMDQ_THR_CURR_ADDR		0x20
358c2ecf20Sopenharmony_ci#define CMDQ_THR_END_ADDR		0x24
368c2ecf20Sopenharmony_ci#define CMDQ_THR_WAIT_TOKEN		0x30
378c2ecf20Sopenharmony_ci#define CMDQ_THR_PRIORITY		0x40
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
408c2ecf20Sopenharmony_ci#define CMDQ_THR_ENABLED		0x1
418c2ecf20Sopenharmony_ci#define CMDQ_THR_DISABLED		0x0
428c2ecf20Sopenharmony_ci#define CMDQ_THR_SUSPEND		0x1
438c2ecf20Sopenharmony_ci#define CMDQ_THR_RESUME			0x0
448c2ecf20Sopenharmony_ci#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
458c2ecf20Sopenharmony_ci#define CMDQ_THR_DO_WARM_RESET		BIT(0)
468c2ecf20Sopenharmony_ci#define CMDQ_THR_IRQ_DONE		0x1
478c2ecf20Sopenharmony_ci#define CMDQ_THR_IRQ_ERROR		0x12
488c2ecf20Sopenharmony_ci#define CMDQ_THR_IRQ_EN			(CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
498c2ecf20Sopenharmony_ci#define CMDQ_THR_IS_WAITING		BIT(31)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define CMDQ_JUMP_BY_OFFSET		0x10000000
528c2ecf20Sopenharmony_ci#define CMDQ_JUMP_BY_PA			0x10000001
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct cmdq_thread {
558c2ecf20Sopenharmony_ci	struct mbox_chan	*chan;
568c2ecf20Sopenharmony_ci	void __iomem		*base;
578c2ecf20Sopenharmony_ci	struct list_head	task_busy_list;
588c2ecf20Sopenharmony_ci	u32			priority;
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistruct cmdq_task {
628c2ecf20Sopenharmony_ci	struct cmdq		*cmdq;
638c2ecf20Sopenharmony_ci	struct list_head	list_entry;
648c2ecf20Sopenharmony_ci	dma_addr_t		pa_base;
658c2ecf20Sopenharmony_ci	struct cmdq_thread	*thread;
668c2ecf20Sopenharmony_ci	struct cmdq_pkt		*pkt; /* the packet sent from mailbox client */
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct cmdq {
708c2ecf20Sopenharmony_ci	struct mbox_controller	mbox;
718c2ecf20Sopenharmony_ci	void __iomem		*base;
728c2ecf20Sopenharmony_ci	int			irq;
738c2ecf20Sopenharmony_ci	u32			thread_nr;
748c2ecf20Sopenharmony_ci	u32			irq_mask;
758c2ecf20Sopenharmony_ci	struct cmdq_thread	*thread;
768c2ecf20Sopenharmony_ci	struct clk		*clock;
778c2ecf20Sopenharmony_ci	bool			suspended;
788c2ecf20Sopenharmony_ci	u8			shift_pa;
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistruct gce_plat {
828c2ecf20Sopenharmony_ci	u32 thread_nr;
838c2ecf20Sopenharmony_ci	u8 shift;
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ciu8 cmdq_get_shift_pa(struct mbox_chan *chan)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return cmdq->shift_pa;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmdq_get_shift_pa);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	u32 status;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	writel(CMDQ_THR_SUSPEND, thread->base + CMDQ_THR_SUSPEND_TASK);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* If already disabled, treat as suspended successful. */
1018c2ecf20Sopenharmony_ci	if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
1028c2ecf20Sopenharmony_ci		return 0;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
1058c2ecf20Sopenharmony_ci			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
1068c2ecf20Sopenharmony_ci		dev_err(cmdq->mbox.dev, "suspend GCE thread 0x%x failed\n",
1078c2ecf20Sopenharmony_ci			(u32)(thread->base - cmdq->base));
1088c2ecf20Sopenharmony_ci		return -EFAULT;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void cmdq_thread_resume(struct cmdq_thread *thread)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	writel(CMDQ_THR_RESUME, thread->base + CMDQ_THR_SUSPEND_TASK);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic void cmdq_init(struct cmdq *cmdq)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	int i;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	WARN_ON(clk_enable(cmdq->clock) < 0);
1248c2ecf20Sopenharmony_ci	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
1258c2ecf20Sopenharmony_ci	for (i = 0; i <= CMDQ_MAX_EVENT; i++)
1268c2ecf20Sopenharmony_ci		writel(i, cmdq->base + CMDQ_SYNC_TOKEN_UPDATE);
1278c2ecf20Sopenharmony_ci	clk_disable(cmdq->clock);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	u32 warm_reset;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	writel(CMDQ_THR_DO_WARM_RESET, thread->base + CMDQ_THR_WARM_RESET);
1358c2ecf20Sopenharmony_ci	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
1368c2ecf20Sopenharmony_ci			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
1378c2ecf20Sopenharmony_ci			0, 10)) {
1388c2ecf20Sopenharmony_ci		dev_err(cmdq->mbox.dev, "reset GCE thread 0x%x failed\n",
1398c2ecf20Sopenharmony_ci			(u32)(thread->base - cmdq->base));
1408c2ecf20Sopenharmony_ci		return -EFAULT;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	cmdq_thread_reset(cmdq, thread);
1498c2ecf20Sopenharmony_ci	writel(CMDQ_THR_DISABLED, thread->base + CMDQ_THR_ENABLE_TASK);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* notify GCE to re-fetch commands by setting GCE thread PC */
1538c2ecf20Sopenharmony_cistatic void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	writel(readl(thread->base + CMDQ_THR_CURR_ADDR),
1568c2ecf20Sopenharmony_ci	       thread->base + CMDQ_THR_CURR_ADDR);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic void cmdq_task_insert_into_thread(struct cmdq_task *task)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct device *dev = task->cmdq->mbox.dev;
1628c2ecf20Sopenharmony_ci	struct cmdq_thread *thread = task->thread;
1638c2ecf20Sopenharmony_ci	struct cmdq_task *prev_task = list_last_entry(
1648c2ecf20Sopenharmony_ci			&thread->task_busy_list, typeof(*task), list_entry);
1658c2ecf20Sopenharmony_ci	u64 *prev_task_base = prev_task->pkt->va_base;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* let previous task jump to this task */
1688c2ecf20Sopenharmony_ci	dma_sync_single_for_cpu(dev, prev_task->pa_base,
1698c2ecf20Sopenharmony_ci				prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
1708c2ecf20Sopenharmony_ci	prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] =
1718c2ecf20Sopenharmony_ci		(u64)CMDQ_JUMP_BY_PA << 32 |
1728c2ecf20Sopenharmony_ci		(task->pa_base >> task->cmdq->shift_pa);
1738c2ecf20Sopenharmony_ci	dma_sync_single_for_device(dev, prev_task->pa_base,
1748c2ecf20Sopenharmony_ci				   prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	cmdq_thread_invalidate_fetched_data(thread);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic bool cmdq_thread_is_in_wfe(struct cmdq_thread *thread)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	return readl(thread->base + CMDQ_THR_WAIT_TOKEN) & CMDQ_THR_IS_WAITING;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void cmdq_task_exec_done(struct cmdq_task *task, enum cmdq_cb_status sta)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct cmdq_task_cb *cb = &task->pkt->async_cb;
1878c2ecf20Sopenharmony_ci	struct cmdq_cb_data data;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	WARN_ON(cb->cb == (cmdq_async_flush_cb)NULL);
1908c2ecf20Sopenharmony_ci	data.sta = sta;
1918c2ecf20Sopenharmony_ci	data.data = cb->data;
1928c2ecf20Sopenharmony_ci	cb->cb(data);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	list_del(&task->list_entry);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void cmdq_task_handle_error(struct cmdq_task *task)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	struct cmdq_thread *thread = task->thread;
2008c2ecf20Sopenharmony_ci	struct cmdq_task *next_task;
2018c2ecf20Sopenharmony_ci	struct cmdq *cmdq = task->cmdq;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	dev_err(cmdq->mbox.dev, "task 0x%p error\n", task);
2048c2ecf20Sopenharmony_ci	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
2058c2ecf20Sopenharmony_ci	next_task = list_first_entry_or_null(&thread->task_busy_list,
2068c2ecf20Sopenharmony_ci			struct cmdq_task, list_entry);
2078c2ecf20Sopenharmony_ci	if (next_task)
2088c2ecf20Sopenharmony_ci		writel(next_task->pa_base >> cmdq->shift_pa,
2098c2ecf20Sopenharmony_ci		       thread->base + CMDQ_THR_CURR_ADDR);
2108c2ecf20Sopenharmony_ci	cmdq_thread_resume(thread);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void cmdq_thread_irq_handler(struct cmdq *cmdq,
2148c2ecf20Sopenharmony_ci				    struct cmdq_thread *thread)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct cmdq_task *task, *tmp, *curr_task = NULL;
2178c2ecf20Sopenharmony_ci	u32 curr_pa, irq_flag, task_end_pa;
2188c2ecf20Sopenharmony_ci	bool err;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	irq_flag = readl(thread->base + CMDQ_THR_IRQ_STATUS);
2218c2ecf20Sopenharmony_ci	writel(~irq_flag, thread->base + CMDQ_THR_IRQ_STATUS);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/*
2248c2ecf20Sopenharmony_ci	 * When ISR call this function, another CPU core could run
2258c2ecf20Sopenharmony_ci	 * "release task" right before we acquire the spin lock, and thus
2268c2ecf20Sopenharmony_ci	 * reset / disable this GCE thread, so we need to check the enable
2278c2ecf20Sopenharmony_ci	 * bit of this GCE thread.
2288c2ecf20Sopenharmony_ci	 */
2298c2ecf20Sopenharmony_ci	if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
2308c2ecf20Sopenharmony_ci		return;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (irq_flag & CMDQ_THR_IRQ_ERROR)
2338c2ecf20Sopenharmony_ci		err = true;
2348c2ecf20Sopenharmony_ci	else if (irq_flag & CMDQ_THR_IRQ_DONE)
2358c2ecf20Sopenharmony_ci		err = false;
2368c2ecf20Sopenharmony_ci	else
2378c2ecf20Sopenharmony_ci		return;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) << cmdq->shift_pa;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
2428c2ecf20Sopenharmony_ci				 list_entry) {
2438c2ecf20Sopenharmony_ci		task_end_pa = task->pa_base + task->pkt->cmd_buf_size;
2448c2ecf20Sopenharmony_ci		if (curr_pa >= task->pa_base && curr_pa < task_end_pa)
2458c2ecf20Sopenharmony_ci			curr_task = task;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		if (!curr_task || curr_pa == task_end_pa - CMDQ_INST_SIZE) {
2488c2ecf20Sopenharmony_ci			cmdq_task_exec_done(task, CMDQ_CB_NORMAL);
2498c2ecf20Sopenharmony_ci			kfree(task);
2508c2ecf20Sopenharmony_ci		} else if (err) {
2518c2ecf20Sopenharmony_ci			cmdq_task_exec_done(task, CMDQ_CB_ERROR);
2528c2ecf20Sopenharmony_ci			cmdq_task_handle_error(curr_task);
2538c2ecf20Sopenharmony_ci			kfree(task);
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		if (curr_task)
2578c2ecf20Sopenharmony_ci			break;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (list_empty(&thread->task_busy_list)) {
2618c2ecf20Sopenharmony_ci		cmdq_thread_disable(cmdq, thread);
2628c2ecf20Sopenharmony_ci		clk_disable(cmdq->clock);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic irqreturn_t cmdq_irq_handler(int irq, void *dev)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct cmdq *cmdq = dev;
2698c2ecf20Sopenharmony_ci	unsigned long irq_status, flags = 0L;
2708c2ecf20Sopenharmony_ci	int bit;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS) & cmdq->irq_mask;
2738c2ecf20Sopenharmony_ci	if (!(irq_status ^ cmdq->irq_mask))
2748c2ecf20Sopenharmony_ci		return IRQ_NONE;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	for_each_clear_bit(bit, &irq_status, cmdq->thread_nr) {
2778c2ecf20Sopenharmony_ci		struct cmdq_thread *thread = &cmdq->thread[bit];
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		spin_lock_irqsave(&thread->chan->lock, flags);
2808c2ecf20Sopenharmony_ci		cmdq_thread_irq_handler(cmdq, thread);
2818c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&thread->chan->lock, flags);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int cmdq_suspend(struct device *dev)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct cmdq *cmdq = dev_get_drvdata(dev);
2908c2ecf20Sopenharmony_ci	struct cmdq_thread *thread;
2918c2ecf20Sopenharmony_ci	int i;
2928c2ecf20Sopenharmony_ci	bool task_running = false;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	cmdq->suspended = true;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	for (i = 0; i < cmdq->thread_nr; i++) {
2978c2ecf20Sopenharmony_ci		thread = &cmdq->thread[i];
2988c2ecf20Sopenharmony_ci		if (!list_empty(&thread->task_busy_list)) {
2998c2ecf20Sopenharmony_ci			task_running = true;
3008c2ecf20Sopenharmony_ci			break;
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (task_running)
3058c2ecf20Sopenharmony_ci		dev_warn(dev, "exist running task(s) in suspend\n");
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	clk_unprepare(cmdq->clock);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return 0;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int cmdq_resume(struct device *dev)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct cmdq *cmdq = dev_get_drvdata(dev);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	WARN_ON(clk_prepare(cmdq->clock) < 0);
3178c2ecf20Sopenharmony_ci	cmdq->suspended = false;
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int cmdq_remove(struct platform_device *pdev)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct cmdq *cmdq = platform_get_drvdata(pdev);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	clk_unprepare(cmdq->clock);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct cmdq_pkt *pkt = (struct cmdq_pkt *)data;
3338c2ecf20Sopenharmony_ci	struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv;
3348c2ecf20Sopenharmony_ci	struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);
3358c2ecf20Sopenharmony_ci	struct cmdq_task *task;
3368c2ecf20Sopenharmony_ci	unsigned long curr_pa, end_pa;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/* Client should not flush new tasks if suspended. */
3398c2ecf20Sopenharmony_ci	WARN_ON(cmdq->suspended);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	task = kzalloc(sizeof(*task), GFP_ATOMIC);
3428c2ecf20Sopenharmony_ci	if (!task)
3438c2ecf20Sopenharmony_ci		return -ENOMEM;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	task->cmdq = cmdq;
3468c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&task->list_entry);
3478c2ecf20Sopenharmony_ci	task->pa_base = pkt->pa_base;
3488c2ecf20Sopenharmony_ci	task->thread = thread;
3498c2ecf20Sopenharmony_ci	task->pkt = pkt;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (list_empty(&thread->task_busy_list)) {
3528c2ecf20Sopenharmony_ci		WARN_ON(clk_enable(cmdq->clock) < 0);
3538c2ecf20Sopenharmony_ci		/*
3548c2ecf20Sopenharmony_ci		 * The thread reset will clear thread related register to 0,
3558c2ecf20Sopenharmony_ci		 * including pc, end, priority, irq, suspend and enable. Thus
3568c2ecf20Sopenharmony_ci		 * set CMDQ_THR_ENABLED to CMDQ_THR_ENABLE_TASK will enable
3578c2ecf20Sopenharmony_ci		 * thread and make it running.
3588c2ecf20Sopenharmony_ci		 */
3598c2ecf20Sopenharmony_ci		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		writel(task->pa_base >> cmdq->shift_pa,
3628c2ecf20Sopenharmony_ci		       thread->base + CMDQ_THR_CURR_ADDR);
3638c2ecf20Sopenharmony_ci		writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->shift_pa,
3648c2ecf20Sopenharmony_ci		       thread->base + CMDQ_THR_END_ADDR);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		writel(thread->priority, thread->base + CMDQ_THR_PRIORITY);
3678c2ecf20Sopenharmony_ci		writel(CMDQ_THR_IRQ_EN, thread->base + CMDQ_THR_IRQ_ENABLE);
3688c2ecf20Sopenharmony_ci		writel(CMDQ_THR_ENABLED, thread->base + CMDQ_THR_ENABLE_TASK);
3698c2ecf20Sopenharmony_ci	} else {
3708c2ecf20Sopenharmony_ci		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
3718c2ecf20Sopenharmony_ci		curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) <<
3728c2ecf20Sopenharmony_ci			cmdq->shift_pa;
3738c2ecf20Sopenharmony_ci		end_pa = readl(thread->base + CMDQ_THR_END_ADDR) <<
3748c2ecf20Sopenharmony_ci			cmdq->shift_pa;
3758c2ecf20Sopenharmony_ci		/* check boundary */
3768c2ecf20Sopenharmony_ci		if (curr_pa == end_pa - CMDQ_INST_SIZE ||
3778c2ecf20Sopenharmony_ci		    curr_pa == end_pa) {
3788c2ecf20Sopenharmony_ci			/* set to this task directly */
3798c2ecf20Sopenharmony_ci			writel(task->pa_base >> cmdq->shift_pa,
3808c2ecf20Sopenharmony_ci			       thread->base + CMDQ_THR_CURR_ADDR);
3818c2ecf20Sopenharmony_ci		} else {
3828c2ecf20Sopenharmony_ci			cmdq_task_insert_into_thread(task);
3838c2ecf20Sopenharmony_ci			smp_mb(); /* modify jump before enable thread */
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci		writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->shift_pa,
3868c2ecf20Sopenharmony_ci		       thread->base + CMDQ_THR_END_ADDR);
3878c2ecf20Sopenharmony_ci		cmdq_thread_resume(thread);
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci	list_move_tail(&task->list_entry, &thread->task_busy_list);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int cmdq_mbox_startup(struct mbox_chan *chan)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic void cmdq_mbox_shutdown(struct mbox_chan *chan)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv;
4028c2ecf20Sopenharmony_ci	struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);
4038c2ecf20Sopenharmony_ci	struct cmdq_task *task, *tmp;
4048c2ecf20Sopenharmony_ci	unsigned long flags;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&thread->chan->lock, flags);
4078c2ecf20Sopenharmony_ci	if (list_empty(&thread->task_busy_list))
4088c2ecf20Sopenharmony_ci		goto done;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* make sure executed tasks have success callback */
4138c2ecf20Sopenharmony_ci	cmdq_thread_irq_handler(cmdq, thread);
4148c2ecf20Sopenharmony_ci	if (list_empty(&thread->task_busy_list))
4158c2ecf20Sopenharmony_ci		goto done;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
4188c2ecf20Sopenharmony_ci				 list_entry) {
4198c2ecf20Sopenharmony_ci		cmdq_task_exec_done(task, CMDQ_CB_ERROR);
4208c2ecf20Sopenharmony_ci		kfree(task);
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	cmdq_thread_disable(cmdq, thread);
4248c2ecf20Sopenharmony_ci	clk_disable(cmdq->clock);
4258c2ecf20Sopenharmony_cidone:
4268c2ecf20Sopenharmony_ci	/*
4278c2ecf20Sopenharmony_ci	 * The thread->task_busy_list empty means thread already disable. The
4288c2ecf20Sopenharmony_ci	 * cmdq_mbox_send_data() always reset thread which clear disable and
4298c2ecf20Sopenharmony_ci	 * suspend statue when first pkt send to channel, so there is no need
4308c2ecf20Sopenharmony_ci	 * to do any operation here, only unlock and leave.
4318c2ecf20Sopenharmony_ci	 */
4328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&thread->chan->lock, flags);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv;
4388c2ecf20Sopenharmony_ci	struct cmdq_task_cb *cb;
4398c2ecf20Sopenharmony_ci	struct cmdq_cb_data data;
4408c2ecf20Sopenharmony_ci	struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);
4418c2ecf20Sopenharmony_ci	struct cmdq_task *task, *tmp;
4428c2ecf20Sopenharmony_ci	unsigned long flags;
4438c2ecf20Sopenharmony_ci	u32 enable;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&thread->chan->lock, flags);
4468c2ecf20Sopenharmony_ci	if (list_empty(&thread->task_busy_list))
4478c2ecf20Sopenharmony_ci		goto out;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
4508c2ecf20Sopenharmony_ci	if (!cmdq_thread_is_in_wfe(thread))
4518c2ecf20Sopenharmony_ci		goto wait;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
4548c2ecf20Sopenharmony_ci				 list_entry) {
4558c2ecf20Sopenharmony_ci		cb = &task->pkt->async_cb;
4568c2ecf20Sopenharmony_ci		if (cb->cb) {
4578c2ecf20Sopenharmony_ci			data.sta = CMDQ_CB_ERROR;
4588c2ecf20Sopenharmony_ci			data.data = cb->data;
4598c2ecf20Sopenharmony_ci			cb->cb(data);
4608c2ecf20Sopenharmony_ci		}
4618c2ecf20Sopenharmony_ci		list_del(&task->list_entry);
4628c2ecf20Sopenharmony_ci		kfree(task);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	cmdq_thread_resume(thread);
4668c2ecf20Sopenharmony_ci	cmdq_thread_disable(cmdq, thread);
4678c2ecf20Sopenharmony_ci	clk_disable(cmdq->clock);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ciout:
4708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&thread->chan->lock, flags);
4718c2ecf20Sopenharmony_ci	return 0;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ciwait:
4748c2ecf20Sopenharmony_ci	cmdq_thread_resume(thread);
4758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&thread->chan->lock, flags);
4768c2ecf20Sopenharmony_ci	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_ENABLE_TASK,
4778c2ecf20Sopenharmony_ci				      enable, enable == 0, 1, timeout)) {
4788c2ecf20Sopenharmony_ci		dev_err(cmdq->mbox.dev, "Fail to wait GCE thread 0x%x done\n",
4798c2ecf20Sopenharmony_ci			(u32)(thread->base - cmdq->base));
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		return -EFAULT;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci	return 0;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops cmdq_mbox_chan_ops = {
4878c2ecf20Sopenharmony_ci	.send_data = cmdq_mbox_send_data,
4888c2ecf20Sopenharmony_ci	.startup = cmdq_mbox_startup,
4898c2ecf20Sopenharmony_ci	.shutdown = cmdq_mbox_shutdown,
4908c2ecf20Sopenharmony_ci	.flush = cmdq_mbox_flush,
4918c2ecf20Sopenharmony_ci};
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,
4948c2ecf20Sopenharmony_ci		const struct of_phandle_args *sp)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	int ind = sp->args[0];
4978c2ecf20Sopenharmony_ci	struct cmdq_thread *thread;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (ind >= mbox->num_chans)
5008c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	thread = (struct cmdq_thread *)mbox->chans[ind].con_priv;
5038c2ecf20Sopenharmony_ci	thread->priority = sp->args[1];
5048c2ecf20Sopenharmony_ci	thread->chan = &mbox->chans[ind];
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return &mbox->chans[ind];
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic int cmdq_probe(struct platform_device *pdev)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
5128c2ecf20Sopenharmony_ci	struct resource *res;
5138c2ecf20Sopenharmony_ci	struct cmdq *cmdq;
5148c2ecf20Sopenharmony_ci	int err, i;
5158c2ecf20Sopenharmony_ci	struct gce_plat *plat_data;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
5188c2ecf20Sopenharmony_ci	if (!cmdq)
5198c2ecf20Sopenharmony_ci		return -ENOMEM;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5228c2ecf20Sopenharmony_ci	cmdq->base = devm_ioremap_resource(dev, res);
5238c2ecf20Sopenharmony_ci	if (IS_ERR(cmdq->base)) {
5248c2ecf20Sopenharmony_ci		dev_err(dev, "failed to ioremap gce\n");
5258c2ecf20Sopenharmony_ci		return PTR_ERR(cmdq->base);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	cmdq->irq = platform_get_irq(pdev, 0);
5298c2ecf20Sopenharmony_ci	if (cmdq->irq < 0)
5308c2ecf20Sopenharmony_ci		return cmdq->irq;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	plat_data = (struct gce_plat *)of_device_get_match_data(dev);
5338c2ecf20Sopenharmony_ci	if (!plat_data) {
5348c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get match data\n");
5358c2ecf20Sopenharmony_ci		return -EINVAL;
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	cmdq->thread_nr = plat_data->thread_nr;
5398c2ecf20Sopenharmony_ci	cmdq->shift_pa = plat_data->shift;
5408c2ecf20Sopenharmony_ci	cmdq->irq_mask = GENMASK(cmdq->thread_nr - 1, 0);
5418c2ecf20Sopenharmony_ci	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
5428c2ecf20Sopenharmony_ci			       "mtk_cmdq", cmdq);
5438c2ecf20Sopenharmony_ci	if (err < 0) {
5448c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register ISR (%d)\n", err);
5458c2ecf20Sopenharmony_ci		return err;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
5498c2ecf20Sopenharmony_ci		dev, cmdq->base, cmdq->irq);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	cmdq->clock = devm_clk_get(dev, "gce");
5528c2ecf20Sopenharmony_ci	if (IS_ERR(cmdq->clock)) {
5538c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get gce clk\n");
5548c2ecf20Sopenharmony_ci		return PTR_ERR(cmdq->clock);
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	cmdq->mbox.dev = dev;
5588c2ecf20Sopenharmony_ci	cmdq->mbox.chans = devm_kcalloc(dev, cmdq->thread_nr,
5598c2ecf20Sopenharmony_ci					sizeof(*cmdq->mbox.chans), GFP_KERNEL);
5608c2ecf20Sopenharmony_ci	if (!cmdq->mbox.chans)
5618c2ecf20Sopenharmony_ci		return -ENOMEM;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	cmdq->mbox.num_chans = cmdq->thread_nr;
5648c2ecf20Sopenharmony_ci	cmdq->mbox.ops = &cmdq_mbox_chan_ops;
5658c2ecf20Sopenharmony_ci	cmdq->mbox.of_xlate = cmdq_xlate;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* make use of TXDONE_BY_ACK */
5688c2ecf20Sopenharmony_ci	cmdq->mbox.txdone_irq = false;
5698c2ecf20Sopenharmony_ci	cmdq->mbox.txdone_poll = false;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	cmdq->thread = devm_kcalloc(dev, cmdq->thread_nr,
5728c2ecf20Sopenharmony_ci					sizeof(*cmdq->thread), GFP_KERNEL);
5738c2ecf20Sopenharmony_ci	if (!cmdq->thread)
5748c2ecf20Sopenharmony_ci		return -ENOMEM;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	for (i = 0; i < cmdq->thread_nr; i++) {
5778c2ecf20Sopenharmony_ci		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
5788c2ecf20Sopenharmony_ci				CMDQ_THR_SIZE * i;
5798c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
5808c2ecf20Sopenharmony_ci		cmdq->mbox.chans[i].con_priv = (void *)&cmdq->thread[i];
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	err = devm_mbox_controller_register(dev, &cmdq->mbox);
5848c2ecf20Sopenharmony_ci	if (err < 0) {
5858c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register mailbox: %d\n", err);
5868c2ecf20Sopenharmony_ci		return err;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, cmdq);
5908c2ecf20Sopenharmony_ci	WARN_ON(clk_prepare(cmdq->clock) < 0);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	cmdq_init(cmdq);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	return 0;
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic const struct dev_pm_ops cmdq_pm_ops = {
5988c2ecf20Sopenharmony_ci	.suspend = cmdq_suspend,
5998c2ecf20Sopenharmony_ci	.resume = cmdq_resume,
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic const struct gce_plat gce_plat_v2 = {.thread_nr = 16};
6038c2ecf20Sopenharmony_cistatic const struct gce_plat gce_plat_v3 = {.thread_nr = 24};
6048c2ecf20Sopenharmony_cistatic const struct gce_plat gce_plat_v4 = {.thread_nr = 24, .shift = 3};
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic const struct of_device_id cmdq_of_ids[] = {
6078c2ecf20Sopenharmony_ci	{.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_v2},
6088c2ecf20Sopenharmony_ci	{.compatible = "mediatek,mt8183-gce", .data = (void *)&gce_plat_v3},
6098c2ecf20Sopenharmony_ci	{.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_v4},
6108c2ecf20Sopenharmony_ci	{}
6118c2ecf20Sopenharmony_ci};
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic struct platform_driver cmdq_drv = {
6148c2ecf20Sopenharmony_ci	.probe = cmdq_probe,
6158c2ecf20Sopenharmony_ci	.remove = cmdq_remove,
6168c2ecf20Sopenharmony_ci	.driver = {
6178c2ecf20Sopenharmony_ci		.name = "mtk_cmdq",
6188c2ecf20Sopenharmony_ci		.pm = &cmdq_pm_ops,
6198c2ecf20Sopenharmony_ci		.of_match_table = cmdq_of_ids,
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci};
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic int __init cmdq_drv_init(void)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	return platform_driver_register(&cmdq_drv);
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic void __exit cmdq_drv_exit(void)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	platform_driver_unregister(&cmdq_drv);
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_cisubsys_initcall(cmdq_drv_init);
6348c2ecf20Sopenharmony_cimodule_exit(cmdq_drv_exit);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
637