xref: /openbmc/qemu/hw/acpi/ich9.c (revision 49ab747f)
1*49ab747fSPaolo Bonzini /*
2*49ab747fSPaolo Bonzini  * ACPI implementation
3*49ab747fSPaolo Bonzini  *
4*49ab747fSPaolo Bonzini  * Copyright (c) 2006 Fabrice Bellard
5*49ab747fSPaolo Bonzini  * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
6*49ab747fSPaolo Bonzini  *                    VA Linux Systems Japan K.K.
7*49ab747fSPaolo Bonzini  * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
8*49ab747fSPaolo Bonzini  *
9*49ab747fSPaolo Bonzini  * This is based on acpi.c.
10*49ab747fSPaolo Bonzini  *
11*49ab747fSPaolo Bonzini  * This library is free software; you can redistribute it and/or
12*49ab747fSPaolo Bonzini  * modify it under the terms of the GNU Lesser General Public
13*49ab747fSPaolo Bonzini  * License version 2 as published by the Free Software Foundation.
14*49ab747fSPaolo Bonzini  *
15*49ab747fSPaolo Bonzini  * This library is distributed in the hope that it will be useful,
16*49ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17*49ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18*49ab747fSPaolo Bonzini  * Lesser General Public License for more details.
19*49ab747fSPaolo Bonzini  *
20*49ab747fSPaolo Bonzini  * You should have received a copy of the GNU Lesser General Public
21*49ab747fSPaolo Bonzini  * License along with this library; if not, see <http://www.gnu.org/licenses/>
22*49ab747fSPaolo Bonzini  *
23*49ab747fSPaolo Bonzini  * Contributions after 2012-01-13 are licensed under the terms of the
24*49ab747fSPaolo Bonzini  * GNU GPL, version 2 or (at your option) any later version.
25*49ab747fSPaolo Bonzini  */
26*49ab747fSPaolo Bonzini #include "hw/hw.h"
27*49ab747fSPaolo Bonzini #include "hw/i386/pc.h"
28*49ab747fSPaolo Bonzini #include "hw/pci/pci.h"
29*49ab747fSPaolo Bonzini #include "qemu/timer.h"
30*49ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
31*49ab747fSPaolo Bonzini #include "hw/acpi/acpi.h"
32*49ab747fSPaolo Bonzini #include "sysemu/kvm.h"
33*49ab747fSPaolo Bonzini #include "exec/address-spaces.h"
34*49ab747fSPaolo Bonzini 
35*49ab747fSPaolo Bonzini #include "hw/i386/ich9.h"
36*49ab747fSPaolo Bonzini 
37*49ab747fSPaolo Bonzini //#define DEBUG
38*49ab747fSPaolo Bonzini 
39*49ab747fSPaolo Bonzini #ifdef DEBUG
40*49ab747fSPaolo Bonzini #define ICH9_DEBUG(fmt, ...) \
41*49ab747fSPaolo Bonzini do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
42*49ab747fSPaolo Bonzini #else
43*49ab747fSPaolo Bonzini #define ICH9_DEBUG(fmt, ...)    do { } while (0)
44*49ab747fSPaolo Bonzini #endif
45*49ab747fSPaolo Bonzini 
46*49ab747fSPaolo Bonzini static void pm_update_sci(ICH9LPCPMRegs *pm)
47*49ab747fSPaolo Bonzini {
48*49ab747fSPaolo Bonzini     int sci_level, pm1a_sts;
49*49ab747fSPaolo Bonzini 
50*49ab747fSPaolo Bonzini     pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs);
51*49ab747fSPaolo Bonzini 
52*49ab747fSPaolo Bonzini     sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
53*49ab747fSPaolo Bonzini                   (ACPI_BITMASK_RT_CLOCK_ENABLE |
54*49ab747fSPaolo Bonzini                    ACPI_BITMASK_POWER_BUTTON_ENABLE |
55*49ab747fSPaolo Bonzini                    ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
56*49ab747fSPaolo Bonzini                    ACPI_BITMASK_TIMER_ENABLE)) != 0);
57*49ab747fSPaolo Bonzini     qemu_set_irq(pm->irq, sci_level);
58*49ab747fSPaolo Bonzini 
59*49ab747fSPaolo Bonzini     /* schedule a timer interruption if needed */
60*49ab747fSPaolo Bonzini     acpi_pm_tmr_update(&pm->acpi_regs,
61*49ab747fSPaolo Bonzini                        (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
62*49ab747fSPaolo Bonzini                        !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
63*49ab747fSPaolo Bonzini }
64*49ab747fSPaolo Bonzini 
65*49ab747fSPaolo Bonzini static void ich9_pm_update_sci_fn(ACPIREGS *regs)
66*49ab747fSPaolo Bonzini {
67*49ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs);
68*49ab747fSPaolo Bonzini     pm_update_sci(pm);
69*49ab747fSPaolo Bonzini }
70*49ab747fSPaolo Bonzini 
71*49ab747fSPaolo Bonzini static uint64_t ich9_gpe_readb(void *opaque, hwaddr addr, unsigned width)
72*49ab747fSPaolo Bonzini {
73*49ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
74*49ab747fSPaolo Bonzini     return acpi_gpe_ioport_readb(&pm->acpi_regs, addr);
75*49ab747fSPaolo Bonzini }
76*49ab747fSPaolo Bonzini 
77*49ab747fSPaolo Bonzini static void ich9_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
78*49ab747fSPaolo Bonzini                             unsigned width)
79*49ab747fSPaolo Bonzini {
80*49ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
81*49ab747fSPaolo Bonzini     acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
82*49ab747fSPaolo Bonzini }
83*49ab747fSPaolo Bonzini 
84*49ab747fSPaolo Bonzini static const MemoryRegionOps ich9_gpe_ops = {
85*49ab747fSPaolo Bonzini     .read = ich9_gpe_readb,
86*49ab747fSPaolo Bonzini     .write = ich9_gpe_writeb,
87*49ab747fSPaolo Bonzini     .valid.min_access_size = 1,
88*49ab747fSPaolo Bonzini     .valid.max_access_size = 4,
89*49ab747fSPaolo Bonzini     .impl.min_access_size = 1,
90*49ab747fSPaolo Bonzini     .impl.max_access_size = 1,
91*49ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
92*49ab747fSPaolo Bonzini };
93*49ab747fSPaolo Bonzini 
94*49ab747fSPaolo Bonzini static uint64_t ich9_smi_readl(void *opaque, hwaddr addr, unsigned width)
95*49ab747fSPaolo Bonzini {
96*49ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
97*49ab747fSPaolo Bonzini     switch (addr) {
98*49ab747fSPaolo Bonzini     case 0:
99*49ab747fSPaolo Bonzini         return pm->smi_en;
100*49ab747fSPaolo Bonzini     case 4:
101*49ab747fSPaolo Bonzini         return pm->smi_sts;
102*49ab747fSPaolo Bonzini     default:
103*49ab747fSPaolo Bonzini         return 0;
104*49ab747fSPaolo Bonzini     }
105*49ab747fSPaolo Bonzini }
106*49ab747fSPaolo Bonzini 
107*49ab747fSPaolo Bonzini static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
108*49ab747fSPaolo Bonzini                             unsigned width)
109*49ab747fSPaolo Bonzini {
110*49ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
111*49ab747fSPaolo Bonzini     switch (addr) {
112*49ab747fSPaolo Bonzini     case 0:
113*49ab747fSPaolo Bonzini         pm->smi_en = val;
114*49ab747fSPaolo Bonzini         break;
115*49ab747fSPaolo Bonzini     }
116*49ab747fSPaolo Bonzini }
117*49ab747fSPaolo Bonzini 
118*49ab747fSPaolo Bonzini static const MemoryRegionOps ich9_smi_ops = {
119*49ab747fSPaolo Bonzini     .read = ich9_smi_readl,
120*49ab747fSPaolo Bonzini     .write = ich9_smi_writel,
121*49ab747fSPaolo Bonzini     .valid.min_access_size = 4,
122*49ab747fSPaolo Bonzini     .valid.max_access_size = 4,
123*49ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
124*49ab747fSPaolo Bonzini };
125*49ab747fSPaolo Bonzini 
126*49ab747fSPaolo Bonzini void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
127*49ab747fSPaolo Bonzini {
128*49ab747fSPaolo Bonzini     ICH9_DEBUG("to 0x%x\n", pm_io_base);
129*49ab747fSPaolo Bonzini 
130*49ab747fSPaolo Bonzini     assert((pm_io_base & ICH9_PMIO_MASK) == 0);
131*49ab747fSPaolo Bonzini 
132*49ab747fSPaolo Bonzini     pm->pm_io_base = pm_io_base;
133*49ab747fSPaolo Bonzini     memory_region_transaction_begin();
134*49ab747fSPaolo Bonzini     memory_region_set_enabled(&pm->io, pm->pm_io_base != 0);
135*49ab747fSPaolo Bonzini     memory_region_set_address(&pm->io, pm->pm_io_base);
136*49ab747fSPaolo Bonzini     memory_region_transaction_commit();
137*49ab747fSPaolo Bonzini }
138*49ab747fSPaolo Bonzini 
139*49ab747fSPaolo Bonzini static int ich9_pm_post_load(void *opaque, int version_id)
140*49ab747fSPaolo Bonzini {
141*49ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
142*49ab747fSPaolo Bonzini     uint32_t pm_io_base = pm->pm_io_base;
143*49ab747fSPaolo Bonzini     pm->pm_io_base = 0;
144*49ab747fSPaolo Bonzini     ich9_pm_iospace_update(pm, pm_io_base);
145*49ab747fSPaolo Bonzini     return 0;
146*49ab747fSPaolo Bonzini }
147*49ab747fSPaolo Bonzini 
148*49ab747fSPaolo Bonzini #define VMSTATE_GPE_ARRAY(_field, _state)                            \
149*49ab747fSPaolo Bonzini  {                                                                   \
150*49ab747fSPaolo Bonzini      .name       = (stringify(_field)),                              \
151*49ab747fSPaolo Bonzini      .version_id = 0,                                                \
152*49ab747fSPaolo Bonzini      .num        = ICH9_PMIO_GPE0_LEN,                               \
153*49ab747fSPaolo Bonzini      .info       = &vmstate_info_uint8,                              \
154*49ab747fSPaolo Bonzini      .size       = sizeof(uint8_t),                                  \
155*49ab747fSPaolo Bonzini      .flags      = VMS_ARRAY | VMS_POINTER,                          \
156*49ab747fSPaolo Bonzini      .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
157*49ab747fSPaolo Bonzini  }
158*49ab747fSPaolo Bonzini 
159*49ab747fSPaolo Bonzini const VMStateDescription vmstate_ich9_pm = {
160*49ab747fSPaolo Bonzini     .name = "ich9_pm",
161*49ab747fSPaolo Bonzini     .version_id = 1,
162*49ab747fSPaolo Bonzini     .minimum_version_id = 1,
163*49ab747fSPaolo Bonzini     .minimum_version_id_old = 1,
164*49ab747fSPaolo Bonzini     .post_load = ich9_pm_post_load,
165*49ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
166*49ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs),
167*49ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs),
168*49ab747fSPaolo Bonzini         VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs),
169*49ab747fSPaolo Bonzini         VMSTATE_TIMER(acpi_regs.tmr.timer, ICH9LPCPMRegs),
170*49ab747fSPaolo Bonzini         VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs),
171*49ab747fSPaolo Bonzini         VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs),
172*49ab747fSPaolo Bonzini         VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs),
173*49ab747fSPaolo Bonzini         VMSTATE_UINT32(smi_en, ICH9LPCPMRegs),
174*49ab747fSPaolo Bonzini         VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
175*49ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
176*49ab747fSPaolo Bonzini     }
177*49ab747fSPaolo Bonzini };
178*49ab747fSPaolo Bonzini 
179*49ab747fSPaolo Bonzini static void pm_reset(void *opaque)
180*49ab747fSPaolo Bonzini {
181*49ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = opaque;
182*49ab747fSPaolo Bonzini     ich9_pm_iospace_update(pm, 0);
183*49ab747fSPaolo Bonzini 
184*49ab747fSPaolo Bonzini     acpi_pm1_evt_reset(&pm->acpi_regs);
185*49ab747fSPaolo Bonzini     acpi_pm1_cnt_reset(&pm->acpi_regs);
186*49ab747fSPaolo Bonzini     acpi_pm_tmr_reset(&pm->acpi_regs);
187*49ab747fSPaolo Bonzini     acpi_gpe_reset(&pm->acpi_regs);
188*49ab747fSPaolo Bonzini 
189*49ab747fSPaolo Bonzini     if (kvm_enabled()) {
190*49ab747fSPaolo Bonzini         /* Mark SMM as already inited to prevent SMM from running. KVM does not
191*49ab747fSPaolo Bonzini          * support SMM mode. */
192*49ab747fSPaolo Bonzini         pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
193*49ab747fSPaolo Bonzini     }
194*49ab747fSPaolo Bonzini 
195*49ab747fSPaolo Bonzini     pm_update_sci(pm);
196*49ab747fSPaolo Bonzini }
197*49ab747fSPaolo Bonzini 
198*49ab747fSPaolo Bonzini static void pm_powerdown_req(Notifier *n, void *opaque)
199*49ab747fSPaolo Bonzini {
200*49ab747fSPaolo Bonzini     ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier);
201*49ab747fSPaolo Bonzini 
202*49ab747fSPaolo Bonzini     acpi_pm1_evt_power_down(&pm->acpi_regs);
203*49ab747fSPaolo Bonzini }
204*49ab747fSPaolo Bonzini 
205*49ab747fSPaolo Bonzini void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
206*49ab747fSPaolo Bonzini                   qemu_irq sci_irq, qemu_irq cmos_s3)
207*49ab747fSPaolo Bonzini {
208*49ab747fSPaolo Bonzini     memory_region_init(&pm->io, "ich9-pm", ICH9_PMIO_SIZE);
209*49ab747fSPaolo Bonzini     memory_region_set_enabled(&pm->io, false);
210*49ab747fSPaolo Bonzini     memory_region_add_subregion(pci_address_space_io(lpc_pci),
211*49ab747fSPaolo Bonzini                                 0, &pm->io);
212*49ab747fSPaolo Bonzini 
213*49ab747fSPaolo Bonzini     acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
214*49ab747fSPaolo Bonzini     acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
215*49ab747fSPaolo Bonzini     acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, 2);
216*49ab747fSPaolo Bonzini 
217*49ab747fSPaolo Bonzini     acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN);
218*49ab747fSPaolo Bonzini     memory_region_init_io(&pm->io_gpe, &ich9_gpe_ops, pm, "apci-gpe0",
219*49ab747fSPaolo Bonzini                           ICH9_PMIO_GPE0_LEN);
220*49ab747fSPaolo Bonzini     memory_region_add_subregion(&pm->io, ICH9_PMIO_GPE0_STS, &pm->io_gpe);
221*49ab747fSPaolo Bonzini 
222*49ab747fSPaolo Bonzini     memory_region_init_io(&pm->io_smi, &ich9_smi_ops, pm, "apci-smi",
223*49ab747fSPaolo Bonzini                           8);
224*49ab747fSPaolo Bonzini     memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
225*49ab747fSPaolo Bonzini 
226*49ab747fSPaolo Bonzini     pm->irq = sci_irq;
227*49ab747fSPaolo Bonzini     qemu_register_reset(pm_reset, pm);
228*49ab747fSPaolo Bonzini     pm->powerdown_notifier.notify = pm_powerdown_req;
229*49ab747fSPaolo Bonzini     qemu_register_powerdown_notifier(&pm->powerdown_notifier);
230*49ab747fSPaolo Bonzini }
231