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 "qemu/osdep.h"
  23#include "hw/ptimer.h"
  24#include "hw/timer/arm_mptimer.h"
  25#include "qapi/error.h"
  26#include "qemu/main-loop.h"
  27#include "qom/cpu.h"
  28
  29#define PTIMER_POLICY                       \
  30    (PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |  \
  31     PTIMER_POLICY_CONTINUOUS_TRIGGER    |  \
  32     PTIMER_POLICY_NO_IMMEDIATE_TRIGGER  |  \
  33     PTIMER_POLICY_NO_IMMEDIATE_RELOAD   |  \
  34     PTIMER_POLICY_NO_COUNTER_ROUND_DOWN)
  35
  36/* This device implements the per-cpu private timer and watchdog block
  37 * which is used in both the ARM11MPCore and Cortex-A9MP.
  38 */
  39
  40static inline int get_current_cpu(ARMMPTimerState *s)
  41{
  42    int cpu_id = current_cpu ? current_cpu->cpu_index : 0;
  43
  44    if (cpu_id >= s->num_cpu) {
  45        hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
  46                 s->num_cpu, cpu_id);
  47    }
  48
  49    return cpu_id;
  50}
  51
  52static inline void timerblock_update_irq(TimerBlock *tb)
  53{
  54    qemu_set_irq(tb->irq, tb->status && (tb->control & 4));
  55}
  56
  57/* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
  58static inline uint32_t timerblock_scale(uint32_t control)
  59{
  60    return (((control >> 8) & 0xff) + 1) * 10;
  61}
  62
  63static inline void timerblock_set_count(struct ptimer_state *timer,
  64                                        uint32_t control, uint64_t *count)
  65{
  66    /* PTimer would trigger interrupt for periodic timer when counter set
  67     * to 0, MPtimer under certain condition only.
  68     */
  69    if ((control & 3) == 3 && (control & 0xff00) == 0 && *count == 0) {
  70        *count = ptimer_get_limit(timer);
  71    }
  72    ptimer_set_count(timer, *count);
  73}
  74
  75static inline void timerblock_run(struct ptimer_state *timer,
  76                                  uint32_t control, uint32_t load)
  77{
  78    if ((control & 1) && ((control & 0xff00) || load != 0)) {
  79        ptimer_run(timer, !(control & 2));
  80    }
  81}
  82
  83static void timerblock_tick(void *opaque)
  84{
  85    TimerBlock *tb = (TimerBlock *)opaque;
  86    /* Periodic timer with load = 0 and prescaler != 0 would re-trigger
  87     * IRQ after one period, otherwise it either stops or wraps around.
  88     */
  89    if ((tb->control & 2) && (tb->control & 0xff00) == 0 &&
  90            ptimer_get_limit(tb->timer) == 0) {
  91        ptimer_stop(tb->timer);
  92    }
  93    tb->status = 1;
  94    timerblock_update_irq(tb);
  95}
  96
  97static uint64_t timerblock_read(void *opaque, hwaddr addr,
  98                                unsigned size)
  99{
 100    TimerBlock *tb = (TimerBlock *)opaque;
 101    switch (addr) {
 102    case 0: /* Load */
 103        return ptimer_get_limit(tb->timer);
 104    case 4: /* Counter.  */
 105        return ptimer_get_count(tb->timer);
 106    case 8: /* Control.  */
 107        return tb->control;
 108    case 12: /* Interrupt status.  */
 109        return tb->status;
 110    default:
 111        return 0;
 112    }
 113}
 114
 115static void timerblock_write(void *opaque, hwaddr addr,
 116                             uint64_t value, unsigned size)
 117{
 118    TimerBlock *tb = (TimerBlock *)opaque;
 119    uint32_t control = tb->control;
 120    switch (addr) {
 121    case 0: /* Load */
 122        /* Setting load to 0 stops the timer without doing the tick if
 123         * prescaler = 0.
 124         */
 125        if ((control & 1) && (control & 0xff00) == 0 && value == 0) {
 126            ptimer_stop(tb->timer);
 127        }
 128        ptimer_set_limit(tb->timer, value, 1);
 129        timerblock_run(tb->timer, control, value);
 130        break;
 131    case 4: /* Counter.  */
 132        /* Setting counter to 0 stops the one-shot timer, or periodic with
 133         * load = 0, without doing the tick if prescaler = 0.
 134         */
 135        if ((control & 1) && (control & 0xff00) == 0 && value == 0 &&
 136                (!(control & 2) || ptimer_get_limit(tb->timer) == 0)) {
 137            ptimer_stop(tb->timer);
 138        }
 139        timerblock_set_count(tb->timer, control, &value);
 140        timerblock_run(tb->timer, control, value);
 141        break;
 142    case 8: /* Control.  */
 143        if ((control & 3) != (value & 3)) {
 144            ptimer_stop(tb->timer);
 145        }
 146        if ((control & 0xff00) != (value & 0xff00)) {
 147            ptimer_set_period(tb->timer, timerblock_scale(value));
 148        }
 149        if (value & 1) {
 150            uint64_t count = ptimer_get_count(tb->timer);
 151            /* Re-load periodic timer counter if needed.  */
 152            if ((value & 2) && count == 0) {
 153                timerblock_set_count(tb->timer, value, &count);
 154            }
 155            timerblock_run(tb->timer, value, count);
 156        }
 157        tb->control = value;
 158        break;
 159    case 12: /* Interrupt status.  */
 160        tb->status &= ~value;
 161        timerblock_update_irq(tb);
 162        break;
 163    }
 164}
 165
 166/* Wrapper functions to implement the "read timer/watchdog for
 167 * the current CPU" memory regions.
 168 */
 169static uint64_t arm_thistimer_read(void *opaque, hwaddr addr,
 170                                   unsigned size)
 171{
 172    ARMMPTimerState *s = (ARMMPTimerState *)opaque;
 173    int id = get_current_cpu(s);
 174    return timerblock_read(&s->timerblock[id], addr, size);
 175}
 176
 177static void arm_thistimer_write(void *opaque, hwaddr addr,
 178                                uint64_t value, unsigned size)
 179{
 180    ARMMPTimerState *s = (ARMMPTimerState *)opaque;
 181    int id = get_current_cpu(s);
 182    timerblock_write(&s->timerblock[id], addr, value, size);
 183}
 184
 185static const MemoryRegionOps arm_thistimer_ops = {
 186    .read = arm_thistimer_read,
 187    .write = arm_thistimer_write,
 188    .valid = {
 189        .min_access_size = 4,
 190        .max_access_size = 4,
 191    },
 192    .endianness = DEVICE_NATIVE_ENDIAN,
 193};
 194
 195static const MemoryRegionOps timerblock_ops = {
 196    .read = timerblock_read,
 197    .write = timerblock_write,
 198    .valid = {
 199        .min_access_size = 4,
 200        .max_access_size = 4,
 201    },
 202    .endianness = DEVICE_NATIVE_ENDIAN,
 203};
 204
 205static void timerblock_reset(TimerBlock *tb)
 206{
 207    tb->control = 0;
 208    tb->status = 0;
 209    if (tb->timer) {
 210        ptimer_stop(tb->timer);
 211        ptimer_set_limit(tb->timer, 0, 1);
 212        ptimer_set_period(tb->timer, timerblock_scale(0));
 213    }
 214}
 215
 216static void arm_mptimer_reset(DeviceState *dev)
 217{
 218    ARMMPTimerState *s = ARM_MPTIMER(dev);
 219    int i;
 220
 221    for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) {
 222        timerblock_reset(&s->timerblock[i]);
 223    }
 224}
 225
 226static void arm_mptimer_init(Object *obj)
 227{
 228    ARMMPTimerState *s = ARM_MPTIMER(obj);
 229
 230    memory_region_init_io(&s->iomem, obj, &arm_thistimer_ops, s,
 231                          "arm_mptimer_timer", 0x20);
 232    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
 233}
 234
 235static void arm_mptimer_realize(DeviceState *dev, Error **errp)
 236{
 237    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 238    ARMMPTimerState *s = ARM_MPTIMER(dev);
 239    int i;
 240
 241    if (s->num_cpu < 1 || s->num_cpu > ARM_MPTIMER_MAX_CPUS) {
 242        error_setg(errp, "num-cpu must be between 1 and %d",
 243                   ARM_MPTIMER_MAX_CPUS);
 244        return;
 245    }
 246    /* We implement one timer block per CPU, and expose multiple MMIO regions:
 247     *  * region 0 is "timer for this core"
 248     *  * region 1 is "timer for core 0"
 249     *  * region 2 is "timer for core 1"
 250     * and so on.
 251     * The outgoing interrupt lines are
 252     *  * timer for core 0
 253     *  * timer for core 1
 254     * and so on.
 255     */
 256    for (i = 0; i < s->num_cpu; i++) {
 257        TimerBlock *tb = &s->timerblock[i];
 258        QEMUBH *bh = qemu_bh_new(timerblock_tick, tb);
 259        tb->timer = ptimer_init(bh, PTIMER_POLICY);
 260        sysbus_init_irq(sbd, &tb->irq);
 261        memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb,
 262                              "arm_mptimer_timerblock", 0x20);
 263        sysbus_init_mmio(sbd, &tb->iomem);
 264    }
 265}
 266
 267static const VMStateDescription vmstate_timerblock = {
 268    .name = "arm_mptimer_timerblock",
 269    .version_id = 3,
 270    .minimum_version_id = 3,
 271    .fields = (VMStateField[]) {
 272        VMSTATE_UINT32(control, TimerBlock),
 273        VMSTATE_UINT32(status, TimerBlock),
 274        VMSTATE_PTIMER(timer, TimerBlock),
 275        VMSTATE_END_OF_LIST()
 276    }
 277};
 278
 279static const VMStateDescription vmstate_arm_mptimer = {
 280    .name = "arm_mptimer",
 281    .version_id = 3,
 282    .minimum_version_id = 3,
 283    .fields = (VMStateField[]) {
 284        VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu,
 285                                     3, vmstate_timerblock, TimerBlock),
 286        VMSTATE_END_OF_LIST()
 287    }
 288};
 289
 290static Property arm_mptimer_properties[] = {
 291    DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0),
 292    DEFINE_PROP_END_OF_LIST()
 293};
 294
 295static void arm_mptimer_class_init(ObjectClass *klass, void *data)
 296{
 297    DeviceClass *dc = DEVICE_CLASS(klass);
 298
 299    dc->realize = arm_mptimer_realize;
 300    dc->vmsd = &vmstate_arm_mptimer;
 301    dc->reset = arm_mptimer_reset;
 302    dc->props = arm_mptimer_properties;
 303}
 304
 305static const TypeInfo arm_mptimer_info = {
 306    .name          = TYPE_ARM_MPTIMER,
 307    .parent        = TYPE_SYS_BUS_DEVICE,
 308    .instance_size = sizeof(ARMMPTimerState),
 309    .instance_init = arm_mptimer_init,
 310    .class_init    = arm_mptimer_class_init,
 311};
 312
 313static void arm_mptimer_register_types(void)
 314{
 315    type_register_static(&arm_mptimer_info);
 316}
 317
 318type_init(arm_mptimer_register_types)
 319