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