pasemi_edac.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2006-2007 PA Semi, Inc
  4. *
  5. * Author: Egor Martovetsky <[email protected]>
  6. * Maintained by: Olof Johansson <[email protected]>
  7. *
  8. * Driver for the PWRficient onchip memory controllers
  9. */
  10. #include <linux/module.h>
  11. #include <linux/init.h>
  12. #include <linux/pci.h>
  13. #include <linux/pci_ids.h>
  14. #include <linux/edac.h>
  15. #include "edac_module.h"
  16. #define MODULE_NAME "pasemi_edac"
  17. #define MCCFG_MCEN 0x300
  18. #define MCCFG_MCEN_MMC_EN 0x00000001
  19. #define MCCFG_ERRCOR 0x388
  20. #define MCCFG_ERRCOR_RNK_FAIL_DET_EN 0x00000100
  21. #define MCCFG_ERRCOR_ECC_GEN_EN 0x00000010
  22. #define MCCFG_ERRCOR_ECC_CRR_EN 0x00000001
  23. #define MCCFG_SCRUB 0x384
  24. #define MCCFG_SCRUB_RGLR_SCRB_EN 0x00000001
  25. #define MCDEBUG_ERRCTL1 0x728
  26. #define MCDEBUG_ERRCTL1_RFL_LOG_EN 0x00080000
  27. #define MCDEBUG_ERRCTL1_MBE_LOG_EN 0x00040000
  28. #define MCDEBUG_ERRCTL1_SBE_LOG_EN 0x00020000
  29. #define MCDEBUG_ERRSTA 0x730
  30. #define MCDEBUG_ERRSTA_RFL_STATUS 0x00000004
  31. #define MCDEBUG_ERRSTA_MBE_STATUS 0x00000002
  32. #define MCDEBUG_ERRSTA_SBE_STATUS 0x00000001
  33. #define MCDEBUG_ERRCNT1 0x734
  34. #define MCDEBUG_ERRCNT1_SBE_CNT_OVRFLO 0x00000080
  35. #define MCDEBUG_ERRLOG1A 0x738
  36. #define MCDEBUG_ERRLOG1A_MERR_TYPE_M 0x30000000
  37. #define MCDEBUG_ERRLOG1A_MERR_TYPE_NONE 0x00000000
  38. #define MCDEBUG_ERRLOG1A_MERR_TYPE_SBE 0x10000000
  39. #define MCDEBUG_ERRLOG1A_MERR_TYPE_MBE 0x20000000
  40. #define MCDEBUG_ERRLOG1A_MERR_TYPE_RFL 0x30000000
  41. #define MCDEBUG_ERRLOG1A_MERR_BA_M 0x00700000
  42. #define MCDEBUG_ERRLOG1A_MERR_BA_S 20
  43. #define MCDEBUG_ERRLOG1A_MERR_CS_M 0x00070000
  44. #define MCDEBUG_ERRLOG1A_MERR_CS_S 16
  45. #define MCDEBUG_ERRLOG1A_SYNDROME_M 0x0000ffff
  46. #define MCDRAM_RANKCFG 0x114
  47. #define MCDRAM_RANKCFG_EN 0x00000001
  48. #define MCDRAM_RANKCFG_TYPE_SIZE_M 0x000001c0
  49. #define MCDRAM_RANKCFG_TYPE_SIZE_S 6
  50. #define PASEMI_EDAC_NR_CSROWS 8
  51. #define PASEMI_EDAC_NR_CHANS 1
  52. #define PASEMI_EDAC_ERROR_GRAIN 64
  53. static int last_page_in_mmc;
  54. static int system_mmc_id;
  55. static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci)
  56. {
  57. struct pci_dev *pdev = to_pci_dev(mci->pdev);
  58. u32 tmp;
  59. pci_read_config_dword(pdev, MCDEBUG_ERRSTA,
  60. &tmp);
  61. tmp &= (MCDEBUG_ERRSTA_RFL_STATUS | MCDEBUG_ERRSTA_MBE_STATUS
  62. | MCDEBUG_ERRSTA_SBE_STATUS);
  63. if (tmp) {
  64. if (tmp & MCDEBUG_ERRSTA_SBE_STATUS)
  65. pci_write_config_dword(pdev, MCDEBUG_ERRCNT1,
  66. MCDEBUG_ERRCNT1_SBE_CNT_OVRFLO);
  67. pci_write_config_dword(pdev, MCDEBUG_ERRSTA, tmp);
  68. }
  69. return tmp;
  70. }
  71. static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta)
  72. {
  73. struct pci_dev *pdev = to_pci_dev(mci->pdev);
  74. u32 errlog1a;
  75. u32 cs;
  76. if (!errsta)
  77. return;
  78. pci_read_config_dword(pdev, MCDEBUG_ERRLOG1A, &errlog1a);
  79. cs = (errlog1a & MCDEBUG_ERRLOG1A_MERR_CS_M) >>
  80. MCDEBUG_ERRLOG1A_MERR_CS_S;
  81. /* uncorrectable/multi-bit errors */
  82. if (errsta & (MCDEBUG_ERRSTA_MBE_STATUS |
  83. MCDEBUG_ERRSTA_RFL_STATUS)) {
  84. edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
  85. mci->csrows[cs]->first_page, 0, 0,
  86. cs, 0, -1, mci->ctl_name, "");
  87. }
  88. /* correctable/single-bit errors */
  89. if (errsta & MCDEBUG_ERRSTA_SBE_STATUS)
  90. edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
  91. mci->csrows[cs]->first_page, 0, 0,
  92. cs, 0, -1, mci->ctl_name, "");
  93. }
  94. static void pasemi_edac_check(struct mem_ctl_info *mci)
  95. {
  96. u32 errsta;
  97. errsta = pasemi_edac_get_error_info(mci);
  98. if (errsta)
  99. pasemi_edac_process_error_info(mci, errsta);
  100. }
  101. static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
  102. struct pci_dev *pdev,
  103. enum edac_type edac_mode)
  104. {
  105. struct csrow_info *csrow;
  106. struct dimm_info *dimm;
  107. u32 rankcfg;
  108. int index;
  109. for (index = 0; index < mci->nr_csrows; index++) {
  110. csrow = mci->csrows[index];
  111. dimm = csrow->channels[0]->dimm;
  112. pci_read_config_dword(pdev,
  113. MCDRAM_RANKCFG + (index * 12),
  114. &rankcfg);
  115. if (!(rankcfg & MCDRAM_RANKCFG_EN))
  116. continue;
  117. switch ((rankcfg & MCDRAM_RANKCFG_TYPE_SIZE_M) >>
  118. MCDRAM_RANKCFG_TYPE_SIZE_S) {
  119. case 0:
  120. dimm->nr_pages = 128 << (20 - PAGE_SHIFT);
  121. break;
  122. case 1:
  123. dimm->nr_pages = 256 << (20 - PAGE_SHIFT);
  124. break;
  125. case 2:
  126. case 3:
  127. dimm->nr_pages = 512 << (20 - PAGE_SHIFT);
  128. break;
  129. case 4:
  130. dimm->nr_pages = 1024 << (20 - PAGE_SHIFT);
  131. break;
  132. case 5:
  133. dimm->nr_pages = 2048 << (20 - PAGE_SHIFT);
  134. break;
  135. default:
  136. edac_mc_printk(mci, KERN_ERR,
  137. "Unrecognized Rank Config. rankcfg=%u\n",
  138. rankcfg);
  139. return -EINVAL;
  140. }
  141. csrow->first_page = last_page_in_mmc;
  142. csrow->last_page = csrow->first_page + dimm->nr_pages - 1;
  143. last_page_in_mmc += dimm->nr_pages;
  144. csrow->page_mask = 0;
  145. dimm->grain = PASEMI_EDAC_ERROR_GRAIN;
  146. dimm->mtype = MEM_DDR;
  147. dimm->dtype = DEV_UNKNOWN;
  148. dimm->edac_mode = edac_mode;
  149. }
  150. return 0;
  151. }
  152. static int pasemi_edac_probe(struct pci_dev *pdev,
  153. const struct pci_device_id *ent)
  154. {
  155. struct mem_ctl_info *mci = NULL;
  156. struct edac_mc_layer layers[2];
  157. u32 errctl1, errcor, scrub, mcen;
  158. pci_read_config_dword(pdev, MCCFG_MCEN, &mcen);
  159. if (!(mcen & MCCFG_MCEN_MMC_EN))
  160. return -ENODEV;
  161. /*
  162. * We should think about enabling other error detection later on
  163. */
  164. pci_read_config_dword(pdev, MCDEBUG_ERRCTL1, &errctl1);
  165. errctl1 |= MCDEBUG_ERRCTL1_SBE_LOG_EN |
  166. MCDEBUG_ERRCTL1_MBE_LOG_EN |
  167. MCDEBUG_ERRCTL1_RFL_LOG_EN;
  168. pci_write_config_dword(pdev, MCDEBUG_ERRCTL1, errctl1);
  169. layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
  170. layers[0].size = PASEMI_EDAC_NR_CSROWS;
  171. layers[0].is_virt_csrow = true;
  172. layers[1].type = EDAC_MC_LAYER_CHANNEL;
  173. layers[1].size = PASEMI_EDAC_NR_CHANS;
  174. layers[1].is_virt_csrow = false;
  175. mci = edac_mc_alloc(system_mmc_id++, ARRAY_SIZE(layers), layers,
  176. 0);
  177. if (mci == NULL)
  178. return -ENOMEM;
  179. pci_read_config_dword(pdev, MCCFG_ERRCOR, &errcor);
  180. errcor |= MCCFG_ERRCOR_RNK_FAIL_DET_EN |
  181. MCCFG_ERRCOR_ECC_GEN_EN |
  182. MCCFG_ERRCOR_ECC_CRR_EN;
  183. mci->pdev = &pdev->dev;
  184. mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR;
  185. mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
  186. mci->edac_cap = (errcor & MCCFG_ERRCOR_ECC_GEN_EN) ?
  187. ((errcor & MCCFG_ERRCOR_ECC_CRR_EN) ?
  188. (EDAC_FLAG_EC | EDAC_FLAG_SECDED) : EDAC_FLAG_EC) :
  189. EDAC_FLAG_NONE;
  190. mci->mod_name = MODULE_NAME;
  191. mci->dev_name = pci_name(pdev);
  192. mci->ctl_name = "pasemi,pwrficient-mc";
  193. mci->edac_check = pasemi_edac_check;
  194. mci->ctl_page_to_phys = NULL;
  195. pci_read_config_dword(pdev, MCCFG_SCRUB, &scrub);
  196. mci->scrub_cap = SCRUB_FLAG_HW_PROG | SCRUB_FLAG_HW_SRC;
  197. mci->scrub_mode =
  198. ((errcor & MCCFG_ERRCOR_ECC_CRR_EN) ? SCRUB_FLAG_HW_SRC : 0) |
  199. ((scrub & MCCFG_SCRUB_RGLR_SCRB_EN) ? SCRUB_FLAG_HW_PROG : 0);
  200. if (pasemi_edac_init_csrows(mci, pdev,
  201. (mci->edac_cap & EDAC_FLAG_SECDED) ?
  202. EDAC_SECDED :
  203. ((mci->edac_cap & EDAC_FLAG_EC) ?
  204. EDAC_EC : EDAC_NONE)))
  205. goto fail;
  206. /*
  207. * Clear status
  208. */
  209. pasemi_edac_get_error_info(mci);
  210. if (edac_mc_add_mc(mci))
  211. goto fail;
  212. /* get this far and it's successful */
  213. return 0;
  214. fail:
  215. edac_mc_free(mci);
  216. return -ENODEV;
  217. }
  218. static void pasemi_edac_remove(struct pci_dev *pdev)
  219. {
  220. struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev);
  221. if (!mci)
  222. return;
  223. edac_mc_free(mci);
  224. }
  225. static const struct pci_device_id pasemi_edac_pci_tbl[] = {
  226. { PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa00a) },
  227. { }
  228. };
  229. MODULE_DEVICE_TABLE(pci, pasemi_edac_pci_tbl);
  230. static struct pci_driver pasemi_edac_driver = {
  231. .name = MODULE_NAME,
  232. .probe = pasemi_edac_probe,
  233. .remove = pasemi_edac_remove,
  234. .id_table = pasemi_edac_pci_tbl,
  235. };
  236. static int __init pasemi_edac_init(void)
  237. {
  238. /* Ensure that the OPSTATE is set correctly for POLL or NMI */
  239. opstate_init();
  240. return pci_register_driver(&pasemi_edac_driver);
  241. }
  242. static void __exit pasemi_edac_exit(void)
  243. {
  244. pci_unregister_driver(&pasemi_edac_driver);
  245. }
  246. module_init(pasemi_edac_init);
  247. module_exit(pasemi_edac_exit);
  248. MODULE_LICENSE("GPL");
  249. MODULE_AUTHOR("Egor Martovetsky <[email protected]>");
  250. MODULE_DESCRIPTION("MC support for PA Semi PWRficient memory controller");
  251. module_param(edac_op_state, int, 0444);
  252. MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");