xref: /qemu/hw/watchdog/wdt_aspeed.c (revision 854123bf8d4b8f4dcedcb03d0201e4305da45fe8)
1*854123bfSCédric Le Goater /*
2*854123bfSCédric Le Goater  * ASPEED Watchdog Controller
3*854123bfSCédric Le Goater  *
4*854123bfSCédric Le Goater  * Copyright (C) 2016-2017 IBM Corp.
5*854123bfSCédric Le Goater  *
6*854123bfSCédric Le Goater  * This code is licensed under the GPL version 2 or later. See the
7*854123bfSCédric Le Goater  * COPYING file in the top-level directory.
8*854123bfSCédric Le Goater  */
9*854123bfSCédric Le Goater 
10*854123bfSCédric Le Goater #include "qemu/osdep.h"
11*854123bfSCédric Le Goater #include "qemu/log.h"
12*854123bfSCédric Le Goater #include "sysemu/watchdog.h"
13*854123bfSCédric Le Goater #include "hw/sysbus.h"
14*854123bfSCédric Le Goater #include "qemu/timer.h"
15*854123bfSCédric Le Goater #include "hw/watchdog/wdt_aspeed.h"
16*854123bfSCédric Le Goater 
17*854123bfSCédric Le Goater #define WDT_STATUS              (0x00 / 4)
18*854123bfSCédric Le Goater #define WDT_RELOAD_VALUE        (0x04 / 4)
19*854123bfSCédric Le Goater #define WDT_RESTART             (0x08 / 4)
20*854123bfSCédric Le Goater #define WDT_CTRL                (0x0C / 4)
21*854123bfSCédric Le Goater #define   WDT_CTRL_RESET_MODE_SOC       (0x00 << 5)
22*854123bfSCédric Le Goater #define   WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5)
23*854123bfSCédric Le Goater #define   WDT_CTRL_1MHZ_CLK             BIT(4)
24*854123bfSCédric Le Goater #define   WDT_CTRL_WDT_EXT              BIT(3)
25*854123bfSCédric Le Goater #define   WDT_CTRL_WDT_INTR             BIT(2)
26*854123bfSCédric Le Goater #define   WDT_CTRL_RESET_SYSTEM         BIT(1)
27*854123bfSCédric Le Goater #define   WDT_CTRL_ENABLE               BIT(0)
28*854123bfSCédric Le Goater 
29*854123bfSCédric Le Goater #define WDT_TIMEOUT_STATUS      (0x10 / 4)
30*854123bfSCédric Le Goater #define WDT_TIMEOUT_CLEAR       (0x14 / 4)
31*854123bfSCédric Le Goater #define WDT_RESET_WDITH         (0x18 / 4)
32*854123bfSCédric Le Goater 
33*854123bfSCédric Le Goater #define WDT_RESTART_MAGIC       0x4755
34*854123bfSCédric Le Goater 
35*854123bfSCédric Le Goater static bool aspeed_wdt_is_enabled(const AspeedWDTState *s)
36*854123bfSCédric Le Goater {
37*854123bfSCédric Le Goater     return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE;
38*854123bfSCédric Le Goater }
39*854123bfSCédric Le Goater 
40*854123bfSCédric Le Goater static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size)
41*854123bfSCédric Le Goater {
42*854123bfSCédric Le Goater     AspeedWDTState *s = ASPEED_WDT(opaque);
43*854123bfSCédric Le Goater 
44*854123bfSCédric Le Goater     offset >>= 2;
45*854123bfSCédric Le Goater 
46*854123bfSCédric Le Goater     switch (offset) {
47*854123bfSCédric Le Goater     case WDT_STATUS:
48*854123bfSCédric Le Goater         return s->regs[WDT_STATUS];
49*854123bfSCédric Le Goater     case WDT_RELOAD_VALUE:
50*854123bfSCédric Le Goater         return s->regs[WDT_RELOAD_VALUE];
51*854123bfSCédric Le Goater     case WDT_RESTART:
52*854123bfSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR,
53*854123bfSCédric Le Goater                       "%s: read from write-only reg at offset 0x%"
54*854123bfSCédric Le Goater                       HWADDR_PRIx "\n", __func__, offset);
55*854123bfSCédric Le Goater         return 0;
56*854123bfSCédric Le Goater     case WDT_CTRL:
57*854123bfSCédric Le Goater         return s->regs[WDT_CTRL];
58*854123bfSCédric Le Goater     case WDT_TIMEOUT_STATUS:
59*854123bfSCédric Le Goater     case WDT_TIMEOUT_CLEAR:
60*854123bfSCédric Le Goater     case WDT_RESET_WDITH:
61*854123bfSCédric Le Goater         qemu_log_mask(LOG_UNIMP,
62*854123bfSCédric Le Goater                       "%s: uninmplemented read at offset 0x%" HWADDR_PRIx "\n",
63*854123bfSCédric Le Goater                       __func__, offset);
64*854123bfSCédric Le Goater         return 0;
65*854123bfSCédric Le Goater     default:
66*854123bfSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR,
67*854123bfSCédric Le Goater                       "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
68*854123bfSCédric Le Goater                       __func__, offset);
69*854123bfSCédric Le Goater         return 0;
70*854123bfSCédric Le Goater     }
71*854123bfSCédric Le Goater 
72*854123bfSCédric Le Goater }
73*854123bfSCédric Le Goater 
74*854123bfSCédric Le Goater static void aspeed_wdt_reload(AspeedWDTState *s, bool pclk)
75*854123bfSCédric Le Goater {
76*854123bfSCédric Le Goater     uint32_t reload;
77*854123bfSCédric Le Goater 
78*854123bfSCédric Le Goater     if (pclk) {
79*854123bfSCédric Le Goater         reload = muldiv64(s->regs[WDT_RELOAD_VALUE], NANOSECONDS_PER_SECOND,
80*854123bfSCédric Le Goater                           s->pclk_freq);
81*854123bfSCédric Le Goater     } else {
82*854123bfSCédric Le Goater         reload = s->regs[WDT_RELOAD_VALUE] * 1000;
83*854123bfSCédric Le Goater     }
84*854123bfSCédric Le Goater 
85*854123bfSCédric Le Goater     if (aspeed_wdt_is_enabled(s)) {
86*854123bfSCédric Le Goater         timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload);
87*854123bfSCédric Le Goater     }
88*854123bfSCédric Le Goater }
89*854123bfSCédric Le Goater 
90*854123bfSCédric Le Goater static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data,
91*854123bfSCédric Le Goater                              unsigned size)
92*854123bfSCédric Le Goater {
93*854123bfSCédric Le Goater     AspeedWDTState *s = ASPEED_WDT(opaque);
94*854123bfSCédric Le Goater     bool enable = data & WDT_CTRL_ENABLE;
95*854123bfSCédric Le Goater 
96*854123bfSCédric Le Goater     offset >>= 2;
97*854123bfSCédric Le Goater 
98*854123bfSCédric Le Goater     switch (offset) {
99*854123bfSCédric Le Goater     case WDT_STATUS:
100*854123bfSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR,
101*854123bfSCédric Le Goater                       "%s: write to read-only reg at offset 0x%"
102*854123bfSCédric Le Goater                       HWADDR_PRIx "\n", __func__, offset);
103*854123bfSCédric Le Goater         break;
104*854123bfSCédric Le Goater     case WDT_RELOAD_VALUE:
105*854123bfSCédric Le Goater         s->regs[WDT_RELOAD_VALUE] = data;
106*854123bfSCédric Le Goater         break;
107*854123bfSCédric Le Goater     case WDT_RESTART:
108*854123bfSCédric Le Goater         if ((data & 0xFFFF) == WDT_RESTART_MAGIC) {
109*854123bfSCédric Le Goater             s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE];
110*854123bfSCédric Le Goater             aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK));
111*854123bfSCédric Le Goater         }
112*854123bfSCédric Le Goater         break;
113*854123bfSCédric Le Goater     case WDT_CTRL:
114*854123bfSCédric Le Goater         if (enable && !aspeed_wdt_is_enabled(s)) {
115*854123bfSCédric Le Goater             s->regs[WDT_CTRL] = data;
116*854123bfSCédric Le Goater             aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK));
117*854123bfSCédric Le Goater         } else if (!enable && aspeed_wdt_is_enabled(s)) {
118*854123bfSCédric Le Goater             s->regs[WDT_CTRL] = data;
119*854123bfSCédric Le Goater             timer_del(s->timer);
120*854123bfSCédric Le Goater         }
121*854123bfSCédric Le Goater         break;
122*854123bfSCédric Le Goater     case WDT_TIMEOUT_STATUS:
123*854123bfSCédric Le Goater     case WDT_TIMEOUT_CLEAR:
124*854123bfSCédric Le Goater     case WDT_RESET_WDITH:
125*854123bfSCédric Le Goater         qemu_log_mask(LOG_UNIMP,
126*854123bfSCédric Le Goater                       "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n",
127*854123bfSCédric Le Goater                       __func__, offset);
128*854123bfSCédric Le Goater         break;
129*854123bfSCédric Le Goater     default:
130*854123bfSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR,
131*854123bfSCédric Le Goater                       "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
132*854123bfSCédric Le Goater                       __func__, offset);
133*854123bfSCédric Le Goater     }
134*854123bfSCédric Le Goater     return;
135*854123bfSCédric Le Goater }
136*854123bfSCédric Le Goater 
137*854123bfSCédric Le Goater static WatchdogTimerModel model = {
138*854123bfSCédric Le Goater     .wdt_name = TYPE_ASPEED_WDT,
139*854123bfSCédric Le Goater     .wdt_description = "Aspeed watchdog device",
140*854123bfSCédric Le Goater };
141*854123bfSCédric Le Goater 
142*854123bfSCédric Le Goater static const VMStateDescription vmstate_aspeed_wdt = {
143*854123bfSCédric Le Goater     .name = "vmstate_aspeed_wdt",
144*854123bfSCédric Le Goater     .version_id = 0,
145*854123bfSCédric Le Goater     .minimum_version_id = 0,
146*854123bfSCédric Le Goater     .fields = (VMStateField[]) {
147*854123bfSCédric Le Goater         VMSTATE_TIMER_PTR(timer, AspeedWDTState),
148*854123bfSCédric Le Goater         VMSTATE_UINT32_ARRAY(regs, AspeedWDTState, ASPEED_WDT_REGS_MAX),
149*854123bfSCédric Le Goater         VMSTATE_END_OF_LIST()
150*854123bfSCédric Le Goater     }
151*854123bfSCédric Le Goater };
152*854123bfSCédric Le Goater 
153*854123bfSCédric Le Goater static const MemoryRegionOps aspeed_wdt_ops = {
154*854123bfSCédric Le Goater     .read = aspeed_wdt_read,
155*854123bfSCédric Le Goater     .write = aspeed_wdt_write,
156*854123bfSCédric Le Goater     .endianness = DEVICE_LITTLE_ENDIAN,
157*854123bfSCédric Le Goater     .valid.min_access_size = 4,
158*854123bfSCédric Le Goater     .valid.max_access_size = 4,
159*854123bfSCédric Le Goater     .valid.unaligned = false,
160*854123bfSCédric Le Goater };
161*854123bfSCédric Le Goater 
162*854123bfSCédric Le Goater static void aspeed_wdt_reset(DeviceState *dev)
163*854123bfSCédric Le Goater {
164*854123bfSCédric Le Goater     AspeedWDTState *s = ASPEED_WDT(dev);
165*854123bfSCédric Le Goater 
166*854123bfSCédric Le Goater     s->regs[WDT_STATUS] = 0x3EF1480;
167*854123bfSCédric Le Goater     s->regs[WDT_RELOAD_VALUE] = 0x03EF1480;
168*854123bfSCédric Le Goater     s->regs[WDT_RESTART] = 0;
169*854123bfSCédric Le Goater     s->regs[WDT_CTRL] = 0;
170*854123bfSCédric Le Goater 
171*854123bfSCédric Le Goater     timer_del(s->timer);
172*854123bfSCédric Le Goater }
173*854123bfSCédric Le Goater 
174*854123bfSCédric Le Goater static void aspeed_wdt_timer_expired(void *dev)
175*854123bfSCédric Le Goater {
176*854123bfSCédric Le Goater     AspeedWDTState *s = ASPEED_WDT(dev);
177*854123bfSCédric Le Goater 
178*854123bfSCédric Le Goater     qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
179*854123bfSCédric Le Goater     watchdog_perform_action();
180*854123bfSCédric Le Goater     timer_del(s->timer);
181*854123bfSCédric Le Goater }
182*854123bfSCédric Le Goater 
183*854123bfSCédric Le Goater #define PCLK_HZ 24000000
184*854123bfSCédric Le Goater 
185*854123bfSCédric Le Goater static void aspeed_wdt_realize(DeviceState *dev, Error **errp)
186*854123bfSCédric Le Goater {
187*854123bfSCédric Le Goater     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
188*854123bfSCédric Le Goater     AspeedWDTState *s = ASPEED_WDT(dev);
189*854123bfSCédric Le Goater 
190*854123bfSCédric Le Goater     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev);
191*854123bfSCédric Le Goater 
192*854123bfSCédric Le Goater     /* FIXME: This setting should be derived from the SCU hw strapping
193*854123bfSCédric Le Goater      * register SCU70
194*854123bfSCédric Le Goater      */
195*854123bfSCédric Le Goater     s->pclk_freq = PCLK_HZ;
196*854123bfSCédric Le Goater 
197*854123bfSCédric Le Goater     memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s,
198*854123bfSCédric Le Goater                           TYPE_ASPEED_WDT, ASPEED_WDT_REGS_MAX * 4);
199*854123bfSCédric Le Goater     sysbus_init_mmio(sbd, &s->iomem);
200*854123bfSCédric Le Goater }
201*854123bfSCédric Le Goater 
202*854123bfSCédric Le Goater static void aspeed_wdt_class_init(ObjectClass *klass, void *data)
203*854123bfSCédric Le Goater {
204*854123bfSCédric Le Goater     DeviceClass *dc = DEVICE_CLASS(klass);
205*854123bfSCédric Le Goater 
206*854123bfSCédric Le Goater     dc->realize = aspeed_wdt_realize;
207*854123bfSCédric Le Goater     dc->reset = aspeed_wdt_reset;
208*854123bfSCédric Le Goater     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
209*854123bfSCédric Le Goater     dc->vmsd = &vmstate_aspeed_wdt;
210*854123bfSCédric Le Goater }
211*854123bfSCédric Le Goater 
212*854123bfSCédric Le Goater static const TypeInfo aspeed_wdt_info = {
213*854123bfSCédric Le Goater     .parent = TYPE_SYS_BUS_DEVICE,
214*854123bfSCédric Le Goater     .name  = TYPE_ASPEED_WDT,
215*854123bfSCédric Le Goater     .instance_size  = sizeof(AspeedWDTState),
216*854123bfSCédric Le Goater     .class_init = aspeed_wdt_class_init,
217*854123bfSCédric Le Goater };
218*854123bfSCédric Le Goater 
219*854123bfSCédric Le Goater static void wdt_aspeed_register_types(void)
220*854123bfSCédric Le Goater {
221*854123bfSCédric Le Goater     watchdog_add_model(&model);
222*854123bfSCédric Le Goater     type_register_static(&aspeed_wdt_info);
223*854123bfSCédric Le Goater }
224*854123bfSCédric Le Goater 
225*854123bfSCédric Le Goater type_init(wdt_aspeed_register_types)
226