152 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 114#define BMC_DIAG_INTERRUPT_ENABLE 0x4015#define BMC_DIAG_INTERRUPT_STATUS 0x4416#define BMC_DIAG_INTERRUPT_MASK BIT(31)17 18struct a64fx_diag_priv {19 void __iomem *mmsc_reg_base;20 int irq;21 bool has_nmi;22};23 24static 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 31static 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 38static 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 49static 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 62static 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 75static 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 119static 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 else129 free_irq(priv->irq, NULL);130}131 132static const struct acpi_device_id a64fx_diag_acpi_match[] = {133 { "FUJI2007", 0 },134 { },135};136MODULE_DEVICE_TABLE(acpi, a64fx_diag_acpi_match);137 138 139static 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 148module_platform_driver(a64fx_diag_driver);149 150MODULE_AUTHOR("Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>");151MODULE_DESCRIPTION("A64FX diag driver");152