diff mbox series

[2/2] ACPI: AGDI: Add driver for Arm Generic Diagnostic Dump and Reset device

Message ID 20211203024311.49865-3-ilkka@os.amperecomputing.com (mailing list archive)
State New, archived
Headers show
Series ACPI: Arm Generic Diagnostic Dump and Reset device | expand

Commit Message

Ilkka Koskinen Dec. 3, 2021, 2:43 a.m. UTC
ACPI for Arm Components 1.1 Platform Design Document v1.1 [0] specifices
Arm Generic Diagnostic Device Interface (AGDI). It allows an admin to
issue diagnostic dump and reset via an SDEI event or an interrupt.
This patch implements SDEI path.

[0] https://developer.arm.com/documentation/den0093/latest/

Signed-off-by: Ilkka Koskinen <ilkka@os.amperecomputing.com>
---
 drivers/acpi/arm64/Kconfig  |   8 +++
 drivers/acpi/arm64/Makefile |   1 +
 drivers/acpi/arm64/agdi.c   | 133 ++++++++++++++++++++++++++++++++++++
 3 files changed, 142 insertions(+)
 create mode 100644 drivers/acpi/arm64/agdi.c

Comments

Russell King (Oracle) Dec. 9, 2021, 4:36 p.m. UTC | #1
Hi,

On Thu, Dec 02, 2021 at 06:43:11PM -0800, Ilkka Koskinen wrote:
> +static int __init agdi_init(void)
> +{
> +	int ret;
> +	acpi_status status;
> +	struct acpi_table_agdi *agdi_table;
> +	struct agdi_data *pdata;
> +	struct platform_device *pdev;
> +
> +	if (acpi_disabled)
> +		return 0;
> +
> +	status = acpi_get_table(ACPI_SIG_AGDI, 0,
> +				(struct acpi_table_header **) &agdi_table);
> +	if (ACPI_FAILURE(status))
> +		return -ENODEV;
> +
> +	pdata = kzalloc(sizeof(*pdata), GFP_ATOMIC);

Why does this need to be GFP_ATOMIC? Also, struct agdi_data is a single
int in size, why do you need to kzalloc() it?

> +	if (!pdata) {
> +		ret = -ENOMEM;
> +		goto err_put_table;
> +	}
> +
> +	if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) {
> +		pr_warn("Interrupt signaling is not supported");
> +		ret = -ENODEV;
> +		goto err_free_pdata;
> +	}
> +
> +	pdata->sdei_event = agdi_table->sdei_event;
> +
> +	pdev = platform_device_register_data(NULL, "agdi", 0, pdata, sizeof(*pdata));

platform_device_register_data() uses kmemdup() internally with the
platform data, meaning it takes a copy of the platform data. There is
no need for the pdata allocation to persist past this point. Hence,
given that it is a single int in size, you may as well put
"struct agdi_data" on the stack.

Thanks.
Ilkka Koskinen Dec. 10, 2021, 8:32 a.m. UTC | #2
Hi,

Thanks for reviewing the patch

On Thu, 9 Dec 2021, Russell King (Oracle) wrote:
> Hi,
>
> On Thu, Dec 02, 2021 at 06:43:11PM -0800, Ilkka Koskinen wrote:
>> +static int __init agdi_init(void)
>> +{
>> +	int ret;
>> +	acpi_status status;
>> +	struct acpi_table_agdi *agdi_table;
>> +	struct agdi_data *pdata;
>> +	struct platform_device *pdev;
>> +
>> +	if (acpi_disabled)
>> +		return 0;
>> +
>> +	status = acpi_get_table(ACPI_SIG_AGDI, 0,
>> +				(struct acpi_table_header **) &agdi_table);
>> +	if (ACPI_FAILURE(status))
>> +		return -ENODEV;
>> +
>> +	pdata = kzalloc(sizeof(*pdata), GFP_ATOMIC);
>
> Why does this need to be GFP_ATOMIC? Also, struct agdi_data is a single
> int in size, why do you need to kzalloc() it?

There's no reason for that. It should obviously be GFP_KERNEL

>
>> +	if (!pdata) {
>> +		ret = -ENOMEM;
>> +		goto err_put_table;
>> +	}
>> +
>> +	if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) {
>> +		pr_warn("Interrupt signaling is not supported");
>> +		ret = -ENODEV;
>> +		goto err_free_pdata;
>> +	}
>> +
>> +	pdata->sdei_event = agdi_table->sdei_event;
>> +
>> +	pdev = platform_device_register_data(NULL, "agdi", 0, pdata, sizeof(*pdata));
>
> platform_device_register_data() uses kmemdup() internally with the
> platform data, meaning it takes a copy of the platform data. There is
> no need for the pdata allocation to persist past this point. Hence,
> given that it is a single int in size, you may as well put
> "struct agdi_data" on the stack.

