Revision ce1f4520

b/hw/acpi.c
50 50
    uint8_t smb_data[32];
51 51
    uint8_t smb_index;
52 52
    qemu_irq irq;
53
    int64_t pmtmr;
53 54
} PIIX4PMState;
54 55

  
55 56
#define RTC_EN (1 << 10)
56 57
#define PWRBTN_EN (1 << 8)
57 58
#define GBL_EN (1 << 5)
58 59
#define TMROF_EN (1 << 0)
60
#define TIMER_OVERFLOW_CNT (1 << 23)
61
#define TIMER_MASK 0xffffffLL
59 62

  
60 63
#define SCI_EN (1 << 0)
61 64

  
......
74 77

  
75 78
PIIX4PMState *pm_state;
76 79

  
80
static void update_pmtmr(PIIX4PMState *s)
81
{
82
    int64_t pmtmr;
83

  
84
    pmtmr = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec)
85
            & TIMER_MASK;
86

  
87
    if (!(s->pmsts & TMROF_EN)) {
88
        if ((pmtmr ^ s->pmtmr) & TIMER_OVERFLOW_CNT) {
89
            s->pmsts |= TMROF_EN;
90
            if (s->pmen & TMROF_EN)
91
                qemu_set_irq(s->irq, 1);
92
        } else {
93
            /* Calculate when the timer will neet to set
94
             * the overflow bit again */
95
            uint64_t delta = TIMER_OVERFLOW_CNT -
96
                    (pmtmr & (TIMER_OVERFLOW_CNT - 1));
97

  
98
            delta = muldiv64(delta, ticks_per_sec, PM_FREQ);
99
            qemu_mod_timer(s->tmr_timer, qemu_get_clock(vm_clock) + delta);
100
        }
101
    }
102

  
103
    s->pmtmr = pmtmr;
104
}
105

  
77 106
static uint32_t get_pmtmr(PIIX4PMState *s)
78 107
{
79
    uint32_t d;
80
    d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
81
    return d & 0xffffff;
108
    update_pmtmr(s);
109
    return s->pmtmr & TIMER_MASK;
82 110
}
83 111

  
112

  
84 113
static int get_pmsts(PIIX4PMState *s)
85 114
{
86
    int64_t d;
87
    int pmsts;
88
    pmsts = s->pmsts;
89
    d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
90
    if (d >= s->tmr_overflow_time)
91
        s->pmsts |= TMROF_EN;
92
    return pmsts;
115
    /* Just increase the accurancy by double computing the timer value */
116
    update_pmtmr(s);
117

  
118
    return s->pmsts;
93 119
}
94 120

  
95 121
static void pm_update_sci(PIIX4PMState *s)
96 122
{
97
    int sci_level, pmsts;
98
    int64_t expire_time;
99

  
100
    pmsts = get_pmsts(s);
101
    sci_level = (((pmsts & s->pmen) &
102
                  (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0);
103
    qemu_set_irq(s->irq, sci_level);
104
    /* schedule a timer interruption if needed */
105
    if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) {
106
        expire_time = muldiv64(s->tmr_overflow_time, ticks_per_sec, PM_FREQ);
107
        qemu_mod_timer(s->tmr_timer, expire_time);
108
        s->tmr_overflow_time += 0x800000;
109
    } else {
110
        qemu_del_timer(s->tmr_timer);
111
    }
123
    int sci_level;
124

  
125
    sci_level = (((s->pmsts & s->pmen) & 
126
                   (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0);
127
    if (!sci_level)
128
        qemu_set_irq(s->irq, sci_level);
112 129
}
113 130

  
114 131
static void pm_tmr_timer(void *opaque)
115 132
{
116 133
    PIIX4PMState *s = opaque;
117
    pm_update_sci(s);
134
    update_pmtmr(s);
118 135
}
119 136

  
120 137
static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
......
123 140
    addr &= 0x3f;
124 141
    switch(addr) {
125 142
    case 0x00:
126
        {
127
            int64_t d;
128
            int pmsts;
129
            pmsts = get_pmsts(s);
130
            if (pmsts & val & TMROF_EN) {
131
                /* if TMRSTS is reset, then compute the new overflow time */
132
                d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
133
                s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
134
            }
135
            s->pmsts &= ~val;
136
            pm_update_sci(s);
137
        }
143
        s->pmsts &= ~val;
144
        update_pmtmr(s);
145
        pm_update_sci(s);
138 146
        break;
139 147
    case 0x02:
140 148
        s->pmen = val;

Also available in: Unified diff