[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v3 1/3] hw/misc: sifive_e_aon: Support the watchdog timer of
From: |
Alistair Francis |
Subject: |
Re: [PATCH v3 1/3] hw/misc: sifive_e_aon: Support the watchdog timer of HiFive 1 rev b. |
Date: |
Wed, 7 Dec 2022 14:02:18 +1000 |
On Wed, Nov 30, 2022 at 11:56 AM Tommy Wu <tommy.wu@sifive.com> wrote:
>
> The watchdog timer is in the always-on domain device of HiFive 1 rev b,
> so this patch added the AON device to the sifive_e machine. This patch
> only implemented the functionality of the watchdog timer.
>
> Signed-off-by: Tommy Wu <tommy.wu@sifive.com>
> ---
> hw/misc/Kconfig | 3 +
> hw/misc/meson.build | 1 +
> hw/misc/sifive_e_aon.c | 383 +++++++++++++++++++++++++++++++++
> include/hw/misc/sifive_e_aon.h | 60 ++++++
> 4 files changed, 447 insertions(+)
> create mode 100644 hw/misc/sifive_e_aon.c
> create mode 100644 include/hw/misc/sifive_e_aon.h
>
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index cbabe9f78c..7d1247822e 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -162,6 +162,9 @@ config SIFIVE_TEST
> config SIFIVE_E_PRCI
> bool
>
> +config SIFIVE_E_AON
> + bool
> +
> config SIFIVE_U_OTP
> bool
>
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 95268eddc0..94170dce76 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -31,6 +31,7 @@ softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true:
> files('mchp_pfsoc_ioscb
> softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true:
> files('mchp_pfsoc_sysreg.c'))
> softmmu_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c'))
> softmmu_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true:
> files('sifive_e_prci.c'))
> +softmmu_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c'))
> softmmu_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c'))
> softmmu_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true:
> files('sifive_u_prci.c'))
>
> diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c
> new file mode 100644
> index 0000000000..27ec26cf7c
> --- /dev/null
> +++ b/hw/misc/sifive_e_aon.c
> @@ -0,0 +1,383 @@
> +/*
> + * SiFive HiFive1 AON (Always On Domain) for QEMU.
> + *
> + * Copyright (c) 2022 SiFive, Inc. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/timer.h"
> +#include "qemu/log.h"
> +#include "hw/irq.h"
> +#include "hw/registerfields.h"
> +#include "hw/misc/sifive_e_aon.h"
> +#include "qapi/visitor.h"
> +#include "qapi/error.h"
> +#include "sysemu/watchdog.h"
> +
> +REG32(AON_WDT_WDOGCFG, 0x0)
> + FIELD(AON_WDT_WDOGCFG,
> + SCALE, 0, 4)
> + FIELD(AON_WDT_WDOGCFG,
> + RSVD0, 4, 4)
> + FIELD(AON_WDT_WDOGCFG,
> + RSTEN, 8, 1)
> + FIELD(AON_WDT_WDOGCFG,
> + ZEROCMP, 9, 1)
> + FIELD(AON_WDT_WDOGCFG,
> + RSVD1, 10, 2)
> + FIELD(AON_WDT_WDOGCFG,
> + EN_ALWAYS, 12, 1)
> + FIELD(AON_WDT_WDOGCFG,
> + EN_CORE_AWAKE, 13, 1)
> + FIELD(AON_WDT_WDOGCFG,
> + RSVD2, 14, 14)
> + FIELD(AON_WDT_WDOGCFG,
> + IP0, 28, 1)
> + FIELD(AON_WDT_WDOGCFG,
> + RSVD3, 29, 3)
> +REG32(AON_WDT_WDOGCOUNT, 0x8)
> +REG32(AON_WDT_WDOGS, 0x10)
> +REG32(AON_WDT_WDOGFEED, 0x18)
> +REG32(AON_WDT_WDOGKEY, 0x1c)
> +REG32(AON_WDT_WDOGCMP0, 0x20)
> +
> +static void sifive_e_aon_wdt_update_wdogcount(SiFiveEAONState *r)
> +{
> + int64_t now;
> + if (0 == FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + EN_ALWAYS) &&
> + 0 == FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + EN_CORE_AWAKE)) {
> + return;
> + }
> +
> + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + r->wdogcount += muldiv64(now - r->wdog_restart_time,
> + r->wdogclk_freq, NANOSECONDS_PER_SECOND);
> + /* Clean the most significant bit. */
> + r->wdogcount = ((r->wdogcount << 1) >> 1);
> + r->wdog_restart_time = now;
> +}
> +
> +static void sifive_e_aon_wdt_update_state(SiFiveEAONState *r)
> +{
> + uint16_t wdogs;
> + bool cmp_signal = false;
> + sifive_e_aon_wdt_update_wdogcount(r);
> + wdogs = (uint16_t)(r->wdogcount >>
> + FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + SCALE));
> + if (wdogs >= r->wdogcmp0) {
> + cmp_signal = true;
> + if (1 == FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + ZEROCMP)) {
> + r->wdogcount = 0;
> + wdogs = 0;
> + }
> + }
> +
> + if (cmp_signal) {
> + if (1 == FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + RSTEN)) {
> + watchdog_perform_action();
> + }
> + r->wdogcfg =
> + FIELD_DP32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + IP0, 1);
> + }
> +
> + qemu_set_irq(r->wdog_irq,
> + FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + IP0));
> +
> + if (wdogs < r->wdogcmp0 &&
> + (FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + EN_ALWAYS) ||
> + FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + EN_CORE_AWAKE))) {
> + int64_t next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + next += muldiv64((r->wdogcmp0 - wdogs) <<
> + FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + SCALE),
> + NANOSECONDS_PER_SECOND, r->wdogclk_freq);
> + timer_mod(r->wdog_timer, next);
> + } else {
> + timer_mod(r->wdog_timer, INT64_MAX);
> + }
> +}
> +
> +/*
> + * Callback used when the timer set using timer_mod expires.
> + */
> +static void sifive_e_aon_wdt_expired_cb(void *opaque)
> +{
> + SiFiveEAONState *r = SIFIVE_E_AON(opaque);
> + sifive_e_aon_wdt_update_state(r);
> +}
> +
> +static uint64_t
> +sifive_e_aon_wdt_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> + SiFiveEAONState *r = SIFIVE_E_AON(opaque);
> +
> + switch (addr) {
> + case A_AON_WDT_WDOGCFG:
> + return r->wdogcfg;
> + case A_AON_WDT_WDOGCOUNT:
> + sifive_e_aon_wdt_update_wdogcount(r);
> + return r->wdogcount;
> + case A_AON_WDT_WDOGS:
> + sifive_e_aon_wdt_update_wdogcount(r);
> + return r->wdogcount >>
> + FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + SCALE);
> + case A_AON_WDT_WDOGFEED:
> + return 0;
> + case A_AON_WDT_WDOGKEY:
> + return r->wdogunlock;
> + case A_AON_WDT_WDOGCMP0:
> + return r->wdogcmp0;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
> + __func__, (int)addr);
> + }
> +
> + return 0;
> +}
> +
> +static void
> +sifive_e_aon_wdt_write(void *opaque, hwaddr addr,
> + uint64_t val64, unsigned int size)
> +{
> + SiFiveEAONState *r = SIFIVE_E_AON(opaque);
> + uint32_t value = val64;
> +
> + switch (addr) {
> + case A_AON_WDT_WDOGCFG: {
> + uint8_t new_en_always;
> + uint8_t new_en_core_awake;
> + uint8_t old_en_always;
> + uint8_t old_en_core_awake;
> + if (0 == r->wdogunlock) {
> + return;
> + }
> +
> + new_en_always =
> + FIELD_EX32(value,
> + AON_WDT_WDOGCFG,
> + EN_ALWAYS);
> + new_en_core_awake =
> + FIELD_EX32(value,
> + AON_WDT_WDOGCFG,
> + EN_CORE_AWAKE);
> + old_en_always =
> + FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + EN_ALWAYS);
> + old_en_core_awake =
> + FIELD_EX32(r->wdogcfg,
> + AON_WDT_WDOGCFG,
> + EN_CORE_AWAKE);
> + if (1 == (old_en_always ||
> + old_en_core_awake) &&
> + 0 == (new_en_always ||
> + new_en_core_awake)) {
> + sifive_e_aon_wdt_update_wdogcount(r);
> + } else if (0 == (old_en_always ||
> + old_en_core_awake) &&
> + 1 == (new_en_always ||
> + new_en_core_awake)) {
> + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + }
> + r->wdogcfg = value;
> + r->wdogunlock = 0;
> + break;
> + }
> + case A_AON_WDT_WDOGCOUNT:
> + if (0 == r->wdogunlock) {
> + return;
> + }
> + r->wdogcount = ((value << 1) >> 1);
> + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + r->wdogunlock = 0;
> + break;
> + case A_AON_WDT_WDOGS:
> + return;
> + case A_AON_WDT_WDOGFEED:
> + if (0 == r->wdogunlock) {
> + return;
> + }
> + if (SIFIVE_E_AON_WDOGFEED == value) {
> + r->wdogcount = 0;
> + r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + }
> + r->wdogunlock = 0;
> + break;
> + case A_AON_WDT_WDOGKEY:
> + if (SIFIVE_E_AON_WDOGKEY == value) {
> + r->wdogunlock = 1;
> + }
> + break;
> + case A_AON_WDT_WDOGCMP0:
> + if (0 == r->wdogunlock) {
> + return;
> + }
> + r->wdogcmp0 = (uint16_t) value;
> + r->wdogunlock = 0;
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n",
> + __func__, (int)addr, (int)value);
> + }
> + sifive_e_aon_wdt_update_state(r);
> +}
> +
> +static uint64_t
> +sifive_e_aon_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> + if (addr < SIFIVE_E_AON_RTC) {
> + return sifive_e_aon_wdt_read(opaque, addr, size);
> + } else if (addr < SIFIVE_E_AON_MAX) {
> + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read: addr=0x%x\n",
> + __func__, (int)addr);
> + } else {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
> + __func__, (int)addr);
> + }
> + return 0;
> +}
> +
> +static void
> +sifive_e_aon_write(void *opaque, hwaddr addr,
> + uint64_t val64, unsigned int size)
> +{
> + if (addr < SIFIVE_E_AON_RTC) {
> + sifive_e_aon_wdt_write(opaque, addr, val64, size);
> + } else if (addr < SIFIVE_E_AON_MAX) {
> + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write: addr=0x%x\n",
> + __func__, (int)addr);
> + } else {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x\n",
> + __func__, (int)addr);
> + }
> +}
> +
> +static const MemoryRegionOps sifive_e_aon_ops = {
> + .read = sifive_e_aon_read,
> + .write = sifive_e_aon_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .impl = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + },
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +static void sifive_e_aon_reset(DeviceState *dev)
> +{
> + SiFiveEAONState *s = SIFIVE_E_AON(dev);
> +
> + s->wdogcfg =
> + FIELD_DP32(s->wdogcfg,
> + AON_WDT_WDOGCFG,
> + RSTEN, 0);
> + s->wdogcfg =
> + FIELD_DP32(s->wdogcfg,
> + AON_WDT_WDOGCFG,
> + EN_ALWAYS, 0);
> + s->wdogcfg =
> + FIELD_DP32(s->wdogcfg,
> + AON_WDT_WDOGCFG,
> + EN_CORE_AWAKE, 0);
> + s->wdogcmp0 = 0xbeef;
> +}
> +
> +static void sifive_e_aon_wdt_update_wdogclk_freq(Object *obj, Visitor *v,
> + const char *name,
> + void *opaque,
> + Error **errp) {
> + SiFiveEAONState *r = SIFIVE_E_AON(opaque);
> + uint64_t value;
> + if (!visit_type_uint64(v, name, &value, errp)) {
> + return;
> + }
> +
> + r->wdogclk_freq = value;
> + sifive_e_aon_wdt_update_state(r);
You should be able to call this from reset and then use
`device_class_set_props()` for the properties
Otherwise it looks good :)
Alistair
> +}
> +
> +static void sifive_e_aon_init(Object *obj)
> +{
> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> + SiFiveEAONState *s = SIFIVE_E_AON(obj);
> +
> + memory_region_init_io(&s->mmio, OBJECT(s), &sifive_e_aon_ops, s,
> + TYPE_SIFIVE_E_AON, SIFIVE_E_AON_MAX);
> + sysbus_init_mmio(sbd, &s->mmio);
> +
> + /* watchdog timer */
> + s->wdogclk_freq = SIFIVE_E_LFCLK_DEFAULT_FREQ;
> + sysbus_init_irq(sbd, &s->wdog_irq);
> + object_property_add(obj, "wdogclk-frequency", "uint64",
> + NULL,
> + sifive_e_aon_wdt_update_wdogclk_freq,
> + NULL, s);
> +}
> +
> +static void sifive_e_aon_realize(DeviceState *dev, Error **errp)
> +{
> + SiFiveEAONState *s = SIFIVE_E_AON(dev);
> +
> + /* watchdog timer */
> + s->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> + sifive_e_aon_wdt_expired_cb, s);
> +}
> +
> +static void sifive_e_aon_class_init(ObjectClass *oc, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(oc);
> +
> + dc->realize = sifive_e_aon_realize;
> + dc->reset = sifive_e_aon_reset;
> +}
> +
> +static const TypeInfo sifive_e_aon_info = {
> + .name = TYPE_SIFIVE_E_AON,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(SiFiveEAONState),
> + .instance_init = sifive_e_aon_init,
> + .class_init = sifive_e_aon_class_init,
> +};
> +
> +static void sifive_e_aon_register_types(void)
> +{
> + type_register_static(&sifive_e_aon_info);
> +}
> +
> +type_init(sifive_e_aon_register_types)
> diff --git a/include/hw/misc/sifive_e_aon.h b/include/hw/misc/sifive_e_aon.h
> new file mode 100644
> index 0000000000..2ae1c4139c
> --- /dev/null
> +++ b/include/hw/misc/sifive_e_aon.h
> @@ -0,0 +1,60 @@
> +/*
> + * SiFive HiFive1 AON (Always On Domain) interface.
> + *
> + * Copyright (c) 2022 SiFive, Inc. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef HW_SIFIVE_AON_H
> +#define HW_SIFIVE_AON_H
> +
> +#include "hw/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_SIFIVE_E_AON "riscv.sifive.e.aon"
> +OBJECT_DECLARE_SIMPLE_TYPE(SiFiveEAONState, SIFIVE_E_AON)
> +
> +#define SIFIVE_E_AON_WDOGKEY (0x51F15E)
> +#define SIFIVE_E_AON_WDOGFEED (0xD09F00D)
> +#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768)
> +
> +enum {
> + SIFIVE_E_AON_WDT = 0x0,
> + SIFIVE_E_AON_RTC = 0x40,
> + SIFIVE_E_AON_LFROSC = 0x70,
> + SIFIVE_E_AON_BACKUP = 0x80,
> + SIFIVE_E_AON_PMU = 0x100,
> + SIFIVE_E_AON_MAX = 0x150
> +};
> +
> +struct SiFiveEAONState {
> + /*< private >*/
> + SysBusDevice parent_obj;
> +
> + /*< public >*/
> + MemoryRegion mmio;
> +
> + /*< watchdog timer >*/
> + QEMUTimer *wdog_timer;
> + qemu_irq wdog_irq;
> + uint64_t wdog_restart_time;
> + uint64_t wdogclk_freq;
> +
> + uint32_t wdogcfg;
> + uint16_t wdogcmp0;
> + uint32_t wdogcount;
> + uint8_t wdogunlock;
> +};
> +
> +#endif
> --
> 2.27.0
>
>
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Re: [PATCH v3 1/3] hw/misc: sifive_e_aon: Support the watchdog timer of HiFive 1 rev b.,
Alistair Francis <=