1 /* 2 * QEMU model of the Canon DIGIC timer block. 3 * 4 * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com> 5 * 6 * This model is based on reverse engineering efforts 7 * made by CHDK (http://chdk.wikia.com) and 8 * Magic Lantern (http://www.magiclantern.fm) projects 9 * contributors. 10 * 11 * See "Timer/Clock Module" docs here: 12 * http://magiclantern.wikia.com/wiki/Register_Map 13 * 14 * The QEMU model of the OSTimer in PKUnity SoC by Guan Xuetao 15 * is used as a template. 16 * 17 * This program is free software; you can redistribute it and/or modify 18 * it under the terms of the GNU General Public License as published by 19 * the Free Software Foundation; either version 2 of the License, or 20 * (at your option) any later version. 21 * 22 * This program is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * GNU General Public License for more details. 26 * 27 */ 28 29 #include "hw/sysbus.h" 30 #include "hw/ptimer.h" 31 #include "qemu/main-loop.h" 32 33 #include "hw/timer/digic-timer.h" 34 35 static const VMStateDescription vmstate_digic_timer = { 36 .name = "digic.timer", 37 .version_id = 1, 38 .minimum_version_id = 1, 39 .fields = (VMStateField[]) { 40 VMSTATE_PTIMER(ptimer, DigicTimerState), 41 VMSTATE_UINT32(control, DigicTimerState), 42 VMSTATE_UINT32(relvalue, DigicTimerState), 43 VMSTATE_END_OF_LIST() 44 } 45 }; 46 47 static void digic_timer_reset(DeviceState *dev) 48 { 49 DigicTimerState *s = DIGIC_TIMER(dev); 50 51 ptimer_stop(s->ptimer); 52 s->control = 0; 53 s->relvalue = 0; 54 } 55 56 static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size) 57 { 58 DigicTimerState *s = opaque; 59 uint64_t ret = 0; 60 61 switch (offset) { 62 case DIGIC_TIMER_CONTROL: 63 ret = s->control; 64 break; 65 case DIGIC_TIMER_RELVALUE: 66 ret = s->relvalue; 67 break; 68 case DIGIC_TIMER_VALUE: 69 ret = ptimer_get_count(s->ptimer) & 0xffff; 70 break; 71 default: 72 qemu_log_mask(LOG_UNIMP, 73 "digic-timer: read access to unknown register 0x" 74 TARGET_FMT_plx, offset); 75 } 76 77 return ret; 78 } 79 80 static void digic_timer_write(void *opaque, hwaddr offset, 81 uint64_t value, unsigned size) 82 { 83 DigicTimerState *s = opaque; 84 85 switch (offset) { 86 case DIGIC_TIMER_CONTROL: 87 if (value & DIGIC_TIMER_CONTROL_RST) { 88 digic_timer_reset((DeviceState *)s); 89 break; 90 } 91 92 if (value & DIGIC_TIMER_CONTROL_EN) { 93 ptimer_run(s->ptimer, 0); 94 } 95 96 s->control = (uint32_t)value; 97 break; 98 99 case DIGIC_TIMER_RELVALUE: 100 s->relvalue = extract32(value, 0, 16); 101 ptimer_set_limit(s->ptimer, s->relvalue, 1); 102 break; 103 104 case DIGIC_TIMER_VALUE: 105 break; 106 107 default: 108 qemu_log_mask(LOG_UNIMP, 109 "digic-timer: read access to unknown register 0x" 110 TARGET_FMT_plx, offset); 111 } 112 } 113 114 static const MemoryRegionOps digic_timer_ops = { 115 .read = digic_timer_read, 116 .write = digic_timer_write, 117 .impl = { 118 .min_access_size = 4, 119 .max_access_size = 4, 120 }, 121 .endianness = DEVICE_NATIVE_ENDIAN, 122 }; 123 124 static void digic_timer_init(Object *obj) 125 { 126 DigicTimerState *s = DIGIC_TIMER(obj); 127 128 s->ptimer = ptimer_init(NULL); 129 130 /* 131 * FIXME: there is no documentation on Digic timer 132 * frequency setup so let it always run at 1 MHz 133 */ 134 ptimer_set_freq(s->ptimer, 1 * 1000 * 1000); 135 136 memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s, 137 TYPE_DIGIC_TIMER, 0x100); 138 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); 139 } 140 141 static void digic_timer_class_init(ObjectClass *klass, void *class_data) 142 { 143 DeviceClass *dc = DEVICE_CLASS(klass); 144 145 dc->reset = digic_timer_reset; 146 dc->vmsd = &vmstate_digic_timer; 147 } 148 149 static const TypeInfo digic_timer_info = { 150 .name = TYPE_DIGIC_TIMER, 151 .parent = TYPE_SYS_BUS_DEVICE, 152 .instance_size = sizeof(DigicTimerState), 153 .instance_init = digic_timer_init, 154 .class_init = digic_timer_class_init, 155 }; 156 157 static void digic_timer_register_type(void) 158 { 159 type_register_static(&digic_timer_info); 160 } 161 162 type_init(digic_timer_register_type) 163