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 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 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 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 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 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 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