Statistics
| Branch: | Revision:

root / hw / syborg_timer.c @ ee6847d1

History | View | Annotate | Download (6.6 kB)

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
    if (s->freq == 0) {
213
        fprintf(stderr, "syborg_timer: Zero/unset frequency\n");
214
        exit(1);
215
    }
216
    sysbus_init_irq(dev, &s->irq);
217
    iomemtype = cpu_register_io_memory(syborg_timer_readfn,
218
                                       syborg_timer_writefn, s);
219
    sysbus_init_mmio(dev, 0x1000, iomemtype);
220

    
221
    bh = qemu_bh_new(syborg_timer_tick, s);
222
    s->timer = ptimer_init(bh);
223
    ptimer_set_freq(s->timer, s->freq);
224
    register_savevm("syborg_timer", -1, 1,
225
                    syborg_timer_save, syborg_timer_load, s);
226
}
227

    
228
static SysBusDeviceInfo syborg_timer_info = {
229
    .init = syborg_timer_init,
230
    .qdev.name  = "syborg,timer",
231
    .qdev.size  = sizeof(SyborgTimerState),
232
    .qdev.props = (Property[]) {
233
        {
234
            .name   = "frequency",
235
            .info   = &qdev_prop_uint32,
236
            .offset = offsetof(SyborgTimerState, freq),
237
        },
238
        {/* end of list */}
239
    }
240
};
241

    
242
static void syborg_timer_register_devices(void)
243
{
244
    sysbus_register_withprop(&syborg_timer_info);
245
}
246

    
247
device_init(syborg_timer_register_devices)