qemu/hw/mpcore.c
<<
>>
Prefs
   1/*
   2 * ARM MPCore internal peripheral emulation (common code).
   3 *
   4 * Copyright (c) 2006-2007 CodeSourcery.
   5 * Written by Paul Brook
   6 *
   7 * This code is licenced under the GPL.
   8 */
   9
  10#include "sysbus.h"
  11#include "qemu-timer.h"
  12
  13#define NCPU 4
  14
  15static inline int
  16gic_get_current_cpu(void)
  17{
  18  return cpu_single_env->cpu_index;
  19}
  20
  21#include "arm_gic.c"
  22
  23/* MPCore private memory region.  */
  24
  25typedef struct {
  26    uint32_t count;
  27    uint32_t load;
  28    uint32_t control;
  29    uint32_t status;
  30    uint32_t old_status;
  31    int64_t tick;
  32    QEMUTimer *timer;
  33    struct mpcore_priv_state *mpcore;
  34    int id; /* Encodes both timer/watchdog and CPU.  */
  35} mpcore_timer_state;
  36
  37typedef struct mpcore_priv_state {
  38    gic_state gic;
  39    uint32_t scu_control;
  40    int iomemtype;
  41    mpcore_timer_state timer[8];
  42    uint32_t num_cpu;
  43} mpcore_priv_state;
  44
  45/* Per-CPU Timers.  */
  46
  47static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
  48{
  49    if (s->status & ~s->old_status) {
  50        gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
  51    }
  52    s->old_status = s->status;
  53}
  54
  55/* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
  56static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
  57{
  58    return (((s->control >> 8) & 0xff) + 1) * 10;
  59}
  60
  61static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
  62{
  63    if (s->count == 0)
  64        return;
  65    if (restart)
  66        s->tick = qemu_get_clock(vm_clock);
  67    s->tick += (int64_t)s->count * mpcore_timer_scale(s);
  68    qemu_mod_timer(s->timer, s->tick);
  69}
  70
  71static void mpcore_timer_tick(void *opaque)
  72{
  73    mpcore_timer_state *s = (mpcore_timer_state *)opaque;
  74    s->status = 1;
  75    if (s->control & 2) {
  76        s->count = s->load;
  77        mpcore_timer_reload(s, 0);
  78    } else {
  79        s->count = 0;
  80    }
  81    mpcore_timer_update_irq(s);
  82}
  83
  84static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
  85{
  86    int64_t val;
  87    switch (offset) {
  88    case 0: /* Load */
  89        return s->load;
  90        /* Fall through.  */
  91    case 4: /* Counter.  */
  92        if (((s->control & 1) == 0) || (s->count == 0))
  93            return 0;
  94        /* Slow and ugly, but hopefully won't happen too often.  */
  95        val = s->tick - qemu_get_clock(vm_clock);
  96        val /= mpcore_timer_scale(s);
  97        if (val < 0)
  98            val = 0;
  99        return val;
 100    case 8: /* Control.  */
 101        return s->control;
 102    case 12: /* Interrupt status.  */
 103        return s->status;
 104    default:
 105        return 0;
 106    }
 107}
 108
 109static void mpcore_timer_write(mpcore_timer_state *s, int offset,
 110                               uint32_t value)
 111{
 112    int64_t old;
 113    switch (offset) {
 114    case 0: /* Load */
 115        s->load = value;
 116        /* Fall through.  */
 117    case 4: /* Counter.  */
 118        if ((s->control & 1) && s->count) {
 119            /* Cancel the previous timer.  */
 120            qemu_del_timer(s->timer);
 121        }
 122        s->count = value;
 123        if (s->control & 1) {
 124            mpcore_timer_reload(s, 1);
 125        }
 126        break;
 127    case 8: /* Control.  */
 128        old = s->control;
 129        s->control = value;
 130        if (((old & 1) == 0) && (value & 1)) {
 131            if (s->count == 0 && (s->control & 2))
 132                s->count = s->load;
 133            mpcore_timer_reload(s, 1);
 134        }
 135        break;
 136    case 12: /* Interrupt status.  */
 137        s->status &= ~value;
 138        mpcore_timer_update_irq(s);
 139        break;
 140    }
 141}
 142
 143static void mpcore_timer_init(mpcore_priv_state *mpcore,
 144                              mpcore_timer_state *s, int id)
 145{
 146    s->id = id;
 147    s->mpcore = mpcore;
 148    s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s);
 149}
 150
 151
 152/* Per-CPU private memory mapped IO.  */
 153
 154static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
 155{
 156    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
 157    int id;
 158    offset &= 0xfff;
 159    if (offset < 0x100) {
 160        /* SCU */
 161        switch (offset) {
 162        case 0x00: /* Control.  */
 163            return s->scu_control;
 164        case 0x04: /* Configuration.  */
 165            id = ((1 << s->num_cpu) - 1) << 4;
 166            return id | (s->num_cpu - 1);
 167        case 0x08: /* CPU status.  */
 168            return 0;
 169        case 0x0c: /* Invalidate all.  */
 170            return 0;
 171        default:
 172            goto bad_reg;
 173        }
 174    } else if (offset < 0x600) {
 175        /* Interrupt controller.  */
 176        if (offset < 0x200) {
 177            id = gic_get_current_cpu();
 178        } else {
 179            id = (offset - 0x200) >> 8;
 180            if (id >= s->num_cpu) {
 181                return 0;
 182            }
 183        }
 184        return gic_cpu_read(&s->gic, id, offset & 0xff);
 185    } else if (offset < 0xb00) {
 186        /* Timers.  */
 187        if (offset < 0x700) {
 188            id = gic_get_current_cpu();
 189        } else {
 190            id = (offset - 0x700) >> 8;
 191            if (id >= s->num_cpu) {
 192                return 0;
 193            }
 194        }
 195        id <<= 1;
 196        if (offset & 0x20)
 197          id++;
 198        return mpcore_timer_read(&s->timer[id], offset & 0xf);
 199    }
 200bad_reg:
 201    hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
 202    return 0;
 203}
 204
 205static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
 206                          uint32_t value)
 207{
 208    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
 209    int id;
 210    offset &= 0xfff;
 211    if (offset < 0x100) {
 212        /* SCU */
 213        switch (offset) {
 214        case 0: /* Control register.  */
 215            s->scu_control = value & 1;
 216            break;
 217        case 0x0c: /* Invalidate all.  */
 218            /* This is a no-op as cache is not emulated.  */
 219            break;
 220        default:
 221            goto bad_reg;
 222        }
 223    } else if (offset < 0x600) {
 224        /* Interrupt controller.  */
 225        if (offset < 0x200) {
 226            id = gic_get_current_cpu();
 227        } else {
 228            id = (offset - 0x200) >> 8;
 229        }
 230        if (id < s->num_cpu) {
 231            gic_cpu_write(&s->gic, id, offset & 0xff, value);
 232        }
 233    } else if (offset < 0xb00) {
 234        /* Timers.  */
 235        if (offset < 0x700) {
 236            id = gic_get_current_cpu();
 237        } else {
 238            id = (offset - 0x700) >> 8;
 239        }
 240        if (id < s->num_cpu) {
 241            id <<= 1;
 242            if (offset & 0x20)
 243              id++;
 244            mpcore_timer_write(&s->timer[id], offset & 0xf, value);
 245        }
 246        return;
 247    }
 248    return;
 249bad_reg:
 250    hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
 251}
 252
 253static CPUReadMemoryFunc * const mpcore_priv_readfn[] = {
 254   mpcore_priv_read,
 255   mpcore_priv_read,
 256   mpcore_priv_read
 257};
 258
 259static CPUWriteMemoryFunc * const mpcore_priv_writefn[] = {
 260   mpcore_priv_write,
 261   mpcore_priv_write,
 262   mpcore_priv_write
 263};
 264
 265static void mpcore_priv_map(SysBusDevice *dev, target_phys_addr_t base)
 266{
 267    mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
 268    cpu_register_physical_memory(base, 0x1000, s->iomemtype);
 269    cpu_register_physical_memory(base + 0x1000, 0x1000, s->gic.iomemtype);
 270}
 271
 272static int mpcore_priv_init(SysBusDevice *dev)
 273{
 274    mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
 275    int i;
 276
 277    gic_init(&s->gic, s->num_cpu);
 278    s->iomemtype = cpu_register_io_memory(mpcore_priv_readfn,
 279                                          mpcore_priv_writefn, s,
 280                                          DEVICE_NATIVE_ENDIAN);
 281    sysbus_init_mmio_cb(dev, 0x2000, mpcore_priv_map);
 282    for (i = 0; i < s->num_cpu * 2; i++) {
 283        mpcore_timer_init(s, &s->timer[i], i);
 284    }
 285    return 0;
 286}
 287