Revision ce1f4520 hw/acpi.c
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