|
|
1.1 ! root 1: /* ! 2: * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP ! 3: * ! 4: * Copyright (c) 2006-2007 CodeSourcery. ! 5: * Copyright (c) 2011 Linaro Limited ! 6: * Written by Paul Brook, Peter Maydell ! 7: * ! 8: * This program is free software; you can redistribute it and/or ! 9: * modify it under the terms of the GNU General Public License ! 10: * as published by the Free Software Foundation; either version ! 11: * 2 of the License, or (at your option) any later version. ! 12: * ! 13: * This program is distributed in the hope that it will be useful, ! 14: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 15: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 16: * GNU General Public License for more details. ! 17: * ! 18: * You should have received a copy of the GNU General Public License along ! 19: * with this program; if not, see <http://www.gnu.org/licenses/>. ! 20: */ ! 21: ! 22: #include "sysbus.h" ! 23: #include "qemu-timer.h" ! 24: ! 25: /* This device implements the per-cpu private timer and watchdog block ! 26: * which is used in both the ARM11MPCore and Cortex-A9MP. ! 27: */ ! 28: ! 29: #define MAX_CPUS 4 ! 30: ! 31: /* State of a single timer or watchdog block */ ! 32: typedef struct { ! 33: uint32_t count; ! 34: uint32_t load; ! 35: uint32_t control; ! 36: uint32_t status; ! 37: int64_t tick; ! 38: QEMUTimer *timer; ! 39: qemu_irq irq; ! 40: MemoryRegion iomem; ! 41: } timerblock; ! 42: ! 43: typedef struct { ! 44: SysBusDevice busdev; ! 45: uint32_t num_cpu; ! 46: timerblock timerblock[MAX_CPUS * 2]; ! 47: MemoryRegion iomem[2]; ! 48: } arm_mptimer_state; ! 49: ! 50: static inline int get_current_cpu(arm_mptimer_state *s) ! 51: { ! 52: if (cpu_single_env->cpu_index >= s->num_cpu) { ! 53: hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", ! 54: s->num_cpu, cpu_single_env->cpu_index); ! 55: } ! 56: return cpu_single_env->cpu_index; ! 57: } ! 58: ! 59: static inline void timerblock_update_irq(timerblock *tb) ! 60: { ! 61: qemu_set_irq(tb->irq, tb->status); ! 62: } ! 63: ! 64: /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ ! 65: static inline uint32_t timerblock_scale(timerblock *tb) ! 66: { ! 67: return (((tb->control >> 8) & 0xff) + 1) * 10; ! 68: } ! 69: ! 70: static void timerblock_reload(timerblock *tb, int restart) ! 71: { ! 72: if (tb->count == 0) { ! 73: return; ! 74: } ! 75: if (restart) { ! 76: tb->tick = qemu_get_clock_ns(vm_clock); ! 77: } ! 78: tb->tick += (int64_t)tb->count * timerblock_scale(tb); ! 79: qemu_mod_timer(tb->timer, tb->tick); ! 80: } ! 81: ! 82: static void timerblock_tick(void *opaque) ! 83: { ! 84: timerblock *tb = (timerblock *)opaque; ! 85: tb->status = 1; ! 86: if (tb->control & 2) { ! 87: tb->count = tb->load; ! 88: timerblock_reload(tb, 0); ! 89: } else { ! 90: tb->count = 0; ! 91: } ! 92: timerblock_update_irq(tb); ! 93: } ! 94: ! 95: static uint64_t timerblock_read(void *opaque, target_phys_addr_t addr, ! 96: unsigned size) ! 97: { ! 98: timerblock *tb = (timerblock *)opaque; ! 99: int64_t val; ! 100: switch (addr) { ! 101: case 0: /* Load */ ! 102: return tb->load; ! 103: case 4: /* Counter. */ ! 104: if (((tb->control & 1) == 0) || (tb->count == 0)) { ! 105: return 0; ! 106: } ! 107: /* Slow and ugly, but hopefully won't happen too often. */ ! 108: val = tb->tick - qemu_get_clock_ns(vm_clock); ! 109: val /= timerblock_scale(tb); ! 110: if (val < 0) { ! 111: val = 0; ! 112: } ! 113: return val; ! 114: case 8: /* Control. */ ! 115: return tb->control; ! 116: case 12: /* Interrupt status. */ ! 117: return tb->status; ! 118: default: ! 119: return 0; ! 120: } ! 121: } ! 122: ! 123: static void timerblock_write(void *opaque, target_phys_addr_t addr, ! 124: uint64_t value, unsigned size) ! 125: { ! 126: timerblock *tb = (timerblock *)opaque; ! 127: int64_t old; ! 128: switch (addr) { ! 129: case 0: /* Load */ ! 130: tb->load = value; ! 131: /* Fall through. */ ! 132: case 4: /* Counter. */ ! 133: if ((tb->control & 1) && tb->count) { ! 134: /* Cancel the previous timer. */ ! 135: qemu_del_timer(tb->timer); ! 136: } ! 137: tb->count = value; ! 138: if (tb->control & 1) { ! 139: timerblock_reload(tb, 1); ! 140: } ! 141: break; ! 142: case 8: /* Control. */ ! 143: old = tb->control; ! 144: tb->control = value; ! 145: if (((old & 1) == 0) && (value & 1)) { ! 146: if (tb->count == 0 && (tb->control & 2)) { ! 147: tb->count = tb->load; ! 148: } ! 149: timerblock_reload(tb, 1); ! 150: } ! 151: break; ! 152: case 12: /* Interrupt status. */ ! 153: tb->status &= ~value; ! 154: timerblock_update_irq(tb); ! 155: break; ! 156: } ! 157: } ! 158: ! 159: /* Wrapper functions to implement the "read timer/watchdog for ! 160: * the current CPU" memory regions. ! 161: */ ! 162: static uint64_t arm_thistimer_read(void *opaque, target_phys_addr_t addr, ! 163: unsigned size) ! 164: { ! 165: arm_mptimer_state *s = (arm_mptimer_state *)opaque; ! 166: int id = get_current_cpu(s); ! 167: return timerblock_read(&s->timerblock[id * 2], addr, size); ! 168: } ! 169: ! 170: static void arm_thistimer_write(void *opaque, target_phys_addr_t addr, ! 171: uint64_t value, unsigned size) ! 172: { ! 173: arm_mptimer_state *s = (arm_mptimer_state *)opaque; ! 174: int id = get_current_cpu(s); ! 175: timerblock_write(&s->timerblock[id * 2], addr, value, size); ! 176: } ! 177: ! 178: static uint64_t arm_thiswdog_read(void *opaque, target_phys_addr_t addr, ! 179: unsigned size) ! 180: { ! 181: arm_mptimer_state *s = (arm_mptimer_state *)opaque; ! 182: int id = get_current_cpu(s); ! 183: return timerblock_read(&s->timerblock[id * 2 + 1], addr, size); ! 184: } ! 185: ! 186: static void arm_thiswdog_write(void *opaque, target_phys_addr_t addr, ! 187: uint64_t value, unsigned size) ! 188: { ! 189: arm_mptimer_state *s = (arm_mptimer_state *)opaque; ! 190: int id = get_current_cpu(s); ! 191: timerblock_write(&s->timerblock[id * 2 + 1], addr, value, size); ! 192: } ! 193: ! 194: static const MemoryRegionOps arm_thistimer_ops = { ! 195: .read = arm_thistimer_read, ! 196: .write = arm_thistimer_write, ! 197: .valid = { ! 198: .min_access_size = 4, ! 199: .max_access_size = 4, ! 200: }, ! 201: .endianness = DEVICE_NATIVE_ENDIAN, ! 202: }; ! 203: ! 204: static const MemoryRegionOps arm_thiswdog_ops = { ! 205: .read = arm_thiswdog_read, ! 206: .write = arm_thiswdog_write, ! 207: .valid = { ! 208: .min_access_size = 4, ! 209: .max_access_size = 4, ! 210: }, ! 211: .endianness = DEVICE_NATIVE_ENDIAN, ! 212: }; ! 213: ! 214: static const MemoryRegionOps timerblock_ops = { ! 215: .read = timerblock_read, ! 216: .write = timerblock_write, ! 217: .valid = { ! 218: .min_access_size = 4, ! 219: .max_access_size = 4, ! 220: }, ! 221: .endianness = DEVICE_NATIVE_ENDIAN, ! 222: }; ! 223: ! 224: static void timerblock_reset(timerblock *tb) ! 225: { ! 226: tb->count = 0; ! 227: tb->load = 0; ! 228: tb->control = 0; ! 229: tb->status = 0; ! 230: tb->tick = 0; ! 231: if (tb->timer) { ! 232: qemu_del_timer(tb->timer); ! 233: } ! 234: } ! 235: ! 236: static void arm_mptimer_reset(DeviceState *dev) ! 237: { ! 238: arm_mptimer_state *s = ! 239: FROM_SYSBUS(arm_mptimer_state, sysbus_from_qdev(dev)); ! 240: int i; ! 241: /* We reset every timer in the array, not just the ones we're using, ! 242: * because vmsave will look at every array element. ! 243: */ ! 244: for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) { ! 245: timerblock_reset(&s->timerblock[i]); ! 246: } ! 247: } ! 248: ! 249: static int arm_mptimer_init(SysBusDevice *dev) ! 250: { ! 251: arm_mptimer_state *s = FROM_SYSBUS(arm_mptimer_state, dev); ! 252: int i; ! 253: if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) { ! 254: hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS); ! 255: } ! 256: /* We implement one timer and one watchdog block per CPU, and ! 257: * expose multiple MMIO regions: ! 258: * * region 0 is "timer for this core" ! 259: * * region 1 is "watchdog for this core" ! 260: * * region 2 is "timer for core 0" ! 261: * * region 3 is "watchdog for core 0" ! 262: * * region 4 is "timer for core 1" ! 263: * * region 5 is "watchdog for core 1" ! 264: * and so on. ! 265: * The outgoing interrupt lines are ! 266: * * timer for core 0 ! 267: * * watchdog for core 0 ! 268: * * timer for core 1 ! 269: * * watchdog for core 1 ! 270: * and so on. ! 271: */ ! 272: memory_region_init_io(&s->iomem[0], &arm_thistimer_ops, s, ! 273: "arm_mptimer_timer", 0x20); ! 274: sysbus_init_mmio(dev, &s->iomem[0]); ! 275: memory_region_init_io(&s->iomem[1], &arm_thiswdog_ops, s, ! 276: "arm_mptimer_wdog", 0x20); ! 277: sysbus_init_mmio(dev, &s->iomem[1]); ! 278: for (i = 0; i < (s->num_cpu * 2); i++) { ! 279: timerblock *tb = &s->timerblock[i]; ! 280: tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb); ! 281: sysbus_init_irq(dev, &tb->irq); ! 282: memory_region_init_io(&tb->iomem, &timerblock_ops, tb, ! 283: "arm_mptimer_timerblock", 0x20); ! 284: sysbus_init_mmio(dev, &tb->iomem); ! 285: } ! 286: ! 287: return 0; ! 288: } ! 289: ! 290: static const VMStateDescription vmstate_timerblock = { ! 291: .name = "arm_mptimer_timerblock", ! 292: .version_id = 1, ! 293: .minimum_version_id = 1, ! 294: .fields = (VMStateField[]) { ! 295: VMSTATE_UINT32(count, timerblock), ! 296: VMSTATE_UINT32(load, timerblock), ! 297: VMSTATE_UINT32(control, timerblock), ! 298: VMSTATE_UINT32(status, timerblock), ! 299: VMSTATE_INT64(tick, timerblock), ! 300: VMSTATE_END_OF_LIST() ! 301: } ! 302: }; ! 303: ! 304: static const VMStateDescription vmstate_arm_mptimer = { ! 305: .name = "arm_mptimer", ! 306: .version_id = 1, ! 307: .minimum_version_id = 1, ! 308: .fields = (VMStateField[]) { ! 309: VMSTATE_STRUCT_ARRAY(timerblock, arm_mptimer_state, (MAX_CPUS * 2), ! 310: 1, vmstate_timerblock, timerblock), ! 311: VMSTATE_END_OF_LIST() ! 312: } ! 313: }; ! 314: ! 315: static Property arm_mptimer_properties[] = { ! 316: DEFINE_PROP_UINT32("num-cpu", arm_mptimer_state, num_cpu, 0), ! 317: DEFINE_PROP_END_OF_LIST() ! 318: }; ! 319: ! 320: static void arm_mptimer_class_init(ObjectClass *klass, void *data) ! 321: { ! 322: DeviceClass *dc = DEVICE_CLASS(klass); ! 323: SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); ! 324: ! 325: sbc->init = arm_mptimer_init; ! 326: dc->vmsd = &vmstate_arm_mptimer; ! 327: dc->reset = arm_mptimer_reset; ! 328: dc->no_user = 1; ! 329: dc->props = arm_mptimer_properties; ! 330: } ! 331: ! 332: static TypeInfo arm_mptimer_info = { ! 333: .name = "arm_mptimer", ! 334: .parent = TYPE_SYS_BUS_DEVICE, ! 335: .instance_size = sizeof(arm_mptimer_state), ! 336: .class_init = arm_mptimer_class_init, ! 337: }; ! 338: ! 339: static void arm_mptimer_register_types(void) ! 340: { ! 341: type_register_static(&arm_mptimer_info); ! 342: } ! 343: ! 344: type_init(arm_mptimer_register_types)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.