Statistics
| Branch: | Revision:

root / hw / omap_gptimer.c @ 7267c094

History | View | Annotate | Download (12.7 kB)

1
/*
2
 * TI OMAP2 general purpose timers emulation.
3
 *
4
 * Copyright (C) 2007-2008 Nokia Corporation
5
 * Written by Andrzej Zaborowski <andrew@openedhand.com>
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public License as
9
 * published by the Free Software Foundation; either version 2 or
10
 * (at your option) any later version of the License.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License along
18
 * with this program; if not, see <http://www.gnu.org/licenses/>.
19
 */
20
#include "hw.h"
21
#include "qemu-timer.h"
22
#include "omap.h"
23

    
24
/* GP timers */
25
struct omap_gp_timer_s {
26
    qemu_irq irq;
27
    qemu_irq wkup;
28
    qemu_irq in;
29
    qemu_irq out;
30
    omap_clk clk;
31
    QEMUTimer *timer;
32
    QEMUTimer *match;
33
    struct omap_target_agent_s *ta;
34

    
35
    int in_val;
36
    int out_val;
37
    int64_t time;
38
    int64_t rate;
39
    int64_t ticks_per_sec;
40

    
41
    int16_t config;
42
    int status;
43
    int it_ena;
44
    int wu_ena;
45
    int enable;
46
    int inout;
47
    int capt2;
48
    int pt;
49
    enum {
50
        gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both
51
    } trigger;
52
    enum {
53
        gpt_capture_none, gpt_capture_rising,
54
        gpt_capture_falling, gpt_capture_both
55
    } capture;
56
    int scpwm;
57
    int ce;
58
    int pre;
59
    int ptv;
60
    int ar;
61
    int st;
62
    int posted;
63
    uint32_t val;
64
    uint32_t load_val;
65
    uint32_t capture_val[2];
66
    uint32_t match_val;
67
    int capt_num;
68

    
69
    uint16_t writeh;        /* LSB */
70
    uint16_t readh;        /* MSB */
71
};
72

    
73
#define GPT_TCAR_IT        (1 << 2)
74
#define GPT_OVF_IT        (1 << 1)
75
#define GPT_MAT_IT        (1 << 0)
76

    
77
static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it)
78
{
79
    if (timer->it_ena & it) {
80
        if (!timer->status)
81
            qemu_irq_raise(timer->irq);
82

    
83
        timer->status |= it;
84
        /* Or are the status bits set even when masked?
85
         * i.e. is masking applied before or after the status register?  */
86
    }
87

    
88
    if (timer->wu_ena & it)
89
        qemu_irq_pulse(timer->wkup);
90
}
91

    
92
static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level)
93
{
94
    if (!timer->inout && timer->out_val != level) {
95
        timer->out_val = level;
96
        qemu_set_irq(timer->out, level);
97
    }
98
}
99

    
100
static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer)
101
{
102
    uint64_t distance;
103

    
104
    if (timer->st && timer->rate) {
105
        distance = qemu_get_clock_ns(vm_clock) - timer->time;
106
        distance = muldiv64(distance, timer->rate, timer->ticks_per_sec);
107

    
108
        if (distance >= 0xffffffff - timer->val)
109
            return 0xffffffff;
110
        else
111
            return timer->val + distance;
112
    } else
113
        return timer->val;
114
}
115

    
116
static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer)
117
{
118
    if (timer->st) {
119
        timer->val = omap_gp_timer_read(timer);
120
        timer->time = qemu_get_clock_ns(vm_clock);
121
    }
122
}
123

    
124
static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer)
125
{
126
    int64_t expires, matches;
127

    
128
    if (timer->st && timer->rate) {
129
        expires = muldiv64(0x100000000ll - timer->val,
130
                        timer->ticks_per_sec, timer->rate);
131
        qemu_mod_timer(timer->timer, timer->time + expires);
132

    
133
        if (timer->ce && timer->match_val >= timer->val) {
134
            matches = muldiv64(timer->match_val - timer->val,
135
                            timer->ticks_per_sec, timer->rate);
136
            qemu_mod_timer(timer->match, timer->time + matches);
137
        } else
138
            qemu_del_timer(timer->match);
139
    } else {
140
        qemu_del_timer(timer->timer);
141
        qemu_del_timer(timer->match);
142
        omap_gp_timer_out(timer, timer->scpwm);
143
    }
144
}
145

    
146
static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer)
147
{
148
    if (timer->pt)
149
        /* TODO in overflow-and-match mode if the first event to
150
         * occur is the match, don't toggle.  */
151
        omap_gp_timer_out(timer, !timer->out_val);
152
    else
153
        /* TODO inverted pulse on timer->out_val == 1?  */
154
        qemu_irq_pulse(timer->out);
155
}
156

    
157
static void omap_gp_timer_tick(void *opaque)
158
{
159
    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
160

    
161
    if (!timer->ar) {
162
        timer->st = 0;
163
        timer->val = 0;
164
    } else {
165
        timer->val = timer->load_val;
166
        timer->time = qemu_get_clock_ns(vm_clock);
167
    }
168

    
169
    if (timer->trigger == gpt_trigger_overflow ||
170
                    timer->trigger == gpt_trigger_both)
171
        omap_gp_timer_trigger(timer);
172

    
173
    omap_gp_timer_intr(timer, GPT_OVF_IT);
174
    omap_gp_timer_update(timer);
175
}
176

    
177
static void omap_gp_timer_match(void *opaque)
178
{
179
    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
180

    
181
    if (timer->trigger == gpt_trigger_both)
182
        omap_gp_timer_trigger(timer);
183

    
184
    omap_gp_timer_intr(timer, GPT_MAT_IT);
185
}
186

    
187
static void omap_gp_timer_input(void *opaque, int line, int on)
188
{
189
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
190
    int trigger;
191

    
192
    switch (s->capture) {
193
    default:
194
    case gpt_capture_none:
195
        trigger = 0;
196
        break;
197
    case gpt_capture_rising:
198
        trigger = !s->in_val && on;
199
        break;
200
    case gpt_capture_falling:
201
        trigger = s->in_val && !on;
202
        break;
203
    case gpt_capture_both:
204
        trigger = (s->in_val == !on);
205
        break;
206
    }
207
    s->in_val = on;
208

    
209
    if (s->inout && trigger && s->capt_num < 2) {
210
        s->capture_val[s->capt_num] = omap_gp_timer_read(s);
211

    
212
        if (s->capt2 == s->capt_num ++)
213
            omap_gp_timer_intr(s, GPT_TCAR_IT);
214
    }
215
}
216

    
217
static void omap_gp_timer_clk_update(void *opaque, int line, int on)
218
{
219
    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
220

    
221
    omap_gp_timer_sync(timer);
222
    timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
223
    omap_gp_timer_update(timer);
224
}
225

    
226
static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer)
227
{
228
    omap_clk_adduser(timer->clk,
229
                    qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]);
