Commit 64f89dfa authored by Hitomi Hasegawa's avatar Hitomi Hasegawa Committed by Arnd Bergmann

soc: fujitsu: Add A64FX diagnostic interrupt driver

Register the NMI/IRQ corresponding to the A64FX's device definition
dedicated to diagnostic interrupts, so that when this interrupt is
sent using the BMC, it causes a panic. This can be used to obtain
a kernel dump.
Signed-off-by: default avatarHitomi Hasegawa <hasegawa-hitomi@fujitsu.com>
Link: https://lore.kernel.org/r/20220520074119.3574753-2-hasegawa-hitomi@fujitsu.com'
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parent 3c37074f
......@@ -242,6 +242,11 @@ F: include/trace/events/9p.h
F: include/uapi/linux/virtio_9p.h
F: net/9p/
A64FX DIAG DRIVER
M: Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>
S: Supported
F: drivers/soc/fujitsu/a64fx-diag.c
A8293 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
......
......@@ -9,6 +9,7 @@ source "drivers/soc/atmel/Kconfig"
source "drivers/soc/bcm/Kconfig"
source "drivers/soc/canaan/Kconfig"
source "drivers/soc/fsl/Kconfig"
source "drivers/soc/fujitsu/Kconfig"
source "drivers/soc/imx/Kconfig"
source "drivers/soc/ixp4xx/Kconfig"
source "drivers/soc/litex/Kconfig"
......
......@@ -12,6 +12,7 @@ obj-$(CONFIG_SOC_CANAAN) += canaan/
obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/
obj-y += fujitsu/
obj-$(CONFIG_ARCH_GEMINI) += gemini/
obj-y += imx/
obj-y += ixp4xx/
......
# SPDX-License-Identifier: GPL-2.0-only
menu "fujitsu SoC drivers"
config A64FX_DIAG
tristate "A64FX diag driver"
depends on ARM64
depends on ACPI
help
Say Y here if you want to enable diag interrupt on Fujitsu A64FX.
This driver enables BMC's diagnostic requests and enables
A64FX-specific interrupts. This allows administrators to obtain
kernel dumps via diagnostic requests using ipmitool, etc.
If unsure, say N.
endmenu
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_A64FX_DIAG) += a64fx-diag.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* A64FX diag driver.
* Copyright (c) 2022 Fujitsu Ltd.
*/
#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define A64FX_DIAG_IRQ 1
#define BMC_DIAG_INTERRUPT_ENABLE 0x40
#define BMC_DIAG_INTERRUPT_STATUS 0x44
#define BMC_DIAG_INTERRUPT_MASK BIT(31)
struct a64fx_diag_priv {
void __iomem *mmsc_reg_base;
int irq;
bool has_nmi;
};
static irqreturn_t a64fx_diag_handler_nmi(int irq, void *dev_id)
{
nmi_panic(NULL, "a64fx_diag: interrupt received\n");
return IRQ_HANDLED;
}
static irqreturn_t a64fx_diag_handler_irq(int irq, void *dev_id)
{
panic("a64fx_diag: interrupt received\n");
return IRQ_HANDLED;
}
static void a64fx_diag_interrupt_clear(struct a64fx_diag_priv *priv)
{
void __iomem *diag_status_reg_addr;
u32 mmsc;
diag_status_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_STATUS;
mmsc = readl(diag_status_reg_addr);
if (mmsc & BMC_DIAG_INTERRUPT_MASK)
writel(BMC_DIAG_INTERRUPT_MASK, diag_status_reg_addr);
}
static void a64fx_diag_interrupt_enable(struct a64fx_diag_priv *priv)
{
void __iomem *diag_enable_reg_addr;
u32 mmsc;
diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE;
mmsc = readl(diag_enable_reg_addr);
if (!(mmsc & BMC_DIAG_INTERRUPT_MASK)) {
mmsc |= BMC_DIAG_INTERRUPT_MASK;
writel(mmsc, diag_enable_reg_addr);
}
}
static void a64fx_diag_interrupt_disable(struct a64fx_diag_priv *priv)
{
void __iomem *diag_enable_reg_addr;
u32 mmsc;
diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE;
mmsc = readl(diag_enable_reg_addr);
if (mmsc & BMC_DIAG_INTERRUPT_MASK) {
mmsc &= ~BMC_DIAG_INTERRUPT_MASK;
writel(mmsc, diag_enable_reg_addr);
}
}
static int a64fx_diag_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct a64fx_diag_priv *priv;
unsigned long irq_flags;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
priv->mmsc_reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->mmsc_reg_base))
return PTR_ERR(priv->mmsc_reg_base);
priv->irq = platform_get_irq(pdev, A64FX_DIAG_IRQ);
if (priv->irq < 0)
return priv->irq;
platform_set_drvdata(pdev, priv);
irq_flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_AUTOEN |
IRQF_NO_THREAD;
ret = request_nmi(priv->irq, &a64fx_diag_handler_nmi, irq_flags,
"a64fx_diag_nmi", NULL);
if (ret) {
ret = request_irq(priv->irq, &a64fx_diag_handler_irq,
irq_flags, "a64fx_diag_irq", NULL);
if (ret) {
dev_err(dev, "cannot register IRQ %d\n", ret);
return ret;
}
enable_irq(priv->irq);
} else {
enable_nmi(priv->irq);
priv->has_nmi = true;
}
a64fx_diag_interrupt_clear(priv);
a64fx_diag_interrupt_enable(priv);
return 0;
}
static int a64fx_diag_remove(struct platform_device *pdev)
{
struct a64fx_diag_priv *priv = platform_get_drvdata(pdev);
a64fx_diag_interrupt_disable(priv);
a64fx_diag_interrupt_clear(priv);
if (priv->has_nmi)
free_nmi(priv->irq, NULL);
else
free_irq(priv->irq, NULL);
return 0;
}
static const struct acpi_device_id a64fx_diag_acpi_match[] = {
{ "FUJI2007", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, a64fx_diag_acpi_match);
static struct platform_driver a64fx_diag_driver = {
.driver = {
.name = "a64fx_diag_driver",
.acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match),
},
.probe = a64fx_diag_probe,
.remove = a64fx_diag_remove,
};
module_platform_driver(a64fx_diag_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>");
MODULE_DESCRIPTION("A64FX diag driver");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment