qemu/hw/grlib_gptimer.c
<<
>>
Prefs
   1/*
   2 * QEMU GRLIB GPTimer Emulator
   3 *
   4 * Copyright (c) 2010-2011 AdaCore
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "sysbus.h"
  26#include "qemu-timer.h"
  27#include "ptimer.h"
  28
  29#include "trace.h"
  30
  31#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
  32#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
  33
  34#define GPTIMER_MAX_TIMERS 8
  35
  36/* GPTimer Config register fields */
  37#define GPTIMER_ENABLE      (1 << 0)
  38#define GPTIMER_RESTART     (1 << 1)
  39#define GPTIMER_LOAD        (1 << 2)
  40#define GPTIMER_INT_ENABLE  (1 << 3)
  41#define GPTIMER_INT_PENDING (1 << 4)
  42#define GPTIMER_CHAIN       (1 << 5) /* Not supported */
  43#define GPTIMER_DEBUG_HALT  (1 << 6) /* Not supported */
  44
  45/* Memory mapped register offsets */
  46#define SCALER_OFFSET         0x00
  47#define SCALER_RELOAD_OFFSET  0x04
  48#define CONFIG_OFFSET         0x08
  49#define COUNTER_OFFSET        0x00
  50#define COUNTER_RELOAD_OFFSET 0x04
  51#define TIMER_BASE            0x10
  52
  53typedef struct GPTimer     GPTimer;
  54typedef struct GPTimerUnit GPTimerUnit;
  55
  56struct GPTimer {
  57    QEMUBH *bh;
  58    struct ptimer_state *ptimer;
  59
  60    qemu_irq     irq;
  61    int          id;
  62    GPTimerUnit *unit;
  63
  64    /* registers */
  65    uint32_t counter;
  66    uint32_t reload;
  67    uint32_t config;
  68};
  69
  70struct GPTimerUnit {
  71    SysBusDevice  busdev;
  72    MemoryRegion iomem;
  73
  74    uint32_t nr_timers;         /* Number of timers available */
  75    uint32_t freq_hz;           /* System frequency */
  76    uint32_t irq_line;          /* Base irq line */
  77
  78    GPTimer *timers;
  79
  80    /* registers */
  81    uint32_t scaler;
  82    uint32_t reload;
  83    uint32_t config;
  84};
  85
  86static void grlib_gptimer_enable(GPTimer *timer)
  87{
  88    assert(timer != NULL);
  89
  90
  91    ptimer_stop(timer->ptimer);
  92
  93    if (!(timer->config & GPTIMER_ENABLE)) {
  94        /* Timer disabled */
  95        trace_grlib_gptimer_disabled(timer->id, timer->config);
  96        return;
  97    }
  98
  99    /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
 100       underflow. Set count + 1 to simulate the GPTimer behavior. */
 101
 102    trace_grlib_gptimer_enable(timer->id, timer->counter + 1);
 103
 104    ptimer_set_count(timer->ptimer, timer->counter + 1);
 105    ptimer_run(timer->ptimer, 1);
 106}
 107
 108static void grlib_gptimer_restart(GPTimer *timer)
 109{
 110    assert(timer != NULL);
 111
 112    trace_grlib_gptimer_restart(timer->id, timer->reload);
 113
 114    timer->counter = timer->reload;
 115    grlib_gptimer_enable(timer);
 116}
 117
 118static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
 119{
 120    int i = 0;
 121    uint32_t value = 0;
 122
 123    assert(unit != NULL);
 124
 125    if (scaler > 0) {
 126        value = unit->freq_hz / (scaler + 1);
 127    } else {
 128        value = unit->freq_hz;
 129    }
 130
 131    trace_grlib_gptimer_set_scaler(scaler, value);
 132
 133    for (i = 0; i < unit->nr_timers; i++) {
 134        ptimer_set_freq(unit->timers[i].ptimer, value);
 135    }
 136}
 137
 138static void grlib_gptimer_hit(void *opaque)
 139{
 140    GPTimer *timer = opaque;
 141    assert(timer != NULL);
 142
 143    trace_grlib_gptimer_hit(timer->id);
 144
 145    /* Timer expired */
 146
 147    if (timer->config & GPTIMER_INT_ENABLE) {
 148        /* Set the pending bit (only unset by write in the config register) */
 149        timer->config |= GPTIMER_INT_PENDING;
 150        qemu_irq_pulse(timer->irq);
 151    }
 152
 153    if (timer->config & GPTIMER_RESTART) {
 154        grlib_gptimer_restart(timer);
 155    }
 156}
 157
 158static uint64_t grlib_gptimer_read(void *opaque, target_phys_addr_t addr,
 159                                   unsigned size)
 160{
 161    GPTimerUnit        *unit  = opaque;
 162    target_phys_addr_t  timer_addr;
 163    int                 id;
 164    uint32_t            value = 0;
 165
 166    addr &= 0xff;
 167
 168    /* Unit registers */
 169    switch (addr) {
 170    case SCALER_OFFSET:
 171        trace_grlib_gptimer_readl(-1, addr, unit->scaler);
 172        return unit->scaler;
 173
 174    case SCALER_RELOAD_OFFSET:
 175        trace_grlib_gptimer_readl(-1, addr, unit->reload);
 176        return unit->reload;
 177
 178    case CONFIG_OFFSET:
 179        trace_grlib_gptimer_readl(-1, addr, unit->config);
 180        return unit->config;
 181
 182    default:
 183        break;
 184    }
 185
 186    timer_addr = (addr % TIMER_BASE);
 187    id         = (addr - TIMER_BASE) / TIMER_BASE;
 188
 189    if (id >= 0 && id < unit->nr_timers) {
 190
 191        /* GPTimer registers */
 192        switch (timer_addr) {
 193        case COUNTER_OFFSET:
 194            value = ptimer_get_count(unit->timers[id].ptimer);
 195            trace_grlib_gptimer_readl(id, addr, value);
 196            return value;
 197
 198        case COUNTER_RELOAD_OFFSET:
 199            value = unit->timers[id].reload;
 200            trace_grlib_gptimer_readl(id, addr, value);
 201            return value;
 202
 203        case CONFIG_OFFSET:
 204            trace_grlib_gptimer_readl(id, addr, unit->timers[id].config);
 205            return unit->timers[id].config;
 206
 207        default:
 208            break;
 209        }
 210
 211    }
 212
 213    trace_grlib_gptimer_readl(-1, addr, 0);
 214    return 0;
 215}
 216
 217static void grlib_gptimer_write(void *opaque, target_phys_addr_t addr,
 218                                uint64_t value, unsigned size)
 219{
 220    GPTimerUnit        *unit = opaque;
 221    target_phys_addr_t  timer_addr;
 222    int                 id;
 223
 224    addr &= 0xff;
 225
 226    /* Unit registers */
 227    switch (addr) {
 228    case SCALER_OFFSET:
 229        value &= 0xFFFF; /* clean up the value */
 230        unit->scaler = value;
 231        trace_grlib_gptimer_writel(-1, addr, unit->scaler);
 232        return;
 233
 234    case SCALER_RELOAD_OFFSET:
 235        value &= 0xFFFF; /* clean up the value */
 236        unit->reload = value;
 237        trace_grlib_gptimer_writel(-1, addr, unit->reload);
 238        grlib_gptimer_set_scaler(unit, value);
 239        return;
 240
 241    case CONFIG_OFFSET:
 242        /* Read Only (disable timer freeze not supported) */
 243        trace_grlib_gptimer_writel(-1, addr, 0);
 244        return;
 245
 246    default:
 247        break;
 248    }
 249
 250    timer_addr = (addr % TIMER_BASE);
 251    id         = (addr - TIMER_BASE) / TIMER_BASE;
 252
 253    if (id >= 0 && id < unit->nr_timers) {
 254
 255        /* GPTimer registers */
 256        switch (timer_addr) {
 257        case COUNTER_OFFSET:
 258            trace_grlib_gptimer_writel(id, addr, value);
 259            unit->timers[id].counter = value;
 260            grlib_gptimer_enable(&unit->timers[id]);
 261            return;
 262
 263        case COUNTER_RELOAD_OFFSET:
 264            trace_grlib_gptimer_writel(id, addr, value);
 265            unit->timers[id].reload = value;
 266            return;
 267
 268        case CONFIG_OFFSET:
 269            trace_grlib_gptimer_writel(id, addr, value);
 270
 271            if (value & GPTIMER_INT_PENDING) {
 272                /* clear pending bit */
 273                value &= ~GPTIMER_INT_PENDING;
 274            } else {
 275                /* keep pending bit */
 276                value |= unit->timers[id].config & GPTIMER_INT_PENDING;
 277            }
 278
 279            unit->timers[id].config = value;
 280
 281            /* gptimer_restart calls gptimer_enable, so if "enable" and "load"
 282               bits are present, we just have to call restart. */
 283
 284            if (value & GPTIMER_LOAD) {
 285                grlib_gptimer_restart(&unit->timers[id]);
 286            } else if (value & GPTIMER_ENABLE) {
 287                grlib_gptimer_enable(&unit->timers[id]);
 288            }
 289
 290            /* These fields must always be read as 0 */
 291            value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
 292
 293            unit->timers[id].config = value;
 294            return;
 295
 296        default:
 297            break;
 298        }
 299
 300    }
 301
 302    trace_grlib_gptimer_writel(-1, addr, value);
 303}
 304
 305static const MemoryRegionOps grlib_gptimer_ops = {
 306    .read = grlib_gptimer_read,
 307    .write = grlib_gptimer_write,
 308    .endianness = DEVICE_NATIVE_ENDIAN,
 309    .valid = {
 310        .min_access_size = 4,
 311        .max_access_size = 4,
 312    },
 313};
 314
 315static void grlib_gptimer_reset(DeviceState *d)
 316{
 317    GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev);
 318    int          i    = 0;
 319
 320    assert(unit != NULL);
 321
 322    unit->scaler = 0;
 323    unit->reload = 0;
 324    unit->config = 0;
 325
 326    unit->config  = unit->nr_timers;
 327    unit->config |= unit->irq_line << 3;
 328    unit->config |= 1 << 8;     /* separate interrupt */
 329    unit->config |= 1 << 9;     /* Disable timer freeze */
 330
 331
 332    for (i = 0; i < unit->nr_timers; i++) {
 333        GPTimer *timer = &unit->timers[i];
 334
 335        timer->counter = 0;
 336        timer->reload = 0;
 337        timer->config = 0;
 338        ptimer_stop(timer->ptimer);
 339        ptimer_set_count(timer->ptimer, 0);
 340        ptimer_set_freq(timer->ptimer, unit->freq_hz);
 341    }
 342}
 343
 344static int grlib_gptimer_init(SysBusDevice *dev)
 345{
 346    GPTimerUnit  *unit = FROM_SYSBUS(typeof(*unit), dev);
 347    unsigned int  i;
 348
 349    assert(unit->nr_timers > 0);
 350    assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
 351
 352    unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers);
 353
 354    for (i = 0; i < unit->nr_timers; i++) {
 355        GPTimer *timer = &unit->timers[i];
 356
 357        timer->unit   = unit;
 358        timer->bh     = qemu_bh_new(grlib_gptimer_hit, timer);
 359        timer->ptimer = ptimer_init(timer->bh);
 360        timer->id     = i;
 361
 362        /* One IRQ line for each timer */
 363        sysbus_init_irq(dev, &timer->irq);
 364
 365        ptimer_set_freq(timer->ptimer, unit->freq_hz);
 366    }
 367
 368    memory_region_init_io(&unit->iomem, &grlib_gptimer_ops, unit, "gptimer",
 369                          UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers);
 370
 371    sysbus_init_mmio(dev, &unit->iomem);
 372    return 0;
 373}
 374
 375static Property grlib_gptimer_properties[] = {
 376    DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz,   40000000),
 377    DEFINE_PROP_UINT32("irq-line",  GPTimerUnit, irq_line,  8),
 378    DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2),
 379    DEFINE_PROP_END_OF_LIST(),
 380};
 381
 382static void grlib_gptimer_class_init(ObjectClass *klass, void *data)
 383{
 384    DeviceClass *dc = DEVICE_CLASS(klass);
 385    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 386
 387    k->init = grlib_gptimer_init;
 388    dc->reset = grlib_gptimer_reset;
 389    dc->props = grlib_gptimer_properties;
 390}
 391
 392static TypeInfo grlib_gptimer_info = {
 393    .name          = "grlib,gptimer",
 394    .parent        = TYPE_SYS_BUS_DEVICE,
 395    .instance_size = sizeof(GPTimerUnit),
 396    .class_init    = grlib_gptimer_class_init,
 397};
 398
 399static void grlib_gptimer_register_types(void)
 400{
 401    type_register_static(&grlib_gptimer_info);
 402}
 403
 404type_init(grlib_gptimer_register_types)
 405