| /* |
| * Syborg RTC |
| * |
| * Copyright (c) 2008 CodeSourcery |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| |
| #include "sysbus.h" |
| #include "qemu-timer.h" |
| #include "syborg.h" |
| |
| enum { |
| RTC_ID = 0, |
| RTC_LATCH = 1, |
| RTC_DATA_LOW = 2, |
| RTC_DATA_HIGH = 3 |
| }; |
| |
| typedef struct { |
| SysBusDevice busdev; |
| int64_t offset; |
| int64_t data; |
| qemu_irq irq; |
| } SyborgRTCState; |
| |
| static uint32_t syborg_rtc_read(void *opaque, target_phys_addr_t offset) |
| { |
| SyborgRTCState *s = (SyborgRTCState *)opaque; |
| offset &= 0xfff; |
| switch (offset >> 2) { |
| case RTC_ID: |
| return SYBORG_ID_RTC; |
| case RTC_DATA_LOW: |
| return (uint32_t)s->data; |
| case RTC_DATA_HIGH: |
| return (uint32_t)(s->data >> 32); |
| default: |
| cpu_abort(cpu_single_env, "syborg_rtc_read: Bad offset %x\n", |
| (int)offset); |
| return 0; |
| } |
| } |
| |
| static void syborg_rtc_write(void *opaque, target_phys_addr_t offset, uint32_t value) |
| { |
| SyborgRTCState *s = (SyborgRTCState *)opaque; |
| uint64_t now; |
| |
| offset &= 0xfff; |
| switch (offset >> 2) { |
| case RTC_LATCH: |
| now = qemu_get_clock(vm_clock); |
| if (value >= 4) { |
| s->offset = s->data - now; |
| } else { |
| s->data = now + s->offset; |
| while (value) { |
| s->data /= 1000; |
| value--; |
| } |
| } |
| break; |
| case RTC_DATA_LOW: |
| s->data = (s->data & ~(uint64_t)0xffffffffu) | value; |
| break; |
| case RTC_DATA_HIGH: |
| s->data = (s->data & 0xffffffffu) | ((uint64_t)value << 32); |
| break; |
| default: |
| cpu_abort(cpu_single_env, "syborg_rtc_write: Bad offset %x\n", |
| (int)offset); |
| break; |
| } |
| } |
| |
| static CPUReadMemoryFunc * const syborg_rtc_readfn[] = { |
| syborg_rtc_read, |
| syborg_rtc_read, |
| syborg_rtc_read |
| }; |
| |
| static CPUWriteMemoryFunc * const syborg_rtc_writefn[] = { |
| syborg_rtc_write, |
| syborg_rtc_write, |
| syborg_rtc_write |
| }; |
| |
| static void syborg_rtc_save(QEMUFile *f, void *opaque) |
| { |
| SyborgRTCState *s = opaque; |
| |
| qemu_put_be64(f, s->offset); |
| qemu_put_be64(f, s->data); |
| } |
| |
| static int syborg_rtc_load(QEMUFile *f, void *opaque, int version_id) |
| { |
| SyborgRTCState *s = opaque; |
| |
| if (version_id != 1) |
| return -EINVAL; |
| |
| s->offset = qemu_get_be64(f); |
| s->data = qemu_get_be64(f); |
| |
| return 0; |
| } |
| |
| static int syborg_rtc_init(SysBusDevice *dev) |
| { |
| SyborgRTCState *s = FROM_SYSBUS(SyborgRTCState, dev); |
| struct tm tm; |
| int iomemtype; |
| |
| iomemtype = cpu_register_io_memory(syborg_rtc_readfn, |
| syborg_rtc_writefn, s, |
| DEVICE_NATIVE_ENDIAN); |
| sysbus_init_mmio(dev, 0x1000, iomemtype); |
| |
| qemu_get_timedate(&tm, 0); |
| s->offset = (uint64_t)mktime(&tm) * 1000000000; |
| |
| register_savevm(&dev->qdev, "syborg_rtc", -1, 1, |
| syborg_rtc_save, syborg_rtc_load, s); |
| return 0; |
| } |
| |
| static void syborg_rtc_register_devices(void) |
| { |
| sysbus_register_dev("syborg,rtc", sizeof(SyborgRTCState), syborg_rtc_init); |
| } |
| |
| device_init(syborg_rtc_register_devices) |