root / hw / dma / pl330.c @ df32fd1c
History | View | Annotate | Download (48 kB)
1 |
/*
|
---|---|
2 |
* ARM PrimeCell PL330 DMA Controller
|
3 |
*
|
4 |
* Copyright (c) 2009 Samsung Electronics.
|
5 |
* Contributed by Kirill Batuzov <batuzovk@ispras.ru>
|
6 |
* Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
|
7 |
* Copyright (c) 2012 PetaLogix Pty Ltd.
|
8 |
*
|
9 |
* This program is free software; you can redistribute it and/or
|
10 |
* modify it under the terms of the GNU General Public License
|
11 |
* as published by the Free Software Foundation; version 2 or later.
|
12 |
*
|
13 |
* You should have received a copy of the GNU General Public License along
|
14 |
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
15 |
*/
|
16 |
|
17 |
#include "hw/sysbus.h" |
18 |
#include "qemu/timer.h" |
19 |
#include "sysemu/dma.h" |
20 |
|
21 |
#ifndef PL330_ERR_DEBUG
|
22 |
#define PL330_ERR_DEBUG 0 |
23 |
#endif
|
24 |
|
25 |
#define DB_PRINT_L(lvl, fmt, args...) do {\ |
26 |
if (PL330_ERR_DEBUG >= lvl) {\
|
27 |
fprintf(stderr, "PL330: %s:" fmt, __func__, ## args);\ |
28 |
} \ |
29 |
} while (0); |
30 |
|
31 |
#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) |
32 |
|
33 |
#define PL330_PERIPH_NUM 32 |
34 |
#define PL330_MAX_BURST_LEN 128 |
35 |
#define PL330_INSN_MAXSIZE 6 |
36 |
|
37 |
#define PL330_FIFO_OK 0 |
38 |
#define PL330_FIFO_STALL 1 |
39 |
#define PL330_FIFO_ERR (-1) |
40 |
|
41 |
#define PL330_FAULT_UNDEF_INSTR (1 << 0) |
42 |
#define PL330_FAULT_OPERAND_INVALID (1 << 1) |
43 |
#define PL330_FAULT_DMAGO_ERR (1 << 4) |
44 |
#define PL330_FAULT_EVENT_ERR (1 << 5) |
45 |
#define PL330_FAULT_CH_PERIPH_ERR (1 << 6) |
46 |
#define PL330_FAULT_CH_RDWR_ERR (1 << 7) |
47 |
#define PL330_FAULT_ST_DATA_UNAVAILABLE (1 << 12) |
48 |
#define PL330_FAULT_FIFOEMPTY_ERR (1 << 13) |
49 |
#define PL330_FAULT_INSTR_FETCH_ERR (1 << 16) |
50 |
#define PL330_FAULT_DATA_WRITE_ERR (1 << 17) |
51 |
#define PL330_FAULT_DATA_READ_ERR (1 << 18) |
52 |
#define PL330_FAULT_DBG_INSTR (1 << 30) |
53 |
#define PL330_FAULT_LOCKUP_ERR (1 << 31) |
54 |
|
55 |
#define PL330_UNTAGGED 0xff |
56 |
|
57 |
#define PL330_SINGLE 0x0 |
58 |
#define PL330_BURST 0x1 |
59 |
|
60 |
#define PL330_WATCHDOG_LIMIT 1024 |
61 |
|
62 |
/* IOMEM mapped registers */
|
63 |
#define PL330_REG_DSR 0x000 |
64 |
#define PL330_REG_DPC 0x004 |
65 |
#define PL330_REG_INTEN 0x020 |
66 |
#define PL330_REG_INT_EVENT_RIS 0x024 |
67 |
#define PL330_REG_INTMIS 0x028 |
68 |
#define PL330_REG_INTCLR 0x02C |
69 |
#define PL330_REG_FSRD 0x030 |
70 |
#define PL330_REG_FSRC 0x034 |
71 |
#define PL330_REG_FTRD 0x038 |
72 |
#define PL330_REG_FTR_BASE 0x040 |
73 |
#define PL330_REG_CSR_BASE 0x100 |
74 |
#define PL330_REG_CPC_BASE 0x104 |
75 |
#define PL330_REG_CHANCTRL 0x400 |
76 |
#define PL330_REG_DBGSTATUS 0xD00 |
77 |
#define PL330_REG_DBGCMD 0xD04 |
78 |
#define PL330_REG_DBGINST0 0xD08 |
79 |
#define PL330_REG_DBGINST1 0xD0C |
80 |
#define PL330_REG_CR0_BASE 0xE00 |
81 |
#define PL330_REG_PERIPH_ID 0xFE0 |
82 |
|
83 |
#define PL330_IOMEM_SIZE 0x1000 |
84 |
|
85 |
#define CFG_BOOT_ADDR 2 |
86 |
#define CFG_INS 3 |
87 |
#define CFG_PNS 4 |
88 |
#define CFG_CRD 5 |
89 |
|
90 |
static const uint32_t pl330_id[] = { |
91 |
0x30, 0x13, 0x24, 0x00, 0x0D, 0xF0, 0x05, 0xB1 |
92 |
}; |
93 |
|
94 |
/* DMA channel states as they are described in PL330 Technical Reference Manual
|
95 |
* Most of them will not be used in emulation.
|
96 |
*/
|
97 |
typedef enum { |
98 |
pl330_chan_stopped = 0,
|
99 |
pl330_chan_executing = 1,
|
100 |
pl330_chan_cache_miss = 2,
|
101 |
pl330_chan_updating_pc = 3,
|
102 |
pl330_chan_waiting_event = 4,
|
103 |
pl330_chan_at_barrier = 5,
|
104 |
pl330_chan_queue_busy = 6,
|
105 |
pl330_chan_waiting_periph = 7,
|
106 |
pl330_chan_killing = 8,
|
107 |
pl330_chan_completing = 9,
|
108 |
pl330_chan_fault_completing = 14,
|
109 |
pl330_chan_fault = 15,
|
110 |
} PL330ChanState; |
111 |
|
112 |
typedef struct PL330State PL330State; |
113 |
|
114 |
typedef struct PL330Chan { |
115 |
uint32_t src; |
116 |
uint32_t dst; |
117 |
uint32_t pc; |
118 |
uint32_t control; |
119 |
uint32_t status; |
120 |
uint32_t lc[2];
|
121 |
uint32_t fault_type; |
122 |
uint32_t watchdog_timer; |
123 |
|
124 |
bool ns;
|
125 |
uint8_t request_flag; |
126 |
uint8_t wakeup; |
127 |
uint8_t wfp_sbp; |
128 |
|
129 |
uint8_t state; |
130 |
uint8_t stall; |
131 |
|
132 |
bool is_manager;
|
133 |
PL330State *parent; |
134 |
uint8_t tag; |
135 |
} PL330Chan; |
136 |
|
137 |
static const VMStateDescription vmstate_pl330_chan = { |
138 |
.name = "pl330_chan",
|
139 |
.version_id = 1,
|
140 |
.minimum_version_id = 1,
|
141 |
.minimum_version_id_old = 1,
|
142 |
.fields = (VMStateField[]) { |
143 |
VMSTATE_UINT32(src, PL330Chan), |
144 |
VMSTATE_UINT32(dst, PL330Chan), |
145 |
VMSTATE_UINT32(pc, PL330Chan), |
146 |
VMSTATE_UINT32(control, PL330Chan), |
147 |
VMSTATE_UINT32(status, PL330Chan), |
148 |
VMSTATE_UINT32_ARRAY(lc, PL330Chan, 2),
|
149 |
VMSTATE_UINT32(fault_type, PL330Chan), |
150 |
VMSTATE_UINT32(watchdog_timer, PL330Chan), |
151 |
VMSTATE_BOOL(ns, PL330Chan), |
152 |
VMSTATE_UINT8(request_flag, PL330Chan), |
153 |
VMSTATE_UINT8(wakeup, PL330Chan), |
154 |
VMSTATE_UINT8(wfp_sbp, PL330Chan), |
155 |
VMSTATE_UINT8(state, PL330Chan), |
156 |
VMSTATE_UINT8(stall, PL330Chan), |
157 |
VMSTATE_END_OF_LIST() |
158 |
} |
159 |
}; |
160 |
|
161 |
typedef struct PL330Fifo { |
162 |
uint8_t *buf; |
163 |
uint8_t *tag; |
164 |
uint32_t head; |
165 |
uint32_t num; |
166 |
uint32_t buf_size; |
167 |
} PL330Fifo; |
168 |
|
169 |
static const VMStateDescription vmstate_pl330_fifo = { |
170 |
.name = "pl330_chan",
|
171 |
.version_id = 1,
|
172 |
.minimum_version_id = 1,
|
173 |
.minimum_version_id_old = 1,
|
174 |
.fields = (VMStateField[]) { |
175 |
VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, 0, buf_size), |
176 |
VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, 0, buf_size), |
177 |
VMSTATE_UINT32(head, PL330Fifo), |
178 |
VMSTATE_UINT32(num, PL330Fifo), |
179 |
VMSTATE_UINT32(buf_size, PL330Fifo), |
180 |
VMSTATE_END_OF_LIST() |
181 |
} |
182 |
}; |
183 |
|
184 |
typedef struct PL330QueueEntry { |
185 |
uint32_t addr; |
186 |
uint32_t len; |
187 |
uint8_t n; |
188 |
bool inc;
|
189 |
bool z;
|
190 |
uint8_t tag; |
191 |
uint8_t seqn; |
192 |
} PL330QueueEntry; |
193 |
|
194 |
static const VMStateDescription vmstate_pl330_queue_entry = { |
195 |
.name = "pl330_queue_entry",
|
196 |
.version_id = 1,
|
197 |
.minimum_version_id = 1,
|
198 |
.minimum_version_id_old = 1,
|
199 |
.fields = (VMStateField[]) { |
200 |
VMSTATE_UINT32(addr, PL330QueueEntry), |
201 |
VMSTATE_UINT32(len, PL330QueueEntry), |
202 |
VMSTATE_UINT8(n, PL330QueueEntry), |
203 |
VMSTATE_BOOL(inc, PL330QueueEntry), |
204 |
VMSTATE_BOOL(z, PL330QueueEntry), |
205 |
VMSTATE_UINT8(tag, PL330QueueEntry), |
206 |
VMSTATE_UINT8(seqn, PL330QueueEntry), |
207 |
VMSTATE_END_OF_LIST() |
208 |
} |
209 |
}; |
210 |
|
211 |
typedef struct PL330Queue { |
212 |
PL330State *parent; |
213 |
PL330QueueEntry *queue; |
214 |
uint32_t queue_size; |
215 |
} PL330Queue; |
216 |
|
217 |
static const VMStateDescription vmstate_pl330_queue = { |
218 |
.name = "pl330_queue",
|
219 |
.version_id = 1,
|
220 |
.minimum_version_id = 1,
|
221 |
.minimum_version_id_old = 1,
|
222 |
.fields = (VMStateField[]) { |
223 |
VMSTATE_STRUCT_VARRAY_UINT32(queue, PL330Queue, queue_size, 1,
|
224 |
vmstate_pl330_queue_entry, PL330QueueEntry), |
225 |
VMSTATE_END_OF_LIST() |
226 |
} |
227 |
}; |
228 |
|
229 |
struct PL330State {
|
230 |
SysBusDevice busdev; |
231 |
MemoryRegion iomem; |
232 |
qemu_irq irq_abort; |
233 |
qemu_irq *irq; |
234 |
|
235 |
/* Config registers. cfg[5] = CfgDn. */
|
236 |
uint32_t cfg[6];
|
237 |
#define EVENT_SEC_STATE 3 |
238 |
#define PERIPH_SEC_STATE 4 |
239 |
/* cfg 0 bits and pieces */
|
240 |
uint32_t num_chnls; |
241 |
uint8_t num_periph_req; |
242 |
uint8_t num_events; |
243 |
uint8_t mgr_ns_at_rst; |
244 |
/* cfg 1 bits and pieces */
|
245 |
uint8_t i_cache_len; |
246 |
uint8_t num_i_cache_lines; |
247 |
/* CRD bits and pieces */
|
248 |
uint8_t data_width; |
249 |
uint8_t wr_cap; |
250 |
uint8_t wr_q_dep; |
251 |
uint8_t rd_cap; |
252 |
uint8_t rd_q_dep; |
253 |
uint16_t data_buffer_dep; |
254 |
|
255 |
PL330Chan manager; |
256 |
PL330Chan *chan; |
257 |
PL330Fifo fifo; |
258 |
PL330Queue read_queue; |
259 |
PL330Queue write_queue; |
260 |
uint8_t *lo_seqn; |
261 |
uint8_t *hi_seqn; |
262 |
QEMUTimer *timer; /* is used for restore dma. */
|
263 |
|
264 |
uint32_t inten; |
265 |
uint32_t int_status; |
266 |
uint32_t ev_status; |
267 |
uint32_t dbg[2];
|
268 |
uint8_t debug_status; |
269 |
uint8_t num_faulting; |
270 |
uint8_t periph_busy[PL330_PERIPH_NUM]; |
271 |
|
272 |
}; |
273 |
|
274 |
#define TYPE_PL330 "pl330" |
275 |
#define PL330(obj) OBJECT_CHECK(PL330State, (obj), TYPE_PL330)
|
276 |
|
277 |
static const VMStateDescription vmstate_pl330 = { |
278 |
.name = "pl330",
|
279 |
.version_id = 1,
|
280 |
.minimum_version_id = 1,
|
281 |
.minimum_version_id_old = 1,
|
282 |
.fields = (VMStateField[]) { |
283 |
VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan),
|
284 |
VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0,
|
285 |
vmstate_pl330_chan, PL330Chan), |
286 |
VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, 0, num_chnls), |
287 |
VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, 0, num_chnls), |
288 |
VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo),
|
289 |
VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue,
|
290 |
PL330Queue), |
291 |
VMSTATE_STRUCT(write_queue, PL330State, 0, vmstate_pl330_queue,
|
292 |
PL330Queue), |
293 |
VMSTATE_TIMER(timer, PL330State), |
294 |
VMSTATE_UINT32(inten, PL330State), |
295 |
VMSTATE_UINT32(int_status, PL330State), |
296 |
VMSTATE_UINT32(ev_status, PL330State), |
297 |
VMSTATE_UINT32_ARRAY(dbg, PL330State, 2),
|
298 |
VMSTATE_UINT8(debug_status, PL330State), |
299 |
VMSTATE_UINT8(num_faulting, PL330State), |
300 |
VMSTATE_UINT8_ARRAY(periph_busy, PL330State, PL330_PERIPH_NUM), |
301 |
VMSTATE_END_OF_LIST() |
302 |
} |
303 |
}; |
304 |
|
305 |
typedef struct PL330InsnDesc { |
306 |
/* OPCODE of the instruction */
|
307 |
uint8_t opcode; |
308 |
/* Mask so we can select several sibling instructions, such as
|
309 |
DMALD, DMALDS and DMALDB */
|
310 |
uint8_t opmask; |
311 |
/* Size of instruction in bytes */
|
312 |
uint8_t size; |
313 |
/* Interpreter */
|
314 |
void (*exec)(PL330Chan *, uint8_t opcode, uint8_t *args, int len); |
315 |
} PL330InsnDesc; |
316 |
|
317 |
|
318 |
/* MFIFO Implementation
|
319 |
*
|
320 |
* MFIFO is implemented as a cyclic buffer of BUF_SIZE size. Tagged bytes are
|
321 |
* stored in this buffer. Data is stored in BUF field, tags - in the
|
322 |
* corresponding array elements of TAG field.
|
323 |
*/
|
324 |
|
325 |
/* Initialize queue. */
|
326 |
|
327 |
static void pl330_fifo_init(PL330Fifo *s, uint32_t size) |
328 |
{ |
329 |
s->buf = g_malloc0(size); |
330 |
s->tag = g_malloc0(size); |
331 |
s->buf_size = size; |
332 |
} |
333 |
|
334 |
/* Cyclic increment */
|
335 |
|
336 |
static inline int pl330_fifo_inc(PL330Fifo *s, int x) |
337 |
{ |
338 |
return (x + 1) % s->buf_size; |
339 |
} |
340 |
|
341 |
/* Number of empty bytes in MFIFO */
|
342 |
|
343 |
static inline int pl330_fifo_num_free(PL330Fifo *s) |
344 |
{ |
345 |
return s->buf_size - s->num;
|
346 |
} |
347 |
|
348 |
/* Push LEN bytes of data stored in BUF to MFIFO and tag it with TAG.
|
349 |
* Zero returned on success, PL330_FIFO_STALL if there is no enough free
|
350 |
* space in MFIFO to store requested amount of data. If push was unsuccessful
|
351 |
* no data is stored to MFIFO.
|
352 |
*/
|
353 |
|
354 |
static int pl330_fifo_push(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag) |
355 |
{ |
356 |
int i;
|
357 |
|
358 |
if (s->buf_size - s->num < len) {
|
359 |
return PL330_FIFO_STALL;
|
360 |
} |
361 |
for (i = 0; i < len; i++) { |
362 |
int push_idx = (s->head + s->num + i) % s->buf_size;
|
363 |
s->buf[push_idx] = buf[i]; |
364 |
s->tag[push_idx] = tag; |
365 |
} |
366 |
s->num += len; |
367 |
return PL330_FIFO_OK;
|
368 |
} |
369 |
|
370 |
/* Get LEN bytes of data from MFIFO and store it to BUF. Tag value of each
|
371 |
* byte is verified. Zero returned on success, PL330_FIFO_ERR on tag mismatch
|
372 |
* and PL330_FIFO_STALL if there is no enough data in MFIFO. If get was
|
373 |
* unsuccessful no data is removed from MFIFO.
|
374 |
*/
|
375 |
|
376 |
static int pl330_fifo_get(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag) |
377 |
{ |
378 |
int i;
|
379 |
|
380 |
if (s->num < len) {
|
381 |
return PL330_FIFO_STALL;
|
382 |
} |
383 |
for (i = 0; i < len; i++) { |
384 |
if (s->tag[s->head] == tag) {
|
385 |
int get_idx = (s->head + i) % s->buf_size;
|
386 |
buf[i] = s->buf[get_idx]; |
387 |
} else { /* Tag mismatch - Rollback transaction */ |
388 |
return PL330_FIFO_ERR;
|
389 |
} |
390 |
} |
391 |
s->head = (s->head + len) % s->buf_size; |
392 |
s->num -= len; |
393 |
return PL330_FIFO_OK;
|
394 |
} |
395 |
|
396 |
/* Reset MFIFO. This completely erases all data in it. */
|
397 |
|
398 |
static inline void pl330_fifo_reset(PL330Fifo *s) |
399 |
{ |
400 |
s->head = 0;
|
401 |
s->num = 0;
|
402 |
} |
403 |
|
404 |
/* Return tag of the first byte stored in MFIFO. If MFIFO is empty
|
405 |
* PL330_UNTAGGED is returned.
|
406 |
*/
|
407 |
|
408 |
static inline uint8_t pl330_fifo_tag(PL330Fifo *s) |
409 |
{ |
410 |
return (!s->num) ? PL330_UNTAGGED : s->tag[s->head];
|
411 |
} |
412 |
|
413 |
/* Returns non-zero if tag TAG is present in fifo or zero otherwise */
|
414 |
|
415 |
static int pl330_fifo_has_tag(PL330Fifo *s, uint8_t tag) |
416 |
{ |
417 |
int i, n;
|
418 |
|
419 |
i = s->head; |
420 |
for (n = 0; n < s->num; n++) { |
421 |
if (s->tag[i] == tag) {
|
422 |
return 1; |
423 |
} |
424 |
i = pl330_fifo_inc(s, i); |
425 |
} |
426 |
return 0; |
427 |
} |
428 |
|
429 |
/* Remove all entry tagged with TAG from MFIFO */
|
430 |
|
431 |
static void pl330_fifo_tagged_remove(PL330Fifo *s, uint8_t tag) |
432 |
{ |
433 |
int i, t, n;
|
434 |
|
435 |
t = i = s->head; |
436 |
for (n = 0; n < s->num; n++) { |
437 |
if (s->tag[i] != tag) {
|
438 |
s->buf[t] = s->buf[i]; |
439 |
s->tag[t] = s->tag[i]; |
440 |
t = pl330_fifo_inc(s, t); |
441 |
} else {
|
442 |
s->num = s->num - 1;
|
443 |
} |
444 |
i = pl330_fifo_inc(s, i); |
445 |
} |
446 |
} |
447 |
|
448 |
/* Read-Write Queue implementation
|
449 |
*
|
450 |
* A Read-Write Queue stores up to QUEUE_SIZE instructions (loads or stores).
|
451 |
* Each instruction is described by source (for loads) or destination (for
|
452 |
* stores) address ADDR, width of data to be loaded/stored LEN, number of
|
453 |
* stores/loads to be performed N, INC bit, Z bit and TAG to identify channel
|
454 |
* this instruction belongs to. Queue does not store any information about
|
455 |
* nature of the instruction: is it load or store. PL330 has different queues
|
456 |
* for loads and stores so this is already known at the top level where it
|
457 |
* matters.
|
458 |
*
|
459 |
* Queue works as FIFO for instructions with equivalent tags, but can issue
|
460 |
* instructions with different tags in arbitrary order. SEQN field attached to
|
461 |
* each instruction helps to achieve this. For each TAG queue contains
|
462 |
* instructions with consecutive SEQN values ranging from LO_SEQN[TAG] to
|
463 |
* HI_SEQN[TAG]-1 inclusive. SEQN is 8-bit unsigned integer, so SEQN=255 is
|
464 |
* followed by SEQN=0.
|
465 |
*
|
466 |
* Z bit indicates that zeroes should be stored. No MFIFO fetches are performed
|
467 |
* in this case.
|
468 |
*/
|
469 |
|
470 |
static void pl330_queue_reset(PL330Queue *s) |
471 |
{ |
472 |
int i;
|
473 |
|
474 |
for (i = 0; i < s->queue_size; i++) { |
475 |
s->queue[i].tag = PL330_UNTAGGED; |
476 |
} |
477 |
} |
478 |
|
479 |
/* Initialize queue */
|
480 |
static void pl330_queue_init(PL330Queue *s, int size, PL330State *parent) |
481 |
{ |
482 |
s->parent = parent; |
483 |
s->queue = g_new0(PL330QueueEntry, size); |
484 |
s->queue_size = size; |
485 |
} |
486 |
|
487 |
/* Returns pointer to an empty slot or NULL if queue is full */
|
488 |
static PL330QueueEntry *pl330_queue_find_empty(PL330Queue *s)
|
489 |
{ |
490 |
int i;
|
491 |
|
492 |
for (i = 0; i < s->queue_size; i++) { |
493 |
if (s->queue[i].tag == PL330_UNTAGGED) {
|
494 |
return &s->queue[i];
|
495 |
} |
496 |
} |
497 |
return NULL; |
498 |
} |
499 |
|
500 |
/* Put instruction in queue.
|
501 |
* Return value:
|
502 |
* - zero - OK
|
503 |
* - non-zero - queue is full
|
504 |
*/
|
505 |
|
506 |
static int pl330_queue_put_insn(PL330Queue *s, uint32_t addr, |
507 |
int len, int n, bool inc, bool z, uint8_t tag) |
508 |
{ |
509 |
PL330QueueEntry *entry = pl330_queue_find_empty(s); |
510 |
|
511 |
if (!entry) {
|
512 |
return 1; |
513 |
} |
514 |
entry->tag = tag; |
515 |
entry->addr = addr; |
516 |
entry->len = len; |
517 |
entry->n = n; |
518 |
entry->z = z; |
519 |
entry->inc = inc; |
520 |
entry->seqn = s->parent->hi_seqn[tag]; |
521 |
s->parent->hi_seqn[tag]++; |
522 |
return 0; |
523 |
} |
524 |
|
525 |
/* Returns a pointer to queue slot containing instruction which satisfies
|
526 |
* following conditions:
|
527 |
* - it has valid tag value (not PL330_UNTAGGED)
|
528 |
* - if enforce_seq is set it has to be issuable without violating queue
|
529 |
* logic (see above)
|
530 |
* - if TAG argument is not PL330_UNTAGGED this instruction has tag value
|
531 |
* equivalent to the argument TAG value.
|
532 |
* If such instruction cannot be found NULL is returned.
|
533 |
*/
|
534 |
|
535 |
static PL330QueueEntry *pl330_queue_find_insn(PL330Queue *s, uint8_t tag,
|
536 |
bool enforce_seq)
|
537 |
{ |
538 |
int i;
|
539 |
|
540 |
for (i = 0; i < s->queue_size; i++) { |
541 |
if (s->queue[i].tag != PL330_UNTAGGED) {
|
542 |
if ((!enforce_seq ||
|
543 |
s->queue[i].seqn == s->parent->lo_seqn[s->queue[i].tag]) && |
544 |
(s->queue[i].tag == tag || tag == PL330_UNTAGGED || |
545 |
s->queue[i].z)) { |
546 |
return &s->queue[i];
|
547 |
} |
548 |
} |
549 |
} |
550 |
return NULL; |
551 |
} |
552 |
|
553 |
/* Removes instruction from queue. */
|
554 |
|
555 |
static inline void pl330_queue_remove_insn(PL330Queue *s, PL330QueueEntry *e) |
556 |
{ |
557 |
s->parent->lo_seqn[e->tag]++; |
558 |
e->tag = PL330_UNTAGGED; |
559 |
} |
560 |
|
561 |
/* Removes all instructions tagged with TAG from queue. */
|
562 |
|
563 |
static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag) |
564 |
{ |
565 |
int i;
|
566 |
|
567 |
for (i = 0; i < s->queue_size; i++) { |
568 |
if (s->queue[i].tag == tag) {
|
569 |
s->queue[i].tag = PL330_UNTAGGED; |
570 |
} |
571 |
} |
572 |
} |
573 |
|
574 |
/* DMA instruction execution engine */
|
575 |
|
576 |
/* Moves DMA channel to the FAULT state and updates it's status. */
|
577 |
|
578 |
static inline void pl330_fault(PL330Chan *ch, uint32_t flags) |
579 |
{ |
580 |
DB_PRINT("ch: %p, flags: %x\n", ch, flags);
|
581 |
ch->fault_type |= flags; |
582 |
if (ch->state == pl330_chan_fault) {
|
583 |
return;
|
584 |
} |
585 |
ch->state = pl330_chan_fault; |
586 |
ch->parent->num_faulting++; |
587 |
if (ch->parent->num_faulting == 1) { |
588 |
DB_PRINT("abort interrupt raised\n");
|
589 |
qemu_irq_raise(ch->parent->irq_abort); |
590 |
} |
591 |
} |
592 |
|
593 |
/*
|
594 |
* For information about instructions see PL330 Technical Reference Manual.
|
595 |
*
|
596 |
* Arguments:
|
597 |
* CH - channel executing the instruction
|
598 |
* OPCODE - opcode
|
599 |
* ARGS - array of 8-bit arguments
|
600 |
* LEN - number of elements in ARGS array
|
601 |
*/
|
602 |
|
603 |
static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
604 |
{ |
605 |
uint16_t im = (((uint16_t)args[1]) << 8) | ((uint16_t)args[0]); |
606 |
uint8_t ra = (opcode >> 1) & 1; |
607 |
|
608 |
if (ch->is_manager) {
|
609 |
pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); |
610 |
return;
|
611 |
} |
612 |
if (ra) {
|
613 |
ch->dst += im; |
614 |
} else {
|
615 |
ch->src += im; |
616 |
} |
617 |
} |
618 |
|
619 |
static void pl330_dmaend(PL330Chan *ch, uint8_t opcode, |
620 |
uint8_t *args, int len)
|
621 |
{ |
622 |
PL330State *s = ch->parent; |
623 |
|
624 |
if (ch->state == pl330_chan_executing && !ch->is_manager) {
|
625 |
/* Wait for all transfers to complete */
|
626 |
if (pl330_fifo_has_tag(&s->fifo, ch->tag) ||
|
627 |
pl330_queue_find_insn(&s->read_queue, ch->tag, false) != NULL || |
628 |
pl330_queue_find_insn(&s->write_queue, ch->tag, false) != NULL) { |
629 |
|
630 |
ch->stall = 1;
|
631 |
return;
|
632 |
} |
633 |
} |
634 |
DB_PRINT("DMA ending!\n");
|
635 |
pl330_fifo_tagged_remove(&s->fifo, ch->tag); |
636 |
pl330_queue_remove_tagged(&s->read_queue, ch->tag); |
637 |
pl330_queue_remove_tagged(&s->write_queue, ch->tag); |
638 |
ch->state = pl330_chan_stopped; |
639 |
} |
640 |
|
641 |
static void pl330_dmaflushp(PL330Chan *ch, uint8_t opcode, |
642 |
uint8_t *args, int len)
|
643 |
{ |
644 |
uint8_t periph_id; |
645 |
|
646 |
if (args[0] & 7) { |
647 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
648 |
return;
|
649 |
} |
650 |
periph_id = (args[0] >> 3) & 0x1f; |
651 |
if (periph_id >= ch->parent->num_periph_req) {
|
652 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
653 |
return;
|
654 |
} |
655 |
if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { |
656 |
pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); |
657 |
return;
|
658 |
} |
659 |
/* Do nothing */
|
660 |
} |
661 |
|
662 |
static void pl330_dmago(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
663 |
{ |
664 |
uint8_t chan_id; |
665 |
uint8_t ns; |
666 |
uint32_t pc; |
667 |
PL330Chan *s; |
668 |
|
669 |
DB_PRINT("\n");
|
670 |
|
671 |
if (!ch->is_manager) {
|
672 |
pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); |
673 |
return;
|
674 |
} |
675 |
ns = !!(opcode & 2);
|
676 |
chan_id = args[0] & 7; |
677 |
if ((args[0] >> 3)) { |
678 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
679 |
return;
|
680 |
} |
681 |
if (chan_id >= ch->parent->num_chnls) {
|
682 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
683 |
return;
|
684 |
} |
685 |
pc = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) | |
686 |
(((uint32_t)args[2]) << 8) | (((uint32_t)args[1])); |
687 |
if (ch->parent->chan[chan_id].state != pl330_chan_stopped) {
|
688 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
689 |
return;
|
690 |
} |
691 |
if (ch->ns && !ns) {
|
692 |
pl330_fault(ch, PL330_FAULT_DMAGO_ERR); |
693 |
return;
|
694 |
} |
695 |
s = &ch->parent->chan[chan_id]; |
696 |
s->ns = ns; |
697 |
s->pc = pc; |
698 |
s->state = pl330_chan_executing; |
699 |
} |
700 |
|
701 |
static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
702 |
{ |
703 |
uint8_t bs = opcode & 3;
|
704 |
uint32_t size, num; |
705 |
bool inc;
|
706 |
|
707 |
if (bs == 2) { |
708 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
709 |
return;
|
710 |
} |
711 |
if ((bs == 1 && ch->request_flag == PL330_BURST) || |
712 |
(bs == 3 && ch->request_flag == PL330_SINGLE)) {
|
713 |
/* Perform NOP */
|
714 |
return;
|
715 |
} |
716 |
if (bs == 1 && ch->request_flag == PL330_SINGLE) { |
717 |
num = 1;
|
718 |
} else {
|
719 |
num = ((ch->control >> 4) & 0xf) + 1; |
720 |
} |
721 |
size = (uint32_t)1 << ((ch->control >> 1) & 0x7); |
722 |
inc = !!(ch->control & 1);
|
723 |
ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src, |
724 |
size, num, inc, 0, ch->tag);
|
725 |
if (!ch->stall) {
|
726 |
DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n",
|
727 |
ch->tag, ch->src, size, num, inc ? 'Y' : 'N'); |
728 |
ch->src += inc ? size * num - (ch->src & (size - 1)) : 0; |
729 |
} |
730 |
} |
731 |
|
732 |
static void pl330_dmaldp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
733 |
{ |
734 |
uint8_t periph_id; |
735 |
|
736 |
if (args[0] & 7) { |
737 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
738 |
return;
|
739 |
} |
740 |
periph_id = (args[0] >> 3) & 0x1f; |
741 |
if (periph_id >= ch->parent->num_periph_req) {
|
742 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
743 |
return;
|
744 |
} |
745 |
if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { |
746 |
pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); |
747 |
return;
|
748 |
} |
749 |
pl330_dmald(ch, opcode, args, len); |
750 |
} |
751 |
|
752 |
static void pl330_dmalp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
753 |
{ |
754 |
uint8_t lc = (opcode & 2) >> 1; |
755 |
|
756 |
ch->lc[lc] = args[0];
|
757 |
} |
758 |
|
759 |
static void pl330_dmakill(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
760 |
{ |
761 |
if (ch->state == pl330_chan_fault ||
|
762 |
ch->state == pl330_chan_fault_completing) { |
763 |
/* This is the only way for a channel to leave the faulting state */
|
764 |
ch->fault_type = 0;
|
765 |
ch->parent->num_faulting--; |
766 |
if (ch->parent->num_faulting == 0) { |
767 |
DB_PRINT("abort interrupt lowered\n");
|
768 |
qemu_irq_lower(ch->parent->irq_abort); |
769 |
} |
770 |
} |
771 |
ch->state = pl330_chan_killing; |
772 |
pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag); |
773 |
pl330_queue_remove_tagged(&ch->parent->read_queue, ch->tag); |
774 |
pl330_queue_remove_tagged(&ch->parent->write_queue, ch->tag); |
775 |
ch->state = pl330_chan_stopped; |
776 |
} |
777 |
|
778 |
static void pl330_dmalpend(PL330Chan *ch, uint8_t opcode, |
779 |
uint8_t *args, int len)
|
780 |
{ |
781 |
uint8_t nf = (opcode & 0x10) >> 4; |
782 |
uint8_t bs = opcode & 3;
|
783 |
uint8_t lc = (opcode & 4) >> 2; |
784 |
|
785 |
if (bs == 2) { |
786 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
787 |
return;
|
788 |
} |
789 |
if ((bs == 1 && ch->request_flag == PL330_BURST) || |
790 |
(bs == 3 && ch->request_flag == PL330_SINGLE)) {
|
791 |
/* Perform NOP */
|
792 |
return;
|
793 |
} |
794 |
if (!nf || ch->lc[lc]) {
|
795 |
if (nf) {
|
796 |
ch->lc[lc]--; |
797 |
} |
798 |
DB_PRINT("loop reiteration\n");
|
799 |
ch->pc -= args[0];
|
800 |
ch->pc -= len + 1;
|
801 |
/* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */
|
802 |
} else {
|
803 |
DB_PRINT("loop fallthrough\n");
|
804 |
} |
805 |
} |
806 |
|
807 |
|
808 |
static void pl330_dmamov(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
809 |
{ |
810 |
uint8_t rd = args[0] & 7; |
811 |
uint32_t im; |
812 |
|
813 |
if ((args[0] >> 3)) { |
814 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
815 |
return;
|
816 |
} |
817 |
im = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) | |
818 |
(((uint32_t)args[2]) << 8) | (((uint32_t)args[1])); |
819 |
switch (rd) {
|
820 |
case 0: |
821 |
ch->src = im; |
822 |
break;
|
823 |
case 1: |
824 |
ch->control = im; |
825 |
break;
|
826 |
case 2: |
827 |
ch->dst = im; |
828 |
break;
|
829 |
default:
|
830 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
831 |
return;
|
832 |
} |
833 |
} |
834 |
|
835 |
static void pl330_dmanop(PL330Chan *ch, uint8_t opcode, |
836 |
uint8_t *args, int len)
|
837 |
{ |
838 |
/* NOP is NOP. */
|
839 |
} |
840 |
|
841 |
static void pl330_dmarmb(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
842 |
{ |
843 |
if (pl330_queue_find_insn(&ch->parent->read_queue, ch->tag, false)) { |
844 |
ch->state = pl330_chan_at_barrier; |
845 |
ch->stall = 1;
|
846 |
return;
|
847 |
} else {
|
848 |
ch->state = pl330_chan_executing; |
849 |
} |
850 |
} |
851 |
|
852 |
static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
853 |
{ |
854 |
uint8_t ev_id; |
855 |
|
856 |
if (args[0] & 7) { |
857 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
858 |
return;
|
859 |
} |
860 |
ev_id = (args[0] >> 3) & 0x1f; |
861 |
if (ev_id >= ch->parent->num_events) {
|
862 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
863 |
return;
|
864 |
} |
865 |
if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) { |
866 |
pl330_fault(ch, PL330_FAULT_EVENT_ERR); |
867 |
return;
|
868 |
} |
869 |
if (ch->parent->inten & (1 << ev_id)) { |
870 |
ch->parent->int_status |= (1 << ev_id);
|
871 |
DB_PRINT("event interrupt raised %d\n", ev_id);
|
872 |
qemu_irq_raise(ch->parent->irq[ev_id]); |
873 |
} |
874 |
ch->parent->ev_status |= (1 << ev_id);
|
875 |
} |
876 |
|
877 |
static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) |
878 |
{ |
879 |
uint8_t bs = opcode & 3;
|
880 |
uint32_t size, num; |
881 |
bool inc;
|
882 |
|
883 |
if (bs == 2) { |
884 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
885 |
return;
|
886 |
} |
887 |
if ((bs == 1 && ch->request_flag == PL330_BURST) || |
888 |
(bs == 3 && ch->request_flag == PL330_SINGLE)) {
|
889 |
/* Perform NOP */
|
890 |
return;
|
891 |
} |
892 |
num = ((ch->control >> 18) & 0xf) + 1; |
893 |
size = (uint32_t)1 << ((ch->control >> 15) & 0x7); |
894 |
inc = !!((ch->control >> 14) & 1); |
895 |
ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, |
896 |
size, num, inc, 0, ch->tag);
|
897 |
if (!ch->stall) {
|
898 |
DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n",
|
899 |
ch->tag, ch->dst, size, num, inc ? 'Y' : 'N'); |
900 |
ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0; |
901 |
} |
902 |
} |
903 |
|
904 |
static void pl330_dmastp(PL330Chan *ch, uint8_t opcode, |
905 |
uint8_t *args, int len)
|
906 |
{ |
907 |
uint8_t periph_id; |
908 |
|
909 |
if (args[0] & 7) { |
910 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
911 |
return;
|
912 |
} |
913 |
periph_id = (args[0] >> 3) & 0x1f; |
914 |
if (periph_id >= ch->parent->num_periph_req) {
|
915 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
916 |
return;
|
917 |
} |
918 |
if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { |
919 |
pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); |
920 |
return;
|
921 |
} |
922 |
pl330_dmast(ch, opcode, args, len); |
923 |
} |
924 |
|
925 |
static void pl330_dmastz(PL330Chan *ch, uint8_t opcode, |
926 |
uint8_t *args, int len)
|
927 |
{ |
928 |
uint32_t size, num; |
929 |
bool inc;
|
930 |
|
931 |
num = ((ch->control >> 18) & 0xf) + 1; |
932 |
size = (uint32_t)1 << ((ch->control >> 15) & 0x7); |
933 |
inc = !!((ch->control >> 14) & 1); |
934 |
ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, |
935 |
size, num, inc, 1, ch->tag);
|
936 |
if (inc) {
|
937 |
ch->dst += size * num; |
938 |
} |
939 |
} |
940 |
|
941 |
static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode, |
942 |
uint8_t *args, int len)
|
943 |
{ |
944 |
uint8_t ev_id; |
945 |
int i;
|
946 |
|
947 |
if (args[0] & 5) { |
948 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
949 |
return;
|
950 |
} |
951 |
ev_id = (args[0] >> 3) & 0x1f; |
952 |
if (ev_id >= ch->parent->num_events) {
|
953 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
954 |
return;
|
955 |
} |
956 |
if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) { |
957 |
pl330_fault(ch, PL330_FAULT_EVENT_ERR); |
958 |
return;
|
959 |
} |
960 |
ch->wakeup = ev_id; |
961 |
ch->state = pl330_chan_waiting_event; |
962 |
if (~ch->parent->inten & ch->parent->ev_status & 1 << ev_id) { |
963 |
ch->state = pl330_chan_executing; |
964 |
/* If anyone else is currently waiting on the same event, let them
|
965 |
* clear the ev_status so they pick up event as well
|
966 |
*/
|
967 |
for (i = 0; i < ch->parent->num_chnls; ++i) { |
968 |
PL330Chan *peer = &ch->parent->chan[i]; |
969 |
if (peer->state == pl330_chan_waiting_event &&
|
970 |
peer->wakeup == ev_id) { |
971 |
return;
|
972 |
} |
973 |
} |
974 |
ch->parent->ev_status &= ~(1 << ev_id);
|
975 |
} else {
|
976 |
ch->stall = 1;
|
977 |
} |
978 |
} |
979 |
|
980 |
static void pl330_dmawfp(PL330Chan *ch, uint8_t opcode, |
981 |
uint8_t *args, int len)
|
982 |
{ |
983 |
uint8_t bs = opcode & 3;
|
984 |
uint8_t periph_id; |
985 |
|
986 |
if (args[0] & 7) { |
987 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
988 |
return;
|
989 |
} |
990 |
periph_id = (args[0] >> 3) & 0x1f; |
991 |
if (periph_id >= ch->parent->num_periph_req) {
|
992 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
993 |
return;
|
994 |
} |
995 |
if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { |
996 |
pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); |
997 |
return;
|
998 |
} |
999 |
switch (bs) {
|
1000 |
case 0: /* S */ |
1001 |
ch->request_flag = PL330_SINGLE; |
1002 |
ch->wfp_sbp = 0;
|
1003 |
break;
|
1004 |
case 1: /* P */ |
1005 |
ch->request_flag = PL330_BURST; |
1006 |
ch->wfp_sbp = 2;
|
1007 |
break;
|
1008 |
case 2: /* B */ |
1009 |
ch->request_flag = PL330_BURST; |
1010 |
ch->wfp_sbp = 1;
|
1011 |
break;
|
1012 |
default:
|
1013 |
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); |
1014 |
return;
|
1015 |
} |
1016 |
|
1017 |
if (ch->parent->periph_busy[periph_id]) {
|
1018 |
ch->state = pl330_chan_waiting_periph; |
1019 |
ch->stall = 1;
|
1020 |
} else if (ch->state == pl330_chan_waiting_periph) { |
1021 |
ch->state = pl330_chan_executing; |
1022 |
} |
1023 |
} |
1024 |
|
1025 |
static void pl330_dmawmb(PL330Chan *ch, uint8_t opcode, |
1026 |
uint8_t *args, int len)
|
1027 |
{ |
1028 |
if (pl330_queue_find_insn(&ch->parent->write_queue, ch->tag, false)) { |
1029 |
ch->state = pl330_chan_at_barrier; |
1030 |
ch->stall = 1;
|
1031 |
return;
|
1032 |
} else {
|
1033 |
ch->state = pl330_chan_executing; |
1034 |
} |
1035 |
} |
1036 |
|
1037 |
/* NULL terminated array of the instruction descriptions. */
|
1038 |
static const PL330InsnDesc insn_desc[] = { |
1039 |
{ .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, }, |
1040 |
{ .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, }, |
1041 |
{ .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, }, |
1042 |
{ .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, |
1043 |
{ .opcode = 0x04, .opmask = 0xFC, .size = 1, .exec = pl330_dmald, }, |
1044 |
{ .opcode = 0x25, .opmask = 0xFD, .size = 2, .exec = pl330_dmaldp, }, |
1045 |
{ .opcode = 0x20, .opmask = 0xFD, .size = 2, .exec = pl330_dmalp, }, |
1046 |
/* dmastp must be before dmalpend in this list, because their maps
|
1047 |
* are overlapping
|
1048 |
*/
|
1049 |
{ .opcode = 0x29, .opmask = 0xFD, .size = 2, .exec = pl330_dmastp, }, |
1050 |
{ .opcode = 0x28, .opmask = 0xE8, .size = 2, .exec = pl330_dmalpend, }, |
1051 |
{ .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, }, |
1052 |
{ .opcode = 0xBC, .opmask = 0xFF, .size = 6, .exec = pl330_dmamov, }, |
1053 |
{ .opcode = 0x18, .opmask = 0xFF, .size = 1, .exec = pl330_dmanop, }, |
1054 |
{ .opcode = 0x12, .opmask = 0xFF, .size = 1, .exec = pl330_dmarmb, }, |
1055 |
{ .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, }, |
1056 |
{ .opcode = 0x08, .opmask = 0xFC, .size = 1, .exec = pl330_dmast, }, |
1057 |
{ .opcode = 0x0C, .opmask = 0xFF, .size = 1, .exec = pl330_dmastz, }, |
1058 |
{ .opcode = 0x36, .opmask = 0xFF, .size = 2, .exec = pl330_dmawfe, }, |
1059 |
{ .opcode = 0x30, .opmask = 0xFC, .size = 2, .exec = pl330_dmawfp, }, |
1060 |
{ .opcode = 0x13, .opmask = 0xFF, .size = 1, .exec = pl330_dmawmb, }, |
1061 |
{ .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, } |
1062 |
}; |
1063 |
|
1064 |
/* Instructions which can be issued via debug registers. */
|
1065 |
static const PL330InsnDesc debug_insn_desc[] = { |
1066 |
{ .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, |
1067 |
{ .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, }, |
1068 |
{ .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, }, |
1069 |
{ .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, } |
1070 |
}; |
1071 |
|
1072 |
static inline const PL330InsnDesc *pl330_fetch_insn(PL330Chan *ch) |
1073 |
{ |
1074 |
uint8_t opcode; |
1075 |
int i;
|
1076 |
|
1077 |
dma_memory_read(&address_space_memory, ch->pc, &opcode, 1);
|
1078 |
for (i = 0; insn_desc[i].size; i++) { |
1079 |
if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode) {
|
1080 |
return &insn_desc[i];
|
1081 |
} |
1082 |
} |
1083 |
return NULL; |
1084 |
} |
1085 |
|
1086 |
static inline void pl330_exec_insn(PL330Chan *ch, const PL330InsnDesc *insn) |
1087 |
{ |
1088 |
uint8_t buf[PL330_INSN_MAXSIZE]; |
1089 |
|
1090 |
assert(insn->size <= PL330_INSN_MAXSIZE); |
1091 |
dma_memory_read(&address_space_memory, ch->pc, buf, insn->size); |
1092 |
insn->exec(ch, buf[0], &buf[1], insn->size - 1); |
1093 |
} |
1094 |
|
1095 |
static inline void pl330_update_pc(PL330Chan *ch, |
1096 |
const PL330InsnDesc *insn)
|
1097 |
{ |
1098 |
ch->pc += insn->size; |
1099 |
} |
1100 |
|
1101 |
/* Try to execute current instruction in channel CH. Number of executed
|
1102 |
instructions is returned (0 or 1). */
|
1103 |
static int pl330_chan_exec(PL330Chan *ch) |
1104 |
{ |
1105 |
const PL330InsnDesc *insn;
|
1106 |
|
1107 |
if (ch->state != pl330_chan_executing &&
|
1108 |
ch->state != pl330_chan_waiting_periph && |
1109 |
ch->state != pl330_chan_at_barrier && |
1110 |
ch->state != pl330_chan_waiting_event) { |
1111 |
DB_PRINT("%d\n", ch->state);
|
1112 |
return 0; |
1113 |
} |
1114 |
ch->stall = 0;
|
1115 |
insn = pl330_fetch_insn(ch); |
1116 |
if (!insn) {
|
1117 |
DB_PRINT("pl330 undefined instruction\n");
|
1118 |
pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); |
1119 |
return 0; |
1120 |
} |
1121 |
pl330_exec_insn(ch, insn); |
1122 |
if (!ch->stall) {
|
1123 |
pl330_update_pc(ch, insn); |
1124 |
ch->watchdog_timer = 0;
|
1125 |
return 1; |
1126 |
/* WDT only active in exec state */
|
1127 |
} else if (ch->state == pl330_chan_executing) { |
1128 |
ch->watchdog_timer++; |
1129 |
if (ch->watchdog_timer >= PL330_WATCHDOG_LIMIT) {
|
1130 |
pl330_fault(ch, PL330_FAULT_LOCKUP_ERR); |
1131 |
} |
1132 |
} |
1133 |
return 0; |
1134 |
} |
1135 |
|
1136 |
/* Try to execute 1 instruction in each channel, one instruction from read
|
1137 |
queue and one instruction from write queue. Number of successfully executed
|
1138 |
instructions is returned. */
|
1139 |
static int pl330_exec_cycle(PL330Chan *channel) |
1140 |
{ |
1141 |
PL330State *s = channel->parent; |
1142 |
PL330QueueEntry *q; |
1143 |
int i;
|
1144 |
int num_exec = 0; |
1145 |
int fifo_res = 0; |
1146 |
uint8_t buf[PL330_MAX_BURST_LEN]; |
1147 |
|
1148 |
/* Execute one instruction in each channel */
|
1149 |
num_exec += pl330_chan_exec(channel); |
1150 |
|
1151 |
/* Execute one instruction from read queue */
|
1152 |
q = pl330_queue_find_insn(&s->read_queue, PL330_UNTAGGED, true);
|
1153 |
if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) { |
1154 |
int len = q->len - (q->addr & (q->len - 1)); |
1155 |
|
1156 |
dma_memory_read(&address_space_memory, q->addr, buf, len); |
1157 |
if (PL330_ERR_DEBUG > 1) { |
1158 |
DB_PRINT("PL330 read from memory @%08x (size = %08x):\n",
|
1159 |
q->addr, len); |
1160 |
qemu_hexdump((char *)buf, stderr, "", len); |
1161 |
} |
1162 |
fifo_res = pl330_fifo_push(&s->fifo, buf, len, q->tag); |
1163 |
if (fifo_res == PL330_FIFO_OK) {
|
1164 |
if (q->inc) {
|
1165 |
q->addr += len; |
1166 |
} |
1167 |
q->n--; |
1168 |
if (!q->n) {
|
1169 |
pl330_queue_remove_insn(&s->read_queue, q); |
1170 |
} |
1171 |
num_exec++; |
1172 |
} |
1173 |
} |
1174 |
|
1175 |
/* Execute one instruction from write queue. */
|
1176 |
q = pl330_queue_find_insn(&s->write_queue, pl330_fifo_tag(&s->fifo), true);
|
1177 |
if (q != NULL) { |
1178 |
int len = q->len - (q->addr & (q->len - 1)); |
1179 |
|
1180 |
if (q->z) {
|
1181 |
for (i = 0; i < len; i++) { |
1182 |
buf[i] = 0;
|
1183 |
} |
1184 |
} else {
|
1185 |
fifo_res = pl330_fifo_get(&s->fifo, buf, len, q->tag); |
1186 |
} |
1187 |
if (fifo_res == PL330_FIFO_OK || q->z) {
|
1188 |
dma_memory_write(&address_space_memory, q->addr, buf, len); |
1189 |
if (PL330_ERR_DEBUG > 1) { |
1190 |
DB_PRINT("PL330 read from memory @%08x (size = %08x):\n",
|
1191 |
q->addr, len); |
1192 |
qemu_hexdump((char *)buf, stderr, "", len); |
1193 |
} |
1194 |
if (q->inc) {
|
1195 |
q->addr += len; |
1196 |
} |
1197 |
num_exec++; |
1198 |
} else if (fifo_res == PL330_FIFO_STALL) { |
1199 |
pl330_fault(&channel->parent->chan[q->tag], |
1200 |
PL330_FAULT_FIFOEMPTY_ERR); |
1201 |
} |
1202 |
q->n--; |
1203 |
if (!q->n) {
|
1204 |
pl330_queue_remove_insn(&s->write_queue, q); |
1205 |
} |
1206 |
} |
1207 |
|
1208 |
return num_exec;
|
1209 |
} |
1210 |
|
1211 |
static int pl330_exec_channel(PL330Chan *channel) |
1212 |
{ |
1213 |
int insr_exec = 0; |
1214 |
|
1215 |
/* TODO: Is it all right to execute everything or should we do per-cycle
|
1216 |
simulation? */
|
1217 |
while (pl330_exec_cycle(channel)) {
|
1218 |
insr_exec++; |
1219 |
} |
1220 |
|
1221 |
/* Detect deadlock */
|
1222 |
if (channel->state == pl330_chan_executing) {
|
1223 |
pl330_fault(channel, PL330_FAULT_LOCKUP_ERR); |
1224 |
} |
1225 |
/* Situation when one of the queues has deadlocked but all channels
|
1226 |
* have finished their programs should be impossible.
|
1227 |
*/
|
1228 |
|
1229 |
return insr_exec;
|
1230 |
} |
1231 |
|
1232 |
static inline void pl330_exec(PL330State *s) |
1233 |
{ |
1234 |
DB_PRINT("\n");
|
1235 |
int i, insr_exec;
|
1236 |
do {
|
1237 |
insr_exec = pl330_exec_channel(&s->manager); |
1238 |
|
1239 |
for (i = 0; i < s->num_chnls; i++) { |
1240 |
insr_exec += pl330_exec_channel(&s->chan[i]); |
1241 |
} |
1242 |
} while (insr_exec);
|
1243 |
} |
1244 |
|
1245 |
static void pl330_exec_cycle_timer(void *opaque) |
1246 |
{ |
1247 |
PL330State *s = (PL330State *)opaque; |
1248 |
pl330_exec(s); |
1249 |
} |
1250 |
|
1251 |
/* Stop or restore dma operations */
|
1252 |
|
1253 |
static void pl330_dma_stop_irq(void *opaque, int irq, int level) |
1254 |
{ |
1255 |
PL330State *s = (PL330State *)opaque; |
1256 |
|
1257 |
if (s->periph_busy[irq] != level) {
|
1258 |
s->periph_busy[irq] = level; |
1259 |
qemu_mod_timer(s->timer, qemu_get_clock_ns(vm_clock)); |
1260 |
} |
1261 |
} |
1262 |
|
1263 |
static void pl330_debug_exec(PL330State *s) |
1264 |
{ |
1265 |
uint8_t args[5];
|
1266 |
uint8_t opcode; |
1267 |
uint8_t chan_id; |
1268 |
int i;
|
1269 |
PL330Chan *ch; |
1270 |
const PL330InsnDesc *insn;
|
1271 |
|
1272 |
s->debug_status = 1;
|
1273 |
chan_id = (s->dbg[0] >> 8) & 0x07; |
1274 |
opcode = (s->dbg[0] >> 16) & 0xff; |
1275 |
args[0] = (s->dbg[0] >> 24) & 0xff; |
1276 |
args[1] = (s->dbg[1] >> 0) & 0xff; |
1277 |
args[2] = (s->dbg[1] >> 8) & 0xff; |
1278 |
args[3] = (s->dbg[1] >> 16) & 0xff; |
1279 |
args[4] = (s->dbg[1] >> 24) & 0xff; |
1280 |
DB_PRINT("chan id: %d\n", chan_id);
|
1281 |
if (s->dbg[0] & 1) { |
1282 |
ch = &s->chan[chan_id]; |
1283 |
} else {
|
1284 |
ch = &s->manager; |
1285 |
} |
1286 |
insn = NULL;
|
1287 |
for (i = 0; debug_insn_desc[i].size; i++) { |
1288 |
if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode) {
|
1289 |
insn = &debug_insn_desc[i]; |
1290 |
} |
1291 |
} |
1292 |
if (!insn) {
|
1293 |
pl330_fault(ch, PL330_FAULT_UNDEF_INSTR | PL330_FAULT_DBG_INSTR); |
1294 |
return ;
|
1295 |
} |
1296 |
ch->stall = 0;
|
1297 |
insn->exec(ch, opcode, args, insn->size - 1);
|
1298 |
if (ch->fault_type) {
|
1299 |
ch->fault_type |= PL330_FAULT_DBG_INSTR; |
1300 |
} |
1301 |
if (ch->stall) {
|
1302 |
qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not "
|
1303 |
"implemented\n");
|
1304 |
} |
1305 |
s->debug_status = 0;
|
1306 |
} |
1307 |
|
1308 |
/* IOMEM mapped registers */
|
1309 |
|
1310 |
static void pl330_iomem_write(void *opaque, hwaddr offset, |
1311 |
uint64_t value, unsigned size)
|
1312 |
{ |
1313 |
PL330State *s = (PL330State *) opaque; |
1314 |
uint32_t i; |
1315 |
|
1316 |
DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value); |
1317 |
|
1318 |
switch (offset) {
|
1319 |
case PL330_REG_INTEN:
|
1320 |
s->inten = value; |
1321 |
break;
|
1322 |
case PL330_REG_INTCLR:
|
1323 |
for (i = 0; i < s->num_events; i++) { |
1324 |
if (s->int_status & s->inten & value & (1 << i)) { |
1325 |
DB_PRINT("event interrupt lowered %d\n", i);
|
1326 |
qemu_irq_lower(s->irq[i]); |
1327 |
} |
1328 |
} |
1329 |
s->ev_status &= ~(value & s->inten); |
1330 |
s->int_status &= ~(value & s->inten); |
1331 |
break;
|
1332 |
case PL330_REG_DBGCMD:
|
1333 |
if ((value & 3) == 0) { |
1334 |
pl330_debug_exec(s); |
1335 |
pl330_exec(s); |
1336 |
} else {
|
1337 |
qemu_log_mask(LOG_GUEST_ERROR, "pl330: write of illegal value %u "
|
1338 |
"for offset " TARGET_FMT_plx "\n", (unsigned)value, |
1339 |
offset); |
1340 |
} |
1341 |
break;
|
1342 |
case PL330_REG_DBGINST0:
|
1343 |
DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value); |
1344 |
s->dbg[0] = value;
|
1345 |
break;
|
1346 |
case PL330_REG_DBGINST1:
|
1347 |
DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value); |
1348 |
s->dbg[1] = value;
|
1349 |
break;
|
1350 |
default:
|
1351 |
qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " TARGET_FMT_plx
|
1352 |
"\n", offset);
|
1353 |
break;
|
1354 |
} |
1355 |
} |
1356 |
|
1357 |
static inline uint32_t pl330_iomem_read_imp(void *opaque, |
1358 |
hwaddr offset) |
1359 |
{ |
1360 |
PL330State *s = (PL330State *)opaque; |
1361 |
int chan_id;
|
1362 |
int i;
|
1363 |
uint32_t res; |
1364 |
|
1365 |
if (offset >= PL330_REG_PERIPH_ID && offset < PL330_REG_PERIPH_ID + 32) { |
1366 |
return pl330_id[(offset - PL330_REG_PERIPH_ID) >> 2]; |
1367 |
} |
1368 |
if (offset >= PL330_REG_CR0_BASE && offset < PL330_REG_CR0_BASE + 24) { |
1369 |
return s->cfg[(offset - PL330_REG_CR0_BASE) >> 2]; |
1370 |
} |
1371 |
if (offset >= PL330_REG_CHANCTRL && offset < PL330_REG_DBGSTATUS) {
|
1372 |
offset -= PL330_REG_CHANCTRL; |
1373 |
chan_id = offset >> 5;
|
1374 |
if (chan_id >= s->num_chnls) {
|
1375 |
qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
|
1376 |
TARGET_FMT_plx "\n", offset);
|
1377 |
return 0; |
1378 |
} |
1379 |
switch (offset & 0x1f) { |
1380 |
case 0x00: |
1381 |
return s->chan[chan_id].src;
|
1382 |
case 0x04: |
1383 |
return s->chan[chan_id].dst;
|
1384 |
case 0x08: |
1385 |
return s->chan[chan_id].control;
|
1386 |
case 0x0C: |
1387 |
return s->chan[chan_id].lc[0]; |
1388 |
case 0x10: |
1389 |
return s->chan[chan_id].lc[1]; |
1390 |
default:
|
1391 |
qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
|
1392 |
TARGET_FMT_plx "\n", offset);
|
1393 |
return 0; |
1394 |
} |
1395 |
} |
1396 |
if (offset >= PL330_REG_CSR_BASE && offset < 0x400) { |
1397 |
offset -= PL330_REG_CSR_BASE; |
1398 |
chan_id = offset >> 3;
|
1399 |
if (chan_id >= s->num_chnls) {
|
1400 |
qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
|
1401 |
TARGET_FMT_plx "\n", offset);
|
1402 |
return 0; |
1403 |
} |
1404 |
switch ((offset >> 2) & 1) { |
1405 |
case 0x0: |
1406 |
res = (s->chan[chan_id].ns << 21) |
|
1407 |
(s->chan[chan_id].wakeup << 4) |
|
1408 |
(s->chan[chan_id].state) | |
1409 |
(s->chan[chan_id].wfp_sbp << 14);
|
1410 |
return res;
|
1411 |
case 0x1: |
1412 |
return s->chan[chan_id].pc;
|
1413 |
default:
|
1414 |
qemu_log_mask(LOG_GUEST_ERROR, "pl330: read error\n");
|
1415 |
return 0; |
1416 |
} |
1417 |
} |
1418 |
if (offset >= PL330_REG_FTR_BASE && offset < 0x100) { |
1419 |
offset -= PL330_REG_FTR_BASE; |
1420 |
chan_id = offset >> 2;
|
1421 |
if (chan_id >= s->num_chnls) {
|
1422 |
qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
|
1423 |
TARGET_FMT_plx "\n", offset);
|
1424 |
return 0; |
1425 |
} |
1426 |
return s->chan[chan_id].fault_type;
|
1427 |
} |
1428 |
switch (offset) {
|
1429 |
case PL330_REG_DSR:
|
1430 |
return (s->manager.ns << 9) | (s->manager.wakeup << 4) | |
1431 |
(s->manager.state & 0xf);
|
1432 |
case PL330_REG_DPC:
|
1433 |
return s->manager.pc;
|
1434 |
case PL330_REG_INTEN:
|
1435 |
return s->inten;
|
1436 |
case PL330_REG_INT_EVENT_RIS:
|
1437 |
return s->ev_status;
|
1438 |
case PL330_REG_INTMIS:
|
1439 |
return s->int_status;
|
1440 |
case PL330_REG_INTCLR:
|
1441 |
/* Documentation says that we can't read this register
|
1442 |
* but linux kernel does it
|
1443 |
*/
|
1444 |
return 0; |
1445 |
case PL330_REG_FSRD:
|
1446 |
return s->manager.state ? 1 : 0; |
1447 |
case PL330_REG_FSRC:
|
1448 |
res = 0;
|
1449 |
for (i = 0; i < s->num_chnls; i++) { |
1450 |
if (s->chan[i].state == pl330_chan_fault ||
|
1451 |
s->chan[i].state == pl330_chan_fault_completing) { |
1452 |
res |= 1 << i;
|
1453 |
} |
1454 |
} |
1455 |
return res;
|
1456 |
case PL330_REG_FTRD:
|
1457 |
return s->manager.fault_type;
|
1458 |
case PL330_REG_DBGSTATUS:
|
1459 |
return s->debug_status;
|
1460 |
default:
|
1461 |
qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
|
1462 |
TARGET_FMT_plx "\n", offset);
|
1463 |
} |
1464 |
return 0; |
1465 |
} |
1466 |
|
1467 |
static uint64_t pl330_iomem_read(void *opaque, hwaddr offset, |
1468 |
unsigned size)
|
1469 |
{ |
1470 |
int ret = pl330_iomem_read_imp(opaque, offset);
|
1471 |
DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, ret); |
1472 |
return ret;
|
1473 |
} |
1474 |
|
1475 |
static const MemoryRegionOps pl330_ops = { |
1476 |
.read = pl330_iomem_read, |
1477 |
.write = pl330_iomem_write, |
1478 |
.endianness = DEVICE_NATIVE_ENDIAN, |
1479 |
.impl = { |
1480 |
.min_access_size = 4,
|
1481 |
.max_access_size = 4,
|
1482 |
} |
1483 |
}; |
1484 |
|
1485 |
/* Controller logic and initialization */
|
1486 |
|
1487 |
static void pl330_chan_reset(PL330Chan *ch) |
1488 |
{ |
1489 |
ch->src = 0;
|
1490 |
ch->dst = 0;
|
1491 |
ch->pc = 0;
|
1492 |
ch->state = pl330_chan_stopped; |
1493 |
ch->watchdog_timer = 0;
|
1494 |
ch->stall = 0;
|
1495 |
ch->control = 0;
|
1496 |
ch->status = 0;
|
1497 |
ch->fault_type = 0;
|
1498 |
} |
1499 |
|
1500 |
static void pl330_reset(DeviceState *d) |
1501 |
{ |
1502 |
int i;
|
1503 |
PL330State *s = PL330(d); |
1504 |
|
1505 |
s->inten = 0;
|
1506 |
s->int_status = 0;
|
1507 |
s->ev_status = 0;
|
1508 |
s->debug_status = 0;
|
1509 |
s->num_faulting = 0;
|
1510 |
s->manager.ns = s->mgr_ns_at_rst; |
1511 |
pl330_fifo_reset(&s->fifo); |
1512 |
pl330_queue_reset(&s->read_queue); |
1513 |
pl330_queue_reset(&s->write_queue); |
1514 |
|
1515 |
for (i = 0; i < s->num_chnls; i++) { |
1516 |
pl330_chan_reset(&s->chan[i]); |
1517 |
} |
1518 |
for (i = 0; i < s->num_periph_req; i++) { |
1519 |
s->periph_busy[i] = 0;
|
1520 |
} |
1521 |
|
1522 |
qemu_del_timer(s->timer); |
1523 |
} |
1524 |
|
1525 |
static void pl330_realize(DeviceState *dev, Error **errp) |
1526 |
{ |
1527 |
int i;
|
1528 |
PL330State *s = PL330(dev); |
1529 |
|
1530 |
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_abort); |
1531 |
memory_region_init_io(&s->iomem, &pl330_ops, s, "dma", PL330_IOMEM_SIZE);
|
1532 |
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); |
1533 |
|
1534 |
s->timer = qemu_new_timer_ns(vm_clock, pl330_exec_cycle_timer, s); |
1535 |
|
1536 |
s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) | |
1537 |
(s->num_periph_req > 0 ? 1 : 0) | |
1538 |
((s->num_chnls - 1) & 0x7) << 4 | |
1539 |
((s->num_periph_req - 1) & 0x1f) << 12 | |
1540 |
((s->num_events - 1) & 0x1f) << 17; |
1541 |
|
1542 |
switch (s->i_cache_len) {
|
1543 |
case (4): |
1544 |
s->cfg[1] |= 2; |
1545 |
break;
|
1546 |
case (8): |
1547 |
s->cfg[1] |= 3; |
1548 |
break;
|
1549 |
case (16): |
1550 |
s->cfg[1] |= 4; |
1551 |
break;
|
1552 |
case (32): |
1553 |
s->cfg[1] |= 5; |
1554 |
break;
|
1555 |
default:
|
1556 |
error_setg(errp, "Bad value for i-cache_len property: %d\n",
|
1557 |
s->i_cache_len); |
1558 |
return;
|
1559 |
} |
1560 |
s->cfg[1] |= ((s->num_i_cache_lines - 1) & 0xf) << 4; |
1561 |
|
1562 |
s->chan = g_new0(PL330Chan, s->num_chnls); |
1563 |
s->hi_seqn = g_new0(uint8_t, s->num_chnls); |
1564 |
s->lo_seqn = g_new0(uint8_t, s->num_chnls); |
1565 |
for (i = 0; i < s->num_chnls; i++) { |
1566 |
s->chan[i].parent = s; |
1567 |
s->chan[i].tag = (uint8_t)i; |
1568 |
} |
1569 |
s->manager.parent = s; |
1570 |
s->manager.tag = s->num_chnls; |
1571 |
s->manager.is_manager = true;
|
1572 |
|
1573 |
s->irq = g_new0(qemu_irq, s->num_events); |
1574 |
for (i = 0; i < s->num_events; i++) { |
1575 |
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); |
1576 |
} |
1577 |
|
1578 |
qdev_init_gpio_in(dev, pl330_dma_stop_irq, PL330_PERIPH_NUM); |
1579 |
|
1580 |
switch (s->data_width) {
|
1581 |
case (32): |
1582 |
s->cfg[CFG_CRD] |= 0x2;
|
1583 |
break;
|
1584 |
case (64): |
1585 |
s->cfg[CFG_CRD] |= 0x3;
|
1586 |
break;
|
1587 |
case (128): |
1588 |
s->cfg[CFG_CRD] |= 0x4;
|
1589 |
break;
|
1590 |
default:
|
1591 |
error_setg(errp, "Bad value for data_width property: %d\n",
|
1592 |
s->data_width); |
1593 |
return;
|
1594 |
} |
1595 |
|
1596 |
s->cfg[CFG_CRD] |= ((s->wr_cap - 1) & 0x7) << 4 | |
1597 |
((s->wr_q_dep - 1) & 0xf) << 8 | |
1598 |
((s->rd_cap - 1) & 0x7) << 12 | |
1599 |
((s->rd_q_dep - 1) & 0xf) << 16 | |
1600 |
((s->data_buffer_dep - 1) & 0x1ff) << 20; |
1601 |
|
1602 |
pl330_queue_init(&s->read_queue, s->rd_q_dep, s); |
1603 |
pl330_queue_init(&s->write_queue, s->wr_q_dep, s); |
1604 |
pl330_fifo_init(&s->fifo, s->data_buffer_dep); |
1605 |
} |
1606 |
|
1607 |
static Property pl330_properties[] = {
|
1608 |
/* CR0 */
|
1609 |
DEFINE_PROP_UINT32("num_chnls", PL330State, num_chnls, 8), |
1610 |
DEFINE_PROP_UINT8("num_periph_req", PL330State, num_periph_req, 4), |
1611 |
DEFINE_PROP_UINT8("num_events", PL330State, num_events, 16), |
1612 |
DEFINE_PROP_UINT8("mgr_ns_at_rst", PL330State, mgr_ns_at_rst, 0), |
1613 |
/* CR1 */
|
1614 |
DEFINE_PROP_UINT8("i-cache_len", PL330State, i_cache_len, 4), |
1615 |
DEFINE_PROP_UINT8("num_i-cache_lines", PL330State, num_i_cache_lines, 8), |
1616 |
/* CR2-4 */
|
1617 |
DEFINE_PROP_UINT32("boot_addr", PL330State, cfg[CFG_BOOT_ADDR], 0), |
1618 |
DEFINE_PROP_UINT32("INS", PL330State, cfg[CFG_INS], 0), |
1619 |
DEFINE_PROP_UINT32("PNS", PL330State, cfg[CFG_PNS], 0), |
1620 |
/* CRD */
|
1621 |
DEFINE_PROP_UINT8("data_width", PL330State, data_width, 64), |
1622 |
DEFINE_PROP_UINT8("wr_cap", PL330State, wr_cap, 8), |
1623 |
DEFINE_PROP_UINT8("wr_q_dep", PL330State, wr_q_dep, 16), |
1624 |
DEFINE_PROP_UINT8("rd_cap", PL330State, rd_cap, 8), |
1625 |
DEFINE_PROP_UINT8("rd_q_dep", PL330State, rd_q_dep, 16), |
1626 |
DEFINE_PROP_UINT16("data_buffer_dep", PL330State, data_buffer_dep, 256), |
1627 |
|
1628 |
DEFINE_PROP_END_OF_LIST(), |
1629 |
}; |
1630 |
|
1631 |
static void pl330_class_init(ObjectClass *klass, void *data) |
1632 |
{ |
1633 |
DeviceClass *dc = DEVICE_CLASS(klass); |
1634 |
|
1635 |
dc->realize = pl330_realize; |
1636 |
dc->reset = pl330_reset; |
1637 |
dc->props = pl330_properties; |
1638 |
dc->vmsd = &vmstate_pl330; |
1639 |
} |
1640 |
|
1641 |
static const TypeInfo pl330_type_info = { |
1642 |
.name = TYPE_PL330, |
1643 |
.parent = TYPE_SYS_BUS_DEVICE, |
1644 |
.instance_size = sizeof(PL330State),
|
1645 |
.class_init = pl330_class_init, |
1646 |
}; |
1647 |
|
1648 |
static void pl330_register_types(void) |
1649 |
{ |
1650 |
type_register_static(&pl330_type_info); |
1651 |
} |
1652 |
|
1653 |
type_init(pl330_register_types) |