qemu/hw/pl031.c
<<
>>
Prefs
   1/*
   2 * ARM AMBA PrimeCell PL031 RTC
   3 *
   4 * Copyright (c) 2007 CodeSourcery
   5 *
   6 * This file is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 */
  11
  12#include "sysbus.h"
  13#include "qemu-timer.h"
  14
  15//#define DEBUG_PL031
  16
  17#ifdef DEBUG_PL031
  18#define DPRINTF(fmt, ...) \
  19do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0)
  20#else
  21#define DPRINTF(fmt, ...) do {} while(0)
  22#endif
  23
  24#define RTC_DR      0x00    /* Data read register */
  25#define RTC_MR      0x04    /* Match register */
  26#define RTC_LR      0x08    /* Data load register */
  27#define RTC_CR      0x0c    /* Control register */
  28#define RTC_IMSC    0x10    /* Interrupt mask and set register */
  29#define RTC_RIS     0x14    /* Raw interrupt status register */
  30#define RTC_MIS     0x18    /* Masked interrupt status register */
  31#define RTC_ICR     0x1c    /* Interrupt clear register */
  32
  33typedef struct {
  34    SysBusDevice busdev;
  35    QEMUTimer *timer;
  36    qemu_irq irq;
  37
  38    uint32_t tick_offset;
  39
  40    uint32_t mr;
  41    uint32_t lr;
  42    uint32_t cr;
  43    uint32_t im;
  44    uint32_t is;
  45} pl031_state;
  46
  47static const VMStateDescription vmstate_pl031 = {
  48    .name = "pl031",
  49    .version_id = 1,
  50    .minimum_version_id = 1,
  51    .fields = (VMStateField[]) {
  52        VMSTATE_UINT32(tick_offset, pl031_state),
  53        VMSTATE_UINT32(mr, pl031_state),
  54        VMSTATE_UINT32(lr, pl031_state),
  55        VMSTATE_UINT32(cr, pl031_state),
  56        VMSTATE_UINT32(im, pl031_state),
  57        VMSTATE_UINT32(is, pl031_state),
  58        VMSTATE_END_OF_LIST()
  59    }
  60};
  61
  62static const unsigned char pl031_id[] = {
  63    0x31, 0x10, 0x14, 0x00,         /* Device ID        */
  64    0x0d, 0xf0, 0x05, 0xb1          /* Cell ID      */
  65};
  66
  67static void pl031_update(pl031_state *s)
  68{
  69    qemu_set_irq(s->irq, s->is & s->im);
  70}
  71
  72static void pl031_interrupt(void * opaque)
  73{
  74    pl031_state *s = (pl031_state *)opaque;
  75
  76    s->im = 1;
  77    DPRINTF("Alarm raised\n");
  78    pl031_update(s);
  79}
  80
  81static uint32_t pl031_get_count(pl031_state *s)
  82{
  83    /* This assumes qemu_get_clock_ns returns the time since the machine was
  84       created.  */
  85    return s->tick_offset + qemu_get_clock_ns(vm_clock) / get_ticks_per_sec();
  86}
  87
  88static void pl031_set_alarm(pl031_state *s)
  89{
  90    int64_t now;
  91    uint32_t ticks;
  92
  93    now = qemu_get_clock_ns(vm_clock);
  94    ticks = s->tick_offset + now / get_ticks_per_sec();
  95
  96    /* The timer wraps around.  This subtraction also wraps in the same way,
  97       and gives correct results when alarm < now_ticks.  */
  98    ticks = s->mr - ticks;
  99    DPRINTF("Alarm set in %ud ticks\n", ticks);
 100    if (ticks == 0) {
 101        qemu_del_timer(s->timer);
 102        pl031_interrupt(s);
 103    } else {
 104        qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
 105    }
 106}
 107
 108static uint32_t pl031_read(void *opaque, target_phys_addr_t offset)
 109{
 110    pl031_state *s = (pl031_state *)opaque;
 111
 112    if (offset >= 0xfe0  &&  offset < 0x1000)
 113        return pl031_id[(offset - 0xfe0) >> 2];
 114
 115    switch (offset) {
 116    case RTC_DR:
 117        return pl031_get_count(s);
 118    case RTC_MR:
 119        return s->mr;
 120    case RTC_IMSC:
 121        return s->im;
 122    case RTC_RIS:
 123        return s->is;
 124    case RTC_LR:
 125        return s->lr;
 126    case RTC_CR:
 127        /* RTC is permanently enabled.  */
 128        return 1;
 129    case RTC_MIS:
 130        return s->is & s->im;
 131    case RTC_ICR:
 132        fprintf(stderr, "qemu: pl031_read: Unexpected offset 0x%x\n",
 133                (int)offset);
 134        break;
 135    default:
 136        hw_error("pl031_read: Bad offset 0x%x\n", (int)offset);
 137        break;
 138    }
 139
 140    return 0;
 141}
 142
 143static void pl031_write(void * opaque, target_phys_addr_t offset,
 144                        uint32_t value)
 145{
 146    pl031_state *s = (pl031_state *)opaque;
 147
 148
 149    switch (offset) {
 150    case RTC_LR:
 151        s->tick_offset += value - pl031_get_count(s);
 152        pl031_set_alarm(s);
 153        break;
 154    case RTC_MR:
 155        s->mr = value;
 156        pl031_set_alarm(s);
 157        break;
 158    case RTC_IMSC:
 159        s->im = value & 1;
 160        DPRINTF("Interrupt mask %d\n", s->im);
 161        pl031_update(s);
 162        break;
 163    case RTC_ICR:
 164        /* The PL031 documentation (DDI0224B) states that the interrupt is
 165           cleared when bit 0 of the written value is set.  However the
 166           arm926e documentation (DDI0287B) states that the interrupt is
 167           cleared when any value is written.  */
 168        DPRINTF("Interrupt cleared");
 169        s->is = 0;
 170        pl031_update(s);
 171        break;
 172    case RTC_CR:
 173        /* Written value is ignored.  */
 174        break;
 175
 176    case RTC_DR:
 177    case RTC_MIS:
 178    case RTC_RIS:
 179        fprintf(stderr, "qemu: pl031_write: Unexpected offset 0x%x\n",
 180                (int)offset);
 181        break;
 182
 183    default:
 184        hw_error("pl031_write: Bad offset 0x%x\n", (int)offset);
 185        break;
 186    }
 187}
 188
 189static CPUWriteMemoryFunc * const  pl031_writefn[] = {
 190    pl031_write,
 191    pl031_write,
 192    pl031_write
 193};
 194
 195static CPUReadMemoryFunc * const  pl031_readfn[] = {
 196    pl031_read,
 197    pl031_read,
 198    pl031_read
 199};
 200
 201static int pl031_init(SysBusDevice *dev)
 202{
 203    int iomemtype;
 204    pl031_state *s = FROM_SYSBUS(pl031_state, dev);
 205    struct tm tm;
 206
 207    iomemtype = cpu_register_io_memory(pl031_readfn, pl031_writefn, s,
 208                                       DEVICE_NATIVE_ENDIAN);
 209    if (iomemtype == -1) {
 210        hw_error("pl031_init: Can't register I/O memory\n");
 211    }
 212
 213    sysbus_init_mmio(dev, 0x1000, iomemtype);
 214
 215    sysbus_init_irq(dev, &s->irq);
 216    /* ??? We assume vm_clock is zero at this point.  */
 217    qemu_get_timedate(&tm, 0);
 218    s->tick_offset = mktimegm(&tm);
 219
 220    s->timer = qemu_new_timer_ns(vm_clock, pl031_interrupt, s);
 221    return 0;
 222}
 223
 224static SysBusDeviceInfo pl031_info = {
 225    .init = pl031_init,
 226    .qdev.name = "pl031",
 227    .qdev.size = sizeof(pl031_state),
 228    .qdev.vmsd = &vmstate_pl031,
 229    .qdev.no_user = 1,
 230};
 231
 232static void pl031_register_devices(void)
 233{
 234    sysbus_register_withprop(&pl031_info);
 235}
 236
 237device_init(pl031_register_devices)
 238