soc: mediatek: mtk-cmdq: Add cmdq_pkt_acquire_event() function
[linux-2.6-block.git] / drivers / soc / mediatek / mtk-cmdq-helper.c
CommitLineData
576f1b4b
HW
1// SPDX-License-Identifier: GPL-2.0
2//
3// Copyright (c) 2018 MediaTek Inc.
4
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>
d01e0aec 10#include <linux/of.h>
576f1b4b
HW
11#include <linux/soc/mediatek/mtk-cmdq.h>
12
576f1b4b 13#define CMDQ_WRITE_ENABLE_MASK BIT(0)
b2ff2356 14#define CMDQ_POLL_ENABLE_MASK BIT(0)
400e2fa8
JJL
15/* dedicate the last GPR_R15 to assign the register address to be poll */
16#define CMDQ_POLL_ADDR_GPR (15)
576f1b4b 17#define CMDQ_EOC_IRQ_EN BIT(0)
613c2e2c 18#define CMDQ_REG_TYPE 1
ed4d5ab1
CKH
19#define CMDQ_JUMP_RELATIVE 0
20#define CMDQ_JUMP_ABSOLUTE 1
576f1b4b 21
5c8b718c
BH
22struct cmdq_instruction {
23 union {
24 u32 value;
25 u32 mask;
5f6e560c
DYH
26 struct {
27 u16 arg_c;
28 u16 src_reg;
29 };
5c8b718c
BH
30 };
31 union {
32 u16 offset;
33 u16 event;
613c2e2c
DYH
34 u16 reg_dst;
35 };
36 union {
37 u8 subsys;
38 struct {
39 u8 sop:5;
40 u8 arg_c_t:1;
41 u8 src_t:1;
42 u8 dst_t:1;
43 };
5c8b718c 44 };
5c8b718c
BH
45 u8 op;
46};
47
d412f18c
BH
48int cmdq_dev_get_client_reg(struct device *dev,
49 struct cmdq_client_reg *client_reg, int idx)
50{
51 struct of_phandle_args spec;
52 int err;
53
54 if (!client_reg)
55 return -ENOENT;
56
57 err = of_parse_phandle_with_fixed_args(dev->of_node,
58 "mediatek,gce-client-reg",
59 3, idx, &spec);
60 if (err < 0) {
61 dev_err(dev,
62 "error %d can't parse gce-client-reg property (%d)",
63 err, idx);
64
65 return err;
66 }
67
68 client_reg->subsys = (u8)spec.args[0];
69 client_reg->offset = (u16)spec.args[1];
70 client_reg->size = (u16)spec.args[2];
71 of_node_put(spec.np);
72
73 return 0;
74}
75EXPORT_SYMBOL(cmdq_dev_get_client_reg);
76
a69dcdfc 77struct cmdq_client *cmdq_mbox_create(struct device *dev, int index)
576f1b4b
HW
78{
79 struct cmdq_client *client;
80
81 client = kzalloc(sizeof(*client), GFP_KERNEL);
82 if (!client)
83 return (struct cmdq_client *)-ENOMEM;
84
576f1b4b
HW
85 client->client.dev = dev;
86 client->client.tx_block = false;
ce35e21d 87 client->client.knows_txdone = true;
576f1b4b
HW
88 client->chan = mbox_request_channel(&client->client, index);
89
90 if (IS_ERR(client->chan)) {
91 long err;
92
93 dev_err(dev, "failed to request channel\n");
94 err = PTR_ERR(client->chan);
95 kfree(client);
96
97 return ERR_PTR(err);
98 }
99
100 return client;
101}
102EXPORT_SYMBOL(cmdq_mbox_create);
103
104void cmdq_mbox_destroy(struct cmdq_client *client)
105{
576f1b4b
HW
106 mbox_free_channel(client->chan);
107 kfree(client);
108}
109EXPORT_SYMBOL(cmdq_mbox_destroy);
110
b81b2d55 111int cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t size)
576f1b4b 112{
576f1b4b
HW
113 struct device *dev;
114 dma_addr_t dma_addr;
115
576f1b4b 116 pkt->va_base = kzalloc(size, GFP_KERNEL);
b81b2d55
CKH
117 if (!pkt->va_base)
118 return -ENOMEM;
119
576f1b4b 120 pkt->buf_size = size;
576f1b4b
HW
121
122 dev = client->chan->mbox->dev;
123 dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size,
124 DMA_TO_DEVICE);
125 if (dma_mapping_error(dev, dma_addr)) {
126 dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size);
127 kfree(pkt->va_base);
b81b2d55 128 return -ENOMEM;
576f1b4b
HW
129 }
130
131 pkt->pa_base = dma_addr;
132
b81b2d55 133 return 0;
576f1b4b
HW
134}
135EXPORT_SYMBOL(cmdq_pkt_create);
136
b81b2d55 137void cmdq_pkt_destroy(struct cmdq_client *client, struct cmdq_pkt *pkt)
576f1b4b 138{
576f1b4b
HW
139 dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size,
140 DMA_TO_DEVICE);
141 kfree(pkt->va_base);
576f1b4b
HW
142}
143EXPORT_SYMBOL(cmdq_pkt_destroy);
144
5c8b718c
BH
145static int cmdq_pkt_append_command(struct cmdq_pkt *pkt,
146 struct cmdq_instruction inst)
576f1b4b 147{
5c8b718c 148 struct cmdq_instruction *cmd_ptr;
576f1b4b
HW
149
150 if (unlikely(pkt->cmd_buf_size + CMDQ_INST_SIZE > pkt->buf_size)) {
151 /*
152 * In the case of allocated buffer size (pkt->buf_size) is used
153 * up, the real required size (pkt->cmdq_buf_size) is still
154 * increased, so that the user knows how much memory should be
155 * ultimately allocated after appending all commands and
156 * flushing the command packet. Therefor, the user can call
157 * cmdq_pkt_create() again with the real required buffer size.
158 */
159 pkt->cmd_buf_size += CMDQ_INST_SIZE;
160 WARN_ONCE(1, "%s: buffer size %u is too small !\n",
161 __func__, (u32)pkt->buf_size);
162 return -ENOMEM;
163 }
5c8b718c 164
576f1b4b 165 cmd_ptr = pkt->va_base + pkt->cmd_buf_size;
5c8b718c 166 *cmd_ptr = inst;
576f1b4b
HW
167 pkt->cmd_buf_size += CMDQ_INST_SIZE;
168
169 return 0;
170}
171
556030f0 172int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value)
576f1b4b 173{
5c8b718c 174 struct cmdq_instruction inst;
576f1b4b 175
5c8b718c
BH
176 inst.op = CMDQ_CODE_WRITE;
177 inst.value = value;
178 inst.offset = offset;
179 inst.subsys = subsys;
576f1b4b 180
5c8b718c 181 return cmdq_pkt_append_command(pkt, inst);
576f1b4b
HW
182}
183EXPORT_SYMBOL(cmdq_pkt_write);
184
556030f0
BH
185int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
186 u16 offset, u32 value, u32 mask)
576f1b4b 187{
5c8b718c
BH
188 struct cmdq_instruction inst = { {0} };
189 u16 offset_mask = offset;
01d1b408 190 int err;
576f1b4b
HW
191
192 if (mask != 0xffffffff) {
5c8b718c
BH
193 inst.op = CMDQ_CODE_MASK;
194 inst.mask = ~mask;
195 err = cmdq_pkt_append_command(pkt, inst);
01d1b408
BH
196 if (err < 0)
197 return err;
198
576f1b4b
HW
199 offset_mask |= CMDQ_WRITE_ENABLE_MASK;
200 }
01d1b408 201 err = cmdq_pkt_write(pkt, subsys, offset_mask, value);
576f1b4b
HW
202
203 return err;
204}
205EXPORT_SYMBOL(cmdq_pkt_write_mask);
206
d3b04aab
DYH
207int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low,
208 u16 reg_idx)
209{
210 struct cmdq_instruction inst = {};
211
212 inst.op = CMDQ_CODE_READ_S;
213 inst.dst_t = CMDQ_REG_TYPE;
214 inst.sop = high_addr_reg_idx;
215 inst.reg_dst = reg_idx;
216 inst.src_reg = addr_low;
217
218 return cmdq_pkt_append_command(pkt, inst);
219}
220EXPORT_SYMBOL(cmdq_pkt_read_s);
221
5f6e560c
DYH
222int cmdq_pkt_write_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx,
223 u16 addr_low, u16 src_reg_idx)
224{
225 struct cmdq_instruction inst = {};
226
227 inst.op = CMDQ_CODE_WRITE_S;
228 inst.src_t = CMDQ_REG_TYPE;
229 inst.sop = high_addr_reg_idx;
230 inst.offset = addr_low;
231 inst.src_reg = src_reg_idx;
232
233 return cmdq_pkt_append_command(pkt, inst);
234}
235EXPORT_SYMBOL(cmdq_pkt_write_s);
236
11c7842d
DYH
237int cmdq_pkt_write_s_mask(struct cmdq_pkt *pkt, u16 high_addr_reg_idx,
238 u16 addr_low, u16 src_reg_idx, u32 mask)
239{
240 struct cmdq_instruction inst = {};
241 int err;
242
243 inst.op = CMDQ_CODE_MASK;
244 inst.mask = ~mask;
245 err = cmdq_pkt_append_command(pkt, inst);
246 if (err < 0)
247 return err;
248
249 inst.mask = 0;
250 inst.op = CMDQ_CODE_WRITE_S_MASK;
251 inst.src_t = CMDQ_REG_TYPE;
252 inst.sop = high_addr_reg_idx;
253 inst.offset = addr_low;
254 inst.src_reg = src_reg_idx;
255
256 return cmdq_pkt_append_command(pkt, inst);
257}
258EXPORT_SYMBOL(cmdq_pkt_write_s_mask);
259
1af43fce
DYH
260int cmdq_pkt_write_s_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx,
261 u16 addr_low, u32 value)
262{
263 struct cmdq_instruction inst = {};
264
265 inst.op = CMDQ_CODE_WRITE_S;
266 inst.sop = high_addr_reg_idx;
267 inst.offset = addr_low;
268 inst.value = value;
269
270 return cmdq_pkt_append_command(pkt, inst);
271}
272EXPORT_SYMBOL(cmdq_pkt_write_s_value);
273
88a2ffc4
DYH
274int cmdq_pkt_write_s_mask_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx,
275 u16 addr_low, u32 value, u32 mask)
276{
277 struct cmdq_instruction inst = {};
278 int err;
279
280 inst.op = CMDQ_CODE_MASK;
281 inst.mask = ~mask;
282 err = cmdq_pkt_append_command(pkt, inst);
283 if (err < 0)
284 return err;
285
286 inst.op = CMDQ_CODE_WRITE_S_MASK;
287 inst.sop = high_addr_reg_idx;
288 inst.offset = addr_low;
289 inst.value = value;
290
291 return cmdq_pkt_append_command(pkt, inst);
292}
293EXPORT_SYMBOL(cmdq_pkt_write_s_mask_value);
294
263801f8
JJL
295int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_addr)
296{
297 const u16 high_addr_reg_idx = CMDQ_THR_SPR_IDX0;
298 const u16 value_reg_idx = CMDQ_THR_SPR_IDX1;
299 int ret;
300
301 /* read the value of src_addr into high_addr_reg_idx */
302 ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(src_addr));
303 if (ret < 0)
304 return ret;
305 ret = cmdq_pkt_read_s(pkt, high_addr_reg_idx, CMDQ_ADDR_LOW(src_addr), value_reg_idx);
306 if (ret < 0)
307 return ret;
308
309 /* write the value of value_reg_idx into dst_addr */
310 ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(dst_addr));
311 if (ret < 0)
312 return ret;
313 ret = cmdq_pkt_write_s(pkt, high_addr_reg_idx, CMDQ_ADDR_LOW(dst_addr), value_reg_idx);
314 if (ret < 0)
315 return ret;
316
317 return 0;
318}
319EXPORT_SYMBOL(cmdq_pkt_mem_move);
320
23c22299 321int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event, bool clear)
576f1b4b 322{
5c8b718c 323 struct cmdq_instruction inst = { {0} };
23c22299 324 u32 clear_option = clear ? CMDQ_WFE_UPDATE : 0;
576f1b4b
HW
325
326 if (event >= CMDQ_MAX_EVENT)
327 return -EINVAL;
328
5c8b718c 329 inst.op = CMDQ_CODE_WFE;
23c22299 330 inst.value = CMDQ_WFE_OPTION | clear_option;
5c8b718c 331 inst.event = event;
576f1b4b 332
5c8b718c 333 return cmdq_pkt_append_command(pkt, inst);
576f1b4b
HW
334}
335EXPORT_SYMBOL(cmdq_pkt_wfe);
336
69ff6833
JJL
337int cmdq_pkt_acquire_event(struct cmdq_pkt *pkt, u16 event)
338{
339 struct cmdq_instruction inst = {};
340
341 if (event >= CMDQ_MAX_EVENT)
342 return -EINVAL;
343
344 inst.op = CMDQ_CODE_WFE;
345 inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE | CMDQ_WFE_WAIT;
346 inst.event = event;
347
348 return cmdq_pkt_append_command(pkt, inst);
349}
350EXPORT_SYMBOL(cmdq_pkt_acquire_event);
351
556030f0 352int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event)
576f1b4b 353{
5c8b718c
BH
354 struct cmdq_instruction inst = { {0} };
355
576f1b4b
HW
356 if (event >= CMDQ_MAX_EVENT)
357 return -EINVAL;
358
5c8b718c
BH
359 inst.op = CMDQ_CODE_WFE;
360 inst.value = CMDQ_WFE_UPDATE;
361 inst.event = event;
362
363 return cmdq_pkt_append_command(pkt, inst);
576f1b4b
HW
364}
365EXPORT_SYMBOL(cmdq_pkt_clear_event);
366
7de796ca
DYH
367int cmdq_pkt_set_event(struct cmdq_pkt *pkt, u16 event)
368{
369 struct cmdq_instruction inst = {};
370
371 if (event >= CMDQ_MAX_EVENT)
372 return -EINVAL;
373
374 inst.op = CMDQ_CODE_WFE;
375 inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE;
376 inst.event = event;
377
378 return cmdq_pkt_append_command(pkt, inst);
379}
380EXPORT_SYMBOL(cmdq_pkt_set_event);
381
b2ff2356
BH
382int cmdq_pkt_poll(struct cmdq_pkt *pkt, u8 subsys,
383 u16 offset, u32 value)
384{
385 struct cmdq_instruction inst = { {0} };
386 int err;
387
388 inst.op = CMDQ_CODE_POLL;
389 inst.value = value;
390 inst.offset = offset;
391 inst.subsys = subsys;
392 err = cmdq_pkt_append_command(pkt, inst);
393
394 return err;
395}
396EXPORT_SYMBOL(cmdq_pkt_poll);
397
398int cmdq_pkt_poll_mask(struct cmdq_pkt *pkt, u8 subsys,
399 u16 offset, u32 value, u32 mask)
400{
401 struct cmdq_instruction inst = { {0} };
402 int err;
403
404 inst.op = CMDQ_CODE_MASK;
405 inst.mask = ~mask;
406 err = cmdq_pkt_append_command(pkt, inst);
407 if (err < 0)
408 return err;
409
410 offset = offset | CMDQ_POLL_ENABLE_MASK;
411 err = cmdq_pkt_poll(pkt, subsys, offset, value);
412
413 return err;
414}
415EXPORT_SYMBOL(cmdq_pkt_poll_mask);
416
400e2fa8
JJL
417int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mask)
418{
419 struct cmdq_instruction inst = { {0} };
420 u8 use_mask = 0;
421 int ret;
422
423 /*
424 * Append an MASK instruction to set the mask for following POLL instruction
425 * which enables use_mask bit.
426 */
427 if (mask != GENMASK(31, 0)) {
428 inst.op = CMDQ_CODE_MASK;
429 inst.mask = ~mask;
430 ret = cmdq_pkt_append_command(pkt, inst);
431 if (ret < 0)
432 return ret;
433 use_mask = CMDQ_POLL_ENABLE_MASK;
434 }
435
436 /*
437 * POLL is an legacy operation in GCE and it does not support SPR and CMDQ_CODE_LOGIC,
438 * so it can not use cmdq_pkt_assign to keep polling register address to SPR.
439 * If user wants to poll a register address which doesn't have a subsys id,
440 * user needs to use GPR and CMDQ_CODE_MASK to move polling register address to GPR.
441 */
442 inst.op = CMDQ_CODE_MASK;
443 inst.dst_t = CMDQ_REG_TYPE;
444 inst.sop = CMDQ_POLL_ADDR_GPR;
445 inst.value = addr;
446 ret = cmdq_pkt_append_command(pkt, inst);
447 if (ret < 0)
448 return ret;
449
450 /* Append POLL instruction to poll the register address assign to GPR previously. */
451 inst.op = CMDQ_CODE_POLL;
452 inst.dst_t = CMDQ_REG_TYPE;
453 inst.sop = CMDQ_POLL_ADDR_GPR;
454 inst.offset = use_mask;
455 inst.value = value;
456 ret = cmdq_pkt_append_command(pkt, inst);
457 if (ret < 0)
458 return ret;
459
460 return 0;
461}
462EXPORT_SYMBOL(cmdq_pkt_poll_addr);
463
613c2e2c
DYH
464int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value)
465{
466 struct cmdq_instruction inst = {};
467
468 inst.op = CMDQ_CODE_LOGIC;
469 inst.dst_t = CMDQ_REG_TYPE;
470 inst.reg_dst = reg_idx;
471 inst.value = value;
472 return cmdq_pkt_append_command(pkt, inst);
473}
474EXPORT_SYMBOL(cmdq_pkt_assign);
475
7218be3b 476int cmdq_pkt_jump_abs(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa)
946f1792
DYH
477{
478 struct cmdq_instruction inst = {};
479
480 inst.op = CMDQ_CODE_JUMP;
ed4d5ab1 481 inst.offset = CMDQ_JUMP_ABSOLUTE;
ade17653 482 inst.value = addr >> shift_pa;
946f1792
DYH
483 return cmdq_pkt_append_command(pkt, inst);
484}
7218be3b 485EXPORT_SYMBOL(cmdq_pkt_jump_abs);
946f1792 486
698cdcb1
CKH
487int cmdq_pkt_jump_rel(struct cmdq_pkt *pkt, s32 offset, u8 shift_pa)
488{
489 struct cmdq_instruction inst = { {0} };
490
491 inst.op = CMDQ_CODE_JUMP;
492 inst.value = (u32)offset >> shift_pa;
493 return cmdq_pkt_append_command(pkt, inst);
494}
495EXPORT_SYMBOL(cmdq_pkt_jump_rel);
496
3d86ced9
CKH
497int cmdq_pkt_eoc(struct cmdq_pkt *pkt)
498{
499 struct cmdq_instruction inst = { {0} };
500
501 inst.op = CMDQ_CODE_EOC;
502 inst.value = CMDQ_EOC_IRQ_EN;
503 return cmdq_pkt_append_command(pkt, inst);
504}
505EXPORT_SYMBOL(cmdq_pkt_eoc);
506
99581858 507int cmdq_pkt_finalize(struct cmdq_pkt *pkt)
576f1b4b 508{
5c8b718c 509 struct cmdq_instruction inst = { {0} };
576f1b4b
HW
510 int err;
511
512 /* insert EOC and generate IRQ for each command iteration */
5c8b718c
BH
513 inst.op = CMDQ_CODE_EOC;
514 inst.value = CMDQ_EOC_IRQ_EN;
515 err = cmdq_pkt_append_command(pkt, inst);
01d1b408
BH
516 if (err < 0)
517 return err;
576f1b4b
HW
518
519 /* JUMP to end */
5c8b718c 520 inst.op = CMDQ_CODE_JUMP;
2b8cf383
DYH
521 inst.value = CMDQ_JUMP_PASS >>
522 cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan);
5c8b718c 523 err = cmdq_pkt_append_command(pkt, inst);
576f1b4b
HW
524
525 return err;
526}
99581858 527EXPORT_SYMBOL(cmdq_pkt_finalize);
576f1b4b 528
576f1b4b 529MODULE_LICENSE("GPL v2");