]> Zhao Yanbai Git Server - minix.git/commitdiff
Add acpi poweroff
authorXiaoguang Sun <sun.xiaoguang@yoyosys.com>
Thu, 23 May 2013 11:06:13 +0000 (19:06 +0800)
committerBen Gras <ben@minix3.org>
Wed, 29 May 2013 16:12:33 +0000 (16:12 +0000)
Use acpi poweroff if it's possible.

Change-Id: I103cc288523bf63fa536750b1d408ac88bbe35fb
Signed-off-by: Ben Gras <ben@minix3.org>
Signed-off-by: Tomas Hruby <tom@minix3.org>
kernel/arch/i386/acpi.c
kernel/arch/i386/acpi.h
kernel/arch/i386/arch_reset.c

index 225467ba2c4bdfeed8b4ace262bb646bfc373229..7770f336cf75eb7ad48008c573b2454d9c7dec2b 100644 (file)
@@ -12,6 +12,20 @@ struct acpi_rsdp acpi_rsdp;
 static acpi_read_t read_func;
 
 #define MAX_RSDT       35 /* ACPI defines 35 signatures */
+#define SLP_EN_CODE    (1 << 13) /* ACPI SLP_EN_CODE code */
+#define AMI_PACKAGE_OP_CODE (0x12)
+#define AMI_NAME_OP_CODE (0x8)
+#define AMI_BYTE_PREFIX_CODE (0xA)
+#define AMI_PACKAGE_LENGTH_ENCODING_BITS_MASK (0xC0)
+#define AMI_PACKAGE_LENGTH_ENCODING_BITS_SHIFT (6)
+#define AMI_MIN_PACKAGE_LENGTH (1)
+#define AMI_NUM_ELEMENTS_LENGTH (1)
+#define AMI_SLP_TYPA_SHIFT (10)
+#define AMI_SLP_TYPB_SHIFT (10)
+#define AMI_S5_NAME_OP_OFFSET_1 (-1)
+#define AMI_S5_NAME_OP_OFFSET_2 (-2)
+#define AMI_S5_PACKAGE_OP_OFFSET (4)
+#define AMI_S5_PACKET_LENGTH_OFFSET (5)
 
 static struct acpi_rsdt {
        struct acpi_sdt_header  hdr;
@@ -24,6 +38,10 @@ static struct {
 } sdt_trans[MAX_RSDT];
 
 static int sdt_count;
+static u16_t pm1a_cnt_blk = 0;
+static u16_t pm1b_cnt_blk = 0;
+static u16_t slp_typa = 0;
+static u16_t slp_typb = 0;
 
 static int acpi_check_csum(struct acpi_sdt_header * tb, size_t size)
 {
@@ -210,6 +228,86 @@ static int get_acpi_rsdp(void)
        return 0;
 }
 
+static void acpi_init_poweroff(void)
+{
+       u8_t *ptr = NULL;
+       u8_t *start = NULL;
+       u8_t *end = NULL;
+       struct acpi_fadt_header *fadt_header = NULL;
+       struct acpi_rsdt * dsdt_header = NULL;
+       char *msg = NULL;
+
+       /* Everything used here existed since ACPI spec 1.0 */
+       /* So we can safely use them */
+       fadt_header = (struct acpi_fadt_header *)
+               acpi_phys2vir(acpi_get_table_base("FACP"));
+       if (fadt_header == NULL) {
+               msg = "Could not load FACP";
+               goto exit;
+       }
+
+       dsdt_header = (struct acpi_rsdt *)
+               acpi_phys2vir((phys_bytes) fadt_header->dsdt);
+       if (dsdt_header == NULL) {
+               msg = "Could not load DSDT";
+               goto exit;
+       }
+
+       pm1a_cnt_blk = fadt_header->pm1a_cnt_blk;
+       pm1b_cnt_blk = fadt_header->pm1b_cnt_blk;
+
+       ptr = start = (u8_t *) dsdt_header->data;
+       end = start + dsdt_header->hdr.length - 4;
+
+       /* See http://forum.osdev.org/viewtopic.php?t=16990 */
+       /* for layout of \_S5 */
+       while (ptr < end && memcmp(ptr, "_S5_", 4) != 0)
+               ptr++;
+
+       msg = "Could not read S5 data. Use default SLP_TYPa and SLP_TYPb";
+       if (ptr >= end || ptr == start)
+               goto exit;
+
+       /* validate AML structure */
+       if (*(ptr + AMI_S5_PACKAGE_OP_OFFSET) != AMI_PACKAGE_OP_CODE)
+               goto exit;
+
+       if ((ptr < start + (-AMI_S5_NAME_OP_OFFSET_2) ||
+               (*(ptr + AMI_S5_NAME_OP_OFFSET_2) != AMI_NAME_OP_CODE ||
+                *(ptr + AMI_S5_NAME_OP_OFFSET_2 + 1) != '\\')) &&
+               *(ptr + AMI_S5_NAME_OP_OFFSET_1) != AMI_NAME_OP_CODE)
+               goto exit;
+
+       ptr += AMI_S5_PACKET_LENGTH_OFFSET;
+       if (ptr >= end)
+               goto exit;
+
+       /* package length */
+       ptr += ((*ptr & AMI_PACKAGE_LENGTH_ENCODING_BITS_MASK) >>
+               AMI_PACKAGE_LENGTH_ENCODING_BITS_SHIFT) +
+               AMI_MIN_PACKAGE_LENGTH + AMI_NUM_ELEMENTS_LENGTH;
+       if (ptr >= end)
+               goto exit;
+
+       if (*ptr == AMI_BYTE_PREFIX_CODE)
+               ptr++; /* skip byte prefix */
+
+       slp_typa = (*ptr) << AMI_SLP_TYPA_SHIFT;
+
+       ptr++; /* move to SLP_TYPb */
+       if (*ptr == AMI_BYTE_PREFIX_CODE)
+               ptr++; /* skip byte prefix */
+
+       slp_typb = (*ptr) << AMI_SLP_TYPB_SHIFT;
+
+       msg = "poweroff initialized";
+
+exit:
+       if (msg) {
+               printf("acpi: %s\n", msg);
+       }
+}
+
 void acpi_init(void)
 {
        int s, i;
@@ -239,6 +337,8 @@ void acpi_init(void)
                sdt_trans[i].signature[ACPI_SDT_SIGNATURE_LEN] = '\0';
                sdt_trans[i].length = hdr.length;
        }
+
+       acpi_init_poweroff();
 }
 
 struct acpi_madt_ioapic * acpi_get_ioapic_next(void)
