Statistics
| Branch: | Revision:

root / hw / i8254.c @ 6ee093c9

History | View | Annotate | Download (14 kB)

1
/*
2
 * QEMU 8253/8254 interval timer emulation
3
 *
4
 * Copyright (c) 2003-2004 Fabrice Bellard
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 "hw.h"
25
#include "pc.h"
26
#include "isa.h"
27
#include "qemu-timer.h"
28

    
29
//#define DEBUG_PIT
30

    
31
#define RW_STATE_LSB 1
32
#define RW_STATE_MSB 2
33
#define RW_STATE_WORD0 3
34
#define RW_STATE_WORD1 4
35

    
36
typedef struct PITChannelState {
37
    int count; /* can be 65536 */
38
    uint16_t latched_count;
39
    uint8_t count_latched;
40
    uint8_t status_latched;
41
    uint8_t status;
42
    uint8_t read_state;
43
    uint8_t write_state;
44
    uint8_t write_latch;
45
    uint8_t rw_mode;
46
    uint8_t mode;
47
    uint8_t bcd; /* not supported */
48
    uint8_t gate; /* timer start */
49
    int64_t count_load_time;
50
    /* irq handling */
51
    int64_t next_transition_time;
52
    QEMUTimer *irq_timer;
53
    qemu_irq irq;
54
} PITChannelState;
55

    
56
struct PITState {
57
    PITChannelState channels[3];
58
};
59

    
60
static PITState pit_state;
61

    
62
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
63

    
64
static int pit_get_count(PITChannelState *s)
65
{
66
    uint64_t d;
67
    int counter;
68

    
69
    d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ,
70
                 get_ticks_per_sec());
71
    switch(s->mode) {
72
    case 0:
73
    case 1:
74
    case 4:
75
    case 5:
76
        counter = (s->count - d) & 0xffff;
77
        break;
78
    case 3:
79
        /* XXX: may be incorrect for odd counts */
80
        counter = s->count - ((2 * d) % s->count);
81
        break;
82
    default:
83
        counter = s->count - (d % s->count);
84
        break;
85
    }
86
    return counter;
87
}
88

    
89
/* get pit output bit */
90
static int pit_get_out1(PITChannelState *s, int64_t current_time)
91
{
92
    uint64_t d;
93
    int out;
94

    
95
    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
96
                 get_ticks_per_sec());
97
    switch(s->mode) {
98
    default:
99
    case 0:
100
        out = (d >= s->count);
101
        break;
102
    case 1:
103
        out = (d < s->count);
104
        break;
105
    case 2:
106
        if ((d % s->count) == 0 && d != 0)
107
            out = 1;
108
        else
109
            out = 0;
110
        break;
111
    case 3:
112
        out = (d % s->count) < ((s->count + 1) >> 1);
113
        break;
114
    case 4:
115
    case 5:
116
        out = (d == s->count);
117
        break;
118
    }
119
    return out;
120
}
121

    
122
int pit_get_out(PITState *pit, int channel, int64_t current_time)
123
{
124
    PITChannelState *s = &pit->channels[channel];
125
    return pit_get_out1(s, current_time);
126
}
127

    
128
/* return -1 if no transition will occur.  */
129
static int64_t pit_get_next_transition_time(PITChannelState *s,
130
                                            int64_t current_time)
131
{
132
    uint64_t d, next_time, base;
133
    int period2;
134

    
135
    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
136
                 get_ticks_per_sec());
137
    switch(s->mode) {
138
    default:
139
    case 0:
140
    case 1:
141
        if (d < s->count)
142
            next_time = s->count;
143
        else
144
            return -1;
145
        break;
146
    case 2:
147
        base = (d / s->count) * s->count;
148
        if ((d - base) == 0 && d != 0)
149
            next_time = base + s->count;
150
        else
151
            next_time = base + s->count + 1;
152
        break;
153
    case 3:
154
        base = (d / s->count) * s->count;
155
        period2 = ((s->count + 1) >> 1);
156
        if ((d - base) < period2)
157
            next_time = base + period2;
158
        else
159
            next_time = base + s->count;
160
        break;
161
    case 4:
162
    case 5:
163
        if (d < s->count)
164
            next_time = s->count;
165
        else if (d == s->count)
166
            next_time = s->count + 1;
167
        else
168
            return -1;
169
        break;
170
    }
