Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / arm / syborg_timer.c
blob55871fb345ea89c814c9b122eac62aea36295546
1 /*
2 * Syborg Interval Timer.
4 * Copyright (c) 2008 CodeSourcery
5 * Copyright (c) 2010, 2013 Stefan Weil
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
26 #include "qemu/osdep.h"
27 #include "hw/sysbus.h"
28 #include "qemu/timer.h"
29 #include "syborg.h"
30 #include "ptimer.h" /* ptimer_state */
32 //#define DEBUG_SYBORG_TIMER
34 #ifdef DEBUG_SYBORG_TIMER
35 #define DPRINTF(fmt, ...) \
36 do { printf("syborg_timer: " fmt , ##args); } while (0)
37 #define BADF(fmt, ...) \
38 do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__); \
39 exit(1);} while (0)
40 #else
41 #define DPRINTF(fmt, ...) do {} while(0)
42 #define BADF(fmt, ...) \
43 do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__);} while (0)
44 #endif
46 enum {
47 TIMER_ID = 0,
48 TIMER_RUNNING = 1,
49 TIMER_ONESHOT = 2,
50 TIMER_LIMIT = 3,
51 TIMER_VALUE = 4,
52 TIMER_INT_ENABLE = 5,
53 TIMER_INT_STATUS = 6,
54 TIMER_FREQ = 7
57 typedef struct {
58 SysBusDevice busdev;
59 MemoryRegion iomem;
60 ptimer_state *timer;
61 int running;
62 int oneshot;
63 uint32_t limit;
64 uint32_t freq;
65 uint32_t int_level;
66 uint32_t int_enabled;
67 qemu_irq irq;
68 } SyborgTimerState;
70 static void syborg_timer_update(SyborgTimerState *s)
72 /* Update interrupt. */
73 if (s->int_level && s->int_enabled) {
74 qemu_irq_raise(s->irq);
75 } else {
76 qemu_irq_lower(s->irq);
80 static void syborg_timer_tick(void *opaque)
82 SyborgTimerState *s = (SyborgTimerState *)opaque;
83 //DPRINTF("Timer Tick\n");
84 s->int_level = 1;
85 if (s->oneshot)
86 s->running = 0;
87 syborg_timer_update(s);
90 static uint64_t syborg_timer_read(void *opaque, hwaddr offset,
91 unsigned size)
93 SyborgTimerState *s = (SyborgTimerState *)opaque;
95 DPRINTF("Reg read %d\n", (int)offset);
96 offset &= 0xfff;
97 switch (offset >> 2) {
98 case TIMER_ID:
99 return SYBORG_ID_TIMER;
100 case TIMER_RUNNING:
101 return s->running;
102 case TIMER_ONESHOT:
103 return s->oneshot;
104 case TIMER_LIMIT:
105 return s->limit;
106 case TIMER_VALUE:
107 return ptimer_get_count(s->timer);
108 case TIMER_INT_ENABLE:
109 return s->int_enabled;
110 case TIMER_INT_STATUS:
111 return s->int_level;
112 case TIMER_FREQ:
113 return s->freq;
114 default:
115 cpu_abort(cpu_single_env, "syborg_timer_read: Bad offset %x\n",
116 (int)offset);
117 return 0;
121 static void syborg_timer_write(void *opaque, hwaddr offset,
122 uint64_t value, unsigned size)
124 SyborgTimerState *s = (SyborgTimerState *)opaque;
126 DPRINTF("Reg write %d\n", (int)offset);
127 offset &= 0xfff;
128 switch (offset >> 2) {
129 case TIMER_RUNNING:
130 if (value == s->running)
131 break;
132 s->running = value;
133 if (value) {
134 ptimer_run(s->timer, s->oneshot);
135 } else {
136 ptimer_stop(s->timer);
138 break;
139 case TIMER_ONESHOT:
140 if (s->running) {
141 ptimer_stop(s->timer);
143 s->oneshot = value;
144 if (s->running) {
145 ptimer_run(s->timer, s->oneshot);
147 break;
148 case TIMER_LIMIT:
149 s->limit = value;
150 ptimer_set_limit(s->timer, value, 1);
151 break;
152 case TIMER_VALUE:
153 ptimer_set_count(s->timer, value);
154 break;
155 case TIMER_INT_ENABLE:
156 s->int_enabled = value;
157 syborg_timer_update(s);
158 break;
159 case TIMER_INT_STATUS:
160 s->int_level &= ~value;
161 syborg_timer_update(s);
162 break;
163 default:
164 cpu_abort(cpu_single_env, "syborg_timer_write: Bad offset %x\n",
165 (int)offset);
166 break;
170 static const MemoryRegionOps syborg_timer_ops = {
171 .read = syborg_timer_read,
172 .write = syborg_timer_write,
173 .endianness = DEVICE_NATIVE_ENDIAN,
176 static const VMStateDescription vmstate_syborg_timer = {
177 .name = "syborg_timer",
178 .version_id = 1,
179 .minimum_version_id = 1,
180 .minimum_version_id_old = 1,
181 .fields = (VMStateField[]) {
182 VMSTATE_INT32(running, SyborgTimerState),
183 VMSTATE_INT32(oneshot, SyborgTimerState),
184 VMSTATE_UINT32(limit, SyborgTimerState),
185 VMSTATE_UINT32(int_level, SyborgTimerState),
186 VMSTATE_UINT32(int_enabled, SyborgTimerState),
187 VMSTATE_PTIMER(timer, SyborgTimerState),
188 VMSTATE_END_OF_LIST()
192 static int syborg_timer_init(SysBusDevice *sbd)
194 DeviceState *dev = DEVICE(sbd);
195 SyborgTimerState *s = SYBORG_TIMER(dev);
196 QEMUBH *bh;
198 if (s->freq == 0) {
199 fprintf(stderr, "syborg_timer: Zero/unset frequency\n");
200 exit(1);
202 sysbus_init_irq(dev, &s->irq);
203 memory_region_init_io(&s->iomem, &syborg_timer_ops, s, "timer", 0x1000);
204 sysbus_init_mmio(sbd, &s->iomem);
206 bh = qemu_bh_new(syborg_timer_tick, s);
207 s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
208 ptimer_set_freq(s->timer, s->freq);
209 vmstate_register(&dev->qdev, -1, &vmstate_syborg_timer, s);
210 return 0;
213 static Property syborg_timer_properties[] = {
214 DEFINE_PROP_UINT32("frequency",SyborgTimerState, freq, 0),
215 DEFINE_PROP_END_OF_LIST()
218 static void syborg_timer_class_init(ObjectClass *klass, void *data)
220 DeviceClass *dc = DEVICE_CLASS(klass);
221 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
222 dc->props = syborg_timer_properties;
223 k->init = syborg_timer_init;
226 static const TypeInfo syborg_timer_info = {
227 .name = "syborg,timer",
228 .parent = TYPE_SYS_BUS_DEVICE,
229 .instance_size = sizeof(SyborgTimerState),
230 .class_init = syborg_timer_class_init
233 static void syborg_timer_register_types(void)
235 type_register_static(&syborg_timer_info);
238 type_init(syborg_timer_register_types)