wdt_imx2.c (8984B)
1/* 2 * Copyright (c) 2018, Impinj, Inc. 3 * 4 * i.MX2 Watchdog IP block 5 * 6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com> 7 * 8 * This work is licensed under the terms of the GNU GPL, version 2 or later. 9 * See the COPYING file in the top-level directory. 10 */ 11 12#include "qemu/osdep.h" 13#include "qemu/bitops.h" 14#include "qemu/module.h" 15#include "sysemu/watchdog.h" 16#include "migration/vmstate.h" 17#include "hw/qdev-properties.h" 18 19#include "hw/watchdog/wdt_imx2.h" 20 21static void imx2_wdt_interrupt(void *opaque) 22{ 23 IMX2WdtState *s = IMX2_WDT(opaque); 24 25 s->wicr |= IMX2_WDT_WICR_WTIS; 26 qemu_set_irq(s->irq, 1); 27} 28 29static void imx2_wdt_expired(void *opaque) 30{ 31 IMX2WdtState *s = IMX2_WDT(opaque); 32 33 s->wrsr = IMX2_WDT_WRSR_TOUT; 34 35 /* Perform watchdog action if watchdog is enabled */ 36 if (s->wcr & IMX2_WDT_WCR_WDE) { 37 s->wrsr = IMX2_WDT_WRSR_TOUT; 38 watchdog_perform_action(); 39 } 40} 41 42static void imx2_wdt_reset(DeviceState *dev) 43{ 44 IMX2WdtState *s = IMX2_WDT(dev); 45 46 ptimer_transaction_begin(s->timer); 47 ptimer_stop(s->timer); 48 ptimer_transaction_commit(s->timer); 49 50 if (s->pretimeout_support) { 51 ptimer_transaction_begin(s->itimer); 52 ptimer_stop(s->itimer); 53 ptimer_transaction_commit(s->itimer); 54 } 55 56 s->wicr_locked = false; 57 s->wcr_locked = false; 58 s->wcr_wde_locked = false; 59 60 s->wcr = IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS; 61 s->wsr = 0; 62 s->wrsr &= ~(IMX2_WDT_WRSR_TOUT | IMX2_WDT_WRSR_SFTW); 63 s->wicr = IMX2_WDT_WICR_WICT_DEF; 64 s->wmcr = IMX2_WDT_WMCR_PDE; 65} 66 67static uint64_t imx2_wdt_read(void *opaque, hwaddr addr, unsigned int size) 68{ 69 IMX2WdtState *s = IMX2_WDT(opaque); 70 71 switch (addr) { 72 case IMX2_WDT_WCR: 73 return s->wcr; 74 case IMX2_WDT_WSR: 75 return s->wsr; 76 case IMX2_WDT_WRSR: 77 return s->wrsr; 78 case IMX2_WDT_WICR: 79 return s->wicr; 80 case IMX2_WDT_WMCR: 81 return s->wmcr; 82 } 83 return 0; 84} 85 86static void imx_wdt2_update_itimer(IMX2WdtState *s, bool start) 87{ 88 bool running = (s->wcr & IMX2_WDT_WCR_WDE) && (s->wcr & IMX2_WDT_WCR_WT); 89 bool enabled = s->wicr & IMX2_WDT_WICR_WIE; 90 91 ptimer_transaction_begin(s->itimer); 92 if (start || !enabled) { 93 ptimer_stop(s->itimer); 94 } 95 if (running && enabled) { 96 int count = ptimer_get_count(s->timer); 97 int pretimeout = s->wicr & IMX2_WDT_WICR_WICT; 98 99 /* 100 * Only (re-)start pretimeout timer if its counter value is larger 101 * than 0. Otherwise it will fire right away and we'll get an 102 * interrupt loop. 103 */ 104 if (count > pretimeout) { 105 ptimer_set_count(s->itimer, count - pretimeout); 106 if (start) { 107 ptimer_run(s->itimer, 1); 108 } 109 } 110 } 111 ptimer_transaction_commit(s->itimer); 112} 113 114static void imx_wdt2_update_timer(IMX2WdtState *s, bool start) 115{ 116 ptimer_transaction_begin(s->timer); 117 if (start) { 118 ptimer_stop(s->timer); 119 } 120 if ((s->wcr & IMX2_WDT_WCR_WDE) && (s->wcr & IMX2_WDT_WCR_WT)) { 121 int count = (s->wcr & IMX2_WDT_WCR_WT) >> 8; 122 123 /* A value of 0 reflects one period (0.5s). */ 124 ptimer_set_count(s->timer, count + 1); 125 if (start) { 126 ptimer_run(s->timer, 1); 127 } 128 } 129 ptimer_transaction_commit(s->timer); 130 if (s->pretimeout_support) { 131 imx_wdt2_update_itimer(s, start); 132 } 133} 134 135static void imx2_wdt_write(void *opaque, hwaddr addr, 136 uint64_t value, unsigned int size) 137{ 138 IMX2WdtState *s = IMX2_WDT(opaque); 139 140 switch (addr) { 141 case IMX2_WDT_WCR: 142 if (s->wcr_locked) { 143 value &= ~IMX2_WDT_WCR_LOCK_MASK; 144 value |= (s->wicr & IMX2_WDT_WCR_LOCK_MASK); 145 } 146 s->wcr_locked = true; 147 if (s->wcr_wde_locked) { 148 value &= ~IMX2_WDT_WCR_WDE; 149 value |= (s->wicr & ~IMX2_WDT_WCR_WDE); 150 } else if (value & IMX2_WDT_WCR_WDE) { 151 s->wcr_wde_locked = true; 152 } 153 if (s->wcr_wdt_locked) { 154 value &= ~IMX2_WDT_WCR_WDT; 155 value |= (s->wicr & ~IMX2_WDT_WCR_WDT); 156 } else if (value & IMX2_WDT_WCR_WDT) { 157 s->wcr_wdt_locked = true; 158 } 159 160 s->wcr = value; 161 if (!(value & IMX2_WDT_WCR_SRS)) { 162 s->wrsr = IMX2_WDT_WRSR_SFTW; 163 } 164 if (!(value & (IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS)) || 165 (!(value & IMX2_WDT_WCR_WT) && (value & IMX2_WDT_WCR_WDE))) { 166 watchdog_perform_action(); 167 } 168 s->wcr |= IMX2_WDT_WCR_SRS; 169 imx_wdt2_update_timer(s, true); 170 break; 171 case IMX2_WDT_WSR: 172 if (s->wsr == IMX2_WDT_SEQ1 && value == IMX2_WDT_SEQ2) { 173 imx_wdt2_update_timer(s, false); 174 } 175 s->wsr = value; 176 break; 177 case IMX2_WDT_WRSR: 178 break; 179 case IMX2_WDT_WICR: 180 if (!s->pretimeout_support) { 181 return; 182 } 183 value &= IMX2_WDT_WICR_LOCK_MASK | IMX2_WDT_WICR_WTIS; 184 if (s->wicr_locked) { 185 value &= IMX2_WDT_WICR_WTIS; 186 value |= (s->wicr & IMX2_WDT_WICR_LOCK_MASK); 187 } 188 s->wicr = value | (s->wicr & IMX2_WDT_WICR_WTIS); 189 if (value & IMX2_WDT_WICR_WTIS) { 190 s->wicr &= ~IMX2_WDT_WICR_WTIS; 191 qemu_set_irq(s->irq, 0); 192 } 193 imx_wdt2_update_itimer(s, true); 194 s->wicr_locked = true; 195 break; 196 case IMX2_WDT_WMCR: 197 s->wmcr = value & IMX2_WDT_WMCR_PDE; 198 break; 199 } 200} 201 202static const MemoryRegionOps imx2_wdt_ops = { 203 .read = imx2_wdt_read, 204 .write = imx2_wdt_write, 205 .endianness = DEVICE_NATIVE_ENDIAN, 206 .impl = { 207 /* 208 * Our device would not work correctly if the guest was doing 209 * unaligned access. This might not be a limitation on the 210 * real device but in practice there is no reason for a guest 211 * to access this device unaligned. 212 */ 213 .min_access_size = 2, 214 .max_access_size = 2, 215 .unaligned = false, 216 }, 217}; 218 219static const VMStateDescription vmstate_imx2_wdt = { 220 .name = "imx2.wdt", 221 .fields = (VMStateField[]) { 222 VMSTATE_PTIMER(timer, IMX2WdtState), 223 VMSTATE_PTIMER(itimer, IMX2WdtState), 224 VMSTATE_BOOL(wicr_locked, IMX2WdtState), 225 VMSTATE_BOOL(wcr_locked, IMX2WdtState), 226 VMSTATE_BOOL(wcr_wde_locked, IMX2WdtState), 227 VMSTATE_BOOL(wcr_wdt_locked, IMX2WdtState), 228 VMSTATE_UINT16(wcr, IMX2WdtState), 229 VMSTATE_UINT16(wsr, IMX2WdtState), 230 VMSTATE_UINT16(wrsr, IMX2WdtState), 231 VMSTATE_UINT16(wmcr, IMX2WdtState), 232 VMSTATE_UINT16(wicr, IMX2WdtState), 233 VMSTATE_END_OF_LIST() 234 } 235}; 236 237static void imx2_wdt_realize(DeviceState *dev, Error **errp) 238{ 239 IMX2WdtState *s = IMX2_WDT(dev); 240 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 241 242 memory_region_init_io(&s->mmio, OBJECT(dev), 243 &imx2_wdt_ops, s, 244 TYPE_IMX2_WDT, 245 IMX2_WDT_MMIO_SIZE); 246 sysbus_init_mmio(sbd, &s->mmio); 247 sysbus_init_irq(sbd, &s->irq); 248 249 s->timer = ptimer_init(imx2_wdt_expired, s, 250 PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | 251 PTIMER_POLICY_NO_IMMEDIATE_RELOAD | 252 PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); 253 ptimer_transaction_begin(s->timer); 254 ptimer_set_freq(s->timer, 2); 255 ptimer_set_limit(s->timer, 0xff, 1); 256 ptimer_transaction_commit(s->timer); 257 if (s->pretimeout_support) { 258 s->itimer = ptimer_init(imx2_wdt_interrupt, s, 259 PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | 260 PTIMER_POLICY_NO_IMMEDIATE_RELOAD | 261 PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); 262 ptimer_transaction_begin(s->itimer); 263 ptimer_set_freq(s->itimer, 2); 264 ptimer_set_limit(s->itimer, 0xff, 1); 265 ptimer_transaction_commit(s->itimer); 266 } 267} 268 269static Property imx2_wdt_properties[] = { 270 DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState, pretimeout_support, 271 false), 272 DEFINE_PROP_END_OF_LIST() 273}; 274 275static void imx2_wdt_class_init(ObjectClass *klass, void *data) 276{ 277 DeviceClass *dc = DEVICE_CLASS(klass); 278 279 device_class_set_props(dc, imx2_wdt_properties); 280 dc->realize = imx2_wdt_realize; 281 dc->reset = imx2_wdt_reset; 282 dc->vmsd = &vmstate_imx2_wdt; 283 dc->desc = "i.MX watchdog timer"; 284 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 285} 286 287static const TypeInfo imx2_wdt_info = { 288 .name = TYPE_IMX2_WDT, 289 .parent = TYPE_SYS_BUS_DEVICE, 290 .instance_size = sizeof(IMX2WdtState), 291 .class_init = imx2_wdt_class_init, 292}; 293 294static WatchdogTimerModel model = { 295 .wdt_name = "imx2-watchdog", 296 .wdt_description = "i.MX2 Watchdog", 297}; 298 299static void imx2_wdt_register_type(void) 300{ 301 watchdog_add_model(&model); 302 type_register_static(&imx2_wdt_info); 303} 304type_init(imx2_wdt_register_type)