230
    timer->rate = omap_clk_getrate(timer->clk);
231
}
232

    
233
void omap_gp_timer_reset(struct omap_gp_timer_s *s)
234
{
235
    s->config = 0x000;
236
    s->status = 0;
237
    s->it_ena = 0;
238
    s->wu_ena = 0;
239
    s->inout = 0;
240
    s->capt2 = 0;
241
    s->capt_num = 0;
242
    s->pt = 0;
243
    s->trigger = gpt_trigger_none;
244
    s->capture = gpt_capture_none;
245
    s->scpwm = 0;
246
    s->ce = 0;
247
    s->pre = 0;
248
    s->ptv = 0;
249
    s->ar = 0;
250
    s->st = 0;
251
    s->posted = 1;
252
    s->val = 0x00000000;
253
    s->load_val = 0x00000000;
254
    s->capture_val[0] = 0x00000000;
255
    s->capture_val[1] = 0x00000000;
256
    s->match_val = 0x00000000;
257
    omap_gp_timer_update(s);
258
}
259

    
260
static uint32_t omap_gp_timer_readw(void *opaque, target_phys_addr_t addr)
261
{
262
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
263

    
264
    switch (addr) {
265
    case 0x00:        /* TIDR */
266
        return 0x21;
267

    
268
    case 0x10:        /* TIOCP_CFG */
269
        return s->config;
270

    
271
    case 0x14:        /* TISTAT */
272
        /* ??? When's this bit reset? */
273
        return 1;                                                /* RESETDONE */
274

    
275
    case 0x18:        /* TISR */
276
        return s->status;
277

    
278
    case 0x1c:        /* TIER */
279
        return s->it_ena;
280

    
281
    case 0x20:        /* TWER */
282
        return s->wu_ena;
283

    
284
    case 0x24:        /* TCLR */
285
        return (s->inout << 14) |
286
                (s->capt2 << 13) |
287
                (s->pt << 12) |
288
                (s->trigger << 10) |
289
                (s->capture << 8) |
290
                (s->scpwm << 7) |
291
                (s->ce << 6) |
292
                (s->pre << 5) |
293
                (s->ptv << 2) |
294
                (s->ar << 1) |
295
                (s->st << 0);
296

    
297
    case 0x28:        /* TCRR */
298
        return omap_gp_timer_read(s);
299

    
300
    case 0x2c:        /* TLDR */
301
        return s->load_val;
302

    
303
    case 0x30:        /* TTGR */
304
        return 0xffffffff;
305

    
306
    case 0x34:        /* TWPS */
307
        return 0x00000000;        /* No posted writes pending.  */
308

    
309
    case 0x38:        /* TMAR */
310
        return s->match_val;
311

    
312
    case 0x3c:        /* TCAR1 */
313
        return s->capture_val[0];
314

    
315
    case 0x40:        /* TSICR */
316
        return s->posted << 2;
317

    
318
    case 0x44:        /* TCAR2 */
319
        return s->capture_val[1];
320
    }
321

    
322
    OMAP_BAD_REG(addr);
323
    return 0;
324
}
325

    
326
static uint32_t omap_gp_timer_readh(void *opaque, target_phys_addr_t addr)
327
{
328
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
329
    uint32_t ret;
330

    
331
    if (addr & 2)
332
        return s->readh;
333
    else {
334
        ret = omap_gp_timer_readw(opaque, addr);
335
        s->readh = ret >> 16;
336
        return ret & 0xffff;
337
    }
338
}
339

    
340
static CPUReadMemoryFunc * const omap_gp_timer_readfn[] = {
341
    omap_badwidth_read32,
342
    omap_gp_timer_readh,
343
    omap_gp_timer_readw,
344
};
345

    
346
static void omap_gp_timer_write(void *opaque, target_phys_addr_t addr,
347
                uint32_t value)
