Hi, On Mon, 2015-09-14 at 22:07 +0200, Daniel Fahlgren wrote: > This patch adds support to emulate the watchdog functionality on the > Winbond w83627thf chip. The other features of the chip are not emulated. > It has been tested with Ubuntu 6.06, 14.04 and 15.04 as guests using the > w83627hf_wdt module.
Ping, who should I poke about this? The maintainers file does not mention the watchdog system. > Signed-off-by: Daniel Fahlgren <dan...@fahlgren.se> > --- > default-configs/i386-softmmu.mak | 1 + > default-configs/x86_64-softmmu.mak | 1 + > hw/watchdog/Makefile.objs | 1 + > hw/watchdog/wdt_w83627thf.c | 255 > +++++++++++++++++++++++++++++++++++++ > 4 files changed, 258 insertions(+) > create mode 100644 hw/watchdog/wdt_w83627thf.c > > diff --git a/default-configs/i386-softmmu.mak > b/default-configs/i386-softmmu.mak > index 9393cf0..30abc6f 100644 > --- a/default-configs/i386-softmmu.mak > +++ b/default-configs/i386-softmmu.mak > @@ -35,6 +35,7 @@ CONFIG_MC146818RTC=y > CONFIG_PAM=y > CONFIG_PCI_PIIX=y > CONFIG_WDT_IB700=y > +CONFIG_WDT_W83627THF=y > CONFIG_XEN_I386=$(CONFIG_XEN) > CONFIG_ISA_DEBUG=y > CONFIG_ISA_TESTDEV=y > diff --git a/default-configs/x86_64-softmmu.mak > b/default-configs/x86_64-softmmu.mak > index 28e2099..906d14b 100644 > --- a/default-configs/x86_64-softmmu.mak > +++ b/default-configs/x86_64-softmmu.mak > @@ -35,6 +35,7 @@ CONFIG_MC146818RTC=y > CONFIG_PAM=y > CONFIG_PCI_PIIX=y > CONFIG_WDT_IB700=y > +CONFIG_WDT_W83627THF=y > CONFIG_XEN_I386=$(CONFIG_XEN) > CONFIG_ISA_DEBUG=y > CONFIG_ISA_TESTDEV=y > diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs > index 72e3ffd..e021b24 100644 > --- a/hw/watchdog/Makefile.objs > +++ b/hw/watchdog/Makefile.objs > @@ -2,3 +2,4 @@ common-obj-y += watchdog.o > common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o > common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o > common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o > +common-obj-$(CONFIG_WDT_W83627THF) += wdt_w83627thf.o > diff --git a/hw/watchdog/wdt_w83627thf.c b/hw/watchdog/wdt_w83627thf.c > new file mode 100644 > index 0000000..143bb8f > --- /dev/null > +++ b/hw/watchdog/wdt_w83627thf.c > @@ -0,0 +1,255 @@ > +/* > + * Virtual hardware watchdog. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that 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/>. > + * > + * By Daniel Fahlgren (dan...@fahlgren.se) > + */ > + > +#include <inttypes.h> > + > +#include "qemu-common.h" > +#include "qemu/timer.h" > +#include "sysemu/watchdog.h" > +#include "hw/isa/isa.h" > + > +/* #define W83627THF_DEBUG 1 */ > + > +#ifdef W83627THF_DEBUG > +#define w83627thf_debug(fs, ...) \ > + fprintf(stderr, "w83627thf: %s: "fs, __func__, ##__VA_ARGS__) > +#else > +#define w83627thf_debug(fs, ...) > +#endif > + > +#define WATCHDOG_W83627THF_DEVICE(obj) \ > + OBJECT_CHECK(W83627THFState, (obj), "w83627thf") > + > +#define CHIP_VERSION 0x82 > + > +#define CHIP_VERSION_REGISTER 0x20 > +#define PLED_MODE_REGISTER 0xF5 > +#define TIMEOUT_REGISTER 0xF6 > +#define TIMER_REGISTER 0xF7 > + > +#define PLED_MINUTE_MODE 0x08 > + > +#define WDT_W83627THF_EFER 0x2E > +#define WDT_W83627THF_EFDR 0x2F > + > +enum { > + normal_mode = 0, > + extended_mode1 = 1, > + extended_mode2 = 2 > +}; > + > +/* Device state. */ > +typedef struct W83627THFState { > + ISADevice parent_obj; > + > + QEMUTimer *timer; > + > + PortioList port_list; > + > + uint8_t running_mode; > + > + uint8_t selected_register; > + > + uint8_t pled_mode_register; > + uint8_t timeout_register; > + uint8_t timer_register; > + > +} W83627THFState; > + > +static WatchdogTimerModel model = { > + .wdt_name = "w83627thf", > + .wdt_description = "Winbond w83627thf", > +}; > + > +static const VMStateDescription vmstate_w83627thf = { > + .name = "vmstate_w83627thf", > + .version_id = 0, > + .minimum_version_id = 0, > + .fields = (VMStateField[]) { > + VMSTATE_TIMER_PTR(timer, W83627THFState), > + VMSTATE_UINT8(running_mode, W83627THFState), > + VMSTATE_UINT8(selected_register, W83627THFState), > + VMSTATE_UINT8(pled_mode_register, W83627THFState), > + VMSTATE_UINT8(timeout_register, W83627THFState), > + VMSTATE_UINT8(timer_register, W83627THFState), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +/* This function is called when the watchdog has been changed, either when > the > + * timer has expired or has been keep-alived. > + */ > +static void wdt_w83627thf_restart_timer(W83627THFState *state) > +{ > + uint64_t timeout = 1000; > + > + if (state->timeout_register == 0) { > + timer_del(state->timer); > + return; > + } > + > + if (state->pled_mode_register & PLED_MINUTE_MODE) { > + timeout = 60000; > + } > + > + timer_mod(state->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + timeout); > +} > + > +/* This function is called when the timer has expired. Will count down the > + * counter and possibly fire the watchdog. > + */ > +static void wdt_w83627thf_timer_tick(void *vp) > +{ > + W83627THFState *state = vp; > + > + state->timeout_register--; > + if (state->timeout_register == 0) { > + state->timer_register |= 0x10; > + timer_del(state->timer); > + watchdog_perform_action(); > + return; > + } > + > + wdt_w83627thf_restart_timer(state); > +} > + > +/* This function is called when writing to the Extended Function Enable > + * Registers. > + */ > +static void wdt_w83627thf_write_efer(void *vp, uint32_t addr, uint32_t data) > +{ > + W83627THFState *state = vp; > + > + w83627thf_debug("data = %x\n", data); > + > + if (data == 0x87) { > + if (state->running_mode == normal_mode) { > + state->running_mode = extended_mode1; > + } else { > + state->running_mode = extended_mode2; > + } > + } else if (data == 0xAA) { > + state->running_mode = normal_mode; > + } else if (state->running_mode == extended_mode2) { > + state->selected_register = data; > + } > +} > + > +/* This function is called when reading from the Extended Function Data > + * Register. > + */ > +static uint32_t wdt_w83627thf_read_efdr(void *vp, uint32_t addr) > +{ > + uint8_t data = 0; > + const W83627THFState *state = vp; > + > + switch (state->selected_register) { > + case CHIP_VERSION_REGISTER: > + data = CHIP_VERSION; > + break; > + case PLED_MODE_REGISTER: > + data = state->pled_mode_register; > + break; > + case TIMEOUT_REGISTER: > + data = state->timeout_register; > + break; > + case TIMER_REGISTER: > + data = state->timer_register; > + break; > + } > + > + w83627thf_debug("reg = %x, data = %x\n", state->selected_register, data); > + > + return data; > +} > + > +/* This function is called when writing to the Extended Function Data > Register. > + */ > +static void wdt_w83627thf_write_efdr(void *vp, uint32_t addr, uint32_t data) > +{ > + W83627THFState *state = vp; > + > + w83627thf_debug("reg = %x, data = %x\n", state->selected_register, data); > + > + switch (state->selected_register) { > + case PLED_MODE_REGISTER: > + state->pled_mode_register = data; > + break; > + case TIMEOUT_REGISTER: > + state->timeout_register = data; > + wdt_w83627thf_restart_timer(state); > + break; > + case TIMER_REGISTER: > + if (data & 0x20) { > + timer_del(state->timer); > + watchdog_perform_action(); > + } > + state->timer_register = (data & ~0x20); > + break; > + } > +} > + > +static const MemoryRegionPortio wdt_portio_list[] = { > + { WDT_W83627THF_EFER, 1, 1, .write = wdt_w83627thf_write_efer, }, > + { WDT_W83627THF_EFDR, 1, 1, .read = wdt_w83627thf_read_efdr, > + .write = wdt_w83627thf_write_efdr }, > + PORTIO_END_OF_LIST(), > +}; > + > +static void wdt_w83627thf_realize(DeviceState *dev, Error **errp) > +{ > + W83627THFState *d = WATCHDOG_W83627THF_DEVICE(dev); > + > + d->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, wdt_w83627thf_timer_tick, d); > + > + portio_list_init(&d->port_list, OBJECT(d), wdt_portio_list, d, > "w83627thf"); > + portio_list_add(&d->port_list, isa_address_space_io(&d->parent_obj), 0); > +} > + > +static void wdt_w83627thf_reset(DeviceState *dev) > +{ > + W83627THFState *d = WATCHDOG_W83627THF_DEVICE(dev); > + > + timer_del(d->timer); > +} > + > +static void wdt_w83627thf_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = wdt_w83627thf_realize; > + dc->reset = wdt_w83627thf_reset; > + dc->vmsd = &vmstate_w83627thf; > + set_bit(DEVICE_CATEGORY_MISC, dc->categories); > +} > + > +static const TypeInfo w83627thf_info = { > + .name = "w83627thf", > + .parent = TYPE_ISA_DEVICE, > + .instance_size = sizeof(W83627THFState), > + .class_init = wdt_w83627thf_class_init, > +}; > + > +static void w83627thf_register_types(void) > +{ > + watchdog_add_model(&model); > + type_register_static(&w83627thf_info); > +} > + > +type_init(w83627thf_register_types) Best regards, Daniel Fahlgren