master
c 551 lines 15.5 KB
Raw
1 /*
2 * Kendryte K230 GSDMA
3 *
4 * Copyright (c) 2026 Tao Ding <dingtao0430@163.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 *
8 * GSDMA includes SDMA (System Direct Memory Access, K230 TRM section 10.2) and
9 * GDMA (Graphic Direct Memory Access, K230 TRM section 2.5.2).
10 */
11
12 #include "qemu/osdep.h"
13 #include "qemu/bitops.h"
14 #include "qemu/log.h"
15 #include "qemu/module.h"
16 #include "migration/vmstate.h"
17 #include "system/address-spaces.h"
18 #include "hw/dma/k230_gsdma.h"
19 #include "hw/core/irq.h"
20 #include "trace.h"
21
22 static void k230_gsdma_update_irq(K230GSDMAState *s)
23 {
24 qemu_set_irq(s->irq, !!(s->dma_int_stat & ~s->dma_int_mask));
25 }
26
27 static bool k230_gsdma_decomp_enabled(K230GSDMAState *s)
28 {
29 /* Only sdma channel 0 has decomp enable flag */
30 return !!(s->channels[0].cfg & K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN);
31 }
32
33 static bool k230_gsdma_read_sdma_llt(hwaddr addr, K230GSDMALLT *llt)
34 {
35 bool ok = address_space_read(&address_space_memory, addr,
36 MEMTXATTRS_UNSPECIFIED, llt,
37 sizeof(*llt)) == MEMTX_OK;
38
39 if (ok) {
40 trace_k230_gsdma_read_sdma_llt(addr, le32_to_cpu(llt->cfg),
41 le32_to_cpu(llt->src_addr),
42 le32_to_cpu(llt->line_size),
43 le32_to_cpu(llt->line_cfg),
44 le32_to_cpu(llt->dst_addr),
45 le32_to_cpu(llt->next_llt_addr));
46 } else {
47 trace_k230_gsdma_read_sdma_llt_failed(addr);
48 }
49
50 return ok;
51 }
52
53 static unsigned int k230_gsdma_usr_data_size(uint32_t cfg)
54 {
55 /* decode usr_dat_size in CH_CFG register */
56 switch (extract32(cfg, K230_GSDMA_CH_CFG_USR_DATA_SIZE_SHIFT, 2)) {
57 case 0:
58 return 1;
59 case 1:
60 return 2;
61 default:
62 return 4;
63 }
64 }
65
66 static void k230_gsdma_convert_endian(uint8_t *buf, uint32_t len, uint32_t cfg)
67 {
68 unsigned int mode = extract32(cfg,
69 K230_GSDMA_CH_CFG_DAT_ENDIAN_SHIFT, 2);
70 unsigned int unit_size;
71 unsigned int offset;
72
73 if (mode == 0) {
74 return;
75 }
76
77 unit_size = 1U << mode;
78 for (offset = 0; offset + unit_size <= len; offset += unit_size) {
79 unsigned int i;
80
81 for (i = 0; i < unit_size / 2; i++) {
82 uint8_t tmp = buf[offset + i];
83
84 buf[offset + i] = buf[offset + unit_size - i - 1];
85 buf[offset + unit_size - i - 1] = tmp;
86 }
87 }
88 }
89
90 static bool k230_gsdma_transfer_llt(K230GSDMAChannel *c, const K230GSDMALLT *llt)
91 {
92 uint32_t llt_cfg = le32_to_cpu(llt->cfg);
93 hwaddr src = le32_to_cpu(llt->src_addr);
94 hwaddr dst = le32_to_cpu(llt->dst_addr);
95 uint32_t line_size = le32_to_cpu(llt->line_size);
96 uint32_t line_cfg = le32_to_cpu(llt->line_cfg);
97 /* Number of lines required for 2d mode */
98 uint32_t line_num =
99 (llt_cfg & K230_GSDMA_LLT_2D_MODE) ? extract32(line_cfg, 0, 16) : 1;
100 /* Stride line size required for 2d mode */
101 uint32_t line_space = extract32(line_cfg, 16, 16);
102
103 bool dat_mode = c->cfg & K230_GSDMA_CH_CFG_DAT_MODE;
104 bool src_fixed = c->cfg & K230_GSDMA_CH_CFG_SRC_FIXED;
105 bool dst_fixed = c->cfg & K230_GSDMA_CH_CFG_DST_FIXED;
106
107 uint8_t buf[8]; /* K230 sdma axi data width is 64-bit */
108 uint8_t fill[4]; /* Usr data is 32-bit*/
109 unsigned int fill_len = k230_gsdma_usr_data_size(c->cfg);
110 uint32_t i;
111
112 memcpy(fill, &c->usr_data, sizeof(fill));
113
114 for (i = 0; i < line_num; i++) {
115 uint32_t remaining = line_size;
116 hwaddr line_src = src;
117 hwaddr line_dst = dst;
118
119 while (remaining) {
120 uint32_t chunk = MIN(remaining, (uint32_t)sizeof(buf));
121
122 if (dat_mode) {
123 uint32_t j;
124
125 for (j = 0; j < chunk; j++) {
126 buf[j] = fill[j % fill_len];
127 }
128 } else if (address_space_read(&address_space_memory, line_src,
129 MEMTXATTRS_UNSPECIFIED, buf,
130 chunk) != MEMTX_OK) {
131 return false;
132 }
133
134 k230_gsdma_convert_endian(buf, chunk, c->cfg);
135
136 if (address_space_write(&address_space_memory, line_dst,
137 MEMTXATTRS_UNSPECIFIED, buf,
138 chunk) != MEMTX_OK) {
139 return false;
140 }
141
142 if (!dat_mode && !src_fixed) {
143 line_src += chunk;
144 }
145 if (!dst_fixed) {
146 line_dst += chunk;
147 }
148 remaining -= chunk;
149 }
150
151 if (!dat_mode && !src_fixed) {
152 if (llt_cfg & K230_GSDMA_LLT_2D_MODE) { /* 2d mode */
153 src += line_size + line_space;
154 } else {
155 src += line_size;
156 }
157 }
158
159 if (!dst_fixed) {
160 dst += line_size;
161 }
162 }
163
164 return true;
165 }
166
167 static void k230_gsdma_sdma_set_idle(K230GSDMAChannel *c)
168 {
169 c->status &= ~(K230_GSDMA_SDMA_STATUS_BUSY | K230_GSDMA_SDMA_STATUS_PAUSE);
170 c->next_llt = 0;
171 }
172
173 static void k230_gsdma_sdma_set_paused(K230GSDMAChannel *c, hwaddr next_llt)
174 {
175 c->status &= ~K230_GSDMA_SDMA_STATUS_BUSY;
176 c->status |= K230_GSDMA_SDMA_STATUS_PAUSE;
177 c->next_llt = next_llt;
178 }
179
180 static void k230_gsdma_run_sdma(K230GSDMAState *s, unsigned int ch, hwaddr addr)
181 {
182 K230GSDMAChannel *c = &s->channels[ch];
183
184 c->status &= ~K230_GSDMA_SDMA_STATUS_PAUSE;
185 c->status |= K230_GSDMA_SDMA_STATUS_BUSY;
186 c->next_llt = 0;
187 c->current_llt = addr;
188
189 while (addr) {
190 K230GSDMALLT llt;
191 uint32_t cfg;
192 hwaddr next;
193
194 if (!k230_gsdma_read_sdma_llt(addr, &llt)) {
195 k230_gsdma_sdma_set_idle(c);
196 return;
197 }
198
199 cfg = le32_to_cpu(llt.cfg);
200 next = le32_to_cpu(llt.next_llt_addr);
201 c->current_llt = addr;
202
203 if (!k230_gsdma_transfer_llt(c, &llt)) {
204 k230_gsdma_sdma_set_idle(c);
205 return;
206 }
207
208 if (cfg & K230_GSDMA_LLT_NODE_INTR) {
209 s->dma_int_stat |= K230_GSDMA_SDMA_ITEM_INT(ch);
210 }
211
212 if (cfg & K230_GSDMA_LLT_PAUSE) {
213 s->dma_int_stat |= K230_GSDMA_SDMA_PAUSE_INT(ch);
214 k230_gsdma_sdma_set_paused(c, next);
215 k230_gsdma_update_irq(s);
216 return;
217 }
218
219 addr = next;
220 }
221
222 s->dma_int_stat |= K230_GSDMA_SDMA_DONE_INT(ch);
223 s->dma_ch_en &= ~BIT(ch);
224 k230_gsdma_sdma_set_idle(c);
225 k230_gsdma_update_irq(s);
226 }
227
228 /* Decomp gzip request step sdma */
229 static void k230_gsdma_step_sdma(K230GSDMAState *s, unsigned int ch, hwaddr addr)
230 {
231 K230GSDMAChannel *c = &s->channels[ch];
232 K230GSDMALLT llt;
233 uint32_t cfg;
234 hwaddr next;
235
236 if (!addr || !k230_gsdma_read_sdma_llt(addr, &llt)) {
237 k230_gsdma_sdma_set_idle(c);
238 return;
239 }
240
241 cfg = le32_to_cpu(llt.cfg);
242 next = le32_to_cpu(llt.next_llt_addr);
243
244 c->status |= K230_GSDMA_SDMA_STATUS_BUSY;
245 c->status &= ~K230_GSDMA_SDMA_STATUS_PAUSE;
246 c->current_llt = addr;
247
248 if (!k230_gsdma_transfer_llt(c, &llt)) {
249 k230_gsdma_sdma_set_idle(c);
250 return;
251 }
252
253 if (cfg & K230_GSDMA_LLT_NODE_INTR) {
254 s->dma_int_stat |= K230_GSDMA_SDMA_ITEM_INT(ch);
255 }
256
257 c->status &= ~K230_GSDMA_SDMA_STATUS_BUSY;
258 c->next_llt = next;
259 if (next == 0) {
260 s->dma_int_stat |= K230_GSDMA_SDMA_DONE_INT(ch);
261 s->dma_ch_en &= ~BIT(ch);
262 }
263
264 k230_gsdma_update_irq(s);
265 }
266
267 static const VMStateDescription vmstate_k230_gsdma_channel = {
268 .name = "k230.gsdma.channel",
269 .version_id = 1,
270 .minimum_version_id = 1,
271 .fields = (const VMStateField[]) {
272 VMSTATE_UINT32(ctl, K230GSDMAChannel),
273 VMSTATE_UINT32(status, K230GSDMAChannel),
274 VMSTATE_UINT32(cfg, K230GSDMAChannel),
275 VMSTATE_UINT32(usr_data, K230GSDMAChannel),
276 VMSTATE_UINT32(llt_saddr, K230GSDMAChannel),
277 VMSTATE_UINT32(current_llt, K230GSDMAChannel),
278 VMSTATE_UINT32(next_llt, K230GSDMAChannel),
279 VMSTATE_END_OF_LIST()
280 }
281 };
282
283 static const VMStateDescription vmstate_k230_gsdma = {
284 .name = "k230.gsdma",
285 .version_id = 1,
286 .minimum_version_id = 1,
287 .fields = (const VMStateField[]) {
288 VMSTATE_UINT32(dma_ch_en, K230GSDMAState),
289 VMSTATE_UINT32(dma_int_mask, K230GSDMAState),
290 VMSTATE_UINT32(dma_int_stat, K230GSDMAState),
291 VMSTATE_UINT32(dma_cfg, K230GSDMAState),
292 VMSTATE_UINT32(dma_weight, K230GSDMAState),
293 VMSTATE_STRUCT_ARRAY(channels, K230GSDMAState,
294 K230_GSDMA_NUM_SDMA_CHANNELS, 2,
295 vmstate_k230_gsdma_channel, K230GSDMAChannel),
296 VMSTATE_END_OF_LIST()
297 }
298 };
299
300 static uint64_t k230_gsdma_read(void *opaque, hwaddr addr, unsigned int size)
301 {
302 K230GSDMAState *s = K230_GSDMA(opaque);
303 unsigned int ch;
304 hwaddr ch_off;
305 uint64_t ret = 0;
306
307 switch (addr) {
308 case K230_GSDMA_DMA_CH_EN:
309 ret = s->dma_ch_en;
310 break;
311 case K230_GSDMA_DMA_INT_MASK:
312 ret = s->dma_int_mask;
313 break;
314 case K230_GSDMA_DMA_INT_STAT:
315 ret = s->dma_int_stat;
316 break;
317 case K230_GSDMA_DMA_CFG:
318 ret = s->dma_cfg;
319 break;
320 case K230_GSDMA_DMA_WEIGHT:
321 ret = s->dma_weight;
322 break;
323 default:
324 if (addr < K230_GSDMA_CH_BASE) {
325 qemu_log_mask(LOG_GUEST_ERROR,
326 "%s: not implement gdma at offset 0x%" HWADDR_PRIx
327 "\n", __func__, addr);
328 }
329 break;
330 }
331
332 ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE;
333 ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE;
334 if (ch < K230_GSDMA_NUM_SDMA_CHANNELS) {
335 switch (ch_off) {
336 case K230_GSDMA_CH_CTL:
337 break;
338 case K230_GSDMA_CH_STATUS:
339 ret = s->channels[ch].status;
340 break;
341 case K230_GSDMA_CH_CFG:
342 ret = s->channels[ch].cfg;
343 break;
344 case K230_GSDMA_CH_USR_DATA:
345 ret = s->channels[ch].usr_data;
346 break;
347 case K230_GSDMA_CH_LLT_SADDR:
348 ret = s->channels[ch].llt_saddr;
349 break;
350 case K230_GSDMA_CH_CURRENT_LLT:
351 ret = s->channels[ch].current_llt;
352 break;
353 default:
354 break;
355 }
356 }
357
358 trace_k230_gsdma_read(addr, size, ret);
359 return ret;
360 }
361
362 static void k230_gsdma_write_channel(K230GSDMAState *s, unsigned int ch,
363 hwaddr ch_off, uint32_t value)
364 {
365 K230GSDMAChannel *c = &s->channels[ch];
366 bool handshake_mode = ch < 2 && k230_gsdma_decomp_enabled(s);
367
368 switch (ch_off) {
369 case K230_GSDMA_CH_CTL:
370 c->ctl = value;
371 if ((value & K230_GSDMA_CTL_STOP) != 0) {
372 c->started = false;
373 k230_gsdma_sdma_set_idle(c);
374 break;
375 }
376 if ((value & K230_GSDMA_CTL_RESUME) != 0 &&
377 (c->status & K230_GSDMA_SDMA_STATUS_PAUSE) && c->next_llt != 0) {
378 k230_gsdma_run_sdma(s, ch, c->next_llt);
379 break;
380 }
381 if (handshake_mode && (value & K230_GSDMA_CTL_START) != 0) {
382 c->started = true;
383 c->status &= ~(K230_GSDMA_SDMA_STATUS_BUSY |
384 K230_GSDMA_SDMA_STATUS_PAUSE);
385 c->current_llt = 0;
386 c->next_llt = 0;
387 break;
388 }
389 if ((value & K230_GSDMA_CTL_START) != 0 &&
390 (s->dma_ch_en & BIT(ch)) && c->llt_saddr != 0) {
391 c->started = true;
392 k230_gsdma_run_sdma(s, ch, c->llt_saddr);
393 }
394 break;
395 case K230_GSDMA_CH_CFG:
396 c->cfg = value;
397 break;
398 case K230_GSDMA_CH_USR_DATA:
399 c->usr_data = value;
400 break;
401 case K230_GSDMA_CH_LLT_SADDR:
402 c->llt_saddr = value;
403 break;
404 case K230_GSDMA_CH_CURRENT_LLT:
405 break;
406 default:
407 break;
408 }
409 }
410
411 static void k230_gsdma_write(void *opaque, hwaddr addr,
412 uint64_t value, unsigned int size)
413 {
414 K230GSDMAState *s = K230_GSDMA(opaque);
415 unsigned int ch;
416 hwaddr ch_off;
417 uint32_t v = value;
418
419 trace_k230_gsdma_write(addr, size, value);
420
421 switch (addr) {
422 case K230_GSDMA_DMA_CH_EN:
423 s->dma_ch_en = v & K230_GSDMA_DMA_CH_EN_MASK;
424 return;
425 case K230_GSDMA_DMA_INT_MASK:
426 s->dma_int_mask = v;
427 k230_gsdma_update_irq(s);
428 return;
429 case K230_GSDMA_DMA_INT_STAT:
430 s->dma_int_stat &= ~v;
431 k230_gsdma_update_irq(s);
432 return;
433 case K230_GSDMA_DMA_CFG:
434 s->dma_cfg = v;
435 return;
436 case K230_GSDMA_DMA_WEIGHT:
437 s->dma_weight = v & 0x00ffffff;
438 return;
439 default:
440 break;
441 }
442
443 if (addr < K230_GSDMA_CH_BASE) {
444 qemu_log_mask(LOG_GUEST_ERROR,
445 "%s: not implement gdma at offset 0x%" HWADDR_PRIx
446 "\n", __func__, addr);
447 return;
448 }
449
450 ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE;
451 ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE;
452 if (ch >= K230_GSDMA_NUM_SDMA_CHANNELS) {
453 return;
454 }
455
456 k230_gsdma_write_channel(s, ch, ch_off, v);
457 }
458
459 static void k230_gsdma_handle_signal(void *opaque, int n, int level)
460 {
461 K230GSDMAState *s = opaque;
462 K230GSDMAChannel *c;
463 hwaddr addr;
464 unsigned int ch;
465
466 if (!level || n == K230_GSDMA_GPIO_DECOMP_CTRL_EN) {
467 return;
468 }
469
470 ch = n - 1;
471
472 c = &s->channels[ch];
473 if (!k230_gsdma_decomp_enabled(s) || !(s->dma_ch_en & BIT(ch)) || !c->started) {
474 return;
475 }
476
477 if (c->current_llt == 0) {
478 addr = c->llt_saddr;
479 } else {
480 addr = c->next_llt;
481 }
482 if (!addr) {
483 return;
484 }
485
486 k230_gsdma_step_sdma(s, ch, addr);
487 qemu_set_irq(s->handshake_out[n - 1], 1);
488 qemu_set_irq(s->handshake_out[n - 1], 0);
489 }
490
491 static const MemoryRegionOps k230_gsdma_ops = {
492 .read = k230_gsdma_read,
493 .write = k230_gsdma_write,
494 .endianness = DEVICE_LITTLE_ENDIAN,
495 .impl.min_access_size = 4,
496 .impl.max_access_size = 4,
497 .valid.min_access_size = 4,
498 .valid.max_access_size = 4,
499 };
500
501 static void k230_gsdma_reset_hold(Object *obj, ResetType type)
502 {
503 K230GSDMAState *s = K230_GSDMA(obj);
504
505 memset(s->channels, 0, sizeof(s->channels));
506 s->dma_ch_en = 0;
507 s->dma_int_mask = 0;
508 s->dma_int_stat = 0;
509 s->dma_cfg = K230_GSDMA_DMA_CFG_RESET;
510 s->dma_weight = 0;
511 }
512
513 static void k230_gsdma_realize(DeviceState *dev, Error **errp)
514 {
515 K230GSDMAState *s = K230_GSDMA(dev);
516 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
517
518 memory_region_init_io(&s->iomem, OBJECT(dev), &k230_gsdma_ops, s,
519 TYPE_K230_GSDMA, K230_GSDMA_MMIO_SIZE);
520 sysbus_init_mmio(sbd, &s->iomem);
521 sysbus_init_irq(sbd, &s->irq);
522 qdev_init_gpio_in(DEVICE(dev), k230_gsdma_handle_signal,
523 K230_GSDMA_NUM_GPIOS_IN);
524 qdev_init_gpio_out(DEVICE(dev), s->handshake_out,
525 K230_GSDMA_NUM_GPIOS_OUT);
526 }
527
528 static void k230_gsdma_class_init(ObjectClass *klass, const void *data)
529 {
530 DeviceClass *dc = DEVICE_CLASS(klass);
531 ResettableClass *rc = RESETTABLE_CLASS(klass);
532
533 dc->realize = k230_gsdma_realize;
534 rc->phases.hold = k230_gsdma_reset_hold;
535 dc->vmsd = &vmstate_k230_gsdma;
536 dc->desc = "Kendryte K230 GSDMA";
537 }
538
539 static const TypeInfo k230_gsdma_info = {
540 .name = TYPE_K230_GSDMA,
541 .parent = TYPE_SYS_BUS_DEVICE,
542 .instance_size = sizeof(K230GSDMAState),
543 .class_init = k230_gsdma_class_init,
544 };
545
546 static void k230_gsdma_register_types(void)
547 {
548 type_register_static(&k230_gsdma_info);
549 }
550
551 type_init(k230_gsdma_register_types)