xref: /openbmc/qemu/hw/acpi/ich9.c (revision fbae27e8)
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
1361f3c91aSChetan Pant  * License version 2.1 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  */
2671e8a915SMarkus Armbruster 
27b6a0aa05SPeter Maydell #include "qemu/osdep.h"
28da34e65cSMarkus Armbruster #include "qapi/error.h"
296f1426abSMichael S. Tsirkin #include "qapi/visitor.h"
3049ab747fSPaolo Bonzini #include "hw/pci/pci.h"
31d6454270SMarkus Armbruster #include "migration/vmstate.h"
3249ab747fSPaolo Bonzini #include "qemu/timer.h"
332e5b09fdSMarkus Armbruster #include "hw/core/cpu.h"
3471e8a915SMarkus Armbruster #include "sysemu/reset.h"
3554d31236SMarkus Armbruster #include "sysemu/runstate.h"
3649ab747fSPaolo Bonzini #include "hw/acpi/acpi.h"
37*fbae27e8SPhilippe Mathieu-Daudé #include "hw/acpi/ich9_tco.h"
3849ab747fSPaolo Bonzini 
3949ab747fSPaolo Bonzini #include "hw/i386/ich9.h"
401f862184SIgor Mammedov #include "hw/mem/pc-dimm.h"
41132a908bSPhilippe Mathieu-Daudé #include "hw/mem/nvdimm.h"
4249ab747fSPaolo Bonzini 
4349ab747fSPaolo Bonzini //#define DEBUG
4449ab747fSPaolo Bonzini 
4549ab747fSPaolo Bonzini #ifdef DEBUG
4649ab747fSPaolo Bonzini #define ICH9_DEBUG(fmt, ...) \
4749ab747fSPaolo Bonzini do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
4849ab747fSPaolo Bonzini #else
4949ab747fSPaolo Bonzini #define ICH9_DEBUG(fmt, ...)    do { } while (0)
5049ab747fSPaolo Bonzini #endif
5149ab747fSPaolo Bonzini 
5249ab747fSPaolo Bonzini static void ich9_pm_update_sci_fn(ACPIREGS *regs)
5349ab747fSPaolo Bonzini {
5449ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs);
5506313503SIgor Mammedov     acpi_update_sci(&pm->acpi_regs, pm->irq);
5649ab747fSPaolo Bonzini }
5749ab747fSPaolo Bonzini 
5849ab747fSPaolo Bonzini static uint64_t ich9_gpe_readb(void *opaque, hwaddr addr, unsigned width)
5949ab747fSPaolo Bonzini {
6049ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
6149ab747fSPaolo Bonzini     return acpi_gpe_ioport_readb(&pm->acpi_regs, addr);
6249ab747fSPaolo Bonzini }
6349ab747fSPaolo Bonzini 
6449ab747fSPaolo Bonzini static void ich9_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
6549ab747fSPaolo Bonzini                             unsigned width)
6649ab747fSPaolo Bonzini {
6749ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
6849ab747fSPaolo Bonzini     acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
692c047956SIgor Mammedov     acpi_update_sci(&pm->acpi_regs, pm->irq);
7049ab747fSPaolo Bonzini }
7149ab747fSPaolo Bonzini 
7249ab747fSPaolo Bonzini static const MemoryRegionOps ich9_gpe_ops = {
7349ab747fSPaolo Bonzini     .read = ich9_gpe_readb,
7449ab747fSPaolo Bonzini     .write = ich9_gpe_writeb,
7549ab747fSPaolo Bonzini     .valid.min_access_size = 1,
7649ab747fSPaolo Bonzini     .valid.max_access_size = 4,
7749ab747fSPaolo Bonzini     .impl.min_access_size = 1,
7849ab747fSPaolo Bonzini     .impl.max_access_size = 1,
7949ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
8049ab747fSPaolo Bonzini };
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini static uint64_t ich9_smi_readl(void *opaque, hwaddr addr, unsigned width)
8349ab747fSPaolo Bonzini {
8449ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
8549ab747fSPaolo Bonzini     switch (addr) {
8649ab747fSPaolo Bonzini     case 0:
8749ab747fSPaolo Bonzini         return pm->smi_en;
8849ab747fSPaolo Bonzini     case 4:
8949ab747fSPaolo Bonzini         return pm->smi_sts;
9049ab747fSPaolo Bonzini     default:
9149ab747fSPaolo Bonzini         return 0;
9249ab747fSPaolo Bonzini     }
9349ab747fSPaolo Bonzini }
9449ab747fSPaolo Bonzini 
9549ab747fSPaolo Bonzini static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
9649ab747fSPaolo Bonzini                             unsigned width)
9749ab747fSPaolo Bonzini {
9849ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
9992055797SPaulo Alcantara     TCOIORegs *tr = &pm->tco_regs;
10092055797SPaulo Alcantara     uint64_t tco_en;
10192055797SPaulo Alcantara 
10249ab747fSPaolo Bonzini     switch (addr) {
10349ab747fSPaolo Bonzini     case 0:
10492055797SPaulo Alcantara         tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
10592055797SPaulo Alcantara         /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
10692055797SPaulo Alcantara         if (tr->tco.cnt1 & TCO_LOCK) {
10792055797SPaulo Alcantara             val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
10892055797SPaulo Alcantara         }
10911e66a15SGerd Hoffmann         pm->smi_en &= ~pm->smi_en_wmask;
11011e66a15SGerd Hoffmann         pm->smi_en |= (val & pm->smi_en_wmask);
11149ab747fSPaolo Bonzini         break;
11249ab747fSPaolo Bonzini     }
11349ab747fSPaolo Bonzini }
11449ab747fSPaolo Bonzini 
11549ab747fSPaolo Bonzini static const MemoryRegionOps ich9_smi_ops = {
11649ab747fSPaolo Bonzini     .read = ich9_smi_readl,
11749ab747fSPaolo Bonzini     .write = ich9_smi_writel,
11849ab747fSPaolo Bonzini     .valid.min_access_size = 4,
11949ab747fSPaolo Bonzini     .valid.max_access_size = 4,
12049ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
12149ab747fSPaolo Bonzini };
12249ab747fSPaolo Bonzini 
12349ab747fSPaolo Bonzini void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
12449ab747fSPaolo Bonzini {
12549ab747fSPaolo Bonzini     ICH9_DEBUG("to 0x%x\n", pm_io_base);
12649ab747fSPaolo Bonzini 
12749ab747fSPaolo Bonzini     assert((pm_io_base & ICH9_PMIO_MASK) == 0);
12849ab747fSPaolo Bonzini 
12949ab747fSPaolo Bonzini     pm->pm_io_base = pm_io_base;
13049ab747fSPaolo Bonzini     memory_region_transaction_begin();
13149ab747fSPaolo Bonzini     memory_region_set_enabled(&pm->io, pm->pm_io_base != 0);
13249ab747fSPaolo Bonzini     memory_region_set_address(&pm->io, pm->pm_io_base);
13349ab747fSPaolo Bonzini     memory_region_transaction_commit();
13449ab747fSPaolo Bonzini }
13549ab747fSPaolo Bonzini 
13649ab747fSPaolo Bonzini static int ich9_pm_post_load(void *opaque, int version_id)
13749ab747fSPaolo Bonzini {
13849ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
13949ab747fSPaolo Bonzini     uint32_t pm_io_base = pm->pm_io_base;
14049ab747fSPaolo Bonzini     pm->pm_io_base = 0;
14149ab747fSPaolo Bonzini     ich9_pm_iospace_update(pm, pm_io_base);
14249ab747fSPaolo Bonzini     return 0;
14349ab747fSPaolo Bonzini }
14449ab747fSPaolo Bonzini 
14549ab747fSPaolo Bonzini #define VMSTATE_GPE_ARRAY(_field, _state)                            \
14649ab747fSPaolo Bonzini  {                                                                   \
14749ab747fSPaolo Bonzini      .name       = (stringify(_field)),                              \
14849ab747fSPaolo Bonzini      .version_id = 0,                                                \
14949ab747fSPaolo Bonzini      .num        = ICH9_PMIO_GPE0_LEN,                               \
15049ab747fSPaolo Bonzini      .info       = &vmstate_info_uint8,                              \
15149ab747fSPaolo Bonzini      .size       = sizeof(uint8_t),                                  \
15249ab747fSPaolo Bonzini      .flags      = VMS_ARRAY | VMS_POINTER,                          \
15349ab747fSPaolo Bonzini      .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
15449ab747fSPaolo Bonzini  }
15549ab747fSPaolo Bonzini 
156f816a62dSIgor Mammedov static bool vmstate_test_use_memhp(void *opaque)
157f816a62dSIgor Mammedov {
158f816a62dSIgor Mammedov     ICH9LPCPMRegs *s = opaque;
159f816a62dSIgor Mammedov     return s->acpi_memory_hotplug.is_enabled;
160f816a62dSIgor Mammedov }
161f816a62dSIgor Mammedov 
162f816a62dSIgor Mammedov static const VMStateDescription vmstate_memhp_state = {
163f816a62dSIgor Mammedov     .name = "ich9_pm/memhp",
164f816a62dSIgor Mammedov     .version_id = 1,
165f816a62dSIgor Mammedov     .minimum_version_id = 1,
1665cd8cadaSJuan Quintela     .needed = vmstate_test_use_memhp,
167f816a62dSIgor Mammedov     .fields      = (VMStateField[]) {
168f816a62dSIgor Mammedov         VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs),
169f816a62dSIgor Mammedov         VMSTATE_END_OF_LIST()
170f816a62dSIgor Mammedov     }
171f816a62dSIgor Mammedov };
172f816a62dSIgor Mammedov 
17392055797SPaulo Alcantara static bool vmstate_test_use_tco(void *opaque)
17492055797SPaulo Alcantara {
17592055797SPaulo Alcantara     ICH9LPCPMRegs *s = opaque;
17692055797SPaulo Alcantara     return s->enable_tco;
17792055797SPaulo Alcantara }
17892055797SPaulo Alcantara 
17992055797SPaulo Alcantara static const VMStateDescription vmstate_tco_io_state = {
18092055797SPaulo Alcantara     .name = "ich9_pm/tco",
18192055797SPaulo Alcantara     .version_id = 1,
18292055797SPaulo Alcantara     .minimum_version_id = 1,
18392055797SPaulo Alcantara     .needed = vmstate_test_use_tco,
18492055797SPaulo Alcantara     .fields      = (VMStateField[]) {
18592055797SPaulo Alcantara         VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
18692055797SPaulo Alcantara                        TCOIORegs),
18792055797SPaulo Alcantara         VMSTATE_END_OF_LIST()
18892055797SPaulo Alcantara     }
18992055797SPaulo Alcantara };
19092055797SPaulo Alcantara 
191679dd1a9SIgor Mammedov static bool vmstate_test_use_cpuhp(void *opaque)
192679dd1a9SIgor Mammedov {
193679dd1a9SIgor Mammedov     ICH9LPCPMRegs *s = opaque;
194679dd1a9SIgor Mammedov     return !s->cpu_hotplug_legacy;
195679dd1a9SIgor Mammedov }
196679dd1a9SIgor Mammedov 
197679dd1a9SIgor Mammedov static int vmstate_cpuhp_pre_load(void *opaque)
198679dd1a9SIgor Mammedov {
199679dd1a9SIgor Mammedov     ICH9LPCPMRegs *s = opaque;
200679dd1a9SIgor Mammedov     Object *obj = OBJECT(s->gpe_cpu.device);
2015325cc34SMarkus Armbruster     object_property_set_bool(obj, "cpu-hotplug-legacy", false, &error_abort);
202679dd1a9SIgor Mammedov     return 0;
203679dd1a9SIgor Mammedov }
204679dd1a9SIgor Mammedov 
205679dd1a9SIgor Mammedov static const VMStateDescription vmstate_cpuhp_state = {
206679dd1a9SIgor Mammedov     .name = "ich9_pm/cpuhp",
207679dd1a9SIgor Mammedov     .version_id = 1,
208679dd1a9SIgor Mammedov     .minimum_version_id = 1,
209679dd1a9SIgor Mammedov     .needed = vmstate_test_use_cpuhp,
210679dd1a9SIgor Mammedov     .pre_load = vmstate_cpuhp_pre_load,
211679dd1a9SIgor Mammedov     .fields      = (VMStateField[]) {
212679dd1a9SIgor Mammedov         VMSTATE_CPU_HOTPLUG(cpuhp_state, ICH9LPCPMRegs),
213679dd1a9SIgor Mammedov         VMSTATE_END_OF_LIST()
214679dd1a9SIgor Mammedov     }
215679dd1a9SIgor Mammedov };
216679dd1a9SIgor Mammedov 
217c0e427d6SJulia Suvorova static bool vmstate_test_use_pcihp(void *opaque)
218c0e427d6SJulia Suvorova {
219c0e427d6SJulia Suvorova     ICH9LPCPMRegs *s = opaque;
220c0e427d6SJulia Suvorova 
221c0e427d6SJulia Suvorova     return s->use_acpi_hotplug_bridge;
222c0e427d6SJulia Suvorova }
223c0e427d6SJulia Suvorova 
224c0e427d6SJulia Suvorova static const VMStateDescription vmstate_pcihp_state = {
225c0e427d6SJulia Suvorova     .name = "ich9_pm/pcihp",
226c0e427d6SJulia Suvorova     .version_id = 1,
227c0e427d6SJulia Suvorova     .minimum_version_id = 1,
228c0e427d6SJulia Suvorova     .needed = vmstate_test_use_pcihp,
229c0e427d6SJulia Suvorova     .fields      = (VMStateField[]) {
230c0e427d6SJulia Suvorova         VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug,
231c0e427d6SJulia Suvorova                             ICH9LPCPMRegs,
232c0e427d6SJulia Suvorova                             NULL, NULL),
233c0e427d6SJulia Suvorova         VMSTATE_END_OF_LIST()
234c0e427d6SJulia Suvorova     }
235c0e427d6SJulia Suvorova };
236c0e427d6SJulia Suvorova 
23749ab747fSPaolo Bonzini const VMStateDescription vmstate_ich9_pm = {
23849ab747fSPaolo Bonzini     .name = "ich9_pm",
23949ab747fSPaolo Bonzini     .version_id = 1,
24049ab747fSPaolo Bonzini     .minimum_version_id = 1,
24149ab747fSPaolo Bonzini     .post_load = ich9_pm_post_load,
24249ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
24349ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs),
24449ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs),
24549ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs),
246e720677eSPaolo Bonzini         VMSTATE_TIMER_PTR(acpi_regs.tmr.timer, ICH9LPCPMRegs),
24749ab747fSPaolo Bonzini         VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs),
24849ab747fSPaolo Bonzini         VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs),
24949ab747fSPaolo Bonzini         VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs),
25049ab747fSPaolo Bonzini         VMSTATE_UINT32(smi_en, ICH9LPCPMRegs),
25149ab747fSPaolo Bonzini         VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
25249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
253f816a62dSIgor Mammedov     },
2545cd8cadaSJuan Quintela     .subsections = (const VMStateDescription*[]) {
2555cd8cadaSJuan Quintela         &vmstate_memhp_state,
25692055797SPaulo Alcantara         &vmstate_tco_io_state,
257679dd1a9SIgor Mammedov         &vmstate_cpuhp_state,
258c0e427d6SJulia Suvorova         &vmstate_pcihp_state,
25992055797SPaulo Alcantara         NULL
26049ab747fSPaolo Bonzini     }
26149ab747fSPaolo Bonzini };
26249ab747fSPaolo Bonzini 
26349ab747fSPaolo Bonzini static void pm_reset(void *opaque)
26449ab747fSPaolo Bonzini {
26549ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
26649ab747fSPaolo Bonzini     ich9_pm_iospace_update(pm, 0);
26749ab747fSPaolo Bonzini 
26849ab747fSPaolo Bonzini     acpi_pm1_evt_reset(&pm->acpi_regs);
26949ab747fSPaolo Bonzini     acpi_pm1_cnt_reset(&pm->acpi_regs);
27049ab747fSPaolo Bonzini     acpi_pm_tmr_reset(&pm->acpi_regs);
27149ab747fSPaolo Bonzini     acpi_gpe_reset(&pm->acpi_regs);
27249ab747fSPaolo Bonzini 
273be66680eSLaszlo Ersek     pm->smi_en = 0;
274fba72476SPaolo Bonzini     if (!pm->smm_enabled) {
275f3c30aeaSLaszlo Ersek         /* Mark SMM as already inited to prevent SMM from running. */
27649ab747fSPaolo Bonzini         pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
27749ab747fSPaolo Bonzini     }
27811e66a15SGerd Hoffmann     pm->smi_en_wmask = ~0;
27949ab747fSPaolo Bonzini 
280c0e427d6SJulia Suvorova     if (pm->use_acpi_hotplug_bridge) {
281c0e427d6SJulia Suvorova         acpi_pcihp_reset(&pm->acpi_pci_hotplug, true);
282c0e427d6SJulia Suvorova     }
283c0e427d6SJulia Suvorova 
28406313503SIgor Mammedov     acpi_update_sci(&pm->acpi_regs, pm->irq);
28549ab747fSPaolo Bonzini }
28649ab747fSPaolo Bonzini 
28749ab747fSPaolo Bonzini static void pm_powerdown_req(Notifier *n, void *opaque)
28849ab747fSPaolo Bonzini {
28949ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier);
29049ab747fSPaolo Bonzini 
29149ab747fSPaolo Bonzini     acpi_pm1_evt_power_down(&pm->acpi_regs);
29249ab747fSPaolo Bonzini }
29349ab747fSPaolo Bonzini 
29492055797SPaulo Alcantara void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
29518d6abaeSEduardo Habkost                   bool smm_enabled,
296a3ac6b53SHu Tao                   qemu_irq sci_irq)
29749ab747fSPaolo Bonzini {
29864bde0f3SPaolo Bonzini     memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
29949ab747fSPaolo Bonzini     memory_region_set_enabled(&pm->io, false);
30049ab747fSPaolo Bonzini     memory_region_add_subregion(pci_address_space_io(lpc_pci),
30149ab747fSPaolo Bonzini                                 0, &pm->io);
30249ab747fSPaolo Bonzini 
30349ab747fSPaolo Bonzini     acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
30449ab747fSPaolo Bonzini     acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
3059a10bbb4SLaszlo Ersek     acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->disable_s3, pm->disable_s4,
3066be8cf56SIsaku Yamahata                       pm->s4_val, !pm->smm_compat && !smm_enabled);
30749ab747fSPaolo Bonzini 
30849ab747fSPaolo Bonzini     acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN);
30964bde0f3SPaolo Bonzini     memory_region_init_io(&pm->io_gpe, OBJECT(lpc_pci), &ich9_gpe_ops, pm,
31075902802SHu Tao                           "acpi-gpe0", ICH9_PMIO_GPE0_LEN);
31149ab747fSPaolo Bonzini     memory_region_add_subregion(&pm->io, ICH9_PMIO_GPE0_STS, &pm->io_gpe);
31249ab747fSPaolo Bonzini 
31364bde0f3SPaolo Bonzini     memory_region_init_io(&pm->io_smi, OBJECT(lpc_pci), &ich9_smi_ops, pm,
31475902802SHu Tao                           "acpi-smi", 8);
31549ab747fSPaolo Bonzini     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
31649ab747fSPaolo Bonzini 
317fba72476SPaolo Bonzini     pm->smm_enabled = smm_enabled;
31892055797SPaulo Alcantara 
319ee1c08bdSDaniel P. Berrangé     if (pm->enable_tco) {
32092055797SPaulo Alcantara         acpi_pm_tco_init(&pm->tco_regs, &pm->io);
321ee1c08bdSDaniel P. Berrangé     }
32292055797SPaulo Alcantara 
323c0e427d6SJulia Suvorova     if (pm->use_acpi_hotplug_bridge) {
324c0e427d6SJulia Suvorova         acpi_pcihp_init(OBJECT(lpc_pci),
325c0e427d6SJulia Suvorova                         &pm->acpi_pci_hotplug,
326c0e427d6SJulia Suvorova                         pci_get_bus(lpc_pci),
327c0e427d6SJulia Suvorova                         pci_address_space_io(lpc_pci),
328c0e427d6SJulia Suvorova                         true,
329c0e427d6SJulia Suvorova                         ACPI_PCIHP_ADDR_ICH9);
330c0e427d6SJulia Suvorova 
331c0e427d6SJulia Suvorova         qbus_set_hotplug_handler(BUS(pci_get_bus(lpc_pci)),
332c0e427d6SJulia Suvorova                                  OBJECT(lpc_pci));
333c0e427d6SJulia Suvorova     }
334c0e427d6SJulia Suvorova 
33549ab747fSPaolo Bonzini     pm->irq = sci_irq;
33649ab747fSPaolo Bonzini     qemu_register_reset(pm_reset, pm);
33749ab747fSPaolo Bonzini     pm->powerdown_notifier.notify = pm_powerdown_req;
33849ab747fSPaolo Bonzini     qemu_register_powerdown_notifier(&pm->powerdown_notifier);
339d6610bc2SIgor Mammedov 
34096e3e12bSIgor Mammedov     legacy_acpi_cpu_hotplug_init(pci_address_space_io(lpc_pci),
34196e3e12bSIgor Mammedov         OBJECT(lpc_pci), &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE);
3421f862184SIgor Mammedov 
3431f862184SIgor Mammedov     if (pm->acpi_memory_hotplug.is_enabled) {
3441f862184SIgor Mammedov         acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci),
34580db0e78SIgor Mammedov                                  &pm->acpi_memory_hotplug,
34680db0e78SIgor Mammedov                                  ACPI_MEMORY_HOTPLUG_BASE);
3471f862184SIgor Mammedov     }
34849ab747fSPaolo Bonzini }
3496f1426abSMichael S. Tsirkin 
350d7bce999SEric Blake static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name,
351d7bce999SEric Blake                                  void *opaque, Error **errp)
3526f1426abSMichael S. Tsirkin {
3536f1426abSMichael S. Tsirkin     ICH9LPCPMRegs *pm = opaque;
3546f1426abSMichael S. Tsirkin     uint32_t value = pm->pm_io_base + ICH9_PMIO_GPE0_STS;
3556f1426abSMichael S. Tsirkin 
35651e72bc1SEric Blake     visit_type_uint32(v, name, &value, errp);
3576f1426abSMichael S. Tsirkin }
3586f1426abSMichael S. Tsirkin 
3591f862184SIgor Mammedov static bool ich9_pm_get_memory_hotplug_support(Object *obj, Error **errp)
3601f862184SIgor Mammedov {
3611f862184SIgor Mammedov     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
3621f862184SIgor Mammedov 
3631f862184SIgor Mammedov     return s->pm.acpi_memory_hotplug.is_enabled;
3641f862184SIgor Mammedov }
3651f862184SIgor Mammedov 
3661f862184SIgor Mammedov static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value,
3671f862184SIgor Mammedov                                                Error **errp)
3681f862184SIgor Mammedov {
3691f862184SIgor Mammedov     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
3701f862184SIgor Mammedov 
3711f862184SIgor Mammedov     s->pm.acpi_memory_hotplug.is_enabled = value;
3721f862184SIgor Mammedov }
3731f862184SIgor Mammedov 
37416bcab97SIgor Mammedov static bool ich9_pm_get_cpu_hotplug_legacy(Object *obj, Error **errp)
37516bcab97SIgor Mammedov {
37616bcab97SIgor Mammedov     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
37716bcab97SIgor Mammedov 
37816bcab97SIgor Mammedov     return s->pm.cpu_hotplug_legacy;
37916bcab97SIgor Mammedov }
38016bcab97SIgor Mammedov 
38116bcab97SIgor Mammedov static void ich9_pm_set_cpu_hotplug_legacy(Object *obj, bool value,
38216bcab97SIgor Mammedov                                            Error **errp)
38316bcab97SIgor Mammedov {
38416bcab97SIgor Mammedov     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
38516bcab97SIgor Mammedov 
386679dd1a9SIgor Mammedov     assert(!value);
387679dd1a9SIgor Mammedov     if (s->pm.cpu_hotplug_legacy && value == false) {
388679dd1a9SIgor Mammedov         acpi_switch_to_modern_cphp(&s->pm.gpe_cpu, &s->pm.cpuhp_state,
389679dd1a9SIgor Mammedov                                    ICH9_CPU_HOTPLUG_IO_BASE);
390679dd1a9SIgor Mammedov     }
39116bcab97SIgor Mammedov     s->pm.cpu_hotplug_legacy = value;
39216bcab97SIgor Mammedov }
39316bcab97SIgor Mammedov 
39492055797SPaulo Alcantara static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
39592055797SPaulo Alcantara {
39692055797SPaulo Alcantara     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
39792055797SPaulo Alcantara     return s->pm.enable_tco;
39892055797SPaulo Alcantara }
39992055797SPaulo Alcantara 
40092055797SPaulo Alcantara static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
40192055797SPaulo Alcantara {
40292055797SPaulo Alcantara     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
40392055797SPaulo Alcantara     s->pm.enable_tco = value;
40492055797SPaulo Alcantara }
40592055797SPaulo Alcantara 
406c0e427d6SJulia Suvorova static bool ich9_pm_get_acpi_pci_hotplug(Object *obj, Error **errp)
407c0e427d6SJulia Suvorova {
408c0e427d6SJulia Suvorova     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
409c0e427d6SJulia Suvorova 
410c0e427d6SJulia Suvorova     return s->pm.use_acpi_hotplug_bridge;
411c0e427d6SJulia Suvorova }
412c0e427d6SJulia Suvorova 
413c0e427d6SJulia Suvorova static void ich9_pm_set_acpi_pci_hotplug(Object *obj, bool value, Error **errp)
414c0e427d6SJulia Suvorova {
415c0e427d6SJulia Suvorova     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
416c0e427d6SJulia Suvorova 
417c0e427d6SJulia Suvorova     s->pm.use_acpi_hotplug_bridge = value;
418c0e427d6SJulia Suvorova }
419c0e427d6SJulia Suvorova 
420c318bef7SJulia Suvorova static bool ich9_pm_get_keep_pci_slot_hpc(Object *obj, Error **errp)
421c318bef7SJulia Suvorova {
422c318bef7SJulia Suvorova     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
423c318bef7SJulia Suvorova 
424c318bef7SJulia Suvorova     return s->pm.keep_pci_slot_hpc;
425c318bef7SJulia Suvorova }
426c318bef7SJulia Suvorova 
427c318bef7SJulia Suvorova static void ich9_pm_set_keep_pci_slot_hpc(Object *obj, bool value, Error **errp)
428c318bef7SJulia Suvorova {
429c318bef7SJulia Suvorova     ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
430c318bef7SJulia Suvorova 
431c318bef7SJulia Suvorova     s->pm.keep_pci_slot_hpc = value;
432c318bef7SJulia Suvorova }
433c318bef7SJulia Suvorova 
43440c2281cSMarkus Armbruster void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm)
4356f1426abSMichael S. Tsirkin {
4366f1426abSMichael S. Tsirkin     static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
4371f862184SIgor Mammedov     pm->acpi_memory_hotplug.is_enabled = true;
43816bcab97SIgor Mammedov     pm->cpu_hotplug_legacy = true;
4396ac0d8d4SAmit Shah     pm->disable_s3 = 0;
4406ac0d8d4SAmit Shah     pm->disable_s4 = 0;
4416ac0d8d4SAmit Shah     pm->s4_val = 2;
44217858a16SJulia Suvorova     pm->use_acpi_hotplug_bridge = true;
443c318bef7SJulia Suvorova     pm->keep_pci_slot_hpc = true;
444ee1c08bdSDaniel P. Berrangé     pm->enable_tco = true;
4456f1426abSMichael S. Tsirkin 
4466f1426abSMichael S. Tsirkin     object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE,
447d2623129SMarkus Armbruster                                    &pm->pm_io_base, OBJ_PROP_FLAG_READ);
4486f1426abSMichael S. Tsirkin     object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32",
4496f1426abSMichael S. Tsirkin                         ich9_pm_get_gpe0_blk,
450d2623129SMarkus Armbruster                         NULL, NULL, pm);
4516f1426abSMichael S. Tsirkin     object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN,
452d2623129SMarkus Armbruster                                    &gpe0_len, OBJ_PROP_FLAG_READ);
4531f862184SIgor Mammedov     object_property_add_bool(obj, "memory-hotplug-support",
4541f862184SIgor Mammedov                              ich9_pm_get_memory_hotplug_support,
455d2623129SMarkus Armbruster                              ich9_pm_set_memory_hotplug_support);
45616bcab97SIgor Mammedov     object_property_add_bool(obj, "cpu-hotplug-legacy",
45716bcab97SIgor Mammedov                              ich9_pm_get_cpu_hotplug_legacy,
458d2623129SMarkus Armbruster                              ich9_pm_set_cpu_hotplug_legacy);
45964a7b8deSFelipe Franciosi     object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S3_DISABLED,
460d2623129SMarkus Armbruster                                   &pm->disable_s3, OBJ_PROP_FLAG_READWRITE);
46164a7b8deSFelipe Franciosi     object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S4_DISABLED,
462d2623129SMarkus Armbruster                                   &pm->disable_s4, OBJ_PROP_FLAG_READWRITE);
46364a7b8deSFelipe Franciosi     object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S4_VAL,
464d2623129SMarkus Armbruster                                   &pm->s4_val, OBJ_PROP_FLAG_READWRITE);
46592055797SPaulo Alcantara     object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
46692055797SPaulo Alcantara                              ich9_pm_get_enable_tco,
467d2623129SMarkus Armbruster                              ich9_pm_set_enable_tco);
468aa29466bSAni Sinha     object_property_add_bool(obj, ACPI_PM_PROP_ACPI_PCIHP_BRIDGE,
469c0e427d6SJulia Suvorova                              ich9_pm_get_acpi_pci_hotplug,
470c0e427d6SJulia Suvorova                              ich9_pm_set_acpi_pci_hotplug);
471c318bef7SJulia Suvorova     object_property_add_bool(obj, "x-keep-pci-slot-hpc",
472c318bef7SJulia Suvorova                              ich9_pm_get_keep_pci_slot_hpc,
473c318bef7SJulia Suvorova                              ich9_pm_set_keep_pci_slot_hpc);
4741f862184SIgor Mammedov }
4751f862184SIgor Mammedov 
4769040e6dfSWei Yang void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
4779040e6dfSWei Yang                                 Error **errp)
4789040e6dfSWei Yang {
4799040e6dfSWei Yang     ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev);
4809040e6dfSWei Yang 
481c0e427d6SJulia Suvorova     if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
482c0e427d6SJulia Suvorova         acpi_pcihp_device_pre_plug_cb(hotplug_dev, dev, errp);
483c0e427d6SJulia Suvorova         return;
484c0e427d6SJulia Suvorova     }
485c0e427d6SJulia Suvorova 
4869040e6dfSWei Yang     if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
487c5be7517SIgor Mammedov         !lpc->pm.acpi_memory_hotplug.is_enabled) {
4889040e6dfSWei Yang         error_setg(errp,
4899040e6dfSWei Yang                    "memory hotplug is not enabled: %s.memory-hotplug-support "
4909040e6dfSWei Yang                    "is not set", object_get_typename(OBJECT(lpc)));
491c5be7517SIgor Mammedov     } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
492c5be7517SIgor Mammedov         uint64_t negotiated = lpc->smi_negotiated_features;
493c5be7517SIgor Mammedov 
494c5be7517SIgor Mammedov         if (negotiated & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT) &&
495c5be7517SIgor Mammedov             !(negotiated & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT))) {
496c5be7517SIgor Mammedov             error_setg(errp, "cpu hotplug with SMI wasn't enabled by firmware");
497c5be7517SIgor Mammedov             error_append_hint(errp, "update machine type to newer than 5.1 "
498c5be7517SIgor Mammedov                 "and firmware that suppors CPU hotplug with SMM");
499c5be7517SIgor Mammedov         }
500c5be7517SIgor Mammedov     }
5019040e6dfSWei Yang }
5029040e6dfSWei Yang 
5030058c082SIgor Mammedov void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
5040058c082SIgor Mammedov                             Error **errp)
5051f862184SIgor Mammedov {
5060058c082SIgor Mammedov     ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev);
5070058c082SIgor Mammedov 
5089040e6dfSWei Yang     if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
50975f27498SXiao Guangrong         if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
51075f27498SXiao Guangrong             nvdimm_acpi_plug_cb(hotplug_dev, dev);
51175f27498SXiao Guangrong         } else {
5120058c082SIgor Mammedov             acpi_memory_plug_cb(hotplug_dev, &lpc->pm.acpi_memory_hotplug,
5131f862184SIgor Mammedov                                 dev, errp);
51475f27498SXiao Guangrong         }
5155e1b5d93SIgor Mammedov     } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
5165e1b5d93SIgor Mammedov         if (lpc->pm.cpu_hotplug_legacy) {
5170058c082SIgor Mammedov             legacy_acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.gpe_cpu, dev, errp);
5181f862184SIgor Mammedov         } else {
5195e1b5d93SIgor Mammedov             acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.cpuhp_state, dev, errp);
5205e1b5d93SIgor Mammedov         }
521c0e427d6SJulia Suvorova     } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
522c0e427d6SJulia Suvorova         acpi_pcihp_device_plug_cb(hotplug_dev, &lpc->pm.acpi_pci_hotplug,
523c0e427d6SJulia Suvorova                                   dev, errp);
5245e1b5d93SIgor Mammedov     } else {
5251f862184SIgor Mammedov         error_setg(errp, "acpi: device plug request for not supported device"
5261f862184SIgor Mammedov                    " type: %s", object_get_typename(OBJECT(dev)));
5271f862184SIgor Mammedov     }
5286f1426abSMichael S. Tsirkin }
52943f50410SIgor Mammedov 
5300058c082SIgor Mammedov void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev,
5310058c082SIgor Mammedov                                       DeviceState *dev, Error **errp)
532469b8ad2STang Chen {
5330058c082SIgor Mammedov     ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev);
5340058c082SIgor Mammedov 
5350058c082SIgor Mammedov     if (lpc->pm.acpi_memory_hotplug.is_enabled &&
53664fec58eSTang Chen         object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
5370058c082SIgor Mammedov         acpi_memory_unplug_request_cb(hotplug_dev,
5380058c082SIgor Mammedov                                       &lpc->pm.acpi_memory_hotplug, dev,
5390058c082SIgor Mammedov                                       errp);
5408872c25aSIgor Mammedov     } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
5418872c25aSIgor Mammedov                !lpc->pm.cpu_hotplug_legacy) {
542b48ad7c0SIgor Mammedov         uint64_t negotiated = lpc->smi_negotiated_features;
543b48ad7c0SIgor Mammedov 
544b48ad7c0SIgor Mammedov         if (negotiated & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT) &&
545b48ad7c0SIgor Mammedov             !(negotiated & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT))) {
546b48ad7c0SIgor Mammedov             error_setg(errp, "cpu hot-unplug with SMI wasn't enabled "
547b48ad7c0SIgor Mammedov                              "by firmware");
548b48ad7c0SIgor Mammedov             error_append_hint(errp, "update machine type to a version having "
549b48ad7c0SIgor Mammedov                                     "x-smi-cpu-hotunplug=on and firmware that "
550b48ad7c0SIgor Mammedov                                     "supports CPU hot-unplug with SMM");
551b48ad7c0SIgor Mammedov             return;
552b48ad7c0SIgor Mammedov         }
553b48ad7c0SIgor Mammedov 
5548872c25aSIgor Mammedov         acpi_cpu_unplug_request_cb(hotplug_dev, &lpc->pm.cpuhp_state,
5558872c25aSIgor Mammedov                                    dev, errp);
556c0e427d6SJulia Suvorova     } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
557c0e427d6SJulia Suvorova         acpi_pcihp_device_unplug_request_cb(hotplug_dev,
558c0e427d6SJulia Suvorova                                             &lpc->pm.acpi_pci_hotplug,
559c0e427d6SJulia Suvorova                                             dev, errp);
56064fec58eSTang Chen     } else {
561469b8ad2STang Chen         error_setg(errp, "acpi: device unplug request for not supported device"
562469b8ad2STang Chen                    " type: %s", object_get_typename(OBJECT(dev)));
563469b8ad2STang Chen     }
56464fec58eSTang Chen }
565469b8ad2STang Chen 
5660058c082SIgor Mammedov void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
56791a734a6STang Chen                               Error **errp)
56891a734a6STang Chen {
5690058c082SIgor Mammedov     ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev);
5700058c082SIgor Mammedov 
5710058c082SIgor Mammedov     if (lpc->pm.acpi_memory_hotplug.is_enabled &&
572f7d3e29dSTang Chen         object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
5730058c082SIgor Mammedov         acpi_memory_unplug_cb(&lpc->pm.acpi_memory_hotplug, dev, errp);
5748872c25aSIgor Mammedov     } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
5758872c25aSIgor Mammedov                !lpc->pm.cpu_hotplug_legacy) {
5768872c25aSIgor Mammedov         acpi_cpu_unplug_cb(&lpc->pm.cpuhp_state, dev, errp);
577c0e427d6SJulia Suvorova     } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
578c0e427d6SJulia Suvorova         acpi_pcihp_device_unplug_cb(hotplug_dev, &lpc->pm.acpi_pci_hotplug,
579c0e427d6SJulia Suvorova                                     dev, errp);
580f7d3e29dSTang Chen     } else {
58191a734a6STang Chen         error_setg(errp, "acpi: device unplug for not supported device"
58291a734a6STang Chen                    " type: %s", object_get_typename(OBJECT(dev)));
58391a734a6STang Chen     }
584f7d3e29dSTang Chen }
58591a734a6STang Chen 
58643f50410SIgor Mammedov void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
58743f50410SIgor Mammedov {
58843f50410SIgor Mammedov     ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
58943f50410SIgor Mammedov 
59043f50410SIgor Mammedov     acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list);
59176623d00SIgor Mammedov     if (!s->pm.cpu_hotplug_legacy) {
59276623d00SIgor Mammedov         acpi_cpu_ospm_status(&s->pm.cpuhp_state, list);
59376623d00SIgor Mammedov     }
59443f50410SIgor Mammedov }
595