diff mbox series

[v5,40/43] hw/loongarch: Add LoongArch ls7a acpi device support

Message ID 20220524081804.3608101-41-yangxiaojuan@loongson.cn (mailing list archive)
State New, archived
Headers show
Series Add LoongArch softmmu support | expand

Commit Message

Xiaojuan Yang May 24, 2022, 8:18 a.m. UTC
From: Song Gao <gaosong@loongson.cn>

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

Comments

Igor Mammedov May 26, 2022, 8:42 a.m. UTC | #1
On Tue, 24 May 2022 16:18:01 +0800
Xiaojuan Yang <yangxiaojuan@loongson.cn> wrote:

commit message needs pointers to specification,
+ in patch comments that point to specific chapters
within the spec for newly introduced  registers


> From: Song Gao <gaosong@loongson.cn>
> 
> 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 6e03a8bca8..6f861dec0a 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 3703aca212..c65965c9b9 100644
> --- a/hw/acpi/Kconfig
> +++ b/hw/acpi/Kconfig
> @@ -13,6 +13,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 cea2f5f93a..d9078a2e26 100644
> --- a/hw/acpi/meson.build
> +++ b/hw/acpi/meson.build
> @@ -26,6 +26,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 661345f2bb..b3275967d5 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
bibo mao May 26, 2022, 10:18 p.m. UTC | #2
On 5/26/22 16:42, Igor Mammedov wrote:
> On Tue, 24 May 2022 16:18:01 +0800
> Xiaojuan Yang <yangxiaojuan@loongson.cn> wrote:
> 
> commit message needs pointers to specification,
> + in patch comments that point to specific chapters
> within the spec for newly introduced  registers 
Igor,

Thanks for reviewing the patch and guidance, ls7A acpi registers has 
minimium registers required by ACPI spec, including pm1a stat/en/cnt, 
pm_tmr and GPE stat/enable registers, there is no smi mode in loongarch 
architecture. The  LS7A acpi driver is copied from acpi core driver 
since register layout of pm1a/pm_tmr/gpe is different from x86 acpi 
registers.

By the ACPI spec, there is no specific requirement for layout of ACPI 
registers, later we will reuse acpi core driver if the acpi registers 
layout can be set dynamically. And we will send the second patch with 
detailed description with LS7A ACPI module.

regards
bibo, mao
> 
>> From: Song Gao <gaosong@loongson.cn>
>>
>> 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 6e03a8bca8..6f861dec0a 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 3703aca212..c65965c9b9 100644
>> --- a/hw/acpi/Kconfig
>> +++ b/hw/acpi/Kconfig
>> @@ -13,6 +13,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 cea2f5f93a..d9078a2e26 100644
>> --- a/hw/acpi/meson.build
>> +++ b/hw/acpi/meson.build
>> @@ -26,6 +26,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 661345f2bb..b3275967d5 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
>
Igor Mammedov May 30, 2022, 10:21 a.m. UTC | #3
On Fri, 27 May 2022 06:18:43 +0800
maobibo <maobibo@loongson.cn> wrote:

> On 5/26/22 16:42, Igor Mammedov wrote:
> > On Tue, 24 May 2022 16:18:01 +0800
> > Xiaojuan Yang <yangxiaojuan@loongson.cn> wrote:
> > 
> > commit message needs pointers to specification,
> > + in patch comments that point to specific chapters
> > within the spec for newly introduced  registers   
> Igor,
> 
> Thanks for reviewing the patch and guidance, ls7A acpi registers has 
> minimium registers required by ACPI spec, including pm1a stat/en/cnt, 
> pm_tmr and GPE stat/enable registers, there is no smi mode in loongarch 

those only required for legacy 'Fixed Hardware Programming Model'
which is historically used on x86.
For new platforms if you don't have hardware yet it's better to use
'Hardware-Reduced ACPI' approach and reuse code we already have for
aarch64.

> architecture. The  LS7A acpi driver is copied from acpi core driver 
> since register layout of pm1a/pm_tmr/gpe is different from x86 acpi 
> registers.

sorry, I couldn't parse above sentence.

> By the ACPI spec, there is no specific requirement for layout of ACPI 
> registers, later we will reuse acpi core driver if the acpi registers 
> layout can be set dynamically. And we will send the second patch with 
> detailed description with LS7A ACPI module.

regardless of a separate doc patch, this patch should have a minimal
documentation as it have been pointed earlier, otherwise reviewer or
someone who will later have to look on this code, will have no point
of reference and have no idea if this code is correct or not.


Also introducing ACPI hardware without an ACPI tables to complement
it is rather pointless as OSPM won't be able to discover/use it.
It might be better to drop this patch until you have corresponding
ACPI tables to describe it.

