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