1 // SPDX-License-Identifier: GPL-2.0
3 // Copyright (c) 2018 MediaTek Inc.
5 #include <linux/completion.h>
6 #include <linux/errno.h>
7 #include <linux/dma-mapping.h>
8 #include <linux/module.h>
9 #include <linux/mailbox_controller.h>
10 #include <linux/soc/mediatek/mtk-cmdq.h>
12 #define CMDQ_WRITE_ENABLE_MASK BIT(0)
13 #define CMDQ_POLL_ENABLE_MASK BIT(0)
14 #define CMDQ_EOC_IRQ_EN BIT(0)
16 struct cmdq_instruction
{
29 int cmdq_dev_get_client_reg(struct device
*dev
,
30 struct cmdq_client_reg
*client_reg
, int idx
)
32 struct of_phandle_args spec
;
38 err
= of_parse_phandle_with_fixed_args(dev
->of_node
,
39 "mediatek,gce-client-reg",
43 "error %d can't parse gce-client-reg property (%d)",
49 client_reg
->subsys
= (u8
)spec
.args
[0];
50 client_reg
->offset
= (u16
)spec
.args
[1];
51 client_reg
->size
= (u16
)spec
.args
[2];
56 EXPORT_SYMBOL(cmdq_dev_get_client_reg
);
58 static void cmdq_client_timeout(struct timer_list
*t
)
60 struct cmdq_client
*client
= from_timer(client
, t
, timer
);
62 dev_err(client
->client
.dev
, "cmdq timeout!\n");
65 struct cmdq_client
*cmdq_mbox_create(struct device
*dev
, int index
, u32 timeout
)
67 struct cmdq_client
*client
;
69 client
= kzalloc(sizeof(*client
), GFP_KERNEL
);
71 return (struct cmdq_client
*)-ENOMEM
;
73 client
->timeout_ms
= timeout
;
74 if (timeout
!= CMDQ_NO_TIMEOUT
) {
75 spin_lock_init(&client
->lock
);
76 timer_setup(&client
->timer
, cmdq_client_timeout
, 0);
79 client
->client
.dev
= dev
;
80 client
->client
.tx_block
= false;
81 client
->client
.knows_txdone
= true;
82 client
->chan
= mbox_request_channel(&client
->client
, index
);
84 if (IS_ERR(client
->chan
)) {
87 dev_err(dev
, "failed to request channel\n");
88 err
= PTR_ERR(client
->chan
);
96 EXPORT_SYMBOL(cmdq_mbox_create
);
98 void cmdq_mbox_destroy(struct cmdq_client
*client
)
100 if (client
->timeout_ms
!= CMDQ_NO_TIMEOUT
) {
101 spin_lock(&client
->lock
);
102 del_timer_sync(&client
->timer
);
103 spin_unlock(&client
->lock
);
105 mbox_free_channel(client
->chan
);
108 EXPORT_SYMBOL(cmdq_mbox_destroy
);
110 struct cmdq_pkt
*cmdq_pkt_create(struct cmdq_client
*client
, size_t size
)
112 struct cmdq_pkt
*pkt
;
116 pkt
= kzalloc(sizeof(*pkt
), GFP_KERNEL
);
118 return ERR_PTR(-ENOMEM
);
119 pkt
->va_base
= kzalloc(size
, GFP_KERNEL
);
122 return ERR_PTR(-ENOMEM
);
124 pkt
->buf_size
= size
;
125 pkt
->cl
= (void *)client
;
127 dev
= client
->chan
->mbox
->dev
;
128 dma_addr
= dma_map_single(dev
, pkt
->va_base
, pkt
->buf_size
,
130 if (dma_mapping_error(dev
, dma_addr
)) {
131 dev_err(dev
, "dma map failed, size=%u\n", (u32
)(u64
)size
);
134 return ERR_PTR(-ENOMEM
);
137 pkt
->pa_base
= dma_addr
;
141 EXPORT_SYMBOL(cmdq_pkt_create
);
143 void cmdq_pkt_destroy(struct cmdq_pkt
*pkt
)
145 struct cmdq_client
*client
= (struct cmdq_client
*)pkt
->cl
;
147 dma_unmap_single(client
->chan
->mbox
->dev
, pkt
->pa_base
, pkt
->buf_size
,
152 EXPORT_SYMBOL(cmdq_pkt_destroy
);
154 static int cmdq_pkt_append_command(struct cmdq_pkt
*pkt
,
155 struct cmdq_instruction inst
)
157 struct cmdq_instruction
*cmd_ptr
;
159 if (unlikely(pkt
->cmd_buf_size
+ CMDQ_INST_SIZE
> pkt
->buf_size
)) {
161 * In the case of allocated buffer size (pkt->buf_size) is used
162 * up, the real required size (pkt->cmdq_buf_size) is still
163 * increased, so that the user knows how much memory should be
164 * ultimately allocated after appending all commands and
165 * flushing the command packet. Therefor, the user can call
166 * cmdq_pkt_create() again with the real required buffer size.
168 pkt
->cmd_buf_size
+= CMDQ_INST_SIZE
;
169 WARN_ONCE(1, "%s: buffer size %u is too small !\n",
170 __func__
, (u32
)pkt
->buf_size
);
174 cmd_ptr
= pkt
->va_base
+ pkt
->cmd_buf_size
;
176 pkt
->cmd_buf_size
+= CMDQ_INST_SIZE
;
181 int cmdq_pkt_write(struct cmdq_pkt
*pkt
, u8 subsys
, u16 offset
, u32 value
)
183 struct cmdq_instruction inst
;
185 inst
.op
= CMDQ_CODE_WRITE
;
187 inst
.offset
= offset
;
188 inst
.subsys
= subsys
;
190 return cmdq_pkt_append_command(pkt
, inst
);
192 EXPORT_SYMBOL(cmdq_pkt_write
);
194 int cmdq_pkt_write_mask(struct cmdq_pkt
*pkt
, u8 subsys
,
195 u16 offset
, u32 value
, u32 mask
)
197 struct cmdq_instruction inst
= { {0} };
198 u16 offset_mask
= offset
;
201 if (mask
!= 0xffffffff) {
202 inst
.op
= CMDQ_CODE_MASK
;
204 err
= cmdq_pkt_append_command(pkt
, inst
);
208 offset_mask
|= CMDQ_WRITE_ENABLE_MASK
;
210 err
= cmdq_pkt_write(pkt
, subsys
, offset_mask
, value
);
214 EXPORT_SYMBOL(cmdq_pkt_write_mask
);
216 int cmdq_pkt_wfe(struct cmdq_pkt
*pkt
, u16 event
)
218 struct cmdq_instruction inst
= { {0} };
220 if (event
>= CMDQ_MAX_EVENT
)
223 inst
.op
= CMDQ_CODE_WFE
;
224 inst
.value
= CMDQ_WFE_OPTION
;
227 return cmdq_pkt_append_command(pkt
, inst
);
229 EXPORT_SYMBOL(cmdq_pkt_wfe
);
231 int cmdq_pkt_clear_event(struct cmdq_pkt
*pkt
, u16 event
)
233 struct cmdq_instruction inst
= { {0} };
235 if (event
>= CMDQ_MAX_EVENT
)
238 inst
.op
= CMDQ_CODE_WFE
;
239 inst
.value
= CMDQ_WFE_UPDATE
;
242 return cmdq_pkt_append_command(pkt
, inst
);
244 EXPORT_SYMBOL(cmdq_pkt_clear_event
);
246 int cmdq_pkt_poll(struct cmdq_pkt
*pkt
, u8 subsys
,
247 u16 offset
, u32 value
)
249 struct cmdq_instruction inst
= { {0} };
252 inst
.op
= CMDQ_CODE_POLL
;
254 inst
.offset
= offset
;
255 inst
.subsys
= subsys
;
256 err
= cmdq_pkt_append_command(pkt
, inst
);
260 EXPORT_SYMBOL(cmdq_pkt_poll
);
262 int cmdq_pkt_poll_mask(struct cmdq_pkt
*pkt
, u8 subsys
,
263 u16 offset
, u32 value
, u32 mask
)
265 struct cmdq_instruction inst
= { {0} };
268 inst
.op
= CMDQ_CODE_MASK
;
270 err
= cmdq_pkt_append_command(pkt
, inst
);
274 offset
= offset
| CMDQ_POLL_ENABLE_MASK
;
275 err
= cmdq_pkt_poll(pkt
, subsys
, offset
, value
);
279 EXPORT_SYMBOL(cmdq_pkt_poll_mask
);
281 static int cmdq_pkt_finalize(struct cmdq_pkt
*pkt
)
283 struct cmdq_instruction inst
= { {0} };
286 /* insert EOC and generate IRQ for each command iteration */
287 inst
.op
= CMDQ_CODE_EOC
;
288 inst
.value
= CMDQ_EOC_IRQ_EN
;
289 err
= cmdq_pkt_append_command(pkt
, inst
);
294 inst
.op
= CMDQ_CODE_JUMP
;
295 inst
.value
= CMDQ_JUMP_PASS
;
296 err
= cmdq_pkt_append_command(pkt
, inst
);
301 static void cmdq_pkt_flush_async_cb(struct cmdq_cb_data data
)
303 struct cmdq_pkt
*pkt
= (struct cmdq_pkt
*)data
.data
;
304 struct cmdq_task_cb
*cb
= &pkt
->cb
;
305 struct cmdq_client
*client
= (struct cmdq_client
*)pkt
->cl
;
307 if (client
->timeout_ms
!= CMDQ_NO_TIMEOUT
) {
308 unsigned long flags
= 0;
310 spin_lock_irqsave(&client
->lock
, flags
);
311 if (--client
->pkt_cnt
== 0)
312 del_timer(&client
->timer
);
314 mod_timer(&client
->timer
, jiffies
+
315 msecs_to_jiffies(client
->timeout_ms
));
316 spin_unlock_irqrestore(&client
->lock
, flags
);
319 dma_sync_single_for_cpu(client
->chan
->mbox
->dev
, pkt
->pa_base
,
320 pkt
->cmd_buf_size
, DMA_TO_DEVICE
);
322 data
.data
= cb
->data
;
327 int cmdq_pkt_flush_async(struct cmdq_pkt
*pkt
, cmdq_async_flush_cb cb
,
331 unsigned long flags
= 0;
332 struct cmdq_client
*client
= (struct cmdq_client
*)pkt
->cl
;
334 err
= cmdq_pkt_finalize(pkt
);
340 pkt
->async_cb
.cb
= cmdq_pkt_flush_async_cb
;
341 pkt
->async_cb
.data
= pkt
;
343 dma_sync_single_for_device(client
->chan
->mbox
->dev
, pkt
->pa_base
,
344 pkt
->cmd_buf_size
, DMA_TO_DEVICE
);
346 if (client
->timeout_ms
!= CMDQ_NO_TIMEOUT
) {
347 spin_lock_irqsave(&client
->lock
, flags
);
348 if (client
->pkt_cnt
++ == 0)
349 mod_timer(&client
->timer
, jiffies
+
350 msecs_to_jiffies(client
->timeout_ms
));
351 spin_unlock_irqrestore(&client
->lock
, flags
);
354 mbox_send_message(client
->chan
, pkt
);
355 /* We can send next packet immediately, so just call txdone. */
356 mbox_client_txdone(client
->chan
, 0);
360 EXPORT_SYMBOL(cmdq_pkt_flush_async
);
362 struct cmdq_flush_completion
{
363 struct completion cmplt
;
367 static void cmdq_pkt_flush_cb(struct cmdq_cb_data data
)
369 struct cmdq_flush_completion
*cmplt
;
371 cmplt
= (struct cmdq_flush_completion
*)data
.data
;
372 if (data
.sta
!= CMDQ_CB_NORMAL
)
376 complete(&cmplt
->cmplt
);
379 int cmdq_pkt_flush(struct cmdq_pkt
*pkt
)
381 struct cmdq_flush_completion cmplt
;
384 init_completion(&cmplt
.cmplt
);
385 err
= cmdq_pkt_flush_async(pkt
, cmdq_pkt_flush_cb
, &cmplt
);
388 wait_for_completion(&cmplt
.cmplt
);
390 return cmplt
.err
? -EFAULT
: 0;
392 EXPORT_SYMBOL(cmdq_pkt_flush
);
394 MODULE_LICENSE("GPL v2");