a84ad8602eccc3171da9bc48fccf869d9d315b56
[qemu] / hw / syborg_timer.c
1 /*
2  * Syborg Interval Timer.
3  *
4  * Copyright (c) 2008 CodeSourcery
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24
25 #include "sysbus.h"
26 #include "qemu-timer.h"
27 #include "syborg.h"
28
29 //#define DEBUG_SYBORG_TIMER
30
31 #ifdef DEBUG_SYBORG_TIMER
32 #define DPRINTF(fmt, ...) \
33 do { printf("syborg_timer: " fmt , ##args); } while (0)
34 #define BADF(fmt, ...) \
35 do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__); \
36     exit(1);} while (0)
37 #else
38 #define DPRINTF(fmt, ...) do {} while(0)
39 #define BADF(fmt, ...) \
40 do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__);} while (0)
41 #endif
42
43 enum {
44     TIMER_ID          = 0,
45     TIMER_RUNNING     = 1,
46     TIMER_ONESHOT     = 2,
47     TIMER_LIMIT       = 3,
48     TIMER_VALUE       = 4,
49     TIMER_INT_ENABLE  = 5,
50     TIMER_INT_STATUS  = 6,
51     TIMER_FREQ        = 7
52 };
53
54 typedef struct {
55     SysBusDevice busdev;
56     ptimer_state *timer;
57     int running;
58     int oneshot;
59     uint32_t limit;
60     uint32_t freq;
61     uint32_t int_level;
62     uint32_t int_enabled;
63     qemu_irq irq;
64 } SyborgTimerState;
65
66 static void syborg_timer_update(SyborgTimerState *s)
67 {
68     /* Update interrupt.  */
69     if (s->int_level && s->int_enabled) {
70         qemu_irq_raise(s->irq);
71     } else {
72         qemu_irq_lower(s->irq);
73     }
74 }
75
76 static void syborg_timer_tick(void *opaque)
77 {
78     SyborgTimerState *s = (SyborgTimerState *)opaque;
79     //DPRINTF("Timer Tick\n");
80     s->int_level = 1;
81     if (s->oneshot)
82         s->running = 0;
83     syborg_timer_update(s);
84 }
85
86 static uint32_t syborg_timer_read(void *opaque, target_phys_addr_t offset)
87 {
88     SyborgTimerState *s = (SyborgTimerState *)opaque;
89
90     DPRINTF("Reg read %d\n", (int)offset);
91     offset &= 0xfff;
92     switch (offset >> 2) {
93     case TIMER_ID:
94         return SYBORG_ID_TIMER;
95     case TIMER_RUNNING:
96         return s->running;
97     case TIMER_ONESHOT:
98         return s->oneshot;
99     case TIMER_LIMIT:
100         return s->limit;
101     case TIMER_VALUE:
102         return ptimer_get_count(s->timer);
103     case TIMER_INT_ENABLE:
104         return s->int_enabled;
105     case TIMER_INT_STATUS:
106         return s->int_level;
107     case TIMER_FREQ:
108         return s->freq;
109     default:
110         cpu_abort(cpu_single_env, "syborg_timer_read: Bad offset %x\n",
111                   (int)offset);
112         return 0;
113     }
114 }
115
116 static void syborg_timer_write(void *opaque, target_phys_addr_t offset,
117                                uint32_t value)
118 {
119     SyborgTimerState *s = (SyborgTimerState *)opaque;
120
121     DPRINTF("Reg write %d\n", (int)offset);
122     offset &= 0xfff;
123     switch (offset >> 2) {
124     case TIMER_RUNNING:
125         if (value == s->running)
126             break;
127         s->running = value;
128         if (value) {
129             ptimer_run(s->timer, s->oneshot);
130         } else {
131             ptimer_stop(s->timer);
132         }
133         break;
134     case TIMER_ONESHOT:
135         if (s->running) {
136             ptimer_stop(s->timer);
137         }
138         s->oneshot = value;
139         if (s->running) {
140             ptimer_run(s->timer, s->oneshot);
141         }
142         break;
143     case TIMER_LIMIT:
144         s->limit = value;
145         ptimer_set_limit(s->timer, value, 1);
146         break;
147     case TIMER_VALUE:
148         ptimer_set_count(s->timer, value);
149         break;
150     case TIMER_INT_ENABLE:
151         s->int_enabled = value;
152         syborg_timer_update(s);
153         break;
154     case TIMER_INT_STATUS:
155         s->int_level &= ~value;
156         syborg_timer_update(s);
157         break;
158     default:
159         cpu_abort(cpu_single_env, "syborg_timer_write: Bad offset %x\n",
160                   (int)offset);
161         break;
162     }
163 }
164
165 static CPUReadMemoryFunc *syborg_timer_readfn[] = {
166     syborg_timer_read,
167     syborg_timer_read,
168     syborg_timer_read
169 };
170
171 static CPUWriteMemoryFunc *syborg_timer_writefn[] = {
172     syborg_timer_write,
173     syborg_timer_write,
174     syborg_timer_write
175 };
176
177 static void syborg_timer_save(QEMUFile *f, void *opaque)
178 {
179     SyborgTimerState *s = opaque;
180
181     qemu_put_be32(f, s->running);
182     qemu_put_be32(f, s->oneshot);
183     qemu_put_be32(f, s->limit);
184     qemu_put_be32(f, s->int_level);
185     qemu_put_be32(f, s->int_enabled);
186     qemu_put_ptimer(f, s->timer);
187 }
188
189 static int syborg_timer_load(QEMUFile *f, void *opaque, int version_id)
190 {
191     SyborgTimerState *s = opaque;
192
193     if (version_id != 1)
194         return -EINVAL;
195
196     s->running = qemu_get_be32(f);
197     s->oneshot = qemu_get_be32(f);
198     s->limit = qemu_get_be32(f);
199     s->int_level = qemu_get_be32(f);
200     s->int_enabled = qemu_get_be32(f);
201     qemu_get_ptimer(f, s->timer);
202
203     return 0;
204 }
205
206 static void syborg_timer_init(SysBusDevice *dev)
207 {
208     SyborgTimerState *s = FROM_SYSBUS(SyborgTimerState, dev);
209     QEMUBH *bh;
210     int iomemtype;
211
212     s->freq = qdev_get_prop_int(&dev->qdev, "frequency", 0);
213     if (s->freq == 0) {
214         fprintf(stderr, "syborg_timer: Zero/unset frequency\n");
215         exit(1);
216     }
217     sysbus_init_irq(dev, &s->irq);
218     iomemtype = cpu_register_io_memory(0, syborg_timer_readfn,
219                                        syborg_timer_writefn, s);
220     sysbus_init_mmio(dev, 0x1000, iomemtype);
221
222     bh = qemu_bh_new(syborg_timer_tick, s);
223     s->timer = ptimer_init(bh);
224     ptimer_set_freq(s->timer, s->freq);
225     register_savevm("syborg_timer", -1, 1,
226                     syborg_timer_save, syborg_timer_load, s);
227 }
228
229 static void syborg_timer_register_devices(void)
230 {
231     sysbus_register_dev("syborg,timer", sizeof(SyborgTimerState),
232                         syborg_timer_init);
233 }
234
235 device_init(syborg_timer_register_devices)