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