]> git.proxmox.com Git - qemu.git/blame - hw/i8254.c
Remove screendump dummy functions.
[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;
ce967e2f 55 uint32_t irq_disabled;
ec844b96
FB
56} PITChannelState;
57
64d7e9a4
BS
58typedef struct PITState {
59 ISADevice dev;
60ea6aa8 60 MemoryRegion ioports;
64d7e9a4 61 uint32_t iobase;
ec844b96 62 PITChannelState channels[3];
64d7e9a4 63} PITState;
ec844b96 64
b0a21b53
FB
65static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
66
80cabfad
FB
67static int pit_get_count(PITChannelState *s)
68{
69 uint64_t d;
70 int counter;
71
74475455 72 d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ,
6ee093c9 73 get_ticks_per_sec());
80cabfad
FB
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;
88 }
89 return counter;
90}
91
92/* get pit output bit */
4aa5d285 93static int pit_get_out(PITChannelState *s, int64_t current_time)
80cabfad
FB
94{
95 uint64_t d;
96 int out;
97
6ee093c9
JQ
98 d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
99 get_ticks_per_sec());
80cabfad
FB
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;
121 }
122 return out;
123}
124
b0a21b53 125/* return -1 if no transition will occur. */
5fafdf24 126static int64_t pit_get_next_transition_time(PITChannelState *s,
b0a21b53 127 int64_t current_time)
80cabfad 128{
b0a21b53
FB
129 uint64_t d, next_time, base;
130 int period2;
80cabfad 131
6ee093c9
JQ
132 d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
133 get_ticks_per_sec());
80cabfad
FB
134 switch(s->mode) {
135 default:
136 case 0:
80cabfad 137 case 1:
b0a21b53
FB
138 if (d < s->count)
139 next_time = s->count;
140 else
141 return -1;
80cabfad
FB
142 break;
143 case 2:
b0a21b53
FB
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;
80cabfad
FB
149 break;
150 case 3:
b0a21b53
FB
151 base = (d / s->count) * s->count;
152 period2 = ((s->count + 1) >> 1);
5fafdf24 153 if ((d - base) < period2)
b0a21b53
FB
154 next_time = base + period2;
155 else
156 next_time = base + s->count;
80cabfad
FB
157 break;
158 case 4:
159 case 5:
b0a21b53
FB
160 if (d < s->count)
161 next_time = s->count;
162 else if (d == s->count)
163 next_time = s->count + 1;
80cabfad 164 else
b0a21b53 165 return -1;
80cabfad
FB
166 break;
167 }
b0a21b53 168 /* convert to timer units */
6ee093c9
JQ
169 next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
170 PIT_FREQ);
1154e441
FB
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;
b0a21b53 175 return next_time;
80cabfad
FB
176}
177
178/* val must be 0 or 1 */
64d7e9a4 179void pit_set_gate(ISADevice *dev, int channel, int val)
80cabfad 180{
64d7e9a4 181 PITState *pit = DO_UPCAST(PITState, dev, dev);
ec844b96
FB
182 PITChannelState *s = &pit->channels[channel];
183
80cabfad
FB
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 */
74475455 194 s->count_load_time = qemu_get_clock_ns(vm_clock);
b0a21b53 195 pit_irq_timer_update(s, s->count_load_time);
80cabfad
FB
196 }
197 break;
198 case 2:
199 case 3:
200 if (s->gate < val) {
201 /* restart counting on rising edge */
74475455 202 s->count_load_time = qemu_get_clock_ns(vm_clock);
b0a21b53 203 pit_irq_timer_update(s, s->count_load_time);
80cabfad
FB
204 }
205 /* XXX: disable/enable counting */
206 break;
207 }
208 s->gate = val;
209}
210
4aa5d285 211void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info)
fd06c375 212{
64d7e9a4 213 PITState *pit = DO_UPCAST(PITState, dev, dev);
fd06c375 214 PITChannelState *s = &pit->channels[channel];
fd06c375 215
4aa5d285
JK
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));
fd06c375
FB
220}
221
80cabfad
FB
222static inline void pit_load_count(PITChannelState *s, int val)
223{
224 if (val == 0)
225 val = 0x10000;
74475455 226 s->count_load_time = qemu_get_clock_ns(vm_clock);
80cabfad 227 s->count = val;
b0a21b53 228 pit_irq_timer_update(s, s->count_load_time);
80cabfad
FB
229}
230
ec844b96
FB
231/* if already latched, do not latch again */
232static void pit_latch_count(PITChannelState *s)
233{
234 if (!s->count_latched) {
235 s->latched_count = pit_get_count(s);
236 s->count_latched = s->rw_mode;
237 }
238}
239
b41a2cd1 240static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
80cabfad 241{
ec844b96 242 PITState *pit = opaque;
80cabfad
FB
243 int channel, access;
244 PITChannelState *s;
245
246 addr &= 3;
247 if (addr == 3) {
248 channel = val >> 6;
ec844b96
FB
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);
256 }
257 if (!(val & 0x10) && !s->status_latched) {
258 /* status latch */
259 /* XXX: add BCD and null count */
4aa5d285
JK
260 s->status =
261 (pit_get_out(s,
262 qemu_get_clock_ns(vm_clock)) << 7) |
ec844b96
FB
263 (s->rw_mode << 4) |
264 (s->mode << 1) |
265 s->bcd;
266 s->status_latched = 1;
267 }
268 }
269 }
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;
279
280 s->mode = (val >> 1) & 7;
281 s->bcd = val & 1;
282 /* XXX: update irq timer ? */
283 }
80cabfad
FB
284 }
285 } else {
ec844b96
FB
286 s = &pit->channels[addr];
287 switch(s->write_state) {
288 default:
80cabfad
FB
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:
ec844b96
FB
296 s->write_latch = val;
297 s->write_state = RW_STATE_WORD1;
298 break;
80cabfad 299 case RW_STATE_WORD1:
ec844b96
FB
300 pit_load_count(s, s->write_latch | (val << 8));
301 s->write_state = RW_STATE_WORD0;
80cabfad
FB
302 break;
303 }
304 }
305}
306
b41a2cd1 307static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
80cabfad 308{
ec844b96 309 PITState *pit = opaque;
80cabfad
FB
310 int ret, count;
311 PITChannelState *s;
3b46e624 312
80cabfad 313 addr &= 3;
ec844b96
FB
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:
80cabfad 326 ret = s->latched_count >> 8;
ec844b96
FB
327 s->count_latched = 0;
328 break;
329 case RW_STATE_WORD0:
80cabfad 330 ret = s->latched_count & 0xff;
ec844b96
FB
331 s->count_latched = RW_STATE_MSB;
332 break;
333 }
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;
355 }
80cabfad
FB
356 }
357 return ret;
358}
359
b0a21b53
FB
360static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
361{
362 int64_t expire_time;
363 int irq_level;
364
ce967e2f 365 if (!s->irq_timer || s->irq_disabled) {
b0a21b53 366 return;
ce967e2f 367 }
b0a21b53 368 expire_time = pit_get_next_transition_time(s, current_time);
4aa5d285 369 irq_level = pit_get_out(s, current_time);
d537cf6c 370 qemu_set_irq(s->irq, irq_level);
b0a21b53
FB
371#ifdef DEBUG_PIT
372 printf("irq_level=%d next_delay=%f\n",
5fafdf24 373 irq_level,
6ee093c9 374 (double)(expire_time - current_time) / get_ticks_per_sec());
b0a21b53
FB
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);
381}
382
383static void pit_irq_timer(void *opaque)
384{
385 PITChannelState *s = opaque;
386
387 pit_irq_timer_update(s, s->next_transition_time);
388}
389
5122b431
JQ
390static 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()
b0a21b53 411 }
5122b431 412};
b0a21b53 413
5122b431 414static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
b0a21b53 415{
ec844b96 416 PITState *pit = opaque;
b0a21b53
FB
417 PITChannelState *s;
418 int i;
3b46e624 419
b0a21b53
FB
420 if (version_id != 1)
421 return -EINVAL;
422
423 for(i = 0; i < 3; i++) {
ec844b96 424 s = &pit->channels[i];
bee8d684 425 s->count=qemu_get_be32(f);
b0a21b53 426 qemu_get_be16s(f, &s->latched_count);
ec844b96
FB
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);
b0a21b53
FB
434 qemu_get_8s(f, &s->mode);
435 qemu_get_8s(f, &s->bcd);
436 qemu_get_8s(f, &s->gate);
bee8d684 437 s->count_load_time=qemu_get_be64(f);
ce967e2f 438 s->irq_disabled = 0;
b0a21b53 439 if (s->irq_timer) {
bee8d684 440 s->next_transition_time=qemu_get_be64(f);
b0a21b53
FB
441 qemu_get_timer(f, s->irq_timer);
442 }
443 }
444 return 0;
445}
446
5122b431
JQ
447static const VMStateDescription vmstate_pit = {
448 .name = "i8254",
ce967e2f 449 .version_id = 3,
5122b431
JQ
450 .minimum_version_id = 2,
451 .minimum_version_id_old = 1,
452 .load_state_old = pit_load_old,
453 .fields = (VMStateField []) {
ce967e2f 454 VMSTATE_UINT32_V(channels[0].irq_disabled, PITState, 3),
5122b431
JQ
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()
458 }
459};
460
64d7e9a4 461static void pit_reset(DeviceState *dev)
80cabfad 462{
64d7e9a4 463 PITState *pit = container_of(dev, PITState, dev.qdev);
80cabfad
FB
464 PITChannelState *s;
465 int i;
466
467 for(i = 0;i < 3; i++) {
ec844b96 468 s = &pit->channels[i];
80cabfad
FB
469 s->mode = 3;
470 s->gate = (i != 2);
61b7b67d
JK
471 s->count_load_time = qemu_get_clock_ns(vm_clock);
472 s->count = 0x10000;
ce967e2f 473 if (i == 0 && !s->irq_disabled) {
61b7b67d
JK
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);
477 }
80cabfad 478 }
d7d02e3c
FB
479}
480
ce967e2f
JK
481/* When HPET is operating in legacy mode, suppress the ignored timer IRQ,
482 * reenable it when legacy mode is left again. */
483static void pit_irq_control(void *opaque, int n, int enable)
16b29ae1 484{
ce967e2f
JK
485 PITState *pit = opaque;
486 PITChannelState *s = &pit->channels[0];
487
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);
494 }
16b29ae1
AL
495}
496
60ea6aa8
RH
497static 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()
501};
502
503static const MemoryRegionOps pit_ioport_ops = {
504 .old_portio = pit_portio
505};
506
64d7e9a4 507static int pit_initfn(ISADevice *dev)
d7d02e3c 508{
64d7e9a4 509 PITState *pit = DO_UPCAST(PITState, dev, dev);
d7d02e3c
FB
510 PITChannelState *s;
511
512 s = &pit->channels[0];
513 /* the timer 0 is connected to an IRQ */
74475455 514 s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
319ba9f5 515 qdev_init_gpio_out(&dev->qdev, &s->irq, 1);
80cabfad 516
60ea6aa8
RH
517 memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
518 isa_register_ioport(dev, &pit->ioports, pit->iobase);
d7d02e3c 519
ce967e2f
JK
520 qdev_init_gpio_in(&dev->qdev, pit_irq_control, 1);
521
ca22a3a3
JK
522 qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
523
64d7e9a4
BS
524 return 0;
525}
526
39bffca2 527static Property pit_properties[] = {
39bffca2
AL
528 DEFINE_PROP_HEX32("iobase", PITState, iobase, -1),
529 DEFINE_PROP_END_OF_LIST(),
530};
531
8f04ee08
AL
532static void pit_class_initfn(ObjectClass *klass, void *data)
533{
39bffca2 534 DeviceClass *dc = DEVICE_CLASS(klass);
8f04ee08
AL
535 ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
536 ic->init = pit_initfn;
39bffca2
AL
537 dc->no_user = 1;
538 dc->reset = pit_reset;
539 dc->vmsd = &vmstate_pit;
540 dc->props = pit_properties;
8f04ee08
AL
541}
542
39bffca2
AL
543static TypeInfo pit_info = {
544 .name = "isa-pit",
545 .parent = TYPE_ISA_DEVICE,
546 .instance_size = sizeof(PITState),
547 .class_init = pit_class_initfn,
64d7e9a4
BS
548};
549
83f7d43a 550static void pit_register_types(void)
64d7e9a4 551{
39bffca2 552 type_register_static(&pit_info);
80cabfad 553}
83f7d43a
AF
554
555type_init(pit_register_types)