This is an automated email from the ASF dual-hosted git repository. ligd pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit e074c865bcaa6e6e268a59d1d783425d4a02fb32 Author: liwenxiang1 <[email protected]> AuthorDate: Fri Oct 18 20:29:59 2024 +0800 arch/x86_64: add poweroff functionality add poweroff feature Signed-off-by: liwenxiang1 <[email protected]> --- arch/Kconfig | 1 + arch/x86_64/include/acpi.h | 46 +++++++++ arch/x86_64/src/common/x86_64_acpi.c | 116 +++++++++++++++++++++++ arch/x86_64/src/intel64/CMakeLists.txt | 4 + arch/x86_64/src/intel64/Make.defs | 4 + arch/x86_64/src/intel64/intel64_systempoweroff.c | 67 +++++++++++++ 6 files changed, 238 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index 1eb8f833636..7e3396308ad 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -150,6 +150,7 @@ config ARCH_X86_64 select ARCH_HAVE_FORK if !BUILD_KERNEL select ARCH_HAVE_SETJMP select ARCH_HAVE_PERF_EVENTS + select ARCH_HAVE_POWEROFF ---help--- x86-64 architectures. diff --git a/arch/x86_64/include/acpi.h b/arch/x86_64/include/acpi.h index ac5a6e930eb..eb322c6be4a 100644 --- a/arch/x86_64/include/acpi.h +++ b/arch/x86_64/include/acpi.h @@ -135,6 +135,14 @@ #define ACPI_LAPIC_FLAGS_ONLINECAP (1 << 1) #define ACPI_LAPIC_FLAGS_RESERVED (0xfffffffc) +/* ACPI Machine Language */ + +#define ACPI_AML_NAME_OP (0x08) +#define ACPI_AML_PACKAGE_OP (0x12) +#define ACPI_AML_BYTE_PREFIX (0x0A) +#define ACPI_AML_ROOT_PREFIX '\\' +#define ACPI_AML_S5_NAME "_S5_" + /**************************************************************************** * Public Types ****************************************************************************/ @@ -185,6 +193,33 @@ begin_packed_struct struct acpi_xsdt_s uint64_t table_ptrs; } end_packed_struct; +/* Fixed ACPI Description Table */ + +begin_packed_struct struct acpi_facp_s +{ + struct acpi_sdt_s sdt; /* Standard ACPI table header */ + uint32_t firmware_ctrl; /* Physical address of FACS table */ + uint32_t dsdt; /* Physical address of DSDT table */ + uint8_t reserved; /* Reserved field, usually 0 */ + uint8_t pre_pm_profile; /* Preferred power management profile */ + uint16_t sci_int; /* System Control Interrupt (SCI) number */ + uint32_t smi_cmd; /* SMI command port address */ + uint8_t acpi_enable; /* Command to enable ACPI */ + uint8_t acpi_disable; /* Command to disable ACPI */ + uint8_t s4bios_req; /* Command for S4BIOS (hibernate request) */ + uint8_t pstate_cnt; /* Processor performance state control */ + uint32_t pm1a_evt_blk; /* Address of PM1a event register block */ + uint32_t pm1b_evt_blk; /* Address of PM1b event register block */ + uint32_t pm1a_cnt_blk; /* Address of PM1a control register block */ + uint32_t pm1b_cnt_blk; /* Address of PM1b control register block */ + uint32_t pm2_cnt_blk; /* Address of PM2 control register block */ + uint32_t pm_tmr_blk; /* Address of power management timer block */ + uint32_t gpe0_blk; /* Address of General Purpose Event 0 register block */ + uint32_t gpe1_blk; /* Address of General Purpose Event 1 register block */ + uint8_t pm1_evt_len; /* Length of PM1 event register block */ + uint8_t pm1_cnt_len; /* Length of PM1 control register block */ +} end_packed_struct; + /* Common structure for tables entry */ begin_packed_struct struct acpi_entry_s @@ -286,6 +321,17 @@ int acpi_madt_get(int type, int n, struct acpi_entry_s **entry); int acpi_lapic_get(int cpu, struct acpi_lapic_s **lapic); +/**************************************************************************** + * Name: acpi_poweroff_param_get + * + * Description: + * Get Poweroff Parm . + * + ****************************************************************************/ + +int acpi_poweroff_param_get(uint32_t *pm1a_cnt, uint32_t *pm1b_cnt, + uint32_t *regvala, uint32_t *regvalb); + #ifdef CONFIG_ARCH_X86_64_ACPI_DUMP /**************************************************************************** * Name: acpi_dump diff --git a/arch/x86_64/src/common/x86_64_acpi.c b/arch/x86_64/src/common/x86_64_acpi.c index ac30c097d2b..ab6179d1d68 100644 --- a/arch/x86_64/src/common/x86_64_acpi.c +++ b/arch/x86_64/src/common/x86_64_acpi.c @@ -632,6 +632,122 @@ int acpi_lapic_get(int cpu, struct acpi_lapic_s **lapic) (struct acpi_entry_s **)lapic); } +#ifdef CONFIG_BOARDCTL_POWEROFF +/**************************************************************************** + * Name: acpi_poweroff_param_get + * + * Description: + * Get Poweroff Parm . + * + ****************************************************************************/ + +int acpi_poweroff_param_get(uint32_t *pm1a_cnt, uint32_t *pm1b_cnt, + uint32_t *regvala, uint32_t *regvalb) +{ + void *tps = NULL; + uint32_t *tp32 = NULL; + uint32_t *end32 = NULL; + struct acpi_sdt_s *sdt = NULL; + struct acpi_facp_s *facp = NULL; + struct acpi_sdt_s *dsdt_h = NULL; + char *s5_addr = NULL; + int dsdt_len = 0; + + if (g_acpi.rsdt == 0) + { + acpi_info("rsdt is null"); + return -EINVAL; + } + + tps = &g_acpi.rsdt->table_ptrs; + tp32 = (uint32_t *)tps; + end32 = (uint32_t *)((uintptr_t)g_acpi.rsdt + g_acpi.rsdt->sdt.length); + + while (tp32 < end32) + { + sdt = (struct acpi_sdt_s *)(uintptr_t)*tp32; + + if (strncmp(sdt->signature, ACPI_SIG_FACP, 4) == 0) + { + facp = (struct acpi_facp_s *)(uintptr_t)*tp32; + dsdt_h = (struct acpi_sdt_s *)(uintptr_t)facp->dsdt; + acpi_map_region((uintptr_t)dsdt_h, sizeof(struct acpi_sdt_s)); + + if (strncmp(dsdt_h->signature, ACPI_SIG_DSDT, 4) == 0) + { + /* search the _S5_ package in the DSDT, skip header */ + + s5_addr = (char *)(uintptr_t)facp->dsdt + + sizeof(struct acpi_sdt_s); + dsdt_len = dsdt_h->length - sizeof(struct acpi_sdt_s); + acpi_map_region((uintptr_t)s5_addr, dsdt_len); + + while (dsdt_len-- > 0) + { + if (strncmp(s5_addr, ACPI_AML_S5_NAME, 4) == 0) + { + break; + } + + s5_addr++; + } + + if (dsdt_len > 0) + { + /* check for valid AML structure */ + + if ((*(s5_addr - 1) == ACPI_AML_NAME_OP + || (*(s5_addr - 2) == ACPI_AML_NAME_OP + && *(s5_addr - 1) == ACPI_AML_ROOT_PREFIX)) + && *(s5_addr + 4) == ACPI_AML_PACKAGE_OP) + { + s5_addr += strlen(ACPI_AML_S5_NAME) + 1; + + /* calculate PkgLength size */ + + s5_addr += ((*s5_addr & 0xc0) >> 6) + 2; + + if (*s5_addr == ACPI_AML_BYTE_PREFIX) + { + /* skip byteprefix */ + + s5_addr++; + } + + *regvala = *(s5_addr) << 10; + s5_addr++; + + if (*s5_addr == ACPI_AML_BYTE_PREFIX) + { + /* skip byteprefix */ + + s5_addr++; + } + + *regvalb = *(s5_addr) << 10; + *pm1a_cnt = facp->pm1a_cnt_blk; + *pm1b_cnt = facp->pm1b_cnt_blk; + + return OK; + } + else + { + acpi_info("\\_S5 parse error"); + break; + } + } + } + } + + /* Next table */ + + tp32 += 1; + } + + return -ENOENT; +} +#endif + #ifdef CONFIG_ARCH_X86_64_ACPI_DUMP /**************************************************************************** * Name: acpi_dump diff --git a/arch/x86_64/src/intel64/CMakeLists.txt b/arch/x86_64/src/intel64/CMakeLists.txt index d42b1890b94..41d39f2f218 100644 --- a/arch/x86_64/src/intel64/CMakeLists.txt +++ b/arch/x86_64/src/intel64/CMakeLists.txt @@ -51,6 +51,10 @@ if(CONFIG_ENABLE_ALL_SIGNALS) list(APPEND SRCS intel64_schedulesigaction.c intel64_sigdeliver.c) endif() +if(CONFIG_BOARDCTL_POWEROFF) + list(APPEND SRCS intel64_systempoweroff.c) +endif() + if(CONFIG_X86_64_UNWINDER_FRAME_POINTER) list(APPEND SRCS intel64_backtrace_fp.c) endif() diff --git a/arch/x86_64/src/intel64/Make.defs b/arch/x86_64/src/intel64/Make.defs index 8d7674703e3..79843c76400 100644 --- a/arch/x86_64/src/intel64/Make.defs +++ b/arch/x86_64/src/intel64/Make.defs @@ -39,6 +39,10 @@ ifeq ($(CONFIG_ENABLE_ALL_SIGNALS),y) CMN_CSRCS += intel64_schedulesigaction.c intel64_sigdeliver.c endif +ifeq ($(CONFIG_BOARDCTL_POWEROFF),y) + CMN_CSRCS += intel64_systempoweroff.c +endif + ifeq ($(CONFIG_X86_64_UNWINDER_FRAME_POINTER),y) CMN_CSRCS += intel64_backtrace_fp.c endif diff --git a/arch/x86_64/src/intel64/intel64_systempoweroff.c b/arch/x86_64/src/intel64/intel64_systempoweroff.c new file mode 100644 index 00000000000..b3368dffccf --- /dev/null +++ b/arch/x86_64/src/intel64/intel64_systempoweroff.c @@ -0,0 +1,67 @@ +/**************************************************************************** + * arch/x86_64/src/intel64/intel64_systempoweroff.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <nuttx/arch.h> +#include <arch/io.h> + +#include <stdint.h> +#include <arch/acpi.h> + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_systempoweroff + * + * Description: + * Internal, intel64 poweroff logic. + * + ****************************************************************************/ + +void up_systempoweroff(void) +{ + uint32_t pm1a_cnt = 0; + uint32_t pm1b_cnt = 0; + uint32_t regvala = 0; + uint32_t regvalb = 0; + + acpi_poweroff_param_get(&pm1a_cnt, &pm1b_cnt, ®vala, ®valb); + + /* Write to Poweroff Control Register */ + + outw(regvala | 0x2000, pm1a_cnt); + if (pm1b_cnt != 0) + { + outw(regvalb | 0x2000, pm1b_cnt); + } + + while (1); +}
