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>
11 #include <linux/soc/mediatek/mtk-cmdq.h>
13 #define CMDQ_WRITE_ENABLE_MASK BIT(0)
14 #define CMDQ_POLL_ENABLE_MASK BIT(0)
15 /* dedicate the last GPR_R15 to assign the register address to be poll */
16 #define CMDQ_POLL_ADDR_GPR (15)
17 #define CMDQ_EOC_IRQ_EN BIT(0)
18 #define CMDQ_IMMEDIATE_VALUE 0
19 #define CMDQ_REG_TYPE 1
20 #define CMDQ_JUMP_RELATIVE 0
21 #define CMDQ_JUMP_ABSOLUTE 1
23 struct cmdq_instruction
{
49 static inline u8
cmdq_operand_get_type(struct cmdq_operand
*op
)
51 return op
->reg
? CMDQ_REG_TYPE
: CMDQ_IMMEDIATE_VALUE
;
54 static inline u16
cmdq_operand_get_idx_value(struct cmdq_operand
*op
)
56 return op
->reg
? op
->idx
: op
->value
;
59 int cmdq_dev_get_client_reg(struct device
*dev
,
60 struct cmdq_client_reg
*client_reg
, int idx
)
62 struct of_phandle_args spec
;
68 err
= of_parse_phandle_with_fixed_args(dev
->of_node
,
69 "mediatek,gce-client-reg",
73 "error %d can't parse gce-client-reg property (%d)",
79 client_reg
->subsys
= (u8
)spec
.args
[0];
80 client_reg
->offset
= (u16
)spec
.args
[1];
81 client_reg
->size
= (u16
)spec
.args
[2];
86 EXPORT_SYMBOL(cmdq_dev_get_client_reg
);
88 struct cmdq_client
*cmdq_mbox_create(struct device
*dev
, int index
)
90 struct cmdq_client
*client
;
92 client
= kzalloc(sizeof(*client
), GFP_KERNEL
);
94 return (struct cmdq_client
*)-ENOMEM
;
96 client
->client
.dev
= dev
;
97 client
->client
.tx_block
= false;
98 client
->client
.knows_txdone
= true;
99 client
->chan
= mbox_request_channel(&client
->client
, index
);
101 if (IS_ERR(client
->chan
)) {
104 dev_err(dev
, "failed to request channel\n");
105 err
= PTR_ERR(client
->chan
);
113 EXPORT_SYMBOL(cmdq_mbox_create
);
115 void cmdq_mbox_destroy(struct cmdq_client
*client
)
117 mbox_free_channel(client
->chan
);
120 EXPORT_SYMBOL(cmdq_mbox_destroy
);
122 int cmdq_pkt_create(struct cmdq_client
*client
, struct cmdq_pkt
*pkt
, size_t size
)
127 pkt
->va_base
= kzalloc(size
, GFP_KERNEL
);
131 pkt
->buf_size
= size
;
133 dev
= client
->chan
->mbox
->dev
;
134 dma_addr
= dma_map_single(dev
, pkt
->va_base
, pkt
->buf_size
,
136 if (dma_mapping_error(dev
, dma_addr
)) {
137 dev_err(dev
, "dma map failed, size=%u\n", (u32
)(u64
)size
);
142 pkt
->pa_base
= dma_addr
;
146 EXPORT_SYMBOL(cmdq_pkt_create
);
148 void cmdq_pkt_destroy(struct cmdq_client
*client
, struct cmdq_pkt
*pkt
)
150 dma_unmap_single(client
->chan
->mbox
->dev
, pkt
->pa_base
, pkt
->buf_size
,
154 EXPORT_SYMBOL(cmdq_pkt_destroy
);
156 static int cmdq_pkt_append_command(struct cmdq_pkt
*pkt
,
157 struct cmdq_instruction inst
)
159 struct cmdq_instruction
*cmd_ptr
;
161 if (unlikely(pkt
->cmd_buf_size
+ CMDQ_INST_SIZE
> pkt
->buf_size
)) {
163 * In the case of allocated buffer size (pkt->buf_size) is used
164 * up, the real required size (pkt->cmdq_buf_size) is still
165 * increased, so that the user knows how much memory should be
166 * ultimately allocated after appending all commands and
167 * flushing the command packet. Therefor, the user can call
168 * cmdq_pkt_create() again with the real required buffer size.
170 pkt
->cmd_buf_size
+= CMDQ_INST_SIZE
;
171 WARN_ONCE(1, "%s: buffer size %u is too small !\n",
172 __func__
, (u32
)pkt
->buf_size
);
176 cmd_ptr
= pkt
->va_base
+ pkt
->cmd_buf_size
;
178 pkt
->cmd_buf_size
+= CMDQ_INST_SIZE
;
183 static int cmdq_pkt_mask(struct cmdq_pkt
*pkt
, u32 mask
)
185 struct cmdq_instruction inst
= {
186 .op
= CMDQ_CODE_MASK
,
189 return cmdq_pkt_append_command(pkt
, inst
);
192 int cmdq_pkt_write(struct cmdq_pkt
*pkt
, u8 subsys
, u16 offset
, u32 value
)
194 struct cmdq_instruction inst
= {
195 .op
= CMDQ_CODE_WRITE
,
200 return cmdq_pkt_append_command(pkt
, inst
);
202 EXPORT_SYMBOL(cmdq_pkt_write
);
204 int cmdq_pkt_write_mask(struct cmdq_pkt
*pkt
, u8 subsys
,
205 u16 offset
, u32 value
, u32 mask
)
207 u16 offset_mask
= offset
;
210 if (mask
!= GENMASK(31, 0)) {
211 err
= cmdq_pkt_mask(pkt
, mask
);
215 offset_mask
|= CMDQ_WRITE_ENABLE_MASK
;
217 return cmdq_pkt_write(pkt
, subsys
, offset_mask
, value
);
219 EXPORT_SYMBOL(cmdq_pkt_write_mask
);
221 int cmdq_pkt_read_s(struct cmdq_pkt
*pkt
, u16 high_addr_reg_idx
, u16 addr_low
,
224 struct cmdq_instruction inst
= {
225 .op
= CMDQ_CODE_READ_S
,
226 .dst_t
= CMDQ_REG_TYPE
,
227 .sop
= high_addr_reg_idx
,
231 return cmdq_pkt_append_command(pkt
, inst
);
233 EXPORT_SYMBOL(cmdq_pkt_read_s
);
235 int cmdq_pkt_write_s(struct cmdq_pkt
*pkt
, u16 high_addr_reg_idx
,
236 u16 addr_low
, u16 src_reg_idx
)
238 struct cmdq_instruction inst
= {
239 .op
= CMDQ_CODE_WRITE_S
,
240 .src_t
= CMDQ_REG_TYPE
,
241 .sop
= high_addr_reg_idx
,
243 .src_reg
= src_reg_idx
245 return cmdq_pkt_append_command(pkt
, inst
);
247 EXPORT_SYMBOL(cmdq_pkt_write_s
);
249 int cmdq_pkt_write_s_mask(struct cmdq_pkt
*pkt
, u16 high_addr_reg_idx
,
250 u16 addr_low
, u16 src_reg_idx
, u32 mask
)
252 struct cmdq_instruction inst
= {
253 .op
= CMDQ_CODE_WRITE_S_MASK
,
254 .src_t
= CMDQ_REG_TYPE
,
255 .sop
= high_addr_reg_idx
,
257 .src_reg
= src_reg_idx
,
261 err
= cmdq_pkt_mask(pkt
, mask
);
265 return cmdq_pkt_append_command(pkt
, inst
);
267 EXPORT_SYMBOL(cmdq_pkt_write_s_mask
);
269 int cmdq_pkt_write_s_value(struct cmdq_pkt
*pkt
, u8 high_addr_reg_idx
,
270 u16 addr_low
, u32 value
)
272 struct cmdq_instruction inst
= {
273 .op
= CMDQ_CODE_WRITE_S
,
274 .sop
= high_addr_reg_idx
,
278 return cmdq_pkt_append_command(pkt
, inst
);
280 EXPORT_SYMBOL(cmdq_pkt_write_s_value
);
282 int cmdq_pkt_write_s_mask_value(struct cmdq_pkt
*pkt
, u8 high_addr_reg_idx
,
283 u16 addr_low
, u32 value
, u32 mask
)
285 struct cmdq_instruction inst
= {
286 .op
= CMDQ_CODE_WRITE_S_MASK
,
287 .sop
= high_addr_reg_idx
,
293 err
= cmdq_pkt_mask(pkt
, mask
);
297 return cmdq_pkt_append_command(pkt
, inst
);
299 EXPORT_SYMBOL(cmdq_pkt_write_s_mask_value
);
301 int cmdq_pkt_mem_move(struct cmdq_pkt
*pkt
, dma_addr_t src_addr
, dma_addr_t dst_addr
)
303 const u16 high_addr_reg_idx
= CMDQ_THR_SPR_IDX0
;
304 const u16 value_reg_idx
= CMDQ_THR_SPR_IDX1
;
307 /* read the value of src_addr into high_addr_reg_idx */
308 ret
= cmdq_pkt_assign(pkt
, high_addr_reg_idx
, CMDQ_ADDR_HIGH(src_addr
));
311 ret
= cmdq_pkt_read_s(pkt
, high_addr_reg_idx
, CMDQ_ADDR_LOW(src_addr
), value_reg_idx
);
315 /* write the value of value_reg_idx into dst_addr */
316 ret
= cmdq_pkt_assign(pkt
, high_addr_reg_idx
, CMDQ_ADDR_HIGH(dst_addr
));
319 ret
= cmdq_pkt_write_s(pkt
, high_addr_reg_idx
, CMDQ_ADDR_LOW(dst_addr
), value_reg_idx
);
325 EXPORT_SYMBOL(cmdq_pkt_mem_move
);
327 int cmdq_pkt_wfe(struct cmdq_pkt
*pkt
, u16 event
, bool clear
)
329 u32 clear_option
= clear
? CMDQ_WFE_UPDATE
: 0;
330 struct cmdq_instruction inst
= {
332 .value
= CMDQ_WFE_OPTION
| clear_option
,
336 if (event
>= CMDQ_MAX_EVENT
)
339 return cmdq_pkt_append_command(pkt
, inst
);
341 EXPORT_SYMBOL(cmdq_pkt_wfe
);
343 int cmdq_pkt_acquire_event(struct cmdq_pkt
*pkt
, u16 event
)
345 struct cmdq_instruction inst
= {
347 .value
= CMDQ_WFE_UPDATE
| CMDQ_WFE_UPDATE_VALUE
| CMDQ_WFE_WAIT
,
351 if (event
>= CMDQ_MAX_EVENT
)
354 return cmdq_pkt_append_command(pkt
, inst
);
356 EXPORT_SYMBOL(cmdq_pkt_acquire_event
);
358 int cmdq_pkt_clear_event(struct cmdq_pkt
*pkt
, u16 event
)
360 struct cmdq_instruction inst
= {
362 .value
= CMDQ_WFE_UPDATE
,
366 if (event
>= CMDQ_MAX_EVENT
)
369 return cmdq_pkt_append_command(pkt
, inst
);
371 EXPORT_SYMBOL(cmdq_pkt_clear_event
);
373 int cmdq_pkt_set_event(struct cmdq_pkt
*pkt
, u16 event
)
375 struct cmdq_instruction inst
= {
377 .value
= CMDQ_WFE_UPDATE
| CMDQ_WFE_UPDATE_VALUE
,
381 if (event
>= CMDQ_MAX_EVENT
)
384 return cmdq_pkt_append_command(pkt
, inst
);
386 EXPORT_SYMBOL(cmdq_pkt_set_event
);
388 int cmdq_pkt_poll(struct cmdq_pkt
*pkt
, u8 subsys
,
389 u16 offset
, u32 value
)
391 struct cmdq_instruction inst
= {
392 .op
= CMDQ_CODE_POLL
,
397 return cmdq_pkt_append_command(pkt
, inst
);
399 EXPORT_SYMBOL(cmdq_pkt_poll
);
401 int cmdq_pkt_poll_mask(struct cmdq_pkt
*pkt
, u8 subsys
,
402 u16 offset
, u32 value
, u32 mask
)
406 err
= cmdq_pkt_mask(pkt
, mask
);
410 offset
= offset
| CMDQ_POLL_ENABLE_MASK
;
411 return cmdq_pkt_poll(pkt
, subsys
, offset
, value
);
413 EXPORT_SYMBOL(cmdq_pkt_poll_mask
);
415 int cmdq_pkt_poll_addr(struct cmdq_pkt
*pkt
, dma_addr_t addr
, u32 value
, u32 mask
)
417 struct cmdq_instruction inst
= { {0} };
422 * Append an MASK instruction to set the mask for following POLL instruction
423 * which enables use_mask bit.
425 if (mask
!= GENMASK(31, 0)) {
426 ret
= cmdq_pkt_mask(pkt
, mask
);
429 use_mask
= CMDQ_POLL_ENABLE_MASK
;
433 * POLL is an legacy operation in GCE and it does not support SPR and CMDQ_CODE_LOGIC,
434 * so it can not use cmdq_pkt_assign to keep polling register address to SPR.
435 * If user wants to poll a register address which doesn't have a subsys id,
436 * user needs to use GPR and CMDQ_CODE_MASK to move polling register address to GPR.
438 inst
.op
= CMDQ_CODE_MASK
;
439 inst
.dst_t
= CMDQ_REG_TYPE
;
440 inst
.sop
= CMDQ_POLL_ADDR_GPR
;
442 ret
= cmdq_pkt_append_command(pkt
, inst
);
446 /* Append POLL instruction to poll the register address assign to GPR previously. */
447 inst
.op
= CMDQ_CODE_POLL
;
448 inst
.dst_t
= CMDQ_REG_TYPE
;
449 inst
.sop
= CMDQ_POLL_ADDR_GPR
;
450 inst
.offset
= use_mask
;
452 ret
= cmdq_pkt_append_command(pkt
, inst
);
458 EXPORT_SYMBOL(cmdq_pkt_poll_addr
);
460 int cmdq_pkt_logic_command(struct cmdq_pkt
*pkt
, u16 result_reg_idx
,
461 struct cmdq_operand
*left_operand
,
462 enum cmdq_logic_op s_op
,
463 struct cmdq_operand
*right_operand
)
465 struct cmdq_instruction inst
;
467 if (!left_operand
|| !right_operand
|| s_op
>= CMDQ_LOGIC_MAX
)
471 inst
.op
= CMDQ_CODE_LOGIC
;
472 inst
.dst_t
= CMDQ_REG_TYPE
;
473 inst
.src_t
= cmdq_operand_get_type(left_operand
);
474 inst
.arg_c_t
= cmdq_operand_get_type(right_operand
);
476 inst
.reg_dst
= result_reg_idx
;
477 inst
.src_reg
= cmdq_operand_get_idx_value(left_operand
);
478 inst
.arg_c
= cmdq_operand_get_idx_value(right_operand
);
480 return cmdq_pkt_append_command(pkt
, inst
);
482 EXPORT_SYMBOL(cmdq_pkt_logic_command
);
484 int cmdq_pkt_assign(struct cmdq_pkt
*pkt
, u16 reg_idx
, u32 value
)
486 struct cmdq_instruction inst
= {
487 .op
= CMDQ_CODE_LOGIC
,
488 .dst_t
= CMDQ_REG_TYPE
,
492 return cmdq_pkt_append_command(pkt
, inst
);
494 EXPORT_SYMBOL(cmdq_pkt_assign
);
496 int cmdq_pkt_jump_abs(struct cmdq_pkt
*pkt
, dma_addr_t addr
, u8 shift_pa
)
498 struct cmdq_instruction inst
= {
499 .op
= CMDQ_CODE_JUMP
,
500 .offset
= CMDQ_JUMP_ABSOLUTE
,
501 .value
= addr
>> shift_pa
503 return cmdq_pkt_append_command(pkt
, inst
);
505 EXPORT_SYMBOL(cmdq_pkt_jump_abs
);
507 int cmdq_pkt_jump_rel(struct cmdq_pkt
*pkt
, s32 offset
, u8 shift_pa
)
509 struct cmdq_instruction inst
= {
510 .op
= CMDQ_CODE_JUMP
,
511 .value
= (u32
)offset
>> shift_pa
513 return cmdq_pkt_append_command(pkt
, inst
);
515 EXPORT_SYMBOL(cmdq_pkt_jump_rel
);
517 int cmdq_pkt_eoc(struct cmdq_pkt
*pkt
)
519 struct cmdq_instruction inst
= {
521 .value
= CMDQ_EOC_IRQ_EN
523 return cmdq_pkt_append_command(pkt
, inst
);
525 EXPORT_SYMBOL(cmdq_pkt_eoc
);
527 int cmdq_pkt_finalize(struct cmdq_pkt
*pkt
)
529 struct cmdq_instruction inst
= { {0} };
532 /* insert EOC and generate IRQ for each command iteration */
533 err
= cmdq_pkt_eoc(pkt
);
538 inst
.op
= CMDQ_CODE_JUMP
;
539 inst
.value
= CMDQ_JUMP_PASS
>>
540 cmdq_get_shift_pa(((struct cmdq_client
*)pkt
->cl
)->chan
);
541 return cmdq_pkt_append_command(pkt
, inst
);
543 EXPORT_SYMBOL(cmdq_pkt_finalize
);
545 MODULE_DESCRIPTION("MediaTek Command Queue (CMDQ) driver");
546 MODULE_LICENSE("GPL v2");