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 "qemu/osdep.h" |
30 | #include "hw/sysbus.h" |
31 | #include "hw/ptimer.h" |
32 | #include "qemu/main-loop.h" |
33 | #include "qemu/module.h" |
34 | #include "qemu/log.h" |
35 | |
36 | #include "hw/timer/digic-timer.h" |
37 | #include "migration/vmstate.h" |
38 | |
39 | static const VMStateDescription vmstate_digic_timer = { |
40 | .name = "digic.timer" , |
41 | .version_id = 1, |
42 | .minimum_version_id = 1, |
43 | .fields = (VMStateField[]) { |
44 | VMSTATE_PTIMER(ptimer, DigicTimerState), |
45 | VMSTATE_UINT32(control, DigicTimerState), |
46 | VMSTATE_UINT32(relvalue, DigicTimerState), |
47 | VMSTATE_END_OF_LIST() |
48 | } |
49 | }; |
50 | |
51 | static void digic_timer_reset(DeviceState *dev) |
52 | { |
53 | DigicTimerState *s = DIGIC_TIMER(dev); |
54 | |
55 | ptimer_stop(s->ptimer); |
56 | s->control = 0; |
57 | s->relvalue = 0; |
58 | } |
59 | |
60 | static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size) |
61 | { |
62 | DigicTimerState *s = opaque; |
63 | uint64_t ret = 0; |
64 | |
65 | switch (offset) { |
66 | case DIGIC_TIMER_CONTROL: |
67 | ret = s->control; |
68 | break; |
69 | case DIGIC_TIMER_RELVALUE: |
70 | ret = s->relvalue; |
71 | break; |
72 | case DIGIC_TIMER_VALUE: |
73 | ret = ptimer_get_count(s->ptimer) & 0xffff; |
74 | break; |
75 | default: |
76 | qemu_log_mask(LOG_UNIMP, |
77 | "digic-timer: read access to unknown register 0x" |
78 | TARGET_FMT_plx "\n" , offset); |
79 | } |
80 | |
81 | return ret; |
82 | } |
83 | |
84 | static void digic_timer_write(void *opaque, hwaddr offset, |
85 | uint64_t value, unsigned size) |
86 | { |
87 | DigicTimerState *s = opaque; |
88 | |
89 | switch (offset) { |
90 | case DIGIC_TIMER_CONTROL: |
91 | if (value & DIGIC_TIMER_CONTROL_RST) { |
92 | digic_timer_reset((DeviceState *)s); |
93 | break; |
94 | } |
95 | |
96 | if (value & DIGIC_TIMER_CONTROL_EN) { |
97 | ptimer_run(s->ptimer, 0); |
98 | } |
99 | |
100 | s->control = (uint32_t)value; |
101 | break; |
102 | |
103 | case DIGIC_TIMER_RELVALUE: |
104 | s->relvalue = extract32(value, 0, 16); |
105 | ptimer_set_limit(s->ptimer, s->relvalue, 1); |
106 | break; |
107 | |
108 | case DIGIC_TIMER_VALUE: |
109 | break; |
110 | |
111 | default: |
112 | qemu_log_mask(LOG_UNIMP, |
113 | "digic-timer: read access to unknown register 0x" |
114 | TARGET_FMT_plx "\n" , offset); |
115 | } |
116 | } |
117 | |
118 | static const MemoryRegionOps digic_timer_ops = { |
119 | .read = digic_timer_read, |
120 | .write = digic_timer_write, |
121 | .impl = { |
122 | .min_access_size = 4, |
123 | .max_access_size = 4, |
124 | }, |
125 | .endianness = DEVICE_NATIVE_ENDIAN, |
126 | }; |
127 | |
128 | static void digic_timer_init(Object *obj) |
129 | { |
130 | DigicTimerState *s = DIGIC_TIMER(obj); |
131 | |
132 | s->ptimer = ptimer_init(NULL, PTIMER_POLICY_DEFAULT); |
133 | |
134 | /* |
135 | * FIXME: there is no documentation on Digic timer |
136 | * frequency setup so let it always run at 1 MHz |
137 | */ |
138 | ptimer_set_freq(s->ptimer, 1 * 1000 * 1000); |
139 | |
140 | memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s, |
141 | TYPE_DIGIC_TIMER, 0x100); |
142 | sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); |
143 | } |
144 | |
145 | static void digic_timer_class_init(ObjectClass *klass, void *class_data) |
146 | { |
147 | DeviceClass *dc = DEVICE_CLASS(klass); |
148 | |
149 | dc->reset = digic_timer_reset; |
150 | dc->vmsd = &vmstate_digic_timer; |
151 | } |
152 | |
153 | static const TypeInfo digic_timer_info = { |
154 | .name = TYPE_DIGIC_TIMER, |
155 | .parent = TYPE_SYS_BUS_DEVICE, |
156 | .instance_size = sizeof(DigicTimerState), |
157 | .instance_init = digic_timer_init, |
158 | .class_init = digic_timer_class_init, |
159 | }; |
160 | |
161 | static void digic_timer_register_type(void) |
162 | { |
163 | type_register_static(&digic_timer_info); |
164 | } |
165 | |
166 | type_init(digic_timer_register_type) |
167 | |