xref: /qemu/hw/dma/bcm2835_dma.c (revision 2155d2dd7f733674586119b6b4ee0f52d2032779)
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  
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  
141  static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch)
142  {
143      ch->cs = 0;
144      ch->conblk_ad = 0;
145  }
146  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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      device_class_set_legacy_reset(dc, 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  
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