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,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;