@@ -293,3 +393,19 @@ struct acpi_madt_lapic * acpi_get_lapic_next(void)
 
        return ret;
 }
+
+void __k_unpaged_acpi_poweroff(void)
+{
+       /* NO OP poweroff symbol*/
+}
+
+void acpi_poweroff(void)
+{
+       if (pm1a_cnt_blk == 0) {
+               return;
+       }
+       outw(pm1a_cnt_blk, slp_typa | SLP_EN_CODE);
+       if (pm1b_cnt_blk != 0) {
+               outw(pm1b_cnt_blk, slp_typb | SLP_EN_CODE);
+       }
+}
index ddbd50419f2eef2a6a5176a754808b55adfd5efd..06e978c9ae99cd08b758e8571c9eca4535598d39 100644 (file)
@@ -30,6 +30,70 @@ struct acpi_sdt_header {
        u32_t   creator_revision;
 };
 
+struct acpi_generic_address {
+       u8_t address_space_id;
+       u8_t register_bit_width;
+       u8_t register_bit_offset;
+       u8_t access_size;
+       u64_t address;
+};
+
+struct acpi_fadt_header
+{
+       struct acpi_sdt_header hdr;
+       u32_t facs;
+       u32_t dsdt;
+       u8_t model;
+       u8_t preferred_pm_profile;
+       u16_t sci_int;
+       u32_t smi_cmd;
+       u8_t acpi_enable;
+       u8_t acpi_disable;
+       u8_t s4bios_req;
+       u8_t pstate_cnt;
+       u32_t pm1a_evt_blk;
+       u32_t pm1b_evt_blk;
+       u32_t pm1a_cnt_blk;
+       u32_t pm1b_cnt_blk;
+       u32_t pm2_cnt_blk;
+       u32_t pm_tmr_blk;
+       u32_t gpe0_blk;
+       u32_t gpe1_blk;
+       u8_t pm1_evt_len;
+       u8_t pm1_cnt_len;
+       u8_t pm2_cnt_len;
+       u8_t pm_tmr_len;
+       u8_t gpe0_blk_len;
+       u8_t gpe1_blk_len;
+       u8_t gpe1_base;
+       u8_t cst_cnt;
+       u16_t p_lvl2_lat;
+       u16_t p_lvl3_lat;
+       u16_t flush_size;
+       u16_t flush_stride;
+       u8_t duty_offset;
+       u8_t duty_width;
+       u8_t day_alrm;
+       u8_t mon_alrm;
+       u8_t century;
+       u16_t iapc_boot_arch;
+       u8_t reserved1;
+       u32_t flags;
+       struct acpi_generic_address reset_reg;
+       u8_t reset_value;
+       u8_t reserved2[3];
+       u64_t xfacs;
+       u64_t xdsdt;
+       struct acpi_generic_address xpm1a_evt_blk;
+       struct acpi_generic_address xpm1b_evt_blk;
+       struct acpi_generic_address xpm1a_cnt_blk;
+       struct acpi_generic_address xpm1b_cnt_blk;
+       struct acpi_generic_address xpm2_cnt_blk;
+       struct acpi_generic_address xpm_tmr_blk;
+       struct acpi_generic_address xgpe0_blk;
+       struct acpi_generic_address xgpe1_blk;
+};
+
 struct acpi_madt_hdr {
        struct acpi_sdt_header  hdr;
        u32_t                   local_apic_address;
@@ -84,6 +148,8 @@ struct acpi_madt_nmi {
 
 void acpi_init(void);
 
+void acpi_poweroff(void);
+
 /* 
  * Returns a pointer to the io acpi structure in the MADT table in ACPI. The
  * pointer is valid only until paging is turned off. No memory is allocated in
index 2aad6f08f8d8b9911f34d773add03725277e9bab..89b43a4a15cf98c19e4af11847b7d55debaa5dc1 100644 (file)
 #include "direct_utils.h"
 #include <machine/multiboot.h>
 
+#ifdef USE_ACPI
+#include "acpi.h"
+#endif
+
 #define     KBCMDP          4       /* kbd controller port (O) */
 #define      KBC_PULSE0     0xfe    /* pulse output bit 0 */
 #define      IO_KBD          0x060           /* 8042 Keyboard */
@@ -85,6 +89,9 @@ poweroff(void)
 {
        const char *shutdown_str;
 
+#ifdef USE_ACPI
+       acpi_poweroff();
+#endif
        /* Bochs/QEMU poweroff */
        shutdown_str = "Shutdown";
         while (*shutdown_str) outb(0x8900, *(shutdown_str++));