Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * A64FX diag driver.
0004  * Copyright (c) 2022 Fujitsu Ltd.
0005  */
0006 
0007 #include <linux/acpi.h>
0008 #include <linux/interrupt.h>
0009 #include <linux/irq.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 
0013 #define A64FX_DIAG_IRQ 1
0014 #define BMC_DIAG_INTERRUPT_ENABLE 0x40
0015 #define BMC_DIAG_INTERRUPT_STATUS 0x44
0016 #define BMC_DIAG_INTERRUPT_MASK BIT(31)
0017 
0018 struct a64fx_diag_priv {
0019     void __iomem *mmsc_reg_base;
0020     int irq;
0021     bool has_nmi;
0022 };
0023 
0024 static irqreturn_t a64fx_diag_handler_nmi(int irq, void *dev_id)
0025 {
0026     nmi_panic(NULL, "a64fx_diag: interrupt received\n");
0027 
0028     return IRQ_HANDLED;
0029 }
0030 
0031 static irqreturn_t a64fx_diag_handler_irq(int irq, void *dev_id)
0032 {
0033     panic("a64fx_diag: interrupt received\n");
0034 
0035     return IRQ_HANDLED;
0036 }
0037 
0038 static void a64fx_diag_interrupt_clear(struct a64fx_diag_priv *priv)
0039 {
0040     void __iomem *diag_status_reg_addr;
0041     u32 mmsc;
0042 
0043     diag_status_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_STATUS;
0044     mmsc = readl(diag_status_reg_addr);
0045     if (mmsc & BMC_DIAG_INTERRUPT_MASK)
0046         writel(BMC_DIAG_INTERRUPT_MASK, diag_status_reg_addr);
0047 }
0048 
0049 static void a64fx_diag_interrupt_enable(struct a64fx_diag_priv *priv)
0050 {
0051     void __iomem *diag_enable_reg_addr;
0052     u32 mmsc;
0053 
0054     diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE;
0055     mmsc = readl(diag_enable_reg_addr);
0056     if (!(mmsc & BMC_DIAG_INTERRUPT_MASK)) {
0057         mmsc |= BMC_DIAG_INTERRUPT_MASK;
0058         writel(mmsc, diag_enable_reg_addr);
0059     }
0060 }
0061 
0062 static void a64fx_diag_interrupt_disable(struct a64fx_diag_priv *priv)
0063 {
0064     void __iomem *diag_enable_reg_addr;
0065     u32 mmsc;
0066 
0067     diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE;
0068     mmsc = readl(diag_enable_reg_addr);
0069     if (mmsc & BMC_DIAG_INTERRUPT_MASK) {
0070         mmsc &= ~BMC_DIAG_INTERRUPT_MASK;
0071         writel(mmsc, diag_enable_reg_addr);
0072     }
0073 }
0074 
0075 static int a64fx_diag_probe(struct platform_device *pdev)
0076 {
0077     struct device *dev = &pdev->dev;
0078     struct a64fx_diag_priv *priv;
0079     unsigned long irq_flags;
0080     int ret;
0081 
0082     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0083     if (priv == NULL)
0084         return -ENOMEM;
0085 
0086     priv->mmsc_reg_base = devm_platform_ioremap_resource(pdev, 0);
0087     if (IS_ERR(priv->mmsc_reg_base))
0088         return PTR_ERR(priv->mmsc_reg_base);
0089 
0090     priv->irq = platform_get_irq(pdev, A64FX_DIAG_IRQ);
0091     if (priv->irq < 0)
0092         return priv->irq;
0093 
0094     platform_set_drvdata(pdev, priv);
0095 
0096     irq_flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_AUTOEN |
0097            IRQF_NO_THREAD;
0098     ret = request_nmi(priv->irq, &a64fx_diag_handler_nmi, irq_flags,
0099             "a64fx_diag_nmi", NULL);
0100     if (ret) {
0101         ret = request_irq(priv->irq, &a64fx_diag_handler_irq,
0102                 irq_flags, "a64fx_diag_irq", NULL);
0103         if (ret) {
0104             dev_err(dev, "cannot register IRQ %d\n", ret);
0105             return ret;
0106         }
0107         enable_irq(priv->irq);
0108     } else {
0109         enable_nmi(priv->irq);
0110         priv->has_nmi = true;
0111     }
0112 
0113     a64fx_diag_interrupt_clear(priv);
0114     a64fx_diag_interrupt_enable(priv);
0115 
0116     return 0;
0117 }
0118 
0119 static int a64fx_diag_remove(struct platform_device *pdev)
0120 {
0121     struct a64fx_diag_priv *priv = platform_get_drvdata(pdev);
0122 
0123     a64fx_diag_interrupt_disable(priv);
0124     a64fx_diag_interrupt_clear(priv);
0125 
0126     if (priv->has_nmi)
0127         free_nmi(priv->irq, NULL);
0128     else
0129         free_irq(priv->irq, NULL);
0130 
0131     return 0;
0132 }
0133 
0134 static const struct acpi_device_id a64fx_diag_acpi_match[] = {
0135     { "FUJI2007", 0 },
0136     { },
0137 };
0138 MODULE_DEVICE_TABLE(acpi, a64fx_diag_acpi_match);
0139 
0140 
0141 static struct platform_driver a64fx_diag_driver = {
0142     .driver = {
0143         .name = "a64fx_diag_driver",
0144         .acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match),
0145     },
0146     .probe = a64fx_diag_probe,
0147     .remove = a64fx_diag_remove,
0148 };
0149 
0150 module_platform_driver(a64fx_diag_driver);
0151 
0152 MODULE_LICENSE("GPL v2");
0153 MODULE_AUTHOR("Hitomi Hasegawa <hasegawa-hitomi@fujitsu.com>");
0154 MODULE_DESCRIPTION("A64FX diag driver");