|
|
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.