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: Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com> Link: https://lore.kernel.org/r/20220520074119.3574753-2-hasegawa-hitomi@fujitsu.com' Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Родитель
3c37074f0d
Коммит
64f89dfaa4
|
@ -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/
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# 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
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_A64FX_DIAG) += a64fx-diag.o
|
|
@ -0,0 +1,154 @@
|
|||
// 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");
|
Загрузка…
Ссылка в новой задаче