|
|
1.1 ! root 1: /* ! 2: * Motorola ColdFire MCF5206 SoC embedded peripheral emulation. ! 3: * ! 4: * Copyright (c) 2007 CodeSourcery. ! 5: * ! 6: * This code is licenced under the GPL ! 7: */ ! 8: #include "hw.h" ! 9: #include "mcf.h" ! 10: #include "qemu-timer.h" ! 11: #include "sysemu.h" ! 12: ! 13: /* General purpose timer module. */ ! 14: typedef struct { ! 15: uint16_t tmr; ! 16: uint16_t trr; ! 17: uint16_t tcr; ! 18: uint16_t ter; ! 19: ptimer_state *timer; ! 20: qemu_irq irq; ! 21: int irq_state; ! 22: } m5206_timer_state; ! 23: ! 24: #define TMR_RST 0x01 ! 25: #define TMR_CLK 0x06 ! 26: #define TMR_FRR 0x08 ! 27: #define TMR_ORI 0x10 ! 28: #define TMR_OM 0x20 ! 29: #define TMR_CE 0xc0 ! 30: ! 31: #define TER_CAP 0x01 ! 32: #define TER_REF 0x02 ! 33: ! 34: static void m5206_timer_update(m5206_timer_state *s) ! 35: { ! 36: if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF)) ! 37: qemu_irq_raise(s->irq); ! 38: else ! 39: qemu_irq_lower(s->irq); ! 40: } ! 41: ! 42: static void m5206_timer_reset(m5206_timer_state *s) ! 43: { ! 44: s->tmr = 0; ! 45: s->trr = 0; ! 46: } ! 47: ! 48: static void m5206_timer_recalibrate(m5206_timer_state *s) ! 49: { ! 50: int prescale; ! 51: int mode; ! 52: ! 53: ptimer_stop(s->timer); ! 54: ! 55: if ((s->tmr & TMR_RST) == 0) ! 56: return; ! 57: ! 58: prescale = (s->tmr >> 8) + 1; ! 59: mode = (s->tmr >> 1) & 3; ! 60: if (mode == 2) ! 61: prescale *= 16; ! 62: ! 63: if (mode == 3 || mode == 0) ! 64: cpu_abort(cpu_single_env, ! 65: "m5206_timer: mode %d not implemented\n", mode); ! 66: if ((s->tmr & TMR_FRR) == 0) ! 67: cpu_abort(cpu_single_env, ! 68: "m5206_timer: free running mode not implemented\n"); ! 69: ! 70: /* Assume 66MHz system clock. */ ! 71: ptimer_set_freq(s->timer, 66000000 / prescale); ! 72: ! 73: ptimer_set_limit(s->timer, s->trr, 0); ! 74: ! 75: ptimer_run(s->timer, 0); ! 76: } ! 77: ! 78: static void m5206_timer_trigger(void *opaque) ! 79: { ! 80: m5206_timer_state *s = (m5206_timer_state *)opaque; ! 81: s->ter |= TER_REF; ! 82: m5206_timer_update(s); ! 83: } ! 84: ! 85: static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr) ! 86: { ! 87: switch (addr) { ! 88: case 0: ! 89: return s->tmr; ! 90: case 4: ! 91: return s->trr; ! 92: case 8: ! 93: return s->tcr; ! 94: case 0xc: ! 95: return s->trr - ptimer_get_count(s->timer); ! 96: case 0x11: ! 97: return s->ter; ! 98: default: ! 99: return 0; ! 100: } ! 101: } ! 102: ! 103: static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val) ! 104: { ! 105: switch (addr) { ! 106: case 0: ! 107: if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) { ! 108: m5206_timer_reset(s); ! 109: } ! 110: s->tmr = val; ! 111: m5206_timer_recalibrate(s); ! 112: break; ! 113: case 4: ! 114: s->trr = val; ! 115: m5206_timer_recalibrate(s); ! 116: break; ! 117: case 8: ! 118: s->tcr = val; ! 119: break; ! 120: case 0xc: ! 121: ptimer_set_count(s->timer, val); ! 122: break; ! 123: case 0x11: ! 124: s->ter &= ~val; ! 125: break; ! 126: default: ! 127: break; ! 128: } ! 129: m5206_timer_update(s); ! 130: } ! 131: ! 132: static m5206_timer_state *m5206_timer_init(qemu_irq irq) ! 133: { ! 134: m5206_timer_state *s; ! 135: QEMUBH *bh; ! 136: ! 137: s = (m5206_timer_state *)qemu_mallocz(sizeof(m5206_timer_state)); ! 138: bh = qemu_bh_new(m5206_timer_trigger, s); ! 139: s->timer = ptimer_init(bh); ! 140: s->irq = irq; ! 141: m5206_timer_reset(s); ! 142: return s; ! 143: } ! 144: ! 145: /* System Integration Module. */ ! 146: ! 147: typedef struct { ! 148: CPUState *env; ! 149: m5206_timer_state *timer[2]; ! 150: void *uart[2]; ! 151: uint8_t scr; ! 152: uint8_t icr[14]; ! 153: uint16_t imr; /* 1 == interrupt is masked. */ ! 154: uint16_t ipr; ! 155: uint8_t rsr; ! 156: uint8_t swivr; ! 157: uint8_t par; ! 158: /* Include the UART vector registers here. */ ! 159: uint8_t uivr[2]; ! 160: } m5206_mbar_state; ! 161: ! 162: /* Interrupt controller. */ ! 163: ! 164: static int m5206_find_pending_irq(m5206_mbar_state *s) ! 165: { ! 166: int level; ! 167: int vector; ! 168: uint16_t active; ! 169: int i; ! 170: ! 171: level = 0; ! 172: vector = 0; ! 173: active = s->ipr & ~s->imr; ! 174: if (!active) ! 175: return 0; ! 176: ! 177: for (i = 1; i < 14; i++) { ! 178: if (active & (1 << i)) { ! 179: if ((s->icr[i] & 0x1f) > level) { ! 180: level = s->icr[i] & 0x1f; ! 181: vector = i; ! 182: } ! 183: } ! 184: } ! 185: ! 186: if (level < 4) ! 187: vector = 0; ! 188: ! 189: return vector; ! 190: } ! 191: ! 192: static void m5206_mbar_update(m5206_mbar_state *s) ! 193: { ! 194: int irq; ! 195: int vector; ! 196: int level; ! 197: ! 198: irq = m5206_find_pending_irq(s); ! 199: if (irq) { ! 200: int tmp; ! 201: tmp = s->icr[irq]; ! 202: level = (tmp >> 2) & 7; ! 203: if (tmp & 0x80) { ! 204: /* Autovector. */ ! 205: vector = 24 + level; ! 206: } else { ! 207: switch (irq) { ! 208: case 8: /* SWT */ ! 209: vector = s->swivr; ! 210: break; ! 211: case 12: /* UART1 */ ! 212: vector = s->uivr[0]; ! 213: break; ! 214: case 13: /* UART2 */ ! 215: vector = s->uivr[1]; ! 216: break; ! 217: default: ! 218: /* Unknown vector. */ ! 219: fprintf(stderr, "Unhandled vector for IRQ %d\n", irq); ! 220: vector = 0xf; ! 221: break; ! 222: } ! 223: } ! 224: } else { ! 225: level = 0; ! 226: vector = 0; ! 227: } ! 228: m68k_set_irq_level(s->env, level, vector); ! 229: } ! 230: ! 231: static void m5206_mbar_set_irq(void *opaque, int irq, int level) ! 232: { ! 233: m5206_mbar_state *s = (m5206_mbar_state *)opaque; ! 234: if (level) { ! 235: s->ipr |= 1 << irq; ! 236: } else { ! 237: s->ipr &= ~(1 << irq); ! 238: } ! 239: m5206_mbar_update(s); ! 240: } ! 241: ! 242: /* System Integration Module. */ ! 243: ! 244: static void m5206_mbar_reset(m5206_mbar_state *s) ! 245: { ! 246: s->scr = 0xc0; ! 247: s->icr[1] = 0x04; ! 248: s->icr[2] = 0x08; ! 249: s->icr[3] = 0x0c; ! 250: s->icr[4] = 0x10; ! 251: s->icr[5] = 0x14; ! 252: s->icr[6] = 0x18; ! 253: s->icr[7] = 0x1c; ! 254: s->icr[8] = 0x1c; ! 255: s->icr[9] = 0x80; ! 256: s->icr[10] = 0x80; ! 257: s->icr[11] = 0x80; ! 258: s->icr[12] = 0x00; ! 259: s->icr[13] = 0x00; ! 260: s->imr = 0x3ffe; ! 261: s->rsr = 0x80; ! 262: s->swivr = 0x0f; ! 263: s->par = 0; ! 264: } ! 265: ! 266: static uint32_t m5206_mbar_read(m5206_mbar_state *s, uint32_t offset) ! 267: { ! 268: if (offset >= 0x100 && offset < 0x120) { ! 269: return m5206_timer_read(s->timer[0], offset - 0x100); ! 270: } else if (offset >= 0x120 && offset < 0x140) { ! 271: return m5206_timer_read(s->timer[1], offset - 0x120); ! 272: } else if (offset >= 0x140 && offset < 0x160) { ! 273: return mcf_uart_read(s->uart[0], offset - 0x140); ! 274: } else if (offset >= 0x180 && offset < 0x1a0) { ! 275: return mcf_uart_read(s->uart[1], offset - 0x180); ! 276: } ! 277: switch (offset) { ! 278: case 0x03: return s->scr; ! 279: case 0x14 ... 0x20: return s->icr[offset - 0x13]; ! 280: case 0x36: return s->imr; ! 281: case 0x3a: return s->ipr; ! 282: case 0x40: return s->rsr; ! 283: case 0x41: return 0; ! 284: case 0x42: return s->swivr; ! 285: case 0x50: ! 286: /* DRAM mask register. */ ! 287: /* FIXME: currently hardcoded to 128Mb. */ ! 288: { ! 289: uint32_t mask = ~0; ! 290: while (mask > ram_size) ! 291: mask >>= 1; ! 292: return mask & 0x0ffe0000; ! 293: } ! 294: case 0x5c: return 1; /* DRAM bank 1 empty. */ ! 295: case 0xcb: return s->par; ! 296: case 0x170: return s->uivr[0]; ! 297: case 0x1b0: return s->uivr[1]; ! 298: } ! 299: cpu_abort(cpu_single_env, "Bad MBAR read offset 0x%x", (int)offset); ! 300: return 0; ! 301: } ! 302: ! 303: static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset, ! 304: uint32_t value) ! 305: { ! 306: if (offset >= 0x100 && offset < 0x120) { ! 307: m5206_timer_write(s->timer[0], offset - 0x100, value); ! 308: return; ! 309: } else if (offset >= 0x120 && offset < 0x140) { ! 310: m5206_timer_write(s->timer[1], offset - 0x120, value); ! 311: return; ! 312: } else if (offset >= 0x140 && offset < 0x160) { ! 313: mcf_uart_write(s->uart[0], offset - 0x140, value); ! 314: return; ! 315: } else if (offset >= 0x180 && offset < 0x1a0) { ! 316: mcf_uart_write(s->uart[1], offset - 0x180, value); ! 317: return; ! 318: } ! 319: switch (offset) { ! 320: case 0x03: ! 321: s->scr = value; ! 322: break; ! 323: case 0x14 ... 0x20: ! 324: s->icr[offset - 0x13] = value; ! 325: m5206_mbar_update(s); ! 326: break; ! 327: case 0x36: ! 328: s->imr = value; ! 329: m5206_mbar_update(s); ! 330: break; ! 331: case 0x40: ! 332: s->rsr &= ~value; ! 333: break; ! 334: case 0x41: ! 335: /* TODO: implement watchdog. */ ! 336: break; ! 337: case 0x42: ! 338: s->swivr = value; ! 339: break; ! 340: case 0xcb: ! 341: s->par = value; ! 342: break; ! 343: case 0x170: ! 344: s->uivr[0] = value; ! 345: break; ! 346: case 0x178: case 0x17c: case 0x1c8: case 0x1bc: ! 347: /* Not implemented: UART Output port bits. */ ! 348: break; ! 349: case 0x1b0: ! 350: s->uivr[1] = value; ! 351: break; ! 352: default: ! 353: cpu_abort(cpu_single_env, "Bad MBAR write offset 0x%x", (int)offset); ! 354: break; ! 355: } ! 356: } ! 357: ! 358: /* Internal peripherals use a variety of register widths. ! 359: This lookup table allows a single routine to handle all of them. */ ! 360: static const int m5206_mbar_width[] = ! 361: { ! 362: /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, ! 363: /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2, ! 364: /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, ! 365: /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ! 366: /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0, ! 367: /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ! 368: /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ! 369: /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ! 370: }; ! 371: ! 372: static uint32_t m5206_mbar_readw(void *opaque, target_phys_addr_t offset); ! 373: static uint32_t m5206_mbar_readl(void *opaque, target_phys_addr_t offset); ! 374: ! 375: static uint32_t m5206_mbar_readb(void *opaque, target_phys_addr_t offset) ! 376: { ! 377: m5206_mbar_state *s = (m5206_mbar_state *)opaque; ! 378: offset &= 0x3ff; ! 379: if (offset > 0x200) { ! 380: cpu_abort(cpu_single_env, "Bad MBAR read offset 0x%x", (int)offset); ! 381: } ! 382: if (m5206_mbar_width[offset >> 2] > 1) { ! 383: uint16_t val; ! 384: val = m5206_mbar_readw(opaque, offset & ~1); ! 385: if ((offset & 1) == 0) { ! 386: val >>= 8; ! 387: } ! 388: return val & 0xff; ! 389: } ! 390: return m5206_mbar_read(s, offset); ! 391: } ! 392: ! 393: static uint32_t m5206_mbar_readw(void *opaque, target_phys_addr_t offset) ! 394: { ! 395: m5206_mbar_state *s = (m5206_mbar_state *)opaque; ! 396: int width; ! 397: offset &= 0x3ff; ! 398: if (offset > 0x200) { ! 399: cpu_abort(cpu_single_env, "Bad MBAR read offset 0x%x", (int)offset); ! 400: } ! 401: width = m5206_mbar_width[offset >> 2]; ! 402: if (width > 2) { ! 403: uint32_t val; ! 404: val = m5206_mbar_readl(opaque, offset & ~3); ! 405: if ((offset & 3) == 0) ! 406: val >>= 16; ! 407: return val & 0xffff; ! 408: } else if (width < 2) { ! 409: uint16_t val; ! 410: val = m5206_mbar_readb(opaque, offset) << 8; ! 411: val |= m5206_mbar_readb(opaque, offset + 1); ! 412: return val; ! 413: } ! 414: return m5206_mbar_read(s, offset); ! 415: } ! 416: ! 417: static uint32_t m5206_mbar_readl(void *opaque, target_phys_addr_t offset) ! 418: { ! 419: m5206_mbar_state *s = (m5206_mbar_state *)opaque; ! 420: int width; ! 421: offset &= 0x3ff; ! 422: if (offset > 0x200) { ! 423: cpu_abort(cpu_single_env, "Bad MBAR read offset 0x%x", (int)offset); ! 424: } ! 425: width = m5206_mbar_width[offset >> 2]; ! 426: if (width < 4) { ! 427: uint32_t val; ! 428: val = m5206_mbar_readw(opaque, offset) << 16; ! 429: val |= m5206_mbar_readw(opaque, offset + 2); ! 430: return val; ! 431: } ! 432: return m5206_mbar_read(s, offset); ! 433: } ! 434: ! 435: static void m5206_mbar_writew(void *opaque, target_phys_addr_t offset, ! 436: uint32_t value); ! 437: static void m5206_mbar_writel(void *opaque, target_phys_addr_t offset, ! 438: uint32_t value); ! 439: ! 440: static void m5206_mbar_writeb(void *opaque, target_phys_addr_t offset, ! 441: uint32_t value) ! 442: { ! 443: m5206_mbar_state *s = (m5206_mbar_state *)opaque; ! 444: int width; ! 445: offset &= 0x3ff; ! 446: if (offset > 0x200) { ! 447: cpu_abort(cpu_single_env, "Bad MBAR write offset 0x%x", (int)offset); ! 448: } ! 449: width = m5206_mbar_width[offset >> 2]; ! 450: if (width > 1) { ! 451: uint32_t tmp; ! 452: tmp = m5206_mbar_readw(opaque, offset & ~1); ! 453: if (offset & 1) { ! 454: tmp = (tmp & 0xff00) | value; ! 455: } else { ! 456: tmp = (tmp & 0x00ff) | (value << 8); ! 457: } ! 458: m5206_mbar_writew(opaque, offset & ~1, tmp); ! 459: return; ! 460: } ! 461: m5206_mbar_write(s, offset, value); ! 462: } ! 463: ! 464: static void m5206_mbar_writew(void *opaque, target_phys_addr_t offset, ! 465: uint32_t value) ! 466: { ! 467: m5206_mbar_state *s = (m5206_mbar_state *)opaque; ! 468: int width; ! 469: offset &= 0x3ff; ! 470: if (offset > 0x200) { ! 471: cpu_abort(cpu_single_env, "Bad MBAR write offset 0x%x", (int)offset); ! 472: } ! 473: width = m5206_mbar_width[offset >> 2]; ! 474: if (width > 2) { ! 475: uint32_t tmp; ! 476: tmp = m5206_mbar_readl(opaque, offset & ~3); ! 477: if (offset & 3) { ! 478: tmp = (tmp & 0xffff0000) | value; ! 479: } else { ! 480: tmp = (tmp & 0x0000ffff) | (value << 16); ! 481: } ! 482: m5206_mbar_writel(opaque, offset & ~3, tmp); ! 483: return; ! 484: } else if (width < 2) { ! 485: m5206_mbar_writeb(opaque, offset, value >> 8); ! 486: m5206_mbar_writeb(opaque, offset + 1, value & 0xff); ! 487: return; ! 488: } ! 489: m5206_mbar_write(s, offset, value); ! 490: } ! 491: ! 492: static void m5206_mbar_writel(void *opaque, target_phys_addr_t offset, ! 493: uint32_t value) ! 494: { ! 495: m5206_mbar_state *s = (m5206_mbar_state *)opaque; ! 496: int width; ! 497: offset &= 0x3ff; ! 498: if (offset > 0x200) { ! 499: cpu_abort(cpu_single_env, "Bad MBAR write offset 0x%x", (int)offset); ! 500: } ! 501: width = m5206_mbar_width[offset >> 2]; ! 502: if (width < 4) { ! 503: m5206_mbar_writew(opaque, offset, value >> 16); ! 504: m5206_mbar_writew(opaque, offset + 2, value & 0xffff); ! 505: return; ! 506: } ! 507: m5206_mbar_write(s, offset, value); ! 508: } ! 509: ! 510: static CPUReadMemoryFunc *m5206_mbar_readfn[] = { ! 511: m5206_mbar_readb, ! 512: m5206_mbar_readw, ! 513: m5206_mbar_readl ! 514: }; ! 515: ! 516: static CPUWriteMemoryFunc *m5206_mbar_writefn[] = { ! 517: m5206_mbar_writeb, ! 518: m5206_mbar_writew, ! 519: m5206_mbar_writel ! 520: }; ! 521: ! 522: qemu_irq *mcf5206_init(uint32_t base, CPUState *env) ! 523: { ! 524: m5206_mbar_state *s; ! 525: qemu_irq *pic; ! 526: int iomemtype; ! 527: ! 528: s = (m5206_mbar_state *)qemu_mallocz(sizeof(m5206_mbar_state)); ! 529: iomemtype = cpu_register_io_memory(0, m5206_mbar_readfn, ! 530: m5206_mbar_writefn, s); ! 531: cpu_register_physical_memory(base, 0x00001000, iomemtype); ! 532: ! 533: pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); ! 534: s->timer[0] = m5206_timer_init(pic[9]); ! 535: s->timer[1] = m5206_timer_init(pic[10]); ! 536: s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]); ! 537: s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]); ! 538: s->env = env; ! 539: ! 540: m5206_mbar_reset(s); ! 541: return pic; ! 542: } ! 543:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.