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