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, &regvala, &regvalb);
+
+  /* Write to Poweroff Control Register */
+
+  outw(regvala | 0x2000, pm1a_cnt);
+  if (pm1b_cnt != 0)
+    {
+      outw(regvalb | 0x2000, pm1b_cnt);
+    }
+
+  while (1);
+}

Reply via email to