]> git.proxmox.com Git - mirror_qemu.git/blame - hw/i8254.c
i8254: Factor out interface header
[mirror_qemu.git] / hw / i8254.c
CommitLineData
80cabfad
FB
1/*
2 * QEMU 8253/8254 interval timer emulation
5fafdf24 3 *
80cabfad 4 * Copyright (c) 2003-2004 Fabrice Bellard
5fafdf24 5 *
80cabfad
FB
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 */
87ecb68b
PB
24#include "hw.h"
25#include "pc.h"
26#include "isa.h"
27#include "qemu-timer.h"
b1277b03 28#include "i8254.h"
80cabfad 29
b0a21b53
FB
30//#define DEBUG_PIT
31
ec844b96
FB
32#define RW_STATE_LSB 1
33#define RW_STATE_MSB 2
34#define RW_STATE_WORD0 3
35#define RW_STATE_WORD1 4
80cabfad 36
ec844b96
FB
37typedef 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;
d537cf6c 54 qemu_irq irq;
ec844b96
FB
55} PITChannelState;
56
64d7e9a4
BS
57typedef struct PITState {
58 ISADevice dev;
60ea6aa8 59 MemoryRegion ioports;
64d7e9a4
BS
60 uint32_t irq;
61 uint32_t iobase;
ec844b96 62 PITChannelState channels[3];
64d7e9a4 63} PITState;
ec844b96
FB
64
65static PITState pit_state;
80cabfad 66
b0a21b53
FB
67static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
68
80cabfad
FB
69static int pit_get_count(PITChannelState *s)
70{
71 uint64_t d;
72 int counter;
73
74475455 74 d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ,
6ee093c9 75 get_ticks_per_sec());
80cabfad
FB
76 switch(s->mode) {
77 case 0:
78 case 1:
79 case 4:
80 case 5:
81 counter = (s->count - d) & 0xffff;
82 break;
83 case 3:
84 /* XXX: may be incorrect for odd counts */
85 counter = s->count - ((2 * d) % s->count);
86 break;
87 default:
88 counter = s->count - (d % s->count);
89 break;
90 }
91 return counter;
92}
93
94/* get pit output bit */
ec844b96 95static int pit_get_out1(PITChannelState *s, int64_t current_time)
80cabfad
FB
96{
97 uint64_t d;
98 int out;
99
6ee093c9
JQ
100 d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
101 get_ticks_per_sec());
80cabfad
FB
102 switch(s->mode) {
103 default:
104 case 0:
105 out = (d >= s->count);
106 break;
107 case 1:
108 out = (d < s->count);
109 break;
110 case 2:
111 if ((d % s->count) == 0 && d != 0)
112 out = 1;
113 else
114 out = 0;
115 break;
116 case 3:
117 out = (d % s->count) < ((s->count + 1) >> 1);
118 break;
119 case 4:
120 case 5:
121 out = (d == s->count);
122 break;
123 }
124 return out;
125}
126
64d7e9a4 127int pit_get_out(ISADevice *dev, int channel, int64_t current_time)
ec844b96 128{
64d7e9a4 129 PITState *pit = DO_UPCAST(PITState, dev, dev);
ec844b96
FB
130 PITChannelState *s = &pit->channels[channel];
131 return pit_get_out1(s, current_time);
132}
133
b0a21b53 134/* return -1 if no transition will occur. */
5fafdf24 135static int64_t pit_get_next_transition_time(PITChannelState *s,
b0a21b53 136 int64_t current_time)
80cabfad 137{
b0a21b53
FB
138 uint64_t d, next_time, base;
139 int period2;
80cabfad 140
6ee093c9
JQ
141 d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
142 get_ticks_per_sec());
80cabfad
FB
143 switch(s->mode) {
144 default:
145 case 0:
80cabfad 146 case 1:
b0a21b53
FB
147 if (d < s->count)
148 next_time = s->count;
149 else
150 return -1;
80cabfad
FB
151 break;
152 case 2:
b0a21b53
FB
153 base = (d / s->count) * s->count;
154 if ((d - base) == 0 && d != 0)
155 next_time = base + s->count;
156 else
157 next_time = base + s->count + 1;
80cabfad
FB
158 break;
159 case 3:
b0a21b53
FB
160 base = (d / s->count) * s->count;
161 period2 = ((s->count + 1) >> 1);
5fafdf24 162 if ((d - base) < period2)
b0a21b53
FB
163 next_time = base + period2;
164 else
165 next_time = base + s->count;
80cabfad
FB
166 break;
167 case 4:
168 case 5:
b0a21b53
FB
169 if (d < s->count)
170 next_time = s->count;
171 else if (d == s->count)
172 next_time = s->count + 1;
80cabfad 173 else
b0a21b53 174 return -1;
80cabfad
FB
175 break;
176 }
b0a21b53 177 /* convert to timer units */
6ee093c9
JQ
178 next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
179 PIT_FREQ);
1154e441
FB
180 /* fix potential rounding problems */
181 /* XXX: better solution: use a clock at PIT_FREQ Hz */
182 if (next_time <= current_time)
183 next_time = current_time + 1;
b0a21b53 184 return next_time;
80cabfad
FB
185}
186
187/* val must be 0 or 1 */
64d7e9a4 188void pit_set_gate(ISADevice *dev, int channel, int val)
80cabfad 189{
64d7e9a4 190 PITState *pit = DO_UPCAST(PITState, dev, dev);
ec844b96
FB
191 PITChannelState *s = &pit->channels[channel];
192
80cabfad
FB
193 switch(s->mode) {
194 default:
195 case 0:
196 case 4:
197 /* XXX: just disable/enable counting */
198 break;
199 case 1:
200 case 5:
201 if (s->gate < val) {
202 /* restart counting on rising edge */
74475455 203 s->count_load_time = qemu_get_clock_ns(vm_clock);
b0a21b53 204 pit_irq_timer_update(s, s->count_load_time);
80cabfad
FB
205 }
206 break;
207 case 2:
208 case 3:
209 if (s->gate < val) {
210 /* restart counting on rising edge */
74475455 211 s->count_load_time = qemu_get_clock_ns(vm_clock);
b0a21b53 212 pit_irq_timer_update(s, s->count_load_time);
80cabfad
FB
213 }
214 /* XXX: disable/enable counting */
215 break;
216 }
217 s->gate = val;
218}
219
64d7e9a4 220int pit_get_gate(ISADevice *dev, int channel)
ec844b96 221{
64d7e9a4 222 PITState *pit = DO_UPCAST(PITState, dev, dev);
ec844b96
FB
223 PITChannelState *s = &pit->channels[channel];
224 return s->gate;
225}
226
64d7e9a4 227int pit_get_initial_count(ISADevice *dev, int channel)
fd06c375 228{
64d7e9a4 229 PITState *pit = DO_UPCAST(PITState, dev, dev);
fd06c375
FB
230 PITChannelState *s = &pit->channels[channel];
231 return s->count;
232}
233
64d7e9a4 234int pit_get_mode(ISADevice *dev, int channel)
fd06c375 235{
64d7e9a4 236 PITState *pit = DO_UPCAST(PITState, dev, dev);
fd06c375
FB
237 PITChannelState *s = &pit->channels[channel];
238 return s->mode;
239}
240
80cabfad
FB
241static inline void pit_load_count(PITChannelState *s, int val)
242{
243 if (val == 0)
244 val = 0x10000;
74475455 245 s->count_load_time = qemu_get_clock_ns(vm_clock);
80cabfad 246 s->count = val;
b0a21b53 247 pit_irq_timer_update(s, s->count_load_time);
80cabfad
FB
248}
249
ec844b96
FB
250/* if already latched, do not latch again */
251static void pit_latch_count(PITChannelState *s)
252{
253 if (!s->count_latched) {
254 s->latched_count = pit_get_count(s);
255 s->count_latched = s->rw_mode;
256 }
257}
258
b41a2cd1 259static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
80cabfad 260{
ec844b96 261 PITState *pit = opaque;
80cabfad
FB
262 int channel, access;
263 PITChannelState *s;
264
265 addr &= 3;
266 if (addr == 3) {
267 channel = val >> 6;
ec844b96
FB
268 if (channel == 3) {
269 /* read back command */
270 for(channel = 0; channel < 3; channel++) {
271 s = &pit->channels[channel];
272 if (val & (2 << channel)) {
273 if (!(val & 0x20)) {
274 pit_latch_count(s);
275 }
276 if (!(val & 0x10) && !s->status_latched) {
277 /* status latch */
278 /* XXX: add BCD and null count */
74475455 279 s->status = (pit_get_out1(s, qemu_get_clock_ns(vm_clock)) << 7) |
ec844b96
FB
280 (s->rw_mode << 4) |
281 (s->mode << 1) |
282 s->bcd;
283 s->status_latched = 1;
284 }
285 }
286 }
287 } else {
288 s = &pit->channels[channel];
289 access = (val >> 4) & 3;
290 if (access == 0) {
291 pit_latch_count(s);
292 } else {
293 s->rw_mode = access;
294 s->read_state = access;
295 s->write_state = access;
296
297 s->mode = (val >> 1) & 7;
298 s->bcd = val & 1;
299 /* XXX: update irq timer ? */
300 }
80cabfad
FB
301 }
302 } else {
ec844b96
FB
303 s = &pit->channels[addr];
304 switch(s->write_state) {
305 default:
80cabfad
FB
306 case RW_STATE_LSB:
307 pit_load_count(s, val);
308 break;
309 case RW_STATE_MSB:
310 pit_load_count(s, val << 8);
311 break;
312 case RW_STATE_WORD0:
ec844b96
FB
313 s->write_latch = val;
314 s->write_state = RW_STATE_WORD1;
315 break;
80cabfad 316 case RW_STATE_WORD1:
ec844b96
FB
317 pit_load_count(s, s->write_latch | (val << 8));
318 s->write_state = RW_STATE_WORD0;
80cabfad
FB
319 break;
320 }
321 }
322}
323
b41a2cd1 324static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
80cabfad 325{
ec844b96 326 PITState *pit = opaque;
80cabfad
FB
327 int ret, count;
328 PITChannelState *s;
3b46e624 329
80cabfad 330 addr &= 3;
ec844b96
FB
331 s = &pit->channels[addr];
332 if (s->status_latched) {
333 s->status_latched = 0;
334 ret = s->status;
335 } else if (s->count_latched) {
336 switch(s->count_latched) {
337 default:
338 case RW_STATE_LSB:
339 ret = s->latched_count & 0xff;
340 s->count_latched = 0;
341 break;
342 case RW_STATE_MSB:
80cabfad 343 ret = s->latched_count >> 8;
ec844b96
FB
344 s->count_latched = 0;
345 break;
346 case RW_STATE_WORD0:
80cabfad 347 ret = s->latched_count & 0xff;
ec844b96
FB
348 s->count_latched = RW_STATE_MSB;
349 break;
350 }
351 } else {
352 switch(s->read_state) {
353 default:
354 case RW_STATE_LSB:
355 count = pit_get_count(s);
356 ret = count & 0xff;
357 break;
358 case RW_STATE_MSB:
359 count = pit_get_count(s);
360 ret = (count >> 8) & 0xff;
361 break;
362 case RW_STATE_WORD0:
363 count = pit_get_count(s);
364 ret = count & 0xff;
365 s->read_state = RW_STATE_WORD1;
366 break;
367 case RW_STATE_WORD1:
368 count = pit_get_count(s);
369 ret = (count >> 8) & 0xff;
370 s->read_state = RW_STATE_WORD0;
371 break;
372 }
80cabfad
FB
373 }
374 return ret;
375}
376
b0a21b53
FB
377static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
378{
379 int64_t expire_time;
380 int irq_level;
381
382 if (!s->irq_timer)
383 return;
384 expire_time = pit_get_next_transition_time(s, current_time);
ec844b96 385 irq_level = pit_get_out1(s, current_time);
d537cf6c 386 qemu_set_irq(s->irq, irq_level);
b0a21b53
FB
387#ifdef DEBUG_PIT
388 printf("irq_level=%d next_delay=%f\n",
5fafdf24 389 irq_level,
6ee093c9 390 (double)(expire_time - current_time) / get_ticks_per_sec());
b0a21b53
FB
391#endif
392 s->next_transition_time = expire_time;
393 if (expire_time != -1)
394 qemu_mod_timer(s->irq_timer, expire_time);
395 else
396 qemu_del_timer(s->irq_timer);
397}
398
399static void pit_irq_timer(void *opaque)
400{
401 PITChannelState *s = opaque;
402
403 pit_irq_timer_update(s, s->next_transition_time);
404}
405
5122b431
JQ
406static const VMStateDescription vmstate_pit_channel = {
407 .name = "pit channel",
408 .version_id = 2,
409 .minimum_version_id = 2,
410 .minimum_version_id_old = 2,
411 .fields = (VMStateField []) {
412 VMSTATE_INT32(count, PITChannelState),
413 VMSTATE_UINT16(latched_count, PITChannelState),
414 VMSTATE_UINT8(count_latched, PITChannelState),
415 VMSTATE_UINT8(status_latched, PITChannelState),
416 VMSTATE_UINT8(status, PITChannelState),
417 VMSTATE_UINT8(read_state, PITChannelState),
418 VMSTATE_UINT8(write_state, PITChannelState),
419 VMSTATE_UINT8(write_latch, PITChannelState),
420 VMSTATE_UINT8(rw_mode, PITChannelState),
421 VMSTATE_UINT8(mode, PITChannelState),
422 VMSTATE_UINT8(bcd, PITChannelState),
423 VMSTATE_UINT8(gate, PITChannelState),
424 VMSTATE_INT64(count_load_time, PITChannelState),
425 VMSTATE_INT64(next_transition_time, PITChannelState),
426 VMSTATE_END_OF_LIST()
b0a21b53 427 }
5122b431 428};
b0a21b53 429
5122b431 430static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
b0a21b53 431{
ec844b96 432 PITState *pit = opaque;
b0a21b53
FB
433 PITChannelState *s;
434 int i;
3b46e624 435
b0a21b53
FB
436 if (version_id != 1)
437 return -EINVAL;
438
439 for(i = 0; i < 3; i++) {
ec844b96 440 s = &pit->channels[i];
bee8d684 441 s->count=qemu_get_be32(f);
b0a21b53 442 qemu_get_be16s(f, &s->latched_count);
ec844b96
FB
443 qemu_get_8s(f, &s->count_latched);
444 qemu_get_8s(f, &s->status_latched);
445 qemu_get_8s(f, &s->status);
446 qemu_get_8s(f, &s->read_state);
447 qemu_get_8s(f, &s->write_state);
448 qemu_get_8s(f, &s->write_latch);
449 qemu_get_8s(f, &s->rw_mode);
b0a21b53
FB
450 qemu_get_8s(f, &s->mode);
451 qemu_get_8s(f, &s->bcd);
452 qemu_get_8s(f, &s->gate);
bee8d684 453 s->count_load_time=qemu_get_be64(f);
b0a21b53 454 if (s->irq_timer) {
bee8d684 455 s->next_transition_time=qemu_get_be64(f);
b0a21b53
FB
456 qemu_get_timer(f, s->irq_timer);
457 }
458 }
459 return 0;
460}
461
5122b431
JQ
462static const VMStateDescription vmstate_pit = {
463 .name = "i8254",
464 .version_id = 2,
465 .minimum_version_id = 2,
466 .minimum_version_id_old = 1,
467 .load_state_old = pit_load_old,
468 .fields = (VMStateField []) {
469 VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState),
470 VMSTATE_TIMER(channels[0].irq_timer, PITState),
471 VMSTATE_END_OF_LIST()
472 }
473};
474
64d7e9a4 475static void pit_reset(DeviceState *dev)
80cabfad 476{
64d7e9a4 477 PITState *pit = container_of(dev, PITState, dev.qdev);
80cabfad
FB
478 PITChannelState *s;
479 int i;
480
481 for(i = 0;i < 3; i++) {
ec844b96 482 s = &pit->channels[i];
80cabfad
FB
483 s->mode = 3;
484 s->gate = (i != 2);
61b7b67d
JK
485 s->count_load_time = qemu_get_clock_ns(vm_clock);
486 s->count = 0x10000;
487 if (i == 0) {
488 s->next_transition_time =
489 pit_get_next_transition_time(s, s->count_load_time);
490 qemu_mod_timer(s->irq_timer, s->next_transition_time);
491 }
80cabfad 492 }
d7d02e3c
FB
493}
494
16b29ae1
AL
495/* When HPET is operating in legacy mode, i8254 timer0 is disabled */
496void hpet_pit_disable(void) {
497 PITChannelState *s;
498 s = &pit_state.channels[0];
e0dd114c
AL
499 if (s->irq_timer)
500 qemu_del_timer(s->irq_timer);
16b29ae1
AL
501}
502
c50c2d68 503/* When HPET is reset or leaving legacy mode, it must reenable i8254
16b29ae1
AL
504 * timer 0
505 */
506
507void hpet_pit_enable(void)
508{
509 PITState *pit = &pit_state;
510 PITChannelState *s;
511 s = &pit->channels[0];
512 s->mode = 3;
513 s->gate = 1;
514 pit_load_count(s, 0);
515}
516
60ea6aa8
RH
517static const MemoryRegionPortio pit_portio[] = {
518 { 0, 4, 1, .write = pit_ioport_write },
519 { 0, 3, 1, .read = pit_ioport_read },
520 PORTIO_END_OF_LIST()
521};
522
523static const MemoryRegionOps pit_ioport_ops = {
524 .old_portio = pit_portio
525};
526
64d7e9a4 527static int pit_initfn(ISADevice *dev)
d7d02e3c 528{
64d7e9a4 529 PITState *pit = DO_UPCAST(PITState, dev, dev);
d7d02e3c
FB
530 PITChannelState *s;
531
532 s = &pit->channels[0];
533 /* the timer 0 is connected to an IRQ */
74475455 534 s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
48a18b3c 535 s->irq = isa_get_irq(dev, pit->irq);
80cabfad 536
60ea6aa8
RH
537 memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
538 isa_register_ioport(dev, &pit->ioports, pit->iobase);
d7d02e3c 539
ca22a3a3
JK
540 qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
541
64d7e9a4
BS
542 return 0;
543}
544
39bffca2
AL
545static Property pit_properties[] = {
546 DEFINE_PROP_UINT32("irq", PITState, irq, -1),
547 DEFINE_PROP_HEX32("iobase", PITState, iobase, -1),
548 DEFINE_PROP_END_OF_LIST(),
549};
550
8f04ee08
AL
551static void pit_class_initfn(ObjectClass *klass, void *data)
552{
39bffca2 553 DeviceClass *dc = DEVICE_CLASS(klass);
8f04ee08
AL
554 ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
555 ic->init = pit_initfn;
39bffca2
AL
556 dc->no_user = 1;
557 dc->reset = pit_reset;
558 dc->vmsd = &vmstate_pit;
559 dc->props = pit_properties;
8f04ee08
AL
560}
561
39bffca2
AL
562static TypeInfo pit_info = {
563 .name = "isa-pit",
564 .parent = TYPE_ISA_DEVICE,
565 .instance_size = sizeof(PITState),
566 .class_init = pit_class_initfn,
64d7e9a4
BS
567};
568
83f7d43a 569static void pit_register_types(void)
64d7e9a4 570{
39bffca2 571 type_register_static(&pit_info);
80cabfad 572}
83f7d43a
AF
573
574type_init(pit_register_types)