Commit ce1f4520ffcc7b835098da466db8755fc7bbd04d

Authored by balrog
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 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,47 +77,61 @@ typedef struct PIIX4PMState {
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,18 +140,9 @@ 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;
... ...