|
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 "hw.h" |
|
20 #include "pc.h" |
|
21 #include "pci.h" |
|
22 #include "qemu-timer.h" |
|
23 #include "sysemu.h" |
|
24 #include "i2c.h" |
|
25 #include "smbus.h" |
|
26 #include "kvm.h" |
|
27 |
|
28 //#define DEBUG |
|
29 |
|
30 /* i82731AB (PIIX4) compatible power management function */ |
|
31 #define PM_FREQ 3579545 |
|
32 |
|
33 #define ACPI_DBG_IO_ADDR 0xb044 |
|
34 |
|
35 typedef struct PIIX4PMState { |
|
36 PCIDevice dev; |
|
37 uint16_t pmsts; |
|
38 uint16_t pmen; |
|
39 uint16_t pmcntrl; |
|
40 uint8_t apmc; |
|
41 uint8_t apms; |
|
42 QEMUTimer *tmr_timer; |
|
43 int64_t tmr_overflow_time; |
|
44 i2c_bus *smbus; |
|
45 uint8_t smb_stat; |
|
46 uint8_t smb_ctl; |
|
47 uint8_t smb_cmd; |
|
48 uint8_t smb_addr; |
|
49 uint8_t smb_data0; |
|
50 uint8_t smb_data1; |
|
51 uint8_t smb_data[32]; |
|
52 uint8_t smb_index; |
|
53 qemu_irq irq; |
|
54 } PIIX4PMState; |
|
55 |
|
56 #define RSM_STS (1 << 15) |
|
57 #define PWRBTN_STS (1 << 8) |
|
58 #define RTC_EN (1 << 10) |
|
59 #define PWRBTN_EN (1 << 8) |
|
60 #define GBL_EN (1 << 5) |
|
61 #define TMROF_EN (1 << 0) |
|
62 |
|
63 #define SCI_EN (1 << 0) |
|
64 |
|
65 #define SUS_EN (1 << 13) |
|
66 |
|
67 #define ACPI_ENABLE 0xf1 |
|
68 #define ACPI_DISABLE 0xf0 |
|
69 |
|
70 #define SMBHSTSTS 0x00 |
|
71 #define SMBHSTCNT 0x02 |
|
72 #define SMBHSTCMD 0x03 |
|
73 #define SMBHSTADD 0x04 |
|
74 #define SMBHSTDAT0 0x05 |
|
75 #define SMBHSTDAT1 0x06 |
|
76 #define SMBBLKDAT 0x07 |
|
77 |
|
78 static PIIX4PMState *pm_state; |
|
79 |
|
80 static uint32_t get_pmtmr(PIIX4PMState *s) |
|
81 { |
|
82 uint32_t d; |
|
83 d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec); |
|
84 return d & 0xffffff; |
|
85 } |
|
86 |
|
87 static int get_pmsts(PIIX4PMState *s) |
|
88 { |
|
89 int64_t d; |
|
90 int pmsts; |
|
91 pmsts = s->pmsts; |
|
92 d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec); |
|
93 if (d >= s->tmr_overflow_time) |
|
94 s->pmsts |= TMROF_EN; |
|
95 return pmsts; |
|
96 } |
|
97 |
|
98 static void pm_update_sci(PIIX4PMState *s) |
|
99 { |
|
100 int sci_level, pmsts; |
|
101 int64_t expire_time; |
|
102 |
|
103 pmsts = get_pmsts(s); |
|
104 sci_level = (((pmsts & s->pmen) & |
|
105 (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0); |
|
106 qemu_set_irq(s->irq, sci_level); |
|
107 /* schedule a timer interruption if needed */ |
|
108 if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) { |
|
109 expire_time = muldiv64(s->tmr_overflow_time, ticks_per_sec, PM_FREQ); |
|
110 qemu_mod_timer(s->tmr_timer, expire_time); |
|
111 } else { |
|
112 qemu_del_timer(s->tmr_timer); |
|
113 } |
|
114 } |
|
115 |
|
116 static void pm_tmr_timer(void *opaque) |
|
117 { |
|
118 PIIX4PMState *s = opaque; |
|
119 pm_update_sci(s); |
|
120 } |
|
121 |
|
122 static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) |
|
123 { |
|
124 PIIX4PMState *s = opaque; |
|
125 addr &= 0x3f; |
|
126 switch(addr) { |
|
127 case 0x00: |
|
128 { |
|
129 int64_t d; |
|
130 int pmsts; |
|
131 pmsts = get_pmsts(s); |
|
132 if (pmsts & val & TMROF_EN) { |
|
133 /* if TMRSTS is reset, then compute the new overflow time */ |
|
134 d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, ticks_per_sec); |
|
135 s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL; |
|
136 } |
|
137 s->pmsts &= ~val; |
|
138 pm_update_sci(s); |
|
139 } |
|
140 break; |
|
141 case 0x02: |
|
142 s->pmen = val; |
|
143 pm_update_sci(s); |
|
144 break; |
|
145 case 0x04: |
|
146 { |
|
147 int sus_typ; |
|
148 s->pmcntrl = val & ~(SUS_EN); |
|
149 if (val & SUS_EN) { |
|
150 /* change suspend type */ |
|
151 sus_typ = (val >> 10) & 7; |
|
152 switch(sus_typ) { |
|
153 case 0: /* soft power off */ |
|
154 qemu_system_shutdown_request(); |
|
155 break; |
|
156 case 1: |
|
157 /* RSM_STS should be set on resume. Pretend that resume |
|
158 was caused by power button */ |
|
159 s->pmsts |= (RSM_STS | PWRBTN_STS); |
|
160 qemu_system_reset_request(); |
|
161 #if defined(TARGET_I386) |
|
162 cmos_set_s3_resume(); |
|
163 #endif |
|
164 default: |
|
165 break; |
|
166 } |
|
167 } |
|
168 } |
|
169 break; |
|
170 default: |
|
171 break; |
|
172 } |
|
173 #ifdef DEBUG |
|
174 printf("PM writew port=0x%04x val=0x%04x\n", addr, val); |
|
175 #endif |
|
176 } |
|
177 |
|
178 static uint32_t pm_ioport_readw(void *opaque, uint32_t addr) |
|
179 { |
|
180 PIIX4PMState *s = opaque; |
|
181 uint32_t val; |
|
182 |
|
183 addr &= 0x3f; |
|
184 switch(addr) { |
|
185 case 0x00: |
|
186 val = get_pmsts(s); |
|
187 break; |
|
188 case 0x02: |
|
189 val = s->pmen; |
|
190 break; |
|
191 case 0x04: |
|
192 val = s->pmcntrl; |
|
193 break; |
|
194 default: |
|
195 val = 0; |
|
196 break; |
|
197 } |
|
198 #ifdef DEBUG |
|
199 printf("PM readw port=0x%04x val=0x%04x\n", addr, val); |
|
200 #endif |
|
201 return val; |
|
202 } |
|
203 |
|
204 static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val) |
|
205 { |
|
206 // PIIX4PMState *s = opaque; |
|
207 addr &= 0x3f; |
|
208 #ifdef DEBUG |
|
209 printf("PM writel port=0x%04x val=0x%08x\n", addr, val); |
|
210 #endif |
|
211 } |
|
212 |
|
213 static uint32_t pm_ioport_readl(void *opaque, uint32_t addr) |
|
214 { |
|
215 PIIX4PMState *s = opaque; |
|
216 uint32_t val; |
|
217 |
|
218 addr &= 0x3f; |
|
219 switch(addr) { |
|
220 case 0x08: |
|
221 val = get_pmtmr(s); |
|
222 break; |
|
223 default: |
|
224 val = 0; |
|
225 break; |
|
226 } |
|
227 #ifdef DEBUG |
|
228 printf("PM readl port=0x%04x val=0x%08x\n", addr, val); |
|
229 #endif |
|
230 return val; |
|
231 } |
|
232 |
|
233 static void pm_smi_writeb(void *opaque, uint32_t addr, uint32_t val) |
|
234 { |
|
235 PIIX4PMState *s = opaque; |
|
236 addr &= 1; |
|
237 #ifdef DEBUG |
|
238 printf("pm_smi_writeb addr=0x%x val=0x%02x\n", addr, val); |
|
239 #endif |
|
240 if (addr == 0) { |
|
241 s->apmc = val; |
|
242 |
|
243 /* ACPI specs 3.0, 4.7.2.5 */ |
|
244 if (val == ACPI_ENABLE) { |
|
245 s->pmcntrl |= SCI_EN; |
|
246 } else if (val == ACPI_DISABLE) { |
|
247 s->pmcntrl &= ~SCI_EN; |
|
248 } |
|
249 |
|
250 if (s->dev.config[0x5b] & (1 << 1)) { |
|
251 cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI); |
|
252 } |
|
253 } else { |
|
254 s->apms = val; |
|
255 } |
|
256 } |
|
257 |
|
258 static uint32_t pm_smi_readb(void *opaque, uint32_t addr) |
|
259 { |
|
260 PIIX4PMState *s = opaque; |
|
261 uint32_t val; |
|
262 |
|
263 addr &= 1; |
|
264 if (addr == 0) { |
|
265 val = s->apmc; |
|
266 } else { |
|
267 val = s->apms; |
|
268 } |
|
269 #ifdef DEBUG |
|
270 printf("pm_smi_readb addr=0x%x val=0x%02x\n", addr, val); |
|
271 #endif |
|
272 return val; |
|
273 } |
|
274 |
|
275 static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val) |
|
276 { |
|
277 #if defined(DEBUG) |
|
278 printf("ACPI: DBG: 0x%08x\n", val); |
|
279 #endif |
|
280 } |
|
281 |
|
282 static void smb_transaction(PIIX4PMState *s) |
|
283 { |
|
284 uint8_t prot = (s->smb_ctl >> 2) & 0x07; |
|
285 uint8_t read = s->smb_addr & 0x01; |
|
286 uint8_t cmd = s->smb_cmd; |
|
287 uint8_t addr = s->smb_addr >> 1; |
|
288 i2c_bus *bus = s->smbus; |
|
289 |
|
290 #ifdef DEBUG |
|
291 printf("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); |
|
292 #endif |
|
293 switch(prot) { |
|
294 case 0x0: |
|
295 smbus_quick_command(bus, addr, read); |
|
296 break; |
|
297 case 0x1: |
|
298 if (read) { |
|
299 s->smb_data0 = smbus_receive_byte(bus, addr); |
|
300 } else { |
|
301 smbus_send_byte(bus, addr, cmd); |
|
302 } |
|
303 break; |
|
304 case 0x2: |
|
305 if (read) { |
|
306 s->smb_data0 = smbus_read_byte(bus, addr, cmd); |
|
307 } else { |
|
308 smbus_write_byte(bus, addr, cmd, s->smb_data0); |
|
309 } |
|
310 break; |
|
311 case 0x3: |
|
312 if (read) { |
|
313 uint16_t val; |
|
314 val = smbus_read_word(bus, addr, cmd); |
|
315 s->smb_data0 = val; |
|
316 s->smb_data1 = val >> 8; |
|
317 } else { |
|
318 smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); |
|
319 } |
|
320 break; |
|
321 case 0x5: |
|
322 if (read) { |
|
323 s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data); |
|
324 } else { |
|
325 smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); |
|
326 } |
|
327 break; |
|
328 default: |
|
329 goto error; |
|
330 } |
|
331 return; |
|
332 |
|
333 error: |
|
334 s->smb_stat |= 0x04; |
|
335 } |
|
336 |
|
337 static void smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) |
|
338 { |
|
339 PIIX4PMState *s = opaque; |
|
340 addr &= 0x3f; |
|
341 #ifdef DEBUG |
|
342 printf("SMB writeb port=0x%04x val=0x%02x\n", addr, val); |
|
343 #endif |
|
344 switch(addr) { |
|
345 case SMBHSTSTS: |
|
346 s->smb_stat = 0; |
|
347 s->smb_index = 0; |
|
348 break; |
|
349 case SMBHSTCNT: |
|
350 s->smb_ctl = val; |
|
351 if (val & 0x40) |
|
352 smb_transaction(s); |
|
353 break; |
|
354 case SMBHSTCMD: |
|
355 s->smb_cmd = val; |
|
356 break; |
|
357 case SMBHSTADD: |
|
358 s->smb_addr = val; |
|
359 break; |
|
360 case SMBHSTDAT0: |
|
361 s->smb_data0 = val; |
|
362 break; |
|
363 case SMBHSTDAT1: |
|
364 s->smb_data1 = val; |
|
365 break; |
|
366 case SMBBLKDAT: |
|
367 s->smb_data[s->smb_index++] = val; |
|
368 if (s->smb_index > 31) |
|
369 s->smb_index = 0; |
|
370 break; |
|
371 default: |
|
372 break; |
|
373 } |
|
374 } |
|
375 |
|
376 static uint32_t smb_ioport_readb(void *opaque, uint32_t addr) |
|
377 { |
|
378 PIIX4PMState *s = opaque; |
|
379 uint32_t val; |
|
380 |
|
381 addr &= 0x3f; |
|
382 switch(addr) { |
|
383 case SMBHSTSTS: |
|
384 val = s->smb_stat; |
|
385 break; |
|
386 case SMBHSTCNT: |
|
387 s->smb_index = 0; |
|
388 val = s->smb_ctl & 0x1f; |
|
389 break; |
|
390 case SMBHSTCMD: |
|
391 val = s->smb_cmd; |
|
392 break; |
|
393 case SMBHSTADD: |
|
394 val = s->smb_addr; |
|
395 break; |
|
396 case SMBHSTDAT0: |
|
397 val = s->smb_data0; |
|
398 break; |
|
399 case SMBHSTDAT1: |
|
400 val = s->smb_data1; |
|
401 break; |
|
402 case SMBBLKDAT: |
|
403 val = s->smb_data[s->smb_index++]; |
|
404 if (s->smb_index > 31) |
|
405 s->smb_index = 0; |
|
406 break; |
|
407 default: |
|
408 val = 0; |
|
409 break; |
|
410 } |
|
411 #ifdef DEBUG |
|
412 printf("SMB readb port=0x%04x val=0x%02x\n", addr, val); |
|
413 #endif |
|
414 return val; |
|
415 } |
|
416 |
|
417 static void pm_io_space_update(PIIX4PMState *s) |
|
418 { |
|
419 uint32_t pm_io_base; |
|
420 |
|
421 if (s->dev.config[0x80] & 1) { |
|
422 pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40)); |
|
423 pm_io_base &= 0xffc0; |
|
424 |
|
425 /* XXX: need to improve memory and ioport allocation */ |
|
426 #if defined(DEBUG) |
|
427 printf("PM: mapping to 0x%x\n", pm_io_base); |
|
428 #endif |
|
429 register_ioport_write(pm_io_base, 64, 2, pm_ioport_writew, s); |
|
430 register_ioport_read(pm_io_base, 64, 2, pm_ioport_readw, s); |
|
431 register_ioport_write(pm_io_base, 64, 4, pm_ioport_writel, s); |
|
432 register_ioport_read(pm_io_base, 64, 4, pm_ioport_readl, s); |
|
433 } |
|
434 } |
|
435 |
|
436 static void pm_write_config(PCIDevice *d, |
|
437 uint32_t address, uint32_t val, int len) |
|
438 { |
|
439 pci_default_write_config(d, address, val, len); |
|
440 if (address == 0x80) |
|
441 pm_io_space_update((PIIX4PMState *)d); |
|
442 } |
|
443 |
|
444 static void pm_save(QEMUFile* f,void *opaque) |
|
445 { |
|
446 PIIX4PMState *s = opaque; |
|
447 |
|
448 pci_device_save(&s->dev, f); |
|
449 |
|
450 qemu_put_be16s(f, &s->pmsts); |
|
451 qemu_put_be16s(f, &s->pmen); |
|
452 qemu_put_be16s(f, &s->pmcntrl); |
|
453 qemu_put_8s(f, &s->apmc); |
|
454 qemu_put_8s(f, &s->apms); |
|
455 qemu_put_timer(f, s->tmr_timer); |
|
456 qemu_put_be64(f, s->tmr_overflow_time); |
|
457 } |
|
458 |
|
459 static int pm_load(QEMUFile* f,void* opaque,int version_id) |
|
460 { |
|
461 PIIX4PMState *s = opaque; |
|
462 int ret; |
|
463 |
|
464 if (version_id > 1) |
|
465 return -EINVAL; |
|
466 |
|
467 ret = pci_device_load(&s->dev, f); |
|
468 if (ret < 0) |
|
469 return ret; |
|
470 |
|
471 qemu_get_be16s(f, &s->pmsts); |
|
472 qemu_get_be16s(f, &s->pmen); |
|
473 qemu_get_be16s(f, &s->pmcntrl); |
|
474 qemu_get_8s(f, &s->apmc); |
|
475 qemu_get_8s(f, &s->apms); |
|
476 qemu_get_timer(f, s->tmr_timer); |
|
477 s->tmr_overflow_time=qemu_get_be64(f); |
|
478 |
|
479 pm_io_space_update(s); |
|
480 |
|
481 return 0; |
|
482 } |
|
483 |
|
484 static void piix4_reset(void *opaque) |
|
485 { |
|
486 PIIX4PMState *s = opaque; |
|
487 uint8_t *pci_conf = s->dev.config; |
|
488 |
|
489 pci_conf[0x58] = 0; |
|
490 pci_conf[0x59] = 0; |
|
491 pci_conf[0x5a] = 0; |
|
492 pci_conf[0x5b] = 0; |
|
493 } |
|
494 |
|
495 i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, |
|
496 qemu_irq sci_irq) |
|
497 { |
|
498 PIIX4PMState *s; |
|
499 uint8_t *pci_conf; |
|
500 |
|
501 s = (PIIX4PMState *)pci_register_device(bus, |
|
502 "PM", sizeof(PIIX4PMState), |
|
503 devfn, NULL, pm_write_config); |
|
504 pm_state = s; |
|
505 pci_conf = s->dev.config; |
|
506 pci_conf[0x00] = 0x86; |
|
507 pci_conf[0x01] = 0x80; |
|
508 pci_conf[0x02] = 0x13; |
|
509 pci_conf[0x03] = 0x71; |
|
510 pci_conf[0x06] = 0x80; |
|
511 pci_conf[0x07] = 0x02; |
|
512 pci_conf[0x08] = 0x03; // revision number |
|
513 pci_conf[0x09] = 0x00; |
|
514 pci_conf[0x0a] = 0x80; // other bridge device |
|
515 pci_conf[0x0b] = 0x06; // bridge device |
|
516 pci_conf[0x0e] = 0x00; // header_type |
|
517 pci_conf[0x3d] = 0x01; // interrupt pin 1 |
|
518 |
|
519 pci_conf[0x40] = 0x01; /* PM io base read only bit */ |
|
520 |
|
521 register_ioport_write(0xb2, 2, 1, pm_smi_writeb, s); |
|
522 register_ioport_read(0xb2, 2, 1, pm_smi_readb, s); |
|
523 |
|
524 register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s); |
|
525 |
|
526 if (kvm_enabled()) { |
|
527 /* Mark SMM as already inited to prevent SMM from running. KVM does not |
|
528 * support SMM mode. */ |
|
529 pci_conf[0x5B] = 0x02; |
|
530 } |
|
531 |
|
532 /* XXX: which specification is used ? The i82731AB has different |
|
533 mappings */ |
|
534 pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10; |
|
535 pci_conf[0x63] = 0x60; |
|
536 pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) | |
|
537 (serial_hds[1] != NULL ? 0x90 : 0); |
|
538 |
|
539 pci_conf[0x90] = smb_io_base | 1; |
|
540 pci_conf[0x91] = smb_io_base >> 8; |
|
541 pci_conf[0xd2] = 0x09; |
|
542 register_ioport_write(smb_io_base, 64, 1, smb_ioport_writeb, s); |
|
543 register_ioport_read(smb_io_base, 64, 1, smb_ioport_readb, s); |
|
544 |
|
545 s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s); |
|
546 |
|
547 register_savevm("piix4_pm", 0, 1, pm_save, pm_load, s); |
|
548 |
|
549 s->smbus = i2c_init_bus(); |
|
550 s->irq = sci_irq; |
|
551 qemu_register_reset(piix4_reset, s); |
|
552 |
|
553 return s->smbus; |
|
554 } |
|
555 |
|
556 #if defined(TARGET_I386) |
|
557 void qemu_system_powerdown(void) |
|
558 { |
|
559 if (!pm_state) { |
|
560 qemu_system_shutdown_request(); |
|
561 } else if (pm_state->pmen & PWRBTN_EN) { |
|
562 pm_state->pmsts |= PWRBTN_EN; |
|
563 pm_update_sci(pm_state); |
|
564 } |
|
565 } |
|
566 #endif |