qemu/hw/syborg_rtc.c
<<
>>
Prefs
   1/*
   2 * Syborg RTC
   3 *
   4 * Copyright (c) 2008 CodeSourcery
   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 "syborg.h"
  28
  29enum {
  30    RTC_ID        = 0,
  31    RTC_LATCH     = 1,
  32    RTC_DATA_LOW  = 2,
  33    RTC_DATA_HIGH = 3
  34};
  35
  36typedef struct {
  37    SysBusDevice busdev;
  38    int64_t offset;
  39    int64_t data;
  40    qemu_irq irq;
  41} SyborgRTCState;
  42
  43static uint32_t syborg_rtc_read(void *opaque, target_phys_addr_t offset)
  44{
  45    SyborgRTCState *s = (SyborgRTCState *)opaque;
  46    offset &= 0xfff;
  47    switch (offset >> 2) {
  48    case RTC_ID:
  49        return SYBORG_ID_RTC;
  50    case RTC_DATA_LOW:
  51        return (uint32_t)s->data;
  52    case RTC_DATA_HIGH:
  53        return (uint32_t)(s->data >> 32);
  54    default:
  55        cpu_abort(cpu_single_env, "syborg_rtc_read: Bad offset %x\n",
  56                  (int)offset);
  57        return 0;
  58    }
  59}
  60
  61static void syborg_rtc_write(void *opaque, target_phys_addr_t offset, uint32_t value)
  62{
  63    SyborgRTCState *s = (SyborgRTCState *)opaque;
  64    uint64_t now;
  65
  66    offset &= 0xfff;
  67    switch (offset >> 2) {
  68    case RTC_LATCH:
  69        now = qemu_get_clock(vm_clock);
  70        if (value >= 4) {
  71            s->offset = s->data - now;
  72        } else {
  73            s->data = now + s->offset;
  74            while (value) {
  75                s->data /= 1000;
  76                value--;
  77            }
  78        }
  79        break;
  80    case RTC_DATA_LOW:
  81        s->data = (s->data & ~(uint64_t)0xffffffffu) | value;
  82        break;
  83    case RTC_DATA_HIGH:
  84        s->data = (s->data & 0xffffffffu) | ((uint64_t)value << 32);
  85        break;
  86    default:
  87        cpu_abort(cpu_single_env, "syborg_rtc_write: Bad offset %x\n",
  88                  (int)offset);
  89        break;
  90    }
  91}
  92
  93static CPUReadMemoryFunc * const syborg_rtc_readfn[] = {
  94    syborg_rtc_read,
  95    syborg_rtc_read,
  96    syborg_rtc_read
  97};
  98
  99static CPUWriteMemoryFunc * const syborg_rtc_writefn[] = {
 100    syborg_rtc_write,
 101    syborg_rtc_write,
 102    syborg_rtc_write
 103};
 104
 105static void syborg_rtc_save(QEMUFile *f, void *opaque)
 106{
 107    SyborgRTCState *s = opaque;
 108
 109    qemu_put_be64(f, s->offset);
 110    qemu_put_be64(f, s->data);
 111}
 112
 113static int syborg_rtc_load(QEMUFile *f, void *opaque, int version_id)
 114{
 115    SyborgRTCState *s = opaque;
 116
 117    if (version_id != 1)
 118        return -EINVAL;
 119
 120    s->offset = qemu_get_be64(f);
 121    s->data = qemu_get_be64(f);
 122
 123    return 0;
 124}
 125
 126static int syborg_rtc_init(SysBusDevice *dev)
 127{
 128    SyborgRTCState *s = FROM_SYSBUS(SyborgRTCState, dev);
 129    struct tm tm;
 130    int iomemtype;
 131
 132    iomemtype = cpu_register_io_memory(syborg_rtc_readfn,
 133                                       syborg_rtc_writefn, s,
 134                                       DEVICE_NATIVE_ENDIAN);
 135    sysbus_init_mmio(dev, 0x1000, iomemtype);
 136
 137    qemu_get_timedate(&tm, 0);
 138    s->offset = (uint64_t)mktime(&tm) * 1000000000;
 139
 140    register_savevm(&dev->qdev, "syborg_rtc", -1, 1,
 141                    syborg_rtc_save, syborg_rtc_load, s);
 142    return 0;
 143}
 144
 145static void syborg_rtc_register_devices(void)
 146{
 147    sysbus_register_dev("syborg,rtc", sizeof(SyborgRTCState), syborg_rtc_init);
 148}
 149
 150device_init(syborg_rtc_register_devices)
 151