aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHoulong Wei <houlong.wei@mediatek.com>2018-07-24 21:26:40 -0400
committerJassi Brar <jaswinder.singh@linaro.org>2018-08-03 10:22:14 -0400
commit623a6143a845bd485b00ba684f0ccef11835edab (patch)
tree4d19effb909e5027a0396cd73790675055f05d93
parent1c82407aa302774b24bf619e56973aa97cbf25bd (diff)
mailbox: mediatek: Add Mediatek CMDQ driver
This patch is first version of Mediatek Command Queue(CMDQ) driver. The CMDQ is used to help write registers with critical time limitation, such as updating display configuration during the vblank. It controls Global Command Engine (GCE) hardware to achieve this requirement. Currently, CMDQ only supports display related hardwares, but we expect it can be extended to other hardwares for future requirements. Signed-off-by: Houlong Wei <houlong.wei@mediatek.com> Signed-off-by: HS Liao <hs.liao@mediatek.com> Signed-off-by: CK Hu <ck.hu@mediatek.com> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
-rw-r--r--drivers/mailbox/Kconfig10
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/mtk-cmdq-mailbox.c569
-rw-r--r--include/linux/mailbox/mtk-cmdq-mailbox.h77
4 files changed, 658 insertions, 0 deletions
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index e63d29a95e76..2bbabc907add 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -189,4 +189,14 @@ config STM32_IPCC
189 Mailbox implementation for STMicroelectonics STM32 family chips 189 Mailbox implementation for STMicroelectonics STM32 family chips
190 with hardware for Inter-Processor Communication Controller (IPCC) 190 with hardware for Inter-Processor Communication Controller (IPCC)
191 between processors. Say Y here if you want to have this support. 191 between processors. Say Y here if you want to have this support.
192
193config MTK_CMDQ_MBOX
194 tristate "MediaTek CMDQ Mailbox Support"
195 depends on ARCH_MEDIATEK || COMPILE_TEST
196 select MTK_INFRACFG
197 help
198 Say yes here to add support for the MediaTek Command Queue (CMDQ)
199 mailbox driver. The CMDQ is used to help read/write registers with
200 critical time limitation, such as updating display configuration
201 during the vblank.
192endif 202endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 4d501bea7863..4b0080480661 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -40,3 +40,5 @@ obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o
40obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o 40obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o
41 41
42obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o 42obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o
43
44obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
new file mode 100644
index 000000000000..6f92c5ee12f7
--- /dev/null
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -0,0 +1,569 @@
1// SPDX-License-Identifier: GPL-2.0
2//
3// Copyright (c) 2018 MediaTek Inc.
4
5#include <linux/bitops.h>
6#include <linux/clk.h>
7#include <linux/clk-provider.h>
8#include <linux/dma-mapping.h>
9#include <linux/errno.h>
10#include <linux/interrupt.h>
11#include <linux/iopoll.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/mailbox_controller.h>
16#include <linux/mailbox/mtk-cmdq-mailbox.h>
17#include <linux/of_device.h>
18
19#define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
20#define CMDQ_IRQ_MASK 0xffff
21#define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE)
22
23#define CMDQ_CURR_IRQ_STATUS 0x10
24#define CMDQ_THR_SLOT_CYCLES 0x30
25#define CMDQ_THR_BASE 0x100
26#define CMDQ_THR_SIZE 0x80
27#define CMDQ_THR_WARM_RESET 0x00
28#define CMDQ_THR_ENABLE_TASK 0x04
29#define CMDQ_THR_SUSPEND_TASK 0x08
30#define CMDQ_THR_CURR_STATUS 0x0c
31#define CMDQ_THR_IRQ_STATUS 0x10
32#define CMDQ_THR_IRQ_ENABLE 0x14
33#define CMDQ_THR_CURR_ADDR 0x20
34#define CMDQ_THR_END_ADDR 0x24
35#define CMDQ_THR_WAIT_TOKEN 0x30
36#define CMDQ_THR_PRIORITY 0x40
37
38#define CMDQ_THR_ACTIVE_SLOT_CYCLES 0x3200
39#define CMDQ_THR_ENABLED 0x1
40#define CMDQ_THR_DISABLED 0x0
41#define CMDQ_THR_SUSPEND 0x1
42#define CMDQ_THR_RESUME 0x0
43#define CMDQ_THR_STATUS_SUSPENDED BIT(1)
44#define CMDQ_THR_DO_WARM_RESET BIT(0)
45#define CMDQ_THR_IRQ_DONE 0x1
46#define CMDQ_THR_IRQ_ERROR 0x12
47#define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
48#define CMDQ_THR_IS_WAITING BIT(31)
49
50#define CMDQ_JUMP_BY_OFFSET 0x10000000
51#define CMDQ_JUMP_BY_PA 0x10000001
52
53struct cmdq_thread {
54 struct mbox_chan *chan;
55 void __iomem *base;
56 struct list_head task_busy_list;
57 u32 priority;
58 bool atomic_exec;
59};
60
61struct cmdq_task {
62 struct cmdq *cmdq;
63 struct list_head list_entry;
64 dma_addr_t pa_base;
65 struct cmdq_thread *thread;
66 struct cmdq_pkt *pkt; /* the packet sent from mailbox client */
67};
68
69struct cmdq {
70 struct mbox_controller mbox;
71 void __iomem *base;
72 u32 irq;
73 u32 thread_nr;
74 struct cmdq_thread *thread;
75 struct clk *clock;
76 bool suspended;
77};
78
79static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
80{
81 u32 status;
82
83 writel(CMDQ_THR_SUSPEND, thread->base + CMDQ_THR_SUSPEND_TASK);
84
85 /* If already disabled, treat as suspended successful. */
86 if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
87 return 0;
88
89 if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
90 status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
91 dev_err(cmdq->mbox.dev, "suspend GCE thread 0x%x failed\n",
92 (u32)(thread->base - cmdq->base));
93 return -EFAULT;
94 }
95
96 return 0;
97}
98
99static void cmdq_thread_resume(struct cmdq_thread *thread)
100{
101 writel(CMDQ_THR_RESUME, thread->base + CMDQ_THR_SUSPEND_TASK);
102}
103
104static void cmdq_init(struct cmdq *cmdq)
105{
106 WARN_ON(clk_enable(cmdq->clock) < 0);
107 writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
108 clk_disable(cmdq->clock);
109}
110
111static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
112{
113 u32 warm_reset;
114
115 writel(CMDQ_THR_DO_WARM_RESET, thread->base + CMDQ_THR_WARM_RESET);
116 if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
117 warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
118 0, 10)) {
119 dev_err(cmdq->mbox.dev, "reset GCE thread 0x%x failed\n",
120 (u32)(thread->base - cmdq->base));
121 return -EFAULT;
122 }
123
124 return 0;
125}
126
127static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
128{
129 cmdq_thread_reset(cmdq, thread);
130 writel(CMDQ_THR_DISABLED, thread->base + CMDQ_THR_ENABLE_TASK);
131}
132
133/* notify GCE to re-fetch commands by setting GCE thread PC */
134static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
135{
136 writel(readl(thread->base + CMDQ_THR_CURR_ADDR),
137 thread->base + CMDQ_THR_CURR_ADDR);
138}
139
140static void cmdq_task_insert_into_thread(struct cmdq_task *task)
141{
142 struct device *dev = task->cmdq->mbox.dev;
143 struct cmdq_thread *thread = task->thread;
144 struct cmdq_task *prev_task = list_last_entry(
145 &thread->task_busy_list, typeof(*task), list_entry);
146 u64 *prev_task_base = prev_task->pkt->va_base;
147
148 /* let previous task jump to this task */
149 dma_sync_single_for_cpu(dev, prev_task->pa_base,
150 prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
151 prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] =
152 (u64)CMDQ_JUMP_BY_PA << 32 | task->pa_base;
153 dma_sync_single_for_device(dev, prev_task->pa_base,
154 prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
155
156 cmdq_thread_invalidate_fetched_data(thread);
157}
158
159static bool cmdq_command_is_wfe(u64 cmd)
160{
161 u64 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
162 u64 wfe_op = (u64)(CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT) << 32;
163 u64 wfe_mask = (u64)CMDQ_OP_CODE_MASK << 32 | 0xffffffff;
164
165 return ((cmd & wfe_mask) == (wfe_op | wfe_option));
166}
167
168/* we assume tasks in the same display GCE thread are waiting the same event. */
169static void cmdq_task_remove_wfe(struct cmdq_task *task)
170{