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