Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

arch_power_management.c

/*
 * $Id: arch_power_management.c,v 1.171 2009-01-28 12:59:16 potyra Exp $
 *
 * Copyright (C) 2005-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#ifdef STATE

struct {
      /* filedescriptor of power button socketpair */
      int button_fd;
      /* whether power button is pressed */
      int button_pressed;
      /*
       * PCI Configuration Space
       */
#define RES(a, ab, nb)
#define RO(a, ab, c, nb)
#define RW(a, ab, v, vb, nb, def, act) unsigned int v : nb + vb;
#define RWC(a, ab, v, vb, nb, def, act) unsigned int v : nb + vb;
#include "arch_power_management.regs"
#undef RWC
#undef RW
#undef RO
#undef RES

      /*
       * PCI I/O Space Registers
       */
      /* Power Management */
      unsigned short pmsts;
      unsigned short pmen;
      unsigned short pmcntrl;
      unsigned short gpsts;
      unsigned short gpen;
      unsigned short glben;
      unsigned long glbctl;
      unsigned short glbsts;
      /* ISA Registers */
      unsigned char apms;
      unsigned char apmc;
      /* PM Timer */
      unsigned long long tsc_passed;
      unsigned long long tsc_per_timer;
      unsigned long tsc_per_tick;
      unsigned char timer_high;

      /* SMBus Management */
      /* Failed bus transaction due to kill bit set. */
      int fail;
      /* Failed bus transaction due to collision. */
      int collision;
      /* Device error. */
      int error;
      /* Interrupt pending. */
      int interrupt;
      /* SMBus busy flag. */
      int busy;

      /* Protokol. */
      int proto;
      /* Kill bit. */
      int kill;
      /* Interrupt enable bit. */
      int interrupt_enable;

      /* Slave address register */
      unsigned char addr;
      /* Command register */
      unsigned char cmd;
      /* Data0/Block Count register */
      unsigned char data0;
      /* Data1 register */
      unsigned char data1;
      /* Block data registers */
      unsigned char block[32];
      unsigned char block_count;
} NAME;

#endif /* STATE */
#ifdef BEHAVIOR

/* lots of defines for debugging */
#define DEBUG_CHIPSET_SMB     0
#define DEBUG_PM_TIMER        0
#define DEBUG_PM_REGS         0
#define DEBUG_PM_IRQ          0
#define DEBUG_PM_ISA          0
#define DEBUG_PM_CONFIGSPACE  0
#define DEBUG_PM_SUSPEND      0
#define DEBUG_PM_SIGIO        0
#define DEBUG_PM_SMI          0

