~ubuntu-branches/ubuntu/wily/qemu-kvm-spice/wily

« back to all changes in this revision

Viewing changes to hw/omap_gptimer.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-10-19 10:44:56 UTC
  • Revision ID: james.westby@ubuntu.com-20111019104456-xgvskumk3sxi97f4
Tags: upstream-0.15.0+noroms
ImportĀ upstreamĀ versionĀ 0.15.0+noroms

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
            qemu_mallocz(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
}