bcm2835_dma.c (12444B)
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 56static 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 141static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch) 142{ 143 ch->cs = 0; 144 ch->conblk_ad = 0; 145} 146 147static 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 194static 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 241static 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 261static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size) 262{ 263 return bcm2835_dma_read(opaque, (offset & 0xff), size, 15); 264} 265 266static 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 288static 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 294static 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 302static 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 310static const VMStateDescription vmstate_bcm2835_dma_chan = { 311 .name = TYPE_BCM2835_DMA "-chan", 312 .version_id = 1, 313 .minimum_version_id = 1, 314 .fields = (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 328static const VMStateDescription vmstate_bcm2835_dma = { 329 .name = TYPE_BCM2835_DMA, 330 .version_id = 1, 331 .minimum_version_id = 1, 332 .fields = (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 341static 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 364static 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 376static 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 388static 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 397static 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 405static void bcm2835_dma_register_types(void) 406{ 407 type_register_static(&bcm2835_dma_info); 408} 409 410type_init(bcm2835_dma_register_types)