RISC-V: Add ACPI initialization in setup_arch()
authorSunil V L <sunilvl@ventanamicro.com>
Mon, 15 May 2023 05:49:15 +0000 (11:19 +0530)
committerPalmer Dabbelt <palmer@rivosinc.com>
Thu, 1 Jun 2023 15:45:03 +0000 (08:45 -0700)
Initialize the ACPI core for RISC-V during boot.

ACPI tables and interpreter are initialized based on
the information passed from the firmware and the value of
the kernel parameter 'acpi'.

With ACPI support added for RISC-V, the kernel parameter 'acpi'
is also supported on RISC-V. Hence, update the documentation.

Signed-off-by: Sunil V L <sunilvl@ventanamicro.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Link: https://lore.kernel.org/r/20230515054928.2079268-9-sunilvl@ventanamicro.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
Documentation/admin-guide/kernel-parameters.txt
arch/riscv/kernel/acpi.c
arch/riscv/kernel/setup.c

index 9e5bab2..d910fba 100644 (file)
@@ -1,17 +1,17 @@
-       acpi=           [HW,ACPI,X86,ARM64]
+       acpi=           [HW,ACPI,X86,ARM64,RISCV64]
                        Advanced Configuration and Power Interface
                        Format: { force | on | off | strict | noirq | rsdt |
                                  copy_dsdt }
                        force -- enable ACPI if default was off
-                       on -- enable ACPI but allow fallback to DT [arm64]
+                       on -- enable ACPI but allow fallback to DT [arm64,riscv64]
                        off -- disable ACPI if default was on
                        noirq -- do not use ACPI for IRQ routing
                        strict -- Be less tolerant of platforms that are not
                                strictly ACPI specification compliant.
                        rsdt -- prefer RSDT over (default) XSDT
                        copy_dsdt -- copy DSDT to memory
-                       For ARM64, ONLY "acpi=off", "acpi=on" or "acpi=force"
-                       are available
+                       For ARM64 and RISCV64, ONLY "acpi=off", "acpi=on" or
+                       "acpi=force" are available
 
                        See also Documentation/power/runtime_pm.rst, pci=noacpi
 
index 81d448c..7c080c8 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/acpi.h>
 #include <linux/io.h>
 #include <linux/pci.h>
+#include <linux/efi.h>
 
 int acpi_noirq = 1;            /* skip ACPI IRQ initialization */
 int acpi_disabled = 1;
@@ -24,6 +25,131 @@ EXPORT_SYMBOL(acpi_disabled);
 int acpi_pci_disabled = 1;     /* skip ACPI PCI scan and IRQ initialization */
 EXPORT_SYMBOL(acpi_pci_disabled);
 
