]> Git Repo - linux.git/blob - drivers/soc/mediatek/mtk-cmdq-helper.c
x86/kaslr: Expose and use the end of the physical memory address space
[linux.git] / drivers / soc / mediatek / mtk-cmdq-helper.c
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");
This page took 0.06283 seconds and 4 git commands to generate.