]> git.proxmox.com Git - qemu.git/blame - hw/acpi.c
32 bit syscall fix (Juergen Keil)
[qemu.git] / hw / acpi.c
CommitLineData
6515b203
FB
1/*
2 * ACPI implementation
3 *
4 * Copyright (c) 2006 Fabrice Bellard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2 as published by the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "vl.h"
20
21//#define DEBUG
ab1e34ad 22#define USE_SMM
6515b203
FB
23
24/* i82731AB (PIIX4) compatible power management function */
25#define PM_FREQ 3579545
26
6515b203
FB
27#define ACPI_DBG_IO_ADDR 0xb044
28
29typedef struct PIIX4PMState {
30 PCIDevice dev;
31 uint16_t pmsts;
32 uint16_t pmen;
33 uint16_t pmcntrl;
ab1e34ad
FB
34 uint8_t apmc;
35 uint8_t apms;
6515b203
FB
36 QEMUTimer *tmr_timer;
37 int64_t tmr_overflow_time;
38} PIIX4PMState;
39
40#define RTC_EN (1 << 10)
41#define PWRBTN_EN (1 << 8)
42#define GBL_EN (1 << 5)
43#define TMROF_EN (1 << 0)
44
45#define SCI_EN (1 << 0)
46
47#define SUS_EN (1 << 13)
48
6515b203
FB
49static uint32_t get_pmtmr(PIIX4PMState *s)
50{
51 uint32_t d;
52 d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
53 return d & 0xffffff;
54}
55
56static int get_pmsts(PIIX4PMState *s)
57{
58 int64_t d;
59 int pmsts;
60 pmsts = s->pmsts;
61 d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
62 if (d >= s->tmr_overflow_time)
63 s->pmsts |= TMROF_EN;
64 return pmsts;
65}
66
67static void pm_update_sci(PIIX4PMState *s)
68{
69 int sci_level, pmsts;
70 int64_t expire_time;
71
72 pmsts = get_pmsts(s);
73 sci_level = (((pmsts & s->pmen) &
74 (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0);
75 pci_set_irq(&s->dev, 0, sci_level);
76 /* schedule a timer interruption if needed */
77 if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) {
78 expire_time = muldiv64(s->tmr_overflow_time, ticks_per_sec, PM_FREQ);
79 qemu_mod_timer(s->tmr_timer, expire_time);
80 } else {
81 qemu_del_timer(s->tmr_timer);
82 }
83}
84
85static void pm_tmr_timer(void *opaque)
86{
87 PIIX4PMState *s = opaque;
88 pm_update_sci(s);
89}
90
91static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
92{
93 PIIX4PMState *s = opaque;
94 addr &= 0x3f;
95 switch(addr) {
96 case 0x00:
97 {
98 int64_t d;
99 int pmsts;
100 pmsts = get_pmsts(s);
101 if (pmsts & val & TMROF_EN) {
102 /* if TMRSTS is reset, then compute the new overflow time */
103 d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec);
104 s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
105 }
106 s->pmsts &= ~val;
107 pm_update_sci(s);
108 }
109 break;
110 case 0x02:
111 s->pmen = val;
112 pm_update_sci(s);
113 break;
114 case 0x04:
115 {
116 int sus_typ;
117 s->pmcntrl = val & ~(SUS_EN);
118 if (val & SUS_EN) {
119 /* change suspend type */
120 sus_typ = (val >> 10) & 3;
121 switch(sus_typ) {
122 case 0: /* soft power off */
123 qemu_system_shutdown_request();
124 break;
125 default:
126 break;
127 }
128 }
129 }
130 break;
131 default:
132 break;
133 }
134#ifdef DEBUG
135 printf("PM writew port=0x%04x val=0x%04x\n", addr, val);
136#endif
137}
138
139static uint32_t pm_ioport_readw(void *opaque, uint32_t addr)
140{
141 PIIX4PMState *s = opaque;
142 uint32_t val;
143
144 addr &= 0x3f;
145 switch(addr) {
146 case 0x00:
147 val = get_pmsts(s);
148 break;
149 case 0x02:
150 val = s->pmen;
151 break;
152 case 0x04:
153 val = s->pmcntrl;
154 break;
155 default:
156 val = 0;
157 break;
158 }
159#ifdef DEBUG
160 printf("PM readw port=0x%04x val=0x%04x\n", addr, val);
161#endif
162 return val;
163}
164
165static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
166{
167 // PIIX4PMState *s = opaque;
168 addr &= 0x3f;
169#ifdef DEBUG
170 printf("PM writel port=0x%04x val=0x%08x\n", addr, val);
171#endif
172}
173
174static uint32_t pm_ioport_readl(void *opaque, uint32_t addr)
175{
176 PIIX4PMState *s = opaque;
177 uint32_t val;
178
179 addr &= 0x3f;
180 switch(addr) {
181 case 0x08:
182 val = get_pmtmr(s);
183 break;
184 default:
185 val = 0;
186 break;
187 }
188#ifdef DEBUG
189 printf("PM readl port=0x%04x val=0x%08x\n", addr, val);
190#endif
191 return val;
192}
193
ab1e34ad 194static void pm_smi_writeb(void *opaque, uint32_t addr, uint32_t val)
6515b203
FB
195{
196 PIIX4PMState *s = opaque;
ab1e34ad 197 addr &= 1;
6515b203 198#ifdef DEBUG
ab1e34ad 199 printf("pm_smi_writeb addr=0x%x val=0x%02x\n", addr, val);
6515b203 200#endif
ab1e34ad
FB
201 if (addr == 0) {
202 s->apmc = val;
203#ifdef USE_SMM
204 cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
205#else
206 /* emulation of what the SMM BIOS should do */
207 switch(val) {
208 case 0xf0: /* ACPI disable */
209 s->pmcntrl &= ~SCI_EN;
210 break;
211 case 0xf1: /* ACPI enable */
212 s->pmcntrl |= SCI_EN;
213 break;
214 }
215#endif
216 } else {
217 s->apms = val;
6515b203
FB
218 }
219}
220
ab1e34ad
FB
221static uint32_t pm_smi_readb(void *opaque, uint32_t addr)
222{
223 PIIX4PMState *s = opaque;
224 uint32_t val;
225
226 addr &= 1;
227 if (addr == 0) {
228 val = s->apmc;
229 } else {
230 val = s->apms;
231 }
232#ifdef DEBUG
233 printf("pm_smi_readb addr=0x%x val=0x%02x\n", addr, val);
234#endif
235 return val;
236}
237
6515b203
FB
238static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val)
239{
240#if defined(DEBUG)
241 printf("ACPI: DBG: 0x%08x\n", val);
242#endif
243}
244
ab1e34ad
FB
245static void pm_io_space_update(PIIX4PMState *s)
246{
247 uint32_t pm_io_base;
248
249 if (s->dev.config[0x80] & 1) {
250 pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40));
251 pm_io_base &= 0xfffe;
252
253 /* XXX: need to improve memory and ioport allocation */
254#if defined(DEBUG)
255 printf("PM: mapping to 0x%x\n", pm_io_base);
256#endif
257 register_ioport_write(pm_io_base, 64, 2, pm_ioport_writew, s);
258 register_ioport_read(pm_io_base, 64, 2, pm_ioport_readw, s);
259 register_ioport_write(pm_io_base, 64, 4, pm_ioport_writel, s);
260 register_ioport_read(pm_io_base, 64, 4, pm_ioport_readl, s);
261 }
262}
263
264static void pm_write_config(PCIDevice *d,
265 uint32_t address, uint32_t val, int len)
266{
267 pci_default_write_config(d, address, val, len);
268 if (address == 0x80)
269 pm_io_space_update((PIIX4PMState *)d);
270}
271
272static void pm_save(QEMUFile* f,void *opaque)
273{
274 PIIX4PMState *s = opaque;
275
276 pci_device_save(&s->dev, f);
277
278 qemu_put_be16s(f, &s->pmsts);
279 qemu_put_be16s(f, &s->pmen);
280 qemu_put_be16s(f, &s->pmcntrl);
281 qemu_put_8s(f, &s->apmc);
282 qemu_put_8s(f, &s->apms);
283 qemu_put_timer(f, s->tmr_timer);
284 qemu_put_be64s(f, &s->tmr_overflow_time);
285}
286
287static int pm_load(QEMUFile* f,void* opaque,int version_id)
288{
289 PIIX4PMState *s = opaque;
290 int ret;
291
292 if (version_id > 1)
293 return -EINVAL;
294
295 ret = pci_device_load(&s->dev, f);
296 if (ret < 0)
297 return ret;
298
299 qemu_get_be16s(f, &s->pmsts);
300 qemu_get_be16s(f, &s->pmen);
301 qemu_get_be16s(f, &s->pmcntrl);
302 qemu_get_8s(f, &s->apmc);
303 qemu_get_8s(f, &s->apms);
304 qemu_get_timer(f, s->tmr_timer);
305 qemu_get_be64s(f, &s->tmr_overflow_time);
306
307 pm_io_space_update(s);
308
309 return 0;
310}
311
502a5395 312void piix4_pm_init(PCIBus *bus, int devfn)
6515b203
FB
313{
314 PIIX4PMState *s;
315 uint8_t *pci_conf;
6515b203
FB
316
317 s = (PIIX4PMState *)pci_register_device(bus,
318 "PM", sizeof(PIIX4PMState),
ab1e34ad 319 devfn, NULL, pm_write_config);
6515b203
FB
320 pci_conf = s->dev.config;
321 pci_conf[0x00] = 0x86;
322 pci_conf[0x01] = 0x80;
323 pci_conf[0x02] = 0x13;
7ef4da1c 324 pci_conf[0x03] = 0x71;
6515b203
FB
325 pci_conf[0x08] = 0x00; // revision number
326 pci_conf[0x09] = 0x00;
327 pci_conf[0x0a] = 0x80; // other bridge device
328 pci_conf[0x0b] = 0x06; // bridge device
329 pci_conf[0x0e] = 0x00; // header_type
330 pci_conf[0x3d] = 0x01; // interrupt pin 1
6515b203 331
ab1e34ad 332 pci_conf[0x40] = 0x01; /* PM io base read only bit */
6515b203 333
ab1e34ad
FB
334 register_ioport_write(0xb2, 2, 1, pm_smi_writeb, s);
335 register_ioport_read(0xb2, 2, 1, pm_smi_readb, s);
336
6515b203
FB
337 register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s);
338
1ce549ab
FB
339 /* XXX: which specification is used ? The i82731AB has different
340 mappings */
341 pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10;
342 pci_conf[0x63] = 0x60;
343 pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) |
344 (serial_hds[1] != NULL ? 0x90 : 0);
345
6515b203 346 s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s);
6515b203 347
ab1e34ad 348 register_savevm("piix4_pm", 0, 1, pm_save, pm_load, s);
6515b203 349}