2 * QEMU 8253/8254 interval timer emulation
4 * Copyright (c) 2003-2004 Fabrice Bellard
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
27 #include "qemu-timer.h"
31 #define RW_STATE_LSB 1
32 #define RW_STATE_MSB 2
33 #define RW_STATE_WORD0 3
34 #define RW_STATE_WORD1 4
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;
47 uint8_t bcd; /* not supported */
48 uint8_t gate; /* timer start */
49 int64_t count_load_time;
51 int64_t next_transition_time;
56 typedef struct PITState {
61 PITChannelState channels[3];
64 static PITState pit_state;
66 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
68 static int pit_get_count(PITChannelState *s)
73 d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ,
80 counter = (s->count - d) & 0xffff;
83 /* XXX: may be incorrect for odd counts */
84 counter = s->count - ((2 * d) % s->count);
87 counter = s->count - (d % s->count);
93 /* get pit output bit */
94 static int pit_get_out1(PITChannelState *s, int64_t current_time)
99 d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
100 get_ticks_per_sec());
104 out = (d >= s->count);
107 out = (d < s->count);
110 if ((d % s->count) == 0 && d != 0)
116 out = (d % s->count) < ((s->count + 1) >> 1);
120 out = (d == s->count);
126 int pit_get_out(ISADevice *dev, int channel, int64_t current_time)
128 PITState *pit = DO_UPCAST(PITState, dev, dev);
129 PITChannelState *s = &pit->channels[channel];
130 return pit_get_out1(s, current_time);
133 /* return -1 if no transition will occur. */
134 static int64_t pit_get_next_transition_time(PITChannelState *s,
135 int64_t current_time)
137 uint64_t d, next_time, base;
140 d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
141 get_ticks_per_sec());
147 next_time = s->count;
152 base = (d / s->count) * s->count;
153 if ((d - base) == 0 && d != 0)
154 next_time = base + s->count;
156 next_time = base + s->count + 1;
159 base = (d / s->count) * s->count;
160 period2 = ((s->count + 1) >> 1);
161 if ((d - base) < period2)
162 next_time = base + period2;
164 next_time = base + s->count;
169 next_time = s->count;
170 else if (d == s->count)
171 next_time = s->count + 1;
176 /* convert to timer units */
177 next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
179 /* fix potential rounding problems */
180 /* XXX: better solution: use a clock at PIT_FREQ Hz */
181 if (next_time <= current_time)
182 next_time = current_time + 1;
186 /* val must be 0 or 1 */
187 void pit_set_gate(ISADevice *dev, int channel, int val)
189 PITState *pit = DO_UPCAST(PITState, dev, dev);
190 PITChannelState *s = &pit->channels[channel];
196 /* XXX: just disable/enable counting */
201 /* restart counting on rising edge */
202 s->count_load_time = qemu_get_clock_ns(vm_clock);
203 pit_irq_timer_update(s, s->count_load_time);
209 /* restart counting on rising edge */
210 s->count_load_time = qemu_get_clock_ns(vm_clock);
211 pit_irq_timer_update(s, s->count_load_time);
213 /* XXX: disable/enable counting */
219 int pit_get_gate(ISADevice *dev, int channel)
221 PITState *pit = DO_UPCAST(PITState, dev, dev);
222 PITChannelState *s = &pit->channels[channel];
226 int pit_get_initial_count(ISADevice *dev, int channel)
228 PITState *pit = DO_UPCAST(PITState, dev, dev);
229 PITChannelState *s = &pit->channels[channel];
233 int pit_get_mode(ISADevice *dev, int channel)
235 PITState *pit = DO_UPCAST(PITState, dev, dev);
236 PITChannelState *s = &pit->channels[channel];
240 static inline void pit_load_count(PITChannelState *s, int val)
244 s->count_load_time = qemu_get_clock_ns(vm_clock);
246 pit_irq_timer_update(s, s->count_load_time);
249 /* if already latched, do not latch again */
250 static void pit_latch_count(PITChannelState *s)
252 if (!s->count_latched) {
253 s->latched_count = pit_get_count(s);
254 s->count_latched = s->rw_mode;
258 static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
260 PITState *pit = opaque;
268 /* read back command */
269 for(channel = 0; channel < 3; channel++) {
270 s = &pit->channels[channel];
271 if (val & (2 << channel)) {
275 if (!(val & 0x10) && !s->status_latched) {
277 /* XXX: add BCD and null count */
278 s->status = (pit_get_out1(s, qemu_get_clock_ns(vm_clock)) << 7) |
282 s->status_latched = 1;
287 s = &pit->channels[channel];
288 access = (val >> 4) & 3;
293 s->read_state = access;
294 s->write_state = access;
296 s->mode = (val >> 1) & 7;
298 /* XXX: update irq timer ? */
302 s = &pit->channels[addr];
303 switch(s->write_state) {
306 pit_load_count(s, val);
309 pit_load_count(s, val << 8);
312 s->write_latch = val;
313 s->write_state = RW_STATE_WORD1;
316 pit_load_count(s, s->write_latch | (val << 8));
317 s->write_state = RW_STATE_WORD0;
323 static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
325 PITState *pit = opaque;
330 s = &pit->channels[addr];
331 if (s->status_latched) {
332 s->status_latched = 0;
334 } else if (s->count_latched) {
335 switch(s->count_latched) {
338 ret = s->latched_count & 0xff;
339 s->count_latched = 0;
342 ret = s->latched_count >> 8;
343 s->count_latched = 0;
346 ret = s->latched_count & 0xff;
347 s->count_latched = RW_STATE_MSB;
351 switch(s->read_state) {
354 count = pit_get_count(s);
358 count = pit_get_count(s);
359 ret = (count >> 8) & 0xff;
362 count = pit_get_count(s);
364 s->read_state = RW_STATE_WORD1;
367 count = pit_get_count(s);
368 ret = (count >> 8) & 0xff;
369 s->read_state = RW_STATE_WORD0;
376 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
383 expire_time = pit_get_next_transition_time(s, current_time);
384 irq_level = pit_get_out1(s, current_time);
385 qemu_set_irq(s->irq, irq_level);
387 printf("irq_level=%d next_delay=%f\n",
389 (double)(expire_time - current_time) / get_ticks_per_sec());
391 s->next_transition_time = expire_time;
392 if (expire_time != -1)
393 qemu_mod_timer(s->irq_timer, expire_time);
395 qemu_del_timer(s->irq_timer);
398 static void pit_irq_timer(void *opaque)
400 PITChannelState *s = opaque;
402 pit_irq_timer_update(s, s->next_transition_time);
405 static const VMStateDescription vmstate_pit_channel = {
406 .name = "pit channel",
408 .minimum_version_id = 2,
409 .minimum_version_id_old = 2,
410 .fields = (VMStateField []) {
411 VMSTATE_INT32(count, PITChannelState),
412 VMSTATE_UINT16(latched_count, PITChannelState),
413 VMSTATE_UINT8(count_latched, PITChannelState),
414 VMSTATE_UINT8(status_latched, PITChannelState),
415 VMSTATE_UINT8(status, PITChannelState),
416 VMSTATE_UINT8(read_state, PITChannelState),
417 VMSTATE_UINT8(write_state, PITChannelState),
418 VMSTATE_UINT8(write_latch, PITChannelState),
419 VMSTATE_UINT8(rw_mode, PITChannelState),
420 VMSTATE_UINT8(mode, PITChannelState),
421 VMSTATE_UINT8(bcd, PITChannelState),
422 VMSTATE_UINT8(gate, PITChannelState),
423 VMSTATE_INT64(count_load_time, PITChannelState),
424 VMSTATE_INT64(next_transition_time, PITChannelState),
425 VMSTATE_END_OF_LIST()
429 static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
431 PITState *pit = opaque;
438 for(i = 0; i < 3; i++) {
439 s = &pit->channels[i];
440 s->count=qemu_get_be32(f);
441 qemu_get_be16s(f, &s->latched_count);
442 qemu_get_8s(f, &s->count_latched);
443 qemu_get_8s(f, &s->status_latched);
444 qemu_get_8s(f, &s->status);
445 qemu_get_8s(f, &s->read_state);
446 qemu_get_8s(f, &s->write_state);
447 qemu_get_8s(f, &s->write_latch);
448 qemu_get_8s(f, &s->rw_mode);
449 qemu_get_8s(f, &s->mode);
450 qemu_get_8s(f, &s->bcd);
451 qemu_get_8s(f, &s->gate);
452 s->count_load_time=qemu_get_be64(f);
454 s->next_transition_time=qemu_get_be64(f);
455 qemu_get_timer(f, s->irq_timer);
461 static const VMStateDescription vmstate_pit = {
464 .minimum_version_id = 2,
465 .minimum_version_id_old = 1,
466 .load_state_old = pit_load_old,
467 .fields = (VMStateField []) {
468 VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState),
469 VMSTATE_TIMER(channels[0].irq_timer, PITState),
470 VMSTATE_END_OF_LIST()
474 static void pit_reset(DeviceState *dev)
476 PITState *pit = container_of(dev, PITState, dev.qdev);
480 for(i = 0;i < 3; i++) {
481 s = &pit->channels[i];
484 pit_load_count(s, 0);
488 /* When HPET is operating in legacy mode, i8254 timer0 is disabled */
489 void hpet_pit_disable(void) {
491 s = &pit_state.channels[0];
493 qemu_del_timer(s->irq_timer);
496 /* When HPET is reset or leaving legacy mode, it must reenable i8254
500 void hpet_pit_enable(void)
502 PITState *pit = &pit_state;
504 s = &pit->channels[0];
507 pit_load_count(s, 0);
510 static const MemoryRegionPortio pit_portio[] = {
511 { 0, 4, 1, .write = pit_ioport_write },
512 { 0, 3, 1, .read = pit_ioport_read },
516 static const MemoryRegionOps pit_ioport_ops = {
517 .old_portio = pit_portio
520 static int pit_initfn(ISADevice *dev)
522 PITState *pit = DO_UPCAST(PITState, dev, dev);
525 s = &pit->channels[0];
526 /* the timer 0 is connected to an IRQ */
527 s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
528 s->irq = isa_get_irq(pit->irq);
530 memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
531 isa_register_ioport(dev, &pit->ioports, pit->iobase);
533 qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
538 static ISADeviceInfo pit_info = {
539 .qdev.name = "isa-pit",
540 .qdev.size = sizeof(PITState),
541 .qdev.vmsd = &vmstate_pit,
542 .qdev.reset = pit_reset,
545 .qdev.props = (Property[]) {
546 DEFINE_PROP_UINT32("irq", PITState, irq, -1),
547 DEFINE_PROP_HEX32("iobase", PITState, iobase, -1),
548 DEFINE_PROP_END_OF_LIST(),
552 static void pit_register(void)
554 isa_qdev_register(&pit_info);
556 device_init(pit_register)