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