aboutsummaryrefslogtreecommitdiff
path: root/hw/watchdog/wdt_ib700.c
blob: b116c3a3aa6cbdd00ebd32c40e61fb7d3d200ced (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * Virtual hardware watchdog.
 *
 * Copyright (C) 2009 Red Hat Inc.
 *
 * 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 Richard W.M. Jones (rjones@redhat.com).
 */

#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "sysemu/watchdog.h"
#include "hw/isa/isa.h"
#include "migration/vmstate.h"
#include "qom/object.h"

/*#define IB700_DEBUG 1*/

#ifdef IB700_DEBUG
#define ib700_debug(fs,...)					\
    fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__)
#else
#define ib700_debug(fs,...)
#endif

#define TYPE_IB700 "ib700"
typedef struct IB700state IB700State;
DECLARE_INSTANCE_CHECKER(IB700State, IB700,
                         TYPE_IB700)

struct IB700state {
    ISADevice parent_obj;

    QEMUTimer *timer;

    PortioList port_list;
};

/* This is the timer.  We use a global here because the watchdog
 * code ensures there is only one watchdog (it is located at a fixed,
 * unchangeable IO port, so there could only ever be one anyway).
 */

/* A write to this register enables the timer. */
static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data)
{
    IB700State *s = vp;
    static int time_map[] = {
        30, 28, 26, 24, 22, 20, 18, 16,
        14, 12, 10,  8,  6,  4,  2,  0
    };
    int64_t timeout;

    ib700_debug("addr = %x, data = %x\n", addr, data);

    timeout = (int64_t) time_map[data & 0xF] * NANOSECONDS_PER_SECOND;
    timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout);
}

/* A write (of any value) to this register disables the timer. */
static void ib700_write_disable_reg(void *vp, uint32_t addr, uint32_t data)
{
    IB700State *s = vp;

    ib700_debug("addr = %x, data = %x\n", addr, data);

    timer_del(s->timer);
}

/* This is called when the watchdog expires. */
static void ib700_timer_expired(void *vp)
{
    IB700State *s = vp;

    ib700_debug("watchdog expired\n");

    watchdog_perform_action();
    timer_del(s->timer);
}

static const VMStateDescription vmstate_ib700 = {
    .name = "ib700_wdt",
    .version_id = 0,
    .minimum_version_id = 0,
    .fields = (VMStateField[]) {
        VMSTATE_TIMER_PTR(timer, IB700State),
        VMSTATE_END_OF_LIST()
    }
};

static const MemoryRegionPortio wdt_portio_list[] = {
    { 0x441, 2, 1, .write = ib700_write_disable_reg, },
    { 0x443, 2, 1, .write = ib700_write_enable_reg, },
    PORTIO_END_OF_LIST(),
};

static void wdt_ib700_realize(DeviceState *dev, Error **errp)
{
    IB700State *s = IB700(dev);

    ib700_debug("watchdog init\n");

    s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ib700_timer_expired, s);

    portio_list_init(&s->port_list, OBJECT(s), wdt_portio_list, s, "ib700");
    portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj), 0);
}

static void wdt_ib700_reset(DeviceState *dev)
{
    IB700State *s = IB700(dev);

    ib700_debug("watchdog reset\n");

    timer_del(s->timer);
}

static void wdt_ib700_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->realize = wdt_ib700_realize;
    dc->reset = wdt_ib700_reset;
    dc->vmsd = &vmstate_ib700;
    set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
    dc->desc = "iBASE 700";
}

static const TypeInfo wdt_ib700_info = {
    .name          = TYPE_IB700,
    .parent        = TYPE_ISA_DEVICE,
    .instance_size = sizeof(IB700State),
    .class_init    = wdt_ib700_class_init,
};

static void wdt_ib700_register_types(void)
{
    type_register_static(&wdt_ib700_info);
}

type_init(wdt_ib700_register_types)