0001
0002
0003
0004
0005
0006
0007
0008
0009 #define pr_fmt(fmt) "ACPI: AGDI: " fmt
0010
0011 #include <linux/acpi.h>
0012 #include <linux/acpi_agdi.h>
0013 #include <linux/arm_sdei.h>
0014 #include <linux/io.h>
0015 #include <linux/kernel.h>
0016 #include <linux/platform_device.h>
0017
0018 struct agdi_data {
0019 int sdei_event;
0020 };
0021
0022 static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg)
0023 {
0024 nmi_panic(regs, "Arm Generic Diagnostic Dump and Reset SDEI event issued");
0025 return 0;
0026 }
0027
0028 static int agdi_sdei_probe(struct platform_device *pdev,
0029 struct agdi_data *adata)
0030 {
0031 int err;
0032
0033 err = sdei_event_register(adata->sdei_event, agdi_sdei_handler, pdev);
0034 if (err) {
0035 dev_err(&pdev->dev, "Failed to register for SDEI event %d",
0036 adata->sdei_event);
0037 return err;
0038 }
0039
0040 err = sdei_event_enable(adata->sdei_event);
0041 if (err) {
0042 sdei_event_unregister(adata->sdei_event);
0043 dev_err(&pdev->dev, "Failed to enable event %d\n",
0044 adata->sdei_event);
0045 return err;
0046 }
0047
0048 return 0;
0049 }
0050
0051 static int agdi_probe(struct platform_device *pdev)
0052 {
0053 struct agdi_data *adata = dev_get_platdata(&pdev->dev);
0054
0055 if (!adata)
0056 return -EINVAL;
0057
0058 return agdi_sdei_probe(pdev, adata);
0059 }
0060
0061 static int agdi_remove(struct platform_device *pdev)
0062 {
0063 struct agdi_data *adata = dev_get_platdata(&pdev->dev);
0064 int err, i;
0065
0066 err = sdei_event_disable(adata->sdei_event);
0067 if (err)
0068 return err;
0069
0070 for (i = 0; i < 3; i++) {
0071 err = sdei_event_unregister(adata->sdei_event);
0072 if (err != -EINPROGRESS)
0073 break;
0074
0075 schedule();
0076 }
0077
0078 return err;
0079 }
0080
0081 static struct platform_driver agdi_driver = {
0082 .driver = {
0083 .name = "agdi",
0084 },
0085 .probe = agdi_probe,
0086 .remove = agdi_remove,
0087 };
0088
0089 void __init acpi_agdi_init(void)
0090 {
0091 struct acpi_table_agdi *agdi_table;
0092 struct agdi_data pdata;
0093 struct platform_device *pdev;
0094 acpi_status status;
0095
0096 status = acpi_get_table(ACPI_SIG_AGDI, 0,
0097 (struct acpi_table_header **) &agdi_table);
0098 if (ACPI_FAILURE(status))
0099 return;
0100
0101 if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) {
0102 pr_warn("Interrupt signaling is not supported");
0103 goto err_put_table;
0104 }
0105
0106 pdata.sdei_event = agdi_table->sdei_event;
0107
0108 pdev = platform_device_register_data(NULL, "agdi", 0, &pdata, sizeof(pdata));
0109 if (IS_ERR(pdev))
0110 goto err_put_table;
0111
0112 if (platform_driver_register(&agdi_driver))
0113 platform_device_unregister(pdev);
0114
0115 err_put_table:
0116 acpi_put_table((struct acpi_table_header *)agdi_table);
0117 }