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