sifive_edac.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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, NULL, 0,
  46. edac_device_alloc_index());
  47. if (!p->dci)
  48. return -ENOMEM;
  49. p->dci->dev = &pdev->dev;
  50. p->dci->mod_name = "Sifive ECC Manager";
  51. p->dci->ctl_name = dev_name(&pdev->dev);
  52. p->dci->dev_name = dev_name(&pdev->dev);
  53. if (edac_device_add_device(p->dci)) {
  54. dev_err(p->dci->dev, "failed to register with EDAC core\n");
  55. goto err;
  56. }
  57. register_sifive_ccache_error_notifier(&p->notifier);
  58. return 0;
  59. err:
  60. edac_device_free_ctl_info(p->dci);
  61. return -ENXIO;
  62. }
  63. static int ecc_unregister(struct platform_device *pdev)
  64. {
  65. struct sifive_edac_priv *p = platform_get_drvdata(pdev);
  66. unregister_sifive_ccache_error_notifier(&p->notifier);
  67. edac_device_del_device(&pdev->dev);
  68. edac_device_free_ctl_info(p->dci);
  69. return 0;
  70. }
  71. static struct platform_device *sifive_pdev;
  72. static int __init sifive_edac_init(void)
  73. {
  74. int ret;
  75. sifive_pdev = platform_device_register_simple(DRVNAME, 0, NULL, 0);
  76. if (IS_ERR(sifive_pdev))
  77. return PTR_ERR(sifive_pdev);
  78. ret = ecc_register(sifive_pdev);
  79. if (ret)
  80. platform_device_unregister(sifive_pdev);
  81. return ret;
  82. }
  83. static void __exit sifive_edac_exit(void)
  84. {
  85. ecc_unregister(sifive_pdev);
  86. platform_device_unregister(sifive_pdev);
  87. }
  88. module_init(sifive_edac_init);
  89. module_exit(sifive_edac_exit);
  90. MODULE_AUTHOR("SiFive Inc.");
  91. MODULE_DESCRIPTION("SiFive platform EDAC driver");
  92. MODULE_LICENSE("GPL v2");