qemu/hw/m68k/mcf5208.c
<<
>>
Prefs
   1/*
   2 * Motorola ColdFire MCF5208 SoC emulation.
   3 *
   4 * Copyright (c) 2007 CodeSourcery.
   5 *
   6 * This code is licensed under the GPL
   7 */
   8#include "hw/hw.h"
   9#include "hw/m68k/mcf.h"
  10#include "qemu/timer.h"
  11#include "hw/ptimer.h"
  12#include "sysemu/sysemu.h"
  13#include "sysemu/qtest.h"
  14#include "net/net.h"
  15#include "hw/boards.h"
  16#include "hw/loader.h"
  17#include "elf.h"
  18#include "exec/address-spaces.h"
  19
  20#define SYS_FREQ 66000000
  21
  22#define PCSR_EN         0x0001
  23#define PCSR_RLD        0x0002
  24#define PCSR_PIF        0x0004
  25#define PCSR_PIE        0x0008
  26#define PCSR_OVW        0x0010
  27#define PCSR_DBG        0x0020
  28#define PCSR_DOZE       0x0040
  29#define PCSR_PRE_SHIFT  8
  30#define PCSR_PRE_MASK   0x0f00
  31
  32typedef struct {
  33    MemoryRegion iomem;
  34    qemu_irq irq;
  35    ptimer_state *timer;
  36    uint16_t pcsr;
  37    uint16_t pmr;
  38    uint16_t pcntr;
  39} m5208_timer_state;
  40
  41static void m5208_timer_update(m5208_timer_state *s)
  42{
  43    if ((s->pcsr & (PCSR_PIE | PCSR_PIF)) == (PCSR_PIE | PCSR_PIF))
  44        qemu_irq_raise(s->irq);
  45    else
  46        qemu_irq_lower(s->irq);
  47}
  48
  49static void m5208_timer_write(void *opaque, hwaddr offset,
  50                              uint64_t value, unsigned size)
  51{
  52    m5208_timer_state *s = (m5208_timer_state *)opaque;
  53    int prescale;
  54    int limit;
  55    switch (offset) {
  56    case 0:
  57        /* The PIF bit is set-to-clear.  */
  58        if (value & PCSR_PIF) {
  59            s->pcsr &= ~PCSR_PIF;
  60            value &= ~PCSR_PIF;
  61        }
  62        /* Avoid frobbing the timer if we're just twiddling IRQ bits. */
  63        if (((s->pcsr ^ value) & ~PCSR_PIE) == 0) {
  64            s->pcsr = value;
  65            m5208_timer_update(s);
  66            return;
  67        }
  68
  69        if (s->pcsr & PCSR_EN)
  70            ptimer_stop(s->timer);
  71
  72        s->pcsr = value;
  73
  74        prescale = 1 << ((s->pcsr & PCSR_PRE_MASK) >> PCSR_PRE_SHIFT);
  75        ptimer_set_freq(s->timer, (SYS_FREQ / 2) / prescale);
  76        if (s->pcsr & PCSR_RLD)
  77            limit = s->pmr;
  78        else
  79            limit = 0xffff;
  80        ptimer_set_limit(s->timer, limit, 0);
  81
  82        if (s->pcsr & PCSR_EN)
  83            ptimer_run(s->timer, 0);
  84        break;
  85    case 2:
  86        s->pmr = value;
  87        s->pcsr &= ~PCSR_PIF;
  88        if ((s->pcsr & PCSR_RLD) == 0) {
  89            if (s->pcsr & PCSR_OVW)
  90                ptimer_set_count(s->timer, value);
  91        } else {
  92            ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW);
  93        }
  94        break;
  95    case 4:
  96        break;
  97    default:
  98        hw_error("m5208_timer_write: Bad offset 0x%x\n", (int)offset);
  99        break;
 100    }
 101    m5208_timer_update(s);
 102}
 103
 104static void m5208_timer_trigger(void *opaque)
 105{
 106    m5208_timer_state *s = (m5208_timer_state *)opaque;
 107    s->pcsr |= PCSR_PIF;
 108    m5208_timer_update(s);
 109}
 110
 111static uint64_t m5208_timer_read(void *opaque, hwaddr addr,
 112                                 unsigned size)
 113{
 114    m5208_timer_state *s = (m5208_timer_state *)opaque;
 115    switch (addr) {
 116    case 0:
 117        return s->pcsr;
 118    case 2:
 119        return s->pmr;
 120    case 4:
 121        return ptimer_get_count(s->timer);
 122    default:
 123        hw_error("m5208_timer_read: Bad offset 0x%x\n", (int)addr);
 124        return 0;
 125    }
 126}
 127
 128static const MemoryRegionOps m5208_timer_ops = {
 129    .read = m5208_timer_read,
 130    .write = m5208_timer_write,
 131    .endianness = DEVICE_NATIVE_ENDIAN,
 132};
 133
 134static uint64_t m5208_sys_read(void *opaque, hwaddr addr,
 135                               unsigned size)
 136{
 137    switch (addr) {
 138    case 0x110: /* SDCS0 */
 139        {
 140            int n;
 141            for (n = 0; n < 32; n++) {
 142                if (ram_size < (2u << n))
 143                    break;
 144            }
 145            return (n - 1)  | 0x40000000;
 146        }
 147    case 0x114: /* SDCS1 */
 148        return 0;
 149
 150    default:
 151        hw_error("m5208_sys_read: Bad offset 0x%x\n", (int)addr);
 152        return 0;
 153    }
 154}
 155
 156static void m5208_sys_write(void *opaque, hwaddr addr,
 157                            uint64_t value, unsigned size)
 158{
 159    hw_error("m5208_sys_write: Bad offset 0x%x\n", (int)addr);
 160}
 161
 162static const MemoryRegionOps m5208_sys_ops = {
 163    .read = m5208_sys_read,
 164    .write = m5208_sys_write,
 165    .endianness = DEVICE_NATIVE_ENDIAN,
 166};
 167
 168static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic)
 169{
 170    MemoryRegion *iomem = g_new(MemoryRegion, 1);
 171    m5208_timer_state *s;
 172    QEMUBH *bh;
 173    int i;
 174
 175    /* SDRAMC.  */
 176    memory_region_init_io(iomem, NULL, &m5208_sys_ops, NULL, "m5208-sys", 0x00004000);
 177    memory_region_add_subregion(address_space, 0xfc0a8000, iomem);
 178    /* Timers.  */
 179    for (i = 0; i < 2; i++) {
 180        s = (m5208_timer_state *)g_malloc0(sizeof(m5208_timer_state));
 181        bh = qemu_bh_new(m5208_timer_trigger, s);
 182        s->timer = ptimer_init(bh);
 183        memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s,
 184                              "m5208-timer", 0x00004000);
 185        memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i,
 186                                    &s->iomem);
 187        s->irq = pic[4 + i];
 188    }
 189}
 190
 191static void mcf5208evb_init(MachineState *machine)
 192{
 193    ram_addr_t ram_size = machine->ram_size;
 194    const char *cpu_model = machine->cpu_model;
 195    const char *kernel_filename = machine->kernel_filename;
 196    M68kCPU *cpu;
 197    CPUM68KState *env;
 198    int kernel_size;
 199    uint64_t elf_entry;
 200    hwaddr entry;
 201    qemu_irq *pic;
 202    MemoryRegion *address_space_mem = get_system_memory();
 203    MemoryRegion *ram = g_new(MemoryRegion, 1);
 204    MemoryRegion *sram = g_new(MemoryRegion, 1);
 205
 206    if (!cpu_model) {
 207        cpu_model = "m5208";
 208    }
 209    cpu = cpu_m68k_init(cpu_model);
 210    if (!cpu) {
 211        fprintf(stderr, "Unable to find m68k CPU definition\n");
 212        exit(1);
 213    }
 214    env = &cpu->env;
 215
 216    /* Initialize CPU registers.  */
 217    env->vbr = 0;
 218    /* TODO: Configure BARs.  */
 219
 220    /* DRAM at 0x40000000 */
 221    memory_region_init_ram(ram, NULL, "mcf5208.ram", ram_size);
 222    vmstate_register_ram_global(ram);
 223    memory_region_add_subregion(address_space_mem, 0x40000000, ram);
 224
 225    /* Internal SRAM.  */
 226    memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384);
 227    vmstate_register_ram_global(sram);
 228    memory_region_add_subregion(address_space_mem, 0x80000000, sram);
 229
 230    /* Internal peripherals.  */
 231    pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu);
 232
 233    mcf_uart_mm_init(address_space_mem, 0xfc060000, pic[26], serial_hds[0]);
 234    mcf_uart_mm_init(address_space_mem, 0xfc064000, pic[27], serial_hds[1]);
 235    mcf_uart_mm_init(address_space_mem, 0xfc068000, pic[28], serial_hds[2]);
 236
 237    mcf5208_sys_init(address_space_mem, pic);
 238
 239    if (nb_nics > 1) {
 240        fprintf(stderr, "Too many NICs\n");
 241        exit(1);
 242    }
 243    if (nd_table[0].used)
 244        mcf_fec_init(address_space_mem, &nd_table[0],
 245                     0xfc030000, pic + 36);
 246
 247    /*  0xfc000000 SCM.  */
 248    /*  0xfc004000 XBS.  */
 249    /*  0xfc008000 FlexBus CS.  */
 250    /* 0xfc030000 FEC.  */
 251    /*  0xfc040000 SCM + Power management.  */
 252    /*  0xfc044000 eDMA.  */
 253    /* 0xfc048000 INTC.  */
 254    /*  0xfc058000 I2C.  */
 255    /*  0xfc05c000 QSPI.  */
 256    /* 0xfc060000 UART0.  */
 257    /* 0xfc064000 UART0.  */
 258    /* 0xfc068000 UART0.  */
 259    /*  0xfc070000 DMA timers.  */
 260    /* 0xfc080000 PIT0.  */
 261    /* 0xfc084000 PIT1.  */
 262    /*  0xfc088000 EPORT.  */
 263    /*  0xfc08c000 Watchdog.  */
 264    /*  0xfc090000 clock module.  */
 265    /*  0xfc0a0000 CCM + reset.  */
 266    /*  0xfc0a4000 GPIO.  */
 267    /* 0xfc0a8000 SDRAM controller.  */
 268
 269    /* Load kernel.  */
 270    if (!kernel_filename) {
 271        if (qtest_enabled()) {
 272            return;
 273        }
 274        fprintf(stderr, "Kernel image must be specified\n");
 275        exit(1);
 276    }
 277
 278    kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
 279                           NULL, NULL, 1, ELF_MACHINE, 0);
 280    entry = elf_entry;
 281    if (kernel_size < 0) {
 282        kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
 283    }
 284    if (kernel_size < 0) {
 285        kernel_size = load_image_targphys(kernel_filename, 0x40000000,
 286                                          ram_size);
 287        entry = 0x40000000;
 288    }
 289    if (kernel_size < 0) {
 290        fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
 291        exit(1);
 292    }
 293
 294    env->pc = entry;
 295}
 296
 297static QEMUMachine mcf5208evb_machine = {
 298    .name = "mcf5208evb",
 299    .desc = "MCF5206EVB",
 300    .init = mcf5208evb_init,
 301    .is_default = 1,
 302};
 303
 304static void mcf5208evb_machine_init(void)
 305{
 306    qemu_register_machine(&mcf5208evb_machine);
 307}
 308
 309machine_init(mcf5208evb_machine_init);
 310