sifive_edac.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * SiFive Platform EDAC Driver
  4. *
  5. * Copyright (C) 2018-2022 SiFive, Inc.
  6. *
  7. * This driver is partially based on octeon_edac-pc.c
  8. *
  9. */
  10. #include <linux/edac.h>
  11. #include <linux/platform_device.h>
  12. #include "edac_module.h"
  13. #include <soc/sifive/sifive_ccache.h>
  14. #define DRVNAME "sifive_edac"
  15. struct sifive_edac_priv {
  16. struct notifier_block notifier;
  17. struct edac_device_ctl_info *dci;
  18. };
  19. /*
  20. * EDAC error callback
  21. *
  22. * @event: non-zero if unrecoverable.
  23. */
  24. static
  25. int ecc_err_event(struct notifier_block *this, unsigned long event, void *ptr)
  26. {
  27. const char *msg = (char *)ptr;
  28. struct sifive_edac_priv *p;
  29. p = container_of(this, struct sifive_edac_priv, notifier);
  30. if (event == SIFIVE_CCACHE_ERR_TYPE_UE)
  31. edac_device_handle_ue(p->dci, 0, 0, msg);
  32. else if (event == SIFIVE_CCACHE_ERR_TYPE_CE)
  33. edac_device_handle_ce(p->dci, 0, 0, msg);
  34. return NOTIFY_OK;
  35. }
  36. static int ecc_register(struct platform_device *pdev)
  37. {
  38. struct sifive_edac_priv *p;
  39. p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
  40. if (!p)
  41. return -ENOMEM;
  42. p->notifier.notifier_call = ecc_err_event;
  43. platform_set_drvdata(pdev, p);
  44. p->dci = edac_device_alloc_ctl_info(0, "sifive_ecc", 1, "sifive_ecc",
  45. 1, 1, edac_device_alloc_index());
  46. if (!p->dci)
  47. return -ENOMEM;
  48. p->dci->dev = &pdev->dev;
  49. p->dci->mod_name = "Sifive ECC Manager";
  50. p->dci->ctl_name = dev_name(&pdev->dev);
  51. p->dci->dev_name = dev_name(&pdev->dev);
  52. if (edac_device_add_device(p->dci)) {
  53. dev_err(p->dci->dev, "failed to register with EDAC core\n");
  54. goto err;
  55. }
  56. register_sifive_ccache_error_notifier(&p->notifier);
  57. return 0;
  58. err:
  59. edac_device_free_ctl_info(p->dci);
  60. return -ENXIO;
  61. }
  62. static int ecc_unregister(struct platform_device *pdev)
  63. {
  64. struct sifive_edac_priv *p = platform_get_drvdata(pdev);
  65. unregister_sifive_ccache_error_notifier(&p->notifier);
  66. edac_device_del_device(&pdev->dev);
  67. edac_device_free_ctl_info(p->dci);
  68. return 0;
  69. }
  70. static struct platform_device *sifive_pdev;
  71. static int __init sifive_edac_init(void)
  72. {
  73. int ret;
  74. sifive_pdev = platform_device_register_simple(DRVNAME, 0, NULL, 0);
  75. if (IS_ERR(sifive_pdev))
  76. return PTR_ERR(sifive_pdev);
  77. ret = ecc_register(sifive_pdev);
  78. if (ret)
  79. platform_device_unregister(sifive_pdev);
  80. return ret;
  81. }
  82. static void __exit sifive_edac_exit(void)
  83. {
  84. ecc_unregister(sifive_pdev);
  85. platform_device_unregister(sifive_pdev);
  86. }
  87. module_init(sifive_edac_init);
  88. module_exit(sifive_edac_exit);
  89. MODULE_AUTHOR("SiFive Inc.");
  90. MODULE_DESCRIPTION("SiFive platform EDAC driver");
  91. MODULE_LICENSE("GPL v2");