qemu/hw/timer/xilinx_iomod_pit.c
<<
>>
Prefs
   1/*
   2 * QEMU model of Xilinx I/O Module PIT
   3 *
   4 * Copyright (c) 2013 Xilinx Inc
   5 * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27#include "hw/sysbus.h"
  28#include "hw/ptimer.h"
  29#include "hw/register.h"
  30#include "qemu/log.h"
  31#include "qemu/main-loop.h"
  32#include "qapi/error.h"
  33#include "hw/fdt_generic_util.h"
  34
  35#ifndef XILINX_IO_MODULE_PIT_ERR_DEBUG
  36#define XILINX_IO_MODULE_PIT_ERR_DEBUG 0
  37#endif
  38
  39#define TYPE_XILINX_IO_MODULE_PIT "xlnx.io_pit"
  40
  41#define XILINX_IO_MODULE_PIT(obj) \
  42     OBJECT_CHECK(XilinxPIT, (obj), TYPE_XILINX_IO_MODULE_PIT)
  43
  44
  45#define R_IOM_PIT_PRELOAD           (0x00 / 4)
  46#define R_IOM_PIT_COUNTER           (0x04 / 4)
  47#define R_IOM_PIT_CONTROL           (0x08 / 4)
  48#define IOM_PIT_CONTROL_EN          (1 << 0)
  49#define IOM_PIT_CONTROL_PRELOAD     (1 << 1)
  50#define R_MAX                       (R_IOM_PIT_CONTROL + 1)
  51
  52typedef struct XilinxPIT {
  53    SysBusDevice parent_obj;
  54    MemoryRegion iomem;
  55    qemu_irq irq;
  56
  57    struct {
  58        bool use;
  59        uint32_t size;
  60        bool readable;
  61        bool interrupt;
  62    } cfg;
  63    uint32_t frequency;
  64    /* Counter in Pre-Scalar(ps) Mode */
  65    uint32_t ps_counter;
  66    /* ps_mode irq-in to enable/disable pre-scalar */
  67    bool ps_enable;
  68    /* IRQ to pulse out when present timer hits zero */
  69    qemu_irq hit_out;
  70    /* State var to remember hit_in level */
  71    bool ps_level;
  72
  73    QEMUBH *bh;
  74    ptimer_state *ptimer;
  75    uint32_t regs[R_MAX];
  76    RegisterInfo regs_info[R_MAX];
  77    const char *prefix;
  78} XilinxPIT;
  79
  80static Property xlx_iom_properties[] = {
  81    DEFINE_PROP_UINT32("frequency", XilinxPIT, frequency, 66*1000000),
  82    DEFINE_PROP_BOOL("use-pit", XilinxPIT, cfg.use, 0),
  83    DEFINE_PROP_UINT32("pit-size", XilinxPIT, cfg.size, 1),
  84    DEFINE_PROP_BOOL("pit-readable", XilinxPIT, cfg.readable, 1),
  85    DEFINE_PROP_BOOL("pit-interrupt", XilinxPIT, cfg.interrupt, 0),
  86    DEFINE_PROP_END_OF_LIST(),
  87};
  88
  89static uint64_t pit_ctr_pr(RegisterInfo *reg, uint64_t val)
  90{
  91    XilinxPIT *s = XILINX_IO_MODULE_PIT(reg->opaque);
  92    uint32_t r;
  93
  94    if (!s->cfg.use) {
  95        qemu_log_mask(LOG_GUEST_ERROR, "%s: Disabled\n", s->prefix);
  96        return 0xdeadbeef;
  97    }
  98
  99    if (s->ps_enable) {
 100        r = s->ps_counter;
 101    } else {
 102        r = ptimer_get_count(s->ptimer);
 103    }
 104    return r;
 105}
 106
 107static void pit_control_pw(RegisterInfo *reg, uint64_t value)
 108{
 109    XilinxPIT *s = XILINX_IO_MODULE_PIT(reg->opaque);
 110    uint32_t v32 = value;
 111
 112    if (!s->cfg.use) {
 113        qemu_log_mask(LOG_GUEST_ERROR, "%s: Disabled\n", s->prefix);
 114        return;
 115    }
 116
 117    ptimer_stop(s->ptimer);
 118    if (v32 & IOM_PIT_CONTROL_EN) {
 119        if (s->ps_enable) {
 120            /* pre-scalar mode Do-Nothing here. Wait for the friend to hit_in
 121             * and decrement the counter(s->ps_counter)*/
 122            s->ps_counter = s->regs[R_IOM_PIT_PRELOAD];
 123        } else {
 124            ptimer_set_limit(s->ptimer, s->regs[R_IOM_PIT_PRELOAD], 1);
 125            ptimer_run(s->ptimer, !(v32 & IOM_PIT_CONTROL_PRELOAD));
 126
 127        }
 128    }
 129}
 130
 131static void pit_timer_hit(void *opaque)
 132{
 133    XilinxPIT *s = XILINX_IO_MODULE_PIT(opaque);
 134
 135    qemu_irq_pulse(s->irq);
 136    /* hit_out to make another pit move its counter in pre-scalar mode */
 137    qemu_irq_pulse(s->hit_out);
 138}
 139
 140static void iom_pit_ps_hit_in(void *opaque, int n, int level)
 141{
 142    XilinxPIT *s = XILINX_IO_MODULE_PIT(opaque);
 143
 144    if (!(s->regs[R_IOM_PIT_CONTROL] & IOM_PIT_CONTROL_EN)) {
 145        /* PIT disabled */
 146        qemu_log_mask(LOG_GUEST_ERROR, "%s: Received pre-scalar hit when pit is\
 147                      Disabled. PIT is in One-shot mode or not enabled\n",\
 148                      s->prefix);
 149        return;
 150    }
 151
 152    /* Count only on positive edge */
 153    if (!s->ps_level && level) {
 154        s->ps_counter--;
 155        s->ps_level = level;
 156    } else {
 157        /* Not pos edge */
 158        s->ps_level = level;
 159        return;
 160    }
 161
 162    /* If timer expires, try to preload or stop */
 163    if (s->ps_counter == 0) {
 164        pit_timer_hit(opaque);
 165        /* Check for pit preload/one-shot mode */
 166        if (s->regs[R_IOM_PIT_CONTROL] & IOM_PIT_CONTROL_PRELOAD) {
 167            /* Preload Mode, Reload the ps_counter */
 168            s->ps_counter = s->regs[R_IOM_PIT_PRELOAD];
 169        } else {
 170            /* One-Shot mode, turn off the timer */
 171            s->regs[R_IOM_PIT_CONTROL] &= ~IOM_PIT_CONTROL_EN;
 172        }
 173    }
 174}
 175
 176static void iom_pit_ps_config(void *opaque, int n, int level)
 177{
 178    XilinxPIT *s = XILINX_IO_MODULE_PIT(opaque);
 179    s->ps_enable = level;
 180}
 181
 182static const RegisterAccessInfo pit_regs_info[] = {
 183    [R_IOM_PIT_PRELOAD] = { .name = "PRELOAD" },
 184    [R_IOM_PIT_COUNTER] = { .name = "COUNTER", .post_read = pit_ctr_pr },
 185    [R_IOM_PIT_CONTROL] = { .name = "CONTROL", .post_write = pit_control_pw },
 186};
 187
 188static const MemoryRegionOps iom_pit_ops = {
 189    .read = register_read_memory_le,
 190    .write = register_write_memory_le,
 191    .endianness = DEVICE_LITTLE_ENDIAN,
 192    .valid = {
 193        .min_access_size = 4,
 194        .max_access_size = 4,
 195    },
 196};
 197
 198static void iom_pit_reset(DeviceState *dev)
 199{
 200    XilinxPIT *s = XILINX_IO_MODULE_PIT(dev);
 201    unsigned int i;
 202
 203    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 204        register_reset(&s->regs_info[i]);
 205    }
 206    s->ps_level = false;
 207}
 208
 209static void xlx_iom_realize(DeviceState *dev, Error **errp)
 210{
 211    XilinxPIT *s = XILINX_IO_MODULE_PIT(dev);
 212    unsigned int i;
 213
 214    s->prefix = object_get_canonical_path(OBJECT(dev));
 215
 216    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 217        RegisterInfo *r = &s->regs_info[i];
 218
 219        *r = (RegisterInfo) {
 220            .data = (uint8_t *)&s->regs[i],
 221            .data_size = sizeof(uint32_t),
 222            .access = &pit_regs_info[i],
 223            .debug = XILINX_IO_MODULE_PIT_ERR_DEBUG,
 224            .prefix = s->prefix,
 225            .opaque = s,
 226        };
 227        memory_region_init_io(&r->mem, OBJECT(dev), &iom_pit_ops, r,
 228                              r->access->name, 4);
 229        memory_region_add_subregion(&s->iomem, i * 4, &r->mem);
 230    }
 231
 232    if (s->cfg.use) {
 233        s->bh = qemu_bh_new(pit_timer_hit, s);
 234        s->ptimer = ptimer_init(s->bh);
 235        ptimer_set_freq(s->ptimer, s->frequency);
 236        /* IRQ out to pulse when present timer expires/reloads */
 237        qdev_init_gpio_out(dev, &s->hit_out, 1);
 238        /* IRQ in to enable pre-scalar mode. Routed from gpo1 */
 239        qdev_init_gpio_in_named(dev, iom_pit_ps_config, "ps_config", 1);
 240        /* hit_out of neighbouring PIT is received as hit_in */
 241        qdev_init_gpio_in_named(dev, iom_pit_ps_hit_in, "ps_hit_in", 1);
 242    }
 243}
 244
 245static void xlx_iom_pit_init(Object *obj)
 246{
 247    XilinxPIT *s = XILINX_IO_MODULE_PIT(obj);
 248    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 249
 250    memory_region_init_io(&s->iomem, obj, &iom_pit_ops, s,
 251                          TYPE_XILINX_IO_MODULE_PIT,
 252                          R_MAX * 4);
 253    sysbus_init_mmio(sbd, &s->iomem);
 254    sysbus_init_irq(sbd, &s->irq);
 255}
 256
 257static const VMStateDescription vmstate_xlx_iom = {
 258    .name = TYPE_XILINX_IO_MODULE_PIT,
 259    .version_id = 1,
 260    .minimum_version_id = 1,
 261    .minimum_version_id_old = 1,
 262    .fields = (VMStateField[]) {
 263        VMSTATE_END_OF_LIST(),
 264    }
 265};
 266
 267static void xlx_iom_class_init(ObjectClass *klass, void *data)
 268{
 269    DeviceClass *dc = DEVICE_CLASS(klass);
 270
 271    dc->reset = iom_pit_reset;
 272    dc->realize = xlx_iom_realize;
 273    dc->props = xlx_iom_properties;
 274    dc->vmsd = &vmstate_xlx_iom;
 275}
 276
 277static const TypeInfo xlx_iom_info = {
 278    .name          = TYPE_XILINX_IO_MODULE_PIT,
 279    .parent        = TYPE_SYS_BUS_DEVICE,
 280    .instance_size = sizeof(XilinxPIT),
 281    .class_init    = xlx_iom_class_init,
 282    .instance_init = xlx_iom_pit_init,
 283};
 284
 285static void xlx_iom_register_types(void)
 286{
 287    type_register_static(&xlx_iom_info);
 288}
 289
 290type_init(xlx_iom_register_types)
 291