代码拉取完成,页面将自动刷新
同步操作将从 src-openEuler/qemu 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
From 1fc8fa6cd621c17988b043c1b3abe9ccb189a1d7 Mon Sep 17 00:00:00 2001
From: lixianglai <lixianglai@loongson.cn>
Date: Tue, 7 Feb 2023 06:34:32 -0500
Subject: [PATCH] Add PowerManager support.
Add Loongarch ACPI power management device simulation.
Signed-off-by: lixianglai <lixianglai@loongson.cn>
---
hw/acpi/Kconfig | 8 +
hw/acpi/larch_7a.c | 616 +++++++++++++++++++++++++++++++++++++++++
hw/acpi/meson.build | 1 +
include/hw/acpi/ls7a.h | 79 ++++++
4 files changed, 704 insertions(+)
create mode 100644 hw/acpi/larch_7a.c
create mode 100644 include/hw/acpi/ls7a.h
diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
index 622b0b50b7..245c5554df 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -15,6 +15,14 @@ config ACPI_X86_ICH
bool
select ACPI_X86
+config ACPI_LOONGARCH
+ bool
+ select ACPI
+ select ACPI_CPU_HOTPLUG
+ select ACPI_MEMORY_HOTPLUG
+ select ACPI_PIIX4
+ select ACPI_PCIHP
+
config ACPI_CPU_HOTPLUG
bool
diff --git a/hw/acpi/larch_7a.c b/hw/acpi/larch_7a.c
new file mode 100644
index 0000000000..59b43170ff
--- /dev/null
+++ b/hw/acpi/larch_7a.c
@@ -0,0 +1,616 @@
+/*
+ * Loongarch acpi emulation
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/runstate.h"
+#include "sysemu/reset.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/ls7a.h"
+#include "hw/nvram/fw_cfg.h"
+#include "qemu/config-file.h"
+#include "qapi/opts-visitor.h"
+#include "qapi/qapi-events-run-state.h"
+#include "qapi/error.h"
+#include "hw/loongarch/ls7a.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/mem/nvdimm.h"
+#include "migration/vmstate.h"
+
+static void ls7a_pm_update_sci_fn(ACPIREGS *regs)
+{
+ LS7APCIPMRegs *pm = container_of(regs, LS7APCIPMRegs, acpi_regs);
+ acpi_update_sci(&pm->acpi_regs, pm->irq);
+}
+
+static uint64_t ls7a_gpe_readb(void *opaque, hwaddr addr, unsigned width)
+{
+ LS7APCIPMRegs *pm = opaque;
+ return acpi_gpe_ioport_readb(&pm->acpi_regs, addr);
+}
+
+static void ls7a_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ LS7APCIPMRegs *pm = opaque;
+ acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
+ acpi_update_sci(&pm->acpi_regs, pm->irq);
+}
+
+static const MemoryRegionOps ls7a_gpe_ops = {
+ .read = ls7a_gpe_readb,
+ .write = ls7a_gpe_writeb,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 1,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+#define VMSTATE_GPE_ARRAY(_field, _state) \
+{ \
+ .name = (stringify(_field)), .version_id = 0, .num = ACPI_GPE0_LEN, \
+ .info = &vmstate_info_uint8, .size = sizeof(uint8_t), \
+ .flags = VMS_ARRAY | VMS_POINTER, \
+ .offset = vmstate_offset_pointer(_state, _field, uint8_t), \
+}
+
+static uint64_t ls7a_reset_readw(void *opaque, hwaddr addr, unsigned width)
+{
+ return 0;
+}
+
+static void ls7a_reset_writew(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ if (val & 1) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+}
+
+static const MemoryRegionOps ls7a_reset_ops = {
+ .read = ls7a_reset_readw,
+ .write = ls7a_reset_writew,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static bool vmstate_test_use_memhp(void *opaque)
+{
+ LS7APCIPMRegs *s = opaque;
+ return s->acpi_memory_hotplug.is_enabled;
+}
+
+static const VMStateDescription vmstate_memhp_state = {
+ .name = "ls7a_pm/memhp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .needed = vmstate_test_use_memhp,
+ .fields = (VMStateField[]){ VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug,
+ LS7APCIPMRegs),
+ VMSTATE_END_OF_LIST() }
+};
+
+static const VMStateDescription vmstate_cpuhp_state = {
+ .name = "ls7a_pm/cpuhp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields =
+ (VMStateField[]){ VMSTATE_CPU_HOTPLUG(cpuhp_state, LS7APCIPMRegs),
+ VMSTATE_END_OF_LIST() }
+};
+
+const VMStateDescription vmstate_ls7a_pm = {
+ .name = "ls7a_pm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields =
+ (VMStateField[]){
+ VMSTATE_UINT16(acpi_regs.pm1.evt.sts, LS7APCIPMRegs),
+ VMSTATE_UINT16(acpi_regs.pm1.evt.en, LS7APCIPMRegs),
+ VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, LS7APCIPMRegs),
+ VMSTATE_TIMER_PTR(acpi_regs.tmr.timer, LS7APCIPMRegs),
+ VMSTATE_INT64(acpi_regs.tmr.overflow_time, LS7APCIPMRegs),
+ VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, LS7APCIPMRegs),
+ VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, LS7APCIPMRegs),
+ VMSTATE_END_OF_LIST() },
+ .subsections = (const VMStateDescription *[]){ &vmstate_memhp_state,
+ &vmstate_cpuhp_state, NULL }
+};
+
+static inline int64_t acpi_pm_tmr_get_clock(void)
+{
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), PM_TIMER_FREQUENCY,
+ NANOSECONDS_PER_SECOND);
+}
+
+static uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
+{
+ uint32_t d = acpi_pm_tmr_get_clock();
+ return d & 0xffffff;
+}
+
+static void acpi_pm_tmr_timer(void *opaque)
+{
+ ACPIREGS *ar = opaque;
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER, NULL);
+ ar->tmr.update_sci(ar);
+}
+
+static uint64_t acpi_pm_tmr_read(void *opaque, hwaddr addr, unsigned width)
+{
+ return acpi_pm_tmr_get(opaque);
+}
+
+static void acpi_pm_tmr_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ /* nothing */
+}
+
+static const MemoryRegionOps acpi_pm_tmr_ops = {
+ .read = acpi_pm_tmr_read,
+ .write = acpi_pm_tmr_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ls7a_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
+ MemoryRegion *parent, uint64_t offset)
+{
+ ar->tmr.update_sci = update_sci;
+ ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar);
+ memory_region_init_io(&ar->tmr.io, memory_region_owner(parent),
+ &acpi_pm_tmr_ops, ar, "acpi-tmr", 4);
+ memory_region_add_subregion(parent, offset, &ar->tmr.io);
+}
+
+static void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
+{
+ uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
+ if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
+ /* if TMRSTS is reset, then compute the new overflow time */
+ acpi_pm_tmr_calc_overflow_time(ar);
+ }
+ ar->pm1.evt.sts &= ~val;
+}
+
+static uint64_t acpi_pm_evt_read(void *opaque, hwaddr addr, unsigned width)
+{
+ ACPIREGS *ar = opaque;
+ switch (addr) {
+ case 0:
+ return acpi_pm1_evt_get_sts(ar);
+ case 4:
+ return ar->pm1.evt.en;
+ default:
+ return 0;
+ }
+}
+
+static void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
+{
+ ar->pm1.evt.en = val;
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
+ val & ACPI_BITMASK_RT_CLOCK_ENABLE);
+ qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
+ val & ACPI_BITMASK_TIMER_ENABLE);
+}
+
+static void acpi_pm_evt_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ ACPIREGS *ar = opaque;
+ switch (addr) {
+ case 0:
+ acpi_pm1_evt_write_sts(ar, val);
+ ar->pm1.evt.update_sci(ar);
+ break;
+ case 4:
+ acpi_pm1_evt_write_en(ar, val);
+ ar->pm1.evt.update_sci(ar);
+ break;
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps acpi_pm_evt_ops = {
+ .read = acpi_pm_evt_read,
+ .write = acpi_pm_evt_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ls7a_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
+ MemoryRegion *parent, uint64_t offset)
+{
+ ar->pm1.evt.update_sci = update_sci;
+ memory_region_init_io(&ar->pm1.evt.io, memory_region_owner(parent),
+ &acpi_pm_evt_ops, ar, "acpi-evt", 8);
+ memory_region_add_subregion(parent, offset, &ar->pm1.evt.io);
+}
+
+static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width)
+{
+ ACPIREGS *ar = opaque;
+ return ar->pm1.cnt.cnt;
+}
+
+/* ACPI PM1aCNT */
+static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val)
+{
+ ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
+ if (val & ACPI_BITMASK_SLEEP_ENABLE) {
+ /* change suspend type */
+ uint16_t sus_typ = (val >> 10) & 7;
+ switch (sus_typ) {
+ /* s3,s4 not support */
+ case 5:
+ case 6:
+ warn_report("acpi s3,s4 state not support");
+ break;
+ /* s5: soft off */
+ case 7:
+ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ acpi_pm1_cnt_write(opaque, val);
+}
+
+static const MemoryRegionOps acpi_pm_cnt_ops = {
+ .read = acpi_pm_cnt_read,
+ .write = acpi_pm_cnt_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void acpi_notify_wakeup(Notifier *notifier, void *data)
+{
+ ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
+ WakeupReason *reason = data;
+
+ switch (*reason) {
+ case QEMU_WAKEUP_REASON_RTC:
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
+ break;
+ case QEMU_WAKEUP_REASON_PMTIMER:
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
+ break;
+ case QEMU_WAKEUP_REASON_OTHER:
+ /*
+ * ACPI_BITMASK_WAKE_STATUS should be set on resume.
+ * Pretend that resume was caused by power button
+ */
+ ar->pm1.evt.sts |=
+ (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
+ break;
+ default:
+ break;
+ }
+}
+
+static void ls7a_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent,
+ bool disable_s3, bool disable_s4, uint8_t s4_val,
+ uint64_t offset)
+{
+ FWCfgState *fw_cfg;
+
+ ar->pm1.cnt.s4_val = s4_val;
+ ar->wakeup.notify = acpi_notify_wakeup;
+ qemu_register_wakeup_notifier(&ar->wakeup);
+ memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent),
+ &acpi_pm_cnt_ops, ar, "acpi-cnt", 4);
+ memory_region_add_subregion(parent, offset, &ar->pm1.cnt.io);
+
+ fw_cfg = fw_cfg_find();
+ if (fw_cfg) {
+ uint8_t suspend[6] = { 128, 0, 0, 129, 128, 128 };
+ suspend[3] = 1 | ((!disable_s3) << 7);
+ suspend[4] = s4_val | ((!disable_s4) << 7);
+ fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6);
+ }
+}
+
+static void ls7a_pm_reset(void *opaque)
+{
+ LS7APCIPMRegs *pm = opaque;
+
+ acpi_pm1_evt_reset(&pm->acpi_regs);
+ acpi_pm1_cnt_reset(&pm->acpi_regs);
+ acpi_pm_tmr_reset(&pm->acpi_regs);
+ acpi_gpe_reset(&pm->acpi_regs);
+
+ acpi_update_sci(&pm->acpi_regs, pm->irq);
+}
+
+static void pm_powerdown_req(Notifier *n, void *opaque)
+{
+ LS7APCIPMRegs *pm = container_of(n, LS7APCIPMRegs, powerdown_notifier);
+
+ acpi_pm1_evt_power_down(&pm->acpi_regs);
+}
+
+void ls7a_pm_init(LS7APCIPMRegs *pm, qemu_irq *pic)
+{
+ unsigned long base, gpe_len, acpi_aci_irq;
+
+ /*
+ * ls7a board acpi hardware info, including
+ * acpi system io base address
+ * acpi gpe length
+ * acpi sci irq number
+ */
+ base = ACPI_IO_BASE;
+ gpe_len = ACPI_GPE0_LEN;
+ acpi_aci_irq = ACPI_SCI_IRQ;
+
+ pm->irq = pic[acpi_aci_irq - 64];
+ memory_region_init(&pm->iomem, NULL, "ls7a_pm", ACPI_IO_SIZE);
+ memory_region_add_subregion(get_system_memory(), base, &pm->iomem);
+
+ cpu_hotplug_hw_init(get_system_memory(), NULL, &pm->cpuhp_state,
+ CPU_HOTPLUG_BASE);
+
+ ls7a_pm_tmr_init(&pm->acpi_regs, ls7a_pm_update_sci_fn, &pm->iomem,
+ LS7A_PM_TMR_BLK);
+ ls7a_pm1_evt_init(&pm->acpi_regs, ls7a_pm_update_sci_fn, &pm->iomem,
+ LS7A_PM_EVT_BLK);
+ ls7a_pm1_cnt_init(&pm->acpi_regs, &pm->iomem, false, false, 2,
+ LS7A_PM_CNT_BLK);
+
+ acpi_gpe_init(&pm->acpi_regs, gpe_len);
+ memory_region_init_io(&pm->iomem_gpe, NULL, &ls7a_gpe_ops, pm, "acpi-gpe0",
+ gpe_len);
+ memory_region_add_subregion(&pm->iomem, LS7A_GPE0_STS_REG, &pm->iomem_gpe);
+
+ memory_region_init_io(&pm->iomem_reset, NULL, &ls7a_reset_ops, pm,
+ "acpi-reset", 4);
+ memory_region_add_subregion(&pm->iomem, LS7A_GPE0_RESET_REG,
+ &pm->iomem_reset);
+
+ qemu_register_reset(ls7a_pm_reset, pm);
+
+ pm->powerdown_notifier.notify = pm_powerdown_req;
+ qemu_register_powerdown_notifier(&pm->powerdown_notifier);
+
+ if (pm->acpi_memory_hotplug.is_enabled) {
+ acpi_memory_hotplug_init(get_system_memory(), NULL,
+ &pm->acpi_memory_hotplug,
+ MEMORY_HOTPLUG_BASE);
+ }
+}
+
+static void ls7a_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint64_t value = ACPI_IO_BASE + LS7A_GPE0_STS_REG;
+
+ visit_type_uint64(v, name, &value, errp);
+}
+
+static bool ls7a_pm_get_memory_hotplug_support(Object *obj, Error **errp)
+{
+ LS7APCIState *ls7a = get_ls7a_type(obj);
+
+ return ls7a->pm.acpi_memory_hotplug.is_enabled;
+}
+
+static void ls7a_pm_set_memory_hotplug_support(Object *obj, bool value,
+ Error **errp)
+{
+ LS7APCIState *ls7a = get_ls7a_type(obj);
+
+ ls7a->pm.acpi_memory_hotplug.is_enabled = value;
+}
+
+static void ls7a_pm_get_disable_s3(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ LS7APCIPMRegs *pm = opaque;
+ uint8_t value = pm->disable_s3;
+
+ visit_type_uint8(v, name, &value, errp);
+}
+
+static void ls7a_pm_set_disable_s3(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ LS7APCIPMRegs *pm = opaque;
+ Error *local_err = NULL;
+ uint8_t value;
+
+ visit_type_uint8(v, name, &value, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ pm->disable_s3 = value;
+out:
+ error_propagate(errp, local_err);
+}
+
+static void ls7a_pm_get_disable_s4(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ LS7APCIPMRegs *pm = opaque;
+ uint8_t value = pm->disable_s4;
+
+ visit_type_uint8(v, name, &value, errp);
+}
+
+static void ls7a_pm_set_disable_s4(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ LS7APCIPMRegs *pm = opaque;
+ Error *local_err = NULL;
+ uint8_t value;
+
+ visit_type_uint8(v, name, &value, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ pm->disable_s4 = value;
+out:
+ error_propagate(errp, local_err);
+}
+
+static void ls7a_pm_get_s4_val(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ LS7APCIPMRegs *pm = opaque;
+ uint8_t value = pm->s4_val;
+
+ visit_type_uint8(v, name, &value, errp);
+}
+
+static void ls7a_pm_set_s4_val(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ LS7APCIPMRegs *pm = opaque;
+ Error *local_err = NULL;
+ uint8_t value;
+
+ visit_type_uint8(v, name, &value, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ pm->s4_val = value;
+out:
+ error_propagate(errp, local_err);
+}
+
+void ls7a_pm_add_properties(Object *obj, LS7APCIPMRegs *pm, Error **errp)
+{
+ static const uint32_t gpe0_len = ACPI_GPE0_LEN;
+ pm->acpi_memory_hotplug.is_enabled = true;
+ pm->disable_s3 = 0;
+ pm->disable_s4 = 0;
+ pm->s4_val = 2;
+
+ object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
+ &pm->pm_io_base, OBJ_PROP_FLAG_READ);
+ object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32",
+ ls7a_pm_get_gpe0_blk, NULL, NULL, pm);
+ object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, &gpe0_len,
+ OBJ_PROP_FLAG_READ);
+ object_property_add_bool(obj, "memory-hotplug-support",
+ ls7a_pm_get_memory_hotplug_support,
+ ls7a_pm_set_memory_hotplug_support);
+ object_property_add(obj, ACPI_PM_PROP_S3_DISABLED, "uint8",
+ ls7a_pm_get_disable_s3, ls7a_pm_set_disable_s3, NULL,
+ pm);
+ object_property_add(obj, ACPI_PM_PROP_S4_DISABLED, "uint8",
+ ls7a_pm_get_disable_s4, ls7a_pm_set_disable_s4, NULL,
+ pm);
+ object_property_add(obj, ACPI_PM_PROP_S4_VAL, "uint8", ls7a_pm_get_s4_val,
+ ls7a_pm_set_s4_val, NULL, pm);
+}
+
+void ls7a_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ LS7APCIState *ls7a = get_ls7a_type(OBJECT(hotplug_dev));
+
+ if (ls7a->pm.acpi_memory_hotplug.is_enabled &&
+ object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
+ nvdimm_acpi_plug_cb(hotplug_dev, dev);
+ } else {
+ acpi_memory_plug_cb(hotplug_dev, &ls7a->pm.acpi_memory_hotplug,
+ dev, errp);
+ }
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_plug_cb(hotplug_dev, &ls7a->pm.cpuhp_state, dev, errp);
+ } else {
+ error_setg(errp,
+ "acpi: device plug request for not supported device"
+ " type: %s",
+ object_get_typename(OBJECT(dev)));
+ }
+}
+
+void ls7a_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ LS7APCIState *ls7a = get_ls7a_type(OBJECT(hotplug_dev));
+
+ if (ls7a->pm.acpi_memory_hotplug.is_enabled &&
+ object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_unplug_request_cb(
+ hotplug_dev, &ls7a->pm.acpi_memory_hotplug, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_unplug_request_cb(hotplug_dev, &ls7a->pm.cpuhp_state, dev,
+ errp);
+ } else {
+ error_setg(errp,
+ "acpi: device unplug request for not supported device"
+ " type: %s",
+ object_get_typename(OBJECT(dev)));
+ }
+}
+
+void ls7a_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ LS7APCIState *ls7a = get_ls7a_type(OBJECT(hotplug_dev));
+
+ if (ls7a->pm.acpi_memory_hotplug.is_enabled &&
+ object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_unplug_cb(&ls7a->pm.acpi_memory_hotplug, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_unplug_cb(&ls7a->pm.cpuhp_state, dev, errp);
+ } else {
+ error_setg(errp,
+ "acpi: device unplug for not supported device"
+ " type: %s",
+ object_get_typename(OBJECT(dev)));
+ }
+}
+
+void ls7a_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
+{
+ LS7APCIState *ls7a = get_ls7a_type(OBJECT(adev));
+
+ acpi_memory_ospm_status(&ls7a->pm.acpi_memory_hotplug, list);
+ acpi_cpu_ospm_status(&ls7a->pm.cpuhp_state, list);
+}
+
+void ls7a_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
+{
+ LS7APCIState *ls7a = get_ls7a_type(OBJECT(adev));
+
+ acpi_send_gpe_event(&ls7a->pm.acpi_regs, ls7a->pm.irq, ev);
+}
diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build
index 448ea6afb4..4718d143fc 100644
--- a/hw/acpi/meson.build
+++ b/hw/acpi/meson.build
@@ -6,6 +6,7 @@ acpi_ss.add(files(
'core.c',
'utils.c',
))
+acpi_ss.add(when: 'CONFIG_ACPI_LOONGARCH', if_true: files('larch_7a.c'))
acpi_ss.add(when: 'CONFIG_ACPI_CPU_HOTPLUG', if_true: files('cpu.c', 'cpu_hotplug.c'))
acpi_ss.add(when: 'CONFIG_ACPI_CPU_HOTPLUG', if_false: files('acpi-cpu-hotplug-stub.c'))
acpi_ss.add(when: 'CONFIG_ACPI_MEMORY_HOTPLUG', if_true: files('memory_hotplug.c'))
diff --git a/include/hw/acpi/ls7a.h b/include/hw/acpi/ls7a.h
new file mode 100644
index 0000000000..295baa4b5a
--- /dev/null
+++ b/include/hw/acpi/ls7a.h
@@ -0,0 +1,79 @@
+/*
+ * QEMU GMCH/LS7A PCI PM Emulation
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef HW_ACPI_LS7A_H
+#define HW_ACPI_LS7A_H
+
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/cpu_hotplug.h"
+#include "hw/acpi/cpu.h"
+#include "hw/acpi/memory_hotplug.h"
+#include "hw/acpi/acpi_dev_interface.h"
+#include "hw/acpi/tco.h"
+
+#define CPU_HOTPLUG_BASE 0x1e000000
+#define MEMORY_HOTPLUG_BASE 0x1e00000c
+
+typedef struct LS7APCIPMRegs {
+ /*
+ * In ls7a spec says that pm1_cnt register is 32bit width and
+ * that the upper 16bits are reserved and unused.
+ * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t.
+ */
+ ACPIREGS acpi_regs;
+
+ MemoryRegion iomem;
+ MemoryRegion iomem_gpe;
+ MemoryRegion iomem_smi;
+ MemoryRegion iomem_reset;
+
+ qemu_irq irq; /* SCI */
+
+ uint32_t pm_io_base;
+ Notifier powerdown_notifier;
+
+ bool cpu_hotplug_legacy;
+ AcpiCpuHotplug gpe_cpu;
+ CPUHotplugState cpuhp_state;
+
+ MemHotplugState acpi_memory_hotplug;
+
+ uint8_t disable_s3;
+ uint8_t disable_s4;
+ uint8_t s4_val;
+} LS7APCIPMRegs;
+
+void ls7a_pm_init(LS7APCIPMRegs *ls7a, qemu_irq *sci_irq);
+
+void ls7a_pm_iospace_update(LS7APCIPMRegs *pm, uint32_t pm_io_base);
+extern const VMStateDescription vmstate_ls7a_pm;
+
+void ls7a_pm_add_properties(Object *obj, LS7APCIPMRegs *pm, Error **errp);
+
+void ls7a_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp);
+void ls7a_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
+void ls7a_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp);
+
+void ls7a_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list);
+
+void ls7a_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev);
+#endif /* HW_ACPI_LS7A_H */
--
2.27.0
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。