Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Hitomi Hasegawa and committed by
Arnd Bergmann
64f89dfa 3c37074f

+180
+5
MAINTAINERS
··· 242 242 F: include/uapi/linux/virtio_9p.h 243 243 F: net/9p/ 244 244 245 + A64FX DIAG DRIVER 246 + M: Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com> 247 + S: Supported 248 + F: drivers/soc/fujitsu/a64fx-diag.c 249 + 245 250 A8293 MEDIA DRIVER 246 251 M: Antti Palosaari <crope@iki.fi> 247 252 L: linux-media@vger.kernel.org
+1
drivers/soc/Kconfig
··· 9 9 source "drivers/soc/bcm/Kconfig" 10 10 source "drivers/soc/canaan/Kconfig" 11 11 source "drivers/soc/fsl/Kconfig" 12 + source "drivers/soc/fujitsu/Kconfig" 12 13 source "drivers/soc/imx/Kconfig" 13 14 source "drivers/soc/ixp4xx/Kconfig" 14 15 source "drivers/soc/litex/Kconfig"
+1
drivers/soc/Makefile
··· 12 12 obj-$(CONFIG_ARCH_DOVE) += dove/ 13 13 obj-$(CONFIG_MACH_DOVE) += dove/ 14 14 obj-y += fsl/ 15 + obj-y += fujitsu/ 15 16 obj-$(CONFIG_ARCH_GEMINI) += gemini/ 16 17 obj-y += imx/ 17 18 obj-y += ixp4xx/
+16
drivers/soc/fujitsu/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + menu "fujitsu SoC drivers" 3 + 4 + config A64FX_DIAG 5 + tristate "A64FX diag driver" 6 + depends on ARM64 7 + depends on ACPI 8 + help 9 + Say Y here if you want to enable diag interrupt on Fujitsu A64FX. 10 + This driver enables BMC's diagnostic requests and enables 11 + A64FX-specific interrupts. This allows administrators to obtain 12 + kernel dumps via diagnostic requests using ipmitool, etc. 13 + 14 + If unsure, say N. 15 + 16 + endmenu
+3
drivers/soc/fujitsu/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + obj-$(CONFIG_A64FX_DIAG) += a64fx-diag.o
+154
drivers/soc/fujitsu/a64fx-diag.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * A64FX diag driver. 4 + * Copyright (c) 2022 Fujitsu Ltd. 5 + */ 6 + 7 + #include <linux/acpi.h> 8 + #include <linux/interrupt.h> 9 + #include <linux/irq.h> 10 + #include <linux/module.h> 11 + #include <linux/platform_device.h> 12 + 13 + #define A64FX_DIAG_IRQ 1 14 + #define BMC_DIAG_INTERRUPT_ENABLE 0x40 15 + #define BMC_DIAG_INTERRUPT_STATUS 0x44 16 + #define BMC_DIAG_INTERRUPT_MASK BIT(31) 17 + 18 + struct a64fx_diag_priv { 19 + void __iomem *mmsc_reg_base; 20 + int irq; 21 + bool has_nmi; 22 + }; 23 + 24 + static irqreturn_t a64fx_diag_handler_nmi(int irq, void *dev_id) 25 + { 26 + nmi_panic(NULL, "a64fx_diag: interrupt received\n"); 27 + 28 + return IRQ_HANDLED; 29 + } 30 + 31 + static irqreturn_t a64fx_diag_handler_irq(int irq, void *dev_id) 32 + { 33 + panic("a64fx_diag: interrupt received\n"); 34 + 35 + return IRQ_HANDLED; 36 + } 37 + 38 + static void a64fx_diag_interrupt_clear(struct a64fx_diag_priv *priv) 39 + { 40 + void __iomem *diag_status_reg_addr; 41 + u32 mmsc; 42 + 43 + diag_status_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_STATUS; 44 + mmsc = readl(diag_status_reg_addr); 45 + if (mmsc & BMC_DIAG_INTERRUPT_MASK) 46 + writel(BMC_DIAG_INTERRUPT_MASK, diag_status_reg_addr); 47 + } 48 + 49 + static void a64fx_diag_interrupt_enable(struct a64fx_diag_priv *priv) 50 + { 51 + void __iomem *diag_enable_reg_addr; 52 + u32 mmsc; 53 + 54 + diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE; 55 + mmsc = readl(diag_enable_reg_addr); 56 + if (!(mmsc & BMC_DIAG_INTERRUPT_MASK)) { 57 + mmsc |= BMC_DIAG_INTERRUPT_MASK; 58 + writel(mmsc, diag_enable_reg_addr); 59 + } 60 + } 61 + 62 + static void a64fx_diag_interrupt_disable(struct a64fx_diag_priv *priv) 63 + { 64 + void __iomem *diag_enable_reg_addr; 65 + u32 mmsc; 66 + 67 + diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE; 68 + mmsc = readl(diag_enable_reg_addr); 69 + if (mmsc & BMC_DIAG_INTERRUPT_MASK) { 70 + mmsc &= ~BMC_DIAG_INTERRUPT_MASK; 71 + writel(mmsc, diag_enable_reg_addr); 72 + } 73 + } 74 + 75 + static int a64fx_diag_probe(struct platform_device *pdev) 76 + { 77 + struct device *dev = &pdev->dev; 78 + struct a64fx_diag_priv *priv; 79 + unsigned long irq_flags; 80 + int ret; 81 + 82 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 83 + if (priv == NULL) 84 + return -ENOMEM; 85 + 86 + priv->mmsc_reg_base = devm_platform_ioremap_resource(pdev, 0); 87 + if (IS_ERR(priv->mmsc_reg_base)) 88 + return PTR_ERR(priv->mmsc_reg_base); 89 + 90 + priv->irq = platform_get_irq(pdev, A64FX_DIAG_IRQ); 91 + if (priv->irq < 0) 92 + return priv->irq; 93 + 94 + platform_set_drvdata(pdev, priv); 95 + 96 + irq_flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_AUTOEN | 97 + IRQF_NO_THREAD; 98 + ret = request_nmi(priv->irq, &a64fx_diag_handler_nmi, irq_flags, 99 + "a64fx_diag_nmi", NULL); 100 + if (ret) { 101 + ret = request_irq(priv->irq, &a64fx_diag_handler_irq, 102 + irq_flags, "a64fx_diag_irq", NULL); 103 + if (ret) { 104 + dev_err(dev, "cannot register IRQ %d\n", ret); 105 + return ret; 106 + } 107 + enable_irq(priv->irq); 108 + } else { 109 + enable_nmi(priv->irq); 110 + priv->has_nmi = true; 111 + } 112 + 113 + a64fx_diag_interrupt_clear(priv); 114 + a64fx_diag_interrupt_enable(priv); 115 + 116 + return 0; 117 + } 118 + 119 + static int a64fx_diag_remove(struct platform_device *pdev) 120 + { 121 + struct a64fx_diag_priv *priv = platform_get_drvdata(pdev); 122 + 123 + a64fx_diag_interrupt_disable(priv); 124 + a64fx_diag_interrupt_clear(priv); 125 + 126 + if (priv->has_nmi) 127 + free_nmi(priv->irq, NULL); 128 + else 129 + free_irq(priv->irq, NULL); 130 + 131 + return 0; 132 + } 133 + 134 + static const struct acpi_device_id a64fx_diag_acpi_match[] = { 135 + { "FUJI2007", 0 }, 136 + { }, 137 + }; 138 + MODULE_DEVICE_TABLE(acpi, a64fx_diag_acpi_match); 139 + 140 + 141 + static struct platform_driver a64fx_diag_driver = { 142 + .driver = { 143 + .name = "a64fx_diag_driver", 144 + .acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match), 145 + }, 146 + .probe = a64fx_diag_probe, 147 + .remove = a64fx_diag_remove, 148 + }; 149 + 150 + module_platform_driver(a64fx_diag_driver); 151 + 152 + MODULE_LICENSE("GPL v2"); 153 + MODULE_AUTHOR("Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>"); 154 + MODULE_DESCRIPTION("A64FX diag driver");