#if 1
#define WARN(fmt, arg...) \
faum_log(FAUM_LOG_WARNING, "82371AB", "", "%20s:% 4d: " fmt , \
            __FUNCTION__, __LINE__, ## arg);
#else
#define WARN(fmt, arg...)
#endif

/* 
 * define to 1 if you want to use the gigabyte bios - which has some
 * assumptions about undocumented hardware features (as PMCTRL being at
 * 0x04 _and_ 0x40)
 */
#define DEBUG_GIGABYTE        0

/* positions of base address and enable settings for Power Management and SMBus
 * in PM configspace */
#define PM_SMB_BASE     0x90
/* I/O space length for Power Management and SMBus */
#if DEBUG_GIGABYTE /* debugging broken gigabyte bios */
# define PM_IOLEN 0x42
#else
# define PM_IOLEN 0x38
#endif
#define SMB_IOLEN 0x0e

/* timeout in seconds for power button override feature */
#define POWER_BTNOR_TIMEOUT   3

/*
 * The following SMBus definitions are adopted from the linux source
 * drivers/i2c/busses/i2c-piix4.c       josef 24.05.2005
 */

/* SMBus IO address offsets */
#define SMBHSTSTS 0 
#define SMBHSLVSTS      1 
#define SMBHSTCNT 2 
#define SMBHSTCMD 3 
#define SMBHSTADD 4 
#define SMBHSTDAT0      5 
#define SMBHSTDAT1      6 
#define SMBBLKDAT 7 
#define SMBSLVCNT 8 
#define SMBSHDWCMD      9 
#define SMBSLVEVT 0xA
#define SMBSLVDAT 0xC

/*
 * ------------- SMBUS ------------- SMBUS ------------- SMBUS ------------- 
 */

static void
NAME_(smbus_done)(void *_css)
{
      struct cpssp *css = _css;

      css->NAME.interrupt = 1;
      css->NAME.busy = 0;
}

static int
NAME_(smbus_start_transa)(struct cpssp *css)
{
      int ret;

      switch (css->NAME.proto) {
      case 0x00: /* Quick read or write. */
            goto unsupported;

      case 0x01: /* Byte read or write. */
            goto unsupported;

      case 0x02: /* Byte data read or write. */
            if (css->NAME.addr & 0x01) {
                  /* Read */
                  ret = NAME_(smbus_read_byte_data)(css,
                              css->NAME.addr,
                              css->NAME.cmd,
                              &css->NAME.data0);
            } else {
                  /* Write */
                  ret = NAME_(smbus_write_byte_data)(css,
                              css->NAME.addr,
                              css->NAME.cmd,
                              css->NAME.data0);
            }
            break;

      case 0x03: /* Word data read or write. */
            goto unsupported;

      case 0x04: /* Reserved. */
            goto reserved;

      case 0x05: /* Block read or write. */
            if (css->NAME.addr & 1) {
                  /* Read */
                  ret = NAME_(smbus_read_block_data)(css,
                              css->NAME.addr,
                              css->NAME.cmd, &css->NAME.data0,
                              css->NAME.block);
            } else {
                  /* Write */
                  ret = NAME_(smbus_write_block_data)(css,
                              css->NAME.addr,
                              css->NAME.cmd, css->NAME.data0,
                              css->NAME.block);
            }
            break;

      case 0x06: /* Reserved. */
      case 0x07: /* Reserved. */
      reserved:;
            WARN("Reserved SMBus protocol (id: 0x%x)\n", css->NAME.proto);
            ret = -1;
            break;

      default:
      unsupported:;
            WARN("unsupported SMBus protocol (id: 0x%x)\n", css->NAME.proto);
            ret = -1;
            break;
      }
      /* FIXME: SMBus has to generate IRQ9 or SMI (depending on SMB_INTRSEL)
       *        when transactions are finished and SMB_HST_EN is set */
      return ret;
}

static void
NAME_(_smbus_outb)(
      struct cpssp *css, 
      uint8_t val,
      uint16_t port
)
{
      DEBUGPRINT(DEBUG_CHIPSET_SMB,
                  "writing to SMBus controller address 0x%x value 0x%x\n",
                  port, val);
      switch (port) {
      case SMBHSTSTS: /* offset 0x00 */
            /* Bit 7:5 are reserved. */
            if (val & (1 << 4)) {
                  /* Reset "fail" bit. */
                  css->NAME.fail = 0;
            }
            if (val & (1 << 3)) {
                  /* Reset "collision" bit. */
                  css->NAME.collision = 0;
            }
            if (val & (1 << 2)) {
                  /* Reset "error" bit. */
                  css->NAME.error = 0;
            }
            if (val & (1 << 1)) {
                  /* Reset "interrupt" bit. */
                  css->NAME.interrupt = 0;
            }
            /* Bit 0 ("busy") is read-only. */
            break;

      case SMBHSLVSTS: /* offset 0x01 */
            goto unsupported;

      case SMBHSTCNT: /* offset 0x02 */
            /* Bit 7: reserved. */
            /* Bit 6: Start bit - see below. */
            /* Bit 5: reserved. */
            css->NAME.proto = (val >> 2) & 7;
            css->NAME.kill = (val >> 1) & 1;
            css->NAME.interrupt_enable = (val >> 0) & 1;

            if ((val >> 6) & 1) {
                  /* Start bit set. */
                  if (NAME_(smbus_start_transa)(css) != 0) {
                        /* Error */
                        css->NAME.error = 1;
                  }
                  css->NAME.busy = 1;
                  time_call_after(TIME_HZ / 1000000,
                              NAME_(smbus_done), (void *) css);
            }
            break;

      case SMBHSTCMD: /* offset 0x03 */
            css->NAME.cmd = val;
            break;

      case SMBHSTADD: /* offset 0x04 */
            css->NAME.addr = val;
            break;

      case SMBHSTDAT0: /* offset 0x05  */
            css->NAME.data0 = val;
            break;

      case SMBHSTDAT1: /* offset 0x06  */
            css->NAME.data1 = val;
            break;

      case SMBBLKDAT: /* offset  0x07  */
            css->NAME.block[css->NAME.block_count] = val;
            css->NAME.block_count = (css->NAME.block_count + 1) % 32;
            break;

      case SMBSLVCNT: /* offset  0x08  */
            goto unsupported;

      case SMBSHDWCMD: /* offset 0x09  */
            goto unsupported;

      case SMBSLVEVT: /* offset  0x0a */
            goto unsupported;

      case SMBSLVDAT: /* offset  0x0c */
            goto unsupported;

      default:
      unsupported:;
            WARN("unsupported SMBus controller register 0x%x\n",
                        port);
            break;
      };
}

static void
NAME_(_smbus_inb)(
      struct cpssp *css,
      uint8_t *valp,
      uint16_t port
)
{
      unsigned char status;

      switch (port) {
      case SMBHSTSTS:  /* offset 0x00 */
            status = 0;
            if (css->NAME.fail) {
                  status |= 1 << 4;
            }
            if (css->NAME.collision) {
                  status |= 1 << 3;
            }
            if (css->NAME.error) {
                  status |= 1 << 2;
            }
            if (css->NAME.interrupt) {
                  status |= 1 << 1;
            }
            if (css->NAME.busy) {
                  status |= 1 << 0;
            }
            *valp = status;
            break;

      case SMBHSLVSTS: /* offset 0x01 */
            goto unsupported;

      case SMBHSTCNT: /* offset 0x02 */
            status = 0;
            /* Bit 7: Reserved. */
            /* Bit 6: read as 0. */
            /* Bit 5: Reserved. */
            status |= css->NAME.proto << 2;
            status |= css->NAME.kill << 1;
            status |= css->NAME.interrupt_enable << 0;
            *valp = status;

            css->NAME.block_count = 0;
            break;

      case SMBHSTCMD: /* offset 0x03 */
            goto unsupported;

      case SMBHSTADD: /* offset 0x04 */
            goto unsupported;

      case SMBHSTDAT0: /* offset 0x05  */
            *valp = css->NAME.data0;
            break;

      case SMBHSTDAT1: /* offset 0x06  */
            *valp = css->NAME.data1;
            break;

      case SMBBLKDAT: /* offset  0x07  */
            *valp = css->NAME.block[css->NAME.block_count];
            css->NAME.block_count = (css->NAME.block_count + 1) % 32;
            break;

      case SMBSLVCNT: /* offset  0x08  */
            goto unsupported;

      case SMBSHDWCMD: /* offset 0x09  */
            goto unsupported;

      case SMBSLVEVT: /* offset  0x0a */
            goto unsupported;

      case SMBSLVDAT: /* offset  0x0c */
            goto unsupported;

      default:
      unsupported:;
            WARN("unsupported SMBus controller register 0x%x\n",
                  port);
            break;
      };
      DEBUGPRINT(DEBUG_CHIPSET_SMB,
               "reading from SMBus controller address 0x%x value 0x%x\n",
               port, *valp);
}

/*
 * ------------- POWER MANAGEMENT ------------- POWER MANAGEMENT -----
 */

#if DEBUG_PM_REGS
/*
 * just some debugging
 */
static void
POWER_DEBUG_REG_PRINT(unsigned short port, unsigned char val, const char * head)
{
      int i;
      struct regdebugtext {
            unsigned short port;
            unsigned char bit;
            const char * text;
      } bitnames[] = {
            { 0x00, 0, "TMROF_STS" },
            { 0x00, 4, "BM_STS" },
            { 0x00, 5, "GBL_STS" },
            { 0x01, 0, "PWRBTN_STS" },
            { 0x01, 2, "RTC_STS" } ,
            { 0x01, 3, "PWRBTNOR_STS" } ,
            { 0x01, 7, "RSM_STS" } ,
            { 0x02, 0, "TMROF_EN" } ,
            { 0x02, 5, "GBL_EN" } ,
            { 0x03, 0, "PWRBTN_EN" } ,
            { 0x03, 2, "RTC_EN" } ,
            { 0x04, 0, "SCI_EN" } ,
            { 0x04, 1, "BRLD_EN_BM" } ,
            { 0x04, 2, "GBL_RLS" } ,
            { 0x05, 2, "SUS_TYP" } ,
            { 0x05, 3, "" /* SUS_TYP */} ,
            { 0x05, 4, "" /* SUS_TYP */} ,
            { 0x05, 5, "SUS_EN" } ,
            { 0x0e, 0, "THRM_EN" } ,
            { 0x0f, 0, "USB_EN" } ,
            { 0x0f, 1, "GPI_EN" } ,
            { 0x0f, 2, "RI_EN" } ,
            { 0x0f, 3, "LID_EN" } ,
            { 0x0c, 0, "THRM_STS" } ,
            { 0x0c, 7, "THRMOR_STS" } ,
            { 0x0d, 0, "USB_STS" } ,
            { 0x0d, 1, "GPI_STS" } ,
            { 0x0d, 2, "RI_STS" } ,
            { 0x0d, 3, "LID_STS" } ,
            { 0x18, 0, "BIOS_STS" },
            { 0x18, 5, "APM_STS" },
            { 0x18, 6, "PM1_STS" },
            { 0x18, 7, "GP_STS" },
            { 0x28, 0, "SMI_EN" },
            { 0x28, 1, "BIOS_RLS" },
            { 0x30, 0, "EOS" },
            { 0x00, 0, NULL }
      };

      fprintf(stderr, "power_debug_regs: %s:", head);
      for (i = 0; bitnames[i].text != NULL; i++) {
            if (bitnames[i].port == port) {
                  fprintf(stderr, " %s: %d", bitnames[i].text,
                        (val >> bitnames[i].bit) & 1);
            }
      }
      fprintf(stderr, "\n");
}
#else
#define POWER_DEBUG_REG_PRINT(oppenheimer, bohr, fermi)
#endif

/* forward declaration for do_suspend */
static void NAME_(button_remove_callback)(struct cpssp *css);

/*
 * actually suspend hardware, e.g. power off unneeded parts. "type" is the
 * same as the SUS_TYP field in PMCNTRL register
 */
static void
NAME_(do_suspend)(struct cpssp *css, char type)
{
      /* FIXME knilch: implement STR, POS... */
      DEBUGPRINT(DEBUG_PM_SUSPEND,
            "SUS_TYP is 0x%02x (%d %d %d), trying to suspend...\n", type,
            type & 4, type & 2, type & 1);

      switch(type) {
      case 0:     /* Soff/STD (Soft OFF or Suspend to Disk) */
            if (css->NAME.button_pressed) {
                  css->NAME.button_pressed = 0;
                  /* there's still a callback we've to get rid of */
                  NAME_(button_remove_callback)(css);
            }
            NAME_(n_susc_set)(css, 1);
            break;

      case 1: /* STR (Suspend To RAM) */
            goto unimplemented;

      case 2: /* POSCL (Powered On Suspend, Context Lost) */
            goto unimplemented;

      case 3: /* POSCCL (Powered On Suspend, CPU Context Lost) */
            goto unimplemented;

      case 4: /* POS (Powered On Suspend, Context Maintained) */
            goto unimplemented;

      case 5: /* Working (Clock Control) */
            goto unimplemented;

      case 6: /* Reserved */
      case 7: /* Reserved */
            goto reserved;

      unimplemented:
            WARN("called to suspend to unimplemented SUS_TYP 0x%02x!\n",
                  type);
            break;

      reserved:
            WARN("called to suspend with reserved SUS_TYP 0x%02x!\n",
                  type);
            break;
            
      default: /* something's completely wrong */
            assert(0);
            break;
      }
      DEBUGPRINT(DEBUG_PM_SUSPEND, "couldn't suspend! (%d)\n", type);
}

/*
 * return true if bit <nr> is set in <en> and in <sts>
 */
static int
NAME_(_check_en_sts_bit)(unsigned long en, unsigned long sts, unsigned char nr)
{
      return ((en & (1 << nr)) && (sts & (1 << nr)));
}

/*
 * helper function to set SMI if it is enabled and "armed".
 * SMI is reset by software writing EOS bit.
 */
static void
NAME_(_smi_set)(struct cpssp *css)
{
      if ((css->NAME.glbctl & (1 << 0) /* SMI_EN */ )
       && (css->NAME.glbctl & (1 << 16) /* EOS */ )) {
            NAME_(smi_out_set)(css, 1);
      }
}

/*
 * checks if there are reasons for an SCI;
 * sets/clears SCI/SMI line accordingly
 */
static void
NAME_(checknset_sci)(struct cpssp *css)
{
      int val = 0;
#if DEBUG_PM_IRQ
      static int oldval = 0;
#endif

      /* check if we have reasons for setting SCI/SMI */
      val = NAME_(_check_en_sts_bit)(css->NAME.pmen, css->NAME.pmsts, 0)      /* TMR_OF */
         || NAME_(_check_en_sts_bit)(css->NAME.pmen, css->NAME.pmsts, 5)      /* GBL */
         || NAME_(_check_en_sts_bit)(css->NAME.pmen, css->NAME.pmsts, 8)      /* PWRBTN */
         || NAME_(_check_en_sts_bit)(css->NAME.pmen, css->NAME.pmsts, 10)     /* RTC */
         || NAME_(_check_en_sts_bit)(css->NAME.gpen, css->NAME.gpsts, 0)      /* THRM */
         || NAME_(_check_en_sts_bit)(css->NAME.gpen, css->NAME.gpsts, 9)      /* GPI */
         || NAME_(_check_en_sts_bit)(css->NAME.gpen, css->NAME.gpsts, 11);    /* LID */

#if DEBUG_PM_IRQ
      if (oldval != val) {
            POWER_DEBUG_REG_PRINT(0x01, (css->NAME.pmsts >> 8) & 0xff, "(SCI) PMSTS");
            POWER_DEBUG_REG_PRINT(0x00, css->NAME.pmsts & 0xff, "(SCI) PMSTS");
            POWER_DEBUG_REG_PRINT(0x03, (css->NAME.pmen >> 8) & 0xff, "(SCI) PMEN");
            POWER_DEBUG_REG_PRINT(0x02, css->NAME.pmen & 0xff, "(SCI) PMEN");
            POWER_DEBUG_REG_PRINT(0x05, (css->NAME.pmcntrl >> 8) & 0xff, "(SCI) PMCNTRL");
            POWER_DEBUG_REG_PRINT(0x04, css->NAME.pmcntrl & 0xff, "(SCI) PMCNTRL");
            POWER_DEBUG_REG_PRINT(0x0d, (css->NAME.gpsts >> 8) & 0xff, "(SCI) GPSTS");
            POWER_DEBUG_REG_PRINT(0x0c, css->NAME.gpsts & 0xff, "(SCI) GPSTS");
            POWER_DEBUG_REG_PRINT(0x0f, (css->NAME.gpen >> 8) & 0xff, "(SCI) GPEN");
            POWER_DEBUG_REG_PRINT(0x0e, css->NAME.gpen & 0xff, "(SCI) GPEN");
            DEBUGPRINT(DEBUG_PM_IRQ, "=====> %sing SCI\n\n", (val ? "SETt" : "CLEAR"));
            oldval = val;
      }
#endif
      /* set PM1_STS in GLBSTS-register for SMI handler */
      if (val) {
            css->NAME.glbsts |= (1 << 6); /* PM1_STS */
      }
      
      /* set/reset SCI if SCI_EN is set, else set SMI */
      if (css->NAME.pmcntrl & (1 << 0) /* SCI_EN */ ) {
            NAME_(irqrq_out_set)(css, val);
            NAME_(smi_out_set)(css, 0);
      } else {
            NAME_(irqrq_out_set)(css, 0);
            if (val) {
                  NAME_(_smi_set)(css);
            }
      }
}

/*
 * callback function for power button, called when power button is pressed
 * for 4 seconds
 * "[...] If the PWRBTN# signal is held active for greater than 4 seconds and
 * Power Button Override feature is enabled, the [PWRBTN_STS] bit is cleared,
 * the [PWRBTNOR_STS] bit is set, and PIIX4 will automatically transition the
 * system into the Soft Off Suspend State." (82371AB specs, 11.5.2)
 */
static void
NAME_(button_override)(void *_css)
{
      struct cpssp *css = _css;
      
      fprintf(stderr, "timer expired.\n");
      DEBUGPRINT(DEBUG_PM_SUSPEND, "in power button callback - button %s\n",
                  (css->NAME.button_pressed ? "pressed" : "not pressed"));

      if (! css->NAME.button_pressed) {
            return;
      } else {
            /* tell do_suspend to not try to delete callback */
            css->NAME.button_pressed = 0;
      }

      /* clear PWRBTN_STS */
      css->NAME.pmsts &= ~(1 << 8);

      /* set PWRBTNOR_STS */
      css->NAME.pmsts |= (1 << 11);

      /* suspend to soft off */
      NAME_(do_suspend)(css, 0);

      /* he's dead, jim. */
}

/*
 * register power_button_override callback with time_call_at
 */
static void
NAME_(button_register_callback)(struct cpssp *css)
{
      time_call_at(time_virt() + POWER_BTNOR_TIMEOUT * TIME_HZ,
                    NAME_(button_override), css);
}

/*
 * remove power_button_override callback from time queue
 */
static void
NAME_(button_remove_callback)(struct cpssp *css)
{
      if (time_call_delete(NAME_(button_override), css) != 0) {
#if 0
            /* tried to delete callback function, but it wasn't inserted
             * before -> something really bad happened */
            WARN("time_call_delete failed! this should not hapepn.\n");
#endif
      }
}

/*
 * callback function for time_call_at
 * - called every 2.34... seconds (when highest bit in power management timer
 *   toggles)
 * - adjusts timer register offset
 * - signals interrupt when enabled
 */
static void
NAME_(timer_event)(void *_css)
{
      struct cpssp *css = _css;

      /* adjust tsc offset and highest bit */
      css->NAME.tsc_passed += css->NAME.tsc_per_timer;
      css->NAME.timer_high = 1 - css->NAME.timer_high;

      DEBUGPRINT(DEBUG_PM_TIMER, "timer is now 0x%06x, TMROF_STS was %s\n",
                  (css->NAME.timer_high << 23),
                  (css->NAME.pmsts & 1) ? "set" : "unset");

      /* report timer status in status register */
      css->NAME.pmsts |= (1 << 0);

      /* signal interrupt */
      NAME_(checknset_sci)(css);
      
      /* re-schedule timer */
      time_call_at(css->NAME.tsc_passed + css->NAME.tsc_per_timer,
                    NAME_(timer_event), css);
}

/*
 * returns value of the timer register
 */
static unsigned int
NAME_(get_timer)(struct cpssp *css)
{
      /* The power management timer is a 24-bit [...] fixed rate free
       * running count-up timer that runs off a 3.579545 MHz clock. */
      /* we don't use an own timer here, but map the time timestamp to our
       * rate */
      unsigned int timer;
      timer = ((time_virt() - css->NAME.tsc_passed) / css->NAME.tsc_per_tick)
            & 0xffffff;
      if ((timer >> 23) & 1) {
            WARN("PM timer overflow! value: 0x%06x (%d)\n",
                        timer, timer);
            timer = (1 << 23) - 1;
            /* FIXME: maybe increase tsc_per_tick here if this happens
             *        more than once? */
      }

      timer |= css->NAME.timer_high << 23;
      return timer;
}

static void
NAME_(n_button_set)(struct cpssp *css, unsigned int n_pressed)
{
      /*
       * - set PWRBTN_STS
       * - keep track of button state
       * - generate SCI/SMI (using *checknset_sci)
       * - start/stop timer for callback after 4 seconds to implement
       *   power button override feature
       */
      css->NAME.button_pressed = ! n_pressed;

      if (! n_pressed) {
            /* detect rising edge of PWRBTN signal */
            NAME_(n_susc_set)(css, 0);
            NAME_(button_register_callback)(css);
      } else {
            NAME_(button_remove_callback)(css);
      }

      /*
       * when button went from low to high, set status bits and irq
       */
      if (! n_pressed) {
            css->NAME.pmsts |= (1 << 8); /* PWRBTN_STS */
            NAME_(checknset_sci)(css);
      }
}

static void
NAME_(_inb_core)(
      struct cpssp *css,
      uint8_t *valp,
      uint16_t port
)
{
      switch (port) {
      case 0x00 ... 0x01:     /* Power Management Status */
            *valp = (css->NAME.pmsts >> (port * 8)) & 0xff;
            POWER_DEBUG_REG_PRINT(port, *valp, "reading PMSTS");
            break;

      case 0x02 ... 0x03:     /* Power Management Resume Enable */
            *valp = (css->NAME.pmen >> ((port - 0x02) * 8)) & 0xff;
            POWER_DEBUG_REG_PRINT(port, *valp, "reading PMEN");
            break;

      case 0x04 ... 0x05:     /* Power Management Control */
            *valp = (css->NAME.pmcntrl >> ((port - 0x04) * 8)) & 0xff;
            POWER_DEBUG_REG_PRINT(port, *valp, "reading PMCNTRL");
            break;

      case 0x08 ... 0x0a:     /* Power Management Timer */
            *valp = (NAME_(get_timer)(css) >> ((port - 0x08) * 8)) & 0xff;
            break;

      case 0x0b:        /* Power Management Timer/Reserved */
            *valp = 0x00;
            break;

      case 0x0c ... 0x0d:     /* General Purpose Status */
            *valp = (css->NAME.gpsts >> ((port - 0x0c) * 8)) & 0xff;
            POWER_DEBUG_REG_PRINT(port, *valp, "reading GPSTS (GPE0_BLK)");
            break;

      case 0x0e ... 0x0f:     /* General Purpose Enable */
            *valp = (css->NAME.gpen >> ((port - 0x0c) * 8)) & 0xff;
            POWER_DEBUG_REG_PRINT(port, *valp, "reading GPEN (GPE0_BLK)");
            break;

      case 0x10 ... 0x13:     /* Processor Control */
            *valp = 0x00;
            WARN("reading from unsupported power management processor "
                        "control 0x%02x\n", port);
            break;

      case 0x14:        /* Processor Level 2 Power State Entry */
            /* Reads to this register cause PIIX4 to transition into a Stop
             * Grant or Quick Start power state (LVL2) and return a value
             * of 00h. */
            *valp = 0x00;
            break;
      case 0x15:        /* Processor Level 3 Power State Entry */
            /* Reads to this register cause PIIX4 to transition into a Stop
             * Clock, Sleep, or Deep Sleep power state (LVL3) and return a
             * value of 00h. */
            *valp = 0x00;
            break;

      case 0x18 ... 0x19:     /* Global Status Register */
            *valp = (css->NAME.glbsts >> ((port - 0x18) * 8)) & 0xff;
            POWER_DEBUG_REG_PRINT(port, *valp, "reading GLBSTS");
            break;

      case 0x28 ... 0x2b:     /* Global Control Register */
            *valp = (css->NAME.glbctl >> ((port - 0x28) * 8)) & 0xff;
            POWER_DEBUG_REG_PRINT(port, *valp, "reading GLBCTL");
            break;

      /* General Purpose Input */
      /* 82371AB, page 36 */
      case 0x30:
            *valp = 0x00;
            /* Bit 0: ? */
            /* Bit 1: ? */
            /* Bit 2: ? */

            /* Bit 3: */
            /* CPU FAN Status; 0: Fail, 1: OK */
            *valp |= 1 << (3 - 0);

            /* Bit 4: ? */
            /* If bit is 0 Award Bios shows VCore ?.?? */
            *valp |= 1 << (4 - 0);

            /* Bit 5: ? */
            /* Bit 6: ? */
            /* Bit 7: ? */
            break;

      case 0x31:        /* General Purpose Input */
            *valp = 0x00;
            /* Bit 8: THRM# (Page 35) */
            /* CPU Themperature; 0: High, 1: Normal */
            *valp |= 1 << (8 - 8);

            /* Bit 9: BATLOW# (Page ?) */
            *valp |= 1 << (9 - 8);

            /* Bit 10: LID# (Page ?) */
            *valp |= 1 << (10 - 8);

            /* Bit 11: SMBALERT# (Page ?) */
            *valp |= 1 << (11 - 8);

            /* Bit 12: RI# (Page ?) */
            *valp |= 1 << (12 - 8);

            /* Bit 13: */
            /* +12V; 0: OK, 1: Fail */
            *valp &= ~(1 << (13 - 8));

            /* Bit 14: */
            /* -12V: 0: Fail, 1: OK */
            *valp |= 1 << (14 - 8);

            /* Bit 15 */
            /* +5V; 0: OK, 1: Fail */
            *valp &= ~(1 << (15 - 8));
            break;

      case 0x32:        /* General Purpose Input */
            *valp = 0x00;
            /* Bit 16 */
            /* -5V; 0: Fail; 1: OK */
            *valp |= 1 << (16 - 16);

            /* Bit 17 */
            /* Battery Low; 0: OK, 1: Fail */
            *valp &= ~(1 << (17 - 16));

            /* Bit 18-21 */
            /* VCore: 0000: 3.5V, 0001: 3.4V, 0010: 3.3V, ... */
            *valp |= 1 << (18 - 16);
            break;

#if DEBUG_GIGABYTE /* debugging broken gigabyte bios */
      case 0x40 ... 0x41:     /* Power Management Control */
            *valp = (css->NAME.pmcntrl >> ((port - 0x40) * 8)) & 0xff;
            POWER_DEBUG_REG_PRINT(port-0x3c, *valp, "reading (wrong) PMCNTRL");
            break;
#endif

      unsupported:
            WARN("reading from unsupported power management io address "
                        "0x%02x value 0x%x\n",
                        port, *valp);
            break;

      default:
            *valp = 0xff;
            goto unsupported;
      }
}

static void
NAME_(_outb_core)(
      struct cpssp *css,
      uint8_t val,
      uint16_t port
)
{
      switch (port) {
      case 0x00 ... 0x01:     /* Power Management Status */
            POWER_DEBUG_REG_PRINT(port, val, "writing PMSTS");
            /* Writing '1' clears the correspondent bit */
            css->NAME.pmsts &= ~(val << (port * 8));
            /* after clearing, check for irq reasons again */
            NAME_(checknset_sci)(css);
            break;
      
      case 0x02 ... 0x03:     /* Power Management Resume Enable */
            POWER_DEBUG_REG_PRINT(port, val, "writing PMEN");
            css->NAME.pmen =             (val  << ((port - 0x02) * 8))
                      | (css->NAME.pmen & (0xff << ((0x03 - port) * 8)));
            /* check if this should generate an irq */
            NAME_(checknset_sci)(css);
            break;
      
      case 0x05:        /* Power Management Control (Bits 8-15) */
            POWER_DEBUG_REG_PRINT(port, val, "writing PMCNTRL");
            if (val & (1 << 5)) {
                  /* Suspend Enable (SUS_EN)--R/W. [...] Writing this bit
                   * to a 1 causes the system to automatically sequence
                   * into the suspend state defined by the SUS_TYP field.
                   * This bit corresponds to the SLP_EN bit in ACPI
                   * specification */
                  NAME_(do_suspend)(css, (val >> 2) & 0x07);
                  val &= ~(1 << 5); /* clear SUS_EN bit */
            }
            css->NAME.pmcntrl = (val << 8) | (css->NAME.pmcntrl & 0xff);
            break;

      case 0x04:        /* Power Management Control (Bits 0-7) */
            POWER_DEBUG_REG_PRINT(port, val, "writing PMCNTRL");
            if (val & (1 << 2)) {
                  /* Global Release (GBL_RLS)--R/W.
                   * A 1 written to this bit position will cause an SMI#
                   * to be generated and BIOS_STS bit set if enabled by
                   * the BIOS_EN bit. 0=No SMI# generated. */
                  if (css->NAME.glben & (1 << 1) /* BIOS_EN */ ) {
                        css->NAME.glbsts |= (1 << 0); /* BIOS_STS */
                        DEBUGPRINT(DEBUG_PM_IRQ,
                              "GBL_RLS written and enabled => SMI\n");
                        NAME_(_smi_set)(css);
                  }
                  val &= ~(1 << 2); /* clear GBL_RLS bit */
            }
            css->NAME.pmcntrl = val | (css->NAME.pmcntrl & 0xff00);
            break;

      case 0x08 ... 0x0a:     /* Power Management Timer */
      case 0x0b:        /* Power Management Timer/Reserved */
            goto read_only;

      case 0x0c ... 0x0d:     /* General Purpose Status */
            POWER_DEBUG_REG_PRINT(port, val, "writing GPSTS (GPE0_BLK)");
            /* Writing '1' clears the correspondent bit.
             * Bits are only set by hardware. */
            css->NAME.gpsts &= ~(val << ((port - 0x0c) * 8));
            /* after clearing, check for irq reasons again */
            NAME_(checknset_sci)(css);
            break;

      case 0x0e ... 0x0f:     /* General Purpose Enable */
            POWER_DEBUG_REG_PRINT(port, val, "writing GPEN (GPE0_BLK)");
            css->NAME.gpen =             (val  << ((port - 0x0e) * 8))
                      | (css->NAME.gpen & (0xff << ((0x0f - port) * 8)));
            /* check if this should generate an irq */
            NAME_(checknset_sci)(css);
            break;

      case 0x10 ... 0x13:     /* Processor Control */
            WARN("writing 0x%02x to unsupported power management "
                        "processor control 0x%02x\n", val, port);
            break;

      case 0x14:        /* Processor Level 2 Power State Entry */
            /* Writes to this register have no effect */
            break;

      case 0x15:        /* Processor Level 3 Power State Entry */
            /* Writes to this register have no effect */
            break;

      case 0x18:        /* Global Status Register (bits 0-7) */
            POWER_DEBUG_REG_PRINT(port, val, "writing GLBSTS");
            /* Writing '1' clears bits 5, 2 and 0.
             * Bit 3 is reserved, bits 6, 7, 4, 1 are readonly */
            val &= (1 << 0) | (1 << 2) | (1 << 5); /* mask WC bits */
            css->NAME.glbsts &= ~val;
            break;

      case 0x19:        /* Global Status Register (bits 8-15) */
            POWER_DEBUG_REG_PRINT(port, val, "writing GLBSTS");
            /* Writing '1' clears the correspondent bit.
             * Bits 11, 10, 8 are only set by hardware.
             * Bits 12-15 and 9 are reserved. */
            css->NAME.glbsts &= ~(val << 8);
            break;

      case 0x20:        /* Global Enable Register (bits 0-7) */
            if (val & ~(1 << 1)) {
                  /* something other than BIOS_EN set */
                  WARN("writing unsupported bits in GLB_EN: value "
                        "0x%02x to port 0x%02x\n", val, port);
            }
            css->NAME.glben = val | (css->NAME.glben & 0xff00);
            break;

      case 0x21:        /* Global Enable Register (bits 8-15) */
            if (val) {
                  WARN("writing unsupported bits in GLB_EN: value "
                        "0x%02x to port 0x%02x\n", val, port);
            }
            css->NAME.glben = (val << 8) | (css->NAME.glben & 0xff);
            break;
                  

      case 0x28:        /* Global Control Register (bits 0-7) */
            POWER_DEBUG_REG_PRINT(port, val, "writing GLBCTL");
            if (val & (1 << 1)) {
                  /* BIOS Release (BIOS_RLS)
                   * A 1 written to this bit position causes an SCI to be
                   * generated and GBL_STS bit set, if enabled by the
                   * GBL_EN bit. 0=No SCI generated. */
                  css->NAME.pmsts |= (1 << 5); /* GBL_STS */
                  NAME_(checknset_sci)(css);
                  val &= ~(1 << 1); /* clear BIOS_RLS */
            }
            css->NAME.glbctl = val | (css->NAME.glbctl & 0xffffff00);
            break;
            
      case 0x29:        /* Global Control Register (bits 8-15) */
            POWER_DEBUG_REG_PRINT(port, val, "writing GLBCTL");
            css->NAME.glbctl =      (val << 8) | (css->NAME.glbctl & 0xffff00ff);
            break;
            
      case 0x2a:        /* Global Control Register (bits 16-23) */
            POWER_DEBUG_REG_PRINT(port, val, "writing GLBCTL");
            /* bits 17-23 reserved
             * bit 16: End of SMI (EOS) */
            val &= (1 << 0); /* mask reserved bits */
            /* only bit left is EOS */
            /* EOS can not be set until all SMI status bits are cleared */
            if ((val) && (css->NAME.glbsts)) {
                  val = 0;
            }
            /* if set, SMI line has to be driven inactive */
            if (val) {
                  NAME_(smi_out_set)(css, 0);
            }
            css->NAME.glbctl = (val << 16) | (css->NAME.glbctl & 0xff00ffff);
            break;

      case 0x2b:        /* Global Control Register (bits 24-31) */
            POWER_DEBUG_REG_PRINT(port, val, "writing GLBCTL");
            css->NAME.glbctl = (val << 24) | (css->NAME.glbctl & 0x00ffffff);
            break;
            
      case 0x30 ... 0x32:     /* General Purpose Input */
            goto read_only;

        case 0x34:
            goto unsupported;
        case 0x35:
            goto unsupported;
        case 0x36:
            goto unsupported;
        case 0x37:
            /* Bit 24 */
            /* CPU2 FAN Control; 0: enabled, 1: enabled */
            /* FIXME */

            /* Bit 28 */
            /* CPU1 FAN Control; 0: enabled, 1: enabled */
            /* FIXME */
            goto unsupported;

#if DEBUG_GIGABYTE /* debugging broken gigabyte bios */
      case 0x41:        /* Power Management Control (Bits 8-15) */
            POWER_DEBUG_REG_PRINT(0x5, val, "writing (wrong) PMCNTRL");
            if (val & (1 << 5)) {
                  NAME_(do_suspend)(css->NAME.pmcntrl >> 2) & 0x07);
            }
            val &= ~(1 << 5); /* clear SUS_EN bit */
            css->NAME.pmcntrl = (val << 8) | (css->NAME.pmcntrl & 0xff);
            break;

      case 0x40:        /* Power Management Control (Bits 0-7) */
            POWER_DEBUG_REG_PRINT(0x4, val, "writing (wrong) PMCNTRL");
            if (val & (1 << 2)) {
                  WARN("GBL_RLS set!\n");
            }
            css->NAME.pmcntrl = val | (css->NAME.pmcntrl & 0xff00);
            break;
#endif

      read_only:
            WARN("writing 0x%02x to read-only power management io address 0x%02x!\n",
                        val, port);
            break;

      unsupported:
      default:
            WARN("writing 0x%02x to unsupported power management io address 0x%02x\n",
                        val, port);
            break;
      }
}