I somehow missed kmemdup() part. I remove the allocation and move pdata to 
the stack instead.

Thanks,
Ilkka


>
> Thanks.
>
> -- 
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
>
diff mbox series

Patch

diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
index 6dba187f4f2e..24869ba5b365 100644
--- a/drivers/acpi/arm64/Kconfig
+++ b/drivers/acpi/arm64/Kconfig
@@ -8,3 +8,11 @@  config ACPI_IORT
 
 config ACPI_GTDT
 	bool
+
+config ACPI_AGDI
+	bool "Arm Generic Diagnostic Dump and Reset Device Interface"
+	depends on ARM_SDE_INTERFACE
+	help
+	  Arm Generic Diagnostic Dump and Reset Device Interface (AGDI) is
+	  a standard that enables issuing a non-maskable diagnostic dump and
+	  reset command.
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
index 66acbe77f46e..7b9e4045659d 100644
--- a/drivers/acpi/arm64/Makefile
+++ b/drivers/acpi/arm64/Makefile
@@ -1,4 +1,5 @@ 
 # SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ACPI_AGDI) 	+= agdi.o
 obj-$(CONFIG_ACPI_IORT) 	+= iort.o
 obj-$(CONFIG_ACPI_GTDT) 	+= gtdt.o
 obj-y				+= dma.o
diff --git a/drivers/acpi/arm64/agdi.c b/drivers/acpi/arm64/agdi.c
new file mode 100644
index 000000000000..cdd8811df3d5
--- /dev/null
+++ b/drivers/acpi/arm64/agdi.c
@@ -0,0 +1,133 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * This file implements handling of
+ * Arm Generic Diagnostic Dump and Reset Interface table (AGDI)
+ *
+ * Copyright (c) 2021, Ampere Computing LLC
+ */
+
+#define pr_fmt(fmt) "ACPI: AGDI: " fmt
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/arm_sdei.h>
+#include <linux/io.h>
+
+struct agdi_data {
+	int sdei_event;
+};
+
+static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg)
+{
+	nmi_panic(regs, "Arm Generic Diagnostic Dump and Reset SDEI event issued");
+	return 0;
+}
+
+static int agdi_sdei_probe(struct platform_device *pdev,
+			   struct agdi_data *adata)
+{
+	int err;
+
+	err = sdei_event_register(adata->sdei_event, agdi_sdei_handler, pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to register for SDEI event %d",
+			adata->sdei_event);
+		return err;
+	}
+
+	err = sdei_event_enable(adata->sdei_event);
+	if (err)  {
+		sdei_event_unregister(adata->sdei_event);
+		dev_err(&pdev->dev, "Failed to enable event %d\n",
+			adata->sdei_event);
+		return err;
+	}
+
+	return 0;
+}
+
+static int agdi_probe(struct platform_device *pdev)
+{
+	struct agdi_data *adata;
+
+	adata = dev_get_platdata(&pdev->dev);
+	if (!adata)
+		return -EINVAL;
+
+	return agdi_sdei_probe(pdev, adata);
+}
+
+static int agdi_remove(struct platform_device *pdev)
+{
+	struct agdi_data *adata = platform_get_drvdata(pdev);
+
+	sdei_event_disable(adata->sdei_event);
+	sdei_event_unregister(adata->sdei_event);
+
+	return 0;
+}
+
+static struct platform_driver agdi_driver = {
+	.driver = {
+		.name = "agdi",
+	},
+	.probe = agdi_probe,
+	.remove = agdi_remove,
+};
+
+static int __init agdi_init(void)
+{
+	int ret;
+	acpi_status status;
+	struct acpi_table_agdi *agdi_table;
+	struct agdi_data *pdata;
+	struct platform_device *pdev;
+
+	if (acpi_disabled)
+		return 0;
+
+	status = acpi_get_table(ACPI_SIG_AGDI, 0,
+				(struct acpi_table_header **) &agdi_table);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_ATOMIC);
+	if (!pdata) {
+		ret = -ENOMEM;
+		goto err_put_table;
+	}
+
+	if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) {
+		pr_warn("Interrupt signaling is not supported");
+		ret = -ENODEV;
+		goto err_free_pdata;
+	}
+
+	pdata->sdei_event = agdi_table->sdei_event;
+
+	pdev = platform_device_register_data(NULL, "agdi", 0, pdata, sizeof(*pdata));
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		goto err_free_pdata;
+	}
+
+	ret = platform_driver_register(&agdi_driver);
+	if (ret)
+		goto err_device_unregister;
+
+	acpi_put_table((struct acpi_table_header *)agdi_table);
+	return 0;
+
+err_device_unregister:
+	platform_device_unregister(pdev);
+err_free_pdata:
+	kfree(pdata);
+err_put_table:
+	acpi_put_table((struct acpi_table_header *)agdi_table);
+	return ret;
+}
+device_initcall(agdi_init);