|
|
1.1 root 1: /*
2: * Arm PrimeCell PL080 DMA controller
3: *
4: * Copyright (c) 2006 CodeSourcery.
5: * Written by Paul Brook
6: *
7: * This code is licenced under the GPL.
8: */
9:
10: #include "vl.h"
11:
12: #define PL080_NUM_CHANNELS 8
13: #define PL080_CONF_E 0x1
14: #define PL080_CONF_M1 0x2
15: #define PL080_CONF_M2 0x4
16:
17: #define PL080_CCONF_H 0x40000
18: #define PL080_CCONF_A 0x20000
19: #define PL080_CCONF_L 0x10000
20: #define PL080_CCONF_ITC 0x08000
21: #define PL080_CCONF_IE 0x04000
22: #define PL080_CCONF_E 0x00001
23:
24: #define PL080_CCTRL_I 0x80000000
25: #define PL080_CCTRL_DI 0x08000000
26: #define PL080_CCTRL_SI 0x04000000
27: #define PL080_CCTRL_D 0x02000000
28: #define PL080_CCTRL_S 0x01000000
29:
30: typedef struct {
31: uint32_t src;
32: uint32_t dest;
33: uint32_t lli;
34: uint32_t ctrl;
35: uint32_t conf;
36: } pl080_channel;
37:
38: typedef struct {
39: uint32_t base;
40: uint8_t tc_int;
41: uint8_t tc_mask;
42: uint8_t err_int;
43: uint8_t err_mask;
44: uint32_t conf;
45: uint32_t sync;
46: uint32_t req_single;
47: uint32_t req_burst;
48: pl080_channel chan[PL080_NUM_CHANNELS];
49: /* Flag to avoid recursive DMA invocations. */
50: int running;
51: void *pic;
52: int irq;
53: } pl080_state;
54:
55: static const unsigned char pl080_id[] =
56: { 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
57:
58: static void pl080_update(pl080_state *s)
59: {
60: if ((s->tc_int & s->tc_mask)
61: || (s->err_int & s->err_mask))
62: pic_set_irq_new(s->pic, s->irq, 1);
63: else
64: pic_set_irq_new(s->pic, s->irq, 1);
65: }
66:
67: static void pl080_run(pl080_state *s)
68: {
69: int c;
70: int flow;
71: pl080_channel *ch;
72: int swidth;
73: int dwidth;
74: int xsize;
75: int n;
76: int src_id;
77: int dest_id;
78: int size;
79: char buff[4];
80: uint32_t req;
81:
82: s->tc_mask = 0;
83: for (c = 0; c < PL080_NUM_CHANNELS; c++) {
84: if (s->chan[c].conf & PL080_CCONF_ITC)
85: s->tc_mask |= 1 << c;
86: if (s->chan[c].conf & PL080_CCONF_IE)
87: s->err_mask |= 1 << c;
88: }
89:
90: if ((s->conf & PL080_CONF_E) == 0)
91: return;
92:
93: cpu_abort(cpu_single_env, "DMA active\n");
94: /* If we are already in the middle of a DMA operation then indicate that
95: there may be new DMA requests and return immediately. */
96: if (s->running) {
97: s->running++;
98: return;
99: }
100: s->running = 1;
101: while (s->running) {
102: for (c = 0; c < PL080_NUM_CHANNELS; c++) {
103: ch = &s->chan[c];
104: again:
105: /* Test if thiws channel has any pending DMA requests. */
106: if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E))
107: != PL080_CCONF_E)
108: continue;
109: flow = (ch->conf >> 11) & 7;
110: if (flow >= 4) {
111: cpu_abort(cpu_single_env,
112: "pl080_run: Peripheral flow control not implemented\n");
113: }
114: src_id = (ch->conf >> 1) & 0x1f;
115: dest_id = (ch->conf >> 6) & 0x1f;
116: size = ch->ctrl & 0xfff;
117: req = s->req_single | s->req_burst;
118: switch (flow) {
119: case 0:
120: break;
121: case 1:
122: if ((req & (1u << dest_id)) == 0)
123: size = 0;
124: break;
125: case 2:
126: if ((req & (1u << src_id)) == 0)
127: size = 0;
128: break;
129: case 3:
130: if ((req & (1u << src_id)) == 0
131: || (req & (1u << dest_id)) == 0)
132: size = 0;
133: break;
134: }
135: if (!size)
136: continue;
137:
138: /* Transfer one element. */
139: /* ??? Should transfer multiple elements for a burst request. */
140: /* ??? Unclear what the proper behavior is when source and
141: destination widths are different. */
142: swidth = 1 << ((ch->ctrl >> 18) & 7);
143: dwidth = 1 << ((ch->ctrl >> 21) & 7);
144: for (n = 0; n < dwidth; n+= swidth) {
145: cpu_physical_memory_read(ch->src, buff + n, swidth);
146: if (ch->ctrl & PL080_CCTRL_SI)
147: ch->src += swidth;
148: }
149: xsize = (dwidth < swidth) ? swidth : dwidth;
150: /* ??? This may pad the value incorrectly for dwidth < 32. */
151: for (n = 0; n < xsize; n += dwidth) {
152: cpu_physical_memory_write(ch->dest + n, buff + n, dwidth);
153: if (ch->ctrl & PL080_CCTRL_DI)
154: ch->dest += swidth;
155: }
156:
157: size--;
158: ch->ctrl = (ch->ctrl & 0xfffff000) | size;
159: if (size == 0) {
160: /* Transfer complete. */
161: if (ch->lli) {
162: ch->src = ldl_phys(ch->lli);
163: ch->dest = ldl_phys(ch->lli + 4);
164: ch->ctrl = ldl_phys(ch->lli + 12);
165: ch->lli = ldl_phys(ch->lli + 8);
166: } else {
167: ch->conf &= ~PL080_CCONF_E;
168: }
169: if (ch->ctrl & PL080_CCTRL_I) {
170: s->tc_int |= 1 << c;
171: }
172: }
173: goto again;
174: }
175: if (--s->running)
176: s->running = 1;
177: }
178: }
179:
180: static uint32_t pl080_read(void *opaque, target_phys_addr_t offset)
181: {
182: pl080_state *s = (pl080_state *)opaque;
183: uint32_t i;
184: uint32_t mask;
185:
186: offset -= s->base;
187: if (offset >= 0xfe0 && offset < 0x1000) {
188: return pl080_id[(offset - 0xfe0) >> 2];
189: }
190: if (offset >= 0x100 && offset < 0x200) {
191: i = (offset & 0xe0) >> 5;
192: switch (offset >> 2) {
193: case 0: /* SrcAddr */
194: return s->chan[i].src;
195: case 1: /* DestAddr */
196: return s->chan[i].dest;
197: case 2: /* LLI */
198: return s->chan[i].lli;
199: case 3: /* Control */
200: return s->chan[i].ctrl;
201: case 4: /* Configuration */
202: return s->chan[i].conf;
203: default:
204: goto bad_offset;
205: }
206: }
207: switch (offset >> 2) {
208: case 0: /* IntStatus */
209: return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask);
210: case 1: /* IntTCStatus */
211: return (s->tc_int & s->tc_mask);
212: case 3: /* IntErrorStatus */
213: return (s->err_int & s->err_mask);
214: case 5: /* RawIntTCStatus */
215: return s->tc_int;
216: case 6: /* RawIntErrorStatus */
217: return s->err_int;
218: case 7: /* EnbldChns */
219: mask = 0;
220: for (i = 0; i < PL080_NUM_CHANNELS; i++) {
221: if (s->chan[i].conf & PL080_CCONF_E)
222: mask |= 1 << i;
223: }
224: return mask;
225: case 8: /* SoftBReq */
226: case 9: /* SoftSReq */
227: case 10: /* SoftLBReq */
228: case 11: /* SoftLSReq */
229: /* ??? Implement these. */
230: return 0;
231: case 12: /* Configuration */
232: return s->conf;
233: case 13: /* Sync */
234: return s->sync;
235: default:
236: bad_offset:
237: cpu_abort(cpu_single_env, "pl080_read: Bad offset %x\n", offset);
238: return 0;
239: }
240: }
241:
242: static void pl080_write(void *opaque, target_phys_addr_t offset,
243: uint32_t value)
244: {
245: pl080_state *s = (pl080_state *)opaque;
246: int i;
247:
248: offset -= s->base;
249: if (offset >= 0x100 && offset < 0x200) {
250: i = (offset & 0xe0) >> 5;
251: switch (offset >> 2) {
252: case 0: /* SrcAddr */
253: s->chan[i].src = value;
254: break;
255: case 1: /* DestAddr */
256: s->chan[i].dest = value;
257: break;
258: case 2: /* LLI */
259: s->chan[i].lli = value;
260: break;
261: case 3: /* Control */
262: s->chan[i].ctrl = value;
263: break;
264: case 4: /* Configuration */
265: s->chan[i].conf = value;
266: pl080_run(s);
267: break;
268: }
269: }
270: switch (offset >> 2) {
271: case 2: /* IntTCClear */
272: s->tc_int &= ~value;
273: break;
274: case 4: /* IntErrorClear */
275: s->err_int &= ~value;
276: break;
277: case 8: /* SoftBReq */
278: case 9: /* SoftSReq */
279: case 10: /* SoftLBReq */
280: case 11: /* SoftLSReq */
281: /* ??? Implement these. */
282: cpu_abort(cpu_single_env, "pl080_write: Soft DMA not implemented\n");
283: break;
284: case 12: /* Configuration */
285: s->conf = value;
286: if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
287: cpu_abort(cpu_single_env,
288: "pl080_write: Big-endian DMA not implemented\n");
289: }
290: pl080_run(s);
291: break;
292: case 13: /* Sync */
293: s->sync = value;
294: break;
295: default:
296: cpu_abort(cpu_single_env, "pl080_write: Bad offset %x\n", offset);
297: }
298: pl080_update(s);
299: }
300:
301: static CPUReadMemoryFunc *pl080_readfn[] = {
302: pl080_read,
303: pl080_read,
304: pl080_read
305: };
306:
307: static CPUWriteMemoryFunc *pl080_writefn[] = {
308: pl080_write,
309: pl080_write,
310: pl080_write
311: };
312:
313: void *pl080_init(uint32_t base, void *pic, int irq)
314: {
315: int iomemtype;
316: pl080_state *s;
317:
318: s = (pl080_state *)qemu_mallocz(sizeof(pl080_state));
319: iomemtype = cpu_register_io_memory(0, pl080_readfn,
320: pl080_writefn, s);
321: cpu_register_physical_memory(base, 0x00000fff, iomemtype);
322: s->base = base;
323: s->pic = pic;
324: s->irq = irq;
325: /* ??? Save/restore. */
326: return s;
327: }
328:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.