a64fx-diag.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * A64FX diag driver.
  4. * Copyright (c) 2022 Fujitsu Ltd.
  5. */
  6. #include <linux/acpi.h>
  7. #include <linux/interrupt.h>
  8. #include <linux/irq.h>
  9. #include <linux/module.h>
  10. #include <linux/platform_device.h>
  11. #define A64FX_DIAG_IRQ 1
  12. #define BMC_DIAG_INTERRUPT_ENABLE 0x40
  13. #define BMC_DIAG_INTERRUPT_STATUS 0x44
  14. #define BMC_DIAG_INTERRUPT_MASK BIT(31)
  15. struct a64fx_diag_priv {
  16. void __iomem *mmsc_reg_base;
  17. int irq;
  18. bool has_nmi;
  19. };
  20. static irqreturn_t a64fx_diag_handler_nmi(int irq, void *dev_id)
  21. {
  22. nmi_panic(NULL, "a64fx_diag: interrupt received\n");
  23. return IRQ_HANDLED;
  24. }
  25. static irqreturn_t a64fx_diag_handler_irq(int irq, void *dev_id)
  26. {
  27. panic("a64fx_diag: interrupt received\n");
  28. return IRQ_HANDLED;
  29. }
  30. static void a64fx_diag_interrupt_clear(struct a64fx_diag_priv *priv)
  31. {
  32. void __iomem *diag_status_reg_addr;
  33. u32 mmsc;
  34. diag_status_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_STATUS;
  35. mmsc = readl(diag_status_reg_addr);
  36. if (mmsc & BMC_DIAG_INTERRUPT_MASK)
  37. writel(BMC_DIAG_INTERRUPT_MASK, diag_status_reg_addr);
  38. }
  39. static void a64fx_diag_interrupt_enable(struct a64fx_diag_priv *priv)
  40. {
  41. void __iomem *diag_enable_reg_addr;
  42. u32 mmsc;
  43. diag_enable_reg_addr = priv->mmsc_reg_base + BMC_DIAG_INTERRUPT_ENABLE;
  44. mmsc = readl(diag_enable_reg_addr);
  45. if (!(mmsc & BMC_DIAG_INTERRUPT_MASK)) {
  46. mmsc |= BMC_DIAG_INTERRUPT_MASK;
  47. writel(mmsc, diag_enable_reg_addr);
  48. }
  49. }
  50. static void a64fx_diag_interrupt_disable(struct a64fx_diag_priv *priv)
  51. {
  52. void __iomem *diag_enable_reg_addr;
  53. u32 mmsc;
  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. static int a64fx_diag_probe(struct platform_device *pdev)
  62. {
  63. struct device *dev = &pdev->dev;
  64. struct a64fx_diag_priv *priv;
  65. unsigned long irq_flags;
  66. int ret;
  67. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  68. if (priv == NULL)
  69. return -ENOMEM;
  70. priv->mmsc_reg_base = devm_platform_ioremap_resource(pdev, 0);
  71. if (IS_ERR(priv->mmsc_reg_base))
  72. return PTR_ERR(priv->mmsc_reg_base);
  73. priv->irq = platform_get_irq(pdev, A64FX_DIAG_IRQ);
  74. if (priv->irq < 0)
  75. return priv->irq;
  76. platform_set_drvdata(pdev, priv);
  77. irq_flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_AUTOEN |
  78. IRQF_NO_THREAD;
  79. ret = request_nmi(priv->irq, &a64fx_diag_handler_nmi, irq_flags,
  80. "a64fx_diag_nmi", NULL);
  81. if (ret) {
  82. ret = request_irq(priv->irq, &a64fx_diag_handler_irq,
  83. irq_flags, "a64fx_diag_irq", NULL);
  84. if (ret) {
  85. dev_err(dev, "cannot register IRQ %d\n", ret);
  86. return ret;
  87. }
  88. enable_irq(priv->irq);
  89. } else {
  90. enable_nmi(priv->irq);
  91. priv->has_nmi = true;
  92. }
  93. a64fx_diag_interrupt_clear(priv);
  94. a64fx_diag_interrupt_enable(priv);
  95. return 0;
  96. }
  97. static int a64fx_diag_remove(struct platform_device *pdev)
  98. {
  99. struct a64fx_diag_priv *priv = platform_get_drvdata(pdev);
  100. a64fx_diag_interrupt_disable(priv);
  101. a64fx_diag_interrupt_clear(priv);
  102. if (priv->has_nmi)
  103. free_nmi(priv->irq, NULL);
  104. else
  105. free_irq(priv->irq, NULL);
  106. return 0;
  107. }
  108. static const struct acpi_device_id a64fx_diag_acpi_match[] = {
  109. { "FUJI2007", 0 },
  110. { },
  111. };
  112. MODULE_DEVICE_TABLE(acpi, a64fx_diag_acpi_match);
  113. static struct platform_driver a64fx_diag_driver = {
  114. .driver = {
  115. .name = "a64fx_diag_driver",
  116. .acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match),
  117. },
  118. .probe = a64fx_diag_probe,
  119. .remove = a64fx_diag_remove,
  120. };
  121. module_platform_driver(a64fx_diag_driver);
  122. MODULE_LICENSE("GPL v2");
  123. MODULE_AUTHOR("Hitomi Hasegawa <[email protected]>");
  124. MODULE_DESCRIPTION("A64FX diag driver");