qemu/hw/timer/etraxfs_timer.c
<<
>>
Prefs
   1/*
   2 * QEMU ETRAX Timers
   3 *
   4 * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
   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 "qemu/osdep.h"
  26#include "hw/sysbus.h"
  27#include "sysemu/reset.h"
  28#include "sysemu/runstate.h"
  29#include "migration/vmstate.h"
  30#include "qemu/module.h"
  31#include "qemu/timer.h"
  32#include "hw/irq.h"
  33#include "hw/ptimer.h"
  34#include "qom/object.h"
  35
  36#define D(x)
  37
  38#define RW_TMR0_DIV   0x00
  39#define R_TMR0_DATA   0x04
  40#define RW_TMR0_CTRL  0x08
  41#define RW_TMR1_DIV   0x10
  42#define R_TMR1_DATA   0x14
  43#define RW_TMR1_CTRL  0x18
  44#define R_TIME        0x38
  45#define RW_WD_CTRL    0x40
  46#define R_WD_STAT     0x44
  47#define RW_INTR_MASK  0x48
  48#define RW_ACK_INTR   0x4c
  49#define R_INTR        0x50
  50#define R_MASKED_INTR 0x54
  51
  52#define TYPE_ETRAX_FS_TIMER "etraxfs-timer"
  53typedef struct ETRAXTimerState ETRAXTimerState;
  54DECLARE_INSTANCE_CHECKER(ETRAXTimerState, ETRAX_TIMER,
  55                         TYPE_ETRAX_FS_TIMER)
  56
  57struct ETRAXTimerState {
  58    SysBusDevice parent_obj;
  59
  60    MemoryRegion mmio;
  61    qemu_irq irq;
  62    qemu_irq nmi;
  63
  64    ptimer_state *ptimer_t0;
  65    ptimer_state *ptimer_t1;
  66    ptimer_state *ptimer_wd;
  67
  68    uint32_t wd_hits;
  69
  70    /* Control registers.  */
  71    uint32_t rw_tmr0_div;
  72    uint32_t r_tmr0_data;
  73    uint32_t rw_tmr0_ctrl;
  74
  75    uint32_t rw_tmr1_div;
  76    uint32_t r_tmr1_data;
  77    uint32_t rw_tmr1_ctrl;
  78
  79    uint32_t rw_wd_ctrl;
  80
  81    uint32_t rw_intr_mask;
  82    uint32_t rw_ack_intr;
  83    uint32_t r_intr;
  84    uint32_t r_masked_intr;
  85};
  86
  87static const VMStateDescription vmstate_etraxfs = {
  88    .name = "etraxfs",
  89    .version_id = 0,
  90    .minimum_version_id = 0,
  91    .fields = (VMStateField[]) {
  92        VMSTATE_PTIMER(ptimer_t0, ETRAXTimerState),
  93        VMSTATE_PTIMER(ptimer_t1, ETRAXTimerState),
  94        VMSTATE_PTIMER(ptimer_wd, ETRAXTimerState),
  95
  96        VMSTATE_UINT32(wd_hits, ETRAXTimerState),
  97
  98        VMSTATE_UINT32(rw_tmr0_div, ETRAXTimerState),
  99        VMSTATE_UINT32(r_tmr0_data, ETRAXTimerState),
 100        VMSTATE_UINT32(rw_tmr0_ctrl, ETRAXTimerState),
 101
 102        VMSTATE_UINT32(rw_tmr1_div, ETRAXTimerState),
 103        VMSTATE_UINT32(r_tmr1_data, ETRAXTimerState),
 104        VMSTATE_UINT32(rw_tmr1_ctrl, ETRAXTimerState),
 105
 106        VMSTATE_UINT32(rw_wd_ctrl, ETRAXTimerState),
 107
 108        VMSTATE_UINT32(rw_intr_mask, ETRAXTimerState),
 109        VMSTATE_UINT32(rw_ack_intr, ETRAXTimerState),
 110        VMSTATE_UINT32(r_intr, ETRAXTimerState),
 111        VMSTATE_UINT32(r_masked_intr, ETRAXTimerState),
 112
 113        VMSTATE_END_OF_LIST()
 114    }
 115};
 116
 117static uint64_t
 118timer_read(void *opaque, hwaddr addr, unsigned int size)
 119{
 120    ETRAXTimerState *t = opaque;
 121    uint32_t r = 0;
 122
 123    switch (addr) {
 124    case R_TMR0_DATA:
 125        r = ptimer_get_count(t->ptimer_t0);
 126        break;
 127    case R_TMR1_DATA:
 128        r = ptimer_get_count(t->ptimer_t1);
 129        break;
 130    case R_TIME:
 131        r = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 10;
 132        break;
 133    case RW_INTR_MASK:
 134        r = t->rw_intr_mask;
 135        break;
 136    case R_MASKED_INTR:
 137        r = t->r_intr & t->rw_intr_mask;
 138        break;
 139    default:
 140        D(printf ("%s %x\n", __func__, addr));
 141        break;
 142    }
 143    return r;
 144}
 145
 146static void update_ctrl(ETRAXTimerState *t, int tnum)
 147{
 148    unsigned int op;
 149    unsigned int freq;
 150    unsigned int freq_hz;
 151    unsigned int div;
 152    uint32_t ctrl;
 153
 154    ptimer_state *timer;
 155
 156    if (tnum == 0) {
 157        ctrl = t->rw_tmr0_ctrl;
 158        div = t->rw_tmr0_div;
 159        timer = t->ptimer_t0;
 160    } else {
 161        ctrl = t->rw_tmr1_ctrl;
 162        div = t->rw_tmr1_div;
 163        timer = t->ptimer_t1;
 164    }
 165
 166
 167    op = ctrl & 3;
 168    freq = ctrl >> 2;
 169    freq_hz = 32000000;
 170
 171    switch (freq)
 172    {
 173    case 0:
 174    case 1:
 175        D(printf ("extern or disabled timer clock?\n"));
 176        break;
 177    case 4: freq_hz =  29493000; break;
 178    case 5: freq_hz =  32000000; break;
 179    case 6: freq_hz =  32768000; break;
 180    case 7: freq_hz = 100000000; break;
 181    default:
 182        abort();
 183        break;
 184    }
 185
 186    D(printf ("freq_hz=%d div=%d\n", freq_hz, div));
 187    ptimer_transaction_begin(timer);
 188    ptimer_set_freq(timer, freq_hz);
 189    ptimer_set_limit(timer, div, 0);
 190
 191    switch (op)
 192    {
 193        case 0:
 194            /* Load.  */
 195            ptimer_set_limit(timer, div, 1);
 196            break;
 197        case 1:
 198            /* Hold.  */
 199            ptimer_stop(timer);
 200            break;
 201        case 2:
 202            /* Run.  */
 203            ptimer_run(timer, 0);
 204            break;
 205        default:
 206            abort();
 207            break;
 208    }
 209    ptimer_transaction_commit(timer);
 210}
 211
 212static void timer_update_irq(ETRAXTimerState *t)
 213{
 214    t->r_intr &= ~(t->rw_ack_intr);
 215    t->r_masked_intr = t->r_intr & t->rw_intr_mask;
 216
 217    D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr));
 218    qemu_set_irq(t->irq, !!t->r_masked_intr);
 219}
 220
 221static void timer0_hit(void *opaque)
 222{
 223    ETRAXTimerState *t = opaque;
 224    t->r_intr |= 1;
 225    timer_update_irq(t);
 226}
 227
 228static void timer1_hit(void *opaque)
 229{
 230    ETRAXTimerState *t = opaque;
 231    t->r_intr |= 2;
 232    timer_update_irq(t);
 233}
 234
 235static void watchdog_hit(void *opaque)
 236{
 237    ETRAXTimerState *t = opaque;
 238    if (t->wd_hits == 0) {
 239        /* real hw gives a single tick before reseting but we are
 240           a bit friendlier to compensate for our slower execution.  */
 241        ptimer_set_count(t->ptimer_wd, 10);
 242        ptimer_run(t->ptimer_wd, 1);
 243        qemu_irq_raise(t->nmi);
 244    }
 245    else
 246        qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
 247
 248    t->wd_hits++;
 249}
 250
 251static inline void timer_watchdog_update(ETRAXTimerState *t, uint32_t value)
 252{
 253    unsigned int wd_en = t->rw_wd_ctrl & (1 << 8);
 254    unsigned int wd_key = t->rw_wd_ctrl >> 9;
 255    unsigned int wd_cnt = t->rw_wd_ctrl & 511;
 256    unsigned int new_key = value >> 9 & ((1 << 7) - 1);
 257    unsigned int new_cmd = (value >> 8) & 1;
 258
 259    /* If the watchdog is enabled, they written key must match the
 260       complement of the previous.  */
 261    wd_key = ~wd_key & ((1 << 7) - 1);
 262
 263    if (wd_en && wd_key != new_key)
 264        return;
 265
 266    D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n", 
 267         wd_en, new_key, wd_key, new_cmd, wd_cnt));
 268
 269    if (t->wd_hits)
 270        qemu_irq_lower(t->nmi);
 271
 272    t->wd_hits = 0;
 273
 274    ptimer_transaction_begin(t->ptimer_wd);
 275    ptimer_set_freq(t->ptimer_wd, 760);
 276    if (wd_cnt == 0)
 277        wd_cnt = 256;
 278    ptimer_set_count(t->ptimer_wd, wd_cnt);
 279    if (new_cmd)
 280        ptimer_run(t->ptimer_wd, 1);
 281    else
 282        ptimer_stop(t->ptimer_wd);
 283
 284    t->rw_wd_ctrl = value;
 285    ptimer_transaction_commit(t->ptimer_wd);
 286}
 287
 288static void
 289timer_write(void *opaque, hwaddr addr,
 290            uint64_t val64, unsigned int size)
 291{
 292    ETRAXTimerState *t = opaque;
 293    uint32_t value = val64;
 294
 295    switch (addr)
 296    {
 297        case RW_TMR0_DIV:
 298            t->rw_tmr0_div = value;
 299            break;
 300        case RW_TMR0_CTRL:
 301            D(printf ("RW_TMR0_CTRL=%x\n", value));
 302            t->rw_tmr0_ctrl = value;
 303            update_ctrl(t, 0);
 304            break;
 305        case RW_TMR1_DIV:
 306            t->rw_tmr1_div = value;
 307            break;
 308        case RW_TMR1_CTRL:
 309            D(printf ("RW_TMR1_CTRL=%x\n", value));
 310            t->rw_tmr1_ctrl = value;
 311            update_ctrl(t, 1);
 312            break;
 313        case RW_INTR_MASK:
 314            D(printf ("RW_INTR_MASK=%x\n", value));
 315            t->rw_intr_mask = value;
 316            timer_update_irq(t);
 317            break;
 318        case RW_WD_CTRL:
 319            timer_watchdog_update(t, value);
 320            break;
 321        case RW_ACK_INTR:
 322            t->rw_ack_intr = value;
 323            timer_update_irq(t);
 324            t->rw_ack_intr = 0;
 325            break;
 326        default:
 327            printf ("%s " TARGET_FMT_plx " %x\n",
 328                __func__, addr, value);
 329            break;
 330    }
 331}
 332
 333static const MemoryRegionOps timer_ops = {
 334    .read = timer_read,
 335    .write = timer_write,
 336    .endianness = DEVICE_LITTLE_ENDIAN,
 337    .valid = {
 338        .min_access_size = 4,
 339        .max_access_size = 4
 340    }
 341};
 342
 343static void etraxfs_timer_reset_enter(Object *obj, ResetType type)
 344{
 345    ETRAXTimerState *t = ETRAX_TIMER(obj);
 346
 347    ptimer_transaction_begin(t->ptimer_t0);
 348    ptimer_stop(t->ptimer_t0);
 349    ptimer_transaction_commit(t->ptimer_t0);
 350    ptimer_transaction_begin(t->ptimer_t1);
 351    ptimer_stop(t->ptimer_t1);
 352    ptimer_transaction_commit(t->ptimer_t1);
 353    ptimer_transaction_begin(t->ptimer_wd);
 354    ptimer_stop(t->ptimer_wd);
 355    ptimer_transaction_commit(t->ptimer_wd);
 356    t->rw_wd_ctrl = 0;
 357    t->r_intr = 0;
 358    t->rw_intr_mask = 0;
 359}
 360
 361static void etraxfs_timer_reset_hold(Object *obj)
 362{
 363    ETRAXTimerState *t = ETRAX_TIMER(obj);
 364
 365    qemu_irq_lower(t->irq);
 366}
 367
 368static void etraxfs_timer_realize(DeviceState *dev, Error **errp)
 369{
 370    ETRAXTimerState *t = ETRAX_TIMER(dev);
 371    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 372
 373    t->ptimer_t0 = ptimer_init(timer0_hit, t, PTIMER_POLICY_DEFAULT);
 374    t->ptimer_t1 = ptimer_init(timer1_hit, t, PTIMER_POLICY_DEFAULT);
 375    t->ptimer_wd = ptimer_init(watchdog_hit, t, PTIMER_POLICY_DEFAULT);
 376
 377    sysbus_init_irq(sbd, &t->irq);
 378    sysbus_init_irq(sbd, &t->nmi);
 379
 380    memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t,
 381                          "etraxfs-timer", 0x5c);
 382    sysbus_init_mmio(sbd, &t->mmio);
 383}
 384
 385static void etraxfs_timer_class_init(ObjectClass *klass, void *data)
 386{
 387    DeviceClass *dc = DEVICE_CLASS(klass);
 388    ResettableClass *rc = RESETTABLE_CLASS(klass);
 389
 390    dc->realize = etraxfs_timer_realize;
 391    dc->vmsd = &vmstate_etraxfs;
 392    rc->phases.enter = etraxfs_timer_reset_enter;
 393    rc->phases.hold = etraxfs_timer_reset_hold;
 394}
 395
 396static const TypeInfo etraxfs_timer_info = {
 397    .name          = TYPE_ETRAX_FS_TIMER,
 398    .parent        = TYPE_SYS_BUS_DEVICE,
 399    .instance_size = sizeof(ETRAXTimerState),
 400    .class_init    = etraxfs_timer_class_init,
 401};
 402
 403static void etraxfs_timer_register_types(void)
 404{
 405    type_register_static(&etraxfs_timer_info);
 406}
 407
 408type_init(etraxfs_timer_register_types)
 409