(linenum→info "unix/slp.c:2238")

qemu/0.9.1/hw/acpi.c

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