Message ID | 20220429100729.1572481-41-yangxiaojuan@loongson.cn (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add LoongArch softmmu support | expand |
Cc: ACPI maintainers. I know nothing about ACPI, could one of you help out here? r~ On 4/29/22 05:07, Xiaojuan Yang wrote: > Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn> > Signed-off-by: Song Gao <gaosong@loongson.cn> > --- > MAINTAINERS | 2 + > hw/acpi/Kconfig | 4 + > hw/acpi/ls7a.c | 374 +++++++++++++++++++++++++++++++++++++ > hw/acpi/meson.build | 1 + > hw/loongarch/Kconfig | 2 + > hw/loongarch/loongson3.c | 19 +- > include/hw/acpi/ls7a.h | 53 ++++++ > include/hw/pci-host/ls7a.h | 6 + > 8 files changed, 458 insertions(+), 3 deletions(-) > create mode 100644 hw/acpi/ls7a.c > create mode 100644 include/hw/acpi/ls7a.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 7969004b91..18d06bb859 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1138,6 +1138,8 @@ F: include/hw/intc/loongarch_*.h > F: hw/intc/loongarch_*.c > F: include/hw/pci-host/ls7a.h > F: hw/rtc/ls7a_rtc.c > +F: include/hw/acpi/ls7a.h > +F: hw/acpi/ls7a.c > > M68K Machines > ------------- > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig > index 19caebde6c..ff9ceb2259 100644 > --- a/hw/acpi/Kconfig > +++ b/hw/acpi/Kconfig > @@ -12,6 +12,10 @@ config ACPI_X86 > select ACPI_PCIHP > select ACPI_ERST > > +config ACPI_LOONGARCH > + bool > + select ACPI > + > config ACPI_X86_ICH > bool > select ACPI_X86 > diff --git a/hw/acpi/ls7a.c b/hw/acpi/ls7a.c > new file mode 100644 > index 0000000000..cc658422dd > --- /dev/null > +++ b/hw/acpi/ls7a.c > @@ -0,0 +1,374 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * LoongArch ACPI implementation > + * > + * Copyright (C) 2021 Loongson Technology Corporation Limited > + */ > + > +#include "qemu/osdep.h" > +#include "sysemu/sysemu.h" > +#include "hw/hw.h" > +#include "hw/irq.h" > +#include "sysemu/reset.h" > +#include "sysemu/runstate.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/pci-host/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) > +{ > + LS7APMState *pm = container_of(regs, LS7APMState, acpi_regs); > + acpi_update_sci(&pm->acpi_regs, pm->irq); > +} > + > +static uint64_t ls7a_gpe_readb(void *opaque, hwaddr addr, unsigned width) > +{ > + LS7APMState *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) > +{ > + LS7APMState *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); > + return; > + } > +} > + > +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, > +}; > + > +const VMStateDescription vmstate_ls7a_pm = { > + .name = "ls7a_pm", > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT16(acpi_regs.pm1.evt.sts, LS7APMState), > + VMSTATE_UINT16(acpi_regs.pm1.evt.en, LS7APMState), > + VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, LS7APMState), > + VMSTATE_TIMER_PTR(acpi_regs.tmr.timer, LS7APMState), > + VMSTATE_INT64(acpi_regs.tmr.overflow_time, LS7APMState), > + VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, LS7APMState), > + VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, LS7APMState), > + VMSTATE_END_OF_LIST() > + }, > +}; > + > +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) > +{ > +} > + > +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; > + } > +} > + > +static const MemoryRegionOps acpi_pm_evt_ops = { > + .read = acpi_pm_evt_read, > + .write = acpi_pm_evt_write, > + .valid.min_access_size = 1, > + .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) { > + /* Not support s3 s4 yet */ > + case 7: /* Soft power off */ > + 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 = 1, > + .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, > + uint64_t offset) > +{ > + 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); > +} > + > +static void ls7a_pm_reset(DeviceState *d) > +{ > + LS7APMState *pm = LS7A_PM(d); > + > + 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) > +{ > + LS7APMState *pm = container_of(n, LS7APMState, powerdown_notifier); > + > + acpi_pm1_evt_power_down(&pm->acpi_regs); > +} > + > +void ls7a_pm_init(DeviceState *ls7a_pm, qemu_irq pm_irq) > +{ > + LS7APMState *pm = LS7A_PM(ls7a_pm); > + pm->irq = pm_irq; > +} > + > +static void ls7a_pm_realize(DeviceState *dev, Error **errp) > +{ > + LS7APMState *pm = LS7A_PM(dev); > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); > + > + /* > + * ls7a board acpi hardware info, including > + * acpi system io base address > + * acpi gpe length > + * acpi sci irq number > + */ > + > + memory_region_init(&pm->iomem, OBJECT(pm), "ls7a_pm", ACPI_IO_SIZE); > + sysbus_init_mmio(sbd, &pm->iomem); > + > + 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, LS7A_PM_CNT_BLK); > + > + acpi_gpe_init(&pm->acpi_regs, ACPI_GPE0_LEN); > + memory_region_init_io(&pm->iomem_gpe, OBJECT(pm), &ls7a_gpe_ops, pm, > + "acpi-gpe0", ACPI_GPE0_LEN); > + sysbus_init_mmio(sbd, &pm->iomem_gpe); > + > + memory_region_init_io(&pm->iomem_reset, OBJECT(pm), > + &ls7a_reset_ops, pm, "acpi-reset", 4); > + sysbus_init_mmio(sbd, &pm->iomem_reset); > + > + pm->powerdown_notifier.notify = pm_powerdown_req; > + qemu_register_powerdown_notifier(&pm->powerdown_notifier); > +} > + > +static void ls7a_pm_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = ls7a_pm_realize; > + dc->reset = ls7a_pm_reset; > + dc->desc = "PM"; > + dc->vmsd = &vmstate_ls7a_pm; > +} > + > +static const TypeInfo ls7a_pm_info = { > + .name = TYPE_LS7A_PM, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(LS7APMState), > + .class_init = ls7a_pm_class_init, > +}; > + > +static void ls7a_pm_register_types(void) > +{ > + type_register_static(&ls7a_pm_info); > +} > + > +type_init(ls7a_pm_register_types) > diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build > index 8bea2e6933..e6b1ba6f3c 100644 > --- a/hw/acpi/meson.build > +++ b/hw/acpi/meson.build > @@ -25,6 +25,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c')) > acpi_ss.add(when: 'CONFIG_ACPI_ERST', if_true: files('erst.c')) > acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c')) > acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) > +acpi_ss.add(when: 'CONFIG_ACPI_LOONGARCH', if_true: files('ls7a.c')) > if have_tpm > acpi_ss.add(files('tpm.c')) > endif > diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig > index 35b6680772..7c863b7150 100644 > --- a/hw/loongarch/Kconfig > +++ b/hw/loongarch/Kconfig > @@ -14,3 +14,5 @@ config LOONGARCH_VIRT > select LOONGARCH_PCH_MSI > select LOONGARCH_EXTIOI > select LS7A_RTC > + select ACPI_LOONGARCH > + select ACPI_PCI > diff --git a/hw/loongarch/loongson3.c b/hw/loongarch/loongson3.c > index f9ee024f63..ee0acf4ba8 100644 > --- a/hw/loongarch/loongson3.c > +++ b/hw/loongarch/loongson3.c > @@ -28,7 +28,8 @@ > #include "hw/pci-host/ls7a.h" > #include "hw/pci-host/gpex.h" > #include "hw/misc/unimp.h" > - > +#include "hw/acpi/aml-build.h" > +#include "qapi/qapi-visit-common.h" > #include "target/loongarch/cpu.h" > > static struct _loaderparams { > @@ -63,11 +64,11 @@ static int64_t load_kernel_info(void) > > static void loongarch_devices_init(DeviceState *pch_pic) > { > - DeviceState *gpex_dev; > + DeviceState *gpex_dev, *ls7a_pm; > SysBusDevice *d; > PCIBus *pci_bus; > MemoryRegion *ecam_alias, *ecam_reg, *pio_alias, *pio_reg; > - MemoryRegion *mmio_alias, *mmio_reg; > + MemoryRegion *mmio_alias, *mmio_reg, *pm_reg; > int i; > > gpex_dev = qdev_new(TYPE_GPEX_HOST); > @@ -133,6 +134,18 @@ static void loongarch_devices_init(DeviceState *pch_pic) > sysbus_create_simple("ls7a_rtc", LS7A_RTC_REG_BASE, > qdev_get_gpio_in(pch_pic, > LS7A_RTC_IRQ - PCH_PIC_IRQ_OFFSET)); > + /* Init pm */ > + ls7a_pm = qdev_new(TYPE_LS7A_PM); > + d = SYS_BUS_DEVICE(ls7a_pm); > + sysbus_realize_and_unref(d, &error_fatal); > + ls7a_pm_init(ls7a_pm, qdev_get_gpio_in(pch_pic, > + ACPI_SCI_IRQ - PCH_PIC_IRQ_OFFSET)); > + pm_reg = sysbus_mmio_get_region(d, 0); > + memory_region_add_subregion(get_system_memory(), ACPI_IO_BASE, pm_reg); > + memory_region_add_subregion(pm_reg, LS7A_GPE0_STS_REG, > + sysbus_mmio_get_region(d, 1)); > + memory_region_add_subregion(pm_reg, LS7A_GPE0_RESET_REG, > + sysbus_mmio_get_region(d, 2)); > } > > static void loongarch_irq_init(LoongArchMachineState *lams) > diff --git a/include/hw/acpi/ls7a.h b/include/hw/acpi/ls7a.h > new file mode 100644 > index 0000000000..28fe23c8a3 > --- /dev/null > +++ b/include/hw/acpi/ls7a.h > @@ -0,0 +1,53 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * QEMU GMCH/LS7A PCI PM Emulation > + * > + * Copyright (C) 2021 Loongson Technology Corporation Limited > + */ > + > +#ifndef HW_ACPI_LS7A_H > +#define HW_ACPI_LS7A_H > + > +#include "hw/acpi/acpi.h" > +#include "hw/sysbus.h" > + > +#define LS7A_ACPI_IO_BASE 0x800 > +#define LS7A_ACPI_IO_SIZE 0x100 > +#define LS7A_PM_EVT_BLK (0x0C) /* 4 bytes */ > +#define LS7A_PM_CNT_BLK (0x14) /* 2 bytes */ > +#define LS7A_GPE0_STS_REG (0x28) /* 4 bytes */ > +#define LS7A_GPE0_ENA_REG (0x2C) /* 4 bytes */ > +#define LS7A_GPE0_RESET_REG (0x30) /* 4 bytes */ > +#define LS7A_PM_TMR_BLK (0x18) /* 4 bytes */ > +#define LS7A_GPE0_LEN (8) > +#define ACPI_IO_BASE (LS7A_ACPI_REG_BASE) > +#define ACPI_GPE0_LEN (LS7A_GPE0_LEN) > +#define ACPI_IO_SIZE (LS7A_ACPI_IO_SIZE) > +#define ACPI_SCI_IRQ (LS7A_SCI_IRQ) > + > +typedef struct LS7APMState { > + SysBusDevice parent_obj; > + /* > + * 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_reset; > + > + qemu_irq irq; /* SCI */ > + > + uint32_t pm_io_base; > + Notifier powerdown_notifier; > +} LS7APMState; > + > +#define TYPE_LS7A_PM "ls7a_pm" > +DECLARE_INSTANCE_CHECKER(struct LS7APMState, LS7A_PM, TYPE_LS7A_PM) > + > +void ls7a_pm_init(DeviceState *ls7a_pm, qemu_irq irq); > + > +extern const VMStateDescription vmstate_ls7a_pm; > +#endif /* HW_ACPI_LS7A_H */ > diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h > index 1110d25306..baf8dde84e 100644 > --- a/include/hw/pci-host/ls7a.h > +++ b/include/hw/pci-host/ls7a.h > @@ -11,6 +11,7 @@ > #include "hw/pci/pci.h" > #include "hw/pci/pcie_host.h" > #include "hw/pci-host/pam.h" > +#include "hw/acpi/ls7a.h" > #include "qemu/units.h" > #include "qemu/range.h" > #include "qom/object.h" > @@ -21,6 +22,9 @@ > #define LS7A_PCI_IO_BASE 0x18004000UL > #define LS7A_PCI_IO_SIZE 0xC000 > > +#define LS7A_PCI_MEM_BASE 0x40000000UL > +#define LS7A_PCI_MEM_SIZE 0x40000000UL > + > #define LS7A_PCH_REG_BASE 0x10000000UL > #define LS7A_IOAPIC_REG_BASE (LS7A_PCH_REG_BASE) > #define LS7A_PCH_MSI_ADDR_LOW 0x2FF00000UL > @@ -39,4 +43,6 @@ > #define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000) > #define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100) > #define LS7A_RTC_LEN 0x100 > +#define LS7A_ACPI_REG_BASE (LS7A_MISC_REG_BASE + 0x00050000) > +#define LS7A_SCI_IRQ (PCH_PIC_IRQ_OFFSET + 4) > #endif
diff --git a/MAINTAINERS b/MAINTAINERS index 7969004b91..18d06bb859 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1138,6 +1138,8 @@ F: include/hw/intc/loongarch_*.h F: hw/intc/loongarch_*.c F: include/hw/pci-host/ls7a.h F: hw/rtc/ls7a_rtc.c +F: include/hw/acpi/ls7a.h +F: hw/acpi/ls7a.c M68K Machines ------------- diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 19caebde6c..ff9ceb2259 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -12,6 +12,10 @@ config ACPI_X86 select ACPI_PCIHP select ACPI_ERST +config ACPI_LOONGARCH + bool + select ACPI + config ACPI_X86_ICH bool select ACPI_X86 diff --git a/hw/acpi/ls7a.c b/hw/acpi/ls7a.c new file mode 100644 index 0000000000..cc658422dd --- /dev/null +++ b/hw/acpi/ls7a.c @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch ACPI implementation + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "sysemu/reset.h" +#include "sysemu/runstate.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/pci-host/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) +{ + LS7APMState *pm = container_of(regs, LS7APMState, acpi_regs); + acpi_update_sci(&pm->acpi_regs, pm->irq); +} + +static uint64_t ls7a_gpe_readb(void *opaque, hwaddr addr, unsigned width) +{ + LS7APMState *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) +{ + LS7APMState *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); + return; + } +} + +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, +}; + +const VMStateDescription vmstate_ls7a_pm = { + .name = "ls7a_pm", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(acpi_regs.pm1.evt.sts, LS7APMState), + VMSTATE_UINT16(acpi_regs.pm1.evt.en, LS7APMState), + VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, LS7APMState), + VMSTATE_TIMER_PTR(acpi_regs.tmr.timer, LS7APMState), + VMSTATE_INT64(acpi_regs.tmr.overflow_time, LS7APMState), + VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, LS7APMState), + VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, LS7APMState), + VMSTATE_END_OF_LIST() + }, +}; + +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) +{ +} + +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; + } +} + +static const MemoryRegionOps acpi_pm_evt_ops = { + .read = acpi_pm_evt_read, + .write = acpi_pm_evt_write, + .valid.min_access_size = 1, + .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) { + /* Not support s3 s4 yet */ + case 7: /* Soft power off */ + 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 = 1, + .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, + uint64_t offset) +{ + 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); +} + +static void ls7a_pm_reset(DeviceState *d) +{ + LS7APMState *pm = LS7A_PM(d); + + 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) +{ + LS7APMState *pm = container_of(n, LS7APMState, powerdown_notifier); + + acpi_pm1_evt_power_down(&pm->acpi_regs); +} + +void ls7a_pm_init(DeviceState *ls7a_pm, qemu_irq pm_irq) +{ + LS7APMState *pm = LS7A_PM(ls7a_pm); + pm->irq = pm_irq; +} + +static void ls7a_pm_realize(DeviceState *dev, Error **errp) +{ + LS7APMState *pm = LS7A_PM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + /* + * ls7a board acpi hardware info, including + * acpi system io base address + * acpi gpe length + * acpi sci irq number + */ + + memory_region_init(&pm->iomem, OBJECT(pm), "ls7a_pm", ACPI_IO_SIZE); + sysbus_init_mmio(sbd, &pm->iomem); + + 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, LS7A_PM_CNT_BLK); + + acpi_gpe_init(&pm->acpi_regs, ACPI_GPE0_LEN); + memory_region_init_io(&pm->iomem_gpe, OBJECT(pm), &ls7a_gpe_ops, pm, + "acpi-gpe0", ACPI_GPE0_LEN); + sysbus_init_mmio(sbd, &pm->iomem_gpe); + + memory_region_init_io(&pm->iomem_reset, OBJECT(pm), + &ls7a_reset_ops, pm, "acpi-reset", 4); + sysbus_init_mmio(sbd, &pm->iomem_reset); + + pm->powerdown_notifier.notify = pm_powerdown_req; + qemu_register_powerdown_notifier(&pm->powerdown_notifier); +} + +static void ls7a_pm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = ls7a_pm_realize; + dc->reset = ls7a_pm_reset; + dc->desc = "PM"; + dc->vmsd = &vmstate_ls7a_pm; +} + +static const TypeInfo ls7a_pm_info = { + .name = TYPE_LS7A_PM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LS7APMState), + .class_init = ls7a_pm_class_init, +}; + +static void ls7a_pm_register_types(void) +{ + type_register_static(&ls7a_pm_info); +} + +type_init(ls7a_pm_register_types) diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index 8bea2e6933..e6b1ba6f3c 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -25,6 +25,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c')) acpi_ss.add(when: 'CONFIG_ACPI_ERST', if_true: files('erst.c')) acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c')) acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) +acpi_ss.add(when: 'CONFIG_ACPI_LOONGARCH', if_true: files('ls7a.c')) if have_tpm acpi_ss.add(files('tpm.c')) endif diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 35b6680772..7c863b7150 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -14,3 +14,5 @@ config LOONGARCH_VIRT select LOONGARCH_PCH_MSI select LOONGARCH_EXTIOI select LS7A_RTC + select ACPI_LOONGARCH + select ACPI_PCI diff --git a/hw/loongarch/loongson3.c b/hw/loongarch/loongson3.c index f9ee024f63..ee0acf4ba8 100644 --- a/hw/loongarch/loongson3.c +++ b/hw/loongarch/loongson3.c @@ -28,7 +28,8 @@ #include "hw/pci-host/ls7a.h" #include "hw/pci-host/gpex.h" #include "hw/misc/unimp.h" - +#include "hw/acpi/aml-build.h" +#include "qapi/qapi-visit-common.h" #include "target/loongarch/cpu.h" static struct _loaderparams { @@ -63,11 +64,11 @@ static int64_t load_kernel_info(void) static void loongarch_devices_init(DeviceState *pch_pic) { - DeviceState *gpex_dev; + DeviceState *gpex_dev, *ls7a_pm; SysBusDevice *d; PCIBus *pci_bus; MemoryRegion *ecam_alias, *ecam_reg, *pio_alias, *pio_reg; - MemoryRegion *mmio_alias, *mmio_reg; + MemoryRegion *mmio_alias, *mmio_reg, *pm_reg; int i; gpex_dev = qdev_new(TYPE_GPEX_HOST); @@ -133,6 +134,18 @@ static void loongarch_devices_init(DeviceState *pch_pic) sysbus_create_simple("ls7a_rtc", LS7A_RTC_REG_BASE, qdev_get_gpio_in(pch_pic, LS7A_RTC_IRQ - PCH_PIC_IRQ_OFFSET)); + /* Init pm */ + ls7a_pm = qdev_new(TYPE_LS7A_PM); + d = SYS_BUS_DEVICE(ls7a_pm); + sysbus_realize_and_unref(d, &error_fatal); + ls7a_pm_init(ls7a_pm, qdev_get_gpio_in(pch_pic, + ACPI_SCI_IRQ - PCH_PIC_IRQ_OFFSET)); + pm_reg = sysbus_mmio_get_region(d, 0); + memory_region_add_subregion(get_system_memory(), ACPI_IO_BASE, pm_reg); + memory_region_add_subregion(pm_reg, LS7A_GPE0_STS_REG, + sysbus_mmio_get_region(d, 1)); + memory_region_add_subregion(pm_reg, LS7A_GPE0_RESET_REG, + sysbus_mmio_get_region(d, 2)); } static void loongarch_irq_init(LoongArchMachineState *lams) diff --git a/include/hw/acpi/ls7a.h b/include/hw/acpi/ls7a.h new file mode 100644 index 0000000000..28fe23c8a3 --- /dev/null +++ b/include/hw/acpi/ls7a.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU GMCH/LS7A PCI PM Emulation + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#ifndef HW_ACPI_LS7A_H +#define HW_ACPI_LS7A_H + +#include "hw/acpi/acpi.h" +#include "hw/sysbus.h" + +#define LS7A_ACPI_IO_BASE 0x800 +#define LS7A_ACPI_IO_SIZE 0x100 +#define LS7A_PM_EVT_BLK (0x0C) /* 4 bytes */ +#define LS7A_PM_CNT_BLK (0x14) /* 2 bytes */ +#define LS7A_GPE0_STS_REG (0x28) /* 4 bytes */ +#define LS7A_GPE0_ENA_REG (0x2C) /* 4 bytes */ +#define LS7A_GPE0_RESET_REG (0x30) /* 4 bytes */ +#define LS7A_PM_TMR_BLK (0x18) /* 4 bytes */ +#define LS7A_GPE0_LEN (8) +#define ACPI_IO_BASE (LS7A_ACPI_REG_BASE) +#define ACPI_GPE0_LEN (LS7A_GPE0_LEN) +#define ACPI_IO_SIZE (LS7A_ACPI_IO_SIZE) +#define ACPI_SCI_IRQ (LS7A_SCI_IRQ) + +typedef struct LS7APMState { + SysBusDevice parent_obj; + /* + * 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_reset; + + qemu_irq irq; /* SCI */ + + uint32_t pm_io_base; + Notifier powerdown_notifier; +} LS7APMState; + +#define TYPE_LS7A_PM "ls7a_pm" +DECLARE_INSTANCE_CHECKER(struct LS7APMState, LS7A_PM, TYPE_LS7A_PM) + +void ls7a_pm_init(DeviceState *ls7a_pm, qemu_irq irq); + +extern const VMStateDescription vmstate_ls7a_pm; +#endif /* HW_ACPI_LS7A_H */ diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h index 1110d25306..baf8dde84e 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -11,6 +11,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pcie_host.h" #include "hw/pci-host/pam.h" +#include "hw/acpi/ls7a.h" #include "qemu/units.h" #include "qemu/range.h" #include "qom/object.h" @@ -21,6 +22,9 @@ #define LS7A_PCI_IO_BASE 0x18004000UL #define LS7A_PCI_IO_SIZE 0xC000 +#define LS7A_PCI_MEM_BASE 0x40000000UL +#define LS7A_PCI_MEM_SIZE 0x40000000UL + #define LS7A_PCH_REG_BASE 0x10000000UL #define LS7A_IOAPIC_REG_BASE (LS7A_PCH_REG_BASE) #define LS7A_PCH_MSI_ADDR_LOW 0x2FF00000UL @@ -39,4 +43,6 @@ #define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000) #define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100) #define LS7A_RTC_LEN 0x100 +#define LS7A_ACPI_REG_BASE (LS7A_MISC_REG_BASE + 0x00050000) +#define LS7A_SCI_IRQ (PCH_PIC_IRQ_OFFSET + 4) #endif