Statistics
| Branch: | Revision:

root / hw / etraxfs_timer.c @ d297f464

History | View | Annotate | Download (8.1 kB)

1
/*
2
 * QEMU ETRAX Timers
3
 *
4
 * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
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
#include <stdio.h>
25
#include <sys/time.h>
26
#include "hw.h"
27
#include "sysemu.h"
28
#include "qemu-timer.h"
29

    
30
#define D(x)
31

    
32
#define RW_TMR0_DIV   0x00
33
#define R_TMR0_DATA   0x04
34
#define RW_TMR0_CTRL  0x08
35
#define RW_TMR1_DIV   0x10
36
#define R_TMR1_DATA   0x14
37
#define RW_TMR1_CTRL  0x18
38
#define R_TIME        0x38
39
#define RW_WD_CTRL    0x40
40
#define R_WD_STAT     0x44
41
#define RW_INTR_MASK  0x48
42
#define RW_ACK_INTR   0x4c
43
#define R_INTR        0x50
44
#define R_MASKED_INTR 0x54
45

    
46
struct fs_timer_t {
47
        CPUState *env;
48
        qemu_irq *irq;
49
        qemu_irq *nmi;
50
        target_phys_addr_t base;
51

    
52
        QEMUBH *bh_t0;
53
        QEMUBH *bh_t1;
54
        QEMUBH *bh_wd;
55
        ptimer_state *ptimer_t0;
56
        ptimer_state *ptimer_t1;
57
        ptimer_state *ptimer_wd;
58
        struct timeval last;
59

    
60
        int wd_hits;
61

    
62
        /* Control registers.  */
63
        uint32_t rw_tmr0_div;
64
        uint32_t r_tmr0_data;
65
        uint32_t rw_tmr0_ctrl;
66

    
67
        uint32_t rw_tmr1_div;
68
        uint32_t r_tmr1_data;
69
        uint32_t rw_tmr1_ctrl;
70

    
71
        uint32_t rw_wd_ctrl;
72

    
73
        uint32_t rw_intr_mask;
74
        uint32_t rw_ack_intr;
75
        uint32_t r_intr;
76
        uint32_t r_masked_intr;
77
};
78

    
79
static uint32_t timer_rinvalid (void *opaque, target_phys_addr_t addr)
80
{
81
        struct fs_timer_t *t = opaque;
82
        CPUState *env = t->env;
83
        cpu_abort(env, "Unsupported short access. reg=" TARGET_FMT_plx
84
                  " pc=%x.\n", addr, env->pc);
85
        return 0;
86
}
87

    
88
static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
89
{
90
        struct fs_timer_t *t = opaque;
91
        D(CPUState *env = t->env);
92
        uint32_t r = 0;
93

    
94
        /* Make addr relative to this instances base.  */
95
        addr -= t->base;
96
        switch (addr) {
97
        case R_TMR0_DATA:
98
                break;
99
        case R_TMR1_DATA:
100
                D(printf ("R_TMR1_DATA\n"));
101
                break;
102
        case R_TIME:
103
                r = qemu_get_clock(vm_clock) * 10;
104
                break;
105
        case RW_INTR_MASK:
106
                r = t->rw_intr_mask;
107
                break;
108
        case R_MASKED_INTR:
109
                r = t->r_intr & t->rw_intr_mask;
110
                break;
111
        default:
112
                D(printf ("%s %x p=%x\n", __func__, addr, env->pc));
113
                break;
114
        }
115
        return r;
116
}
117

    
118
static void
119
timer_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value)
120
{
121
        struct fs_timer_t *t = opaque;
122
        CPUState *env = t->env;
123
        cpu_abort(env, "Unsupported short access. reg=" TARGET_FMT_plx
124
                  " pc=%x.\n", addr, env->pc);
125
}
126

    
127
#define TIMER_SLOWDOWN 1
128
static void update_ctrl(struct fs_timer_t *t, int tnum)
129
{
130
        unsigned int op;
131
        unsigned int freq;
132
        unsigned int freq_hz;
133
        unsigned int div;
134
        uint32_t ctrl;
135

    
136
        ptimer_state *timer;
137

    
138
        if (tnum == 0) {
139
                ctrl = t->rw_tmr0_ctrl;
140
                div = t->rw_tmr0_div;
141
                timer = t->ptimer_t0;
142
        } else {
143
                ctrl = t->rw_tmr1_ctrl;
144
                div = t->rw_tmr1_div;
145
                timer = t->ptimer_t1;
146
        }
147

    
148

    
149
        op = ctrl & 3;
150
        freq = ctrl >> 2;
151
        freq_hz = 32000000;
152

    
153
        switch (freq)
154
        {
155
        case 0:
156
        case 1:
157
                D(printf ("extern or disabled timer clock?\n"));
158
                break;
159
        case 4: freq_hz =  29493000; break;
160
        case 5: freq_hz =  32000000; break;
161
        case 6: freq_hz =  32768000; break;
162
        case 7: freq_hz = 100001000; break;
163
        default:
164
                abort();
165
                break;
166
        }
167

    
168
        D(printf ("freq_hz=%d div=%d\n", freq_hz, div));
169
        div = div * TIMER_SLOWDOWN;
170
        div >>= 10;
171
        freq_hz >>= 10;
172
        ptimer_set_freq(timer, freq_hz);
173
        ptimer_set_limit(timer, div, 0);
174

    
175
        switch (op)
176
        {
177
                case 0:
178
                        /* Load.  */
179
                        ptimer_set_limit(timer, div, 1);
180
                        break;
181
                case 1:
182
                        /* Hold.  */
183
                        ptimer_stop(timer);
184
                        break;
185
                case 2:
186
                        /* Run.  */
187
                        ptimer_run(timer, 0);
188
                        break;
189
                default:
190
                        abort();
191
                        break;
192
        }
193
}
194

    
195
static void timer_update_irq(struct fs_timer_t *t)
196
{
197
        t->r_intr &= ~(t->rw_ack_intr);
198
        t->r_masked_intr = t->r_intr & t->rw_intr_mask;
199

    
200
        D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr));
