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/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->dev);
  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->dev);
  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_ue(mci, mci->csrows[cs].first_page, 0,
  97. cs, mci->ctl_name);
  98. }
  99. /* correctable/single-bit errors */
  100. if (errsta & MCDEBUG_ERRSTA_SBE_STATUS) {
  101. edac_mc_handle_ce(mci, mci->csrows[cs].first_page, 0,
  102. 0, cs, 0, mci->ctl_name);
  103. }
  104. }
  105. static void pasemi_edac_check(struct mem_ctl_info *mci)
  106. {
  107. u32 errsta;
  108. errsta = pasemi_edac_get_error_info(mci);
  109. if (errsta)
  110. pasemi_edac_process_error_info(mci, errsta);
  111. }
  112. static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
  113. struct pci_dev *pdev,
  114. enum edac_type edac_mode)
  115. {
  116. struct csrow_info *csrow;
  117. u32 rankcfg;
  118. int index;
  119. for (index = 0; index < mci->nr_csrows; index++) {
  120. csrow = &mci->csrows[index];
  121. pci_read_config_dword(pdev,
  122. MCDRAM_RANKCFG + (index * 12),
  123. &rankcfg);
  124. if (!(rankcfg & MCDRAM_RANKCFG_EN))
  125. continue;
  126. switch ((rankcfg & MCDRAM_RANKCFG_TYPE_SIZE_M) >>
  127. MCDRAM_RANKCFG_TYPE_SIZE_S) {
  128. case 0:
  129. csrow->nr_pages = 128 << (20 - PAGE_SHIFT);
  130. break;
  131. case 1:
  132. csrow->nr_pages = 256 << (20 - PAGE_SHIFT);
  133. break;
  134. case 2:
  135. case 3:
  136. csrow->nr_pages = 512 << (20 - PAGE_SHIFT);
  137. break;
  138. case 4:
  139. csrow->nr_pages = 1024 << (20 - PAGE_SHIFT);
  140. break;
  141. case 5:
  142. csrow->nr_pages = 2048 << (20 - PAGE_SHIFT);
  143. break;
  144. default:
  145. edac_mc_printk(mci, KERN_ERR,
  146. "Unrecognized Rank Config. rankcfg=%u\n",
  147. rankcfg);
  148. return -EINVAL;
  149. }
  150. csrow->first_page = last_page_in_mmc;
  151. csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
  152. last_page_in_mmc += csrow->nr_pages;
  153. csrow->page_mask = 0;
  154. csrow->grain = PASEMI_EDAC_ERROR_GRAIN;
  155. csrow->mtype = MEM_DDR;
  156. csrow->dtype = DEV_UNKNOWN;
  157. csrow->edac_mode = edac_mode;
  158. }
  159. return 0;
  160. }
  161. static int __devinit pasemi_edac_probe(struct pci_dev *pdev,
  162. const struct pci_device_id *ent)
  163. {
  164. struct mem_ctl_info *mci = NULL;
  165. u32 errctl1, errcor, scrub, mcen;
  166. pci_read_config_dword(pdev, MCCFG_MCEN, &mcen);
  167. if (!(mcen & MCCFG_MCEN_MMC_EN))
  168. return -ENODEV;
  169. /*
  170. * We should think about enabling other error detection later on
  171. */
  172. pci_read_config_dword(pdev, MCDEBUG_ERRCTL1, &errctl1);
  173. errctl1 |= MCDEBUG_ERRCTL1_SBE_LOG_EN |
  174. MCDEBUG_ERRCTL1_MBE_LOG_EN |
  175. MCDEBUG_ERRCTL1_RFL_LOG_EN;
  176. pci_write_config_dword(pdev, MCDEBUG_ERRCTL1, errctl1);
  177. mci = edac_mc_alloc(0, PASEMI_EDAC_NR_CSROWS, PASEMI_EDAC_NR_CHANS,
  178. system_mmc_id++);
  179. if (mci == NULL)
  180. return -ENOMEM;
  181. pci_read_config_dword(pdev, MCCFG_ERRCOR, &errcor);
  182. errcor |= MCCFG_ERRCOR_RNK_FAIL_DET_EN |
  183. MCCFG_ERRCOR_ECC_GEN_EN |
  184. MCCFG_ERRCOR_ECC_CRR_EN;
  185. mci->dev = &pdev->dev;
  186. mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR;
  187. mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
  188. mci->edac_cap = (errcor & MCCFG_ERRCOR_ECC_GEN_EN) ?
  189. ((errcor & MCCFG_ERRCOR_ECC_CRR_EN) ?
  190. (EDAC_FLAG_EC | EDAC_FLAG_SECDED) : EDAC_FLAG_EC) :
  191. EDAC_FLAG_NONE;
  192. mci->mod_name = MODULE_NAME;
  193. mci->dev_name = pci_name(pdev);
  194. mci->ctl_name = "pasemi,pwrficient-mc";
  195. mci->edac_check = pasemi_edac_check;
  196. mci->ctl_page_to_phys = NULL;
  197. pci_read_config_dword(pdev, MCCFG_SCRUB, &scrub);
  198. mci->scrub_cap = SCRUB_FLAG_HW_PROG | SCRUB_FLAG_HW_SRC;
  199. mci->scrub_mode =
  200. ((errcor & MCCFG_ERRCOR_ECC_CRR_EN) ? SCRUB_FLAG_HW_SRC : 0) |
  201. ((scrub & MCCFG_SCRUB_RGLR_SCRB_EN) ? SCRUB_FLAG_HW_PROG : 0);
  202. if (pasemi_edac_init_csrows(mci, pdev,
  203. (mci->edac_cap & EDAC_FLAG_SECDED) ?
  204. EDAC_SECDED :
  205. ((mci->edac_cap & EDAC_FLAG_EC) ?
  206. EDAC_EC : EDAC_NONE)))
  207. goto fail;
  208. /*
  209. * Clear status
  210. */
  211. pasemi_edac_get_error_info(mci);
  212. if (edac_mc_add_mc(mci))
  213. goto fail;
  214. /* get this far and it's successful */
  215. return 0;
  216. fail:
  217. edac_mc_free(mci);
  218. return -ENODEV;
  219. }
  220. static void __devexit pasemi_edac_remove(struct pci_dev *pdev)
  221. {
  222. struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev);
  223. if (!mci)
  224. return;
  225. edac_mc_free(mci);
  226. }
  227. static const struct pci_device_id pasemi_edac_pci_tbl[] = {
  228. { PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa00a) },
  229. { }
  230. };
  231. MODULE_DEVICE_TABLE(pci, pasemi_edac_pci_tbl);
  232. static struct pci_driver pasemi_edac_driver = {
  233. .name = MODULE_NAME,
  234. .probe = pasemi_edac_probe,
  235. .remove = __devexit_p(pasemi_edac_remove),
  236. .id_table = pasemi_edac_pci_tbl,
  237. };
  238. static int __init pasemi_edac_init(void)
  239. {
  240. /* Ensure that the OPSTATE is set correctly for POLL or NMI */
  241. opstate_init();
  242. return pci_register_driver(&pasemi_edac_driver);
  243. }
  244. static void __exit pasemi_edac_exit(void)
  245. {
  246. pci_unregister_driver(&pasemi_edac_driver);
  247. }
  248. module_init(pasemi_edac_init);
  249. module_exit(pasemi_edac_exit);
  250. MODULE_LICENSE("GPL");
  251. MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>");
  252. MODULE_DESCRIPTION("MC support for PA Semi PWRficient memory controller");
  253. module_param(edac_op_state, int, 0444);
  254. MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");