348
{
349
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
350

    
351
    switch (addr) {
352
    case 0x00:        /* TIDR */
353
    case 0x14:        /* TISTAT */
354
    case 0x34:        /* TWPS */
355
    case 0x3c:        /* TCAR1 */
356
    case 0x44:        /* TCAR2 */
357
        OMAP_RO_REG(addr);
358
        break;
359

    
360
    case 0x10:        /* TIOCP_CFG */
361
        s->config = value & 0x33d;
362
        if (((value >> 3) & 3) == 3)                                /* IDLEMODE */
363
            fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n",
364
                            __FUNCTION__);
365
        if (value & 2)                                                /* SOFTRESET */
366
            omap_gp_timer_reset(s);
367
        break;
368

    
369
    case 0x18:        /* TISR */
370
        if (value & GPT_TCAR_IT)
371
            s->capt_num = 0;
372
        if (s->status && !(s->status &= ~value))
373
            qemu_irq_lower(s->irq);
374
        break;
375

    
376
    case 0x1c:        /* TIER */
377
        s->it_ena = value & 7;
378
        break;
379

    
380
    case 0x20:        /* TWER */
381
        s->wu_ena = value & 7;
382
        break;
383

    
384
    case 0x24:        /* TCLR */
385
        omap_gp_timer_sync(s);
386
        s->inout = (value >> 14) & 1;
387
        s->capt2 = (value >> 13) & 1;
388
        s->pt = (value >> 12) & 1;
389
        s->trigger = (value >> 10) & 3;
390
        if (s->capture == gpt_capture_none &&
391
                        ((value >> 8) & 3) != gpt_capture_none)
392
            s->capt_num = 0;
393
        s->capture = (value >> 8) & 3;
394
        s->scpwm = (value >> 7) & 1;
395
        s->ce = (value >> 6) & 1;
396
        s->pre = (value >> 5) & 1;
397
        s->ptv = (value >> 2) & 7;
398
        s->ar = (value >> 1) & 1;
399
        s->st = (value >> 0) & 1;
400
        if (s->inout && s->trigger != gpt_trigger_none)
401
            fprintf(stderr, "%s: GP timer pin must be an output "
402
                            "for this trigger mode\n", __FUNCTION__);
403
        if (!s->inout && s->capture != gpt_capture_none)
404
            fprintf(stderr, "%s: GP timer pin must be an input "
405
                            "for this capture mode\n", __FUNCTION__);
406
        if (s->trigger == gpt_trigger_none)
407
            omap_gp_timer_out(s, s->scpwm);
408
        /* TODO: make sure this doesn't overflow 32-bits */
409
        s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0);
