Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI: AGDI: Add driver for Arm Generic Diagnostic Dump and Reset device

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>
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Ilkka Koskinen and committed by
Rafael J. Wysocki
a2a591fb 3f8dec11

+142
+10
drivers/acpi/arm64/Kconfig
··· 8 8 9 9 config ACPI_GTDT 10 10 bool 11 + 12 + config ACPI_AGDI 13 + bool "Arm Generic Diagnostic Dump and Reset Device Interface" 14 + depends on ARM_SDE_INTERFACE 15 + help 16 + Arm Generic Diagnostic Dump and Reset Device Interface (AGDI) is 17 + a standard that enables issuing a non-maskable diagnostic dump and 18 + reset command. 19 + 20 + If set, the kernel parses AGDI table and listens for the command.
+1
drivers/acpi/arm64/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 + obj-$(CONFIG_ACPI_AGDI) += agdi.o 2 3 obj-$(CONFIG_ACPI_IORT) += iort.o 3 4 obj-$(CONFIG_ACPI_GTDT) += gtdt.o 4 5 obj-y += dma.o
+116
drivers/acpi/arm64/agdi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * This file implements handling of 4 + * Arm Generic Diagnostic Dump and Reset Interface table (AGDI) 5 + * 6 + * Copyright (c) 2022, Ampere Computing LLC 7 + */ 8 + 9 + #define pr_fmt(fmt) "ACPI: AGDI: " fmt 10 + 11 + #include <linux/acpi.h> 12 + #include <linux/arm_sdei.h> 13 + #include <linux/io.h> 14 + #include <linux/kernel.h> 15 + #include <linux/platform_device.h> 16 + 17 + struct agdi_data { 18 + int sdei_event; 19 + }; 20 + 21 + static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg) 22 + { 23 + nmi_panic(regs, "Arm Generic Diagnostic Dump and Reset SDEI event issued"); 24 + return 0; 25 + } 26 + 27 + static int agdi_sdei_probe(struct platform_device *pdev, 28 + struct agdi_data *adata) 29 + { 30 + int err; 31 + 32 + err = sdei_event_register(adata->sdei_event, agdi_sdei_handler, pdev); 33 + if (err) { 34 + dev_err(&pdev->dev, "Failed to register for SDEI event %d", 35 + adata->sdei_event); 36 + return err; 37 + } 38 + 39 + err = sdei_event_enable(adata->sdei_event); 40 + if (err) { 41 + sdei_event_unregister(adata->sdei_event); 42 + dev_err(&pdev->dev, "Failed to enable event %d\n", 43 + adata->sdei_event); 44 + return err; 45 + } 46 + 47 + return 0; 48 + } 49 + 50 + static int agdi_probe(struct platform_device *pdev) 51 + { 52 + struct agdi_data *adata = dev_get_platdata(&pdev->dev); 53 + 54 + if (!adata) 55 + return -EINVAL; 56 + 57 + return agdi_sdei_probe(pdev, adata); 58 + } 59 + 60 + static int agdi_remove(struct platform_device *pdev) 61 + { 62 + struct agdi_data *adata = dev_get_platdata(&pdev->dev); 63 + int err, i; 64 + 65 + err = sdei_event_disable(adata->sdei_event); 66 + if (err) 67 + return err; 68 + 69 + for (i = 0; i < 3; i++) { 70 + err = sdei_event_unregister(adata->sdei_event); 71 + if (err != -EINPROGRESS) 72 + break; 73 + 74 + schedule(); 75 + } 76 + 77 + return err; 78 + } 79 + 80 + static struct platform_driver agdi_driver = { 81 + .driver = { 82 + .name = "agdi", 83 + }, 84 + .probe = agdi_probe, 85 + .remove = agdi_remove, 86 + }; 87 + 88 + void __init acpi_agdi_init(void) 89 + { 90 + struct acpi_table_agdi *agdi_table; 91 + struct agdi_data pdata; 92 + struct platform_device *pdev; 93 + acpi_status status; 94 + 95 + status = acpi_get_table(ACPI_SIG_AGDI, 0, 96 + (struct acpi_table_header **) &agdi_table); 97 + if (ACPI_FAILURE(status)) 98 + return; 99 + 100 + if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) { 101 + pr_warn("Interrupt signaling is not supported"); 102 + goto err_put_table; 103 + } 104 + 105 + pdata.sdei_event = agdi_table->sdei_event; 106 + 107 + pdev = platform_device_register_data(NULL, "agdi", 0, &pdata, sizeof(pdata)); 108 + if (IS_ERR(pdev)) 109 + goto err_put_table; 110 + 111 + if (platform_driver_register(&agdi_driver)) 112 + platform_device_unregister(pdev); 113 + 114 + err_put_table: 115 + acpi_put_table((struct acpi_table_header *)agdi_table); 116 + }
+2
drivers/acpi/bus.c
··· 26 26 #include <asm/mpspec.h> 27 27 #include <linux/dmi.h> 28 28 #endif 29 + #include <linux/acpi_agdi.h> 29 30 #include <linux/acpi_iort.h> 30 31 #include <linux/acpi_viot.h> 31 32 #include <linux/pci.h> ··· 1342 1341 acpi_debugger_init(); 1343 1342 acpi_setup_sb_notify_handler(); 1344 1343 acpi_viot_init(); 1344 + acpi_agdi_init(); 1345 1345 return 0; 1346 1346 } 1347 1347
+13
include/linux/acpi_agdi.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + 3 + #ifndef __ACPI_AGDI_H__ 4 + #define __ACPI_AGDI_H__ 5 + 6 + #include <linux/acpi.h> 7 + 8 + #ifdef CONFIG_ACPI_AGDI 9 + void __init acpi_agdi_init(void); 10 + #else 11 + static inline void acpi_agdi_init(void) {} 12 + #endif 13 + #endif /* __ACPI_AGDI_H__ */