i8254: Factor out pit_get_channel_info
[qemu/kevin.git] / hw / i8254.c
blobf30396af88877a4f93a85acf31488389812cb3f7
1 /*
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
22 * THE SOFTWARE.
24 #include "hw.h"
25 #include "pc.h"
26 #include "isa.h"
27 #include "qemu-timer.h"
28 #include "i8254.h"
30 //#define DEBUG_PIT
32 #define RW_STATE_LSB 1
33 #define RW_STATE_MSB 2
34 #define RW_STATE_WORD0 3
35 #define RW_STATE_WORD1 4
37 typedef struct PITChannelState {
38 int count; /* can be 65536 */
39 uint16_t latched_count;
40 uint8_t count_latched;
41 uint8_t status_latched;
42 uint8_t status;
43 uint8_t read_state;
44 uint8_t write_state;
45 uint8_t write_latch;
46 uint8_t rw_mode;
47 uint8_t mode;
48 uint8_t bcd; /* not supported */
49 uint8_t gate; /* timer start */
50 int64_t count_load_time;
51 /* irq handling */
52 int64_t next_transition_time;
53 QEMUTimer *irq_timer;
54 qemu_irq irq;
55 uint32_t irq_disabled;
56 } PITChannelState;
58 typedef struct PITState {
59 ISADevice dev;
60 MemoryRegion ioports;
61 uint32_t iobase;
62 PITChannelState channels[3];
63 } PITState;
65 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
67 static int pit_get_count(PITChannelState *s)
69 uint64_t d;
70 int counter;
72 d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ,
73 get_ticks_per_sec());
74 switch(s->mode) {
75 case 0:
76 case 1:
77 case 4:
78 case 5:
79 counter = (s->count - d) & 0xffff;
80 break;
81 case 3:
82 /* XXX: may be incorrect for odd counts */
83 counter = s->count - ((2 * d) % s->count);
84 break;
85 default:
86 counter = s->count - (d % s->count);
87 break;
89 return counter;
92 /* get pit output bit */
93 static int pit_get_out(PITChannelState *s, int64_t current_time)
95 uint64_t d;
96 int out;
98 d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
99 get_ticks_per_sec());
100 switch(s->mode) {
101 default:
102 case 0:
103 out = (d >= s->count);
104 break;
105 case 1:
106 out = (d < s->count);
107 break;
108 case 2:
109 if ((d % s->count) == 0 && d != 0)
110 out = 1;
111 else
112 out = 0;
113 break;
114 case 3:
115 out = (d % s->count) < ((s->count + 1) >> 1);
116 break;
117 case 4:
118 case 5:
119 out = (d == s->count);
120 break;
122 return out;
125 /* return -1 if no transition will occur. */
126 static int64_t pit_get_next_transition_time(PITChannelState *s,
127 int64_t current_time)
129 uint64_t d, next_time, base;
130 int period2;
132 d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
133 get_ticks_per_sec());
134 switch(s->mode) {
135 default:
136 case 0:
137 case 1:
138 if (d < s->count)
139 next_time = s->count;
140 else
141 return -1;
142 break;
143 case 2:
144 base = (d / s->count) * s->count;
145 if ((d - base) == 0 && d != 0)
146 next_time = base + s->count;
147 else
148 next_time = base + s->count + 1;
149 break;
150 case 3:
151 base = (d / s->count) * s->count;
152 period2 = ((s->count + 1) >> 1);
153 if ((d - base) < period2)
154 next_time = base + period2;
155 else
156 next_time = base + s->count;
157 break;
158 case 4:
159 case 5:
160 if (d < s->count)
161 next_time = s->count;
162 else if (d == s->count)
163 next_time = s->count + 1;
164 else
165 return -1;
166 break;
168 /* convert to timer units */
169 next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
170 PIT_FREQ);
171 /* fix potential rounding problems */
172 /* XXX: better solution: use a clock at PIT_FREQ Hz */
173 if (next_time <= current_time)
174 next_time = current_time + 1;
175 return next_time;
178 /* val must be 0 or 1 */
179 void pit_set_gate(ISADevice *dev, int channel, int val)
181 PITState *pit = DO_UPCAST(PITState, dev, dev);
182 PITChannelState *s = &pit->channels[channel];
184 switch(s->mode) {
185 default:
186 case 0:
187 case 4:
188 /* XXX: just disable/enable counting */
189 break;
190 case 1:
191 case 5:
192 if (s->gate < val) {
193 /* restart counting on rising edge */
194 s->count_load_time = qemu_get_clock_ns(vm_clock);
195 pit_irq_timer_update(s, s->count_load_time);
197 break;
198 case 2:
199 case 3:
200 if (s->gate < val) {
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);
205 /* XXX: disable/enable counting */
206 break;
208 s->gate = val;
211 void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info)
213 PITState *pit = DO_UPCAST(PITState, dev, dev);
214 PITChannelState *s = &pit->channels[channel];
216 info->gate = s->gate;
217 info->mode = s->mode;
218 info->initial_count = s->count;
219 info->out = pit_get_out(s, qemu_get_clock_ns(vm_clock));
222 static inline void pit_load_count(PITChannelState *s, int val)
224 if (val == 0)
225 val = 0x10000;
226 s->count_load_time = qemu_get_clock_ns(vm_clock);
227 s->count = val;
228 pit_irq_timer_update(s, s->count_load_time);
231 /* if already latched, do not latch again */
232 static void pit_latch_count(PITChannelState *s)
234 if (!s->count_latched) {
235 s->latched_count = pit_get_count(s);
236 s->count_latched = s->rw_mode;
240 static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
242 PITState *pit = opaque;
243 int channel, access;
244 PITChannelState *s;
246 addr &= 3;
247 if (addr == 3) {
248 channel = val >> 6;
249 if (channel == 3) {
250 /* read back command */
251 for(channel = 0; channel < 3; channel++) {
252 s = &pit->channels[channel];
253 if (val & (2 << channel)) {
254 if (!(val & 0x20)) {
255 pit_latch_count(s);
257 if (!(val & 0x10) && !s->status_latched) {
258 /* status latch */
259 /* XXX: add BCD and null count */
260 s->status =
261 (pit_get_out(s,
262 qemu_get_clock_ns(vm_clock)) << 7) |
263 (s->rw_mode << 4) |
264 (s->mode << 1) |
265 s->bcd;
266 s->status_latched = 1;
270 } else {
271 s = &pit->channels[channel];
272 access = (val >> 4) & 3;
273 if (access == 0) {
274 pit_latch_count(s);
275 } else {
276 s->rw_mode = access;
277 s->read_state = access;
278 s->write_state = access;
280 s->mode = (val >> 1) & 7;
281 s->bcd = val & 1;
282 /* XXX: update irq timer ? */
285 } else {
286 s = &pit->channels[addr];
287 switch(s->write_state) {
288 default:
289 case RW_STATE_LSB:
290 pit_load_count(s, val);
291 break;
292 case RW_STATE_MSB:
293 pit_load_count(s, val << 8);
294 break;
295 case RW_STATE_WORD0:
296 s->write_latch = val;
297 s->write_state = RW_STATE_WORD1;
298 break;
299 case RW_STATE_WORD1:
300 pit_load_count(s, s->write_latch | (val << 8));
301 s->write_state = RW_STATE_WORD0;
302 break;
307 static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
309 PITState *pit = opaque;
310 int ret, count;
311 PITChannelState *s;
313 addr &= 3;
314 s = &pit->channels[addr];
315 if (s->status_latched) {
316 s->status_latched = 0;
317 ret = s->status;
318 } else if (s->count_latched) {
319 switch(s->count_latched) {
320 default:
321 case RW_STATE_LSB:
322 ret = s->latched_count & 0xff;
323 s->count_latched = 0;
324 break;
325 case RW_STATE_MSB:
326 ret = s->latched_count >> 8;
327 s->count_latched = 0;
328 break;
329 case RW_STATE_WORD0:
330 ret = s->latched_count & 0xff;
331 s->count_latched = RW_STATE_MSB;
332 break;
334 } else {
335 switch(s->read_state) {
336 default:
337 case RW_STATE_LSB:
338 count = pit_get_count(s);
339 ret = count & 0xff;
340 break;
341 case RW_STATE_MSB:
342 count = pit_get_count(s);
343 ret = (count >> 8) & 0xff;
344 break;
345 case RW_STATE_WORD0:
346 count = pit_get_count(s);
347 ret = count & 0xff;
348 s->read_state = RW_STATE_WORD1;
349 break;
350 case RW_STATE_WORD1:
351 count = pit_get_count(s);
352 ret = (count >> 8) & 0xff;
353 s->read_state = RW_STATE_WORD0;
354 break;
357 return ret;
360 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
362 int64_t expire_time;
363 int irq_level;
365 if (!s->irq_timer || s->irq_disabled) {
366 return;
368 expire_time = pit_get_next_transition_time(s, current_time);
369 irq_level = pit_get_out(s, current_time);
370 qemu_set_irq(s->irq, irq_level);
371 #ifdef DEBUG_PIT
372 printf("irq_level=%d next_delay=%f\n",
373 irq_level,
374 (double)(expire_time - current_time) / get_ticks_per_sec());
375 #endif
376 s->next_transition_time = expire_time;
377 if (expire_time != -1)
378 qemu_mod_timer(s->irq_timer, expire_time);
379 else
380 qemu_del_timer(s->irq_timer);
383 static void pit_irq_timer(void *opaque)
385 PITChannelState *s = opaque;
387 pit_irq_timer_update(s, s->next_transition_time);
390 static const VMStateDescription vmstate_pit_channel = {
391 .name = "pit channel",
392 .version_id = 2,
393 .minimum_version_id = 2,
394 .minimum_version_id_old = 2,
395 .fields = (VMStateField []) {
396 VMSTATE_INT32(count, PITChannelState),
397 VMSTATE_UINT16(latched_count, PITChannelState),
398 VMSTATE_UINT8(count_latched, PITChannelState),
399 VMSTATE_UINT8(status_latched, PITChannelState),
400 VMSTATE_UINT8(status, PITChannelState),
401 VMSTATE_UINT8(read_state, PITChannelState),
402 VMSTATE_UINT8(write_state, PITChannelState),
403 VMSTATE_UINT8(write_latch, PITChannelState),
404 VMSTATE_UINT8(rw_mode, PITChannelState),
405 VMSTATE_UINT8(mode, PITChannelState),
406 VMSTATE_UINT8(bcd, PITChannelState),
407 VMSTATE_UINT8(gate, PITChannelState),
408 VMSTATE_INT64(count_load_time, PITChannelState),
409 VMSTATE_INT64(next_transition_time, PITChannelState),
410 VMSTATE_END_OF_LIST()
414 static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
416 PITState *pit = opaque;
417 PITChannelState *s;
418 int i;
420 if (version_id != 1)
421 return -EINVAL;
423 for(i = 0; i < 3; i++) {
424 s = &pit->channels[i];
425 s->count=qemu_get_be32(f);
426 qemu_get_be16s(f, &s->latched_count);
427 qemu_get_8s(f, &s->count_latched);
428 qemu_get_8s(f, &s->status_latched);
429 qemu_get_8s(f, &s->status);
430 qemu_get_8s(f, &s->read_state);
431 qemu_get_8s(f, &s->write_state);
432 qemu_get_8s(f, &s->write_latch);
433 qemu_get_8s(f, &s->rw_mode);
434 qemu_get_8s(f, &s->mode);
435 qemu_get_8s(f, &s->bcd);
436 qemu_get_8s(f, &s->gate);
437 s->count_load_time=qemu_get_be64(f);
438 s->irq_disabled = 0;
439 if (s->irq_timer) {
440 s->next_transition_time=qemu_get_be64(f);
441 qemu_get_timer(f, s->irq_timer);
444 return 0;
447 static const VMStateDescription vmstate_pit = {
448 .name = "i8254",
449 .version_id = 3,
450 .minimum_version_id = 2,
451 .minimum_version_id_old = 1,
452 .load_state_old = pit_load_old,
453 .fields = (VMStateField []) {
454 VMSTATE_UINT32_V(channels[0].irq_disabled, PITState, 3),
455 VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState),
456 VMSTATE_TIMER(channels[0].irq_timer, PITState),
457 VMSTATE_END_OF_LIST()
461 static void pit_reset(DeviceState *dev)
463 PITState *pit = container_of(dev, PITState, dev.qdev);
464 PITChannelState *s;
465 int i;
467 for(i = 0;i < 3; i++) {
468 s = &pit->channels[i];
469 s->mode = 3;
470 s->gate = (i != 2);
471 s->count_load_time = qemu_get_clock_ns(vm_clock);
472 s->count = 0x10000;
473 if (i == 0 && !s->irq_disabled) {
474 s->next_transition_time =
475 pit_get_next_transition_time(s, s->count_load_time);
476 qemu_mod_timer(s->irq_timer, s->next_transition_time);
481 /* When HPET is operating in legacy mode, suppress the ignored timer IRQ,
482 * reenable it when legacy mode is left again. */
483 static void pit_irq_control(void *opaque, int n, int enable)
485 PITState *pit = opaque;
486 PITChannelState *s = &pit->channels[0];
488 if (enable) {
489 s->irq_disabled = 0;
490 pit_irq_timer_update(s, qemu_get_clock_ns(vm_clock));
491 } else {
492 s->irq_disabled = 1;
493 qemu_del_timer(s->irq_timer);
497 static const MemoryRegionPortio pit_portio[] = {
498 { 0, 4, 1, .write = pit_ioport_write },
499 { 0, 3, 1, .read = pit_ioport_read },
500 PORTIO_END_OF_LIST()
503 static const MemoryRegionOps pit_ioport_ops = {
504 .old_portio = pit_portio
507 static int pit_initfn(ISADevice *dev)
509 PITState *pit = DO_UPCAST(PITState, dev, dev);
510 PITChannelState *s;
512 s = &pit->channels[0];
513 /* the timer 0 is connected to an IRQ */
514 s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
515 qdev_init_gpio_out(&dev->qdev, &s->irq, 1);
517 memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
518 isa_register_ioport(dev, &pit->ioports, pit->iobase);
520 qdev_init_gpio_in(&dev->qdev, pit_irq_control, 1);
522 qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
524 return 0;
527 static Property pit_properties[] = {
528 DEFINE_PROP_HEX32("iobase", PITState, iobase, -1),
529 DEFINE_PROP_END_OF_LIST(),
532 static void pit_class_initfn(ObjectClass *klass, void *data)
534 DeviceClass *dc = DEVICE_CLASS(klass);
535 ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
536 ic->init = pit_initfn;
537 dc->no_user = 1;
538 dc->reset = pit_reset;
539 dc->vmsd = &vmstate_pit;
540 dc->props = pit_properties;
543 static TypeInfo pit_info = {
544 .name = "isa-pit",
545 .parent = TYPE_ISA_DEVICE,
546 .instance_size = sizeof(PITState),
547 .class_init = pit_class_initfn,
550 static void pit_register_types(void)
552 type_register_static(&pit_info);
555 type_init(pit_register_types)