static int
NAME_(isa_inb)(
      struct cpssp *css,
      uint8_t *valp,
      uint16_t port
)
{
      switch(port) {
      case 0x00b2:            /* APM Control Port (APMC). */
            /* reads return the last data written. Reads do not generate
             * an SMI. */
            *valp = css->NAME.apmc;
            break;

      case 0x00b3:            /* APM Status Port (APMS). */
            /* reads return the last data written. */
            *valp = css->NAME.apms;
            break;

      default:
            return 1;
      }
      DEBUGPRINT(DEBUG_PM_ISA, "reading register 0x%02x: returning 0x%02x\n",
                  port, *valp);
      return 0;
}

static int
NAME_(isa_outb)(
      struct cpssp *css,
      uint8_t val,
      uint16_t port
)
{
      switch(port) {
      case 0x00b2:            /* APM Control Port (APMC). */
            /* Writes to this register store data in the APMC Register. In
             * addition, writes generate an SMI, if the APMC_EN bit is set
             * to 1. */
            css->NAME.apmc = val;
            /* APMC_EN: Bit 1 of configspace address 0x5b */
            if (css->NAME.apmc_en) {
                  css->NAME.glbsts |= (1 << 5); /* APM_STS */
                  DEBUGPRINT(DEBUG_PM_IRQ,
                              "APMC written and enabled => SMI\n");
                  NAME_(_smi_set)(css);
            }
            break;

      case 0x00b3:            /* APM Status Port (APMS). */
            /* Writes store data in this register */
            css->NAME.apms = val;
            break;

      default:
            return 1;
      }
      DEBUGPRINT(DEBUG_PM_ISA, "writing register 0x%02x: value 0x%02x\n",
                  port, val);
      return 0;
}

