xref: /openbmc/qemu/hw/acpi/ich9_timer.c (revision 2e1cacfb)
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 
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 
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 
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 
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 
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 
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