201
        if (t->r_masked_intr)
202
                qemu_irq_raise(t->irq[0]);
203
        else
204
                qemu_irq_lower(t->irq[0]);
205
}
206

    
207
static void timer0_hit(void *opaque)
208
{
209
        struct fs_timer_t *t = opaque;
210
        t->r_intr |= 1;
211
        timer_update_irq(t);
212
}
213

    
214
static void timer1_hit(void *opaque)
215
{
216
        struct fs_timer_t *t = opaque;
217
        t->r_intr |= 2;
218
        timer_update_irq(t);
219
}
220

    
221
static void watchdog_hit(void *opaque)
222
{
223
        struct fs_timer_t *t = opaque;
224
        if (t->wd_hits == 0) {
225
                /* real hw gives a single tick before reseting but we are
226
                   a bit friendlier to compensate for our slower execution.  */
227
                ptimer_set_count(t->ptimer_wd, 10);
228
                ptimer_run(t->ptimer_wd, 1);
229
                qemu_irq_raise(t->nmi[0]);
230
        }
231
        else
232
                qemu_system_reset_request();
233

    
234
        t->wd_hits++;
235
}
236

    
237
static inline void timer_watchdog_update(struct fs_timer_t *t, uint32_t value)
238
{
239
        unsigned int wd_en = t->rw_wd_ctrl & (1 << 8);
240
        unsigned int wd_key = t->rw_wd_ctrl >> 9;
241
        unsigned int wd_cnt = t->rw_wd_ctrl & 511;
242
        unsigned int new_key = value >> 9 & ((1 << 7) - 1);
243
        unsigned int new_cmd = (value >> 8) & 1;
244

    
245
        /* If the watchdog is enabled, they written key must match the
246
           complement of the previous.  */
247
        wd_key = ~wd_key & ((1 << 7) - 1);
248

    
249
        if (wd_en && wd_key != new_key)
250
                return;
251

    
252
        D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n", 
253
                 wd_en, new_key, wd_key, new_cmd, wd_cnt));
254

    
255
        if (t->wd_hits)
256
                qemu_irq_lower(t->nmi[0]);
257

    
258
        t->wd_hits = 0;
