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 } 117