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