0001
0002
0003
0004
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");