mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-09 10:34:58 -06:00
use new timer API
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@689 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
dff38e7b40
commit
b0a21b5334
6 changed files with 331 additions and 81 deletions
172
hw/i8254.c
172
hw/i8254.c
|
@ -43,6 +43,8 @@
|
|||
#include "cpu.h"
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_PIT
|
||||
|
||||
#define RW_STATE_LSB 0
|
||||
#define RW_STATE_MSB 1
|
||||
#define RW_STATE_WORD0 2
|
||||
|
@ -52,12 +54,14 @@
|
|||
|
||||
PITChannelState pit_channels[3];
|
||||
|
||||
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
|
||||
|
||||
static int pit_get_count(PITChannelState *s)
|
||||
{
|
||||
uint64_t d;
|
||||
int counter;
|
||||
|
||||
d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
switch(s->mode) {
|
||||
case 0:
|
||||
case 1:
|
||||
|
@ -77,12 +81,12 @@ static int pit_get_count(PITChannelState *s)
|
|||
}
|
||||
|
||||
/* get pit output bit */
|
||||
int pit_get_out(PITChannelState *s)
|
||||
int pit_get_out(PITChannelState *s, int64_t current_time)
|
||||
{
|
||||
uint64_t d;
|
||||
int out;
|
||||
|
||||
d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
|
@ -108,53 +112,51 @@ int pit_get_out(PITChannelState *s)
|
|||
return out;
|
||||
}
|
||||
|
||||
/* get the number of 0 to 1 transitions we had since we call this
|
||||
function */
|
||||
/* XXX: maybe better to use ticks precision to avoid getting edges
|
||||
twice if checks are done at very small intervals */
|
||||
int pit_get_out_edges(PITChannelState *s)
|
||||
/* return -1 if no transition will occur. */
|
||||
static int64_t pit_get_next_transition_time(PITChannelState *s,
|
||||
int64_t current_time)
|
||||
{
|
||||
uint64_t d1, d2;
|
||||
int64_t ticks;
|
||||
int ret, v;
|
||||
uint64_t d, next_time, base;
|
||||
int period2;
|
||||
|
||||
ticks = cpu_get_ticks();
|
||||
d1 = muldiv64(s->count_last_edge_check_time - s->count_load_time,
|
||||
PIT_FREQ, ticks_per_sec);
|
||||
d2 = muldiv64(ticks - s->count_load_time,
|
||||
PIT_FREQ, ticks_per_sec);
|
||||
s->count_last_edge_check_time = ticks;
|
||||
d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
if (d1 < s->count && d2 >= s->count)
|
||||
ret = 1;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
case 1:
|
||||
ret = 0;
|
||||
if (d < s->count)
|
||||
next_time = s->count;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 2:
|
||||
d1 /= s->count;
|
||||
d2 /= s->count;
|
||||
ret = d2 - d1;
|
||||
base = (d / s->count) * s->count;
|
||||
if ((d - base) == 0 && d != 0)
|
||||
next_time = base + s->count;
|
||||
else
|
||||
next_time = base + s->count + 1;
|
||||
break;
|
||||
case 3:
|
||||
v = s->count - ((s->count + 1) >> 1);
|
||||
d1 = (d1 + v) / s->count;
|
||||
d2 = (d2 + v) / s->count;
|
||||
ret = d2 - d1;
|
||||
base = (d / s->count) * s->count;
|
||||
period2 = ((s->count + 1) >> 1);
|
||||
if ((d - base) < period2)
|
||||
next_time = base + period2;
|
||||
else
|
||||
next_time = base + s->count;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if (d1 < s->count && d2 >= s->count)
|
||||
ret = 1;
|
||||
if (d < s->count)
|
||||
next_time = s->count;
|
||||
else if (d == s->count)
|
||||
next_time = s->count + 1;
|
||||
else
|
||||
ret = 0;
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
/* convert to timer units */
|
||||
next_time = s->count_load_time + muldiv64(next_time, ticks_per_sec, PIT_FREQ);
|
||||
return next_time;
|
||||
}
|
||||
|
||||
/* val must be 0 or 1 */
|
||||
|
@ -170,16 +172,16 @@ void pit_set_gate(PITChannelState *s, int val)
|
|||
case 5:
|
||||
if (s->gate < val) {
|
||||
/* restart counting on rising edge */
|
||||
s->count_load_time = cpu_get_ticks();
|
||||
s->count_last_edge_check_time = s->count_load_time;
|
||||
s->count_load_time = qemu_get_clock(vm_clock);
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if (s->gate < val) {
|
||||
/* restart counting on rising edge */
|
||||
s->count_load_time = cpu_get_ticks();
|
||||
s->count_last_edge_check_time = s->count_load_time;
|
||||
s->count_load_time = qemu_get_clock(vm_clock);
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
/* XXX: disable/enable counting */
|
||||
break;
|
||||
|
@ -191,14 +193,9 @@ static inline void pit_load_count(PITChannelState *s, int val)
|
|||
{
|
||||
if (val == 0)
|
||||
val = 0x10000;
|
||||
s->count_load_time = cpu_get_ticks();
|
||||
s->count_last_edge_check_time = s->count_load_time;
|
||||
s->count_load_time = qemu_get_clock(vm_clock);
|
||||
s->count = val;
|
||||
if (s == &pit_channels[0] && val <= pit_min_timer_count) {
|
||||
fprintf(stderr,
|
||||
"\nWARNING: qemu: on your system, accurate timer emulation is impossible if its frequency is more than %d Hz. If using a 2.6 guest Linux kernel, you must patch asm/param.h to change HZ from 1000 to 100.\n\n",
|
||||
PIT_FREQ / pit_min_timer_count);
|
||||
}
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
|
||||
static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
|
@ -222,6 +219,7 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
|||
s->mode = (val >> 1) & 7;
|
||||
s->bcd = val & 1;
|
||||
s->rw_state = access - 1 + RW_STATE_LSB;
|
||||
/* XXX: update irq timer ? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -279,18 +277,100 @@ static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void pit_init(int base)
|
||||
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
|
||||
{
|
||||
int64_t expire_time;
|
||||
int irq_level;
|
||||
|
||||
if (!s->irq_timer)
|
||||
return;
|
||||
expire_time = pit_get_next_transition_time(s, current_time);
|
||||
irq_level = pit_get_out(s, current_time);
|
||||
pic_set_irq(s->irq, irq_level);
|
||||
#ifdef DEBUG_PIT
|
||||
printf("irq_level=%d next_delay=%f\n",
|
||||
irq_level,
|
||||
(double)(expire_time - current_time) / ticks_per_sec);
|
||||
#endif
|
||||
s->next_transition_time = expire_time;
|
||||
if (expire_time != -1)
|
||||
qemu_mod_timer(s->irq_timer, expire_time);
|
||||
else
|
||||
qemu_del_timer(s->irq_timer);
|
||||
}
|
||||
|
||||
static void pit_irq_timer(void *opaque)
|
||||
{
|
||||
PITChannelState *s = opaque;
|
||||
|
||||
pit_irq_timer_update(s, s->next_transition_time);
|
||||
}
|
||||
|
||||
static void pit_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
PITChannelState *s;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
s = &pit_channels[i];
|
||||
qemu_put_be32s(f, &s->count);
|
||||
qemu_put_be16s(f, &s->latched_count);
|
||||
qemu_put_8s(f, &s->rw_state);
|
||||
qemu_put_8s(f, &s->mode);
|
||||
qemu_put_8s(f, &s->bcd);
|
||||
qemu_put_8s(f, &s->gate);
|
||||
qemu_put_be64s(f, &s->count_load_time);
|
||||
if (s->irq_timer) {
|
||||
qemu_put_be64s(f, &s->next_transition_time);
|
||||
qemu_put_timer(f, s->irq_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int pit_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
PITChannelState *s;
|
||||
int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
s = &pit_channels[i];
|
||||
qemu_get_be32s(f, &s->count);
|
||||
qemu_get_be16s(f, &s->latched_count);
|
||||
qemu_get_8s(f, &s->rw_state);
|
||||
qemu_get_8s(f, &s->mode);
|
||||
qemu_get_8s(f, &s->bcd);
|
||||
qemu_get_8s(f, &s->gate);
|
||||
qemu_get_be64s(f, &s->count_load_time);
|
||||
if (s->irq_timer) {
|
||||
qemu_get_be64s(f, &s->next_transition_time);
|
||||
qemu_get_timer(f, s->irq_timer);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pit_init(int base, int irq)
|
||||
{
|
||||
PITChannelState *s;
|
||||
int i;
|
||||
|
||||
for(i = 0;i < 3; i++) {
|
||||
s = &pit_channels[i];
|
||||
if (i == 0) {
|
||||
/* the timer 0 is connected to an IRQ */
|
||||
s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
|
||||
s->irq = irq;
|
||||
}
|
||||
s->mode = 3;
|
||||
s->gate = (i != 2);
|
||||
pit_load_count(s, 0);
|
||||
}
|
||||
|
||||
register_savevm("i8254", base, 1, pit_save, pit_load, NULL);
|
||||
|
||||
register_ioport_write(base, 4, 1, pit_ioport_write, NULL);
|
||||
register_ioport_read(base, 3, 1, pit_ioport_read, NULL);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue