brintos

brintos / linux-shallow public Read only

0
0
Text · 3.6 KiB · 3309018 Raw
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