> regards
> bibo, mao
> >   
> >> From: Song Gao <gaosong@loongson.cn>
> >>
> >> 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 6e03a8bca8..6f861dec0a 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 3703aca212..c65965c9b9 100644
> >> --- a/hw/acpi/Kconfig
> >> +++ b/hw/acpi/Kconfig
> >> @@ -13,6 +13,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 cea2f5f93a..d9078a2e26 100644
> >> --- a/hw/acpi/meson.build
> >> +++ b/hw/acpi/meson.build
> >> @@ -26,6 +26,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 661345f2bb..b3275967d5 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  
> >   
> 
>
bibo mao May 31, 2022, 3:43 a.m. UTC | #4
Ignor,

Thanks for guidance, I reply inline.

在 2022/5/30 下午6:21, Igor Mammedov 写道:
> On Fri, 27 May 2022 06:18:43 +0800
> maobibo <maobibo@loongson.cn> wrote:
> 
>> On 5/26/22 16:42, Igor Mammedov wrote:
>>> On Tue, 24 May 2022 16:18:01 +0800
>>> Xiaojuan Yang <yangxiaojuan@loongson.cn> wrote:
>>>
>>> commit message needs pointers to specification,
>>> + in patch comments that point to specific chapters
>>> within the spec for newly introduced  registers
>> Igor,
>>
>> Thanks for reviewing the patch and guidance, ls7A acpi registers has
>> minimium registers required by ACPI spec, including pm1a stat/en/cnt,
>> pm_tmr and GPE stat/enable registers, there is no smi mode in loongarch
> 
> those only required for legacy 'Fixed Hardware Programming Model'
> which is historically used on x86.
> For new platforms if you don't have hardware yet it's better to use
> 'Hardware-Reduced ACPI' approach and reuse code we already have for
> aarch64.
The real loongarch hardware has hw acpi registers like x86, 
'Hardware-Reduced ACPI' approach is a good choice for loongarch virt 
platform and we will investigate this method. Previously we only 
consider to emulate real hardware, anyway the general method is ok for 
virt machine.

>> architecture. The  LS7A acpi driver is copied from acpi core driver
>> since register layout of pm1a/pm_tmr/gpe is different from x86 acpi
>> registers.
> 
> sorry, I couldn't parse above sentence.
The real loongarch ACPI hw registers has PM1a_EVT_BLK/PM1a_CNT_BLK, only 
that PM1_EVT_LEN is 8 bytes, and PM1a_STS/PM1a_EN register width is 4 
bytes, which is different legacy acpi registers on x86, this causes 
different acpi register size and offset. So we do not use original 
driver hw/acpi/core.c, and partly copy and modify from it.

> 
>> By the ACPI spec, there is no specific requirement for layout of ACPI
>> registers, later we will reuse acpi core driver if the acpi registers
>> layout can be set dynamically. And we will send the second patch with
>> detailed description with LS7A ACPI module.
> 
> regardless of a separate doc patch, this patch should have a minimal
> documentation as it have been pointed earlier, otherwise reviewer or
> someone who will later have to look on this code, will have no point
> of reference and have no idea if this code is correct or not.
Well we will add document about acpi module.

> 
> 
> Also introducing ACPI hardware without an ACPI tables to complement
> it is rather pointless as OSPM won't be able to discover/use it.
> It might be better to drop this patch until you have corresponding
> ACPI tables to describe it.
Originally there is ACPI table and fw_cfg table, in order to speed up 
merging progrss it is dropped. We add acpi driver since the testcases 
need to shutdown machine. How about dropping the acpi patch and adding 
simple virt pm device with shutdown machine function?

After this patch is merged, linux booting function will be added 
incluing acpi driver/table and fw_cfg table also etc.

regards
bibo,mao

>>>    
>>>> From: Song Gao <gaosong@loongson.cn>
>>>>
>>>> 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 6e03a8bca8..6f861dec0a 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 3703aca212..c65965c9b9 100644
>>>> --- a/hw/acpi/Kconfig
>>>> +++ b/hw/acpi/Kconfig
>>>> @@ -13,6 +13,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 cea2f5f93a..d9078a2e26 100644
>>>> --- a/hw/acpi/meson.build
>>>> +++ b/hw/acpi/meson.build
>>>> @@ -26,6 +26,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 661345f2bb..b3275967d5 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 mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 6e03a8bca8..6f861dec0a 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 3703aca212..c65965c9b9 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -13,6 +13,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 cea2f5f93a..d9078a2e26 100644
--- a/hw/acpi/meson.build
+++ b/hw/acpi/meson.build
@@ -26,6 +26,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 661345f2bb..b3275967d5 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