1 /*
2 * QEMU ICH9 Timer emulation
3 *
4 * Copyright (c) 2024 Dominic Prinz <git@dprinz.de>
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
8 */
9
10 #include "qemu/osdep.h"
11 #include "hw/core/cpu.h"
12 #include "hw/pci/pci.h"
13 #include "hw/southbridge/ich9.h"
14 #include "qemu/timer.h"
15
16 #include "hw/acpi/ich9_timer.h"
17
ich9_pm_update_swsmi_timer(ICH9LPCPMRegs * pm,bool enable)18 void ich9_pm_update_swsmi_timer(ICH9LPCPMRegs *pm, bool enable)
19 {
20 uint16_t swsmi_rate_sel;
21 int64_t expire_time;
22 ICH9LPCState *lpc;
23
24 if (enable) {
25 lpc = container_of(pm, ICH9LPCState, pm);
26 swsmi_rate_sel =
27 (pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_3) & 0xc0) >> 6;
28
29 if (swsmi_rate_sel == 0) {
30 expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 1500000LL;
31 } else {
32 expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
33 8 * (1 << swsmi_rate_sel) * 1000000LL;
34 }
35
36 timer_mod(pm->swsmi_timer, expire_time);
37 } else {
38 timer_del(pm->swsmi_timer);
39 }
40 }
41
ich9_pm_swsmi_timer_expired(void * opaque)42 static void ich9_pm_swsmi_timer_expired(void *opaque)
43 {
44 ICH9LPCPMRegs *pm = opaque;
45
46 pm->smi_sts |= ICH9_PMIO_SMI_STS_SWSMI_STS;
47 ich9_generate_smi();
48
49 ich9_pm_update_swsmi_timer(pm, pm->smi_en & ICH9_PMIO_SMI_EN_SWSMI_EN);
50 }
51
ich9_pm_swsmi_timer_init(ICH9LPCPMRegs * pm)52 void ich9_pm_swsmi_timer_init(ICH9LPCPMRegs *pm)
53 {
54 pm->smi_sts_wmask |= ICH9_PMIO_SMI_STS_SWSMI_STS;
55 pm->swsmi_timer =
56 timer_new_ns(QEMU_CLOCK_VIRTUAL, ich9_pm_swsmi_timer_expired, pm);
57 }
58
ich9_pm_update_periodic_timer(ICH9LPCPMRegs * pm,bool enable)59 void ich9_pm_update_periodic_timer(ICH9LPCPMRegs *pm, bool enable)
60 {
61 uint16_t per_smi_sel;
62 int64_t expire_time;
63 ICH9LPCState *lpc;
64
65 if (enable) {
66 lpc = container_of(pm, ICH9LPCState, pm);
67 per_smi_sel = pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_1) & 3;
68 expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
69 8 * (1 << (3 - per_smi_sel)) * NANOSECONDS_PER_SECOND;
70
71 timer_mod(pm->periodic_timer, expire_time);
72 } else {
73 timer_del(pm->periodic_timer);
74 }
75 }
76
ich9_pm_periodic_timer_expired(void * opaque)77 static void ich9_pm_periodic_timer_expired(void *opaque)
78 {
79 ICH9LPCPMRegs *pm = opaque;
80
81 pm->smi_sts = ICH9_PMIO_SMI_STS_PERIODIC_STS;
82 ich9_generate_smi();
83
84 ich9_pm_update_periodic_timer(pm,
85 pm->smi_en & ICH9_PMIO_SMI_EN_PERIODIC_EN);
86 }
87
ich9_pm_periodic_timer_init(ICH9LPCPMRegs * pm)88 void ich9_pm_periodic_timer_init(ICH9LPCPMRegs *pm)
89 {
90 pm->smi_sts_wmask |= ICH9_PMIO_SMI_STS_PERIODIC_STS;
91 pm->periodic_timer =
92 timer_new_ns(QEMU_CLOCK_VIRTUAL, ich9_pm_periodic_timer_expired, pm);
93 }
94