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