pasemi_edac.c 8.2 KB

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