259

    
260
        ptimer_set_freq(t->ptimer_wd, 760);
261
        if (wd_cnt == 0)
262
                wd_cnt = 256;
263
        ptimer_set_count(t->ptimer_wd, wd_cnt);
264
        if (new_cmd)
265
                ptimer_run(t->ptimer_wd, 1);
266
        else
267
                ptimer_stop(t->ptimer_wd);
268

    
269
        t->rw_wd_ctrl = value;
270
}
271

    
272
static void
273
timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
274
{
275
        struct fs_timer_t *t = opaque;
276
        CPUState *env = t->env;
277

    
278
        /* Make addr relative to this instances base.  */
279
        addr -= t->base;
280
        switch (addr)
281
        {
282
                case RW_TMR0_DIV:
283
                        t->rw_tmr0_div = value;
284
                        break;
285
                case RW_TMR0_CTRL:
286
                        D(printf ("RW_TMR0_CTRL=%x\n", value));
287
                        t->rw_tmr0_ctrl = value;
288
                        update_ctrl(t, 0);
289
                        break;
290
                case RW_TMR1_DIV:
291
                        t->rw_tmr1_div = value;
292
                        break;
293
                case RW_TMR1_CTRL:
294
                        D(printf ("RW_TMR1_CTRL=%x\n", value));
295
                        t->rw_tmr1_ctrl = value;
296
                        update_ctrl(t, 1);
297
                        break;
298
                case RW_INTR_MASK:
299
                        D(printf ("RW_INTR_MASK=%x\n", value));
300
                        t->rw_intr_mask = value;
301
                        timer_update_irq(t);
302
                        break;
303
                case RW_WD_CTRL:
304
                        timer_watchdog_update(t, value);
305
                        break;
306
                case RW_ACK_INTR:
307
                        t->rw_ack_intr = value;
308
                        timer_update_irq(t);
309
                        t->rw_ack_intr = 0;
310
                        break;
311
                default:
312
                        printf ("%s " TARGET_FMT_plx " %x pc=%x\n",
313
                                __func__, addr, value, env->pc);
314
                        break;
315
        }
316
}
317

    
318
static CPUReadMemoryFunc *timer_read[] = {
319
        &timer_rinvalid,
320
        &timer_rinvalid,
321
        &timer_readl,
322
};
323

    
324
static CPUWriteMemoryFunc *timer_write[] = {
325
        &timer_winvalid,
326
        &timer_winvalid,
327
        &timer_writel,
328
};
329

    
330
static void etraxfs_timer_reset(void *opaque)
331
{
332
        struct fs_timer_t *t = opaque;
333

    
334
        ptimer_stop(t->ptimer_t0);
335
        ptimer_stop(t->ptimer_t1);
336
        ptimer_stop(t->ptimer_wd);
337
        t->rw_wd_ctrl = 0;
338
        t->r_intr = 0;
339
        t->rw_intr_mask = 0;
340
        qemu_irq_lower(t->irq[0]);
341
}
342

    
343
void etraxfs_timer_init(CPUState *env, qemu_irq *irqs, qemu_irq *nmi,
344
                        target_phys_addr_t base)
345
{
346
        static struct fs_timer_t *t;
347
        int timer_regs;
348

    
349
        t = qemu_mallocz(sizeof *t);
350
        if (!t)
351
                return;
352

    
353
        t->bh_t0 = qemu_bh_new(timer0_hit, t);
354
        t->bh_t1 = qemu_bh_new(timer1_hit, t);
355
        t->bh_wd = qemu_bh_new(watchdog_hit, t);
356
        t->ptimer_t0 = ptimer_init(t->bh_t0);
357
        t->ptimer_t1 = ptimer_init(t->bh_t1);
358
        t->ptimer_wd = ptimer_init(t->bh_wd);
359
        t->irq = irqs;
360
        t->nmi = nmi;
361
        t->env = env;
362
        t->base = base;
363

    
364
        timer_regs = cpu_register_io_memory(0, timer_read, timer_write, t);
365
        cpu_register_physical_memory (base, 0x5c, timer_regs);
366

    
367
        qemu_register_reset(etraxfs_timer_reset, t);
368
}