Skip to content

Commit f7d5e9e

Browse files
Kazuhiro-Abe-fjwilldeacon
authored andcommitted
ACPI: AGDI: Add interrupt signaling mode support
AGDI has two types of signaling modes: SDEI and interrupt. Currently, the AGDI driver only supports SDEI. Therefore, add support for interrupt signaling mode. The interrupt vector is retrieved from the AGDI table, and call panic function when an interrupt occurs. Acked-by: Hanjun Guo <guohanjun@huawei.com> Reviewed-by: Ilkka Koskinen <ilkka@os.amperecomputing.com> Signed-off-by: Kazuhiro Abe <fj1078ii@aa.jp.fujitsu.com> Signed-off-by: Will Deacon <will@kernel.org>
1 parent e97e3e3 commit f7d5e9e

1 file changed

Lines changed: 92 additions & 9 deletions

File tree

drivers/acpi/arm64/agdi.c

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
#include "init.h"
1717

1818
struct agdi_data {
19+
unsigned char flags; /* AGDI Signaling Mode */
1920
int sdei_event;
21+
unsigned int gsiv;
22+
bool use_nmi;
23+
int irq;
2024
};
2125

2226
static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg)
@@ -48,19 +52,73 @@ static int agdi_sdei_probe(struct platform_device *pdev,
4852
return 0;
4953
}
5054

55+
static irqreturn_t agdi_interrupt_handler_nmi(int irq, void *dev_id)
56+
{
57+
nmi_panic(NULL, "Arm Generic Diagnostic Dump and Reset NMI Interrupt event issued\n");
58+
return IRQ_HANDLED;
59+
}
60+
61+
static irqreturn_t agdi_interrupt_handler_irq(int irq, void *dev_id)
62+
{
63+
panic("Arm Generic Diagnostic Dump and Reset Interrupt event issued\n");
64+
return IRQ_HANDLED;
65+
}
66+
67+
static int agdi_interrupt_probe(struct platform_device *pdev,
68+
struct agdi_data *adata)
69+
{
70+
unsigned long irq_flags;
71+
int ret;
72+
int irq;
73+
74+
irq = acpi_register_gsi(NULL, adata->gsiv, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_HIGH);
75+
if (irq < 0) {
76+
dev_err(&pdev->dev, "cannot register GSI#%d (%d)\n", adata->gsiv, irq);
77+
return irq;
78+
}
79+
80+
irq_flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_AUTOEN |
81+
IRQF_NO_THREAD;
82+
/* try NMI first */
83+
ret = request_nmi(irq, &agdi_interrupt_handler_nmi, irq_flags,
84+
"agdi_interrupt_nmi", NULL);
85+
if (!ret) {
86+
enable_nmi(irq);
87+
adata->irq = irq;
88+
adata->use_nmi = true;
89+
return 0;
90+
}
91+
92+
/* Then try normal interrupt */
93+
ret = request_irq(irq, &agdi_interrupt_handler_irq,
94+
irq_flags, "agdi_interrupt_irq", NULL);
95+
if (ret) {
96+
dev_err(&pdev->dev, "cannot register IRQ %d\n", ret);
97+
acpi_unregister_gsi(adata->gsiv);
98+
return ret;
99+
}
100+
enable_irq(irq);
101+
adata->irq = irq;
102+
103+
return 0;
104+
}
105+
51106
static int agdi_probe(struct platform_device *pdev)
52107
{
53108
struct agdi_data *adata = dev_get_platdata(&pdev->dev);
54109

55110
if (!adata)
56111
return -EINVAL;
57112

58-
return agdi_sdei_probe(pdev, adata);
113+
if (adata->flags & ACPI_AGDI_SIGNALING_MODE)
114+
return agdi_interrupt_probe(pdev, adata);
115+
else
116+
return agdi_sdei_probe(pdev, adata);
59117
}
60118

61-
static void agdi_remove(struct platform_device *pdev)
119+
static void agdi_sdei_remove(struct platform_device *pdev,
120+
struct agdi_data *adata)
62121
{
63-
struct agdi_data *adata = dev_get_platdata(&pdev->dev);
64122
int err, i;
65123

66124
err = sdei_event_disable(adata->sdei_event);
@@ -83,6 +141,30 @@ static void agdi_remove(struct platform_device *pdev)
83141
adata->sdei_event, ERR_PTR(err));
84142
}
85143

144+
static void agdi_interrupt_remove(struct platform_device *pdev,
145+
struct agdi_data *adata)
146+
{
147+
if (adata->irq == -1)
148+
return;
149+
150+
if (adata->use_nmi)
151+
free_nmi(adata->irq, NULL);
152+
else
153+
free_irq(adata->irq, NULL);
154+
155+
acpi_unregister_gsi(adata->gsiv);
156+
}
157+
158+
static void agdi_remove(struct platform_device *pdev)
159+
{
160+
struct agdi_data *adata = dev_get_platdata(&pdev->dev);
161+
162+
if (adata->flags & ACPI_AGDI_SIGNALING_MODE)
163+
agdi_interrupt_remove(pdev, adata);
164+
else
165+
agdi_sdei_remove(pdev, adata);
166+
}
167+
86168
static struct platform_driver agdi_driver = {
87169
.driver = {
88170
.name = "agdi",
@@ -94,7 +176,7 @@ static struct platform_driver agdi_driver = {
94176
void __init acpi_agdi_init(void)
95177
{
96178
struct acpi_table_agdi *agdi_table;
97-
struct agdi_data pdata;
179+
struct agdi_data pdata = { 0 };
98180
struct platform_device *pdev;
99181
acpi_status status;
100182

@@ -103,12 +185,13 @@ void __init acpi_agdi_init(void)
103185
if (ACPI_FAILURE(status))
104186
return;
105187

106-
if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) {
107-
pr_warn("Interrupt signaling is not supported");
108-
goto err_put_table;
109-
}
188+
if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE)
189+
pdata.gsiv = agdi_table->gsiv;
190+
else
191+
pdata.sdei_event = agdi_table->sdei_event;
110192

111-
pdata.sdei_event = agdi_table->sdei_event;
193+
pdata.irq = -1;
194+
pdata.flags = agdi_table->flags;
112195

113196
pdev = platform_device_register_data(NULL, "agdi", 0, &pdata, sizeof(pdata));
114197
if (IS_ERR(pdev))

0 commit comments

Comments
 (0)