mcf5206.c (16657B)
1/* 2 * Motorola ColdFire MCF5206 SoC embedded peripheral emulation. 3 * 4 * Copyright (c) 2007 CodeSourcery. 5 * 6 * This code is licensed under the GPL 7 */ 8 9#include "qemu/osdep.h" 10#include "qemu/error-report.h" 11#include "qemu/log.h" 12#include "cpu.h" 13#include "hw/boards.h" 14#include "hw/irq.h" 15#include "hw/m68k/mcf.h" 16#include "qemu/timer.h" 17#include "hw/ptimer.h" 18#include "sysemu/sysemu.h" 19#include "hw/sysbus.h" 20 21/* General purpose timer module. */ 22typedef struct { 23 uint16_t tmr; 24 uint16_t trr; 25 uint16_t tcr; 26 uint16_t ter; 27 ptimer_state *timer; 28 qemu_irq irq; 29 int irq_state; 30} m5206_timer_state; 31 32#define TMR_RST 0x01 33#define TMR_CLK 0x06 34#define TMR_FRR 0x08 35#define TMR_ORI 0x10 36#define TMR_OM 0x20 37#define TMR_CE 0xc0 38 39#define TER_CAP 0x01 40#define TER_REF 0x02 41 42static void m5206_timer_update(m5206_timer_state *s) 43{ 44 if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF)) 45 qemu_irq_raise(s->irq); 46 else 47 qemu_irq_lower(s->irq); 48} 49 50static void m5206_timer_reset(m5206_timer_state *s) 51{ 52 s->tmr = 0; 53 s->trr = 0; 54} 55 56static void m5206_timer_recalibrate(m5206_timer_state *s) 57{ 58 int prescale; 59 int mode; 60 61 ptimer_transaction_begin(s->timer); 62 ptimer_stop(s->timer); 63 64 if ((s->tmr & TMR_RST) == 0) { 65 goto exit; 66 } 67 68 prescale = (s->tmr >> 8) + 1; 69 mode = (s->tmr >> 1) & 3; 70 if (mode == 2) 71 prescale *= 16; 72 73 if (mode == 3 || mode == 0) { 74 qemu_log_mask(LOG_UNIMP, "m5206_timer: mode %d not implemented\n", 75 mode); 76 goto exit; 77 } 78 if ((s->tmr & TMR_FRR) == 0) { 79 qemu_log_mask(LOG_UNIMP, 80 "m5206_timer: free running mode not implemented\n"); 81 goto exit; 82 } 83 84 /* Assume 66MHz system clock. */ 85 ptimer_set_freq(s->timer, 66000000 / prescale); 86 87 ptimer_set_limit(s->timer, s->trr, 0); 88 89 ptimer_run(s->timer, 0); 90exit: 91 ptimer_transaction_commit(s->timer); 92} 93 94static void m5206_timer_trigger(void *opaque) 95{ 96 m5206_timer_state *s = (m5206_timer_state *)opaque; 97 s->ter |= TER_REF; 98 m5206_timer_update(s); 99} 100 101static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr) 102{ 103 switch (addr) { 104 case 0: 105 return s->tmr; 106 case 4: 107 return s->trr; 108 case 8: 109 return s->tcr; 110 case 0xc: 111 return s->trr - ptimer_get_count(s->timer); 112 case 0x11: 113 return s->ter; 114 default: 115 return 0; 116 } 117} 118 119static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val) 120{ 121 switch (addr) { 122 case 0: 123 if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) { 124 m5206_timer_reset(s); 125 } 126 s->tmr = val; 127 m5206_timer_recalibrate(s); 128 break; 129 case 4: 130 s->trr = val; 131 m5206_timer_recalibrate(s); 132 break; 133 case 8: 134 s->tcr = val; 135 break; 136 case 0xc: 137 ptimer_transaction_begin(s->timer); 138 ptimer_set_count(s->timer, val); 139 ptimer_transaction_commit(s->timer); 140 break; 141 case 0x11: 142 s->ter &= ~val; 143 break; 144 default: 145 break; 146 } 147 m5206_timer_update(s); 148} 149 150static m5206_timer_state *m5206_timer_init(qemu_irq irq) 151{ 152 m5206_timer_state *s; 153 154 s = g_new0(m5206_timer_state, 1); 155 s->timer = ptimer_init(m5206_timer_trigger, s, PTIMER_POLICY_DEFAULT); 156 s->irq = irq; 157 m5206_timer_reset(s); 158 return s; 159} 160 161/* System Integration Module. */ 162 163typedef struct { 164 SysBusDevice parent_obj; 165 166 M68kCPU *cpu; 167 MemoryRegion iomem; 168 qemu_irq *pic; 169 m5206_timer_state *timer[2]; 170 void *uart[2]; 171 uint8_t scr; 172 uint8_t icr[14]; 173 uint16_t imr; /* 1 == interrupt is masked. */ 174 uint16_t ipr; 175 uint8_t rsr; 176 uint8_t swivr; 177 uint8_t par; 178 /* Include the UART vector registers here. */ 179 uint8_t uivr[2]; 180} m5206_mbar_state; 181 182#define MCF5206_MBAR(obj) OBJECT_CHECK(m5206_mbar_state, (obj), TYPE_MCF5206_MBAR) 183 184/* Interrupt controller. */ 185 186static int m5206_find_pending_irq(m5206_mbar_state *s) 187{ 188 int level; 189 int vector; 190 uint16_t active; 191 int i; 192 193 level = 0; 194 vector = 0; 195 active = s->ipr & ~s->imr; 196 if (!active) 197 return 0; 198 199 for (i = 1; i < 14; i++) { 200 if (active & (1 << i)) { 201 if ((s->icr[i] & 0x1f) > level) { 202 level = s->icr[i] & 0x1f; 203 vector = i; 204 } 205 } 206 } 207 208 if (level < 4) 209 vector = 0; 210 211 return vector; 212} 213 214static void m5206_mbar_update(m5206_mbar_state *s) 215{ 216 int irq; 217 int vector; 218 int level; 219 220 irq = m5206_find_pending_irq(s); 221 if (irq) { 222 int tmp; 223 tmp = s->icr[irq]; 224 level = (tmp >> 2) & 7; 225 if (tmp & 0x80) { 226 /* Autovector. */ 227 vector = 24 + level; 228 } else { 229 switch (irq) { 230 case 8: /* SWT */ 231 vector = s->swivr; 232 break; 233 case 12: /* UART1 */ 234 vector = s->uivr[0]; 235 break; 236 case 13: /* UART2 */ 237 vector = s->uivr[1]; 238 break; 239 default: 240 /* Unknown vector. */ 241 qemu_log_mask(LOG_UNIMP, "%s: Unhandled vector for IRQ %d\n", 242 __func__, irq); 243 vector = 0xf; 244 break; 245 } 246 } 247 } else { 248 level = 0; 249 vector = 0; 250 } 251 m68k_set_irq_level(s->cpu, level, vector); 252} 253 254static void m5206_mbar_set_irq(void *opaque, int irq, int level) 255{ 256 m5206_mbar_state *s = (m5206_mbar_state *)opaque; 257 if (level) { 258 s->ipr |= 1 << irq; 259 } else { 260 s->ipr &= ~(1 << irq); 261 } 262 m5206_mbar_update(s); 263} 264 265/* System Integration Module. */ 266 267static void m5206_mbar_reset(DeviceState *dev) 268{ 269 m5206_mbar_state *s = MCF5206_MBAR(dev); 270 271 s->scr = 0xc0; 272 s->icr[1] = 0x04; 273 s->icr[2] = 0x08; 274 s->icr[3] = 0x0c; 275 s->icr[4] = 0x10; 276 s->icr[5] = 0x14; 277 s->icr[6] = 0x18; 278 s->icr[7] = 0x1c; 279 s->icr[8] = 0x1c; 280 s->icr[9] = 0x80; 281 s->icr[10] = 0x80; 282 s->icr[11] = 0x80; 283 s->icr[12] = 0x00; 284 s->icr[13] = 0x00; 285 s->imr = 0x3ffe; 286 s->rsr = 0x80; 287 s->swivr = 0x0f; 288 s->par = 0; 289} 290 291static uint64_t m5206_mbar_read(m5206_mbar_state *s, 292 uint16_t offset, unsigned size) 293{ 294 if (offset >= 0x100 && offset < 0x120) { 295 return m5206_timer_read(s->timer[0], offset - 0x100); 296 } else if (offset >= 0x120 && offset < 0x140) { 297 return m5206_timer_read(s->timer[1], offset - 0x120); 298 } else if (offset >= 0x140 && offset < 0x160) { 299 return mcf_uart_read(s->uart[0], offset - 0x140, size); 300 } else if (offset >= 0x180 && offset < 0x1a0) { 301 return mcf_uart_read(s->uart[1], offset - 0x180, size); 302 } 303 switch (offset) { 304 case 0x03: return s->scr; 305 case 0x14 ... 0x20: return s->icr[offset - 0x13]; 306 case 0x36: return s->imr; 307 case 0x3a: return s->ipr; 308 case 0x40: return s->rsr; 309 case 0x41: return 0; 310 case 0x42: return s->swivr; 311 case 0x50: 312 /* DRAM mask register. */ 313 /* FIXME: currently hardcoded to 128Mb. */ 314 { 315 uint32_t mask = ~0; 316 while (mask > current_machine->ram_size) { 317 mask >>= 1; 318 } 319 return mask & 0x0ffe0000; 320 } 321 case 0x5c: return 1; /* DRAM bank 1 empty. */ 322 case 0xcb: return s->par; 323 case 0x170: return s->uivr[0]; 324 case 0x1b0: return s->uivr[1]; 325 } 326 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad MBAR offset 0x%"PRIx16"\n", 327 __func__, offset); 328 return 0; 329} 330 331static void m5206_mbar_write(m5206_mbar_state *s, uint16_t offset, 332 uint64_t value, unsigned size) 333{ 334 if (offset >= 0x100 && offset < 0x120) { 335 m5206_timer_write(s->timer[0], offset - 0x100, value); 336 return; 337 } else if (offset >= 0x120 && offset < 0x140) { 338 m5206_timer_write(s->timer[1], offset - 0x120, value); 339 return; 340 } else if (offset >= 0x140 && offset < 0x160) { 341 mcf_uart_write(s->uart[0], offset - 0x140, value, size); 342 return; 343 } else if (offset >= 0x180 && offset < 0x1a0) { 344 mcf_uart_write(s->uart[1], offset - 0x180, value, size); 345 return; 346 } 347 switch (offset) { 348 case 0x03: 349 s->scr = value; 350 break; 351 case 0x14 ... 0x20: 352 s->icr[offset - 0x13] = value; 353 m5206_mbar_update(s); 354 break; 355 case 0x36: 356 s->imr = value; 357 m5206_mbar_update(s); 358 break; 359 case 0x40: 360 s->rsr &= ~value; 361 break; 362 case 0x41: 363 /* TODO: implement watchdog. */ 364 break; 365 case 0x42: 366 s->swivr = value; 367 break; 368 case 0xcb: 369 s->par = value; 370 break; 371 case 0x170: 372 s->uivr[0] = value; 373 break; 374 case 0x178: case 0x17c: case 0x1c8: case 0x1bc: 375 /* Not implemented: UART Output port bits. */ 376 break; 377 case 0x1b0: 378 s->uivr[1] = value; 379 break; 380 default: 381 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad MBAR offset 0x%"PRIx16"\n", 382 __func__, offset); 383 break; 384 } 385} 386 387/* Internal peripherals use a variety of register widths. 388 This lookup table allows a single routine to handle all of them. */ 389static const uint8_t m5206_mbar_width[] = 390{ 391 /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 392 /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2, 393 /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 394 /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 395 /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0, 396 /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 397 /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 398 /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 399}; 400 401static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset); 402static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset); 403 404static uint32_t m5206_mbar_readb(void *opaque, hwaddr offset) 405{ 406 m5206_mbar_state *s = (m5206_mbar_state *)opaque; 407 offset &= 0x3ff; 408 if (offset >= 0x200) { 409 qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX, 410 offset); 411 return 0; 412 } 413 if (m5206_mbar_width[offset >> 2] > 1) { 414 uint16_t val; 415 val = m5206_mbar_readw(opaque, offset & ~1); 416 if ((offset & 1) == 0) { 417 val >>= 8; 418 } 419 return val & 0xff; 420 } 421 return m5206_mbar_read(s, offset, 1); 422} 423 424static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset) 425{ 426 m5206_mbar_state *s = (m5206_mbar_state *)opaque; 427 int width; 428 offset &= 0x3ff; 429 if (offset >= 0x200) { 430 qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX, 431 offset); 432 return 0; 433 } 434 width = m5206_mbar_width[offset >> 2]; 435 if (width > 2) { 436 uint32_t val; 437 val = m5206_mbar_readl(opaque, offset & ~3); 438 if ((offset & 3) == 0) 439 val >>= 16; 440 return val & 0xffff; 441 } else if (width < 2) { 442 uint16_t val; 443 val = m5206_mbar_readb(opaque, offset) << 8; 444 val |= m5206_mbar_readb(opaque, offset + 1); 445 return val; 446 } 447 return m5206_mbar_read(s, offset, 2); 448} 449 450static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset) 451{ 452 m5206_mbar_state *s = (m5206_mbar_state *)opaque; 453 int width; 454 offset &= 0x3ff; 455 if (offset >= 0x200) { 456 qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX, 457 offset); 458 return 0; 459 } 460 width = m5206_mbar_width[offset >> 2]; 461 if (width < 4) { 462 uint32_t val; 463 val = m5206_mbar_readw(opaque, offset) << 16; 464 val |= m5206_mbar_readw(opaque, offset + 2); 465 return val; 466 } 467 return m5206_mbar_read(s, offset, 4); 468} 469 470static void m5206_mbar_writew(void *opaque, hwaddr offset, 471 uint32_t value); 472static void m5206_mbar_writel(void *opaque, hwaddr offset, 473 uint32_t value); 474 475static void m5206_mbar_writeb(void *opaque, hwaddr offset, 476 uint32_t value) 477{ 478 m5206_mbar_state *s = (m5206_mbar_state *)opaque; 479 int width; 480 offset &= 0x3ff; 481 if (offset >= 0x200) { 482 qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX, 483 offset); 484 return; 485 } 486 width = m5206_mbar_width[offset >> 2]; 487 if (width > 1) { 488 uint32_t tmp; 489 tmp = m5206_mbar_readw(opaque, offset & ~1); 490 if (offset & 1) { 491 tmp = (tmp & 0xff00) | value; 492 } else { 493 tmp = (tmp & 0x00ff) | (value << 8); 494 } 495 m5206_mbar_writew(opaque, offset & ~1, tmp); 496 return; 497 } 498 m5206_mbar_write(s, offset, value, 1); 499} 500 501static void m5206_mbar_writew(void *opaque, hwaddr offset, 502 uint32_t value) 503{ 504 m5206_mbar_state *s = (m5206_mbar_state *)opaque; 505 int width; 506 offset &= 0x3ff; 507 if (offset >= 0x200) { 508 qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX, 509 offset); 510 return; 511 } 512 width = m5206_mbar_width[offset >> 2]; 513 if (width > 2) { 514 uint32_t tmp; 515 tmp = m5206_mbar_readl(opaque, offset & ~3); 516 if (offset & 3) { 517 tmp = (tmp & 0xffff0000) | value; 518 } else { 519 tmp = (tmp & 0x0000ffff) | (value << 16); 520 } 521 m5206_mbar_writel(opaque, offset & ~3, tmp); 522 return; 523 } else if (width < 2) { 524 m5206_mbar_writeb(opaque, offset, value >> 8); 525 m5206_mbar_writeb(opaque, offset + 1, value & 0xff); 526 return; 527 } 528 m5206_mbar_write(s, offset, value, 2); 529} 530 531static void m5206_mbar_writel(void *opaque, hwaddr offset, 532 uint32_t value) 533{ 534 m5206_mbar_state *s = (m5206_mbar_state *)opaque; 535 int width; 536 offset &= 0x3ff; 537 if (offset >= 0x200) { 538 qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX, 539 offset); 540 return; 541 } 542 width = m5206_mbar_width[offset >> 2]; 543 if (width < 4) { 544 m5206_mbar_writew(opaque, offset, value >> 16); 545 m5206_mbar_writew(opaque, offset + 2, value & 0xffff); 546 return; 547 } 548 m5206_mbar_write(s, offset, value, 4); 549} 550 551static uint64_t m5206_mbar_readfn(void *opaque, hwaddr addr, unsigned size) 552{ 553 switch (size) { 554 case 1: 555 return m5206_mbar_readb(opaque, addr); 556 case 2: 557 return m5206_mbar_readw(opaque, addr); 558 case 4: 559 return m5206_mbar_readl(opaque, addr); 560 default: 561 g_assert_not_reached(); 562 } 563} 564 565static void m5206_mbar_writefn(void *opaque, hwaddr addr, 566 uint64_t value, unsigned size) 567{ 568 switch (size) { 569 case 1: 570 m5206_mbar_writeb(opaque, addr, value); 571 break; 572 case 2: 573 m5206_mbar_writew(opaque, addr, value); 574 break; 575 case 4: 576 m5206_mbar_writel(opaque, addr, value); 577 break; 578 default: 579 g_assert_not_reached(); 580 } 581} 582 583static const MemoryRegionOps m5206_mbar_ops = { 584 .read = m5206_mbar_readfn, 585 .write = m5206_mbar_writefn, 586 .valid.min_access_size = 1, 587 .valid.max_access_size = 4, 588 .endianness = DEVICE_NATIVE_ENDIAN, 589}; 590 591static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) 592{ 593 m5206_mbar_state *s = MCF5206_MBAR(dev); 594 595 memory_region_init_io(&s->iomem, NULL, &m5206_mbar_ops, s, 596 "mbar", 0x00001000); 597 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); 598 599 s->pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); 600 s->timer[0] = m5206_timer_init(s->pic[9]); 601 s->timer[1] = m5206_timer_init(s->pic[10]); 602 s->uart[0] = mcf_uart_init(s->pic[12], serial_hd(0)); 603 s->uart[1] = mcf_uart_init(s->pic[13], serial_hd(1)); 604 s->cpu = M68K_CPU(qemu_get_cpu(0)); 605} 606 607static void mcf5206_mbar_class_init(ObjectClass *oc, void *data) 608{ 609 DeviceClass *dc = DEVICE_CLASS(oc); 610 611 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 612 dc->desc = "MCF5206 system integration module"; 613 dc->realize = mcf5206_mbar_realize; 614 dc->reset = m5206_mbar_reset; 615} 616 617static const TypeInfo mcf5206_mbar_info = { 618 .name = TYPE_MCF5206_MBAR, 619 .parent = TYPE_SYS_BUS_DEVICE, 620 .instance_size = sizeof(m5206_mbar_state), 621 .class_init = mcf5206_mbar_class_init, 622}; 623 624static void mcf5206_mbar_register_types(void) 625{ 626 type_register_static(&mcf5206_mbar_info); 627} 628 629type_init(mcf5206_mbar_register_types)