static void
NAME_(_pci_inb)(
      struct cpssp *css,
      uint8_t *valp,
      uint16_t port
)
{
      NAME_(_inb_core)(css, valp, port);
}

static void
NAME_(_pci_inw)(
      struct cpssp *css,
      uint16_t *valp,
      uint16_t port
)
{
      unsigned char ret;

      assert((port & 0x01) == 0);

      switch (port) {
      case 0x08 ... 0x0a:     /* Power Management Timer */
            *valp = (NAME_(get_timer)(css)
                        >> ((port - 0x08) * 8)) & 0xffff;
            break;

      default:          /* other inw()s are mapped to inb()s */
            NAME_(_inb_core)(css, &ret, port);
            *valp = ret;
            NAME_(_inb_core)(css, &ret, port + 1);
            *valp += (ret << 8);
            break;
      }
}

static void
NAME_(_pci_inl)(
      struct cpssp *css,
      uint32_t *valp,
      uint16_t port
)
{
      unsigned char ret;
      assert((port & 0x03) == 0);

      switch (port) {
      case 0x08:        /* Power Management Timer */
            *valp = NAME_(get_timer)(css);
            break;

      default:          /* other inl()s are mapped to inb()s */
            NAME_(_inb_core)(css, &ret, port);
            *valp = ret;
            NAME_(_inb_core)(css, &ret, port + 1);
            *valp += (ret << 8);
            NAME_(_inb_core)(css, &ret, port + 2);
            *valp += (ret << 16);
            NAME_(_inb_core)(css, &ret, port + 3);
            *valp += (ret << 24);
            break;
      }
}

