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