1 /*
2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 *
4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
5 * See the COPYING file in the top-level directory.
6 */
7
8 #include "qemu/osdep.h"
9 #include "qapi/error.h"
10 #include "hw/dma/bcm2835_dma.h"
11 #include "hw/irq.h"
12 #include "migration/vmstate.h"
13 #include "qemu/log.h"
14 #include "qemu/module.h"
15
16 /* DMA CS Control and Status bits */
17 #define BCM2708_DMA_ACTIVE (1 << 0)
18 #define BCM2708_DMA_END (1 << 1) /* GE */
19 #define BCM2708_DMA_INT (1 << 2)
20 #define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */
21 #define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */
22 #define BCM2708_DMA_ERR (1 << 8)
23 #define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */
24 #define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */
25
26 /* DMA control block "info" field bits */
27 #define BCM2708_DMA_INT_EN (1 << 0)
28 #define BCM2708_DMA_TDMODE (1 << 1)
29 #define BCM2708_DMA_WAIT_RESP (1 << 3)
30 #define BCM2708_DMA_D_INC (1 << 4)
31 #define BCM2708_DMA_D_WIDTH (1 << 5)
32 #define BCM2708_DMA_D_DREQ (1 << 6)
33 #define BCM2708_DMA_D_IGNORE (1 << 7)
34 #define BCM2708_DMA_S_INC (1 << 8)
35 #define BCM2708_DMA_S_WIDTH (1 << 9)
36 #define BCM2708_DMA_S_DREQ (1 << 10)
37 #define BCM2708_DMA_S_IGNORE (1 << 11)
38
39 /* Register offsets */
40 #define BCM2708_DMA_CS 0x00 /* Control and Status */
41 #define BCM2708_DMA_ADDR 0x04 /* Control block address */
42 /* the current control block appears in the following registers - read only */
43 #define BCM2708_DMA_INFO 0x08
44 #define BCM2708_DMA_SOURCE_AD 0x0c
45 #define BCM2708_DMA_DEST_AD 0x10
46 #define BCM2708_DMA_TXFR_LEN 0x14
47 #define BCM2708_DMA_STRIDE 0x18
48 #define BCM2708_DMA_NEXTCB 0x1C
49 #define BCM2708_DMA_DEBUG 0x20
50
51 #define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */
52 #define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */
53
54 #define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */
55
bcm2835_dma_update(BCM2835DMAState * s,unsigned c)56 static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c)
57 {
58 BCM2835DMAChan *ch = &s->chan[c];
59 uint32_t data, xlen, xlen_td, ylen;
60 int16_t dst_stride, src_stride;
61
62 if (!(s->enable & (1 << c))) {
63 return;
64 }
65
66 while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) {
67 /* CB fetch */
68 ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad);
69 ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4);
70 ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8);
71 ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12);
72 ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16);
73 ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20);
74
75 ylen = 1;
76 if (ch->ti & BCM2708_DMA_TDMODE) {
77 /* 2D transfer mode */
78 ylen += (ch->txfr_len >> 16) & 0x3fff;
79 xlen = ch->txfr_len & 0xffff;
80 dst_stride = ch->stride >> 16;
81 src_stride = ch->stride & 0xffff;
82 } else {
83 xlen = ch->txfr_len;
84 dst_stride = 0;
85 src_stride = 0;
86 }
87 xlen_td = xlen;
88
89 while (ylen != 0) {
90 /* Normal transfer mode */
91 while (xlen != 0) {
92 if (ch->ti & BCM2708_DMA_S_IGNORE) {
93 /* Ignore reads */
94 data = 0;
95 } else {
96 data = ldl_le_phys(&s->dma_as, ch->source_ad);
97 }
98 if (ch->ti & BCM2708_DMA_S_INC) {
99 ch->source_ad += 4;
100 }
101
102 if (ch->ti & BCM2708_DMA_D_IGNORE) {
103 /* Ignore writes */
104 } else {
105 stl_le_phys(&s->dma_as, ch->dest_ad, data);
106 }
107 if (ch->ti & BCM2708_DMA_D_INC) {
108 ch->dest_ad += 4;
109 }
110
111 /* update remaining transfer length */
112 xlen -= 4;
113 if (ch->ti & BCM2708_DMA_TDMODE) {
114 ch->txfr_len = (ylen << 16) | xlen;
115 } else {
116 ch->txfr_len = xlen;
117 }
118 }
119
120 if (--ylen != 0) {
121 ch->source_ad += src_stride;
122 ch->dest_ad += dst_stride;
123 xlen = xlen_td;
124 }
125 }
126 ch->cs |= BCM2708_DMA_END;
127 if (ch->ti & BCM2708_DMA_INT_EN) {
128 ch->cs |= BCM2708_DMA_INT;
129 s->int_status |= (1 << c);
130 qemu_set_irq(ch->irq, 1);
131 }
132
133 /* Process next CB */
134 ch->conblk_ad = ch->nextconbk;
135 }
136
137 ch->cs &= ~BCM2708_DMA_ACTIVE;
138 ch->cs |= BCM2708_DMA_ISPAUSED;
139 }
140
bcm2835_dma_chan_reset(BCM2835DMAChan * ch)141 static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
142 {
143 ch->cs = 0;
144 ch->conblk_ad = 0;
145 }
146
bcm2835_dma_read(BCM2835DMAState * s,hwaddr offset,unsigned size,unsigned c)147 static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset,
148 unsigned size, unsigned c)
149 {
150 BCM2835DMAChan *ch;
151 uint32_t res = 0;
152
153 assert(size == 4);
154 assert(c < BCM2835_DMA_NCHANS);
155
156 ch = &s->chan[c];
157
158 switch (offset) {
159 case BCM2708_DMA_CS:
160 res = ch->cs;
161 break;
162 case BCM2708_DMA_ADDR:
163 res = ch->conblk_ad;
164 break;
165 case BCM2708_DMA_INFO:
166 res = ch->ti;
167 break;
168 case BCM2708_DMA_SOURCE_AD:
169 res = ch->source_ad;
170 break;
171 case BCM2708_DMA_DEST_AD:
172 res = ch->dest_ad;
173 break;
174 case BCM2708_DMA_TXFR_LEN:
175 res = ch->txfr_len;
176 break;
177 case BCM2708_DMA_STRIDE:
178 res = ch->stride;
179 break;
180 case BCM2708_DMA_NEXTCB:
181 res = ch->nextconbk;
182 break;
183 case BCM2708_DMA_DEBUG:
184 res = ch->debug;
185 break;
186 default:
187 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
188 __func__, offset);
189 break;
190 }
191 return res;
192 }
193
bcm2835_dma_write(BCM2835DMAState * s,hwaddr offset,uint64_t value,unsigned size,unsigned c)194 static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset,
195 uint64_t value, unsigned size, unsigned c)
196 {
197 BCM2835DMAChan *ch;
198 uint32_t oldcs;
199
200 assert(size == 4);
201 assert(c < BCM2835_DMA_NCHANS);
202
203 ch = &s->chan[c];
204
205 switch (offset) {
206 case BCM2708_DMA_CS:
207 oldcs = ch->cs;
208 if (value & BCM2708_DMA_RESET) {
209 bcm2835_dma_chan_reset(ch);
210 }
211 if (value & BCM2708_DMA_ABORT) {
212 /* abort is a no-op, since we always run to completion */
213 }
214 if (value & BCM2708_DMA_END) {
215 ch->cs &= ~BCM2708_DMA_END;
216 }
217 if (value & BCM2708_DMA_INT) {
218 ch->cs &= ~BCM2708_DMA_INT;
219 s->int_status &= ~(1 << c);
220 qemu_set_irq(ch->irq, 0);
221 }
222 ch->cs &= ~BCM2708_DMA_CS_RW_MASK;
223 ch->cs |= (value & BCM2708_DMA_CS_RW_MASK);
224 if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) {
225 bcm2835_dma_update(s, c);
226 }
227 break;
228 case BCM2708_DMA_ADDR:
229 ch->conblk_ad = value;
230 break;
231 case BCM2708_DMA_DEBUG:
232 ch->debug = value;
233 break;
234 default:
235 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
236 __func__, offset);
237 break;
238 }
239 }
240
bcm2835_dma0_read(void * opaque,hwaddr offset,unsigned size)241 static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size)
242 {
243 BCM2835DMAState *s = opaque;
244
245 if (offset < 0xf00) {
246 return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf);
247 } else {
248 switch (offset) {
249 case BCM2708_DMA_INT_STATUS:
250 return s->int_status;
251 case BCM2708_DMA_ENABLE:
252 return s->enable;
253 default:
254 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
255 __func__, offset);
256 return 0;
257 }
258 }
259 }
260
bcm2835_dma15_read(void * opaque,hwaddr offset,unsigned size)261 static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size)
262 {
263 return bcm2835_dma_read(opaque, (offset & 0xff), size, 15);
264 }
265
bcm2835_dma0_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)266 static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value,
267 unsigned size)
268 {
269 BCM2835DMAState *s = opaque;
270
271 if (offset < 0xf00) {
272 bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf);
273 } else {
274 switch (offset) {
275 case BCM2708_DMA_INT_STATUS:
276 break;
277 case BCM2708_DMA_ENABLE:
278 s->enable = (value & 0xffff);
279 break;
280 default:
281 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
282 __func__, offset);
283 }
284 }
285
286 }
287
bcm2835_dma15_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)288 static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value,
289 unsigned size)
290 {
291 bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15);
292 }
293
294 static const MemoryRegionOps bcm2835_dma0_ops = {
295 .read = bcm2835_dma0_read,
296 .write = bcm2835_dma0_write,
297 .endianness = DEVICE_NATIVE_ENDIAN,
298 .valid.min_access_size = 4,
299 .valid.max_access_size = 4,
300 };
301
302 static const MemoryRegionOps bcm2835_dma15_ops = {
303 .read = bcm2835_dma15_read,
304 .write = bcm2835_dma15_write,
305 .endianness = DEVICE_NATIVE_ENDIAN,
306 .valid.min_access_size = 4,
307 .valid.max_access_size = 4,
308 };
309
310 static const VMStateDescription vmstate_bcm2835_dma_chan = {
311 .name = TYPE_BCM2835_DMA "-chan",
312 .version_id = 1,
313 .minimum_version_id = 1,
314 .fields = (const VMStateField[]) {
315 VMSTATE_UINT32(cs, BCM2835DMAChan),
316 VMSTATE_UINT32(conblk_ad, BCM2835DMAChan),
317 VMSTATE_UINT32(ti, BCM2835DMAChan),
318 VMSTATE_UINT32(source_ad, BCM2835DMAChan),
319 VMSTATE_UINT32(dest_ad, BCM2835DMAChan),
320 VMSTATE_UINT32(txfr_len, BCM2835DMAChan),
321 VMSTATE_UINT32(stride, BCM2835DMAChan),
322 VMSTATE_UINT32(nextconbk, BCM2835DMAChan),
323 VMSTATE_UINT32(debug, BCM2835DMAChan),
324 VMSTATE_END_OF_LIST()
325 }
326 };
327
328 static const VMStateDescription vmstate_bcm2835_dma = {
329 .name = TYPE_BCM2835_DMA,
330 .version_id = 1,
331 .minimum_version_id = 1,
332 .fields = (const VMStateField[]) {
333 VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1,
334 vmstate_bcm2835_dma_chan, BCM2835DMAChan),
335 VMSTATE_UINT32(int_status, BCM2835DMAState),
336 VMSTATE_UINT32(enable, BCM2835DMAState),
337 VMSTATE_END_OF_LIST()
338 }
339 };
340
bcm2835_dma_init(Object * obj)341 static void bcm2835_dma_init(Object *obj)
342 {
343 BCM2835DMAState *s = BCM2835_DMA(obj);
344 int n;
345
346 /* DMA channels 0-14 occupy a contiguous block of IO memory, along
347 * with the global enable and interrupt status bits. Channel 15
348 * has the same register map, but is mapped at a discontiguous
349 * address in a separate IO block.
350 */
351 memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s,
352 TYPE_BCM2835_DMA, 0x1000);
353 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0);
354
355 memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s,
356 TYPE_BCM2835_DMA "-chan15", 0x100);
357 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15);
358
359 for (n = 0; n < 16; n++) {
360 sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq);
361 }
362 }
363
bcm2835_dma_reset(DeviceState * dev)364 static void bcm2835_dma_reset(DeviceState *dev)
365 {
366 BCM2835DMAState *s = BCM2835_DMA(dev);
367 int n;
368
369 s->enable = 0xffff;
370 s->int_status = 0;
371 for (n = 0; n < BCM2835_DMA_NCHANS; n++) {
372 bcm2835_dma_chan_reset(&s->chan[n]);
373 }
374 }
375
bcm2835_dma_realize(DeviceState * dev,Error ** errp)376 static void bcm2835_dma_realize(DeviceState *dev, Error **errp)
377 {
378 BCM2835DMAState *s = BCM2835_DMA(dev);
379 Object *obj;
380
381 obj = object_property_get_link(OBJECT(dev), "dma-mr", &error_abort);
382 s->dma_mr = MEMORY_REGION(obj);
383 address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_DMA "-memory");
384
385 bcm2835_dma_reset(dev);
386 }
387
bcm2835_dma_class_init(ObjectClass * klass,void * data)388 static void bcm2835_dma_class_init(ObjectClass *klass, void *data)
389 {
390 DeviceClass *dc = DEVICE_CLASS(klass);
391
392 dc->realize = bcm2835_dma_realize;
393 dc->reset = bcm2835_dma_reset;
394 dc->vmsd = &vmstate_bcm2835_dma;
395 }
396
397 static const TypeInfo bcm2835_dma_info = {
398 .name = TYPE_BCM2835_DMA,
399 .parent = TYPE_SYS_BUS_DEVICE,
400 .instance_size = sizeof(BCM2835DMAState),
401 .class_init = bcm2835_dma_class_init,
402 .instance_init = bcm2835_dma_init,
403 };
404
bcm2835_dma_register_types(void)405 static void bcm2835_dma_register_types(void)
406 {
407 type_register_static(&bcm2835_dma_info);
408 }
409
410 type_init(bcm2835_dma_register_types)
411