Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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#include "init.h"
 17
 18struct agdi_data {
 19	int sdei_event;
 20};
 21
 22static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg)
 23{
 24	nmi_panic(regs, "Arm Generic Diagnostic Dump and Reset SDEI event issued");
 25	return 0;
 26}
 27
 28static int agdi_sdei_probe(struct platform_device *pdev,
 29			   struct agdi_data *adata)
 30{
 31	int err;
 32
 33	err = sdei_event_register(adata->sdei_event, agdi_sdei_handler, pdev);
 34	if (err) {
 35		dev_err(&pdev->dev, "Failed to register for SDEI event %d",
 36			adata->sdei_event);
 37		return err;
 38	}
 39
 40	err = sdei_event_enable(adata->sdei_event);
 41	if (err)  {
 42		sdei_event_unregister(adata->sdei_event);
 43		dev_err(&pdev->dev, "Failed to enable event %d\n",
 44			adata->sdei_event);
 45		return err;
 46	}
 47
 48	return 0;
 49}
 50
 51static int agdi_probe(struct platform_device *pdev)
 52{
 53	struct agdi_data *adata = dev_get_platdata(&pdev->dev);
 54
 55	if (!adata)
 56		return -EINVAL;
 57
 58	return agdi_sdei_probe(pdev, adata);
 59}
 60
 61static void agdi_remove(struct platform_device *pdev)
 62{
 63	struct agdi_data *adata = dev_get_platdata(&pdev->dev);
 64	int err, i;
 65
 66	err = sdei_event_disable(adata->sdei_event);
 67	if (err) {
 68		dev_err(&pdev->dev, "Failed to disable sdei-event #%d (%pe)\n",
 69			adata->sdei_event, ERR_PTR(err));
 70		return;
 71	}
 72
 73	for (i = 0; i < 3; i++) {
 74		err = sdei_event_unregister(adata->sdei_event);
 75		if (err != -EINPROGRESS)
 76			break;
 77
 78		schedule();
 79	}
 80
 81	if (err)
 82		dev_err(&pdev->dev, "Failed to unregister sdei-event #%d (%pe)\n",
 83			adata->sdei_event, ERR_PTR(err));
 84}
 85
 86static struct platform_driver agdi_driver = {
 87	.driver = {
 88		.name = "agdi",
 89	},
 90	.probe = agdi_probe,
 91	.remove = agdi_remove,
 92};
 93
 94void __init acpi_agdi_init(void)
 95{
 96	struct acpi_table_agdi *agdi_table;
 97	struct agdi_data pdata;
 98	struct platform_device *pdev;
 99	acpi_status status;
100
101	status = acpi_get_table(ACPI_SIG_AGDI, 0,
102				(struct acpi_table_header **) &agdi_table);
103	if (ACPI_FAILURE(status))
104		return;
105
106	if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) {
107		pr_warn("Interrupt signaling is not supported");
108		goto err_put_table;
109	}
110
111	pdata.sdei_event = agdi_table->sdei_event;
112
113	pdev = platform_device_register_data(NULL, "agdi", 0, &pdata, sizeof(pdata));
114	if (IS_ERR(pdev))
115		goto err_put_table;
116
117	if (platform_driver_register(&agdi_driver))
118		platform_device_unregister(pdev);
119
120err_put_table:
121	acpi_put_table((struct acpi_table_header *)agdi_table);
122}