+static bool param_acpi_off __initdata;
+static bool param_acpi_on __initdata;
+static bool param_acpi_force __initdata;
+
+static int __init parse_acpi(char *arg)
+{
+       if (!arg)
+               return -EINVAL;
+
+       /* "acpi=off" disables both ACPI table parsing and interpreter */
+       if (strcmp(arg, "off") == 0)
+               param_acpi_off = true;
+       else if (strcmp(arg, "on") == 0) /* prefer ACPI over DT */
+               param_acpi_on = true;
+       else if (strcmp(arg, "force") == 0) /* force ACPI to be enabled */
+               param_acpi_force = true;
+       else
+               return -EINVAL; /* Core will print when we return error */
+
+       return 0;
+}
+early_param("acpi", parse_acpi);
+
+/*
+ * acpi_fadt_sanity_check() - Check FADT presence and carry out sanity
+ *                           checks on it
+ *
+ * Return 0 on success,  <0 on failure
+ */
+static int __init acpi_fadt_sanity_check(void)
+{
+       struct acpi_table_header *table;
+       struct acpi_table_fadt *fadt;
+       acpi_status status;
+       int ret = 0;
+
+       /*
+        * FADT is required on riscv; retrieve it to check its presence
+        * and carry out revision and ACPI HW reduced compliancy tests
+        */
+       status = acpi_get_table(ACPI_SIG_FADT, 0, &table);
+       if (ACPI_FAILURE(status)) {
+               const char *msg = acpi_format_exception(status);
+
+               pr_err("Failed to get FADT table, %s\n", msg);
+               return -ENODEV;
+       }
+
+       fadt = (struct acpi_table_fadt *)table;
+
+       /*
+        * The revision in the table header is the FADT's Major revision. The
+        * FADT also has a minor revision, which is stored in the FADT itself.
+        *
+        * TODO: Currently, we check for 6.5 as the minimum version to check
+        * for HW_REDUCED flag. However, once RISC-V updates are released in
+        * the ACPI spec, we need to update this check for exact minor revision
+        */
+       if (table->revision < 6 || (table->revision == 6 && fadt->minor_revision < 5))
+               pr_err(FW_BUG "Unsupported FADT revision %d.%d, should be 6.5+\n",
+                      table->revision, fadt->minor_revision);
+
+       if (!(fadt->flags & ACPI_FADT_HW_REDUCED)) {
+               pr_err("FADT not ACPI hardware reduced compliant\n");
+               ret = -EINVAL;
+       }
+
+       /*
+        * acpi_get_table() creates FADT table mapping that
+        * should be released after parsing and before resuming boot
+        */
+       acpi_put_table(table);
+       return ret;
+}
+
+/*
+ * acpi_boot_table_init() called from setup_arch(), always.
+ *     1. find RSDP and get its address, and then find XSDT
+ *     2. extract all tables and checksums them all
+ *     3. check ACPI FADT HW reduced flag
+ *
+ * We can parse ACPI boot-time tables such as MADT after
+ * this function is called.
+ *
+ * On return ACPI is enabled if either:
+ *
+ * - ACPI tables are initialized and sanity checks passed
+ * - acpi=force was passed in the command line and ACPI was not disabled
+ *   explicitly through acpi=off command line parameter
+ *
+ * ACPI is disabled on function return otherwise
+ */
+void __init acpi_boot_table_init(void)
+{
+       /*
+        * Enable ACPI instead of device tree unless
+        * - ACPI has been disabled explicitly (acpi=off), or
+        * - firmware has not populated ACPI ptr in EFI system table
+        *   and ACPI has not been [force] enabled (acpi=on|force)
+        */
+       if (param_acpi_off ||
+           (!param_acpi_on && !param_acpi_force &&
+            efi.acpi20 == EFI_INVALID_TABLE_ADDR))
+               return;
+
+       /*
+        * ACPI is disabled at this point. Enable it in order to parse
+        * the ACPI tables and carry out sanity checks
+        */
+       enable_acpi();
+
+       /*
+        * If ACPI tables are initialized and FADT sanity checks passed,
+        * leave ACPI enabled and carry on booting; otherwise disable ACPI
+        * on initialization error.
+        * If acpi=force was passed on the command line it forces ACPI
+        * to be enabled even if its initialization failed.
+        */
+       if (acpi_table_init() || acpi_fadt_sanity_check()) {
+               pr_err("Failed to init ACPI tables\n");
+               if (!param_acpi_force)
+                       disable_acpi();
+       }
+}
+
 /*
  * __acpi_map_table() will be called before paging_init(), so early_ioremap()
  * or early_memremap() should be called here to for ACPI table mapping.
index 9fb8390..45df7cc 100644 (file)
@@ -8,6 +8,7 @@
  *  Nick Kossifidis <mick@ics.forth.gr>
  */
 
+#include <linux/acpi.h>
 #include <linux/cpu.h>
 #include <linux/init.h>
 #include <linux/mm.h>
@@ -276,6 +277,10 @@ void __init setup_arch(char **cmdline_p)
 
        efi_init();
        paging_init();
+
+       /* Parse the ACPI tables for possible boot-time configuration */
+       acpi_boot_table_init();
+
 #if IS_ENABLED(CONFIG_BUILTIN_DTB)
        unflatten_and_copy_device_tree();
 #else