xref: /openbmc/qemu/hw/acpi/ich9.c (revision 64bde0f3)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * ACPI implementation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2006 Fabrice Bellard
549ab747fSPaolo Bonzini  * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
649ab747fSPaolo Bonzini  *                    VA Linux Systems Japan K.K.
749ab747fSPaolo Bonzini  * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
849ab747fSPaolo Bonzini  *
949ab747fSPaolo Bonzini  * This is based on acpi.c.
1049ab747fSPaolo Bonzini  *
1149ab747fSPaolo Bonzini  * This library is free software; you can redistribute it and/or
1249ab747fSPaolo Bonzini  * modify it under the terms of the GNU Lesser General Public
1349ab747fSPaolo Bonzini  * License version 2 as published by the Free Software Foundation.
1449ab747fSPaolo Bonzini  *
1549ab747fSPaolo Bonzini  * This library is distributed in the hope that it will be useful,
1649ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1749ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1849ab747fSPaolo Bonzini  * Lesser General Public License for more details.
1949ab747fSPaolo Bonzini  *
2049ab747fSPaolo Bonzini  * You should have received a copy of the GNU Lesser General Public
2149ab747fSPaolo Bonzini  * License along with this library; if not, see <http://www.gnu.org/licenses/>
2249ab747fSPaolo Bonzini  *
2349ab747fSPaolo Bonzini  * Contributions after 2012-01-13 are licensed under the terms of the
2449ab747fSPaolo Bonzini  * GNU GPL, version 2 or (at your option) any later version.
2549ab747fSPaolo Bonzini  */
2649ab747fSPaolo Bonzini #include "hw/hw.h"
2749ab747fSPaolo Bonzini #include "hw/i386/pc.h"
2849ab747fSPaolo Bonzini #include "hw/pci/pci.h"
2949ab747fSPaolo Bonzini #include "qemu/timer.h"
3049ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
3149ab747fSPaolo Bonzini #include "hw/acpi/acpi.h"
3249ab747fSPaolo Bonzini #include "sysemu/kvm.h"
3349ab747fSPaolo Bonzini #include "exec/address-spaces.h"
3449ab747fSPaolo Bonzini 
3549ab747fSPaolo Bonzini #include "hw/i386/ich9.h"
3649ab747fSPaolo Bonzini 
3749ab747fSPaolo Bonzini //#define DEBUG
3849ab747fSPaolo Bonzini 
3949ab747fSPaolo Bonzini #ifdef DEBUG
4049ab747fSPaolo Bonzini #define ICH9_DEBUG(fmt, ...) \
4149ab747fSPaolo Bonzini do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
4249ab747fSPaolo Bonzini #else
4349ab747fSPaolo Bonzini #define ICH9_DEBUG(fmt, ...)    do { } while (0)
4449ab747fSPaolo Bonzini #endif
4549ab747fSPaolo Bonzini 
4649ab747fSPaolo Bonzini static void pm_update_sci(ICH9LPCPMRegs *pm)
4749ab747fSPaolo Bonzini {
4849ab747fSPaolo Bonzini     int sci_level, pm1a_sts;
4949ab747fSPaolo Bonzini 
5049ab747fSPaolo Bonzini     pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs);
5149ab747fSPaolo Bonzini 
5249ab747fSPaolo Bonzini     sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
5349ab747fSPaolo Bonzini                   (ACPI_BITMASK_RT_CLOCK_ENABLE |
5449ab747fSPaolo Bonzini                    ACPI_BITMASK_POWER_BUTTON_ENABLE |
5549ab747fSPaolo Bonzini                    ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
5649ab747fSPaolo Bonzini                    ACPI_BITMASK_TIMER_ENABLE)) != 0);
5749ab747fSPaolo Bonzini     qemu_set_irq(pm->irq, sci_level);
5849ab747fSPaolo Bonzini 
5949ab747fSPaolo Bonzini     /* schedule a timer interruption if needed */
6049ab747fSPaolo Bonzini     acpi_pm_tmr_update(&pm->acpi_regs,
6149ab747fSPaolo Bonzini                        (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
6249ab747fSPaolo Bonzini                        !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
6349ab747fSPaolo Bonzini }
6449ab747fSPaolo Bonzini 
6549ab747fSPaolo Bonzini static void ich9_pm_update_sci_fn(ACPIREGS *regs)
6649ab747fSPaolo Bonzini {
6749ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs);
6849ab747fSPaolo Bonzini     pm_update_sci(pm);
6949ab747fSPaolo Bonzini }
7049ab747fSPaolo Bonzini 
7149ab747fSPaolo Bonzini static uint64_t ich9_gpe_readb(void *opaque, hwaddr addr, unsigned width)
7249ab747fSPaolo Bonzini {
7349ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
7449ab747fSPaolo Bonzini     return acpi_gpe_ioport_readb(&pm->acpi_regs, addr);
7549ab747fSPaolo Bonzini }
7649ab747fSPaolo Bonzini 
7749ab747fSPaolo Bonzini static void ich9_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
7849ab747fSPaolo Bonzini                             unsigned width)
7949ab747fSPaolo Bonzini {
8049ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
8149ab747fSPaolo Bonzini     acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
8249ab747fSPaolo Bonzini }
8349ab747fSPaolo Bonzini 
8449ab747fSPaolo Bonzini static const MemoryRegionOps ich9_gpe_ops = {
8549ab747fSPaolo Bonzini     .read = ich9_gpe_readb,
8649ab747fSPaolo Bonzini     .write = ich9_gpe_writeb,
8749ab747fSPaolo Bonzini     .valid.min_access_size = 1,
8849ab747fSPaolo Bonzini     .valid.max_access_size = 4,
8949ab747fSPaolo Bonzini     .impl.min_access_size = 1,
9049ab747fSPaolo Bonzini     .impl.max_access_size = 1,
9149ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
9249ab747fSPaolo Bonzini };
9349ab747fSPaolo Bonzini 
9449ab747fSPaolo Bonzini static uint64_t ich9_smi_readl(void *opaque, hwaddr addr, unsigned width)
9549ab747fSPaolo Bonzini {
9649ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
9749ab747fSPaolo Bonzini     switch (addr) {
9849ab747fSPaolo Bonzini     case 0:
9949ab747fSPaolo Bonzini         return pm->smi_en;
10049ab747fSPaolo Bonzini     case 4:
10149ab747fSPaolo Bonzini         return pm->smi_sts;
10249ab747fSPaolo Bonzini     default:
10349ab747fSPaolo Bonzini         return 0;
10449ab747fSPaolo Bonzini     }
10549ab747fSPaolo Bonzini }
10649ab747fSPaolo Bonzini 
10749ab747fSPaolo Bonzini static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
10849ab747fSPaolo Bonzini                             unsigned width)
10949ab747fSPaolo Bonzini {
11049ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
11149ab747fSPaolo Bonzini     switch (addr) {
11249ab747fSPaolo Bonzini     case 0:
11349ab747fSPaolo Bonzini         pm->smi_en = val;
11449ab747fSPaolo Bonzini         break;
11549ab747fSPaolo Bonzini     }
11649ab747fSPaolo Bonzini }
11749ab747fSPaolo Bonzini 
11849ab747fSPaolo Bonzini static const MemoryRegionOps ich9_smi_ops = {
11949ab747fSPaolo Bonzini     .read = ich9_smi_readl,
12049ab747fSPaolo Bonzini     .write = ich9_smi_writel,
12149ab747fSPaolo Bonzini     .valid.min_access_size = 4,
12249ab747fSPaolo Bonzini     .valid.max_access_size = 4,
12349ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
12449ab747fSPaolo Bonzini };
12549ab747fSPaolo Bonzini 
12649ab747fSPaolo Bonzini void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
12749ab747fSPaolo Bonzini {
12849ab747fSPaolo Bonzini     ICH9_DEBUG("to 0x%x\n", pm_io_base);
12949ab747fSPaolo Bonzini 
13049ab747fSPaolo Bonzini     assert((pm_io_base & ICH9_PMIO_MASK) == 0);
13149ab747fSPaolo Bonzini 
13249ab747fSPaolo Bonzini     pm->pm_io_base = pm_io_base;
13349ab747fSPaolo Bonzini     memory_region_transaction_begin();
13449ab747fSPaolo Bonzini     memory_region_set_enabled(&pm->io, pm->pm_io_base != 0);
13549ab747fSPaolo Bonzini     memory_region_set_address(&pm->io, pm->pm_io_base);
13649ab747fSPaolo Bonzini     memory_region_transaction_commit();
13749ab747fSPaolo Bonzini }
13849ab747fSPaolo Bonzini 
13949ab747fSPaolo Bonzini static int ich9_pm_post_load(void *opaque, int version_id)
14049ab747fSPaolo Bonzini {
14149ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
14249ab747fSPaolo Bonzini     uint32_t pm_io_base = pm->pm_io_base;
14349ab747fSPaolo Bonzini     pm->pm_io_base = 0;
14449ab747fSPaolo Bonzini     ich9_pm_iospace_update(pm, pm_io_base);
14549ab747fSPaolo Bonzini     return 0;
14649ab747fSPaolo Bonzini }
14749ab747fSPaolo Bonzini 
14849ab747fSPaolo Bonzini #define VMSTATE_GPE_ARRAY(_field, _state)                            \
14949ab747fSPaolo Bonzini  {                                                                   \
15049ab747fSPaolo Bonzini      .name       = (stringify(_field)),                              \
15149ab747fSPaolo Bonzini      .version_id = 0,                                                \
15249ab747fSPaolo Bonzini      .num        = ICH9_PMIO_GPE0_LEN,                               \
15349ab747fSPaolo Bonzini      .info       = &vmstate_info_uint8,                              \
15449ab747fSPaolo Bonzini      .size       = sizeof(uint8_t),                                  \
15549ab747fSPaolo Bonzini      .flags      = VMS_ARRAY | VMS_POINTER,                          \
15649ab747fSPaolo Bonzini      .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
15749ab747fSPaolo Bonzini  }
15849ab747fSPaolo Bonzini 
15949ab747fSPaolo Bonzini const VMStateDescription vmstate_ich9_pm = {
16049ab747fSPaolo Bonzini     .name = "ich9_pm",
16149ab747fSPaolo Bonzini     .version_id = 1,
16249ab747fSPaolo Bonzini     .minimum_version_id = 1,
16349ab747fSPaolo Bonzini     .minimum_version_id_old = 1,
16449ab747fSPaolo Bonzini     .post_load = ich9_pm_post_load,
16549ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
16649ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs),
16749ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs),
16849ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs),
16949ab747fSPaolo Bonzini         VMSTATE_TIMER(acpi_regs.tmr.timer, ICH9LPCPMRegs),
17049ab747fSPaolo Bonzini         VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs),
17149ab747fSPaolo Bonzini         VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs),
17249ab747fSPaolo Bonzini         VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs),
17349ab747fSPaolo Bonzini         VMSTATE_UINT32(smi_en, ICH9LPCPMRegs),
17449ab747fSPaolo Bonzini         VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
17549ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
17649ab747fSPaolo Bonzini     }
17749ab747fSPaolo Bonzini };
17849ab747fSPaolo Bonzini 
17949ab747fSPaolo Bonzini static void pm_reset(void *opaque)
18049ab747fSPaolo Bonzini {
18149ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
18249ab747fSPaolo Bonzini     ich9_pm_iospace_update(pm, 0);
18349ab747fSPaolo Bonzini 
18449ab747fSPaolo Bonzini     acpi_pm1_evt_reset(&pm->acpi_regs);
18549ab747fSPaolo Bonzini     acpi_pm1_cnt_reset(&pm->acpi_regs);
18649ab747fSPaolo Bonzini     acpi_pm_tmr_reset(&pm->acpi_regs);
18749ab747fSPaolo Bonzini     acpi_gpe_reset(&pm->acpi_regs);
18849ab747fSPaolo Bonzini 
18949ab747fSPaolo Bonzini     if (kvm_enabled()) {
19049ab747fSPaolo Bonzini         /* Mark SMM as already inited to prevent SMM from running. KVM does not
19149ab747fSPaolo Bonzini          * support SMM mode. */
19249ab747fSPaolo Bonzini         pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
19349ab747fSPaolo Bonzini     }
19449ab747fSPaolo Bonzini 
19549ab747fSPaolo Bonzini     pm_update_sci(pm);
19649ab747fSPaolo Bonzini }
19749ab747fSPaolo Bonzini 
19849ab747fSPaolo Bonzini static void pm_powerdown_req(Notifier *n, void *opaque)
19949ab747fSPaolo Bonzini {
20049ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier);
20149ab747fSPaolo Bonzini 
20249ab747fSPaolo Bonzini     acpi_pm1_evt_power_down(&pm->acpi_regs);
20349ab747fSPaolo Bonzini }
20449ab747fSPaolo Bonzini 
20549ab747fSPaolo Bonzini void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
206a3ac6b53SHu Tao                   qemu_irq sci_irq)
20749ab747fSPaolo Bonzini {
208*64bde0f3SPaolo Bonzini     memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
20949ab747fSPaolo Bonzini     memory_region_set_enabled(&pm->io, false);
21049ab747fSPaolo Bonzini     memory_region_add_subregion(pci_address_space_io(lpc_pci),
21149ab747fSPaolo Bonzini                                 0, &pm->io);
21249ab747fSPaolo Bonzini 
21349ab747fSPaolo Bonzini     acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
21449ab747fSPaolo Bonzini     acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
21549ab747fSPaolo Bonzini     acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, 2);
21649ab747fSPaolo Bonzini 
21749ab747fSPaolo Bonzini     acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN);
218*64bde0f3SPaolo Bonzini     memory_region_init_io(&pm->io_gpe, OBJECT(lpc_pci), &ich9_gpe_ops, pm,
219*64bde0f3SPaolo Bonzini                           "apci-gpe0", ICH9_PMIO_GPE0_LEN);
22049ab747fSPaolo Bonzini     memory_region_add_subregion(&pm->io, ICH9_PMIO_GPE0_STS, &pm->io_gpe);
22149ab747fSPaolo Bonzini 
222*64bde0f3SPaolo Bonzini     memory_region_init_io(&pm->io_smi, OBJECT(lpc_pci), &ich9_smi_ops, pm,
223*64bde0f3SPaolo Bonzini                           "apci-smi", 8);
22449ab747fSPaolo Bonzini     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
22549ab747fSPaolo Bonzini 
22649ab747fSPaolo Bonzini     pm->irq = sci_irq;
22749ab747fSPaolo Bonzini     qemu_register_reset(pm_reset, pm);
22849ab747fSPaolo Bonzini     pm->powerdown_notifier.notify = pm_powerdown_req;
22949ab747fSPaolo Bonzini     qemu_register_powerdown_notifier(&pm->powerdown_notifier);
23049ab747fSPaolo Bonzini }
231