Add acpi poweroff

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>
This commit is contained in:
Xiaoguang Sun 2013-05-23 19:06:13 +08:00 committed by Ben Gras
parent 4fb3945025
commit 26428d4bc6
3 changed files with 189 additions and 0 deletions

View file

@ -12,6 +12,20 @@ struct acpi_rsdp acpi_rsdp;
static acpi_read_t read_func; static acpi_read_t read_func;
#define MAX_RSDT 35 /* ACPI defines 35 signatures */ #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 { static struct acpi_rsdt {
struct acpi_sdt_header hdr; struct acpi_sdt_header hdr;
@ -24,6 +38,10 @@ static struct {
} sdt_trans[MAX_RSDT]; } sdt_trans[MAX_RSDT];
static int sdt_count; 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) 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; 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) void acpi_init(void)
{ {
int s, i; int s, i;
@ -239,6 +337,8 @@ void acpi_init(void)
sdt_trans[i].signature[ACPI_SDT_SIGNATURE_LEN] = '\0'; sdt_trans[i].signature[ACPI_SDT_SIGNATURE_LEN] = '\0';
sdt_trans[i].length = hdr.length; sdt_trans[i].length = hdr.length;
} }
acpi_init_poweroff();
} }
struct acpi_madt_ioapic * acpi_get_ioapic_next(void) struct acpi_madt_ioapic * acpi_get_ioapic_next(void)
@ -293,3 +393,19 @@ struct acpi_madt_lapic * acpi_get_lapic_next(void)
return ret; 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);
}
}

View file

@ -30,6 +30,70 @@ struct acpi_sdt_header {
u32_t creator_revision; 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_madt_hdr {
struct acpi_sdt_header hdr; struct acpi_sdt_header hdr;
u32_t local_apic_address; u32_t local_apic_address;
@ -84,6 +148,8 @@ struct acpi_madt_nmi {
void acpi_init(void); void acpi_init(void);
void acpi_poweroff(void);
/* /*
* Returns a pointer to the io acpi structure in the MADT table in ACPI. The * 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 * pointer is valid only until paging is turned off. No memory is allocated in

View file

@ -22,6 +22,10 @@
#include "direct_utils.h" #include "direct_utils.h"
#include <machine/multiboot.h> #include <machine/multiboot.h>
#ifdef USE_ACPI
#include "acpi.h"
#endif
#define KBCMDP 4 /* kbd controller port (O) */ #define KBCMDP 4 /* kbd controller port (O) */
#define KBC_PULSE0 0xfe /* pulse output bit 0 */ #define KBC_PULSE0 0xfe /* pulse output bit 0 */
#define IO_KBD 0x060 /* 8042 Keyboard */ #define IO_KBD 0x060 /* 8042 Keyboard */
@ -85,6 +89,9 @@ poweroff(void)
{ {
const char *shutdown_str; const char *shutdown_str;
#ifdef USE_ACPI
acpi_poweroff();
#endif
/* Bochs/QEMU poweroff */ /* Bochs/QEMU poweroff */
shutdown_str = "Shutdown"; shutdown_str = "Shutdown";
while (*shutdown_str) outb(0x8900, *(shutdown_str++)); while (*shutdown_str) outb(0x8900, *(shutdown_str++));