171
    /* convert to timer units */
172
    next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
173
                                              PIT_FREQ);
174
    /* fix potential rounding problems */
175
    /* XXX: better solution: use a clock at PIT_FREQ Hz */
176
    if (next_time <= current_time)
177
        next_time = current_time + 1;
178
    return next_time;
179
}
180

    
181
/* val must be 0 or 1 */
182
void pit_set_gate(PITState *pit, int channel, int val)
183
{
184
    PITChannelState *s = &pit->channels[channel];
185

    
186
    switch(s->mode) {
187
    default:
188
    case 0:
189
    case 4:
190
        /* XXX: just disable/enable counting */
191
        break;
192
    case 1:
193
    case 5:
194
        if (s->gate < val) {
195
            /* restart counting on rising edge */
196
            s->count_load_time = qemu_get_clock(vm_clock);
197
            pit_irq_timer_update(s, s->count_load_time);
198
        }
199
        break;
200
    case 2:
201
    case 3:
202
        if (s->gate < val) {
203
            /* restart counting on rising edge */
204
            s->count_load_time = qemu_get_clock(vm_clock);
205
            pit_irq_timer_update(s, s->count_load_time);
206
        }
207
        /* XXX: disable/enable counting */
208
        break;
209
    }
210
    s->gate = val;
211
}
212

    
213
int pit_get_gate(PITState *pit, int channel)
214
{
215
    PITChannelState *s = &pit->channels[channel];
216
    return s->gate;
217
}
218

    
219
int pit_get_initial_count(PITState *pit, int channel)
220
{
221
    PITChannelState *s = &pit->channels[channel];
222
    return s->count;
223
}
224

    
225
int pit_get_mode(PITState *pit, int channel)
226
{
227
    PITChannelState *s = &pit->channels[channel];
228
    return s->mode;
229
}
230

    
231
static inline void pit_load_count(PITChannelState *s, int val)
232
{
233
    if (val == 0)
234
        val = 0x10000;
235
    s->count_load_time = qemu_get_clock(vm_clock);
236
    s->count = val;
237
    pit_irq_timer_update(s, s->count_load_time);
238
}
239

    
240
/* if already latched, do not latch again */
241
static void pit_latch_count(PITChannelState *s)
242
{
243
    if (!s->count_latched) {
244
        s->latched_count = pit_get_count(s);
245
        s->count_latched = s->rw_mode;
246
    }
247
}
248

    
249
static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
250
{
251
    PITState *pit = opaque;
252
    int channel, access;
253
    PITChannelState *s;
254

    
255
    addr &= 3;
256
    if (addr == 3) {
257
        channel = val >> 6;
258
        if (channel == 3) {
259
            /* read back command */
260
            for(channel = 0; channel < 3; channel++) {
261
                s = &pit->channels[channel];
262
                if (val & (2 << channel)) {
263
                    if (!(val & 0x20)) {
264
                        pit_latch_count(s);
265
                    }
266
                    if (!(val & 0x10) && !s->status_latched) {
267
                        /* status latch */
268
                        /* XXX: add BCD and null count */
269
                        s->status =  (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) |
270
                            (s->rw_mode << 4) |
271
                            (s->mode << 1) |
272
                            s->bcd;
273
                        s->status_latched = 1;
274
                    }
275
                }
276
            }
277
        } else {
278
            s = &pit->channels[channel];
279
            access = (val >> 4) & 3;
280
            if (access == 0) {
281
                pit_latch_count(s);
282
            } else {
283
                s->rw_mode = access;
284
                s->read_state = access;
285
                s->write_state = access;
286

    
287
                s->mode = (val >> 1) & 7;
288
                s->bcd = val & 1;
289
                /* XXX: update irq timer ? */
290
            }
291
        }
292
    } else {
293
        s = &pit->channels[addr];
294
        switch(s->write_state) {
295
        default:
296
        case RW_STATE_LSB:
297
            pit_load_count(s, val);
298
            break;
299
        case RW_STATE_MSB:
300
            pit_load_count(s, val << 8);
301
            break;
302
        case RW_STATE_WORD0:
303
            s->write_latch = val;
304
            s->write_state = RW_STATE_WORD1;
305
            break;
306
        case RW_STATE_WORD1:
307
            pit_load_count(s, s->write_latch | (val << 8));
308
            s->write_state = RW_STATE_WORD0;
309
            break;
310
        }
311
    }
312
}
313

    
314
static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
315
{
316
    PITState *pit = opaque;
317
    int ret, count;
318
    PITChannelState *s;
319

    
320
    addr &= 3;
321
    s = &pit->channels[addr];
322
    if (s->status_latched) {
323
        s->status_latched = 0;
324
        ret = s->status;
325
    } else if (s->count_latched) {
326
        switch(s->count_latched) {
327
        default:
328
        case RW_STATE_LSB:
329
            ret = s->latched_count & 0xff;
330
            s->count_latched = 0;
331
            break;
332
        case RW_STATE_MSB:
333
            ret = s->latched_count >> 8;
334
            s->count_latched = 0;
335
            break;
336
        case RW_STATE_WORD0:
337
            ret = s->latched_count & 0xff;
338
            s->count_latched = RW_STATE_MSB;
339
            break;
340
        }
341
    } else {
342
        switch(s->read_state) {
343
        default:
344
        case RW_STATE_LSB:
345
            count = pit_get_count(s);
346
            ret = count & 0xff;
347
            break;
348
        case RW_STATE_MSB:
349
            count = pit_get_count(s);
350
            ret = (count >> 8) & 0xff;
351
            break;
352
        case RW_STATE_WORD0:
353
            count = pit_get_count(s);
354
            ret = count & 0xff;
355
            s->read_state = RW_STATE_WORD1;
356
            break;
357
        case RW_STATE_WORD1:
358
            count = pit_get_count(s);
359
            ret = (count >> 8) & 0xff;
360
            s->read_state = RW_STATE_WORD0;
361
            break;
362
        }
363
    }
364
    return ret;
365
}
366

    
367
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
368
{
369
    int64_t expire_time;
370
    int irq_level;
371

    
372
    if (!s->irq_timer)
373
        return;
374
    expire_time = pit_get_next_transition_time(s, current_time);
375
    irq_level = pit_get_out1(s, current_time);
376
    qemu_set_irq(s->irq, irq_level);
377
#ifdef DEBUG_PIT
378
    printf("irq_level=%d next_delay=%f\n",
379
           irq_level,
380
           (double)(expire_time - current_time) / get_ticks_per_sec());
381
#endif
382
    s->next_transition_time = expire_time;
383
    if (expire_time != -1)
384
        qemu_mod_timer(s->irq_timer, expire_time);
385
    else
386
        qemu_del_timer(s->irq_timer);
387
}
388

    
389
static void pit_irq_timer(void *opaque)
390
{
391
    PITChannelState *s = opaque;
392

    
393
    pit_irq_timer_update(s, s->next_transition_time);
394
}
395

    
396
static const VMStateDescription vmstate_pit_channel = {
397
    .name = "pit channel",
398
    .version_id = 2,
399
    .minimum_version_id = 2,
400
    .minimum_version_id_old = 2,
401
    .fields      = (VMStateField []) {
402
        VMSTATE_INT32(count, PITChannelState),
403
        VMSTATE_UINT16(latched_count, PITChannelState),
404
        VMSTATE_UINT8(count_latched, PITChannelState),
405
        VMSTATE_UINT8(status_latched, PITChannelState),
406
        VMSTATE_UINT8(status, PITChannelState),
407
        VMSTATE_UINT8(read_state, PITChannelState),
408
        VMSTATE_UINT8(write_state, PITChannelState),
409
        VMSTATE_UINT8(write_latch, PITChannelState),
410
        VMSTATE_UINT8(rw_mode, PITChannelState),
411
        VMSTATE_UINT8(mode, PITChannelState),
412
        VMSTATE_UINT8(bcd, PITChannelState),
413
        VMSTATE_UINT8(gate, PITChannelState),
414
        VMSTATE_INT64(count_load_time, PITChannelState),
415
        VMSTATE_INT64(next_transition_time, PITChannelState),
416
        VMSTATE_END_OF_LIST()
417
    }
418
};
419

    
420
static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
421
{
422
    PITState *pit = opaque;
423
    PITChannelState *s;
424
    int i;
425

    
426
    if (version_id != 1)
427
        return -EINVAL;
428

    
429
    for(i = 0; i < 3; i++) {
430
        s = &pit->channels[i];
431
        s->count=qemu_get_be32(f);
432
        qemu_get_be16s(f, &s->latched_count);
433
        qemu_get_8s(f, &s->count_latched);
434
        qemu_get_8s(f, &s->status_latched);
435
        qemu_get_8s(f, &s->status);
436
        qemu_get_8s(f, &s->read_state);
437
        qemu_get_8s(f, &s->write_state);
438
        qemu_get_8s(f, &s->write_latch);
439
        qemu_get_8s(f, &s->rw_mode);
440
        qemu_get_8s(f, &s->mode);
441
        qemu_get_8s(f, &s->bcd);
442
        qemu_get_8s(f, &s->gate);
443
        s->count_load_time=qemu_get_be64(f);
444
        if (s->irq_timer) {
445
            s->next_transition_time=qemu_get_be64(f);
446
            qemu_get_timer(f, s->irq_timer);
447
        }
448
    }
449
    return 0;
450
}
451

    
452
static const VMStateDescription vmstate_pit = {
453
    .name = "i8254",
454
    .version_id = 2,
455
    .minimum_version_id = 2,
456
    .minimum_version_id_old = 1,
457
    .load_state_old = pit_load_old,
458
    .fields      = (VMStateField []) {
459
        VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState),
460
        VMSTATE_TIMER(channels[0].irq_timer, PITState),
461
        VMSTATE_END_OF_LIST()
462
    }
