pasemi_edac.c 8.6 KB

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