|
1 /* |
|
2 * High Precisition Event Timer emulation |
|
3 * |
|
4 * Copyright (c) 2007 Alexander Graf |
|
5 * Copyright (c) 2008 IBM Corporation |
|
6 * |
|
7 * Authors: Beth Kon <bkon@us.ibm.com> |
|
8 * |
|
9 * This library is free software; you can redistribute it and/or |
|
10 * modify it under the terms of the GNU Lesser General Public |
|
11 * License as published by the Free Software Foundation; either |
|
12 * version 2 of the License, or (at your option) any later version. |
|
13 * |
|
14 * This library is distributed in the hope that it will be useful, |
|
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
17 * Lesser General Public License for more details. |
|
18 * |
|
19 * You should have received a copy of the GNU Lesser General Public |
|
20 * License along with this library; if not, write to the Free Software |
|
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
22 * |
|
23 * ***************************************************************** |
|
24 * |
|
25 * This driver attempts to emulate an HPET device in software. |
|
26 */ |
|
27 |
|
28 #include "hw.h" |
|
29 #include "pc.h" |
|
30 #include "console.h" |
|
31 #include "qemu-timer.h" |
|
32 #include "hpet_emul.h" |
|
33 |
|
34 //#define HPET_DEBUG |
|
35 #ifdef HPET_DEBUG |
|
36 #define dprintf printf |
|
37 #else |
|
38 #define dprintf(...) |
|
39 #endif |
|
40 |
|
41 static HPETState *hpet_statep; |
|
42 |
|
43 uint32_t hpet_in_legacy_mode(void) |
|
44 { |
|
45 if (hpet_statep) |
|
46 return hpet_statep->config & HPET_CFG_LEGACY; |
|
47 else |
|
48 return 0; |
|
49 } |
|
50 |
|
51 static uint32_t timer_int_route(struct HPETTimer *timer) |
|
52 { |
|
53 uint32_t route; |
|
54 route = (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT; |
|
55 return route; |
|
56 } |
|
57 |
|
58 static uint32_t hpet_enabled(void) |
|
59 { |
|
60 return hpet_statep->config & HPET_CFG_ENABLE; |
|
61 } |
|
62 |
|
63 static uint32_t timer_is_periodic(HPETTimer *t) |
|
64 { |
|
65 return t->config & HPET_TN_PERIODIC; |
|
66 } |
|
67 |
|
68 static uint32_t timer_enabled(HPETTimer *t) |
|
69 { |
|
70 return t->config & HPET_TN_ENABLE; |
|
71 } |
|
72 |
|
73 static uint32_t hpet_time_after(uint64_t a, uint64_t b) |
|
74 { |
|
75 return ((int32_t)(b) - (int32_t)(a) < 0); |
|
76 } |
|
77 |
|
78 static uint32_t hpet_time_after64(uint64_t a, uint64_t b) |
|
79 { |
|
80 return ((int64_t)(b) - (int64_t)(a) < 0); |
|
81 } |
|
82 |
|
83 static uint64_t ticks_to_ns(uint64_t value) |
|
84 { |
|
85 return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS)); |
|
86 } |
|
87 |
|
88 static uint64_t ns_to_ticks(uint64_t value) |
|
89 { |
|
90 return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD)); |
|
91 } |
|
92 |
|
93 static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask) |
|
94 { |
|
95 new &= mask; |
|
96 new |= old & ~mask; |
|
97 return new; |
|
98 } |
|
99 |
|
100 static int activating_bit(uint64_t old, uint64_t new, uint64_t mask) |
|
101 { |
|
102 return (!(old & mask) && (new & mask)); |
|
103 } |
|
104 |
|
105 static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask) |
|
106 { |
|
107 return ((old & mask) && !(new & mask)); |
|
108 } |
|
109 |
|
110 static uint64_t hpet_get_ticks(void) |
|
111 { |
|
112 uint64_t ticks; |
|
113 ticks = ns_to_ticks(qemu_get_clock(vm_clock) + hpet_statep->hpet_offset); |
|
114 return ticks; |
|
115 } |
|
116 |
|
117 /* |
|
118 * calculate diff between comparator value and current ticks |
|
119 */ |
|
120 static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current) |
|
121 { |
|
122 |
|
123 if (t->config & HPET_TN_32BIT) { |
|
124 uint32_t diff, cmp; |
|
125 cmp = (uint32_t)t->cmp; |
|
126 diff = cmp - (uint32_t)current; |
|
127 diff = (int32_t)diff > 0 ? diff : (uint32_t)0; |
|
128 return (uint64_t)diff; |
|
129 } else { |
|
130 uint64_t diff, cmp; |
|
131 cmp = t->cmp; |
|
132 diff = cmp - current; |
|
133 diff = (int64_t)diff > 0 ? diff : (uint64_t)0; |
|
134 return diff; |
|
135 } |
|
136 } |
|
137 |
|
138 static void update_irq(struct HPETTimer *timer) |
|
139 { |
|
140 qemu_irq irq; |
|
141 int route; |
|
142 |
|
143 if (timer->tn <= 1 && hpet_in_legacy_mode()) { |
|
144 /* if LegacyReplacementRoute bit is set, HPET specification requires |
|
145 * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, |
|
146 * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. |
|
147 */ |
|
148 if (timer->tn == 0) { |
|
149 irq=timer->state->irqs[0]; |
|
150 } else |
|
151 irq=timer->state->irqs[8]; |
|
152 } else { |
|
153 route=timer_int_route(timer); |
|
154 irq=timer->state->irqs[route]; |
|
155 } |
|
156 if (timer_enabled(timer) && hpet_enabled()) { |
|
157 qemu_irq_pulse(irq); |
|
158 } |
|
159 } |
|
160 |
|
161 static void hpet_save(QEMUFile *f, void *opaque) |
|
162 { |
|
163 HPETState *s = opaque; |
|
164 int i; |
|
165 qemu_put_be64s(f, &s->config); |
|
166 qemu_put_be64s(f, &s->isr); |
|
167 /* save current counter value */ |
|
168 s->hpet_counter = hpet_get_ticks(); |
|
169 qemu_put_be64s(f, &s->hpet_counter); |
|
170 |
|
171 for (i = 0; i < HPET_NUM_TIMERS; i++) { |
|
172 qemu_put_8s(f, &s->timer[i].tn); |
|
173 qemu_put_be64s(f, &s->timer[i].config); |
|
174 qemu_put_be64s(f, &s->timer[i].cmp); |
|
175 qemu_put_be64s(f, &s->timer[i].fsb); |
|
176 qemu_put_be64s(f, &s->timer[i].period); |
|
177 qemu_put_8s(f, &s->timer[i].wrap_flag); |
|
178 if (s->timer[i].qemu_timer) { |
|
179 qemu_put_timer(f, s->timer[i].qemu_timer); |
|
180 } |
|
181 } |
|
182 } |
|
183 |
|
184 static int hpet_load(QEMUFile *f, void *opaque, int version_id) |
|
185 { |
|
186 HPETState *s = opaque; |
|
187 int i; |
|
188 |
|
189 if (version_id != 1) |
|
190 return -EINVAL; |
|
191 |
|
192 qemu_get_be64s(f, &s->config); |
|
193 qemu_get_be64s(f, &s->isr); |
|
194 qemu_get_be64s(f, &s->hpet_counter); |
|
195 /* Recalculate the offset between the main counter and guest time */ |
|
196 s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock); |
|
197 |
|
198 for (i = 0; i < HPET_NUM_TIMERS; i++) { |
|
199 qemu_get_8s(f, &s->timer[i].tn); |
|
200 qemu_get_be64s(f, &s->timer[i].config); |
|
201 qemu_get_be64s(f, &s->timer[i].cmp); |
|
202 qemu_get_be64s(f, &s->timer[i].fsb); |
|
203 qemu_get_be64s(f, &s->timer[i].period); |
|
204 qemu_get_8s(f, &s->timer[i].wrap_flag); |
|
205 if (s->timer[i].qemu_timer) { |
|
206 qemu_get_timer(f, s->timer[i].qemu_timer); |
|
207 } |
|
208 } |
|
209 return 0; |
|
210 } |
|
211 |
|
212 /* |
|
213 * timer expiration callback |
|
214 */ |
|
215 static void hpet_timer(void *opaque) |
|
216 { |
|
217 HPETTimer *t = (HPETTimer*)opaque; |
|
218 uint64_t diff; |
|
219 |
|
220 uint64_t period = t->period; |
|
221 uint64_t cur_tick = hpet_get_ticks(); |
|
222 |
|
223 if (timer_is_periodic(t) && period != 0) { |
|
224 if (t->config & HPET_TN_32BIT) { |
|
225 while (hpet_time_after(cur_tick, t->cmp)) |
|
226 t->cmp = (uint32_t)(t->cmp + t->period); |
|
227 } else |
|
228 while (hpet_time_after64(cur_tick, t->cmp)) |
|
229 t->cmp += period; |
|
230 |
|
231 diff = hpet_calculate_diff(t, cur_tick); |
|
232 qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) |
|
233 + (int64_t)ticks_to_ns(diff)); |
|
234 } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { |
|
235 if (t->wrap_flag) { |
|
236 diff = hpet_calculate_diff(t, cur_tick); |
|
237 qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) |
|
238 + (int64_t)ticks_to_ns(diff)); |
|
239 t->wrap_flag = 0; |
|
240 } |
|
241 } |
|
242 update_irq(t); |
|
243 } |
|
244 |
|
245 static void hpet_set_timer(HPETTimer *t) |
|
246 { |
|
247 uint64_t diff; |
|
248 uint32_t wrap_diff; /* how many ticks until we wrap? */ |
|
249 uint64_t cur_tick = hpet_get_ticks(); |
|
250 |
|
251 /* whenever new timer is being set up, make sure wrap_flag is 0 */ |
|
252 t->wrap_flag = 0; |
|
253 diff = hpet_calculate_diff(t, cur_tick); |
|
254 |
|
255 /* hpet spec says in one-shot 32-bit mode, generate an interrupt when |
|
256 * counter wraps in addition to an interrupt with comparator match. |
|
257 */ |
|
258 if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { |
|
259 wrap_diff = 0xffffffff - (uint32_t)cur_tick; |
|
260 if (wrap_diff < (uint32_t)diff) { |
|
261 diff = wrap_diff; |
|
262 t->wrap_flag = 1; |
|
263 } |
|
264 } |
|
265 qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) |
|
266 + (int64_t)ticks_to_ns(diff)); |
|
267 } |
|
268 |
|
269 static void hpet_del_timer(HPETTimer *t) |
|
270 { |
|
271 qemu_del_timer(t->qemu_timer); |
|
272 } |
|
273 |
|
274 #ifdef HPET_DEBUG |
|
275 static uint32_t hpet_ram_readb(void *opaque, target_phys_addr_t addr) |
|
276 { |
|
277 printf("qemu: hpet_read b at %" PRIx64 "\n", addr); |
|
278 return 0; |
|
279 } |
|
280 |
|
281 static uint32_t hpet_ram_readw(void *opaque, target_phys_addr_t addr) |
|
282 { |
|
283 printf("qemu: hpet_read w at %" PRIx64 "\n", addr); |
|
284 return 0; |
|
285 } |
|
286 #endif |
|
287 |
|
288 static uint32_t hpet_ram_readl(void *opaque, target_phys_addr_t addr) |
|
289 { |
|
290 HPETState *s = (HPETState *)opaque; |
|
291 uint64_t cur_tick, index; |
|
292 |
|
293 dprintf("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr); |
|
294 index = addr; |
|
295 /*address range of all TN regs*/ |
|
296 if (index >= 0x100 && index <= 0x3ff) { |
|
297 uint8_t timer_id = (addr - 0x100) / 0x20; |
|
298 if (timer_id > HPET_NUM_TIMERS - 1) { |
|
299 printf("qemu: timer id out of range\n"); |
|
300 return 0; |
|
301 } |
|
302 HPETTimer *timer = &s->timer[timer_id]; |
|
303 |
|
304 switch ((addr - 0x100) % 0x20) { |
|
305 case HPET_TN_CFG: |
|
306 return timer->config; |
|
307 case HPET_TN_CFG + 4: // Interrupt capabilities |
|
308 return timer->config >> 32; |
|
309 case HPET_TN_CMP: // comparator register |
|
310 return timer->cmp; |
|
311 case HPET_TN_CMP + 4: |
|
312 return timer->cmp >> 32; |
|
313 case HPET_TN_ROUTE: |
|
314 return timer->fsb >> 32; |
|
315 default: |
|
316 dprintf("qemu: invalid hpet_ram_readl\n"); |
|
317 break; |
|
318 } |
|
319 } else { |
|
320 switch (index) { |
|
321 case HPET_ID: |
|
322 return s->capability; |
|
323 case HPET_PERIOD: |
|
324 return s->capability >> 32; |
|
325 case HPET_CFG: |
|
326 return s->config; |
|
327 case HPET_CFG + 4: |
|
328 dprintf("qemu: invalid HPET_CFG + 4 hpet_ram_readl \n"); |
|
329 return 0; |
|
330 case HPET_COUNTER: |
|
331 if (hpet_enabled()) |
|
332 cur_tick = hpet_get_ticks(); |
|
333 else |
|
334 cur_tick = s->hpet_counter; |
|
335 dprintf("qemu: reading counter = %" PRIx64 "\n", cur_tick); |
|
336 return cur_tick; |
|
337 case HPET_COUNTER + 4: |
|
338 if (hpet_enabled()) |
|
339 cur_tick = hpet_get_ticks(); |
|
340 else |
|
341 cur_tick = s->hpet_counter; |
|
342 dprintf("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick); |
|
343 return cur_tick >> 32; |
|
344 case HPET_STATUS: |
|
345 return s->isr; |
|
346 default: |
|
347 dprintf("qemu: invalid hpet_ram_readl\n"); |
|
348 break; |
|
349 } |
|
350 } |
|
351 return 0; |
|
352 } |
|
353 |
|
354 #ifdef HPET_DEBUG |
|
355 static void hpet_ram_writeb(void *opaque, target_phys_addr_t addr, |
|
356 uint32_t value) |
|
357 { |
|
358 printf("qemu: invalid hpet_write b at %" PRIx64 " = %#x\n", |
|
359 addr, value); |
|
360 } |
|
361 |
|
362 static void hpet_ram_writew(void *opaque, target_phys_addr_t addr, |
|
363 uint32_t value) |
|
364 { |
|
365 printf("qemu: invalid hpet_write w at %" PRIx64 " = %#x\n", |
|
366 addr, value); |
|
367 } |
|
368 #endif |
|
369 |
|
370 static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, |
|
371 uint32_t value) |
|
372 { |
|
373 int i; |
|
374 HPETState *s = (HPETState *)opaque; |
|
375 uint64_t old_val, new_val, index; |
|
376 |
|
377 dprintf("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value); |
|
378 index = addr; |
|
379 old_val = hpet_ram_readl(opaque, addr); |
|
380 new_val = value; |
|
381 |
|
382 /*address range of all TN regs*/ |
|
383 if (index >= 0x100 && index <= 0x3ff) { |
|
384 uint8_t timer_id = (addr - 0x100) / 0x20; |
|
385 dprintf("qemu: hpet_ram_writel timer_id = %#x \n", timer_id); |
|
386 HPETTimer *timer = &s->timer[timer_id]; |
|
387 |
|
388 switch ((addr - 0x100) % 0x20) { |
|
389 case HPET_TN_CFG: |
|
390 dprintf("qemu: hpet_ram_writel HPET_TN_CFG\n"); |
|
391 timer->config = hpet_fixup_reg(new_val, old_val, 0x3e4e); |
|
392 if (new_val & HPET_TN_32BIT) { |
|
393 timer->cmp = (uint32_t)timer->cmp; |
|
394 timer->period = (uint32_t)timer->period; |
|
395 } |
|
396 if (new_val & HPET_TIMER_TYPE_LEVEL) { |
|
397 printf("qemu: level-triggered hpet not supported\n"); |
|
398 exit (-1); |
|
399 } |
|
400 |
|
401 break; |
|
402 case HPET_TN_CFG + 4: // Interrupt capabilities |
|
403 dprintf("qemu: invalid HPET_TN_CFG+4 write\n"); |
|
404 break; |
|
405 case HPET_TN_CMP: // comparator register |
|
406 dprintf("qemu: hpet_ram_writel HPET_TN_CMP \n"); |
|
407 if (timer->config & HPET_TN_32BIT) |
|
408 new_val = (uint32_t)new_val; |
|
409 if (!timer_is_periodic(timer) || |
|
410 (timer->config & HPET_TN_SETVAL)) |
|
411 timer->cmp = (timer->cmp & 0xffffffff00000000ULL) |
|
412 | new_val; |
|
413 else { |
|
414 /* |
|
415 * FIXME: Clamp period to reasonable min value? |
|
416 * Clamp period to reasonable max value |
|
417 */ |
|
418 new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; |
|
419 timer->period = (timer->period & 0xffffffff00000000ULL) |
|
420 | new_val; |
|
421 } |
|
422 timer->config &= ~HPET_TN_SETVAL; |
|
423 if (hpet_enabled()) |
|
424 hpet_set_timer(timer); |
|
425 break; |
|
426 case HPET_TN_CMP + 4: // comparator register high order |
|
427 dprintf("qemu: hpet_ram_writel HPET_TN_CMP + 4\n"); |
|
428 if (!timer_is_periodic(timer) || |
|
429 (timer->config & HPET_TN_SETVAL)) |
|
430 timer->cmp = (timer->cmp & 0xffffffffULL) |
|
431 | new_val << 32; |
|
432 else { |
|
433 /* |
|
434 * FIXME: Clamp period to reasonable min value? |
|
435 * Clamp period to reasonable max value |
|
436 */ |
|
437 new_val &= (timer->config |
|
438 & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; |
|
439 timer->period = (timer->period & 0xffffffffULL) |
|
440 | new_val << 32; |
|
441 } |
|
442 timer->config &= ~HPET_TN_SETVAL; |
|
443 if (hpet_enabled()) |
|
444 hpet_set_timer(timer); |
|
445 break; |
|
446 case HPET_TN_ROUTE + 4: |
|
447 dprintf("qemu: hpet_ram_writel HPET_TN_ROUTE + 4\n"); |
|
448 break; |
|
449 default: |
|
450 dprintf("qemu: invalid hpet_ram_writel\n"); |
|
451 break; |
|
452 } |
|
453 return; |
|
454 } else { |
|
455 switch (index) { |
|
456 case HPET_ID: |
|
457 return; |
|
458 case HPET_CFG: |
|
459 s->config = hpet_fixup_reg(new_val, old_val, 0x3); |
|
460 if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { |
|
461 /* Enable main counter and interrupt generation. */ |
|
462 s->hpet_offset = ticks_to_ns(s->hpet_counter) |
|
463 - qemu_get_clock(vm_clock); |
|
464 for (i = 0; i < HPET_NUM_TIMERS; i++) |
|
465 if ((&s->timer[i])->cmp != ~0ULL) |
|
466 hpet_set_timer(&s->timer[i]); |
|
467 } |
|
468 else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { |
|
469 /* Halt main counter and disable interrupt generation. */ |
|
470 s->hpet_counter = hpet_get_ticks(); |
|
471 for (i = 0; i < HPET_NUM_TIMERS; i++) |
|
472 hpet_del_timer(&s->timer[i]); |
|
473 } |
|
474 /* i8254 and RTC are disabled when HPET is in legacy mode */ |
|
475 if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { |
|
476 hpet_pit_disable(); |
|
477 } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { |
|
478 hpet_pit_enable(); |
|
479 } |
|
480 break; |
|
481 case HPET_CFG + 4: |
|
482 dprintf("qemu: invalid HPET_CFG+4 write \n"); |
|
483 break; |
|
484 case HPET_STATUS: |
|
485 /* FIXME: need to handle level-triggered interrupts */ |
|
486 break; |
|
487 case HPET_COUNTER: |
|
488 if (hpet_enabled()) |
|
489 printf("qemu: Writing counter while HPET enabled!\n"); |
|
490 s->hpet_counter = (s->hpet_counter & 0xffffffff00000000ULL) |
|
491 | value; |
|
492 dprintf("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n", |
|
493 value, s->hpet_counter); |
|
494 break; |
|
495 case HPET_COUNTER + 4: |
|
496 if (hpet_enabled()) |
|
497 printf("qemu: Writing counter while HPET enabled!\n"); |
|
498 s->hpet_counter = (s->hpet_counter & 0xffffffffULL) |
|
499 | (((uint64_t)value) << 32); |
|
500 dprintf("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n", |
|
501 value, s->hpet_counter); |
|
502 break; |
|
503 default: |
|
504 dprintf("qemu: invalid hpet_ram_writel\n"); |
|
505 break; |
|
506 } |
|
507 } |
|
508 } |
|
509 |
|
510 static CPUReadMemoryFunc *hpet_ram_read[] = { |
|
511 #ifdef HPET_DEBUG |
|
512 hpet_ram_readb, |
|
513 hpet_ram_readw, |
|
514 #else |
|
515 NULL, |
|
516 NULL, |
|
517 #endif |
|
518 hpet_ram_readl, |
|
519 }; |
|
520 |
|
521 static CPUWriteMemoryFunc *hpet_ram_write[] = { |
|
522 #ifdef HPET_DEBUG |
|
523 hpet_ram_writeb, |
|
524 hpet_ram_writew, |
|
525 #else |
|
526 NULL, |
|
527 NULL, |
|
528 #endif |
|
529 hpet_ram_writel, |
|
530 }; |
|
531 |
|
532 static void hpet_reset(void *opaque) { |
|
533 HPETState *s = opaque; |
|
534 int i; |
|
535 static int count = 0; |
|
536 |
|
537 for (i=0; i<HPET_NUM_TIMERS; i++) { |
|
538 HPETTimer *timer = &s->timer[i]; |
|
539 hpet_del_timer(timer); |
|
540 timer->tn = i; |
|
541 timer->cmp = ~0ULL; |
|
542 timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP; |
|
543 /* advertise availability of irqs 5,10,11 */ |
|
544 timer->config |= 0x00000c20ULL << 32; |
|
545 timer->state = s; |
|
546 timer->period = 0ULL; |
|
547 timer->wrap_flag = 0; |
|
548 } |
|
549 |
|
550 s->hpet_counter = 0ULL; |
|
551 s->hpet_offset = 0ULL; |
|
552 /* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */ |
|
553 s->capability = 0x8086a201ULL; |
|
554 s->capability |= ((HPET_CLK_PERIOD) << 32); |
|
555 if (count > 0) |
|
556 /* we don't enable pit when hpet_reset is first called (by hpet_init) |
|
557 * because hpet is taking over for pit here. On subsequent invocations, |
|
558 * hpet_reset is called due to system reset. At this point control must |
|
559 * be returned to pit until SW reenables hpet. |
|
560 */ |
|
561 hpet_pit_enable(); |
|
562 count = 1; |
|
563 } |
|
564 |
|
565 |
|
566 void hpet_init(qemu_irq *irq) { |
|
567 int i, iomemtype; |
|
568 HPETState *s; |
|
569 |
|
570 dprintf ("hpet_init\n"); |
|
571 |
|
572 s = qemu_mallocz(sizeof(HPETState)); |
|
573 hpet_statep = s; |
|
574 s->irqs = irq; |
|
575 for (i=0; i<HPET_NUM_TIMERS; i++) { |
|
576 HPETTimer *timer = &s->timer[i]; |
|
577 timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer); |
|
578 } |
|
579 hpet_reset(s); |
|
580 register_savevm("hpet", -1, 1, hpet_save, hpet_load, s); |
|
581 qemu_register_reset(hpet_reset, s); |
|
582 /* HPET Area */ |
|
583 iomemtype = cpu_register_io_memory(0, hpet_ram_read, |
|
584 hpet_ram_write, s); |
|
585 cpu_register_physical_memory(HPET_BASE, 0x400, iomemtype); |
|
586 } |