410
        omap_gp_timer_update(s);
411
        break;
412

    
413
    case 0x28:        /* TCRR */
414
        s->time = qemu_get_clock_ns(vm_clock);
415
        s->val = value;
416
        omap_gp_timer_update(s);
417
        break;
418

    
419
    case 0x2c:        /* TLDR */
420
        s->load_val = value;
421
        break;
422

    
423
    case 0x30:        /* TTGR */
424
        s->time = qemu_get_clock_ns(vm_clock);
425
        s->val = s->load_val;
426
        omap_gp_timer_update(s);
427
        break;
428

    
429
    case 0x38:        /* TMAR */
430
        omap_gp_timer_sync(s);
431
        s->match_val = value;
432
        omap_gp_timer_update(s);
433
        break;
434

    
435
    case 0x40:        /* TSICR */
436
        s->posted = (value >> 2) & 1;
437
        if (value & 2)        /* How much exactly are we supposed to reset? */
438
            omap_gp_timer_reset(s);
439
        break;
440

    
441
    default:
442
        OMAP_BAD_REG(addr);
443
    }
444
}
445

    
446
static void omap_gp_timer_writeh(void *opaque, target_phys_addr_t addr,
447
                uint32_t value)
448
{
449
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
450

    
451
    if (addr & 2)
452
        return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh);
453
    else
454
        s->writeh = (uint16_t) value;
455
}
456

    
457
static CPUWriteMemoryFunc * const omap_gp_timer_writefn[] = {
458
    omap_badwidth_write32,
459
    omap_gp_timer_writeh,
460
    omap_gp_timer_write,
461
};
462

    
463
struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
464
                qemu_irq irq, omap_clk fclk, omap_clk iclk)
465
{
466
    int iomemtype;
467
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
468
            g_malloc0(sizeof(struct omap_gp_timer_s));
469

    
470
    s->ta = ta;
471
    s->irq = irq;
472
    s->clk = fclk;
473
    s->timer = qemu_new_timer_ns(vm_clock, omap_gp_timer_tick, s);
474
    s->match = qemu_new_timer_ns(vm_clock, omap_gp_timer_match, s);
475
    s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0];
476
    omap_gp_timer_reset(s);
477
    omap_gp_timer_clk_setup(s);
478

    
479
    iomemtype = l4_register_io_memory(omap_gp_timer_readfn,
480
                    omap_gp_timer_writefn, s);
481
    omap_l4_attach(ta, 0, iomemtype);
482

    
483
    return s;
484
}