Statistics
| Branch: | Revision:

root / hw / omap_gptimer.c @ 1de7afc9

History | View | Annotate | Download (12.8 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
    MemoryRegion iomem;
27
    qemu_irq irq;
28
    qemu_irq wkup;
29
    qemu_irq in;
30
    qemu_irq out;
31
    omap_clk clk;
32
    QEMUTimer *timer;
33
    QEMUTimer *match;
34
    struct omap_target_agent_s *ta;
35

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
185
    omap_gp_timer_intr(timer, GPT_MAT_IT);
186
}
187

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
341
static void omap_gp_timer_write(void *opaque, hwaddr addr,
342
                uint32_t value)
343
{
344
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
345

    
346
    switch (addr) {
347
    case 0x00:        /* TIDR */
348
    case 0x14:        /* TISTAT */
349
    case 0x34:        /* TWPS */
350
    case 0x3c:        /* TCAR1 */
351
    case 0x44:        /* TCAR2 */
352
        OMAP_RO_REG(addr);
353
        break;
354

    
355
    case 0x10:        /* TIOCP_CFG */
356
        s->config = value & 0x33d;
357
        if (((value >> 3) & 3) == 3)                                /* IDLEMODE */
358
            fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n",
359
                            __FUNCTION__);
360
        if (value & 2)                                                /* SOFTRESET */
361
            omap_gp_timer_reset(s);
362
        break;
363

    
364
    case 0x18:        /* TISR */
365
        if (value & GPT_TCAR_IT)
366
            s->capt_num = 0;
367
        if (s->status && !(s->status &= ~value))
368
            qemu_irq_lower(s->irq);
369
        break;
370

    
371
    case 0x1c:        /* TIER */
372
        s->it_ena = value & 7;
373
        break;
374

    
375
    case 0x20:        /* TWER */
376
        s->wu_ena = value & 7;
377
        break;
378

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

    
408
    case 0x28:        /* TCRR */
409
        s->time = qemu_get_clock_ns(vm_clock);
410
        s->val = value;
411
        omap_gp_timer_update(s);
412
        break;
413

    
414
    case 0x2c:        /* TLDR */
415
        s->load_val = value;
416
        break;
417

    
418
    case 0x30:        /* TTGR */
419
        s->time = qemu_get_clock_ns(vm_clock);
420
        s->val = s->load_val;
421
        omap_gp_timer_update(s);
422
        break;
423

    
424
    case 0x38:        /* TMAR */
425
        omap_gp_timer_sync(s);
426
        s->match_val = value;
427
        omap_gp_timer_update(s);
428
        break;
429

    
430
    case 0x40:        /* TSICR */
431
        s->posted = (value >> 2) & 1;
432
        if (value & 2)        /* How much exactly are we supposed to reset? */
433
            omap_gp_timer_reset(s);
434
        break;
435

    
436
    default:
437
        OMAP_BAD_REG(addr);
438
    }
439
}
440

    
441
static void omap_gp_timer_writeh(void *opaque, hwaddr addr,
442
                uint32_t value)
443
{
444
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
445

    
446
    if (addr & 2)
447
        return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh);
448
    else
449
        s->writeh = (uint16_t) value;
450
}
451

    
452
static const MemoryRegionOps omap_gp_timer_ops = {
453
    .old_mmio = {
454
        .read = {
455
            omap_badwidth_read32,
456
            omap_gp_timer_readh,
457
            omap_gp_timer_readw,
458
        },
459
        .write = {
460
            omap_badwidth_write32,
461
            omap_gp_timer_writeh,
462
            omap_gp_timer_write,
463
        },
464
    },
465
    .endianness = DEVICE_NATIVE_ENDIAN,
466
};
467

    
468
struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
469
                qemu_irq irq, omap_clk fclk, omap_clk iclk)
470
{
471
    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
472
            g_malloc0(sizeof(struct omap_gp_timer_s));
473

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

    
483
    memory_region_init_io(&s->iomem, &omap_gp_timer_ops, s, "omap.gptimer",
484
                          omap_l4_region_size(ta, 0));
485
    omap_l4_attach(ta, 0, &s->iomem);
486

    
487
    return s;
488
}