From 877c181cd41e024ba1d076f96efe3c5777938846 Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Fri, 4 Oct 2019 01:03:52 +0200 Subject: hw: Move PL031 device from hw/timer/ to hw/rtc/ subdirectory The PL031 is a Real Time Clock, not a timer. Move it under the hw/rtc/ subdirectory. Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Acked-by: Peter Maydell Message-Id: <20191003230404.19384-3-philmd@redhat.com> Signed-off-by: Laurent Vivier --- include/hw/rtc/pl031.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ include/hw/timer/pl031.h | 46 ---------------------------------------------- 2 files changed, 47 insertions(+), 46 deletions(-) create mode 100644 include/hw/rtc/pl031.h delete mode 100644 include/hw/timer/pl031.h (limited to 'include') diff --git a/include/hw/rtc/pl031.h b/include/hw/rtc/pl031.h new file mode 100644 index 0000000000..e3cb1d646f --- /dev/null +++ b/include/hw/rtc/pl031.h @@ -0,0 +1,47 @@ +/* + * ARM AMBA PrimeCell PL031 RTC + * + * Copyright (c) 2007 CodeSourcery + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef HW_RTC_PL031_H +#define HW_RTC_PL031_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" + +#define TYPE_PL031 "pl031" +#define PL031(obj) OBJECT_CHECK(PL031State, (obj), TYPE_PL031) + +typedef struct PL031State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + QEMUTimer *timer; + qemu_irq irq; + + /* + * Needed to preserve the tick_count across migration, even if the + * absolute value of the rtc_clock is different on the source and + * destination. + */ + uint32_t tick_offset_vmstate; + uint32_t tick_offset; + bool tick_offset_migrated; + bool migrate_tick_offset; + + uint32_t mr; + uint32_t lr; + uint32_t cr; + uint32_t im; + uint32_t is; +} PL031State; + +#endif diff --git a/include/hw/timer/pl031.h b/include/hw/timer/pl031.h deleted file mode 100644 index 8c3f555ee2..0000000000 --- a/include/hw/timer/pl031.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * ARM AMBA PrimeCell PL031 RTC - * - * Copyright (c) 2007 CodeSourcery - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#ifndef HW_TIMER_PL031_H -#define HW_TIMER_PL031_H - -#include "hw/sysbus.h" - -#define TYPE_PL031 "pl031" -#define PL031(obj) OBJECT_CHECK(PL031State, (obj), TYPE_PL031) - -typedef struct PL031State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - QEMUTimer *timer; - qemu_irq irq; - - /* - * Needed to preserve the tick_count across migration, even if the - * absolute value of the rtc_clock is different on the source and - * destination. - */ - uint32_t tick_offset_vmstate; - uint32_t tick_offset; - bool tick_offset_migrated; - bool migrate_tick_offset; - - uint32_t mr; - uint32_t lr; - uint32_t cr; - uint32_t im; - uint32_t is; -} PL031State; - -#endif -- cgit v1.2.3-55-g7522 From bcdb90640ae41128e0c2ae2ec8ebf5a832102097 Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Fri, 4 Oct 2019 01:03:53 +0200 Subject: hw: Move MC146818 device from hw/timer/ to hw/rtc/ subdirectory The MC146818 is a Real Time Clock, not a timer. Move it under the hw/rtc/ subdirectory. Use copyright statement from 80cabfad163 for "hw/rtc/mc146818rtc.h". Reviewed-by: Alistair Francis Acked-by: David Gibson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191003230404.19384-4-philmd@redhat.com> Signed-off-by: Laurent Vivier --- MAINTAINERS | 4 +- hw/alpha/dp264.c | 2 +- hw/hppa/machine.c | 2 +- hw/i386/acpi-build.c | 2 +- hw/i386/pc.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/mips/mips_fulong2e.c | 2 +- hw/mips/mips_jazz.c | 2 +- hw/mips/mips_malta.c | 2 +- hw/mips/mips_r4k.c | 2 +- hw/ppc/pnv.c | 2 +- hw/ppc/prep.c | 2 +- hw/rtc/Kconfig | 3 + hw/rtc/Makefile.objs | 1 + hw/rtc/mc146818rtc.c | 1063 +++++++++++++++++++++++++++++++++++ hw/timer/Kconfig | 3 - hw/timer/Makefile.objs | 2 - hw/timer/hpet.c | 2 +- hw/timer/mc146818rtc.c | 1063 ----------------------------------- include/hw/rtc/mc146818rtc.h | 22 + include/hw/rtc/mc146818rtc_regs.h | 91 +++ include/hw/timer/mc146818rtc.h | 14 - include/hw/timer/mc146818rtc_regs.h | 90 --- tests/rtc-test.c | 2 +- 24 files changed, 1195 insertions(+), 1187 deletions(-) create mode 100644 hw/rtc/mc146818rtc.c delete mode 100644 hw/timer/mc146818rtc.c create mode 100644 include/hw/rtc/mc146818rtc.h create mode 100644 include/hw/rtc/mc146818rtc_regs.h delete mode 100644 include/hw/timer/mc146818rtc.h delete mode 100644 include/hw/timer/mc146818rtc_regs.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 2e13ba4628..7eba146444 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1261,7 +1261,7 @@ F: hw/misc/debugexit.c F: hw/misc/pc-testdev.c F: hw/timer/hpet* F: hw/timer/i8254* -F: hw/timer/mc146818rtc* +F: hw/rtc/mc146818rtc* F: hw/watchdog/wdt_ib700.c F: hw/watchdog/wdt_i6300esb.c F: include/hw/display/vga.h @@ -1273,7 +1273,7 @@ F: include/hw/isa/i8259_internal.h F: include/hw/isa/superio.h F: include/hw/timer/hpet.h F: include/hw/timer/i8254* -F: include/hw/timer/mc146818rtc* +F: include/hw/rtc/mc146818rtc* Machine core M: Eduardo Habkost diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 51feee8558..51b3cf7a61 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -14,7 +14,7 @@ #include "alpha_sys.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/isa/superio.h" diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 953d454f48..b30aba6d54 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -12,7 +12,7 @@ #include "qemu/error-report.h" #include "sysemu/reset.h" #include "sysemu/sysemu.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/char/serial.h" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1d077a7cb7..d9435ba0b3 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -45,7 +45,7 @@ #include "hw/acpi/vmgenid.h" #include "hw/boards.h" #include "sysemu/tpm_backend.h" -#include "hw/timer/mc146818rtc_regs.h" +#include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "hw/mem/memory-device.h" #include "sysemu/numa.h" diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 4b1904237e..51b72439b4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -42,7 +42,7 @@ #include "elf.h" #include "migration/vmstate.h" #include "multiboot.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/dma/i8257.h" #include "hw/timer/i8254.h" #include "hw/input/i8042.h" diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 8fad20f314..748fc2ee15 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -33,7 +33,7 @@ #include "hw/loader.h" #include "sysemu/arch_init.h" #include "hw/i2c/smbus_eeprom.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/xen/xen.h" #include "sysemu/kvm.h" #include "kvm_i386.h" diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index cf537dd7e6..03a27e1767 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -39,7 +39,7 @@ #include "hw/ide.h" #include "elf.h" #include "hw/isa/vt82c686.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/timer/i8254.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 8d010a0b6e..d978bb64a0 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -39,7 +39,7 @@ #include "hw/scsi/esp.h" #include "hw/mips/bios.h" #include "hw/loader.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/timer/i8254.h" #include "hw/display/vga.h" #include "hw/audio/pcspk.h" diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 4d9c64b36a..c1c8810e71 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -45,7 +45,7 @@ #include "hw/irq.h" #include "hw/loader.h" #include "elf.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/timer/i8254.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" /* SysBusDevice */ diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index bc0be26544..70024235ae 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -28,7 +28,7 @@ #include "hw/ide.h" #include "hw/loader.h" #include "elf.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/input/i8042.h" #include "hw/timer/i8254.h" #include "exec/address-spaces.h" diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 4a51fb65a8..60632720ef 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -48,7 +48,7 @@ #include "hw/isa/isa.h" #include "hw/boards.h" #include "hw/char/serial.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 4f3c6bf190..3a51536e1a 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -40,7 +40,7 @@ #include "hw/ide.h" #include "hw/irq.h" #include "hw/loader.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "hw/isa/pc87312.h" #include "hw/net/ne2000-isa.h" #include "sysemu/arch_init.h" diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index 8a4383bca9..7ffd702268 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -1,2 +1,5 @@ config PL031 bool + +config MC146818RTC + bool diff --git a/hw/rtc/Makefile.objs b/hw/rtc/Makefile.objs index 3e1eb42563..3cac0d5a63 100644 --- a/hw/rtc/Makefile.objs +++ b/hw/rtc/Makefile.objs @@ -1 +1,2 @@ common-obj-$(CONFIG_PL031) += pl031.o +obj-$(CONFIG_MC146818RTC) += mc146818rtc.o diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c new file mode 100644 index 0000000000..ced15f764f --- /dev/null +++ b/hw/rtc/mc146818rtc.c @@ -0,0 +1,1063 @@ +/* + * QEMU MC146818 RTC emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * 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 "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/module.h" +#include "qemu/bcd.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysemu/replay.h" +#include "sysemu/reset.h" +#include "sysemu/runstate.h" +#include "hw/rtc/mc146818rtc.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qapi-events-misc-target.h" +#include "qapi/visitor.h" +#include "exec/address-spaces.h" + +#ifdef TARGET_I386 +#include "hw/i386/apic.h" +#endif + +//#define DEBUG_CMOS +//#define DEBUG_COALESCED + +#ifdef DEBUG_CMOS +# define CMOS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define CMOS_DPRINTF(format, ...) do { } while (0) +#endif + +#ifdef DEBUG_COALESCED +# define DPRINTF_C(format, ...) printf(format, ## __VA_ARGS__) +#else +# define DPRINTF_C(format, ...) do { } while (0) +#endif + +#define SEC_PER_MIN 60 +#define MIN_PER_HOUR 60 +#define SEC_PER_HOUR 3600 +#define HOUR_PER_DAY 24 +#define SEC_PER_DAY 86400 + +#define RTC_REINJECT_ON_ACK_COUNT 20 +#define RTC_CLOCK_RATE 32768 +#define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) + +#define MC146818_RTC(obj) OBJECT_CHECK(RTCState, (obj), TYPE_MC146818_RTC) + +typedef struct RTCState { + ISADevice parent_obj; + + MemoryRegion io; + MemoryRegion coalesced_io; + uint8_t cmos_data[128]; + uint8_t cmos_index; + int32_t base_year; + uint64_t base_rtc; + uint64_t last_update; + int64_t offset; + qemu_irq irq; + int it_shift; + /* periodic timer */ + QEMUTimer *periodic_timer; + int64_t next_periodic_time; + /* update-ended timer */ + QEMUTimer *update_timer; + uint64_t next_alarm_time; + uint16_t irq_reinject_on_ack_count; + uint32_t irq_coalesced; + uint32_t period; + QEMUTimer *coalesced_timer; + LostTickPolicy lost_tick_policy; + Notifier suspend_notifier; + QLIST_ENTRY(RTCState) link; +} RTCState; + +static void rtc_set_time(RTCState *s); +static void rtc_update_time(RTCState *s); +static void rtc_set_cmos(RTCState *s, const struct tm *tm); +static inline int rtc_from_bcd(RTCState *s, int a); +static uint64_t get_next_alarm(RTCState *s); + +static inline bool rtc_running(RTCState *s) +{ + return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) && + (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20); +} + +static uint64_t get_guest_rtc_ns(RTCState *s) +{ + uint64_t guest_clock = qemu_clock_get_ns(rtc_clock); + + return s->base_rtc * NANOSECONDS_PER_SECOND + + guest_clock - s->last_update + s->offset; +} + +static void rtc_coalesced_timer_update(RTCState *s) +{ + if (s->irq_coalesced == 0) { + timer_del(s->coalesced_timer); + } else { + /* divide each RTC interval to 2 - 8 smaller intervals */ + int c = MIN(s->irq_coalesced, 7) + 1; + int64_t next_clock = qemu_clock_get_ns(rtc_clock) + + periodic_clock_to_ns(s->period / c); + timer_mod(s->coalesced_timer, next_clock); + } +} + +static QLIST_HEAD(, RTCState) rtc_devices = + QLIST_HEAD_INITIALIZER(rtc_devices); + +#ifdef TARGET_I386 +void qmp_rtc_reset_reinjection(Error **errp) +{ + RTCState *s; + + QLIST_FOREACH(s, &rtc_devices, link) { + s->irq_coalesced = 0; + } +} + +static bool rtc_policy_slew_deliver_irq(RTCState *s) +{ + apic_reset_irq_delivered(); + qemu_irq_raise(s->irq); + return apic_get_irq_delivered(); +} + +static void rtc_coalesced_timer(void *opaque) +{ + RTCState *s = opaque; + + if (s->irq_coalesced != 0) { + s->cmos_data[RTC_REG_C] |= 0xc0; + DPRINTF_C("cmos: injecting from timer\n"); + if (rtc_policy_slew_deliver_irq(s)) { + s->irq_coalesced--; + DPRINTF_C("cmos: coalesced irqs decreased to %d\n", + s->irq_coalesced); + } + } + + rtc_coalesced_timer_update(s); +} +#else +static bool rtc_policy_slew_deliver_irq(RTCState *s) +{ + assert(0); + return false; +} +#endif + +static uint32_t rtc_periodic_clock_ticks(RTCState *s) +{ + int period_code; + + if (!(s->cmos_data[RTC_REG_B] & REG_B_PIE)) { + return 0; + } + + period_code = s->cmos_data[RTC_REG_A] & 0x0f; + + return periodic_period_to_clock(period_code); +} + +/* + * handle periodic timer. @old_period indicates the periodic timer update + * is just due to period adjustment. + */ +static void +periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period) +{ + uint32_t period; + int64_t cur_clock, next_irq_clock, lost_clock = 0; + + period = rtc_periodic_clock_ticks(s); + + if (period) { + /* compute 32 khz clock */ + cur_clock = + muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); + + /* + * if the periodic timer's update is due to period re-configuration, + * we should count the clock since last interrupt. + */ + if (old_period) { + int64_t last_periodic_clock, next_periodic_clock; + + next_periodic_clock = muldiv64(s->next_periodic_time, + RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); + last_periodic_clock = next_periodic_clock - old_period; + lost_clock = cur_clock - last_periodic_clock; + assert(lost_clock >= 0); + } + + /* + * s->irq_coalesced can change for two reasons: + * + * a) if one or more periodic timer interrupts have been lost, + * lost_clock will be more that a period. + * + * b) when the period may be reconfigured, we expect the OS to + * treat delayed tick as the new period. So, when switching + * from a shorter to a longer period, scale down the missing, + * because the OS will treat past delayed ticks as longer + * (leftovers are put back into lost_clock). When switching + * to a shorter period, scale up the missing ticks since the + * OS handler will treat past delayed ticks as shorter. + */ + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + uint32_t old_irq_coalesced = s->irq_coalesced; + + s->period = period; + lost_clock += old_irq_coalesced * old_period; + s->irq_coalesced = lost_clock / s->period; + lost_clock %= s->period; + if (old_irq_coalesced != s->irq_coalesced || + old_period != s->period) { + DPRINTF_C("cmos: coalesced irqs scaled from %d to %d, " + "period scaled from %d to %d\n", old_irq_coalesced, + s->irq_coalesced, old_period, s->period); + rtc_coalesced_timer_update(s); + } + } else { + /* + * no way to compensate the interrupt if LOST_TICK_POLICY_SLEW + * is not used, we should make the time progress anyway. + */ + lost_clock = MIN(lost_clock, period); + } + + assert(lost_clock >= 0 && lost_clock <= period); + + next_irq_clock = cur_clock + period - lost_clock; + s->next_periodic_time = periodic_clock_to_ns(next_irq_clock) + 1; + timer_mod(s->periodic_timer, s->next_periodic_time); + } else { + s->irq_coalesced = 0; + timer_del(s->periodic_timer); + } +} + +static void rtc_periodic_timer(void *opaque) +{ + RTCState *s = opaque; + + periodic_timer_update(s, s->next_periodic_time, 0); + s->cmos_data[RTC_REG_C] |= REG_C_PF; + if (s->cmos_data[RTC_REG_B] & REG_B_PIE) { + s->cmos_data[RTC_REG_C] |= REG_C_IRQF; + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT) + s->irq_reinject_on_ack_count = 0; + if (!rtc_policy_slew_deliver_irq(s)) { + s->irq_coalesced++; + rtc_coalesced_timer_update(s); + DPRINTF_C("cmos: coalesced irqs increased to %d\n", + s->irq_coalesced); + } + } else + qemu_irq_raise(s->irq); + } +} + +/* handle update-ended timer */ +static void check_update_timer(RTCState *s) +{ + uint64_t next_update_time; + uint64_t guest_nsec; + int next_alarm_sec; + + /* From the data sheet: "Holding the dividers in reset prevents + * interrupts from operating, while setting the SET bit allows" + * them to occur. + */ + if ((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) { + assert((s->cmos_data[RTC_REG_A] & REG_A_UIP) == 0); + timer_del(s->update_timer); + return; + } + + guest_nsec = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND; + next_update_time = qemu_clock_get_ns(rtc_clock) + + NANOSECONDS_PER_SECOND - guest_nsec; + + /* Compute time of next alarm. One second is already accounted + * for in next_update_time. + */ + next_alarm_sec = get_next_alarm(s); + s->next_alarm_time = next_update_time + + (next_alarm_sec - 1) * NANOSECONDS_PER_SECOND; + + /* If update_in_progress latched the UIP bit, we must keep the timer + * programmed to the next second, so that UIP is cleared. Otherwise, + * if UF is already set, we might be able to optimize. + */ + if (!(s->cmos_data[RTC_REG_A] & REG_A_UIP) && + (s->cmos_data[RTC_REG_C] & REG_C_UF)) { + /* If AF cannot change (i.e. either it is set already, or + * SET=1 and then the time is not updated), nothing to do. + */ + if ((s->cmos_data[RTC_REG_B] & REG_B_SET) || + (s->cmos_data[RTC_REG_C] & REG_C_AF)) { + timer_del(s->update_timer); + return; + } + + /* UF is set, but AF is clear. Program the timer to target + * the alarm time. */ + next_update_time = s->next_alarm_time; + } + if (next_update_time != timer_expire_time_ns(s->update_timer)) { + timer_mod(s->update_timer, next_update_time); + } +} + +static inline uint8_t convert_hour(RTCState *s, uint8_t hour) +{ + if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { + hour %= 12; + if (s->cmos_data[RTC_HOURS] & 0x80) { + hour += 12; + } + } + return hour; +} + +static uint64_t get_next_alarm(RTCState *s) +{ + int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; + int32_t hour, min, sec; + + rtc_update_time(s); + + alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]); + alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]); + alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]); + alarm_hour = alarm_hour == -1 ? -1 : convert_hour(s, alarm_hour); + + cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); + cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); + cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]); + cur_hour = convert_hour(s, cur_hour); + + if (alarm_hour == -1) { + alarm_hour = cur_hour; + if (alarm_min == -1) { + alarm_min = cur_min; + if (alarm_sec == -1) { + alarm_sec = cur_sec + 1; + } else if (cur_sec > alarm_sec) { + alarm_min++; + } + } else if (cur_min == alarm_min) { + if (alarm_sec == -1) { + alarm_sec = cur_sec + 1; + } else { + if (cur_sec > alarm_sec) { + alarm_hour++; + } + } + if (alarm_sec == SEC_PER_MIN) { + /* wrap to next hour, minutes is not in don't care mode */ + alarm_sec = 0; + alarm_hour++; + } + } else if (cur_min > alarm_min) { + alarm_hour++; + } + } else if (cur_hour == alarm_hour) { + if (alarm_min == -1) { + alarm_min = cur_min; + if (alarm_sec == -1) { + alarm_sec = cur_sec + 1; + } else if (cur_sec > alarm_sec) { + alarm_min++; + } + + if (alarm_sec == SEC_PER_MIN) { + alarm_sec = 0; + alarm_min++; + } + /* wrap to next day, hour is not in don't care mode */ + alarm_min %= MIN_PER_HOUR; + } else if (cur_min == alarm_min) { + if (alarm_sec == -1) { + alarm_sec = cur_sec + 1; + } + /* wrap to next day, hours+minutes not in don't care mode */ + alarm_sec %= SEC_PER_MIN; + } + } + + /* values that are still don't care fire at the next min/sec */ + if (alarm_min == -1) { + alarm_min = 0; + } + if (alarm_sec == -1) { + alarm_sec = 0; + } + + /* keep values in range */ + if (alarm_sec == SEC_PER_MIN) { + alarm_sec = 0; + alarm_min++; + } + if (alarm_min == MIN_PER_HOUR) { + alarm_min = 0; + alarm_hour++; + } + alarm_hour %= HOUR_PER_DAY; + + hour = alarm_hour - cur_hour; + min = hour * MIN_PER_HOUR + alarm_min - cur_min; + sec = min * SEC_PER_MIN + alarm_sec - cur_sec; + return sec <= 0 ? sec + SEC_PER_DAY : sec; +} + +static void rtc_update_timer(void *opaque) +{ + RTCState *s = opaque; + int32_t irqs = REG_C_UF; + int32_t new_irqs; + + assert((s->cmos_data[RTC_REG_A] & 0x60) != 0x60); + + /* UIP might have been latched, update time and clear it. */ + rtc_update_time(s); + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + + if (qemu_clock_get_ns(rtc_clock) >= s->next_alarm_time) { + irqs |= REG_C_AF; + if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC, NULL); + } + } + + new_irqs = irqs & ~s->cmos_data[RTC_REG_C]; + s->cmos_data[RTC_REG_C] |= irqs; + if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) { + s->cmos_data[RTC_REG_C] |= REG_C_IRQF; + qemu_irq_raise(s->irq); + } + check_update_timer(s); +} + +static void cmos_ioport_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + RTCState *s = opaque; + uint32_t old_period; + bool update_periodic_timer; + + if ((addr & 1) == 0) { + s->cmos_index = data & 0x7f; + } else { + CMOS_DPRINTF("cmos: write index=0x%02x val=0x%02" PRIx64 "\n", + s->cmos_index, data); + switch(s->cmos_index) { + case RTC_SECONDS_ALARM: + case RTC_MINUTES_ALARM: + case RTC_HOURS_ALARM: + s->cmos_data[s->cmos_index] = data; + check_update_timer(s); + break; + case RTC_IBM_PS2_CENTURY_BYTE: + s->cmos_index = RTC_CENTURY; + /* fall through */ + case RTC_CENTURY: + case RTC_SECONDS: + case RTC_MINUTES: + case RTC_HOURS: + case RTC_DAY_OF_WEEK: + case RTC_DAY_OF_MONTH: + case RTC_MONTH: + case RTC_YEAR: + s->cmos_data[s->cmos_index] = data; + /* if in set mode, do not update the time */ + if (rtc_running(s)) { + rtc_set_time(s); + check_update_timer(s); + } + break; + case RTC_REG_A: + update_periodic_timer = (s->cmos_data[RTC_REG_A] ^ data) & 0x0f; + old_period = rtc_periodic_clock_ticks(s); + + if ((data & 0x60) == 0x60) { + if (rtc_running(s)) { + rtc_update_time(s); + } + /* What happens to UIP when divider reset is enabled is + * unclear from the datasheet. Shouldn't matter much + * though. + */ + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + } else if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) && + (data & 0x70) <= 0x20) { + /* when the divider reset is removed, the first update cycle + * begins one-half second later*/ + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + s->offset = 500000000; + rtc_set_time(s); + } + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + } + /* UIP bit is read only */ + s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | + (s->cmos_data[RTC_REG_A] & REG_A_UIP); + + if (update_periodic_timer) { + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), + old_period); + } + + check_update_timer(s); + break; + case RTC_REG_B: + update_periodic_timer = (s->cmos_data[RTC_REG_B] ^ data) + & REG_B_PIE; + old_period = rtc_periodic_clock_ticks(s); + + if (data & REG_B_SET) { + /* update cmos to when the rtc was stopping */ + if (rtc_running(s)) { + rtc_update_time(s); + } + /* set mode: reset UIP mode */ + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + data &= ~REG_B_UIE; + } else { + /* if disabling set mode, update the time */ + if ((s->cmos_data[RTC_REG_B] & REG_B_SET) && + (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) { + s->offset = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND; + rtc_set_time(s); + } + } + /* if an interrupt flag is already set when the interrupt + * becomes enabled, raise an interrupt immediately. */ + if (data & s->cmos_data[RTC_REG_C] & REG_C_MASK) { + s->cmos_data[RTC_REG_C] |= REG_C_IRQF; + qemu_irq_raise(s->irq); + } else { + s->cmos_data[RTC_REG_C] &= ~REG_C_IRQF; + qemu_irq_lower(s->irq); + } + s->cmos_data[RTC_REG_B] = data; + + if (update_periodic_timer) { + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), + old_period); + } + + check_update_timer(s); + break; + case RTC_REG_C: + case RTC_REG_D: + /* cannot write to them */ + break; + default: + s->cmos_data[s->cmos_index] = data; + break; + } + } +} + +static inline int rtc_to_bcd(RTCState *s, int a) +{ + if (s->cmos_data[RTC_REG_B] & REG_B_DM) { + return a; + } else { + return ((a / 10) << 4) | (a % 10); + } +} + +static inline int rtc_from_bcd(RTCState *s, int a) +{ + if ((a & 0xc0) == 0xc0) { + return -1; + } + if (s->cmos_data[RTC_REG_B] & REG_B_DM) { + return a; + } else { + return ((a >> 4) * 10) + (a & 0x0f); + } +} + +static void rtc_get_time(RTCState *s, struct tm *tm) +{ + tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); + tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); + tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f); + if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { + tm->tm_hour %= 12; + if (s->cmos_data[RTC_HOURS] & 0x80) { + tm->tm_hour += 12; + } + } + tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1; + tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); + tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1; + tm->tm_year = + rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year + + rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; +} + +static void rtc_set_time(RTCState *s) +{ + struct tm tm; + + rtc_get_time(s, &tm); + s->base_rtc = mktimegm(&tm); + s->last_update = qemu_clock_get_ns(rtc_clock); + + qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); +} + +static void rtc_set_cmos(RTCState *s, const struct tm *tm) +{ + int year; + + s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec); + s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min); + if (s->cmos_data[RTC_REG_B] & REG_B_24H) { + /* 24 hour format */ + s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour); + } else { + /* 12 hour format */ + int h = (tm->tm_hour % 12) ? tm->tm_hour % 12 : 12; + s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, h); + if (tm->tm_hour >= 12) + s->cmos_data[RTC_HOURS] |= 0x80; + } + s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1); + s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday); + s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1); + year = tm->tm_year + 1900 - s->base_year; + s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year % 100); + s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100); +} + +static void rtc_update_time(RTCState *s) +{ + struct tm ret; + time_t guest_sec; + int64_t guest_nsec; + + guest_nsec = get_guest_rtc_ns(s); + guest_sec = guest_nsec / NANOSECONDS_PER_SECOND; + gmtime_r(&guest_sec, &ret); + + /* Is SET flag of Register B disabled? */ + if ((s->cmos_data[RTC_REG_B] & REG_B_SET) == 0) { + rtc_set_cmos(s, &ret); + } +} + +static int update_in_progress(RTCState *s) +{ + int64_t guest_nsec; + + if (!rtc_running(s)) { + return 0; + } + if (timer_pending(s->update_timer)) { + int64_t next_update_time = timer_expire_time_ns(s->update_timer); + /* Latch UIP until the timer expires. */ + if (qemu_clock_get_ns(rtc_clock) >= + (next_update_time - UIP_HOLD_LENGTH)) { + s->cmos_data[RTC_REG_A] |= REG_A_UIP; + return 1; + } + } + + guest_nsec = get_guest_rtc_ns(s); + /* UIP bit will be set at last 244us of every second. */ + if ((guest_nsec % NANOSECONDS_PER_SECOND) >= + (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH)) { + return 1; + } + return 0; +} + +static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, + unsigned size) +{ + RTCState *s = opaque; + int ret; + if ((addr & 1) == 0) { + return 0xff; + } else { + switch(s->cmos_index) { + case RTC_IBM_PS2_CENTURY_BYTE: + s->cmos_index = RTC_CENTURY; + /* fall through */ + case RTC_CENTURY: + case RTC_SECONDS: + case RTC_MINUTES: + case RTC_HOURS: + case RTC_DAY_OF_WEEK: + case RTC_DAY_OF_MONTH: + case RTC_MONTH: + case RTC_YEAR: + /* if not in set mode, calibrate cmos before + * reading*/ + if (rtc_running(s)) { + rtc_update_time(s); + } + ret = s->cmos_data[s->cmos_index]; + break; + case RTC_REG_A: + ret = s->cmos_data[s->cmos_index]; + if (update_in_progress(s)) { + ret |= REG_A_UIP; + } + break; + case RTC_REG_C: + ret = s->cmos_data[s->cmos_index]; + qemu_irq_lower(s->irq); + s->cmos_data[RTC_REG_C] = 0x00; + if (ret & (REG_C_UF | REG_C_AF)) { + check_update_timer(s); + } + + if(s->irq_coalesced && + (s->cmos_data[RTC_REG_B] & REG_B_PIE) && + s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) { + s->irq_reinject_on_ack_count++; + s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_PF; + DPRINTF_C("cmos: injecting on ack\n"); + if (rtc_policy_slew_deliver_irq(s)) { + s->irq_coalesced--; + DPRINTF_C("cmos: coalesced irqs decreased to %d\n", + s->irq_coalesced); + } + } + break; + default: + ret = s->cmos_data[s->cmos_index]; + break; + } + CMOS_DPRINTF("cmos: read index=0x%02x val=0x%02x\n", + s->cmos_index, ret); + return ret; + } +} + +void rtc_set_memory(ISADevice *dev, int addr, int val) +{ + RTCState *s = MC146818_RTC(dev); + if (addr >= 0 && addr <= 127) + s->cmos_data[addr] = val; +} + +int rtc_get_memory(ISADevice *dev, int addr) +{ + RTCState *s = MC146818_RTC(dev); + assert(addr >= 0 && addr <= 127); + return s->cmos_data[addr]; +} + +static void rtc_set_date_from_host(ISADevice *dev) +{ + RTCState *s = MC146818_RTC(dev); + struct tm tm; + + qemu_get_timedate(&tm, 0); + + s->base_rtc = mktimegm(&tm); + s->last_update = qemu_clock_get_ns(rtc_clock); + s->offset = 0; + + /* set the CMOS date */ + rtc_set_cmos(s, &tm); +} + +static int rtc_pre_save(void *opaque) +{ + RTCState *s = opaque; + + rtc_update_time(s); + + return 0; +} + +static int rtc_post_load(void *opaque, int version_id) +{ + RTCState *s = opaque; + + if (version_id <= 2 || rtc_clock == QEMU_CLOCK_REALTIME) { + rtc_set_time(s); + s->offset = 0; + check_update_timer(s); + } + + /* The periodic timer is deterministic in record/replay mode, + * so there is no need to update it after loading the vmstate. + * Reading RTC here would misalign record and replay. + */ + if (replay_mode == REPLAY_MODE_NONE) { + uint64_t now = qemu_clock_get_ns(rtc_clock); + if (now < s->next_periodic_time || + now > (s->next_periodic_time + get_max_clock_jump())) { + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), 0); + } + } + + if (version_id >= 2) { + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + rtc_coalesced_timer_update(s); + } + } + return 0; +} + +static bool rtc_irq_reinject_on_ack_count_needed(void *opaque) +{ + RTCState *s = (RTCState *)opaque; + return s->irq_reinject_on_ack_count != 0; +} + +static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { + .name = "mc146818rtc/irq_reinject_on_ack_count", + .version_id = 1, + .minimum_version_id = 1, + .needed = rtc_irq_reinject_on_ack_count_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_rtc = { + .name = "mc146818rtc", + .version_id = 3, + .minimum_version_id = 1, + .pre_save = rtc_pre_save, + .post_load = rtc_post_load, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(cmos_data, RTCState), + VMSTATE_UINT8(cmos_index, RTCState), + VMSTATE_UNUSED(7*4), + VMSTATE_TIMER_PTR(periodic_timer, RTCState), + VMSTATE_INT64(next_periodic_time, RTCState), + VMSTATE_UNUSED(3*8), + VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), + VMSTATE_UINT32_V(period, RTCState, 2), + VMSTATE_UINT64_V(base_rtc, RTCState, 3), + VMSTATE_UINT64_V(last_update, RTCState, 3), + VMSTATE_INT64_V(offset, RTCState, 3), + VMSTATE_TIMER_PTR_V(update_timer, RTCState, 3), + VMSTATE_UINT64_V(next_alarm_time, RTCState, 3), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_rtc_irq_reinject_on_ack_count, + NULL + } +}; + +/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE) + BIOS will read it and start S3 resume at POST Entry */ +static void rtc_notify_suspend(Notifier *notifier, void *data) +{ + RTCState *s = container_of(notifier, RTCState, suspend_notifier); + rtc_set_memory(ISA_DEVICE(s), 0xF, 0xFE); +} + +static void rtc_reset(void *opaque) +{ + RTCState *s = opaque; + + s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE); + s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF); + check_update_timer(s); + + qemu_irq_lower(s->irq); + + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + s->irq_coalesced = 0; + s->irq_reinject_on_ack_count = 0; + } +} + +static const MemoryRegionOps cmos_ops = { + .read = cmos_ioport_read, + .write = cmos_ioport_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) +{ + RTCState *s = MC146818_RTC(obj); + + rtc_update_time(s); + rtc_get_time(s, current_tm); +} + +static void rtc_realizefn(DeviceState *dev, Error **errp) +{ + ISADevice *isadev = ISA_DEVICE(dev); + RTCState *s = MC146818_RTC(dev); + int base = 0x70; + + s->cmos_data[RTC_REG_A] = 0x26; + s->cmos_data[RTC_REG_B] = 0x02; + s->cmos_data[RTC_REG_C] = 0x00; + s->cmos_data[RTC_REG_D] = 0x80; + + /* This is for historical reasons. The default base year qdev property + * was set to 2000 for most machine types before the century byte was + * implemented. + * + * This if statement means that the century byte will be always 0 + * (at least until 2079...) for base_year = 1980, but will be set + * correctly for base_year = 2000. + */ + if (s->base_year == 2000) { + s->base_year = 0; + } + + rtc_set_date_from_host(isadev); + + switch (s->lost_tick_policy) { +#ifdef TARGET_I386 + case LOST_TICK_POLICY_SLEW: + s->coalesced_timer = + timer_new_ns(rtc_clock, rtc_coalesced_timer, s); + break; +#endif + case LOST_TICK_POLICY_DISCARD: + break; + default: + error_setg(errp, "Invalid lost tick policy."); + return; + } + + s->periodic_timer = timer_new_ns(rtc_clock, rtc_periodic_timer, s); + s->update_timer = timer_new_ns(rtc_clock, rtc_update_timer, s); + check_update_timer(s); + + s->suspend_notifier.notify = rtc_notify_suspend; + qemu_register_suspend_notifier(&s->suspend_notifier); + + memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2); + isa_register_ioport(isadev, &s->io, base); + + /* register rtc 0x70 port for coalesced_pio */ + memory_region_set_flush_coalesced(&s->io); + memory_region_init_io(&s->coalesced_io, OBJECT(s), &cmos_ops, + s, "rtc-index", 1); + memory_region_add_subregion(&s->io, 0, &s->coalesced_io); + memory_region_add_coalescing(&s->coalesced_io, 0, 1); + + qdev_set_legacy_instance_id(dev, base, 3); + qemu_register_reset(rtc_reset, s); + + object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL); + + qdev_init_gpio_out(dev, &s->irq, 1); +} + +ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) +{ + DeviceState *dev; + ISADevice *isadev; + RTCState *s; + + isadev = isa_create(bus, TYPE_MC146818_RTC); + dev = DEVICE(isadev); + s = MC146818_RTC(isadev); + qdev_prop_set_int32(dev, "base_year", base_year); + qdev_init_nofail(dev); + if (intercept_irq) { + qdev_connect_gpio_out(dev, 0, intercept_irq); + } else { + isa_connect_gpio_out(isadev, 0, RTC_ISA_IRQ); + } + QLIST_INSERT_HEAD(&rtc_devices, s, link); + + object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(s), + "date", NULL); + + return isadev; +} + +static Property mc146818rtc_properties[] = { + DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980), + DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState, + lost_tick_policy, LOST_TICK_POLICY_DISCARD), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rtc_resetdev(DeviceState *d) +{ + RTCState *s = MC146818_RTC(d); + + /* Reason: VM do suspend self will set 0xfe + * Reset any values other than 0xfe(Guest suspend case) */ + if (s->cmos_data[0x0f] != 0xfe) { + s->cmos_data[0x0f] = 0x00; + } +} + +static void rtc_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rtc_realizefn; + dc->reset = rtc_resetdev; + dc->vmsd = &vmstate_rtc; + dc->props = mc146818rtc_properties; + /* Reason: needs to be wired up by rtc_init() */ + dc->user_creatable = false; +} + +static const TypeInfo mc146818rtc_info = { + .name = TYPE_MC146818_RTC, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(RTCState), + .class_init = rtc_class_initfn, +}; + +static void mc146818rtc_register_types(void) +{ + type_register_static(&mc146818rtc_info); +} + +type_init(mc146818rtc_register_types) diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 27c5dce09e..af415c8ef8 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -35,9 +35,6 @@ config ALTERA_TIMER bool select PTIMER -config MC146818RTC - bool - config ALLWINNER_A10_PIT bool select PTIMER diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 9f64f6e11e..b0159189cf 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -35,8 +35,6 @@ common-obj-$(CONFIG_SH4) += sh_timer.o common-obj-$(CONFIG_DIGIC) += digic-timer.o common-obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o -obj-$(CONFIG_MC146818RTC) += mc146818rtc.o - common-obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 1ddae4e7d7..02bf8a8ce8 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -33,7 +33,7 @@ #include "qemu/timer.h" #include "hw/timer/hpet.h" #include "hw/sysbus.h" -#include "hw/timer/mc146818rtc.h" +#include "hw/rtc/mc146818rtc.h" #include "migration/vmstate.h" #include "hw/timer/i8254.h" diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c deleted file mode 100644 index 6cb378751b..0000000000 --- a/hw/timer/mc146818rtc.c +++ /dev/null @@ -1,1063 +0,0 @@ -/* - * QEMU MC146818 RTC emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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 "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/cutils.h" -#include "qemu/module.h" -#include "qemu/bcd.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/replay.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "hw/timer/mc146818rtc.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" -#include "qapi/qapi-events-misc-target.h" -#include "qapi/visitor.h" -#include "exec/address-spaces.h" - -#ifdef TARGET_I386 -#include "hw/i386/apic.h" -#endif - -//#define DEBUG_CMOS -//#define DEBUG_COALESCED - -#ifdef DEBUG_CMOS -# define CMOS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define CMOS_DPRINTF(format, ...) do { } while (0) -#endif - -#ifdef DEBUG_COALESCED -# define DPRINTF_C(format, ...) printf(format, ## __VA_ARGS__) -#else -# define DPRINTF_C(format, ...) do { } while (0) -#endif - -#define SEC_PER_MIN 60 -#define MIN_PER_HOUR 60 -#define SEC_PER_HOUR 3600 -#define HOUR_PER_DAY 24 -#define SEC_PER_DAY 86400 - -#define RTC_REINJECT_ON_ACK_COUNT 20 -#define RTC_CLOCK_RATE 32768 -#define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) - -#define MC146818_RTC(obj) OBJECT_CHECK(RTCState, (obj), TYPE_MC146818_RTC) - -typedef struct RTCState { - ISADevice parent_obj; - - MemoryRegion io; - MemoryRegion coalesced_io; - uint8_t cmos_data[128]; - uint8_t cmos_index; - int32_t base_year; - uint64_t base_rtc; - uint64_t last_update; - int64_t offset; - qemu_irq irq; - int it_shift; - /* periodic timer */ - QEMUTimer *periodic_timer; - int64_t next_periodic_time; - /* update-ended timer */ - QEMUTimer *update_timer; - uint64_t next_alarm_time; - uint16_t irq_reinject_on_ack_count; - uint32_t irq_coalesced; - uint32_t period; - QEMUTimer *coalesced_timer; - LostTickPolicy lost_tick_policy; - Notifier suspend_notifier; - QLIST_ENTRY(RTCState) link; -} RTCState; - -static void rtc_set_time(RTCState *s); -static void rtc_update_time(RTCState *s); -static void rtc_set_cmos(RTCState *s, const struct tm *tm); -static inline int rtc_from_bcd(RTCState *s, int a); -static uint64_t get_next_alarm(RTCState *s); - -static inline bool rtc_running(RTCState *s) -{ - return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) && - (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20); -} - -static uint64_t get_guest_rtc_ns(RTCState *s) -{ - uint64_t guest_clock = qemu_clock_get_ns(rtc_clock); - - return s->base_rtc * NANOSECONDS_PER_SECOND + - guest_clock - s->last_update + s->offset; -} - -static void rtc_coalesced_timer_update(RTCState *s) -{ - if (s->irq_coalesced == 0) { - timer_del(s->coalesced_timer); - } else { - /* divide each RTC interval to 2 - 8 smaller intervals */ - int c = MIN(s->irq_coalesced, 7) + 1; - int64_t next_clock = qemu_clock_get_ns(rtc_clock) + - periodic_clock_to_ns(s->period / c); - timer_mod(s->coalesced_timer, next_clock); - } -} - -static QLIST_HEAD(, RTCState) rtc_devices = - QLIST_HEAD_INITIALIZER(rtc_devices); - -#ifdef TARGET_I386 -void qmp_rtc_reset_reinjection(Error **errp) -{ - RTCState *s; - - QLIST_FOREACH(s, &rtc_devices, link) { - s->irq_coalesced = 0; - } -} - -static bool rtc_policy_slew_deliver_irq(RTCState *s) -{ - apic_reset_irq_delivered(); - qemu_irq_raise(s->irq); - return apic_get_irq_delivered(); -} - -static void rtc_coalesced_timer(void *opaque) -{ - RTCState *s = opaque; - - if (s->irq_coalesced != 0) { - s->cmos_data[RTC_REG_C] |= 0xc0; - DPRINTF_C("cmos: injecting from timer\n"); - if (rtc_policy_slew_deliver_irq(s)) { - s->irq_coalesced--; - DPRINTF_C("cmos: coalesced irqs decreased to %d\n", - s->irq_coalesced); - } - } - - rtc_coalesced_timer_update(s); -} -#else -static bool rtc_policy_slew_deliver_irq(RTCState *s) -{ - assert(0); - return false; -} -#endif - -static uint32_t rtc_periodic_clock_ticks(RTCState *s) -{ - int period_code; - - if (!(s->cmos_data[RTC_REG_B] & REG_B_PIE)) { - return 0; - } - - period_code = s->cmos_data[RTC_REG_A] & 0x0f; - - return periodic_period_to_clock(period_code); -} - -/* - * handle periodic timer. @old_period indicates the periodic timer update - * is just due to period adjustment. - */ -static void -periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period) -{ - uint32_t period; - int64_t cur_clock, next_irq_clock, lost_clock = 0; - - period = rtc_periodic_clock_ticks(s); - - if (period) { - /* compute 32 khz clock */ - cur_clock = - muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); - - /* - * if the periodic timer's update is due to period re-configuration, - * we should count the clock since last interrupt. - */ - if (old_period) { - int64_t last_periodic_clock, next_periodic_clock; - - next_periodic_clock = muldiv64(s->next_periodic_time, - RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); - last_periodic_clock = next_periodic_clock - old_period; - lost_clock = cur_clock - last_periodic_clock; - assert(lost_clock >= 0); - } - - /* - * s->irq_coalesced can change for two reasons: - * - * a) if one or more periodic timer interrupts have been lost, - * lost_clock will be more that a period. - * - * b) when the period may be reconfigured, we expect the OS to - * treat delayed tick as the new period. So, when switching - * from a shorter to a longer period, scale down the missing, - * because the OS will treat past delayed ticks as longer - * (leftovers are put back into lost_clock). When switching - * to a shorter period, scale up the missing ticks since the - * OS handler will treat past delayed ticks as shorter. - */ - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - uint32_t old_irq_coalesced = s->irq_coalesced; - - s->period = period; - lost_clock += old_irq_coalesced * old_period; - s->irq_coalesced = lost_clock / s->period; - lost_clock %= s->period; - if (old_irq_coalesced != s->irq_coalesced || - old_period != s->period) { - DPRINTF_C("cmos: coalesced irqs scaled from %d to %d, " - "period scaled from %d to %d\n", old_irq_coalesced, - s->irq_coalesced, old_period, s->period); - rtc_coalesced_timer_update(s); - } - } else { - /* - * no way to compensate the interrupt if LOST_TICK_POLICY_SLEW - * is not used, we should make the time progress anyway. - */ - lost_clock = MIN(lost_clock, period); - } - - assert(lost_clock >= 0 && lost_clock <= period); - - next_irq_clock = cur_clock + period - lost_clock; - s->next_periodic_time = periodic_clock_to_ns(next_irq_clock) + 1; - timer_mod(s->periodic_timer, s->next_periodic_time); - } else { - s->irq_coalesced = 0; - timer_del(s->periodic_timer); - } -} - -static void rtc_periodic_timer(void *opaque) -{ - RTCState *s = opaque; - - periodic_timer_update(s, s->next_periodic_time, 0); - s->cmos_data[RTC_REG_C] |= REG_C_PF; - if (s->cmos_data[RTC_REG_B] & REG_B_PIE) { - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT) - s->irq_reinject_on_ack_count = 0; - if (!rtc_policy_slew_deliver_irq(s)) { - s->irq_coalesced++; - rtc_coalesced_timer_update(s); - DPRINTF_C("cmos: coalesced irqs increased to %d\n", - s->irq_coalesced); - } - } else - qemu_irq_raise(s->irq); - } -} - -/* handle update-ended timer */ -static void check_update_timer(RTCState *s) -{ - uint64_t next_update_time; - uint64_t guest_nsec; - int next_alarm_sec; - - /* From the data sheet: "Holding the dividers in reset prevents - * interrupts from operating, while setting the SET bit allows" - * them to occur. - */ - if ((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) { - assert((s->cmos_data[RTC_REG_A] & REG_A_UIP) == 0); - timer_del(s->update_timer); - return; - } - - guest_nsec = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND; - next_update_time = qemu_clock_get_ns(rtc_clock) - + NANOSECONDS_PER_SECOND - guest_nsec; - - /* Compute time of next alarm. One second is already accounted - * for in next_update_time. - */ - next_alarm_sec = get_next_alarm(s); - s->next_alarm_time = next_update_time + - (next_alarm_sec - 1) * NANOSECONDS_PER_SECOND; - - /* If update_in_progress latched the UIP bit, we must keep the timer - * programmed to the next second, so that UIP is cleared. Otherwise, - * if UF is already set, we might be able to optimize. - */ - if (!(s->cmos_data[RTC_REG_A] & REG_A_UIP) && - (s->cmos_data[RTC_REG_C] & REG_C_UF)) { - /* If AF cannot change (i.e. either it is set already, or - * SET=1 and then the time is not updated), nothing to do. - */ - if ((s->cmos_data[RTC_REG_B] & REG_B_SET) || - (s->cmos_data[RTC_REG_C] & REG_C_AF)) { - timer_del(s->update_timer); - return; - } - - /* UF is set, but AF is clear. Program the timer to target - * the alarm time. */ - next_update_time = s->next_alarm_time; - } - if (next_update_time != timer_expire_time_ns(s->update_timer)) { - timer_mod(s->update_timer, next_update_time); - } -} - -static inline uint8_t convert_hour(RTCState *s, uint8_t hour) -{ - if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { - hour %= 12; - if (s->cmos_data[RTC_HOURS] & 0x80) { - hour += 12; - } - } - return hour; -} - -static uint64_t get_next_alarm(RTCState *s) -{ - int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; - int32_t hour, min, sec; - - rtc_update_time(s); - - alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]); - alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]); - alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]); - alarm_hour = alarm_hour == -1 ? -1 : convert_hour(s, alarm_hour); - - cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); - cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); - cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]); - cur_hour = convert_hour(s, cur_hour); - - if (alarm_hour == -1) { - alarm_hour = cur_hour; - if (alarm_min == -1) { - alarm_min = cur_min; - if (alarm_sec == -1) { - alarm_sec = cur_sec + 1; - } else if (cur_sec > alarm_sec) { - alarm_min++; - } - } else if (cur_min == alarm_min) { - if (alarm_sec == -1) { - alarm_sec = cur_sec + 1; - } else { - if (cur_sec > alarm_sec) { - alarm_hour++; - } - } - if (alarm_sec == SEC_PER_MIN) { - /* wrap to next hour, minutes is not in don't care mode */ - alarm_sec = 0; - alarm_hour++; - } - } else if (cur_min > alarm_min) { - alarm_hour++; - } - } else if (cur_hour == alarm_hour) { - if (alarm_min == -1) { - alarm_min = cur_min; - if (alarm_sec == -1) { - alarm_sec = cur_sec + 1; - } else if (cur_sec > alarm_sec) { - alarm_min++; - } - - if (alarm_sec == SEC_PER_MIN) { - alarm_sec = 0; - alarm_min++; - } - /* wrap to next day, hour is not in don't care mode */ - alarm_min %= MIN_PER_HOUR; - } else if (cur_min == alarm_min) { - if (alarm_sec == -1) { - alarm_sec = cur_sec + 1; - } - /* wrap to next day, hours+minutes not in don't care mode */ - alarm_sec %= SEC_PER_MIN; - } - } - - /* values that are still don't care fire at the next min/sec */ - if (alarm_min == -1) { - alarm_min = 0; - } - if (alarm_sec == -1) { - alarm_sec = 0; - } - - /* keep values in range */ - if (alarm_sec == SEC_PER_MIN) { - alarm_sec = 0; - alarm_min++; - } - if (alarm_min == MIN_PER_HOUR) { - alarm_min = 0; - alarm_hour++; - } - alarm_hour %= HOUR_PER_DAY; - - hour = alarm_hour - cur_hour; - min = hour * MIN_PER_HOUR + alarm_min - cur_min; - sec = min * SEC_PER_MIN + alarm_sec - cur_sec; - return sec <= 0 ? sec + SEC_PER_DAY : sec; -} - -static void rtc_update_timer(void *opaque) -{ - RTCState *s = opaque; - int32_t irqs = REG_C_UF; - int32_t new_irqs; - - assert((s->cmos_data[RTC_REG_A] & 0x60) != 0x60); - - /* UIP might have been latched, update time and clear it. */ - rtc_update_time(s); - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - - if (qemu_clock_get_ns(rtc_clock) >= s->next_alarm_time) { - irqs |= REG_C_AF; - if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC, NULL); - } - } - - new_irqs = irqs & ~s->cmos_data[RTC_REG_C]; - s->cmos_data[RTC_REG_C] |= irqs; - if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) { - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - qemu_irq_raise(s->irq); - } - check_update_timer(s); -} - -static void cmos_ioport_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - RTCState *s = opaque; - uint32_t old_period; - bool update_periodic_timer; - - if ((addr & 1) == 0) { - s->cmos_index = data & 0x7f; - } else { - CMOS_DPRINTF("cmos: write index=0x%02x val=0x%02" PRIx64 "\n", - s->cmos_index, data); - switch(s->cmos_index) { - case RTC_SECONDS_ALARM: - case RTC_MINUTES_ALARM: - case RTC_HOURS_ALARM: - s->cmos_data[s->cmos_index] = data; - check_update_timer(s); - break; - case RTC_IBM_PS2_CENTURY_BYTE: - s->cmos_index = RTC_CENTURY; - /* fall through */ - case RTC_CENTURY: - case RTC_SECONDS: - case RTC_MINUTES: - case RTC_HOURS: - case RTC_DAY_OF_WEEK: - case RTC_DAY_OF_MONTH: - case RTC_MONTH: - case RTC_YEAR: - s->cmos_data[s->cmos_index] = data; - /* if in set mode, do not update the time */ - if (rtc_running(s)) { - rtc_set_time(s); - check_update_timer(s); - } - break; - case RTC_REG_A: - update_periodic_timer = (s->cmos_data[RTC_REG_A] ^ data) & 0x0f; - old_period = rtc_periodic_clock_ticks(s); - - if ((data & 0x60) == 0x60) { - if (rtc_running(s)) { - rtc_update_time(s); - } - /* What happens to UIP when divider reset is enabled is - * unclear from the datasheet. Shouldn't matter much - * though. - */ - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - } else if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) && - (data & 0x70) <= 0x20) { - /* when the divider reset is removed, the first update cycle - * begins one-half second later*/ - if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { - s->offset = 500000000; - rtc_set_time(s); - } - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - } - /* UIP bit is read only */ - s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | - (s->cmos_data[RTC_REG_A] & REG_A_UIP); - - if (update_periodic_timer) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), - old_period); - } - - check_update_timer(s); - break; - case RTC_REG_B: - update_periodic_timer = (s->cmos_data[RTC_REG_B] ^ data) - & REG_B_PIE; - old_period = rtc_periodic_clock_ticks(s); - - if (data & REG_B_SET) { - /* update cmos to when the rtc was stopping */ - if (rtc_running(s)) { - rtc_update_time(s); - } - /* set mode: reset UIP mode */ - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - data &= ~REG_B_UIE; - } else { - /* if disabling set mode, update the time */ - if ((s->cmos_data[RTC_REG_B] & REG_B_SET) && - (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) { - s->offset = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND; - rtc_set_time(s); - } - } - /* if an interrupt flag is already set when the interrupt - * becomes enabled, raise an interrupt immediately. */ - if (data & s->cmos_data[RTC_REG_C] & REG_C_MASK) { - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - qemu_irq_raise(s->irq); - } else { - s->cmos_data[RTC_REG_C] &= ~REG_C_IRQF; - qemu_irq_lower(s->irq); - } - s->cmos_data[RTC_REG_B] = data; - - if (update_periodic_timer) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), - old_period); - } - - check_update_timer(s); - break; - case RTC_REG_C: - case RTC_REG_D: - /* cannot write to them */ - break; - default: - s->cmos_data[s->cmos_index] = data; - break; - } - } -} - -static inline int rtc_to_bcd(RTCState *s, int a) -{ - if (s->cmos_data[RTC_REG_B] & REG_B_DM) { - return a; - } else { - return ((a / 10) << 4) | (a % 10); - } -} - -static inline int rtc_from_bcd(RTCState *s, int a) -{ - if ((a & 0xc0) == 0xc0) { - return -1; - } - if (s->cmos_data[RTC_REG_B] & REG_B_DM) { - return a; - } else { - return ((a >> 4) * 10) + (a & 0x0f); - } -} - -static void rtc_get_time(RTCState *s, struct tm *tm) -{ - tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); - tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); - tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f); - if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { - tm->tm_hour %= 12; - if (s->cmos_data[RTC_HOURS] & 0x80) { - tm->tm_hour += 12; - } - } - tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1; - tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); - tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1; - tm->tm_year = - rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year + - rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; -} - -static void rtc_set_time(RTCState *s) -{ - struct tm tm; - - rtc_get_time(s, &tm); - s->base_rtc = mktimegm(&tm); - s->last_update = qemu_clock_get_ns(rtc_clock); - - qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); -} - -static void rtc_set_cmos(RTCState *s, const struct tm *tm) -{ - int year; - - s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec); - s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min); - if (s->cmos_data[RTC_REG_B] & REG_B_24H) { - /* 24 hour format */ - s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour); - } else { - /* 12 hour format */ - int h = (tm->tm_hour % 12) ? tm->tm_hour % 12 : 12; - s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, h); - if (tm->tm_hour >= 12) - s->cmos_data[RTC_HOURS] |= 0x80; - } - s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1); - s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday); - s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1); - year = tm->tm_year + 1900 - s->base_year; - s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year % 100); - s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100); -} - -static void rtc_update_time(RTCState *s) -{ - struct tm ret; - time_t guest_sec; - int64_t guest_nsec; - - guest_nsec = get_guest_rtc_ns(s); - guest_sec = guest_nsec / NANOSECONDS_PER_SECOND; - gmtime_r(&guest_sec, &ret); - - /* Is SET flag of Register B disabled? */ - if ((s->cmos_data[RTC_REG_B] & REG_B_SET) == 0) { - rtc_set_cmos(s, &ret); - } -} - -static int update_in_progress(RTCState *s) -{ - int64_t guest_nsec; - - if (!rtc_running(s)) { - return 0; - } - if (timer_pending(s->update_timer)) { - int64_t next_update_time = timer_expire_time_ns(s->update_timer); - /* Latch UIP until the timer expires. */ - if (qemu_clock_get_ns(rtc_clock) >= - (next_update_time - UIP_HOLD_LENGTH)) { - s->cmos_data[RTC_REG_A] |= REG_A_UIP; - return 1; - } - } - - guest_nsec = get_guest_rtc_ns(s); - /* UIP bit will be set at last 244us of every second. */ - if ((guest_nsec % NANOSECONDS_PER_SECOND) >= - (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH)) { - return 1; - } - return 0; -} - -static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - RTCState *s = opaque; - int ret; - if ((addr & 1) == 0) { - return 0xff; - } else { - switch(s->cmos_index) { - case RTC_IBM_PS2_CENTURY_BYTE: - s->cmos_index = RTC_CENTURY; - /* fall through */ - case RTC_CENTURY: - case RTC_SECONDS: - case RTC_MINUTES: - case RTC_HOURS: - case RTC_DAY_OF_WEEK: - case RTC_DAY_OF_MONTH: - case RTC_MONTH: - case RTC_YEAR: - /* if not in set mode, calibrate cmos before - * reading*/ - if (rtc_running(s)) { - rtc_update_time(s); - } - ret = s->cmos_data[s->cmos_index]; - break; - case RTC_REG_A: - ret = s->cmos_data[s->cmos_index]; - if (update_in_progress(s)) { - ret |= REG_A_UIP; - } - break; - case RTC_REG_C: - ret = s->cmos_data[s->cmos_index]; - qemu_irq_lower(s->irq); - s->cmos_data[RTC_REG_C] = 0x00; - if (ret & (REG_C_UF | REG_C_AF)) { - check_update_timer(s); - } - - if(s->irq_coalesced && - (s->cmos_data[RTC_REG_B] & REG_B_PIE) && - s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) { - s->irq_reinject_on_ack_count++; - s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_PF; - DPRINTF_C("cmos: injecting on ack\n"); - if (rtc_policy_slew_deliver_irq(s)) { - s->irq_coalesced--; - DPRINTF_C("cmos: coalesced irqs decreased to %d\n", - s->irq_coalesced); - } - } - break; - default: - ret = s->cmos_data[s->cmos_index]; - break; - } - CMOS_DPRINTF("cmos: read index=0x%02x val=0x%02x\n", - s->cmos_index, ret); - return ret; - } -} - -void rtc_set_memory(ISADevice *dev, int addr, int val) -{ - RTCState *s = MC146818_RTC(dev); - if (addr >= 0 && addr <= 127) - s->cmos_data[addr] = val; -} - -int rtc_get_memory(ISADevice *dev, int addr) -{ - RTCState *s = MC146818_RTC(dev); - assert(addr >= 0 && addr <= 127); - return s->cmos_data[addr]; -} - -static void rtc_set_date_from_host(ISADevice *dev) -{ - RTCState *s = MC146818_RTC(dev); - struct tm tm; - - qemu_get_timedate(&tm, 0); - - s->base_rtc = mktimegm(&tm); - s->last_update = qemu_clock_get_ns(rtc_clock); - s->offset = 0; - - /* set the CMOS date */ - rtc_set_cmos(s, &tm); -} - -static int rtc_pre_save(void *opaque) -{ - RTCState *s = opaque; - - rtc_update_time(s); - - return 0; -} - -static int rtc_post_load(void *opaque, int version_id) -{ - RTCState *s = opaque; - - if (version_id <= 2 || rtc_clock == QEMU_CLOCK_REALTIME) { - rtc_set_time(s); - s->offset = 0; - check_update_timer(s); - } - - /* The periodic timer is deterministic in record/replay mode, - * so there is no need to update it after loading the vmstate. - * Reading RTC here would misalign record and replay. - */ - if (replay_mode == REPLAY_MODE_NONE) { - uint64_t now = qemu_clock_get_ns(rtc_clock); - if (now < s->next_periodic_time || - now > (s->next_periodic_time + get_max_clock_jump())) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), 0); - } - } - - if (version_id >= 2) { - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - rtc_coalesced_timer_update(s); - } - } - return 0; -} - -static bool rtc_irq_reinject_on_ack_count_needed(void *opaque) -{ - RTCState *s = (RTCState *)opaque; - return s->irq_reinject_on_ack_count != 0; -} - -static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { - .name = "mc146818rtc/irq_reinject_on_ack_count", - .version_id = 1, - .minimum_version_id = 1, - .needed = rtc_irq_reinject_on_ack_count_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_rtc = { - .name = "mc146818rtc", - .version_id = 3, - .minimum_version_id = 1, - .pre_save = rtc_pre_save, - .post_load = rtc_post_load, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(cmos_data, RTCState), - VMSTATE_UINT8(cmos_index, RTCState), - VMSTATE_UNUSED(7*4), - VMSTATE_TIMER_PTR(periodic_timer, RTCState), - VMSTATE_INT64(next_periodic_time, RTCState), - VMSTATE_UNUSED(3*8), - VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), - VMSTATE_UINT32_V(period, RTCState, 2), - VMSTATE_UINT64_V(base_rtc, RTCState, 3), - VMSTATE_UINT64_V(last_update, RTCState, 3), - VMSTATE_INT64_V(offset, RTCState, 3), - VMSTATE_TIMER_PTR_V(update_timer, RTCState, 3), - VMSTATE_UINT64_V(next_alarm_time, RTCState, 3), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_rtc_irq_reinject_on_ack_count, - NULL - } -}; - -/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE) - BIOS will read it and start S3 resume at POST Entry */ -static void rtc_notify_suspend(Notifier *notifier, void *data) -{ - RTCState *s = container_of(notifier, RTCState, suspend_notifier); - rtc_set_memory(ISA_DEVICE(s), 0xF, 0xFE); -} - -static void rtc_reset(void *opaque) -{ - RTCState *s = opaque; - - s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE); - s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF); - check_update_timer(s); - - qemu_irq_lower(s->irq); - - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - s->irq_coalesced = 0; - s->irq_reinject_on_ack_count = 0; - } -} - -static const MemoryRegionOps cmos_ops = { - .read = cmos_ioport_read, - .write = cmos_ioport_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) -{ - RTCState *s = MC146818_RTC(obj); - - rtc_update_time(s); - rtc_get_time(s, current_tm); -} - -static void rtc_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - RTCState *s = MC146818_RTC(dev); - int base = 0x70; - - s->cmos_data[RTC_REG_A] = 0x26; - s->cmos_data[RTC_REG_B] = 0x02; - s->cmos_data[RTC_REG_C] = 0x00; - s->cmos_data[RTC_REG_D] = 0x80; - - /* This is for historical reasons. The default base year qdev property - * was set to 2000 for most machine types before the century byte was - * implemented. - * - * This if statement means that the century byte will be always 0 - * (at least until 2079...) for base_year = 1980, but will be set - * correctly for base_year = 2000. - */ - if (s->base_year == 2000) { - s->base_year = 0; - } - - rtc_set_date_from_host(isadev); - - switch (s->lost_tick_policy) { -#ifdef TARGET_I386 - case LOST_TICK_POLICY_SLEW: - s->coalesced_timer = - timer_new_ns(rtc_clock, rtc_coalesced_timer, s); - break; -#endif - case LOST_TICK_POLICY_DISCARD: - break; - default: - error_setg(errp, "Invalid lost tick policy."); - return; - } - - s->periodic_timer = timer_new_ns(rtc_clock, rtc_periodic_timer, s); - s->update_timer = timer_new_ns(rtc_clock, rtc_update_timer, s); - check_update_timer(s); - - s->suspend_notifier.notify = rtc_notify_suspend; - qemu_register_suspend_notifier(&s->suspend_notifier); - - memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2); - isa_register_ioport(isadev, &s->io, base); - - /* register rtc 0x70 port for coalesced_pio */ - memory_region_set_flush_coalesced(&s->io); - memory_region_init_io(&s->coalesced_io, OBJECT(s), &cmos_ops, - s, "rtc-index", 1); - memory_region_add_subregion(&s->io, 0, &s->coalesced_io); - memory_region_add_coalescing(&s->coalesced_io, 0, 1); - - qdev_set_legacy_instance_id(dev, base, 3); - qemu_register_reset(rtc_reset, s); - - object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL); - - qdev_init_gpio_out(dev, &s->irq, 1); -} - -ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) -{ - DeviceState *dev; - ISADevice *isadev; - RTCState *s; - - isadev = isa_create(bus, TYPE_MC146818_RTC); - dev = DEVICE(isadev); - s = MC146818_RTC(isadev); - qdev_prop_set_int32(dev, "base_year", base_year); - qdev_init_nofail(dev); - if (intercept_irq) { - qdev_connect_gpio_out(dev, 0, intercept_irq); - } else { - isa_connect_gpio_out(isadev, 0, RTC_ISA_IRQ); - } - QLIST_INSERT_HEAD(&rtc_devices, s, link); - - object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(s), - "date", NULL); - - return isadev; -} - -static Property mc146818rtc_properties[] = { - DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980), - DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState, - lost_tick_policy, LOST_TICK_POLICY_DISCARD), - DEFINE_PROP_END_OF_LIST(), -}; - -static void rtc_resetdev(DeviceState *d) -{ - RTCState *s = MC146818_RTC(d); - - /* Reason: VM do suspend self will set 0xfe - * Reset any values other than 0xfe(Guest suspend case) */ - if (s->cmos_data[0x0f] != 0xfe) { - s->cmos_data[0x0f] = 0x00; - } -} - -static void rtc_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = rtc_realizefn; - dc->reset = rtc_resetdev; - dc->vmsd = &vmstate_rtc; - dc->props = mc146818rtc_properties; - /* Reason: needs to be wired up by rtc_init() */ - dc->user_creatable = false; -} - -static const TypeInfo mc146818rtc_info = { - .name = TYPE_MC146818_RTC, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(RTCState), - .class_init = rtc_class_initfn, -}; - -static void mc146818rtc_register_types(void) -{ - type_register_static(&mc146818rtc_info); -} - -type_init(mc146818rtc_register_types) diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h new file mode 100644 index 0000000000..2e9331637a --- /dev/null +++ b/include/hw/rtc/mc146818rtc.h @@ -0,0 +1,22 @@ +/* + * QEMU MC146818 RTC emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#ifndef HW_RTC_MC146818RTC_H +#define HW_RTC_MC146818RTC_H + +#include "hw/isa/isa.h" +#include "hw/rtc/mc146818rtc_regs.h" + +#define TYPE_MC146818_RTC "mc146818rtc" + +ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, + qemu_irq intercept_irq); +void rtc_set_memory(ISADevice *dev, int addr, int val); +int rtc_get_memory(ISADevice *dev, int addr); + +#endif /* MC146818RTC_H */ diff --git a/include/hw/rtc/mc146818rtc_regs.h b/include/hw/rtc/mc146818rtc_regs.h new file mode 100644 index 0000000000..dd6c09e2fc --- /dev/null +++ b/include/hw/rtc/mc146818rtc_regs.h @@ -0,0 +1,91 @@ +/* + * QEMU MC146818 RTC emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * 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. + */ + +#ifndef HW_RTC_MC146818RTC_REGS_H +#define HW_RTC_MC146818RTC_REGS_H + +#include "qemu/timer.h" +#include "qemu/host-utils.h" + +#define RTC_ISA_IRQ 8 + +#define RTC_SECONDS 0 +#define RTC_SECONDS_ALARM 1 +#define RTC_MINUTES 2 +#define RTC_MINUTES_ALARM 3 +#define RTC_HOURS 4 +#define RTC_HOURS_ALARM 5 +#define RTC_ALARM_DONT_CARE 0xC0 + +#define RTC_DAY_OF_WEEK 6 +#define RTC_DAY_OF_MONTH 7 +#define RTC_MONTH 8 +#define RTC_YEAR 9 + +#define RTC_REG_A 10 +#define RTC_REG_B 11 +#define RTC_REG_C 12 +#define RTC_REG_D 13 + +/* PC cmos mappings */ +#define RTC_CENTURY 0x32 +#define RTC_IBM_PS2_CENTURY_BYTE 0x37 + +#define REG_A_UIP 0x80 + +#define REG_B_SET 0x80 +#define REG_B_PIE 0x40 +#define REG_B_AIE 0x20 +#define REG_B_UIE 0x10 +#define REG_B_SQWE 0x08 +#define REG_B_DM 0x04 +#define REG_B_24H 0x02 + +#define REG_C_UF 0x10 +#define REG_C_IRQF 0x80 +#define REG_C_PF 0x40 +#define REG_C_AF 0x20 +#define REG_C_MASK 0x70 + +static inline uint32_t periodic_period_to_clock(int period_code) +{ + if (!period_code) { + return 0; + } + + if (period_code <= 2) { + period_code += 7; + } + /* period in 32 Khz cycles */ + return 1 << (period_code - 1); +} + +#define RTC_CLOCK_RATE 32768 + +static inline int64_t periodic_clock_to_ns(int64_t clocks) +{ + return muldiv64(clocks, NANOSECONDS_PER_SECOND, RTC_CLOCK_RATE); +} + +#endif diff --git a/include/hw/timer/mc146818rtc.h b/include/hw/timer/mc146818rtc.h deleted file mode 100644 index fe6ed63f71..0000000000 --- a/include/hw/timer/mc146818rtc.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MC146818RTC_H -#define MC146818RTC_H - -#include "hw/isa/isa.h" -#include "hw/timer/mc146818rtc_regs.h" - -#define TYPE_MC146818_RTC "mc146818rtc" - -ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, - qemu_irq intercept_irq); -void rtc_set_memory(ISADevice *dev, int addr, int val); -int rtc_get_memory(ISADevice *dev, int addr); - -#endif /* MC146818RTC_H */ diff --git a/include/hw/timer/mc146818rtc_regs.h b/include/hw/timer/mc146818rtc_regs.h deleted file mode 100644 index bfbb57e570..0000000000 --- a/include/hw/timer/mc146818rtc_regs.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * QEMU MC146818 RTC emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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. - */ - -#ifndef MC146818RTC_REGS_H -#define MC146818RTC_REGS_H - -#include "qemu/timer.h" - -#define RTC_ISA_IRQ 8 - -#define RTC_SECONDS 0 -#define RTC_SECONDS_ALARM 1 -#define RTC_MINUTES 2 -#define RTC_MINUTES_ALARM 3 -#define RTC_HOURS 4 -#define RTC_HOURS_ALARM 5 -#define RTC_ALARM_DONT_CARE 0xC0 - -#define RTC_DAY_OF_WEEK 6 -#define RTC_DAY_OF_MONTH 7 -#define RTC_MONTH 8 -#define RTC_YEAR 9 - -#define RTC_REG_A 10 -#define RTC_REG_B 11 -#define RTC_REG_C 12 -#define RTC_REG_D 13 - -/* PC cmos mappings */ -#define RTC_CENTURY 0x32 -#define RTC_IBM_PS2_CENTURY_BYTE 0x37 - -#define REG_A_UIP 0x80 - -#define REG_B_SET 0x80 -#define REG_B_PIE 0x40 -#define REG_B_AIE 0x20 -#define REG_B_UIE 0x10 -#define REG_B_SQWE 0x08 -#define REG_B_DM 0x04 -#define REG_B_24H 0x02 - -#define REG_C_UF 0x10 -#define REG_C_IRQF 0x80 -#define REG_C_PF 0x40 -#define REG_C_AF 0x20 -#define REG_C_MASK 0x70 - -static inline uint32_t periodic_period_to_clock(int period_code) -{ - if (!period_code) { - return 0; - } - - if (period_code <= 2) { - period_code += 7; - } - /* period in 32 Khz cycles */ - return 1 << (period_code - 1); -} - -#define RTC_CLOCK_RATE 32768 - -static inline int64_t periodic_clock_to_ns(int64_t clocks) -{ - return muldiv64(clocks, NANOSECONDS_PER_SECOND, RTC_CLOCK_RATE); -} - -#endif diff --git a/tests/rtc-test.c b/tests/rtc-test.c index 6309b0ef6c..79a4ff1ed6 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -15,7 +15,7 @@ #include "libqtest-single.h" #include "qemu/timer.h" -#include "hw/timer/mc146818rtc_regs.h" +#include "hw/rtc/mc146818rtc_regs.h" #define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) -- cgit v1.2.3-55-g7522 From 819ce6b2a598b5c5c6866a3a6d1acb5261592f8a Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Fri, 4 Oct 2019 01:03:54 +0200 Subject: hw: Move M48T59 device from hw/timer/ to hw/rtc/ subdirectory The M48T59 is a Real Time Clock, not a timer. Move it under the hw/rtc/ subdirectory. Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191003230404.19384-5-philmd@redhat.com> Signed-off-by: Laurent Vivier --- MAINTAINERS | 4 +- hw/ppc/ppc405_boards.c | 2 +- hw/ppc/prep.c | 2 +- hw/rtc/Kconfig | 3 + hw/rtc/Makefile.objs | 4 + hw/rtc/m48t59-internal.h | 80 +++++ hw/rtc/m48t59-isa.c | 184 ++++++++++++ hw/rtc/m48t59.c | 723 +++++++++++++++++++++++++++++++++++++++++++++ hw/sparc/sun4m.c | 2 +- hw/sparc64/sun4u.c | 2 +- hw/timer/Kconfig | 3 - hw/timer/Makefile.objs | 4 - hw/timer/m48t59-internal.h | 80 ----- hw/timer/m48t59-isa.c | 184 ------------ hw/timer/m48t59.c | 723 --------------------------------------------- include/hw/rtc/m48t59.h | 57 ++++ include/hw/timer/m48t59.h | 32 -- 17 files changed, 1057 insertions(+), 1032 deletions(-) create mode 100644 hw/rtc/m48t59-internal.h create mode 100644 hw/rtc/m48t59-isa.c create mode 100644 hw/rtc/m48t59.c delete mode 100644 hw/timer/m48t59-internal.h delete mode 100644 hw/timer/m48t59-isa.c delete mode 100644 hw/timer/m48t59.c create mode 100644 include/hw/rtc/m48t59.h delete mode 100644 include/hw/timer/m48t59.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 7eba146444..4e65f062f2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1064,9 +1064,9 @@ F: hw/pci-host/prep.[hc] F: hw/isa/i82378.c F: hw/isa/pc87312.c F: hw/dma/i82374.c -F: hw/timer/m48t59-isa.c +F: hw/rtc/m48t59-isa.c F: include/hw/isa/pc87312.h -F: include/hw/timer/m48t59.h +F: include/hw/rtc/m48t59.h F: pc-bios/ppc_rom.bin sPAPR diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 388cae0b43..1f721feed6 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -29,7 +29,7 @@ #include "cpu.h" #include "hw/ppc/ppc.h" #include "ppc405.h" -#include "hw/timer/m48t59.h" +#include "hw/rtc/m48t59.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 3a51536e1a..862345c2ac 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hw/timer/m48t59.h" +#include "hw/rtc/m48t59.h" #include "hw/char/serial.h" #include "hw/block/fdc.h" #include "net/net.h" diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index 7ffd702268..159c233517 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -1,3 +1,6 @@ +config M48T59 + bool + config PL031 bool diff --git a/hw/rtc/Makefile.objs b/hw/rtc/Makefile.objs index 3cac0d5a63..c87f81405e 100644 --- a/hw/rtc/Makefile.objs +++ b/hw/rtc/Makefile.objs @@ -1,2 +1,6 @@ +common-obj-$(CONFIG_M48T59) += m48t59.o +ifeq ($(CONFIG_ISA_BUS),y) +common-obj-$(CONFIG_M48T59) += m48t59-isa.o +endif common-obj-$(CONFIG_PL031) += pl031.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o diff --git a/hw/rtc/m48t59-internal.h b/hw/rtc/m48t59-internal.h new file mode 100644 index 0000000000..4d4f2a6fed --- /dev/null +++ b/hw/rtc/m48t59-internal.h @@ -0,0 +1,80 @@ +/* + * QEMU M48T59 and M48T08 NVRAM emulation (common header) + * + * Copyright (c) 2003-2005, 2007 Jocelyn Mayer + * Copyright (c) 2013 Hervé Poussineau + * + * 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. + */ + +#ifndef HW_M48T59_INTERNAL_H +#define HW_M48T59_INTERNAL_H + +#define M48T59_DEBUG 0 + +#define NVRAM_PRINTF(fmt, ...) do { \ + if (M48T59_DEBUG) { printf(fmt , ## __VA_ARGS__); } } while (0) + +/* + * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has + * alarm and a watchdog timer and related control registers. In the + * PPC platform there is also a nvram lock function. + */ + +typedef struct M48txxInfo { + const char *bus_name; + uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */ + uint32_t size; +} M48txxInfo; + +typedef struct M48t59State { + /* Hardware parameters */ + qemu_irq IRQ; + MemoryRegion iomem; + uint32_t size; + int32_t base_year; + /* RTC management */ + time_t time_offset; + time_t stop_time; + /* Alarm & watchdog */ + struct tm alarm; + QEMUTimer *alrm_timer; + QEMUTimer *wd_timer; + /* NVRAM storage */ + uint8_t *buffer; + /* Model parameters */ + uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */ + /* NVRAM storage */ + uint16_t addr; + uint8_t lock; +} M48t59State; + +uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr); +void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val); +void m48t59_reset_common(M48t59State *NVRAM); +void m48t59_realize_common(M48t59State *s, Error **errp); + +static inline void m48t59_toggle_lock(M48t59State *NVRAM, int lock) +{ + NVRAM->lock ^= 1 << lock; +} + +extern const MemoryRegionOps m48t59_io_ops; + +#endif /* HW_M48T59_INTERNAL_H */ diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c new file mode 100644 index 0000000000..7fde854c0f --- /dev/null +++ b/hw/rtc/m48t59-isa.c @@ -0,0 +1,184 @@ +/* + * QEMU M48T59 and M48T08 NVRAM emulation (ISA bus interface) + * + * Copyright (c) 2003-2005, 2007 Jocelyn Mayer + * Copyright (c) 2013 Hervé Poussineau + * + * 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 "qemu/osdep.h" +#include "hw/isa/isa.h" +#include "hw/qdev-properties.h" +#include "hw/rtc/m48t59.h" +#include "m48t59-internal.h" +#include "qemu/module.h" + +#define TYPE_M48TXX_ISA "isa-m48txx" +#define M48TXX_ISA_GET_CLASS(obj) \ + OBJECT_GET_CLASS(M48txxISADeviceClass, (obj), TYPE_M48TXX_ISA) +#define M48TXX_ISA_CLASS(klass) \ + OBJECT_CLASS_CHECK(M48txxISADeviceClass, (klass), TYPE_M48TXX_ISA) +#define M48TXX_ISA(obj) \ + OBJECT_CHECK(M48txxISAState, (obj), TYPE_M48TXX_ISA) + +typedef struct M48txxISAState { + ISADevice parent_obj; + M48t59State state; + uint32_t io_base; + MemoryRegion io; +} M48txxISAState; + +typedef struct M48txxISADeviceClass { + ISADeviceClass parent_class; + M48txxInfo info; +} M48txxISADeviceClass; + +static M48txxInfo m48txx_isa_info[] = { + { + .bus_name = "isa-m48t59", + .model = 59, + .size = 0x2000, + } +}; + +Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, + int base_year, int model) +{ + DeviceState *dev; + int i; + + for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) { + if (m48txx_isa_info[i].size != size || + m48txx_isa_info[i].model != model) { + continue; + } + + dev = DEVICE(isa_create(bus, m48txx_isa_info[i].bus_name)); + qdev_prop_set_uint32(dev, "iobase", io_base); + qdev_prop_set_int32(dev, "base-year", base_year); + qdev_init_nofail(dev); + return NVRAM(dev); + } + + assert(false); + return NULL; +} + +static uint32_t m48txx_isa_read(Nvram *obj, uint32_t addr) +{ + M48txxISAState *d = M48TXX_ISA(obj); + return m48t59_read(&d->state, addr); +} + +static void m48txx_isa_write(Nvram *obj, uint32_t addr, uint32_t val) +{ + M48txxISAState *d = M48TXX_ISA(obj); + m48t59_write(&d->state, addr, val); +} + +static void m48txx_isa_toggle_lock(Nvram *obj, int lock) +{ + M48txxISAState *d = M48TXX_ISA(obj); + m48t59_toggle_lock(&d->state, lock); +} + +static Property m48t59_isa_properties[] = { + DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0), + DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74), + DEFINE_PROP_END_OF_LIST(), +}; + +static void m48t59_reset_isa(DeviceState *d) +{ + M48txxISAState *isa = M48TXX_ISA(d); + M48t59State *NVRAM = &isa->state; + + m48t59_reset_common(NVRAM); +} + +static void m48t59_isa_realize(DeviceState *dev, Error **errp) +{ + M48txxISADeviceClass *u = M48TXX_ISA_GET_CLASS(dev); + ISADevice *isadev = ISA_DEVICE(dev); + M48txxISAState *d = M48TXX_ISA(dev); + M48t59State *s = &d->state; + + s->model = u->info.model; + s->size = u->info.size; + isa_init_irq(isadev, &s->IRQ, 8); + m48t59_realize_common(s, errp); + memory_region_init_io(&d->io, OBJECT(dev), &m48t59_io_ops, s, "m48t59", 4); + if (d->io_base != 0) { + isa_register_ioport(isadev, &d->io, d->io_base); + } +} + +static void m48txx_isa_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + NvramClass *nc = NVRAM_CLASS(klass); + + dc->realize = m48t59_isa_realize; + dc->reset = m48t59_reset_isa; + dc->props = m48t59_isa_properties; + nc->read = m48txx_isa_read; + nc->write = m48txx_isa_write; + nc->toggle_lock = m48txx_isa_toggle_lock; +} + +static void m48txx_isa_concrete_class_init(ObjectClass *klass, void *data) +{ + M48txxISADeviceClass *u = M48TXX_ISA_CLASS(klass); + M48txxInfo *info = data; + + u->info = *info; +} + +static const TypeInfo m48txx_isa_type_info = { + .name = TYPE_M48TXX_ISA, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(M48txxISAState), + .abstract = true, + .class_init = m48txx_isa_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NVRAM }, + { } + } +}; + +static void m48t59_isa_register_types(void) +{ + TypeInfo isa_type_info = { + .parent = TYPE_M48TXX_ISA, + .class_size = sizeof(M48txxISADeviceClass), + .class_init = m48txx_isa_concrete_class_init, + }; + int i; + + type_register_static(&m48txx_isa_type_info); + + for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) { + isa_type_info.name = m48txx_isa_info[i].bus_name; + isa_type_info.class_data = &m48txx_isa_info[i]; + type_register(&isa_type_info); + } +} + +type_init(m48t59_isa_register_types) diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c new file mode 100644 index 0000000000..fc592b9fb1 --- /dev/null +++ b/hw/rtc/m48t59.c @@ -0,0 +1,723 @@ +/* + * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms + * + * Copyright (c) 2003-2005, 2007, 2017 Jocelyn Mayer + * Copyright (c) 2013 Hervé Poussineau + * + * 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 "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/rtc/m48t59.h" +#include "qemu/timer.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "exec/address-spaces.h" +#include "qemu/bcd.h" +#include "qemu/module.h" + +#include "m48t59-internal.h" +#include "migration/vmstate.h" + +#define TYPE_M48TXX_SYS_BUS "sysbus-m48txx" +#define M48TXX_SYS_BUS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(M48txxSysBusDeviceClass, (obj), TYPE_M48TXX_SYS_BUS) +#define M48TXX_SYS_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(M48txxSysBusDeviceClass, (klass), TYPE_M48TXX_SYS_BUS) +#define M48TXX_SYS_BUS(obj) \ + OBJECT_CHECK(M48txxSysBusState, (obj), TYPE_M48TXX_SYS_BUS) + +/* + * Chipset docs: + * http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf + * http://www.st.com/stonline/products/literature/ds/2411/m48t08.pdf + * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf + */ + +typedef struct M48txxSysBusState { + SysBusDevice parent_obj; + M48t59State state; + MemoryRegion io; +} M48txxSysBusState; + +typedef struct M48txxSysBusDeviceClass { + SysBusDeviceClass parent_class; + M48txxInfo info; +} M48txxSysBusDeviceClass; + +static M48txxInfo m48txx_sysbus_info[] = { + { + .bus_name = "sysbus-m48t02", + .model = 2, + .size = 0x800, + },{ + .bus_name = "sysbus-m48t08", + .model = 8, + .size = 0x2000, + },{ + .bus_name = "sysbus-m48t59", + .model = 59, + .size = 0x2000, + } +}; + + +/* Fake timer functions */ + +/* Alarm management */ +static void alarm_cb (void *opaque) +{ + struct tm tm; + uint64_t next_time; + M48t59State *NVRAM = opaque; + + qemu_set_irq(NVRAM->IRQ, 1); + if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && + (NVRAM->buffer[0x1FF4] & 0x80) == 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + /* Repeat once a month */ + qemu_get_timedate(&tm, NVRAM->time_offset); + tm.tm_mon++; + if (tm.tm_mon == 13) { + tm.tm_mon = 1; + tm.tm_year++; + } + next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset; + } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && + (NVRAM->buffer[0x1FF4] & 0x80) == 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + /* Repeat once a day */ + next_time = 24 * 60 * 60; + } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && + (NVRAM->buffer[0x1FF4] & 0x80) != 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + /* Repeat once an hour */ + next_time = 60 * 60; + } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && + (NVRAM->buffer[0x1FF4] & 0x80) != 0 && + (NVRAM->buffer[0x1FF3] & 0x80) != 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + /* Repeat once a minute */ + next_time = 60; + } else { + /* Repeat once a second */ + next_time = 1; + } + timer_mod(NVRAM->alrm_timer, qemu_clock_get_ns(rtc_clock) + + next_time * 1000); + qemu_set_irq(NVRAM->IRQ, 0); +} + +static void set_alarm(M48t59State *NVRAM) +{ + int diff; + if (NVRAM->alrm_timer != NULL) { + timer_del(NVRAM->alrm_timer); + diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset; + if (diff > 0) + timer_mod(NVRAM->alrm_timer, diff * 1000); + } +} + +/* RTC management helpers */ +static inline void get_time(M48t59State *NVRAM, struct tm *tm) +{ + qemu_get_timedate(tm, NVRAM->time_offset); +} + +static void set_time(M48t59State *NVRAM, struct tm *tm) +{ + NVRAM->time_offset = qemu_timedate_diff(tm); + set_alarm(NVRAM); +} + +/* Watchdog management */ +static void watchdog_cb (void *opaque) +{ + M48t59State *NVRAM = opaque; + + NVRAM->buffer[0x1FF0] |= 0x80; + if (NVRAM->buffer[0x1FF7] & 0x80) { + NVRAM->buffer[0x1FF7] = 0x00; + NVRAM->buffer[0x1FFC] &= ~0x40; + /* May it be a hw CPU Reset instead ? */ + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } else { + qemu_set_irq(NVRAM->IRQ, 1); + qemu_set_irq(NVRAM->IRQ, 0); + } +} + +static void set_up_watchdog(M48t59State *NVRAM, uint8_t value) +{ + uint64_t interval; /* in 1/16 seconds */ + + NVRAM->buffer[0x1FF0] &= ~0x80; + if (NVRAM->wd_timer != NULL) { + timer_del(NVRAM->wd_timer); + if (value != 0) { + interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F); + timer_mod(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) + + ((interval * 1000) >> 4)); + } + } +} + +/* Direct access to NVRAM */ +void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) +{ + struct tm tm; + int tmp; + + if (addr > 0x1FF8 && addr < 0x2000) + NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); + + /* check for NVRAM access */ + if ((NVRAM->model == 2 && addr < 0x7f8) || + (NVRAM->model == 8 && addr < 0x1ff8) || + (NVRAM->model == 59 && addr < 0x1ff0)) { + goto do_write; + } + + /* TOD access */ + switch (addr) { + case 0x1FF0: + /* flags register : read-only */ + break; + case 0x1FF1: + /* unused */ + break; + case 0x1FF2: + /* alarm seconds */ + tmp = from_bcd(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + NVRAM->alarm.tm_sec = tmp; + NVRAM->buffer[0x1FF2] = val; + set_alarm(NVRAM); + } + break; + case 0x1FF3: + /* alarm minutes */ + tmp = from_bcd(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + NVRAM->alarm.tm_min = tmp; + NVRAM->buffer[0x1FF3] = val; + set_alarm(NVRAM); + } + break; + case 0x1FF4: + /* alarm hours */ + tmp = from_bcd(val & 0x3F); + if (tmp >= 0 && tmp <= 23) { + NVRAM->alarm.tm_hour = tmp; + NVRAM->buffer[0x1FF4] = val; + set_alarm(NVRAM); + } + break; + case 0x1FF5: + /* alarm date */ + tmp = from_bcd(val & 0x3F); + if (tmp != 0) { + NVRAM->alarm.tm_mday = tmp; + NVRAM->buffer[0x1FF5] = val; + set_alarm(NVRAM); + } + break; + case 0x1FF6: + /* interrupts */ + NVRAM->buffer[0x1FF6] = val; + break; + case 0x1FF7: + /* watchdog */ + NVRAM->buffer[0x1FF7] = val; + set_up_watchdog(NVRAM, val); + break; + case 0x1FF8: + case 0x07F8: + /* control */ + NVRAM->buffer[addr] = (val & ~0xA0) | 0x90; + break; + case 0x1FF9: + case 0x07F9: + /* seconds (BCD) */ + tmp = from_bcd(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_time(NVRAM, &tm); + tm.tm_sec = tmp; + set_time(NVRAM, &tm); + } + if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) { + if (val & 0x80) { + NVRAM->stop_time = time(NULL); + } else { + NVRAM->time_offset += NVRAM->stop_time - time(NULL); + NVRAM->stop_time = 0; + } + } + NVRAM->buffer[addr] = val & 0x80; + break; + case 0x1FFA: + case 0x07FA: + /* minutes (BCD) */ + tmp = from_bcd(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_time(NVRAM, &tm); + tm.tm_min = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFB: + case 0x07FB: + /* hours (BCD) */ + tmp = from_bcd(val & 0x3F); + if (tmp >= 0 && tmp <= 23) { + get_time(NVRAM, &tm); + tm.tm_hour = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFC: + case 0x07FC: + /* day of the week / century */ + tmp = from_bcd(val & 0x07); + get_time(NVRAM, &tm); + tm.tm_wday = tmp; + set_time(NVRAM, &tm); + NVRAM->buffer[addr] = val & 0x40; + break; + case 0x1FFD: + case 0x07FD: + /* date (BCD) */ + tmp = from_bcd(val & 0x3F); + if (tmp != 0) { + get_time(NVRAM, &tm); + tm.tm_mday = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFE: + case 0x07FE: + /* month */ + tmp = from_bcd(val & 0x1F); + if (tmp >= 1 && tmp <= 12) { + get_time(NVRAM, &tm); + tm.tm_mon = tmp - 1; + set_time(NVRAM, &tm); + } + break; + case 0x1FFF: + case 0x07FF: + /* year */ + tmp = from_bcd(val); + if (tmp >= 0 && tmp <= 99) { + get_time(NVRAM, &tm); + tm.tm_year = from_bcd(val) + NVRAM->base_year - 1900; + set_time(NVRAM, &tm); + } + break; + default: + /* Check lock registers state */ + if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) + break; + if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) + break; + do_write: + if (addr < NVRAM->size) { + NVRAM->buffer[addr] = val & 0xFF; + } + break; + } +} + +uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) +{ + struct tm tm; + uint32_t retval = 0xFF; + + /* check for NVRAM access */ + if ((NVRAM->model == 2 && addr < 0x078f) || + (NVRAM->model == 8 && addr < 0x1ff8) || + (NVRAM->model == 59 && addr < 0x1ff0)) { + goto do_read; + } + + /* TOD access */ + switch (addr) { + case 0x1FF0: + /* flags register */ + goto do_read; + case 0x1FF1: + /* unused */ + retval = 0; + break; + case 0x1FF2: + /* alarm seconds */ + goto do_read; + case 0x1FF3: + /* alarm minutes */ + goto do_read; + case 0x1FF4: + /* alarm hours */ + goto do_read; + case 0x1FF5: + /* alarm date */ + goto do_read; + case 0x1FF6: + /* interrupts */ + goto do_read; + case 0x1FF7: + /* A read resets the watchdog */ + set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]); + goto do_read; + case 0x1FF8: + case 0x07F8: + /* control */ + goto do_read; + case 0x1FF9: + case 0x07F9: + /* seconds (BCD) */ + get_time(NVRAM, &tm); + retval = (NVRAM->buffer[addr] & 0x80) | to_bcd(tm.tm_sec); + break; + case 0x1FFA: + case 0x07FA: + /* minutes (BCD) */ + get_time(NVRAM, &tm); + retval = to_bcd(tm.tm_min); + break; + case 0x1FFB: + case 0x07FB: + /* hours (BCD) */ + get_time(NVRAM, &tm); + retval = to_bcd(tm.tm_hour); + break; + case 0x1FFC: + case 0x07FC: + /* day of the week / century */ + get_time(NVRAM, &tm); + retval = NVRAM->buffer[addr] | tm.tm_wday; + break; + case 0x1FFD: + case 0x07FD: + /* date */ + get_time(NVRAM, &tm); + retval = to_bcd(tm.tm_mday); + break; + case 0x1FFE: + case 0x07FE: + /* month */ + get_time(NVRAM, &tm); + retval = to_bcd(tm.tm_mon + 1); + break; + case 0x1FFF: + case 0x07FF: + /* year */ + get_time(NVRAM, &tm); + retval = to_bcd((tm.tm_year + 1900 - NVRAM->base_year) % 100); + break; + default: + /* Check lock registers state */ + if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) + break; + if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) + break; + do_read: + if (addr < NVRAM->size) { + retval = NVRAM->buffer[addr]; + } + break; + } + if (addr > 0x1FF9 && addr < 0x2000) + NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); + + return retval; +} + +/* IO access to NVRAM */ +static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + M48t59State *NVRAM = opaque; + + NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" => 0x%"PRIx64"\n", __func__, addr, val); + switch (addr) { + case 0: + NVRAM->addr &= ~0x00FF; + NVRAM->addr |= val; + break; + case 1: + NVRAM->addr &= ~0xFF00; + NVRAM->addr |= val << 8; + break; + case 3: + m48t59_write(NVRAM, NVRAM->addr, val); + NVRAM->addr = 0x0000; + break; + default: + break; + } +} + +static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) +{ + M48t59State *NVRAM = opaque; + uint32_t retval; + + switch (addr) { + case 3: + retval = m48t59_read(NVRAM, NVRAM->addr); + break; + default: + retval = -1; + break; + } + NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" <= 0x%08x\n", __func__, addr, retval); + + return retval; +} + +static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size) +{ + M48t59State *NVRAM = opaque; + + return m48t59_read(NVRAM, addr); +} + +static void nvram_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + M48t59State *NVRAM = opaque; + + return m48t59_write(NVRAM, addr, value); +} + +static const MemoryRegionOps nvram_ops = { + .read = nvram_read, + .write = nvram_write, + .impl.min_access_size = 1, + .impl.max_access_size = 1, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static const VMStateDescription vmstate_m48t59 = { + .name = "m48t59", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(lock, M48t59State), + VMSTATE_UINT16(addr, M48t59State), + VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, size), + VMSTATE_END_OF_LIST() + } +}; + +void m48t59_reset_common(M48t59State *NVRAM) +{ + NVRAM->addr = 0; + NVRAM->lock = 0; + if (NVRAM->alrm_timer != NULL) + timer_del(NVRAM->alrm_timer); + + if (NVRAM->wd_timer != NULL) + timer_del(NVRAM->wd_timer); +} + +static void m48t59_reset_sysbus(DeviceState *d) +{ + M48txxSysBusState *sys = M48TXX_SYS_BUS(d); + M48t59State *NVRAM = &sys->state; + + m48t59_reset_common(NVRAM); +} + +const MemoryRegionOps m48t59_io_ops = { + .read = NVRAM_readb, + .write = NVRAM_writeb, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* Initialisation routine */ +Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, + uint32_t io_base, uint16_t size, int base_year, + int model) +{ + DeviceState *dev; + SysBusDevice *s; + int i; + + for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) { + if (m48txx_sysbus_info[i].size != size || + m48txx_sysbus_info[i].model != model) { + continue; + } + + dev = qdev_create(NULL, m48txx_sysbus_info[i].bus_name); + qdev_prop_set_int32(dev, "base-year", base_year); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(s, 0, IRQ); + if (io_base != 0) { + memory_region_add_subregion(get_system_io(), io_base, + sysbus_mmio_get_region(s, 1)); + } + if (mem_base != 0) { + sysbus_mmio_map(s, 0, mem_base); + } + + return NVRAM(s); + } + + assert(false); + return NULL; +} + +void m48t59_realize_common(M48t59State *s, Error **errp) +{ + s->buffer = g_malloc0(s->size); + if (s->model == 59) { + s->alrm_timer = timer_new_ns(rtc_clock, &alarm_cb, s); + s->wd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &watchdog_cb, s); + } + qemu_get_timedate(&s->alarm, 0); +} + +static void m48t59_init1(Object *obj) +{ + M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(obj); + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + M48t59State *s = &d->state; + + s->model = u->info.model; + s->size = u->info.size; + sysbus_init_irq(dev, &s->IRQ); + + memory_region_init_io(&s->iomem, obj, &nvram_ops, s, "m48t59.nvram", + s->size); + memory_region_init_io(&d->io, obj, &m48t59_io_ops, s, "m48t59", 4); +} + +static void m48t59_realize(DeviceState *dev, Error **errp) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(dev); + M48t59State *s = &d->state; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_mmio(sbd, &d->io); + m48t59_realize_common(s, errp); +} + +static uint32_t m48txx_sysbus_read(Nvram *obj, uint32_t addr) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + return m48t59_read(&d->state, addr); +} + +static void m48txx_sysbus_write(Nvram *obj, uint32_t addr, uint32_t val) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + m48t59_write(&d->state, addr, val); +} + +static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + m48t59_toggle_lock(&d->state, lock); +} + +static Property m48t59_sysbus_properties[] = { + DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + NvramClass *nc = NVRAM_CLASS(klass); + + dc->realize = m48t59_realize; + dc->reset = m48t59_reset_sysbus; + dc->props = m48t59_sysbus_properties; + dc->vmsd = &vmstate_m48t59; + nc->read = m48txx_sysbus_read; + nc->write = m48txx_sysbus_write; + nc->toggle_lock = m48txx_sysbus_toggle_lock; +} + +static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data) +{ + M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass); + M48txxInfo *info = data; + + u->info = *info; +} + +static const TypeInfo nvram_info = { + .name = TYPE_NVRAM, + .parent = TYPE_INTERFACE, + .class_size = sizeof(NvramClass), +}; + +static const TypeInfo m48txx_sysbus_type_info = { + .name = TYPE_M48TXX_SYS_BUS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(M48txxSysBusState), + .instance_init = m48t59_init1, + .abstract = true, + .class_init = m48txx_sysbus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NVRAM }, + { } + } +}; + +static void m48t59_register_types(void) +{ + TypeInfo sysbus_type_info = { + .parent = TYPE_M48TXX_SYS_BUS, + .class_size = sizeof(M48txxSysBusDeviceClass), + .class_init = m48txx_sysbus_concrete_class_init, + }; + int i; + + type_register_static(&nvram_info); + type_register_static(&m48txx_sysbus_type_info); + + for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) { + sysbus_type_info.name = m48txx_sysbus_info[i].bus_name; + sysbus_type_info.class_data = &m48txx_sysbus_info[i]; + type_register(&sysbus_type_info); + } +} + +type_init(m48t59_register_types) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 6c5a17a020..2aaa5bf1ae 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -31,7 +31,7 @@ #include "qemu/error-report.h" #include "qemu/timer.h" #include "hw/sparc/sun4m_iommu.h" -#include "hw/timer/m48t59.h" +#include "hw/rtc/m48t59.h" #include "migration/vmstate.h" #include "hw/sparc/sparc32_dma.h" #include "hw/block/fdc.h" diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 1ded2a4c9a..955082773b 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -36,7 +36,7 @@ #include "hw/pci-host/sabre.h" #include "hw/char/serial.h" #include "hw/char/parallel.h" -#include "hw/timer/m48t59.h" +#include "hw/rtc/m48t59.h" #include "migration/vmstate.h" #include "hw/input/i8042.h" #include "hw/block/fdc.h" diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index af415c8ef8..a57e9b59fc 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -24,9 +24,6 @@ config M41T80 bool depends on I2C -config M48T59 - bool - config TWL92230 bool depends on I2C diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index b0159189cf..fe2d1fbc40 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -7,10 +7,6 @@ common-obj-$(CONFIG_DS1338) += ds1338.o common-obj-$(CONFIG_HPET) += hpet.o common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o common-obj-$(CONFIG_M41T80) += m41t80.o -common-obj-$(CONFIG_M48T59) += m48t59.o -ifeq ($(CONFIG_ISA_BUS),y) -common-obj-$(CONFIG_M48T59) += m48t59-isa.o -endif common-obj-$(CONFIG_PUV3) += puv3_ost.o common-obj-$(CONFIG_TWL92230) += twl92230.o common-obj-$(CONFIG_XILINX) += xilinx_timer.o diff --git a/hw/timer/m48t59-internal.h b/hw/timer/m48t59-internal.h deleted file mode 100644 index 4d4f2a6fed..0000000000 --- a/hw/timer/m48t59-internal.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * QEMU M48T59 and M48T08 NVRAM emulation (common header) - * - * Copyright (c) 2003-2005, 2007 Jocelyn Mayer - * Copyright (c) 2013 Hervé Poussineau - * - * 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. - */ - -#ifndef HW_M48T59_INTERNAL_H -#define HW_M48T59_INTERNAL_H - -#define M48T59_DEBUG 0 - -#define NVRAM_PRINTF(fmt, ...) do { \ - if (M48T59_DEBUG) { printf(fmt , ## __VA_ARGS__); } } while (0) - -/* - * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has - * alarm and a watchdog timer and related control registers. In the - * PPC platform there is also a nvram lock function. - */ - -typedef struct M48txxInfo { - const char *bus_name; - uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */ - uint32_t size; -} M48txxInfo; - -typedef struct M48t59State { - /* Hardware parameters */ - qemu_irq IRQ; - MemoryRegion iomem; - uint32_t size; - int32_t base_year; - /* RTC management */ - time_t time_offset; - time_t stop_time; - /* Alarm & watchdog */ - struct tm alarm; - QEMUTimer *alrm_timer; - QEMUTimer *wd_timer; - /* NVRAM storage */ - uint8_t *buffer; - /* Model parameters */ - uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */ - /* NVRAM storage */ - uint16_t addr; - uint8_t lock; -} M48t59State; - -uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr); -void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val); -void m48t59_reset_common(M48t59State *NVRAM); -void m48t59_realize_common(M48t59State *s, Error **errp); - -static inline void m48t59_toggle_lock(M48t59State *NVRAM, int lock) -{ - NVRAM->lock ^= 1 << lock; -} - -extern const MemoryRegionOps m48t59_io_ops; - -#endif /* HW_M48T59_INTERNAL_H */ diff --git a/hw/timer/m48t59-isa.c b/hw/timer/m48t59-isa.c deleted file mode 100644 index 5e5432abfd..0000000000 --- a/hw/timer/m48t59-isa.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * QEMU M48T59 and M48T08 NVRAM emulation (ISA bus interface - * - * Copyright (c) 2003-2005, 2007 Jocelyn Mayer - * Copyright (c) 2013 Hervé Poussineau - * - * 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 "qemu/osdep.h" -#include "hw/isa/isa.h" -#include "hw/qdev-properties.h" -#include "hw/timer/m48t59.h" -#include "m48t59-internal.h" -#include "qemu/module.h" - -#define TYPE_M48TXX_ISA "isa-m48txx" -#define M48TXX_ISA_GET_CLASS(obj) \ - OBJECT_GET_CLASS(M48txxISADeviceClass, (obj), TYPE_M48TXX_ISA) -#define M48TXX_ISA_CLASS(klass) \ - OBJECT_CLASS_CHECK(M48txxISADeviceClass, (klass), TYPE_M48TXX_ISA) -#define M48TXX_ISA(obj) \ - OBJECT_CHECK(M48txxISAState, (obj), TYPE_M48TXX_ISA) - -typedef struct M48txxISAState { - ISADevice parent_obj; - M48t59State state; - uint32_t io_base; - MemoryRegion io; -} M48txxISAState; - -typedef struct M48txxISADeviceClass { - ISADeviceClass parent_class; - M48txxInfo info; -} M48txxISADeviceClass; - -static M48txxInfo m48txx_isa_info[] = { - { - .bus_name = "isa-m48t59", - .model = 59, - .size = 0x2000, - } -}; - -Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, - int base_year, int model) -{ - DeviceState *dev; - int i; - - for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) { - if (m48txx_isa_info[i].size != size || - m48txx_isa_info[i].model != model) { - continue; - } - - dev = DEVICE(isa_create(bus, m48txx_isa_info[i].bus_name)); - qdev_prop_set_uint32(dev, "iobase", io_base); - qdev_prop_set_int32(dev, "base-year", base_year); - qdev_init_nofail(dev); - return NVRAM(dev); - } - - assert(false); - return NULL; -} - -static uint32_t m48txx_isa_read(Nvram *obj, uint32_t addr) -{ - M48txxISAState *d = M48TXX_ISA(obj); - return m48t59_read(&d->state, addr); -} - -static void m48txx_isa_write(Nvram *obj, uint32_t addr, uint32_t val) -{ - M48txxISAState *d = M48TXX_ISA(obj); - m48t59_write(&d->state, addr, val); -} - -static void m48txx_isa_toggle_lock(Nvram *obj, int lock) -{ - M48txxISAState *d = M48TXX_ISA(obj); - m48t59_toggle_lock(&d->state, lock); -} - -static Property m48t59_isa_properties[] = { - DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0), - DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74), - DEFINE_PROP_END_OF_LIST(), -}; - -static void m48t59_reset_isa(DeviceState *d) -{ - M48txxISAState *isa = M48TXX_ISA(d); - M48t59State *NVRAM = &isa->state; - - m48t59_reset_common(NVRAM); -} - -static void m48t59_isa_realize(DeviceState *dev, Error **errp) -{ - M48txxISADeviceClass *u = M48TXX_ISA_GET_CLASS(dev); - ISADevice *isadev = ISA_DEVICE(dev); - M48txxISAState *d = M48TXX_ISA(dev); - M48t59State *s = &d->state; - - s->model = u->info.model; - s->size = u->info.size; - isa_init_irq(isadev, &s->IRQ, 8); - m48t59_realize_common(s, errp); - memory_region_init_io(&d->io, OBJECT(dev), &m48t59_io_ops, s, "m48t59", 4); - if (d->io_base != 0) { - isa_register_ioport(isadev, &d->io, d->io_base); - } -} - -static void m48txx_isa_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - NvramClass *nc = NVRAM_CLASS(klass); - - dc->realize = m48t59_isa_realize; - dc->reset = m48t59_reset_isa; - dc->props = m48t59_isa_properties; - nc->read = m48txx_isa_read; - nc->write = m48txx_isa_write; - nc->toggle_lock = m48txx_isa_toggle_lock; -} - -static void m48txx_isa_concrete_class_init(ObjectClass *klass, void *data) -{ - M48txxISADeviceClass *u = M48TXX_ISA_CLASS(klass); - M48txxInfo *info = data; - - u->info = *info; -} - -static const TypeInfo m48txx_isa_type_info = { - .name = TYPE_M48TXX_ISA, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(M48txxISAState), - .abstract = true, - .class_init = m48txx_isa_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_NVRAM }, - { } - } -}; - -static void m48t59_isa_register_types(void) -{ - TypeInfo isa_type_info = { - .parent = TYPE_M48TXX_ISA, - .class_size = sizeof(M48txxISADeviceClass), - .class_init = m48txx_isa_concrete_class_init, - }; - int i; - - type_register_static(&m48txx_isa_type_info); - - for (i = 0; i < ARRAY_SIZE(m48txx_isa_info); i++) { - isa_type_info.name = m48txx_isa_info[i].bus_name; - isa_type_info.class_data = &m48txx_isa_info[i]; - type_register(&isa_type_info); - } -} - -type_init(m48t59_isa_register_types) diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c deleted file mode 100644 index a9fc2f981a..0000000000 --- a/hw/timer/m48t59.c +++ /dev/null @@ -1,723 +0,0 @@ -/* - * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms - * - * Copyright (c) 2003-2005, 2007, 2017 Jocelyn Mayer - * Copyright (c) 2013 Hervé Poussineau - * - * 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 "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/timer/m48t59.h" -#include "qemu/timer.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" -#include "qemu/bcd.h" -#include "qemu/module.h" - -#include "m48t59-internal.h" -#include "migration/vmstate.h" - -#define TYPE_M48TXX_SYS_BUS "sysbus-m48txx" -#define M48TXX_SYS_BUS_GET_CLASS(obj) \ - OBJECT_GET_CLASS(M48txxSysBusDeviceClass, (obj), TYPE_M48TXX_SYS_BUS) -#define M48TXX_SYS_BUS_CLASS(klass) \ - OBJECT_CLASS_CHECK(M48txxSysBusDeviceClass, (klass), TYPE_M48TXX_SYS_BUS) -#define M48TXX_SYS_BUS(obj) \ - OBJECT_CHECK(M48txxSysBusState, (obj), TYPE_M48TXX_SYS_BUS) - -/* - * Chipset docs: - * http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf - * http://www.st.com/stonline/products/literature/ds/2411/m48t08.pdf - * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf - */ - -typedef struct M48txxSysBusState { - SysBusDevice parent_obj; - M48t59State state; - MemoryRegion io; -} M48txxSysBusState; - -typedef struct M48txxSysBusDeviceClass { - SysBusDeviceClass parent_class; - M48txxInfo info; -} M48txxSysBusDeviceClass; - -static M48txxInfo m48txx_sysbus_info[] = { - { - .bus_name = "sysbus-m48t02", - .model = 2, - .size = 0x800, - },{ - .bus_name = "sysbus-m48t08", - .model = 8, - .size = 0x2000, - },{ - .bus_name = "sysbus-m48t59", - .model = 59, - .size = 0x2000, - } -}; - - -/* Fake timer functions */ - -/* Alarm management */ -static void alarm_cb (void *opaque) -{ - struct tm tm; - uint64_t next_time; - M48t59State *NVRAM = opaque; - - qemu_set_irq(NVRAM->IRQ, 1); - if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && - (NVRAM->buffer[0x1FF4] & 0x80) == 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { - /* Repeat once a month */ - qemu_get_timedate(&tm, NVRAM->time_offset); - tm.tm_mon++; - if (tm.tm_mon == 13) { - tm.tm_mon = 1; - tm.tm_year++; - } - next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset; - } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) == 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { - /* Repeat once a day */ - next_time = 24 * 60 * 60; - } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) != 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { - /* Repeat once an hour */ - next_time = 60 * 60; - } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) != 0 && - (NVRAM->buffer[0x1FF3] & 0x80) != 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { - /* Repeat once a minute */ - next_time = 60; - } else { - /* Repeat once a second */ - next_time = 1; - } - timer_mod(NVRAM->alrm_timer, qemu_clock_get_ns(rtc_clock) + - next_time * 1000); - qemu_set_irq(NVRAM->IRQ, 0); -} - -static void set_alarm(M48t59State *NVRAM) -{ - int diff; - if (NVRAM->alrm_timer != NULL) { - timer_del(NVRAM->alrm_timer); - diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset; - if (diff > 0) - timer_mod(NVRAM->alrm_timer, diff * 1000); - } -} - -/* RTC management helpers */ -static inline void get_time(M48t59State *NVRAM, struct tm *tm) -{ - qemu_get_timedate(tm, NVRAM->time_offset); -} - -static void set_time(M48t59State *NVRAM, struct tm *tm) -{ - NVRAM->time_offset = qemu_timedate_diff(tm); - set_alarm(NVRAM); -} - -/* Watchdog management */ -static void watchdog_cb (void *opaque) -{ - M48t59State *NVRAM = opaque; - - NVRAM->buffer[0x1FF0] |= 0x80; - if (NVRAM->buffer[0x1FF7] & 0x80) { - NVRAM->buffer[0x1FF7] = 0x00; - NVRAM->buffer[0x1FFC] &= ~0x40; - /* May it be a hw CPU Reset instead ? */ - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } else { - qemu_set_irq(NVRAM->IRQ, 1); - qemu_set_irq(NVRAM->IRQ, 0); - } -} - -static void set_up_watchdog(M48t59State *NVRAM, uint8_t value) -{ - uint64_t interval; /* in 1/16 seconds */ - - NVRAM->buffer[0x1FF0] &= ~0x80; - if (NVRAM->wd_timer != NULL) { - timer_del(NVRAM->wd_timer); - if (value != 0) { - interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F); - timer_mod(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) + - ((interval * 1000) >> 4)); - } - } -} - -/* Direct access to NVRAM */ -void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) -{ - struct tm tm; - int tmp; - - if (addr > 0x1FF8 && addr < 0x2000) - NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); - - /* check for NVRAM access */ - if ((NVRAM->model == 2 && addr < 0x7f8) || - (NVRAM->model == 8 && addr < 0x1ff8) || - (NVRAM->model == 59 && addr < 0x1ff0)) { - goto do_write; - } - - /* TOD access */ - switch (addr) { - case 0x1FF0: - /* flags register : read-only */ - break; - case 0x1FF1: - /* unused */ - break; - case 0x1FF2: - /* alarm seconds */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - NVRAM->alarm.tm_sec = tmp; - NVRAM->buffer[0x1FF2] = val; - set_alarm(NVRAM); - } - break; - case 0x1FF3: - /* alarm minutes */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - NVRAM->alarm.tm_min = tmp; - NVRAM->buffer[0x1FF3] = val; - set_alarm(NVRAM); - } - break; - case 0x1FF4: - /* alarm hours */ - tmp = from_bcd(val & 0x3F); - if (tmp >= 0 && tmp <= 23) { - NVRAM->alarm.tm_hour = tmp; - NVRAM->buffer[0x1FF4] = val; - set_alarm(NVRAM); - } - break; - case 0x1FF5: - /* alarm date */ - tmp = from_bcd(val & 0x3F); - if (tmp != 0) { - NVRAM->alarm.tm_mday = tmp; - NVRAM->buffer[0x1FF5] = val; - set_alarm(NVRAM); - } - break; - case 0x1FF6: - /* interrupts */ - NVRAM->buffer[0x1FF6] = val; - break; - case 0x1FF7: - /* watchdog */ - NVRAM->buffer[0x1FF7] = val; - set_up_watchdog(NVRAM, val); - break; - case 0x1FF8: - case 0x07F8: - /* control */ - NVRAM->buffer[addr] = (val & ~0xA0) | 0x90; - break; - case 0x1FF9: - case 0x07F9: - /* seconds (BCD) */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - get_time(NVRAM, &tm); - tm.tm_sec = tmp; - set_time(NVRAM, &tm); - } - if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) { - if (val & 0x80) { - NVRAM->stop_time = time(NULL); - } else { - NVRAM->time_offset += NVRAM->stop_time - time(NULL); - NVRAM->stop_time = 0; - } - } - NVRAM->buffer[addr] = val & 0x80; - break; - case 0x1FFA: - case 0x07FA: - /* minutes (BCD) */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - get_time(NVRAM, &tm); - tm.tm_min = tmp; - set_time(NVRAM, &tm); - } - break; - case 0x1FFB: - case 0x07FB: - /* hours (BCD) */ - tmp = from_bcd(val & 0x3F); - if (tmp >= 0 && tmp <= 23) { - get_time(NVRAM, &tm); - tm.tm_hour = tmp; - set_time(NVRAM, &tm); - } - break; - case 0x1FFC: - case 0x07FC: - /* day of the week / century */ - tmp = from_bcd(val & 0x07); - get_time(NVRAM, &tm); - tm.tm_wday = tmp; - set_time(NVRAM, &tm); - NVRAM->buffer[addr] = val & 0x40; - break; - case 0x1FFD: - case 0x07FD: - /* date (BCD) */ - tmp = from_bcd(val & 0x3F); - if (tmp != 0) { - get_time(NVRAM, &tm); - tm.tm_mday = tmp; - set_time(NVRAM, &tm); - } - break; - case 0x1FFE: - case 0x07FE: - /* month */ - tmp = from_bcd(val & 0x1F); - if (tmp >= 1 && tmp <= 12) { - get_time(NVRAM, &tm); - tm.tm_mon = tmp - 1; - set_time(NVRAM, &tm); - } - break; - case 0x1FFF: - case 0x07FF: - /* year */ - tmp = from_bcd(val); - if (tmp >= 0 && tmp <= 99) { - get_time(NVRAM, &tm); - tm.tm_year = from_bcd(val) + NVRAM->base_year - 1900; - set_time(NVRAM, &tm); - } - break; - default: - /* Check lock registers state */ - if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) - break; - if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) - break; - do_write: - if (addr < NVRAM->size) { - NVRAM->buffer[addr] = val & 0xFF; - } - break; - } -} - -uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) -{ - struct tm tm; - uint32_t retval = 0xFF; - - /* check for NVRAM access */ - if ((NVRAM->model == 2 && addr < 0x078f) || - (NVRAM->model == 8 && addr < 0x1ff8) || - (NVRAM->model == 59 && addr < 0x1ff0)) { - goto do_read; - } - - /* TOD access */ - switch (addr) { - case 0x1FF0: - /* flags register */ - goto do_read; - case 0x1FF1: - /* unused */ - retval = 0; - break; - case 0x1FF2: - /* alarm seconds */ - goto do_read; - case 0x1FF3: - /* alarm minutes */ - goto do_read; - case 0x1FF4: - /* alarm hours */ - goto do_read; - case 0x1FF5: - /* alarm date */ - goto do_read; - case 0x1FF6: - /* interrupts */ - goto do_read; - case 0x1FF7: - /* A read resets the watchdog */ - set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]); - goto do_read; - case 0x1FF8: - case 0x07F8: - /* control */ - goto do_read; - case 0x1FF9: - case 0x07F9: - /* seconds (BCD) */ - get_time(NVRAM, &tm); - retval = (NVRAM->buffer[addr] & 0x80) | to_bcd(tm.tm_sec); - break; - case 0x1FFA: - case 0x07FA: - /* minutes (BCD) */ - get_time(NVRAM, &tm); - retval = to_bcd(tm.tm_min); - break; - case 0x1FFB: - case 0x07FB: - /* hours (BCD) */ - get_time(NVRAM, &tm); - retval = to_bcd(tm.tm_hour); - break; - case 0x1FFC: - case 0x07FC: - /* day of the week / century */ - get_time(NVRAM, &tm); - retval = NVRAM->buffer[addr] | tm.tm_wday; - break; - case 0x1FFD: - case 0x07FD: - /* date */ - get_time(NVRAM, &tm); - retval = to_bcd(tm.tm_mday); - break; - case 0x1FFE: - case 0x07FE: - /* month */ - get_time(NVRAM, &tm); - retval = to_bcd(tm.tm_mon + 1); - break; - case 0x1FFF: - case 0x07FF: - /* year */ - get_time(NVRAM, &tm); - retval = to_bcd((tm.tm_year + 1900 - NVRAM->base_year) % 100); - break; - default: - /* Check lock registers state */ - if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) - break; - if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) - break; - do_read: - if (addr < NVRAM->size) { - retval = NVRAM->buffer[addr]; - } - break; - } - if (addr > 0x1FF9 && addr < 0x2000) - NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); - - return retval; -} - -/* IO access to NVRAM */ -static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - M48t59State *NVRAM = opaque; - - NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" => 0x%"PRIx64"\n", __func__, addr, val); - switch (addr) { - case 0: - NVRAM->addr &= ~0x00FF; - NVRAM->addr |= val; - break; - case 1: - NVRAM->addr &= ~0xFF00; - NVRAM->addr |= val << 8; - break; - case 3: - m48t59_write(NVRAM, NVRAM->addr, val); - NVRAM->addr = 0x0000; - break; - default: - break; - } -} - -static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) -{ - M48t59State *NVRAM = opaque; - uint32_t retval; - - switch (addr) { - case 3: - retval = m48t59_read(NVRAM, NVRAM->addr); - break; - default: - retval = -1; - break; - } - NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" <= 0x%08x\n", __func__, addr, retval); - - return retval; -} - -static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size) -{ - M48t59State *NVRAM = opaque; - - return m48t59_read(NVRAM, addr); -} - -static void nvram_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - M48t59State *NVRAM = opaque; - - return m48t59_write(NVRAM, addr, value); -} - -static const MemoryRegionOps nvram_ops = { - .read = nvram_read, - .write = nvram_write, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static const VMStateDescription vmstate_m48t59 = { - .name = "m48t59", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(lock, M48t59State), - VMSTATE_UINT16(addr, M48t59State), - VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, size), - VMSTATE_END_OF_LIST() - } -}; - -void m48t59_reset_common(M48t59State *NVRAM) -{ - NVRAM->addr = 0; - NVRAM->lock = 0; - if (NVRAM->alrm_timer != NULL) - timer_del(NVRAM->alrm_timer); - - if (NVRAM->wd_timer != NULL) - timer_del(NVRAM->wd_timer); -} - -static void m48t59_reset_sysbus(DeviceState *d) -{ - M48txxSysBusState *sys = M48TXX_SYS_BUS(d); - M48t59State *NVRAM = &sys->state; - - m48t59_reset_common(NVRAM); -} - -const MemoryRegionOps m48t59_io_ops = { - .read = NVRAM_readb, - .write = NVRAM_writeb, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* Initialisation routine */ -Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, - uint32_t io_base, uint16_t size, int base_year, - int model) -{ - DeviceState *dev; - SysBusDevice *s; - int i; - - for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) { - if (m48txx_sysbus_info[i].size != size || - m48txx_sysbus_info[i].model != model) { - continue; - } - - dev = qdev_create(NULL, m48txx_sysbus_info[i].bus_name); - qdev_prop_set_int32(dev, "base-year", base_year); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, IRQ); - if (io_base != 0) { - memory_region_add_subregion(get_system_io(), io_base, - sysbus_mmio_get_region(s, 1)); - } - if (mem_base != 0) { - sysbus_mmio_map(s, 0, mem_base); - } - - return NVRAM(s); - } - - assert(false); - return NULL; -} - -void m48t59_realize_common(M48t59State *s, Error **errp) -{ - s->buffer = g_malloc0(s->size); - if (s->model == 59) { - s->alrm_timer = timer_new_ns(rtc_clock, &alarm_cb, s); - s->wd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &watchdog_cb, s); - } - qemu_get_timedate(&s->alarm, 0); -} - -static void m48t59_init1(Object *obj) -{ - M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(obj); - M48txxSysBusState *d = M48TXX_SYS_BUS(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - M48t59State *s = &d->state; - - s->model = u->info.model; - s->size = u->info.size; - sysbus_init_irq(dev, &s->IRQ); - - memory_region_init_io(&s->iomem, obj, &nvram_ops, s, "m48t59.nvram", - s->size); - memory_region_init_io(&d->io, obj, &m48t59_io_ops, s, "m48t59", 4); -} - -static void m48t59_realize(DeviceState *dev, Error **errp) -{ - M48txxSysBusState *d = M48TXX_SYS_BUS(dev); - M48t59State *s = &d->state; - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_mmio(sbd, &d->io); - m48t59_realize_common(s, errp); -} - -static uint32_t m48txx_sysbus_read(Nvram *obj, uint32_t addr) -{ - M48txxSysBusState *d = M48TXX_SYS_BUS(obj); - return m48t59_read(&d->state, addr); -} - -static void m48txx_sysbus_write(Nvram *obj, uint32_t addr, uint32_t val) -{ - M48txxSysBusState *d = M48TXX_SYS_BUS(obj); - m48t59_write(&d->state, addr, val); -} - -static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock) -{ - M48txxSysBusState *d = M48TXX_SYS_BUS(obj); - m48t59_toggle_lock(&d->state, lock); -} - -static Property m48t59_sysbus_properties[] = { - DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - NvramClass *nc = NVRAM_CLASS(klass); - - dc->realize = m48t59_realize; - dc->reset = m48t59_reset_sysbus; - dc->props = m48t59_sysbus_properties; - dc->vmsd = &vmstate_m48t59; - nc->read = m48txx_sysbus_read; - nc->write = m48txx_sysbus_write; - nc->toggle_lock = m48txx_sysbus_toggle_lock; -} - -static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data) -{ - M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass); - M48txxInfo *info = data; - - u->info = *info; -} - -static const TypeInfo nvram_info = { - .name = TYPE_NVRAM, - .parent = TYPE_INTERFACE, - .class_size = sizeof(NvramClass), -}; - -static const TypeInfo m48txx_sysbus_type_info = { - .name = TYPE_M48TXX_SYS_BUS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(M48txxSysBusState), - .instance_init = m48t59_init1, - .abstract = true, - .class_init = m48txx_sysbus_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_NVRAM }, - { } - } -}; - -static void m48t59_register_types(void) -{ - TypeInfo sysbus_type_info = { - .parent = TYPE_M48TXX_SYS_BUS, - .class_size = sizeof(M48txxSysBusDeviceClass), - .class_init = m48txx_sysbus_concrete_class_init, - }; - int i; - - type_register_static(&nvram_info); - type_register_static(&m48txx_sysbus_type_info); - - for (i = 0; i < ARRAY_SIZE(m48txx_sysbus_info); i++) { - sysbus_type_info.name = m48txx_sysbus_info[i].bus_name; - sysbus_type_info.class_data = &m48txx_sysbus_info[i]; - type_register(&sysbus_type_info); - } -} - -type_init(m48t59_register_types) diff --git a/include/hw/rtc/m48t59.h b/include/hw/rtc/m48t59.h new file mode 100644 index 0000000000..e7ea4e8761 --- /dev/null +++ b/include/hw/rtc/m48t59.h @@ -0,0 +1,57 @@ +/* + * QEMU M48T59 and M48T08 NVRAM emulation + * + * Copyright (c) 2003-2005, 2007 Jocelyn Mayer + * Copyright (c) 2013 Hervé Poussineau + * + * 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. + */ + +#ifndef HW_RTC_M48T59_H +#define HW_RTC_M48T59_H + +#include "exec/hwaddr.h" +#include "qom/object.h" + +#define TYPE_NVRAM "nvram" + +#define NVRAM_CLASS(klass) \ + OBJECT_CLASS_CHECK(NvramClass, (klass), TYPE_NVRAM) +#define NVRAM_GET_CLASS(obj) \ + OBJECT_GET_CLASS(NvramClass, (obj), TYPE_NVRAM) +#define NVRAM(obj) \ + INTERFACE_CHECK(Nvram, (obj), TYPE_NVRAM) + +typedef struct Nvram Nvram; + +typedef struct NvramClass { + InterfaceClass parent; + + uint32_t (*read)(Nvram *obj, uint32_t addr); + void (*write)(Nvram *obj, uint32_t addr, uint32_t val); + void (*toggle_lock)(Nvram *obj, int lock); +} NvramClass; + +Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, + int base_year, int type); +Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, + uint32_t io_base, uint16_t size, int base_year, + int type); + +#endif /* HW_M48T59_H */ diff --git a/include/hw/timer/m48t59.h b/include/hw/timer/m48t59.h deleted file mode 100644 index f74854c026..0000000000 --- a/include/hw/timer/m48t59.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef HW_M48T59_H -#define HW_M48T59_H - -#include "exec/hwaddr.h" -#include "qom/object.h" - -#define TYPE_NVRAM "nvram" - -#define NVRAM_CLASS(klass) \ - OBJECT_CLASS_CHECK(NvramClass, (klass), TYPE_NVRAM) -#define NVRAM_GET_CLASS(obj) \ - OBJECT_GET_CLASS(NvramClass, (obj), TYPE_NVRAM) -#define NVRAM(obj) \ - INTERFACE_CHECK(Nvram, (obj), TYPE_NVRAM) - -typedef struct Nvram Nvram; - -typedef struct NvramClass { - InterfaceClass parent; - - uint32_t (*read)(Nvram *obj, uint32_t addr); - void (*write)(Nvram *obj, uint32_t addr, uint32_t val); - void (*toggle_lock)(Nvram *obj, int lock); -} NvramClass; - -Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, - int base_year, int type); -Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, - uint32_t io_base, uint16_t size, int base_year, - int type); - -#endif /* HW_M48T59_H */ -- cgit v1.2.3-55-g7522 From 2811ac30596644c1bfc3eee4e2ebffd2a107c3e4 Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Fri, 4 Oct 2019 01:03:56 +0200 Subject: hw: Move sun4v hypervisor RTC from hw/timer/ to hw/rtc/ subdirectory Move RTC devices under the hw/rtc/ subdirectory. Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Artyom Tarasenko Message-Id: <20191003230404.19384-7-philmd@redhat.com> Signed-off-by: Laurent Vivier --- MAINTAINERS | 4 +- hw/rtc/Kconfig | 3 ++ hw/rtc/Makefile.objs | 1 + hw/rtc/sun4v-rtc.c | 95 ++++++++++++++++++++++++++++++++++++++++++++ hw/rtc/trace-events | 4 ++ hw/sparc64/niagara.c | 2 +- hw/timer/Kconfig | 3 -- hw/timer/Makefile.objs | 1 - hw/timer/sun4v-rtc.c | 95 -------------------------------------------- hw/timer/trace-events | 4 -- include/hw/rtc/sun4v-rtc.h | 19 +++++++++ include/hw/timer/sun4v-rtc.h | 1 - 12 files changed, 125 insertions(+), 107 deletions(-) create mode 100644 hw/rtc/sun4v-rtc.c delete mode 100644 hw/timer/sun4v-rtc.c create mode 100644 include/hw/rtc/sun4v-rtc.h delete mode 100644 include/hw/timer/sun4v-rtc.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index ba0d1906aa..a7de5e2d12 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1163,8 +1163,8 @@ Sun4v M: Artyom Tarasenko S: Maintained F: hw/sparc64/niagara.c -F: hw/timer/sun4v-rtc.c -F: include/hw/timer/sun4v-rtc.h +F: hw/rtc/sun4v-rtc.c +F: include/hw/rtc/sun4v-rtc.h Leon3 M: Fabien Chouteau diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index 434b20b2b1..cc7fead764 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -10,3 +10,6 @@ config PL031 config MC146818RTC bool + +config SUN4V_RTC + bool diff --git a/hw/rtc/Makefile.objs b/hw/rtc/Makefile.objs index 89e8e48c64..4621b37bc2 100644 --- a/hw/rtc/Makefile.objs +++ b/hw/rtc/Makefile.objs @@ -5,3 +5,4 @@ common-obj-$(CONFIG_M48T59) += m48t59-isa.o endif common-obj-$(CONFIG_PL031) += pl031.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o +common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o diff --git a/hw/rtc/sun4v-rtc.c b/hw/rtc/sun4v-rtc.c new file mode 100644 index 0000000000..ada01b5774 --- /dev/null +++ b/hw/rtc/sun4v-rtc.c @@ -0,0 +1,95 @@ +/* + * QEMU sun4v Real Time Clock device + * + * The sun4v_rtc device (sun4v tod clock) + * + * Copyright (c) 2016 Artyom Tarasenko + * + * This code is licensed under the GNU GPL v3 or (at your option) any later + * version. + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "hw/rtc/sun4v-rtc.h" +#include "trace.h" + + +#define TYPE_SUN4V_RTC "sun4v_rtc" +#define SUN4V_RTC(obj) OBJECT_CHECK(Sun4vRtc, (obj), TYPE_SUN4V_RTC) + +typedef struct Sun4vRtc { + SysBusDevice parent_obj; + + MemoryRegion iomem; +} Sun4vRtc; + +static uint64_t sun4v_rtc_read(void *opaque, hwaddr addr, + unsigned size) +{ + uint64_t val = get_clock_realtime() / NANOSECONDS_PER_SECOND; + if (!(addr & 4ULL)) { + /* accessing the high 32 bits */ + val >>= 32; + } + trace_sun4v_rtc_read(addr, val); + return val; +} + +static void sun4v_rtc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + trace_sun4v_rtc_write(addr, val); +} + +static const MemoryRegionOps sun4v_rtc_ops = { + .read = sun4v_rtc_read, + .write = sun4v_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +void sun4v_rtc_init(hwaddr addr) +{ + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_create(NULL, TYPE_SUN4V_RTC); + s = SYS_BUS_DEVICE(dev); + + qdev_init_nofail(dev); + + sysbus_mmio_map(s, 0, addr); +} + +static void sun4v_rtc_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Sun4vRtc *s = SUN4V_RTC(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &sun4v_rtc_ops, s, + "sun4v-rtc", 0x08ULL); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void sun4v_rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = sun4v_rtc_realize; +} + +static const TypeInfo sun4v_rtc_info = { + .name = TYPE_SUN4V_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Sun4vRtc), + .class_init = sun4v_rtc_class_init, +}; + +static void sun4v_rtc_register_types(void) +{ + type_register_static(&sun4v_rtc_info); +} + +type_init(sun4v_rtc_register_types) diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index 54c94ac557..ac9e0e0fba 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -1,5 +1,9 @@ # See docs/devel/tracing.txt for syntax documentation. +# sun4v-rtc.c +sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64 +sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64 + # pl031.c pl031_irq_state(int level) "irq state %d" pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 5987693659..5eb2d097b9 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -30,7 +30,7 @@ #include "hw/misc/unimp.h" #include "hw/loader.h" #include "hw/sparc/sparc64.h" -#include "hw/timer/sun4v-rtc.h" +#include "hw/rtc/sun4v-rtc.h" #include "exec/address-spaces.h" #include "sysemu/block-backend.h" #include "qemu/error-report.h" diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index a6b668b255..b04c928136 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -35,9 +35,6 @@ config ALLWINNER_A10_PIT config STM32F2XX_TIMER bool -config SUN4V_RTC - bool - config CMSDK_APB_TIMER bool select PTIMER diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 2fb12162a6..034bd30255 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -35,7 +35,6 @@ common-obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o aspeed_rtc.o -common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o common-obj-$(CONFIG_MSF2) += mss-timer.o diff --git a/hw/timer/sun4v-rtc.c b/hw/timer/sun4v-rtc.c deleted file mode 100644 index 54272a822f..0000000000 --- a/hw/timer/sun4v-rtc.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * QEMU sun4v Real Time Clock device - * - * The sun4v_rtc device (sun4v tod clock) - * - * Copyright (c) 2016 Artyom Tarasenko - * - * This code is licensed under the GNU GPL v3 or (at your option) any later - * version. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qemu/module.h" -#include "qemu/timer.h" -#include "hw/timer/sun4v-rtc.h" -#include "trace.h" - - -#define TYPE_SUN4V_RTC "sun4v_rtc" -#define SUN4V_RTC(obj) OBJECT_CHECK(Sun4vRtc, (obj), TYPE_SUN4V_RTC) - -typedef struct Sun4vRtc { - SysBusDevice parent_obj; - - MemoryRegion iomem; -} Sun4vRtc; - -static uint64_t sun4v_rtc_read(void *opaque, hwaddr addr, - unsigned size) -{ - uint64_t val = get_clock_realtime() / NANOSECONDS_PER_SECOND; - if (!(addr & 4ULL)) { - /* accessing the high 32 bits */ - val >>= 32; - } - trace_sun4v_rtc_read(addr, val); - return val; -} - -static void sun4v_rtc_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - trace_sun4v_rtc_write(addr, val); -} - -static const MemoryRegionOps sun4v_rtc_ops = { - .read = sun4v_rtc_read, - .write = sun4v_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void sun4v_rtc_init(hwaddr addr) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, TYPE_SUN4V_RTC); - s = SYS_BUS_DEVICE(dev); - - qdev_init_nofail(dev); - - sysbus_mmio_map(s, 0, addr); -} - -static void sun4v_rtc_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - Sun4vRtc *s = SUN4V_RTC(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &sun4v_rtc_ops, s, - "sun4v-rtc", 0x08ULL); - sysbus_init_mmio(sbd, &s->iomem); -} - -static void sun4v_rtc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sun4v_rtc_realize; -} - -static const TypeInfo sun4v_rtc_info = { - .name = TYPE_SUN4V_RTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Sun4vRtc), - .class_init = sun4v_rtc_class_init, -}; - -static void sun4v_rtc_register_types(void) -{ - type_register_static(&sun4v_rtc_info); -} - -type_init(sun4v_rtc_register_types) diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 6936fe8573..ce34b967db 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -70,10 +70,6 @@ cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset" aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 -# sun4v-rtc.c -sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64 -sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64 - # xlnx-zynqmp-rtc.c xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d" diff --git a/include/hw/rtc/sun4v-rtc.h b/include/hw/rtc/sun4v-rtc.h new file mode 100644 index 0000000000..fd868f6ed2 --- /dev/null +++ b/include/hw/rtc/sun4v-rtc.h @@ -0,0 +1,19 @@ +/* + * QEMU sun4v Real Time Clock device + * + * The sun4v_rtc device (sun4v tod clock) + * + * Copyright (c) 2016 Artyom Tarasenko + * + * This code is licensed under the GNU GPL v3 or (at your option) any later + * version. + */ + +#ifndef HW_RTC_SUN4V +#define HW_RTC_SUN4V + +#include "exec/hwaddr.h" + +void sun4v_rtc_init(hwaddr addr); + +#endif diff --git a/include/hw/timer/sun4v-rtc.h b/include/hw/timer/sun4v-rtc.h deleted file mode 100644 index 407278f918..0000000000 --- a/include/hw/timer/sun4v-rtc.h +++ /dev/null @@ -1 +0,0 @@ -void sun4v_rtc_init(hwaddr addr); -- cgit v1.2.3-55-g7522 From 8035f85ef3ef4bc7ad4e181b9ebfe3f620cf614b Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Fri, 4 Oct 2019 01:03:59 +0200 Subject: hw: Move Xilinx ZynqMP RTC from hw/timer/ to hw/rtc/ subdirectory Move RTC devices under the hw/rtc/ subdirectory. Remove Alistair outdated email address (see commit c22e580c2ad). Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Acked-by: Peter Maydell Message-Id: <20191003230404.19384-10-philmd@redhat.com> Signed-off-by: Laurent Vivier --- hw/rtc/Makefile.objs | 1 + hw/rtc/trace-events | 3 + hw/rtc/xlnx-zynqmp-rtc.c | 276 +++++++++++++++++++++++++++++++++++++ hw/timer/Makefile.objs | 1 - hw/timer/trace-events | 3 - hw/timer/xlnx-zynqmp-rtc.c | 276 ------------------------------------- include/hw/arm/xlnx-zynqmp.h | 2 +- include/hw/rtc/xlnx-zynqmp-rtc.h | 92 +++++++++++++ include/hw/timer/xlnx-zynqmp-rtc.h | 92 ------------- 9 files changed, 373 insertions(+), 373 deletions(-) create mode 100644 hw/rtc/xlnx-zynqmp-rtc.c delete mode 100644 hw/timer/xlnx-zynqmp-rtc.c create mode 100644 include/hw/rtc/xlnx-zynqmp-rtc.h delete mode 100644 include/hw/timer/xlnx-zynqmp-rtc.h (limited to 'include') diff --git a/hw/rtc/Makefile.objs b/hw/rtc/Makefile.objs index b195863291..543a550a0f 100644 --- a/hw/rtc/Makefile.objs +++ b/hw/rtc/Makefile.objs @@ -6,5 +6,6 @@ common-obj-$(CONFIG_M48T59) += m48t59-isa.o endif common-obj-$(CONFIG_PL031) += pl031.o common-obj-$(CONFIG_TWL92230) += twl92230.o +common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index ac9e0e0fba..7f1945ad4c 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -4,6 +4,9 @@ sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64 sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64 +# xlnx-zynqmp-rtc.c +xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d" + # pl031.c pl031_irq_state(int level) "irq state %d" pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" diff --git a/hw/rtc/xlnx-zynqmp-rtc.c b/hw/rtc/xlnx-zynqmp-rtc.c new file mode 100644 index 0000000000..f9f09b7296 --- /dev/null +++ b/hw/rtc/xlnx-zynqmp-rtc.c @@ -0,0 +1,276 @@ +/* + * QEMU model of the Xilinx ZynqMP Real Time Clock (RTC). + * + * Copyright (c) 2017 Xilinx Inc. + * + * Written-by: Alistair Francis + * + * 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 "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/sysbus.h" +#include "hw/register.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/irq.h" +#include "hw/ptimer.h" +#include "qemu/cutils.h" +#include "sysemu/sysemu.h" +#include "trace.h" +#include "hw/rtc/xlnx-zynqmp-rtc.h" +#include "migration/vmstate.h" + +#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG +#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0 +#endif + +static void rtc_int_update_irq(XlnxZynqMPRTC *s) +{ + bool pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK]; + qemu_set_irq(s->irq_rtc_int, pending); +} + +static void addr_error_int_update_irq(XlnxZynqMPRTC *s) +{ + bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK]; + qemu_set_irq(s->irq_addr_error_int, pending); +} + +static uint32_t rtc_get_count(XlnxZynqMPRTC *s) +{ + int64_t now = qemu_clock_get_ns(rtc_clock); + return s->tick_offset + now / NANOSECONDS_PER_SECOND; +} + +static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + return rtc_get_count(s); +} + +static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + rtc_int_update_irq(s); +} + +static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + s->regs[R_RTC_INT_MASK] &= (uint32_t) ~val64; + rtc_int_update_irq(s); + return 0; +} + +static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + s->regs[R_RTC_INT_MASK] |= (uint32_t) val64; + rtc_int_update_irq(s); + return 0; +} + +static void addr_error_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + addr_error_int_update_irq(s); +} + +static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + s->regs[R_ADDR_ERROR_INT_MASK] &= (uint32_t) ~val64; + addr_error_int_update_irq(s); + return 0; +} + +static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); + + s->regs[R_ADDR_ERROR_INT_MASK] |= (uint32_t) val64; + addr_error_int_update_irq(s); + return 0; +} + +static const RegisterAccessInfo rtc_regs_info[] = { + { .name = "SET_TIME_WRITE", .addr = A_SET_TIME_WRITE, + .unimp = MAKE_64BIT_MASK(0, 32), + },{ .name = "SET_TIME_READ", .addr = A_SET_TIME_READ, + .ro = 0xffffffff, + .post_read = current_time_postr, + },{ .name = "CALIB_WRITE", .addr = A_CALIB_WRITE, + .unimp = MAKE_64BIT_MASK(0, 32), + },{ .name = "CALIB_READ", .addr = A_CALIB_READ, + .ro = 0x1fffff, + },{ .name = "CURRENT_TIME", .addr = A_CURRENT_TIME, + .ro = 0xffffffff, + .post_read = current_time_postr, + },{ .name = "CURRENT_TICK", .addr = A_CURRENT_TICK, + .ro = 0xffff, + },{ .name = "ALARM", .addr = A_ALARM, + },{ .name = "RTC_INT_STATUS", .addr = A_RTC_INT_STATUS, + .w1c = 0x3, + .post_write = rtc_int_status_postw, + },{ .name = "RTC_INT_MASK", .addr = A_RTC_INT_MASK, + .reset = 0x3, + .ro = 0x3, + },{ .name = "RTC_INT_EN", .addr = A_RTC_INT_EN, + .pre_write = rtc_int_en_prew, + },{ .name = "RTC_INT_DIS", .addr = A_RTC_INT_DIS, + .pre_write = rtc_int_dis_prew, + },{ .name = "ADDR_ERROR", .addr = A_ADDR_ERROR, + .w1c = 0x1, + .post_write = addr_error_postw, + },{ .name = "ADDR_ERROR_INT_MASK", .addr = A_ADDR_ERROR_INT_MASK, + .reset = 0x1, + .ro = 0x1, + },{ .name = "ADDR_ERROR_INT_EN", .addr = A_ADDR_ERROR_INT_EN, + .pre_write = addr_error_int_en_prew, + },{ .name = "ADDR_ERROR_INT_DIS", .addr = A_ADDR_ERROR_INT_DIS, + .pre_write = addr_error_int_dis_prew, + },{ .name = "CONTROL", .addr = A_CONTROL, + .reset = 0x1000000, + .rsvd = 0x70fffffe, + },{ .name = "SAFETY_CHK", .addr = A_SAFETY_CHK, + } +}; + +static void rtc_reset(DeviceState *dev) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + + rtc_int_update_irq(s); + addr_error_int_update_irq(s); +} + +static const MemoryRegionOps rtc_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void rtc_init(Object *obj) +{ + XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + struct tm current_tm; + + memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC, + XLNX_ZYNQMP_RTC_R_MAX * 4); + reg_array = + register_init_block32(DEVICE(obj), rtc_regs_info, + ARRAY_SIZE(rtc_regs_info), + s->regs_info, s->regs, + &rtc_ops, + XLNX_ZYNQMP_RTC_ERR_DEBUG, + XLNX_ZYNQMP_RTC_R_MAX * 4); + memory_region_add_subregion(&s->iomem, + 0x0, + ®_array->mem); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq_rtc_int); + sysbus_init_irq(sbd, &s->irq_addr_error_int); + + qemu_get_timedate(¤t_tm, 0); + s->tick_offset = mktimegm(¤t_tm) - + qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; + + trace_xlnx_zynqmp_rtc_gettime(current_tm.tm_year, current_tm.tm_mon, + current_tm.tm_mday, current_tm.tm_hour, + current_tm.tm_min, current_tm.tm_sec); +} + +static int rtc_pre_save(void *opaque) +{ + XlnxZynqMPRTC *s = opaque; + int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; + + /* Add the time at migration */ + s->tick_offset = s->tick_offset + now; + + return 0; +} + +static int rtc_post_load(void *opaque, int version_id) +{ + XlnxZynqMPRTC *s = opaque; + int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; + + /* Subtract the time after migration. This combined with the pre_save + * action results in us having subtracted the time that the guest was + * stopped to the offset. + */ + s->tick_offset = s->tick_offset - now; + + return 0; +} + +static const VMStateDescription vmstate_rtc = { + .name = TYPE_XLNX_ZYNQMP_RTC, + .version_id = 1, + .minimum_version_id = 1, + .pre_save = rtc_pre_save, + .post_load = rtc_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX), + VMSTATE_UINT32(tick_offset, XlnxZynqMPRTC), + VMSTATE_END_OF_LIST(), + } +}; + +static void rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = rtc_reset; + dc->vmsd = &vmstate_rtc; +} + +static const TypeInfo rtc_info = { + .name = TYPE_XLNX_ZYNQMP_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZynqMPRTC), + .class_init = rtc_class_init, + .instance_init = rtc_init, +}; + +static void rtc_register_types(void) +{ + type_register_static(&rtc_info); +} + +type_init(rtc_register_types) diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 70b61b69c7..294465ef47 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -14,7 +14,6 @@ common-obj-$(CONFIG_IMX) += imx_epit.o common-obj-$(CONFIG_IMX) += imx_gpt.o common-obj-$(CONFIG_LM32) += lm32_timer.o common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o -common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_timer.o common-obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o diff --git a/hw/timer/trace-events b/hw/timer/trace-events index ce34b967db..1459d07237 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -70,9 +70,6 @@ cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset" aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 -# xlnx-zynqmp-rtc.c -xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d" - # nrf51_timer.c nrf51_timer_read(uint64_t addr, uint32_t value, unsigned size) "read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" nrf51_timer_write(uint64_t addr, uint32_t value, unsigned size) "write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" diff --git a/hw/timer/xlnx-zynqmp-rtc.c b/hw/timer/xlnx-zynqmp-rtc.c deleted file mode 100644 index 5692db98c2..0000000000 --- a/hw/timer/xlnx-zynqmp-rtc.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * QEMU model of the Xilinx ZynqMP Real Time Clock (RTC). - * - * Copyright (c) 2017 Xilinx Inc. - * - * Written-by: Alistair Francis - * - * 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 "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/sysbus.h" -#include "hw/register.h" -#include "qemu/bitops.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "hw/irq.h" -#include "hw/ptimer.h" -#include "qemu/cutils.h" -#include "sysemu/sysemu.h" -#include "trace.h" -#include "hw/timer/xlnx-zynqmp-rtc.h" -#include "migration/vmstate.h" - -#ifndef XLNX_ZYNQMP_RTC_ERR_DEBUG -#define XLNX_ZYNQMP_RTC_ERR_DEBUG 0 -#endif - -static void rtc_int_update_irq(XlnxZynqMPRTC *s) -{ - bool pending = s->regs[R_RTC_INT_STATUS] & ~s->regs[R_RTC_INT_MASK]; - qemu_set_irq(s->irq_rtc_int, pending); -} - -static void addr_error_int_update_irq(XlnxZynqMPRTC *s) -{ - bool pending = s->regs[R_ADDR_ERROR] & ~s->regs[R_ADDR_ERROR_INT_MASK]; - qemu_set_irq(s->irq_addr_error_int, pending); -} - -static uint32_t rtc_get_count(XlnxZynqMPRTC *s) -{ - int64_t now = qemu_clock_get_ns(rtc_clock); - return s->tick_offset + now / NANOSECONDS_PER_SECOND; -} - -static uint64_t current_time_postr(RegisterInfo *reg, uint64_t val64) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); - - return rtc_get_count(s); -} - -static void rtc_int_status_postw(RegisterInfo *reg, uint64_t val64) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); - rtc_int_update_irq(s); -} - -static uint64_t rtc_int_en_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); - - s->regs[R_RTC_INT_MASK] &= (uint32_t) ~val64; - rtc_int_update_irq(s); - return 0; -} - -static uint64_t rtc_int_dis_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); - - s->regs[R_RTC_INT_MASK] |= (uint32_t) val64; - rtc_int_update_irq(s); - return 0; -} - -static void addr_error_postw(RegisterInfo *reg, uint64_t val64) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); - addr_error_int_update_irq(s); -} - -static uint64_t addr_error_int_en_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); - - s->regs[R_ADDR_ERROR_INT_MASK] &= (uint32_t) ~val64; - addr_error_int_update_irq(s); - return 0; -} - -static uint64_t addr_error_int_dis_prew(RegisterInfo *reg, uint64_t val64) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(reg->opaque); - - s->regs[R_ADDR_ERROR_INT_MASK] |= (uint32_t) val64; - addr_error_int_update_irq(s); - return 0; -} - -static const RegisterAccessInfo rtc_regs_info[] = { - { .name = "SET_TIME_WRITE", .addr = A_SET_TIME_WRITE, - .unimp = MAKE_64BIT_MASK(0, 32), - },{ .name = "SET_TIME_READ", .addr = A_SET_TIME_READ, - .ro = 0xffffffff, - .post_read = current_time_postr, - },{ .name = "CALIB_WRITE", .addr = A_CALIB_WRITE, - .unimp = MAKE_64BIT_MASK(0, 32), - },{ .name = "CALIB_READ", .addr = A_CALIB_READ, - .ro = 0x1fffff, - },{ .name = "CURRENT_TIME", .addr = A_CURRENT_TIME, - .ro = 0xffffffff, - .post_read = current_time_postr, - },{ .name = "CURRENT_TICK", .addr = A_CURRENT_TICK, - .ro = 0xffff, - },{ .name = "ALARM", .addr = A_ALARM, - },{ .name = "RTC_INT_STATUS", .addr = A_RTC_INT_STATUS, - .w1c = 0x3, - .post_write = rtc_int_status_postw, - },{ .name = "RTC_INT_MASK", .addr = A_RTC_INT_MASK, - .reset = 0x3, - .ro = 0x3, - },{ .name = "RTC_INT_EN", .addr = A_RTC_INT_EN, - .pre_write = rtc_int_en_prew, - },{ .name = "RTC_INT_DIS", .addr = A_RTC_INT_DIS, - .pre_write = rtc_int_dis_prew, - },{ .name = "ADDR_ERROR", .addr = A_ADDR_ERROR, - .w1c = 0x1, - .post_write = addr_error_postw, - },{ .name = "ADDR_ERROR_INT_MASK", .addr = A_ADDR_ERROR_INT_MASK, - .reset = 0x1, - .ro = 0x1, - },{ .name = "ADDR_ERROR_INT_EN", .addr = A_ADDR_ERROR_INT_EN, - .pre_write = addr_error_int_en_prew, - },{ .name = "ADDR_ERROR_INT_DIS", .addr = A_ADDR_ERROR_INT_DIS, - .pre_write = addr_error_int_dis_prew, - },{ .name = "CONTROL", .addr = A_CONTROL, - .reset = 0x1000000, - .rsvd = 0x70fffffe, - },{ .name = "SAFETY_CHK", .addr = A_SAFETY_CHK, - } -}; - -static void rtc_reset(DeviceState *dev) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(dev); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { - register_reset(&s->regs_info[i]); - } - - rtc_int_update_irq(s); - addr_error_int_update_irq(s); -} - -static const MemoryRegionOps rtc_ops = { - .read = register_read_memory, - .write = register_write_memory, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void rtc_init(Object *obj) -{ - XlnxZynqMPRTC *s = XLNX_ZYNQMP_RTC(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - struct tm current_tm; - - memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_RTC, - XLNX_ZYNQMP_RTC_R_MAX * 4); - reg_array = - register_init_block32(DEVICE(obj), rtc_regs_info, - ARRAY_SIZE(rtc_regs_info), - s->regs_info, s->regs, - &rtc_ops, - XLNX_ZYNQMP_RTC_ERR_DEBUG, - XLNX_ZYNQMP_RTC_R_MAX * 4); - memory_region_add_subregion(&s->iomem, - 0x0, - ®_array->mem); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq_rtc_int); - sysbus_init_irq(sbd, &s->irq_addr_error_int); - - qemu_get_timedate(¤t_tm, 0); - s->tick_offset = mktimegm(¤t_tm) - - qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; - - trace_xlnx_zynqmp_rtc_gettime(current_tm.tm_year, current_tm.tm_mon, - current_tm.tm_mday, current_tm.tm_hour, - current_tm.tm_min, current_tm.tm_sec); -} - -static int rtc_pre_save(void *opaque) -{ - XlnxZynqMPRTC *s = opaque; - int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; - - /* Add the time at migration */ - s->tick_offset = s->tick_offset + now; - - return 0; -} - -static int rtc_post_load(void *opaque, int version_id) -{ - XlnxZynqMPRTC *s = opaque; - int64_t now = qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; - - /* Subtract the time after migration. This combined with the pre_save - * action results in us having subtracted the time that the guest was - * stopped to the offset. - */ - s->tick_offset = s->tick_offset - now; - - return 0; -} - -static const VMStateDescription vmstate_rtc = { - .name = TYPE_XLNX_ZYNQMP_RTC, - .version_id = 1, - .minimum_version_id = 1, - .pre_save = rtc_pre_save, - .post_load = rtc_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPRTC, XLNX_ZYNQMP_RTC_R_MAX), - VMSTATE_UINT32(tick_offset, XlnxZynqMPRTC), - VMSTATE_END_OF_LIST(), - } -}; - -static void rtc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = rtc_reset; - dc->vmsd = &vmstate_rtc; -} - -static const TypeInfo rtc_info = { - .name = TYPE_XLNX_ZYNQMP_RTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XlnxZynqMPRTC), - .class_init = rtc_class_init, - .instance_init = rtc_init, -}; - -static void rtc_register_types(void) -{ - type_register_static(&rtc_info); -} - -type_init(rtc_register_types) diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index d7483c3b42..53076fa29a 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -29,7 +29,7 @@ #include "hw/dma/xlnx-zdma.h" #include "hw/display/xlnx_dp.h" #include "hw/intc/xlnx-zynqmp-ipi.h" -#include "hw/timer/xlnx-zynqmp-rtc.h" +#include "hw/rtc/xlnx-zynqmp-rtc.h" #include "hw/cpu/cluster.h" #include "target/arm/cpu.h" diff --git a/include/hw/rtc/xlnx-zynqmp-rtc.h b/include/hw/rtc/xlnx-zynqmp-rtc.h new file mode 100644 index 0000000000..6fa1cb2f43 --- /dev/null +++ b/include/hw/rtc/xlnx-zynqmp-rtc.h @@ -0,0 +1,92 @@ +/* + * QEMU model of the Xilinx ZynqMP Real Time Clock (RTC). + * + * Copyright (c) 2017 Xilinx Inc. + * + * Written-by: Alistair Francis + * + * 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. + */ + +#ifndef HW_RTC_XLNX_ZYNQMP_H +#define HW_RTC_XLNX_ZYNQMP_H + +#include "hw/register.h" +#include "hw/sysbus.h" + +#define TYPE_XLNX_ZYNQMP_RTC "xlnx-zynmp.rtc" + +#define XLNX_ZYNQMP_RTC(obj) \ + OBJECT_CHECK(XlnxZynqMPRTC, (obj), TYPE_XLNX_ZYNQMP_RTC) + +REG32(SET_TIME_WRITE, 0x0) +REG32(SET_TIME_READ, 0x4) +REG32(CALIB_WRITE, 0x8) + FIELD(CALIB_WRITE, FRACTION_EN, 20, 1) + FIELD(CALIB_WRITE, FRACTION_DATA, 16, 4) + FIELD(CALIB_WRITE, MAX_TICK, 0, 16) +REG32(CALIB_READ, 0xc) + FIELD(CALIB_READ, FRACTION_EN, 20, 1) + FIELD(CALIB_READ, FRACTION_DATA, 16, 4) + FIELD(CALIB_READ, MAX_TICK, 0, 16) +REG32(CURRENT_TIME, 0x10) +REG32(CURRENT_TICK, 0x14) + FIELD(CURRENT_TICK, VALUE, 0, 16) +REG32(ALARM, 0x18) +REG32(RTC_INT_STATUS, 0x20) + FIELD(RTC_INT_STATUS, ALARM, 1, 1) + FIELD(RTC_INT_STATUS, SECONDS, 0, 1) +REG32(RTC_INT_MASK, 0x24) + FIELD(RTC_INT_MASK, ALARM, 1, 1) + FIELD(RTC_INT_MASK, SECONDS, 0, 1) +REG32(RTC_INT_EN, 0x28) + FIELD(RTC_INT_EN, ALARM, 1, 1) + FIELD(RTC_INT_EN, SECONDS, 0, 1) +REG32(RTC_INT_DIS, 0x2c) + FIELD(RTC_INT_DIS, ALARM, 1, 1) + FIELD(RTC_INT_DIS, SECONDS, 0, 1) +REG32(ADDR_ERROR, 0x30) + FIELD(ADDR_ERROR, STATUS, 0, 1) +REG32(ADDR_ERROR_INT_MASK, 0x34) + FIELD(ADDR_ERROR_INT_MASK, MASK, 0, 1) +REG32(ADDR_ERROR_INT_EN, 0x38) + FIELD(ADDR_ERROR_INT_EN, MASK, 0, 1) +REG32(ADDR_ERROR_INT_DIS, 0x3c) + FIELD(ADDR_ERROR_INT_DIS, MASK, 0, 1) +REG32(CONTROL, 0x40) + FIELD(CONTROL, BATTERY_DISABLE, 31, 1) + FIELD(CONTROL, OSC_CNTRL, 24, 4) + FIELD(CONTROL, SLVERR_ENABLE, 0, 1) +REG32(SAFETY_CHK, 0x50) + +#define XLNX_ZYNQMP_RTC_R_MAX (R_SAFETY_CHK + 1) + +typedef struct XlnxZynqMPRTC { + SysBusDevice parent_obj; + MemoryRegion iomem; + qemu_irq irq_rtc_int; + qemu_irq irq_addr_error_int; + + uint32_t tick_offset; + + uint32_t regs[XLNX_ZYNQMP_RTC_R_MAX]; + RegisterInfo regs_info[XLNX_ZYNQMP_RTC_R_MAX]; +} XlnxZynqMPRTC; + +#endif diff --git a/include/hw/timer/xlnx-zynqmp-rtc.h b/include/hw/timer/xlnx-zynqmp-rtc.h deleted file mode 100644 index 97e32322ed..0000000000 --- a/include/hw/timer/xlnx-zynqmp-rtc.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * QEMU model of the Xilinx ZynqMP Real Time Clock (RTC). - * - * Copyright (c) 2017 Xilinx Inc. - * - * Written-by: Alistair Francis - * - * 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. - */ - -#ifndef HW_TIMER_XLNX_ZYNQMP_RTC_H -#define HW_TIMER_XLNX_ZYNQMP_RTC_H - -#include "hw/register.h" -#include "hw/sysbus.h" - -#define TYPE_XLNX_ZYNQMP_RTC "xlnx-zynmp.rtc" - -#define XLNX_ZYNQMP_RTC(obj) \ - OBJECT_CHECK(XlnxZynqMPRTC, (obj), TYPE_XLNX_ZYNQMP_RTC) - -REG32(SET_TIME_WRITE, 0x0) -REG32(SET_TIME_READ, 0x4) -REG32(CALIB_WRITE, 0x8) - FIELD(CALIB_WRITE, FRACTION_EN, 20, 1) - FIELD(CALIB_WRITE, FRACTION_DATA, 16, 4) - FIELD(CALIB_WRITE, MAX_TICK, 0, 16) -REG32(CALIB_READ, 0xc) - FIELD(CALIB_READ, FRACTION_EN, 20, 1) - FIELD(CALIB_READ, FRACTION_DATA, 16, 4) - FIELD(CALIB_READ, MAX_TICK, 0, 16) -REG32(CURRENT_TIME, 0x10) -REG32(CURRENT_TICK, 0x14) - FIELD(CURRENT_TICK, VALUE, 0, 16) -REG32(ALARM, 0x18) -REG32(RTC_INT_STATUS, 0x20) - FIELD(RTC_INT_STATUS, ALARM, 1, 1) - FIELD(RTC_INT_STATUS, SECONDS, 0, 1) -REG32(RTC_INT_MASK, 0x24) - FIELD(RTC_INT_MASK, ALARM, 1, 1) - FIELD(RTC_INT_MASK, SECONDS, 0, 1) -REG32(RTC_INT_EN, 0x28) - FIELD(RTC_INT_EN, ALARM, 1, 1) - FIELD(RTC_INT_EN, SECONDS, 0, 1) -REG32(RTC_INT_DIS, 0x2c) - FIELD(RTC_INT_DIS, ALARM, 1, 1) - FIELD(RTC_INT_DIS, SECONDS, 0, 1) -REG32(ADDR_ERROR, 0x30) - FIELD(ADDR_ERROR, STATUS, 0, 1) -REG32(ADDR_ERROR_INT_MASK, 0x34) - FIELD(ADDR_ERROR_INT_MASK, MASK, 0, 1) -REG32(ADDR_ERROR_INT_EN, 0x38) - FIELD(ADDR_ERROR_INT_EN, MASK, 0, 1) -REG32(ADDR_ERROR_INT_DIS, 0x3c) - FIELD(ADDR_ERROR_INT_DIS, MASK, 0, 1) -REG32(CONTROL, 0x40) - FIELD(CONTROL, BATTERY_DISABLE, 31, 1) - FIELD(CONTROL, OSC_CNTRL, 24, 4) - FIELD(CONTROL, SLVERR_ENABLE, 0, 1) -REG32(SAFETY_CHK, 0x50) - -#define XLNX_ZYNQMP_RTC_R_MAX (R_SAFETY_CHK + 1) - -typedef struct XlnxZynqMPRTC { - SysBusDevice parent_obj; - MemoryRegion iomem; - qemu_irq irq_rtc_int; - qemu_irq irq_addr_error_int; - - uint32_t tick_offset; - - uint32_t regs[XLNX_ZYNQMP_RTC_R_MAX]; - RegisterInfo regs_info[XLNX_ZYNQMP_RTC_R_MAX]; -} XlnxZynqMPRTC; - -#endif -- cgit v1.2.3-55-g7522 From ea5dcf4e1d00ea6de8112968164a97ce8a92ddce Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Fri, 4 Oct 2019 01:04:01 +0200 Subject: hw: Move Aspeed RTC from hw/timer/ to hw/rtc/ subdirectory Move RTC devices under the hw/rtc/ subdirectory. Reviewed-by: Cédric Le Goater Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Acked-by: Peter Maydell Message-Id: <20191003230404.19384-12-philmd@redhat.com> Signed-off-by: Laurent Vivier --- hw/rtc/Makefile.objs | 1 + hw/rtc/aspeed_rtc.c | 181 ++++++++++++++++++++++++++++++++++++++++++ hw/rtc/trace-events | 4 + hw/timer/Makefile.objs | 2 +- hw/timer/aspeed_rtc.c | 181 ------------------------------------------ hw/timer/trace-events | 4 - include/hw/arm/aspeed_soc.h | 2 +- include/hw/rtc/aspeed_rtc.h | 30 +++++++ include/hw/timer/aspeed_rtc.h | 30 ------- 9 files changed, 218 insertions(+), 217 deletions(-) create mode 100644 hw/rtc/aspeed_rtc.c delete mode 100644 hw/timer/aspeed_rtc.c create mode 100644 include/hw/rtc/aspeed_rtc.h delete mode 100644 include/hw/timer/aspeed_rtc.h (limited to 'include') diff --git a/hw/rtc/Makefile.objs b/hw/rtc/Makefile.objs index 3d4763fc26..8dc9fcd3a9 100644 --- a/hw/rtc/Makefile.objs +++ b/hw/rtc/Makefile.objs @@ -10,3 +10,4 @@ common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o common-obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_rtc.o diff --git a/hw/rtc/aspeed_rtc.c b/hw/rtc/aspeed_rtc.c new file mode 100644 index 0000000000..3ca1183558 --- /dev/null +++ b/hw/rtc/aspeed_rtc.c @@ -0,0 +1,181 @@ +/* + * ASPEED Real Time Clock + * Joel Stanley + * + * Copyright 2019 IBM Corp + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/rtc/aspeed_rtc.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/timer.h" + +#include "trace.h" + +#define COUNTER1 (0x00 / 4) +#define COUNTER2 (0x04 / 4) +#define ALARM (0x08 / 4) +#define CONTROL (0x10 / 4) +#define ALARM_STATUS (0x14 / 4) + +#define RTC_UNLOCKED BIT(1) +#define RTC_ENABLED BIT(0) + +static void aspeed_rtc_calc_offset(AspeedRtcState *rtc) +{ + struct tm tm; + uint32_t year, cent; + uint32_t reg1 = rtc->reg[COUNTER1]; + uint32_t reg2 = rtc->reg[COUNTER2]; + + tm.tm_mday = (reg1 >> 24) & 0x1f; + tm.tm_hour = (reg1 >> 16) & 0x1f; + tm.tm_min = (reg1 >> 8) & 0x3f; + tm.tm_sec = (reg1 >> 0) & 0x3f; + + cent = (reg2 >> 16) & 0x1f; + year = (reg2 >> 8) & 0x7f; + tm.tm_mon = ((reg2 >> 0) & 0x0f) - 1; + tm.tm_year = year + (cent * 100) - 1900; + + rtc->offset = qemu_timedate_diff(&tm); +} + +static uint32_t aspeed_rtc_get_counter(AspeedRtcState *rtc, int r) +{ + uint32_t year, cent; + struct tm now; + + qemu_get_timedate(&now, rtc->offset); + + switch (r) { + case COUNTER1: + return (now.tm_mday << 24) | (now.tm_hour << 16) | + (now.tm_min << 8) | now.tm_sec; + case COUNTER2: + cent = (now.tm_year + 1900) / 100; + year = now.tm_year % 100; + return ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) | + ((now.tm_mon + 1) & 0xf); + default: + g_assert_not_reached(); + } +} + +static uint64_t aspeed_rtc_read(void *opaque, hwaddr addr, + unsigned size) +{ + AspeedRtcState *rtc = opaque; + uint64_t val; + uint32_t r = addr >> 2; + + switch (r) { + case COUNTER1: + case COUNTER2: + if (rtc->reg[CONTROL] & RTC_ENABLED) { + rtc->reg[r] = aspeed_rtc_get_counter(rtc, r); + } + /* fall through */ + case CONTROL: + val = rtc->reg[r]; + break; + case ALARM: + case ALARM_STATUS: + default: + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr); + return 0; + } + + trace_aspeed_rtc_read(addr, val); + + return val; +} + +static void aspeed_rtc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + AspeedRtcState *rtc = opaque; + uint32_t r = addr >> 2; + + switch (r) { + case COUNTER1: + case COUNTER2: + if (!(rtc->reg[CONTROL] & RTC_UNLOCKED)) { + break; + } + /* fall through */ + case CONTROL: + rtc->reg[r] = val; + aspeed_rtc_calc_offset(rtc); + break; + case ALARM: + case ALARM_STATUS: + default: + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr); + break; + } + trace_aspeed_rtc_write(addr, val); +} + +static void aspeed_rtc_reset(DeviceState *d) +{ + AspeedRtcState *rtc = ASPEED_RTC(d); + + rtc->offset = 0; + memset(rtc->reg, 0, sizeof(rtc->reg)); +} + +static const MemoryRegionOps aspeed_rtc_ops = { + .read = aspeed_rtc_read, + .write = aspeed_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_aspeed_rtc = { + .name = TYPE_ASPEED_RTC, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg, AspeedRtcState, 0x18), + VMSTATE_INT32(offset, AspeedRtcState), + VMSTATE_INT32(offset, AspeedRtcState), + VMSTATE_END_OF_LIST() + } +}; + +static void aspeed_rtc_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedRtcState *s = ASPEED_RTC(dev); + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_rtc_ops, s, + "aspeed-rtc", 0x18ULL); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = aspeed_rtc_realize; + dc->vmsd = &vmstate_aspeed_rtc; + dc->reset = aspeed_rtc_reset; +} + +static const TypeInfo aspeed_rtc_info = { + .name = TYPE_ASPEED_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedRtcState), + .class_init = aspeed_rtc_class_init, +}; + +static void aspeed_rtc_register_types(void) +{ + type_register_static(&aspeed_rtc_info); +} + +type_init(aspeed_rtc_register_types) diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index 7f1945ad4c..d6749f4616 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -13,3 +13,7 @@ pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" pl031_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" pl031_alarm_raised(void) "alarm raised" pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks" + +# aspeed-rtc.c +aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 +aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 33191d74cb..83091770df 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -29,7 +29,7 @@ common-obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o common-obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o -common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o aspeed_rtc.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o diff --git a/hw/timer/aspeed_rtc.c b/hw/timer/aspeed_rtc.c deleted file mode 100644 index 5313017353..0000000000 --- a/hw/timer/aspeed_rtc.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * ASPEED Real Time Clock - * Joel Stanley - * - * Copyright 2019 IBM Corp - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/timer/aspeed_rtc.h" -#include "migration/vmstate.h" -#include "qemu/log.h" -#include "qemu/timer.h" - -#include "trace.h" - -#define COUNTER1 (0x00 / 4) -#define COUNTER2 (0x04 / 4) -#define ALARM (0x08 / 4) -#define CONTROL (0x10 / 4) -#define ALARM_STATUS (0x14 / 4) - -#define RTC_UNLOCKED BIT(1) -#define RTC_ENABLED BIT(0) - -static void aspeed_rtc_calc_offset(AspeedRtcState *rtc) -{ - struct tm tm; - uint32_t year, cent; - uint32_t reg1 = rtc->reg[COUNTER1]; - uint32_t reg2 = rtc->reg[COUNTER2]; - - tm.tm_mday = (reg1 >> 24) & 0x1f; - tm.tm_hour = (reg1 >> 16) & 0x1f; - tm.tm_min = (reg1 >> 8) & 0x3f; - tm.tm_sec = (reg1 >> 0) & 0x3f; - - cent = (reg2 >> 16) & 0x1f; - year = (reg2 >> 8) & 0x7f; - tm.tm_mon = ((reg2 >> 0) & 0x0f) - 1; - tm.tm_year = year + (cent * 100) - 1900; - - rtc->offset = qemu_timedate_diff(&tm); -} - -static uint32_t aspeed_rtc_get_counter(AspeedRtcState *rtc, int r) -{ - uint32_t year, cent; - struct tm now; - - qemu_get_timedate(&now, rtc->offset); - - switch (r) { - case COUNTER1: - return (now.tm_mday << 24) | (now.tm_hour << 16) | - (now.tm_min << 8) | now.tm_sec; - case COUNTER2: - cent = (now.tm_year + 1900) / 100; - year = now.tm_year % 100; - return ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) | - ((now.tm_mon + 1) & 0xf); - default: - g_assert_not_reached(); - } -} - -static uint64_t aspeed_rtc_read(void *opaque, hwaddr addr, - unsigned size) -{ - AspeedRtcState *rtc = opaque; - uint64_t val; - uint32_t r = addr >> 2; - - switch (r) { - case COUNTER1: - case COUNTER2: - if (rtc->reg[CONTROL] & RTC_ENABLED) { - rtc->reg[r] = aspeed_rtc_get_counter(rtc, r); - } - /* fall through */ - case CONTROL: - val = rtc->reg[r]; - break; - case ALARM: - case ALARM_STATUS: - default: - qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr); - return 0; - } - - trace_aspeed_rtc_read(addr, val); - - return val; -} - -static void aspeed_rtc_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - AspeedRtcState *rtc = opaque; - uint32_t r = addr >> 2; - - switch (r) { - case COUNTER1: - case COUNTER2: - if (!(rtc->reg[CONTROL] & RTC_UNLOCKED)) { - break; - } - /* fall through */ - case CONTROL: - rtc->reg[r] = val; - aspeed_rtc_calc_offset(rtc); - break; - case ALARM: - case ALARM_STATUS: - default: - qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx "\n", __func__, addr); - break; - } - trace_aspeed_rtc_write(addr, val); -} - -static void aspeed_rtc_reset(DeviceState *d) -{ - AspeedRtcState *rtc = ASPEED_RTC(d); - - rtc->offset = 0; - memset(rtc->reg, 0, sizeof(rtc->reg)); -} - -static const MemoryRegionOps aspeed_rtc_ops = { - .read = aspeed_rtc_read, - .write = aspeed_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_aspeed_rtc = { - .name = TYPE_ASPEED_RTC, - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(reg, AspeedRtcState, 0x18), - VMSTATE_INT32(offset, AspeedRtcState), - VMSTATE_INT32(offset, AspeedRtcState), - VMSTATE_END_OF_LIST() - } -}; - -static void aspeed_rtc_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - AspeedRtcState *s = ASPEED_RTC(dev); - - sysbus_init_irq(sbd, &s->irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_rtc_ops, s, - "aspeed-rtc", 0x18ULL); - sysbus_init_mmio(sbd, &s->iomem); -} - -static void aspeed_rtc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = aspeed_rtc_realize; - dc->vmsd = &vmstate_aspeed_rtc; - dc->reset = aspeed_rtc_reset; -} - -static const TypeInfo aspeed_rtc_info = { - .name = TYPE_ASPEED_RTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AspeedRtcState), - .class_init = aspeed_rtc_class_init, -}; - -static void aspeed_rtc_register_types(void) -{ - type_register_static(&aspeed_rtc_info); -} - -type_init(aspeed_rtc_register_types) diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 1459d07237..e18b87fc96 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -66,10 +66,6 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset" -# hw/timer/aspeed-rtc.c -aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 -aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 - # nrf51_timer.c nrf51_timer_read(uint64_t addr, uint32_t value, unsigned size) "read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" nrf51_timer_write(uint64_t addr, uint32_t value, unsigned size) "write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index cccb684a19..495c08be1b 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -18,7 +18,7 @@ #include "hw/misc/aspeed_sdmc.h" #include "hw/misc/aspeed_xdma.h" #include "hw/timer/aspeed_timer.h" -#include "hw/timer/aspeed_rtc.h" +#include "hw/rtc/aspeed_rtc.h" #include "hw/i2c/aspeed_i2c.h" #include "hw/ssi/aspeed_smc.h" #include "hw/watchdog/wdt_aspeed.h" diff --git a/include/hw/rtc/aspeed_rtc.h b/include/hw/rtc/aspeed_rtc.h new file mode 100644 index 0000000000..3fde854ad9 --- /dev/null +++ b/include/hw/rtc/aspeed_rtc.h @@ -0,0 +1,30 @@ +/* + * ASPEED Real Time Clock + * Joel Stanley + * + * Copyright 2019 IBM Corp + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_RTC_ASPEED_RTC_H +#define HW_RTC_ASPEED_RTC_H + +#include + +#include "hw/irq.h" +#include "hw/sysbus.h" + +typedef struct AspeedRtcState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + + uint32_t reg[0x18]; + int offset; + +} AspeedRtcState; + +#define TYPE_ASPEED_RTC "aspeed.rtc" +#define ASPEED_RTC(obj) OBJECT_CHECK(AspeedRtcState, (obj), TYPE_ASPEED_RTC) + +#endif /* HW_RTC_ASPEED_RTC_H */ diff --git a/include/hw/timer/aspeed_rtc.h b/include/hw/timer/aspeed_rtc.h deleted file mode 100644 index 15ba42912b..0000000000 --- a/include/hw/timer/aspeed_rtc.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * ASPEED Real Time Clock - * Joel Stanley - * - * Copyright 2019 IBM Corp - * SPDX-License-Identifier: GPL-2.0-or-later - */ -#ifndef ASPEED_RTC_H -#define ASPEED_RTC_H - -#include - -#include "hw/irq.h" -#include "hw/sysbus.h" - -typedef struct AspeedRtcState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - - uint32_t reg[0x18]; - int offset; - -} AspeedRtcState; - -#define TYPE_ASPEED_RTC "aspeed.rtc" -#define ASPEED_RTC(obj) OBJECT_CHECK(AspeedRtcState, (obj), TYPE_ASPEED_RTC) - -#endif /* ASPEED_RTC_H */ -- cgit v1.2.3-55-g7522 From 7ffcb73d3f16bfffa946e4fc780292725638953f Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Fri, 4 Oct 2019 01:04:02 +0200 Subject: hw/rtc/mc146818: Include mc146818rtc_regs.h a bit less Only 2 source files require the "mc146818rtc_regs.h" header. Instead of having it processed 12 times, by all objects using "mc146818rtc.h", include it directly where used. Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191003230404.19384-13-philmd@redhat.com> Signed-off-by: Laurent Vivier --- hw/rtc/mc146818rtc.c | 1 + hw/timer/hpet.c | 1 + include/hw/rtc/mc146818rtc.h | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index ced15f764f..9d4ed54f65 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -35,6 +35,7 @@ #include "sysemu/reset.h" #include "sysemu/runstate.h" #include "hw/rtc/mc146818rtc.h" +#include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 02bf8a8ce8..9f17aaa278 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -34,6 +34,7 @@ #include "hw/timer/hpet.h" #include "hw/sysbus.h" #include "hw/rtc/mc146818rtc.h" +#include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "hw/timer/i8254.h" diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 2e9331637a..7fa59d4279 100644 --- a/include/hw/rtc/mc146818rtc.h +++ b/include/hw/rtc/mc146818rtc.h @@ -10,7 +10,6 @@ #define HW_RTC_MC146818RTC_H #include "hw/isa/isa.h" -#include "hw/rtc/mc146818rtc_regs.h" #define TYPE_MC146818_RTC "mc146818rtc" -- cgit v1.2.3-55-g7522 From fabb862f76f093cdd1610571de9ba714d3333c1c Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daudé Date: Fri, 4 Oct 2019 01:04:04 +0200 Subject: hw/rtc/aspeed_rtc: Remove unused includes The system include is already provided by "osdep.h" (the scripts/clean-includes file clean such headers). Commit 64552b6be47 suggests we don't need to include "hw/irq.h": Move the qemu_irq and qemu_irq_handler typedefs from hw/irq.h to qemu/typedefs.h, and then include hw/irq.h only where it's still needed. Reviewed-by: Cédric Le Goater Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Acked-by: Peter Maydell Message-Id: <20191003230404.19384-15-philmd@redhat.com> Signed-off-by: Laurent Vivier --- include/hw/rtc/aspeed_rtc.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/hw/rtc/aspeed_rtc.h b/include/hw/rtc/aspeed_rtc.h index 3fde854ad9..b94a710268 100644 --- a/include/hw/rtc/aspeed_rtc.h +++ b/include/hw/rtc/aspeed_rtc.h @@ -8,9 +8,6 @@ #ifndef HW_RTC_ASPEED_RTC_H #define HW_RTC_ASPEED_RTC_H -#include - -#include "hw/irq.h" #include "hw/sysbus.h" typedef struct AspeedRtcState { -- cgit v1.2.3-55-g7522