qemu/hw/timer/arm_mptimer.c
<<
>>
Prefs
   1/*
   2 * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP
   3 *
   4 * Copyright (c) 2006-2007 CodeSourcery.
   5 * Copyright (c) 2011 Linaro Limited
   6 * Written by Paul Brook, Peter Maydell
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * as published by the Free Software Foundation; either version
  11 * 2 of the License, or (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License along
  19 * with this program; if not, see <http://www.gnu.org/licenses/>.
  20 */
  21
  22#include "hw/timer/arm_mptimer.h"
  23#include "qemu/timer.h"
  24#include "qom/cpu.h"
  25
  26/* This device implements the per-cpu private timer and watchdog block
  27 * which is used in both the ARM11MPCore and Cortex-A9MP.
  28 */
  29
  30static inline int get_current_cpu(ARMMPTimerState *s)
  31{
  32    if (current_cpu->cpu_index >= s->num_cpu) {
  33        hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
  34                 s->num_cpu, current_cpu->cpu_index);
  35    }
  36    return current_cpu->cpu_index;
  37}
  38
  39static inline void timerblock_update_irq(TimerBlock *tb)
  40{
  41    qemu_set_irq(tb->irq, tb->status);
  42}
  43
  44/* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
  45static inline uint32_t timerblock_scale(TimerBlock *tb)
  46{
  47    return (((tb->control >> 8) & 0xff) + 1) * 10;
  48}
  49
  50static void timerblock_reload(TimerBlock *tb, int restart)
  51{
  52    if (tb->count == 0) {
  53        return;
  54    }
  55    if (restart) {
  56        tb->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  57    }
  58    tb->tick += (int64_t)tb->count * timerblock_scale(tb);
  59    timer_mod(tb->timer, tb->tick);
  60}
  61
  62static void timerblock_tick(void *opaque)
  63{
  64    TimerBlock *tb = (TimerBlock *)opaque;
  65    tb->status = 1;
  66    if (tb->control & 2) {
  67        tb->count = tb->load;
  68        timerblock_reload(tb, 0);
  69    } else {
  70        tb->count = 0;
  71    }
  72    timerblock_update_irq(tb);
  73}
  74
  75static uint64_t timerblock_read(void *opaque, hwaddr addr,
  76                                unsigned size)
  77{
  78    TimerBlock *tb = (TimerBlock *)opaque;
  79    int64_t val;
  80    switch (addr) {
  81    case 0: /* Load */
  82        return tb->load;
  83    case 4: /* Counter.  */
  84        if (((tb->control & 1) == 0) || (tb->count == 0)) {
  85            return 0;
  86        }
  87        /* Slow and ugly, but hopefully won't happen too often.  */
  88        val = tb->tick - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  89        val /= timerblock_scale(tb);
  90        if (val < 0) {
  91            val = 0;
  92        }
  93        return val;
  94    case 8: /* Control.  */
  95        return tb->control;
  96    case 12: /* Interrupt status.  */
  97        return tb->status;
  98    default:
  99        return 0;
 100    }
 101}
 102
 103static void timerblock_write(void *opaque, hwaddr addr,
 104                             uint64_t value, unsigned size)
 105{
 106    TimerBlock *tb = (TimerBlock *)opaque;
 107    int64_t old;
 108    switch (addr) {
 109    case 0: /* Load */
 110        tb->load = value;
 111        /* Fall through.  */
 112    case 4: /* Counter.  */
 113        if ((tb->control & 1) && tb->count) {
 114            /* Cancel the previous timer.  */
 115            timer_del(tb->timer);
 116        }
 117        tb->count = value;
 118        if (tb->control & 1) {
 119            timerblock_reload(tb, 1);
 120        }
 121        break;
 122    case 8: /* Control.  */
 123        old = tb->control;
 124        tb->control = value;
 125        if (((old & 1) == 0) && (value & 1)) {
 126            if (tb->count == 0 && (tb->control & 2)) {
 127                tb->count = tb->load;
 128            }
 129            timerblock_reload(tb, 1);
 130        }
 131        break;
 132    case 12: /* Interrupt status.  */
 133        tb->status &= ~value;
 134        timerblock_update_irq(tb);
 135        break;
 136    }
 137}
 138
 139/* Wrapper functions to implement the "read timer/watchdog for
 140 * the current CPU" memory regions.
 141 */
 142static uint64_t arm_thistimer_read(void *opaque, hwaddr addr,
 143                                   unsigned size)
 144{
 145    ARMMPTimerState *s = (ARMMPTimerState *)opaque;
 146    int id = get_current_cpu(s);
 147    return timerblock_read(&s->timerblock[id], addr, size);
 148}
 149
 150static void arm_thistimer_write(void *opaque, hwaddr addr,
 151                                uint64_t value, unsigned size)
 152{
 153    ARMMPTimerState *s = (ARMMPTimerState *)opaque;
 154    int id = get_current_cpu(s);
 155    timerblock_write(&s->timerblock[id], addr, value, size);
 156}
 157
 158static const MemoryRegionOps arm_thistimer_ops = {
 159    .read = arm_thistimer_read,
 160    .write = arm_thistimer_write,
 161    .valid = {
 162        .min_access_size = 4,
 163        .max_access_size = 4,
 164    },
 165    .endianness = DEVICE_NATIVE_ENDIAN,
 166};
 167
 168static const MemoryRegionOps timerblock_ops = {
 169    .read = timerblock_read,
 170    .write = timerblock_write,
 171    .valid = {
 172        .min_access_size = 4,
 173        .max_access_size = 4,
 174    },
 175    .endianness = DEVICE_NATIVE_ENDIAN,
 176};
 177
 178static void timerblock_reset(TimerBlock *tb)
 179{
 180    tb->count = 0;
 181    tb->load = 0;
 182    tb->control = 0;
 183    tb->status = 0;
 184    tb->tick = 0;
 185    if (tb->timer) {
 186        timer_del(tb->timer);
 187    }
 188}
 189
 190static void arm_mptimer_reset(DeviceState *dev)
 191{
 192    ARMMPTimerState *s = ARM_MPTIMER(dev);
 193    int i;
 194
 195    for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) {
 196        timerblock_reset(&s->timerblock[i]);
 197    }
 198}
 199
 200static void arm_mptimer_init(Object *obj)
 201{
 202    ARMMPTimerState *s = ARM_MPTIMER(obj);
 203
 204    memory_region_init_io(&s->iomem, obj, &arm_thistimer_ops, s,
 205                          "arm_mptimer_timer", 0x20);
 206    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
 207}
 208
 209static void arm_mptimer_realize(DeviceState *dev, Error **errp)
 210{
 211    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 212    ARMMPTimerState *s = ARM_MPTIMER(dev);
 213    int i;
 214
 215    if (s->num_cpu < 1 || s->num_cpu > ARM_MPTIMER_MAX_CPUS) {
 216        hw_error("%s: num-cpu must be between 1 and %d\n",
 217                 __func__, ARM_MPTIMER_MAX_CPUS);
 218    }
 219    /* We implement one timer block per CPU, and expose multiple MMIO regions:
 220     *  * region 0 is "timer for this core"
 221     *  * region 1 is "timer for core 0"
 222     *  * region 2 is "timer for core 1"
 223     * and so on.
 224     * The outgoing interrupt lines are
 225     *  * timer for core 0
 226     *  * timer for core 1
 227     * and so on.
 228     */
 229    for (i = 0; i < s->num_cpu; i++) {
 230        TimerBlock *tb = &s->timerblock[i];
 231        tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb);
 232        sysbus_init_irq(sbd, &tb->irq);
 233        memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb,
 234                              "arm_mptimer_timerblock", 0x20);
 235        sysbus_init_mmio(sbd, &tb->iomem);
 236    }
 237}
 238
 239static const VMStateDescription vmstate_timerblock = {
 240    .name = "arm_mptimer_timerblock",
 241    .version_id = 2,
 242    .minimum_version_id = 2,
 243    .fields = (VMStateField[]) {
 244        VMSTATE_UINT32(count, TimerBlock),
 245        VMSTATE_UINT32(load, TimerBlock),
 246        VMSTATE_UINT32(control, TimerBlock),
 247        VMSTATE_UINT32(status, TimerBlock),
 248        VMSTATE_INT64(tick, TimerBlock),
 249        VMSTATE_TIMER(timer, TimerBlock),
 250        VMSTATE_END_OF_LIST()
 251    }
 252};
 253
 254static const VMStateDescription vmstate_arm_mptimer = {
 255    .name = "arm_mptimer",
 256    .version_id = 2,
 257    .minimum_version_id = 2,
 258    .fields = (VMStateField[]) {
 259        VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu,
 260                                     2, vmstate_timerblock, TimerBlock),
 261        VMSTATE_END_OF_LIST()
 262    }
 263};
 264
 265static Property arm_mptimer_properties[] = {
 266    DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0),
 267    DEFINE_PROP_END_OF_LIST()
 268};
 269
 270static void arm_mptimer_class_init(ObjectClass *klass, void *data)
 271{
 272    DeviceClass *dc = DEVICE_CLASS(klass);
 273
 274    dc->realize = arm_mptimer_realize;
 275    dc->vmsd = &vmstate_arm_mptimer;
 276    dc->reset = arm_mptimer_reset;
 277    dc->props = arm_mptimer_properties;
 278}
 279
 280static const TypeInfo arm_mptimer_info = {
 281    .name          = TYPE_ARM_MPTIMER,
 282    .parent        = TYPE_SYS_BUS_DEVICE,
 283    .instance_size = sizeof(ARMMPTimerState),
 284    .instance_init = arm_mptimer_init,
 285    .class_init    = arm_mptimer_class_init,
 286};
 287
 288static void arm_mptimer_register_types(void)
 289{
 290    type_register_static(&arm_mptimer_info);
 291}
 292
 293type_init(arm_mptimer_register_types)
 294