463
};
464

    
465
static void pit_reset(void *opaque)
466
{
467
    PITState *pit = opaque;
468
    PITChannelState *s;
469
    int i;
470

    
471
    for(i = 0;i < 3; i++) {
472
        s = &pit->channels[i];
473
        s->mode = 3;
474
        s->gate = (i != 2);
475
        pit_load_count(s, 0);
476
    }
477
}
478

    
479
/* When HPET is operating in legacy mode, i8254 timer0 is disabled */
480
void hpet_pit_disable(void) {
481
    PITChannelState *s;
482
    s = &pit_state.channels[0];
483
    if (s->irq_timer)
484
        qemu_del_timer(s->irq_timer);
485
}
486

    
487
/* When HPET is reset or leaving legacy mode, it must reenable i8254
488
 * timer 0
489
 */
490

    
491
void hpet_pit_enable(void)
492
{
493
    PITState *pit = &pit_state;
494
    PITChannelState *s;
495
    s = &pit->channels[0];
496
    s->mode = 3;
497
    s->gate = 1;
498
    pit_load_count(s, 0);
499
}
500

    
501
PITState *pit_init(int base, qemu_irq irq)
502
{
503
    PITState *pit = &pit_state;
504
    PITChannelState *s;
505

    
506
    s = &pit->channels[0];
507
    /* the timer 0 is connected to an IRQ */
508
    s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
509
    s->irq = irq;
510

    
511
    vmstate_register(base, &vmstate_pit, pit);
512
    qemu_register_reset(pit_reset, pit);
513
    register_ioport_write(base, 4, 1, pit_ioport_write, pit);
514
    register_ioport_read(base, 3, 1, pit_ioport_read, pit);
515

    
516
    pit_reset(pit);
517

    
518
    return pit;
519
}