aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/apei/bert.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/apei/bert.c')
-rw-r--r--drivers/acpi/apei/bert.c65
1 files changed, 51 insertions, 14 deletions
diff --git a/drivers/acpi/apei/bert.c b/drivers/acpi/apei/bert.c
index 598fd19b65fa..2c5c8d2b30f1 100644
--- a/drivers/acpi/apei/bert.c
+++ b/drivers/acpi/apei/bert.c
@@ -24,6 +24,7 @@
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/io.h>
+#include <linux/of.h>
#include "apei-internal.h"
@@ -33,6 +34,26 @@
static int bert_disable;
+static struct acpi_table_bert *__read_mostly bert_tab;
+
+/*
+ * Checks device tree for support of bed-bert [driver].
+ * This driver supports BERT in the absence of ACPI.
+ * on entry:
+ * void
+ * returns:
+ * true if bed-bert support found in Device Tree else false
+ */
+static bool bed_bert_present_dt(void)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_name(NULL, "bed-bert");
+ of_node_put(np);
+
+ return !!np;
+}
+
static void __init bert_print_all(struct acpi_bert_region *region,
unsigned int region_len)
{
@@ -85,10 +106,10 @@ static int __init setup_bert_disable(char *str)
}
__setup("bert_disable", setup_bert_disable);
-static int __init bert_check_table(struct acpi_table_bert *bert_tab)
+static int __init bert_check_table(struct acpi_table_bert *bert)
{
- if (bert_tab->header.length < sizeof(struct acpi_table_bert) ||
- bert_tab->region_length < sizeof(struct acpi_bert_region))
+ if (bert->header.length < sizeof(struct acpi_table_bert) ||
+ bert->region_length < sizeof(struct acpi_bert_region))
return -EINVAL;
return 0;
@@ -98,12 +119,12 @@ static int __init bert_init(void)
{
struct apei_resources bert_resources;
struct acpi_bert_region *boot_error_region;
- struct acpi_table_bert *bert_tab;
unsigned int region_len;
acpi_status status;
int rc = 0;
- if (acpi_disabled)
+ /* permit BERT initialization if either ACPI or GHES_BERT is present */
+ if (acpi_disabled && !bed_bert_present_dt())
return 0;
if (bert_disable) {
@@ -111,7 +132,12 @@ static int __init bert_init(void)
return 0;
}
- status = acpi_get_table(ACPI_SIG_BERT, 0, (struct acpi_table_header **)&bert_tab);
+ /* BERT table may have been initialized by bert_table_set() */
+ if (bert_tab)
+ status = AE_OK;
+ else
+ status = acpi_get_table(ACPI_SIG_BERT, 0,
+ (struct acpi_table_header **)&bert_tab);
if (status == AE_NOT_FOUND)
return 0;
@@ -128,13 +154,15 @@ static int __init bert_init(void)
region_len = bert_tab->region_length;
apei_resources_init(&bert_resources);
- rc = apei_resources_add(&bert_resources, bert_tab->address,
- region_len, true);
- if (rc)
- goto out_put_bert_tab;
- rc = apei_resources_request(&bert_resources, "APEI BERT");
- if (rc)
- goto out_fini;
+ if (!acpi_disabled) {
+ rc = apei_resources_add(&bert_resources, bert_tab->address,
+ region_len, true);
+ if (rc)
+ goto out_put_bert_tab;
+ rc = apei_resources_request(&bert_resources, "APEI BERT");
+ if (rc)
+ goto out_fini;
+ }
boot_error_region = ioremap_cache(bert_tab->address, region_len);
if (boot_error_region) {
bert_print_all(boot_error_region, region_len);
@@ -143,7 +171,8 @@ static int __init bert_init(void)
rc = -ENOMEM;
}
- apei_resources_release(&bert_resources);
+ if (!acpi_disabled)
+ apei_resources_release(&bert_resources);
out_fini:
apei_resources_fini(&bert_resources);
out_put_bert_tab:
@@ -152,4 +181,12 @@ out_put_bert_tab:
return rc;
}
+/*
+ * This allows the BERT to be initialized externally, in the absence of ACPI.
+ */
+void __init bert_table_set(struct acpi_table_bert *table)
+{
+ bert_tab = table;
+}
+
late_initcall(bert_init);