static void
NAME_(_pci_outb)(struct cpssp *css,
          uint8_t val, uint16_t port)
{
      NAME_(_outb_core)(css, val, port);
}

static void
NAME_(_pci_outw)(
      struct cpssp *css,
      uint16_t val,
      uint16_t port
)
{
      assert((port & 0x01) == 0);
      NAME_(_outb_core)(css, val & 0xff, port);
      NAME_(_outb_core)(css, (val >> 8) & 0xff, port + 1);
}

static void
NAME_(_pci_outl)(
      struct cpssp *css,
      uint32_t val,
      uint16_t port
)
{
      assert((port & 0x03) == 0);
      NAME_(_outb_core)(css, (val >>  0) & 0xff, port + 0);
      NAME_(_outb_core)(css, (val >>  8) & 0xff, port + 1);
      NAME_(_outb_core)(css, (val >> 16) & 0xff, port + 2);
      NAME_(_outb_core)(css, (val >> 24) & 0xff, port + 3);
}

/*
 * Wrapper functions for Power Management PCI {in,out}{b,w,l}
 * stolen from ume100.c, adjusted to evaluate the corresponding 'enable'-bit.
 */
#define CREATEIOFUNC(fname, vtype) \
static int \
NAME_(pci_##fname)(struct cpssp *css, vtype val, uint16_t port) \
{ \
      unsigned long iobase = css->NAME.pmba & (~1); \
      unsigned char enabled = (css->NAME.pmiose >> 0) & 1; \
      if (iobase && iobase <= port && port <= iobase + PM_IOLEN) { \
            if (enabled) { \
                  NAME_(_pci_##fname)(css, val, port - iobase); \
                  return 0; \
            } else { /* not enabled */ \
                  WARN("Power Management " # fname " for disabled io " \
                        "port 0x%04x\n", port); \
            } \
      } \
      return 1; \
}

CREATEIOFUNC(inb, uint8_t *);
CREATEIOFUNC(inw, uint16_t *);
CREATEIOFUNC(inl, uint32_t *);
CREATEIOFUNC(outb, uint8_t);
CREATEIOFUNC(outw, uint16_t);
CREATEIOFUNC(outl, uint32_t);

#undef CREATEIOFUNC

/* 
 * Wrapper functions for SMBus inb/outb.
 * These need a pointer to the css struct to find out
 * their base addresses.
 */
#define CREATEIOFUNC(fname, vtype) \
static int \
NAME_(smbus_##fname)(struct cpssp *css, vtype val, uint16_t port) \
{ \
      unsigned long iobase = css->NAME.smbba & (~1); \
      unsigned char enabled = css->NAME.iose; \
      if (iobase && iobase <= port && port < iobase + SMB_IOLEN) { \
            if (enabled) { \
                  NAME_(_smbus_##fname)(css, val, port - iobase); \
                  return 0; \
            } else { /* not enabled */ \
                  static int done = 1; \
                  \
                  if (! done) { \
                        WARN("SMBus " # fname " for disabled io " \
                              "port 0x%04x\n", port); \
                        done = 1; \
                  } \
            } \
      } \
      return 1; \
}

CREATEIOFUNC(inb, uint8_t *);
CREATEIOFUNC(outb, uint8_t);

#undef CREATEIOFUNC

/*
 * Wrappers for Power Management {in,out}{b,w,l} (PCI and ISA)
 * ISA registers must be accessed with 8-bit accesses
 */
static int
NAME_(inb)(struct cpssp *css, uint8_t * valp, uint16_t port)
{
      return (NAME_(pci_inb)(css, valp, port)
           && NAME_(isa_inb)(css, valp, port)
           && NAME_(smbus_inb)(css, valp, port));
}

static int
NAME_(outb)(struct cpssp *css, uint8_t val, uint16_t port)
{
      return (NAME_(pci_outb)(css, val, port)
           && NAME_(isa_outb)(css, val, port)
           && NAME_(smbus_outb)(css, val, port));
}

static int
NAME_(inw)(struct cpssp *css, uint16_t *valp, uint16_t port)
{
      return NAME_(pci_inw)(css, valp, port);
}

static int
NAME_(outw)(struct cpssp *css, uint16_t val, uint16_t port)
{
      return NAME_(pci_outw)(css, val, port);
}

static int
NAME_(inl)(struct cpssp *css, uint32_t *valp, uint16_t port)
{
      return NAME_(pci_inl)(css, valp, port);
}

static int
NAME_(outl)(struct cpssp *css, uint32_t val, uint16_t port)
{
      return NAME_(pci_outl)(css, val, port);
}

/*
 * PIIX4 Power Management Controller Configspace write
 */
static void
NAME_(cread)(
      struct cpssp *css,
      uint8_t addr,
      unsigned int bsel,
      uint32_t *valp
)
{
      assert(/* 0x00 <= addr && addr <= 0xff && */ (addr & 0x3) == 0);
      assert(0x1 <= bsel && bsel <= 0xf);

      *valp = 0x00000000;

#define RES(a, ab, nb)

#define RO(_a, _ab, c, nb) \
      if (addr == ((_a) & 0xfc)) { \
            unsigned int ab = (_a & 3) * 8 + _ab; \
            uint32_t mask = ((1 << nb) - 1) << ab; \
            if ((bsel >> 0) & 1) { \
                  *valp |= ((c) << ab) & mask & 0x000000ff; \
            } \
            if ((bsel >> 1) & 1) { \
                  *valp |= ((c) << ab) & mask & 0x0000ff00; \
            } \
            if ((bsel >> 2) & 1) { \
                  *valp |= ((c) << ab) & mask & 0x00ff0000; \
            } \
            if ((bsel >> 3) & 1) { \
                  *valp |= ((c) << ab) & mask & 0xff000000; \
            } \
      }

#define RW(_a, _ab, v, vb, nb, def, act) \
      if (addr == ((_a) & 0xfc)) { \
            unsigned int ab = (_a & 3) * 8 + _ab; \
            uint32_t mask = ((1 << nb) - 1) << ab; \
            if ((bsel >> 0) & 1) { \
                  *valp |= ((css->NAME.v >> vb) << ab) & mask & 0x000000ff; \
            } \
            if ((bsel >> 1) & 1) { \
                  *valp |= ((css->NAME.v >> vb) << ab) & mask & 0x0000ff00; \
            } \
            if ((bsel >> 2) & 1) { \
                  *valp |= ((css->NAME.v >> vb) << ab) & mask & 0x00ff0000; \
            } \
            if ((bsel >> 3) & 1) { \
                  *valp |= ((css->NAME.v >> vb) << ab) & mask & 0xff000000; \
            } \
      }

#define RWC(_a, _ab, v, vb, nb, def, act) \
      if (addr == ((_a) & 0xfc)) { \
            unsigned int ab = (_a & 3) * 8 + _ab; \
            uint32_t mask = ((1 << nb) - 1) << ab; \
            if ((bsel >> 0) & 1) { \
                  *valp |= ((css->NAME.v >> vb) << ab) & mask & 0x000000ff; \
            } \
            if ((bsel >> 1) & 1) { \
                  *valp |= ((css->NAME.v >> vb) << ab) & mask & 0x0000ff00; \
            } \
            if ((bsel >> 2) & 1) { \
                  *valp |= ((css->NAME.v >> vb) << ab) & mask & 0x00ff0000; \
            } \
            if ((bsel >> 3) & 1) { \
                  *valp |= ((css->NAME.v >> vb) << ab) & mask & 0xff000000; \
            } \
      }

#include "arch_power_management.regs"

#undef RWC
#undef RW
#undef RO
#undef RES
}

static void
NAME_(cwrite)(
      struct cpssp *css,
      uint8_t addr,
      unsigned int bsel,
      uint32_t val
)
{
      assert(/* 0x00 <= addr && addr <= 0xff && */ (addr & 0x3) == 0);
      assert(0x1 <= bsel && bsel <= 0xf);

#define RES(a, ab, nb)
#define RO(a, ab, c, nb)

#define RW(_a, _ab, v, vb, nb, def, act) \
      if (addr == ((_a) & 0xfc)) { \
            unsigned int ab = (_a & 3) * 8 + _ab; \
            uint32_t am = ((1 << nb) - 1) << ab; \
            if ((bsel >> 0) & 1) { \
                  css->NAME.v &= ~(((am & 0x000000ff) >> ab) << vb); \
                  css->NAME.v |= ((val & am & 0x000000ff) >> ab) << vb; \
            } \
            if ((bsel >> 1) & 1) { \
                  css->NAME.v &= ~(((am & 0x0000ff00) >> ab) << vb); \
                  css->NAME.v |= ((val & am & 0x0000ff00) >> ab) << vb; \
            } \
            if ((bsel >> 2) & 1) { \
                  css->NAME.v &= ~(((am & 0x00ff0000) >> ab) << vb); \
                  css->NAME.v |= ((val & am & 0x00ff0000) >> ab) << vb; \
            } \
            if ((bsel >> 3) & 1) { \
                  css->NAME.v &= ~(((am & 0xff000000) >> ab) << vb); \
                  css->NAME.v |= ((val & am & 0xff000000) >> ab) << vb; \
            } \
      }
#define RWC(_a, _ab, v, vb, nb, def, act) \
      if (addr == ((_a) & 0xfc)) { \
            unsigned int ab = (_a & 3) * 8 + _ab; \
            uint32_t am = ((1 << nb) - 1) << ab; \
            assert(nb == 1); \
            if ((bsel >> 0) & 1) { \
                  if (val & am & 0x000000ff) { \
                        css->NAME.v &= ~(1 << vb); \
                  } \
            } \
            if ((bsel >> 1) & 1) { \
                  if (val & am & 0x0000ff00) { \
                        css->NAME.v &= ~(1 << vb); \
                  } \
            } \
            if ((bsel >> 2) & 1) { \
                  if (val & am & 0x00ff0000) { \
                        css->NAME.v &= ~(1 << vb); \
                  } \
            } \
            if ((bsel >> 3) & 1) { \
                  if (val & am & 0xff000000) { \
                        css->NAME.v &= ~(1 << vb); \
                  } \
            } \
      }

#include "arch_power_management.regs"

#undef RWC
#undef RW
#undef RO
#undef RES
}

static void
NAME_(reset)(struct cpssp *css)
{
      css->NAME.pmsts = 0x0000;
      css->NAME.pmen = 0x0000;
      css->NAME.button_pressed = 0;
      css->NAME.pmcntrl = 0x0000;
      css->NAME.gpsts = 0x0000;
      css->NAME.gpen = 0x0000;
      css->NAME.apmc = 0;
      css->NAME.apms = 0;
      css->NAME.glben = 0;
      css->NAME.glbsts = 0;
      css->NAME.glbctl = 1 << 16;   /* Correct? FIXME VOSSI */
                              /* Award BIOS needs it. */

      /* Reset ConfigSpace for Power Management */
#define RES(a, ab, nb)
#define RO(a, ab, c, nb)
#define RW(a, ab, v, vb, nb, def, act) css->NAME.v = def;
#define RWC(a, ab, v, vb, nb, def, act) css->NAME.v = def;
#include "arch_power_management.regs"
#undef RWC
#undef RW
#undef RO
#undef RES

#if 0
      /* FIXME VOSSI */
      /* Should be done by BIOS */

      /* DEVRESA and DEVRESB initializations are stolen from QEMU.
       * these a) should be done by the BIOS and b) are unused anyway,
       * but maybe it will make Solaris happy */
      /* initialize high byte of DEVRESA */
      pci_setconfigb(css->NAME.config_space, 0x5f,
                    0x80 /* Device 8 EIO enable */
                  | 0x10 /* Device 11 Keyboard enable */ );
      /* initialize high byte of DEVRESB */
      pci_setconfigb(css->NAME.config_space, 0x63,
                    0x40 /* Keyboard EIO Enable */
                  | 0x20 /* Device 5 EIO Enable (FDC) */ );
#endif
}

static void
NAME_(power_set)(struct cpssp *css, unsigned int val)
{
      if (val) {
            /*
             * Power On Event
             */
            /* Enter soft off state. */
            NAME_(n_susc_set)(css, 1);

      } else {
            /*
             * Power Off Event
             */
            /* To make shure no interrupts are generated by the timer. */
            css->NAME.pmen = 0x0000;
            /* To make shure power button override isn't triggered. */
            css->NAME.button_pressed = 0; 
            css->NAME.apmc = 0;
      }
}

static void
NAME_(init)(struct cpssp *css)
{
      /* to make shure no interrupts are generated by the timer */
      css->NAME.pmen = 0x0000;
      /* timer runs off a 3.579545 MHz clock; callback is called whenever
       * highest bit (bit 23) toggles. */
      /* assumption: TIME_HZ < 2^40 - true until we get 1099GHz-CPUs */
      assert(TIME_HZ < (1LL << 40));

      css->NAME.tsc_per_timer = (1LL << 23) * TIME_HZ / 3579545;
      css->NAME.tsc_per_tick = (TIME_HZ / 3579545) + 1;
      css->NAME.timer_high = 0;

      css->NAME.tsc_passed = time_virt();
      time_call_at(css->NAME.tsc_passed + css->NAME.tsc_per_timer,
                    NAME_(timer_event), css);
}

#undef WARN

#endif /* BEHAVIOR */

Generated by  Doxygen 1.6.0   Back to index