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 void 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 132 static const struct acpi_device_id a64fx_diag_acpi_match[] = { 133 { "FUJI2007", 0 }, 134 { }, 135 }; 136 MODULE_DEVICE_TABLE(acpi, a64fx_diag_acpi_match); 137 138 139 static struct platform_driver a64fx_diag_driver = { 140 .driver = { 141 .name = "a64fx_diag_driver", 142 .acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match), 143 }, 144 .probe = a64fx_diag_probe, 145 .remove_new = a64fx_diag_remove, 146 }; 147 148 module_platform_driver(a64fx_diag_driver); 149 150 MODULE_AUTHOR("Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>"); 151 MODULE_DESCRIPTION("A64FX diag driver"); 152