Commit ce1f4520ffcc7b835098da466db8755fc7bbd04d
1 parent
b8005914
Fix sci irq set when acpi timer about to wrap (Dor Laor, Yaniv Kamay).
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4258 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
46 additions
and
38 deletions
hw/acpi.c
| @@ -50,12 +50,15 @@ typedef struct PIIX4PMState { | @@ -50,12 +50,15 @@ typedef struct PIIX4PMState { | ||
| 50 | uint8_t smb_data[32]; | 50 | uint8_t smb_data[32]; |
| 51 | uint8_t smb_index; | 51 | uint8_t smb_index; |
| 52 | qemu_irq irq; | 52 | qemu_irq irq; |
| 53 | + int64_t pmtmr; | ||
| 53 | } PIIX4PMState; | 54 | } PIIX4PMState; |
| 54 | 55 | ||
| 55 | #define RTC_EN (1 << 10) | 56 | #define RTC_EN (1 << 10) |
| 56 | #define PWRBTN_EN (1 << 8) | 57 | #define PWRBTN_EN (1 << 8) |
| 57 | #define GBL_EN (1 << 5) | 58 | #define GBL_EN (1 << 5) |
| 58 | #define TMROF_EN (1 << 0) | 59 | #define TMROF_EN (1 << 0) |
| 60 | +#define TIMER_OVERFLOW_CNT (1 << 23) | ||
| 61 | +#define TIMER_MASK 0xffffffLL | ||
| 59 | 62 | ||
| 60 | #define SCI_EN (1 << 0) | 63 | #define SCI_EN (1 << 0) |
| 61 | 64 | ||
| @@ -74,47 +77,61 @@ typedef struct PIIX4PMState { | @@ -74,47 +77,61 @@ typedef struct PIIX4PMState { | ||
| 74 | 77 | ||
| 75 | PIIX4PMState *pm_state; | 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 | static uint32_t get_pmtmr(PIIX4PMState *s) | 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 | static int get_pmsts(PIIX4PMState *s) | 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 | static void pm_update_sci(PIIX4PMState *s) | 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 | static void pm_tmr_timer(void *opaque) | 131 | static void pm_tmr_timer(void *opaque) |
| 115 | { | 132 | { |
| 116 | PIIX4PMState *s = opaque; | 133 | PIIX4PMState *s = opaque; |
| 117 | - pm_update_sci(s); | 134 | + update_pmtmr(s); |
| 118 | } | 135 | } |
| 119 | 136 | ||
| 120 | static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) | 137 | static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) |
| @@ -123,18 +140,9 @@ static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) | @@ -123,18 +140,9 @@ static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) | ||
| 123 | addr &= 0x3f; | 140 | addr &= 0x3f; |
| 124 | switch(addr) { | 141 | switch(addr) { |
| 125 | case 0x00: | 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 | break; | 146 | break; |
| 139 | case 0x02: | 147 | case 0x02: |
| 140 | s->pmen = val; | 148 | s->pmen = val; |