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