|
|
1.1 ! root 1: /* ! 2: * Xilinx Zynq cadence TTC model ! 3: * ! 4: * Copyright (c) 2011 Xilinx Inc. ! 5: * Copyright (c) 2012 Peter A.G. Crosthwaite ([email protected]) ! 6: * Copyright (c) 2012 PetaLogix Pty Ltd. ! 7: * Written By Haibing Ma ! 8: * M. Habib ! 9: * ! 10: * This program is free software; you can redistribute it and/or ! 11: * modify it under the terms of the GNU General Public License ! 12: * as published by the Free Software Foundation; either version ! 13: * 2 of the License, or (at your option) any later version. ! 14: * ! 15: * You should have received a copy of the GNU General Public License along ! 16: * with this program; if not, see <http://www.gnu.org/licenses/>. ! 17: */ ! 18: ! 19: #include "sysbus.h" ! 20: #include "qemu-timer.h" ! 21: ! 22: #ifdef CADENCE_TTC_ERR_DEBUG ! 23: #define DB_PRINT(...) do { \ ! 24: fprintf(stderr, ": %s: ", __func__); \ ! 25: fprintf(stderr, ## __VA_ARGS__); \ ! 26: } while (0); ! 27: #else ! 28: #define DB_PRINT(...) ! 29: #endif ! 30: ! 31: #define COUNTER_INTR_IV 0x00000001 ! 32: #define COUNTER_INTR_M1 0x00000002 ! 33: #define COUNTER_INTR_M2 0x00000004 ! 34: #define COUNTER_INTR_M3 0x00000008 ! 35: #define COUNTER_INTR_OV 0x00000010 ! 36: #define COUNTER_INTR_EV 0x00000020 ! 37: ! 38: #define COUNTER_CTRL_DIS 0x00000001 ! 39: #define COUNTER_CTRL_INT 0x00000002 ! 40: #define COUNTER_CTRL_DEC 0x00000004 ! 41: #define COUNTER_CTRL_MATCH 0x00000008 ! 42: #define COUNTER_CTRL_RST 0x00000010 ! 43: ! 44: #define CLOCK_CTRL_PS_EN 0x00000001 ! 45: #define CLOCK_CTRL_PS_V 0x0000001e ! 46: ! 47: typedef struct { ! 48: QEMUTimer *timer; ! 49: int freq; ! 50: ! 51: uint32_t reg_clock; ! 52: uint32_t reg_count; ! 53: uint32_t reg_value; ! 54: uint16_t reg_interval; ! 55: uint16_t reg_match[3]; ! 56: uint32_t reg_intr; ! 57: uint32_t reg_intr_en; ! 58: uint32_t reg_event_ctrl; ! 59: uint32_t reg_event; ! 60: ! 61: uint64_t cpu_time; ! 62: unsigned int cpu_time_valid; ! 63: ! 64: qemu_irq irq; ! 65: } CadenceTimerState; ! 66: ! 67: typedef struct { ! 68: SysBusDevice busdev; ! 69: MemoryRegion iomem; ! 70: CadenceTimerState timer[3]; ! 71: } CadenceTTCState; ! 72: ! 73: static void cadence_timer_update(CadenceTimerState *s) ! 74: { ! 75: qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en)); ! 76: } ! 77: ! 78: static CadenceTimerState *cadence_timer_from_addr(void *opaque, ! 79: target_phys_addr_t offset) ! 80: { ! 81: unsigned int index; ! 82: CadenceTTCState *s = (CadenceTTCState *)opaque; ! 83: ! 84: index = (offset >> 2) % 3; ! 85: ! 86: return &s->timer[index]; ! 87: } ! 88: ! 89: static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps) ! 90: { ! 91: /* timer_steps has max value of 0x100000000. double check it ! 92: * (or overflow can happen below) */ ! 93: assert(timer_steps <= 1ULL << 32); ! 94: ! 95: uint64_t r = timer_steps * 1000000000ULL; ! 96: if (s->reg_clock & CLOCK_CTRL_PS_EN) { ! 97: r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); ! 98: } else { ! 99: r >>= 16; ! 100: } ! 101: r /= (uint64_t)s->freq; ! 102: return r; ! 103: } ! 104: ! 105: static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns) ! 106: { ! 107: uint64_t to_divide = 1000000000ULL; ! 108: ! 109: uint64_t r = ns; ! 110: /* for very large intervals (> 8s) do some division first to stop ! 111: * overflow (costs some prescision) */ ! 112: while (r >= 8ULL << 30 && to_divide > 1) { ! 113: r /= 1000; ! 114: to_divide /= 1000; ! 115: } ! 116: r <<= 16; ! 117: /* keep early-dividing as needed */ ! 118: while (r >= 8ULL << 30 && to_divide > 1) { ! 119: r /= 1000; ! 120: to_divide /= 1000; ! 121: } ! 122: r *= (uint64_t)s->freq; ! 123: if (s->reg_clock & CLOCK_CTRL_PS_EN) { ! 124: r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); ! 125: } ! 126: ! 127: r /= to_divide; ! 128: return r; ! 129: } ! 130: ! 131: /* determine if x is in between a and b, exclusive of a, inclusive of b */ ! 132: ! 133: static inline int64_t is_between(int64_t x, int64_t a, int64_t b) ! 134: { ! 135: if (a < b) { ! 136: return x > a && x <= b; ! 137: } ! 138: return x < a && x >= b; ! 139: } ! 140: ! 141: static void cadence_timer_run(CadenceTimerState *s) ! 142: { ! 143: int i; ! 144: int64_t event_interval, next_value; ! 145: ! 146: assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */ ! 147: ! 148: if (s->reg_count & COUNTER_CTRL_DIS) { ! 149: s->cpu_time_valid = 0; ! 150: return; ! 151: } ! 152: ! 153: { /* figure out what's going to happen next (rollover or match) */ ! 154: int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ? ! 155: (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; ! 156: next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval; ! 157: for (i = 0; i < 3; ++i) { ! 158: int64_t cand = (uint64_t)s->reg_match[i] << 16; ! 159: if (is_between(cand, (uint64_t)s->reg_value, next_value)) { ! 160: next_value = cand; ! 161: } ! 162: } ! 163: } ! 164: DB_PRINT("next timer event value: %09llx\n", ! 165: (unsigned long long)next_value); ! 166: ! 167: event_interval = next_value - (int64_t)s->reg_value; ! 168: event_interval = (event_interval < 0) ? -event_interval : event_interval; ! 169: ! 170: qemu_mod_timer(s->timer, s->cpu_time + ! 171: cadence_timer_get_ns(s, event_interval)); ! 172: } ! 173: ! 174: static void cadence_timer_sync(CadenceTimerState *s) ! 175: { ! 176: int i; ! 177: int64_t r, x; ! 178: int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ? ! 179: (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; ! 180: uint64_t old_time = s->cpu_time; ! 181: ! 182: s->cpu_time = qemu_get_clock_ns(vm_clock); ! 183: DB_PRINT("cpu time: %lld ns\n", (long long)old_time); ! 184: ! 185: if (!s->cpu_time_valid || old_time == s->cpu_time) { ! 186: s->cpu_time_valid = 1; ! 187: return; ! 188: } ! 189: ! 190: r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time); ! 191: x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r); ! 192: ! 193: for (i = 0; i < 3; ++i) { ! 194: int64_t m = (int64_t)s->reg_match[i] << 16; ! 195: if (m > interval) { ! 196: continue; ! 197: } ! 198: /* check to see if match event has occurred. check m +/- interval ! 199: * to account for match events in wrap around cases */ ! 200: if (is_between(m, s->reg_value, x) || ! 201: is_between(m + interval, s->reg_value, x) || ! 202: is_between(m - interval, s->reg_value, x)) { ! 203: s->reg_intr |= (2 << i); ! 204: } ! 205: } ! 206: while (x < 0) { ! 207: x += interval; ! 208: } ! 209: s->reg_value = (uint32_t)(x % interval); ! 210: ! 211: if (s->reg_value != x) { ! 212: s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? ! 213: COUNTER_INTR_IV : COUNTER_INTR_OV; ! 214: } ! 215: cadence_timer_update(s); ! 216: } ! 217: ! 218: static void cadence_timer_tick(void *opaque) ! 219: { ! 220: CadenceTimerState *s = opaque; ! 221: ! 222: DB_PRINT("\n"); ! 223: cadence_timer_sync(s); ! 224: cadence_timer_run(s); ! 225: } ! 226: ! 227: static uint32_t cadence_ttc_read_imp(void *opaque, target_phys_addr_t offset) ! 228: { ! 229: CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); ! 230: uint32_t value; ! 231: ! 232: cadence_timer_sync(s); ! 233: cadence_timer_run(s); ! 234: ! 235: switch (offset) { ! 236: case 0x00: /* clock control */ ! 237: case 0x04: ! 238: case 0x08: ! 239: return s->reg_clock; ! 240: ! 241: case 0x0c: /* counter control */ ! 242: case 0x10: ! 243: case 0x14: ! 244: return s->reg_count; ! 245: ! 246: case 0x18: /* counter value */ ! 247: case 0x1c: ! 248: case 0x20: ! 249: return (uint16_t)(s->reg_value >> 16); ! 250: ! 251: case 0x24: /* reg_interval counter */ ! 252: case 0x28: ! 253: case 0x2c: ! 254: return s->reg_interval; ! 255: ! 256: case 0x30: /* match 1 counter */ ! 257: case 0x34: ! 258: case 0x38: ! 259: return s->reg_match[0]; ! 260: ! 261: case 0x3c: /* match 2 counter */ ! 262: case 0x40: ! 263: case 0x44: ! 264: return s->reg_match[1]; ! 265: ! 266: case 0x48: /* match 3 counter */ ! 267: case 0x4c: ! 268: case 0x50: ! 269: return s->reg_match[2]; ! 270: ! 271: case 0x54: /* interrupt register */ ! 272: case 0x58: ! 273: case 0x5c: ! 274: /* cleared after read */ ! 275: value = s->reg_intr; ! 276: s->reg_intr = 0; ! 277: return value; ! 278: ! 279: case 0x60: /* interrupt enable */ ! 280: case 0x64: ! 281: case 0x68: ! 282: return s->reg_intr_en; ! 283: ! 284: case 0x6c: ! 285: case 0x70: ! 286: case 0x74: ! 287: return s->reg_event_ctrl; ! 288: ! 289: case 0x78: ! 290: case 0x7c: ! 291: case 0x80: ! 292: return s->reg_event; ! 293: ! 294: default: ! 295: return 0; ! 296: } ! 297: } ! 298: ! 299: static uint64_t cadence_ttc_read(void *opaque, target_phys_addr_t offset, ! 300: unsigned size) ! 301: { ! 302: uint32_t ret = cadence_ttc_read_imp(opaque, offset); ! 303: ! 304: DB_PRINT("addr: %08x data: %08x\n", offset, ret); ! 305: return ret; ! 306: } ! 307: ! 308: static void cadence_ttc_write(void *opaque, target_phys_addr_t offset, ! 309: uint64_t value, unsigned size) ! 310: { ! 311: CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); ! 312: ! 313: DB_PRINT("addr: %08x data %08x\n", offset, (unsigned)value); ! 314: ! 315: cadence_timer_sync(s); ! 316: ! 317: switch (offset) { ! 318: case 0x00: /* clock control */ ! 319: case 0x04: ! 320: case 0x08: ! 321: s->reg_clock = value & 0x3F; ! 322: break; ! 323: ! 324: case 0x0c: /* counter control */ ! 325: case 0x10: ! 326: case 0x14: ! 327: if (value & COUNTER_CTRL_RST) { ! 328: s->reg_value = 0; ! 329: } ! 330: s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST; ! 331: break; ! 332: ! 333: case 0x24: /* interval register */ ! 334: case 0x28: ! 335: case 0x2c: ! 336: s->reg_interval = value & 0xffff; ! 337: break; ! 338: ! 339: case 0x30: /* match register */ ! 340: case 0x34: ! 341: case 0x38: ! 342: s->reg_match[0] = value & 0xffff; ! 343: ! 344: case 0x3c: /* match register */ ! 345: case 0x40: ! 346: case 0x44: ! 347: s->reg_match[1] = value & 0xffff; ! 348: ! 349: case 0x48: /* match register */ ! 350: case 0x4c: ! 351: case 0x50: ! 352: s->reg_match[2] = value & 0xffff; ! 353: break; ! 354: ! 355: case 0x54: /* interrupt register */ ! 356: case 0x58: ! 357: case 0x5c: ! 358: s->reg_intr &= (~value & 0xfff); ! 359: break; ! 360: ! 361: case 0x60: /* interrupt enable */ ! 362: case 0x64: ! 363: case 0x68: ! 364: s->reg_intr_en = value & 0x3f; ! 365: break; ! 366: ! 367: case 0x6c: /* event control */ ! 368: case 0x70: ! 369: case 0x74: ! 370: s->reg_event_ctrl = value & 0x07; ! 371: break; ! 372: ! 373: default: ! 374: return; ! 375: } ! 376: ! 377: cadence_timer_run(s); ! 378: cadence_timer_update(s); ! 379: } ! 380: ! 381: static const MemoryRegionOps cadence_ttc_ops = { ! 382: .read = cadence_ttc_read, ! 383: .write = cadence_ttc_write, ! 384: .endianness = DEVICE_NATIVE_ENDIAN, ! 385: }; ! 386: ! 387: static void cadence_timer_reset(CadenceTimerState *s) ! 388: { ! 389: s->reg_count = 0x21; ! 390: } ! 391: ! 392: static void cadence_timer_init(uint32_t freq, CadenceTimerState *s) ! 393: { ! 394: memset(s, 0, sizeof(CadenceTimerState)); ! 395: s->freq = freq; ! 396: ! 397: cadence_timer_reset(s); ! 398: ! 399: s->timer = qemu_new_timer_ns(vm_clock, cadence_timer_tick, s); ! 400: } ! 401: ! 402: static int cadence_ttc_init(SysBusDevice *dev) ! 403: { ! 404: CadenceTTCState *s = FROM_SYSBUS(CadenceTTCState, dev); ! 405: int i; ! 406: ! 407: for (i = 0; i < 3; ++i) { ! 408: cadence_timer_init(2500000, &s->timer[i]); ! 409: sysbus_init_irq(dev, &s->timer[i].irq); ! 410: } ! 411: ! 412: memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000); ! 413: sysbus_init_mmio(dev, &s->iomem); ! 414: ! 415: return 0; ! 416: } ! 417: ! 418: static void cadence_timer_pre_save(void *opaque) ! 419: { ! 420: cadence_timer_sync((CadenceTimerState *)opaque); ! 421: } ! 422: ! 423: static int cadence_timer_post_load(void *opaque, int version_id) ! 424: { ! 425: CadenceTimerState *s = opaque; ! 426: ! 427: s->cpu_time_valid = 0; ! 428: cadence_timer_sync(s); ! 429: cadence_timer_run(s); ! 430: cadence_timer_update(s); ! 431: return 0; ! 432: } ! 433: ! 434: static const VMStateDescription vmstate_cadence_timer = { ! 435: .name = "cadence_timer", ! 436: .version_id = 1, ! 437: .minimum_version_id = 1, ! 438: .minimum_version_id_old = 1, ! 439: .pre_save = cadence_timer_pre_save, ! 440: .post_load = cadence_timer_post_load, ! 441: .fields = (VMStateField[]) { ! 442: VMSTATE_UINT32(reg_clock, CadenceTimerState), ! 443: VMSTATE_UINT32(reg_count, CadenceTimerState), ! 444: VMSTATE_UINT32(reg_value, CadenceTimerState), ! 445: VMSTATE_UINT16(reg_interval, CadenceTimerState), ! 446: VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3), ! 447: VMSTATE_UINT32(reg_intr, CadenceTimerState), ! 448: VMSTATE_UINT32(reg_intr_en, CadenceTimerState), ! 449: VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState), ! 450: VMSTATE_UINT32(reg_event, CadenceTimerState), ! 451: VMSTATE_END_OF_LIST() ! 452: } ! 453: }; ! 454: ! 455: static const VMStateDescription vmstate_cadence_ttc = { ! 456: .name = "cadence_TTC", ! 457: .version_id = 1, ! 458: .minimum_version_id = 1, ! 459: .minimum_version_id_old = 1, ! 460: .fields = (VMStateField[]) { ! 461: VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0, ! 462: vmstate_cadence_timer, ! 463: CadenceTimerState), ! 464: VMSTATE_END_OF_LIST() ! 465: } ! 466: }; ! 467: ! 468: static void cadence_ttc_class_init(ObjectClass *klass, void *data) ! 469: { ! 470: DeviceClass *dc = DEVICE_CLASS(klass); ! 471: SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); ! 472: ! 473: sdc->init = cadence_ttc_init; ! 474: dc->vmsd = &vmstate_cadence_ttc; ! 475: } ! 476: ! 477: static TypeInfo cadence_ttc_info = { ! 478: .name = "cadence_ttc", ! 479: .parent = TYPE_SYS_BUS_DEVICE, ! 480: .instance_size = sizeof(CadenceTTCState), ! 481: .class_init = cadence_ttc_class_init, ! 482: }; ! 483: ! 484: static void cadence_ttc_register_types(void) ! 485: { ! 486: type_register_static(&cadence_ttc_info); ! 487: } ! 488: ! 489: type_init(cadence_ttc_register_types)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.