0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/module.h>
0013 #include <linux/init.h>
0014 #include <linux/pci.h>
0015 #include <linux/pci_ids.h>
0016 #include <linux/edac.h>
0017 #include "edac_module.h"
0018
0019 #define MODULE_NAME "pasemi_edac"
0020
0021 #define MCCFG_MCEN 0x300
0022 #define MCCFG_MCEN_MMC_EN 0x00000001
0023 #define MCCFG_ERRCOR 0x388
0024 #define MCCFG_ERRCOR_RNK_FAIL_DET_EN 0x00000100
0025 #define MCCFG_ERRCOR_ECC_GEN_EN 0x00000010
0026 #define MCCFG_ERRCOR_ECC_CRR_EN 0x00000001
0027 #define MCCFG_SCRUB 0x384
0028 #define MCCFG_SCRUB_RGLR_SCRB_EN 0x00000001
0029 #define MCDEBUG_ERRCTL1 0x728
0030 #define MCDEBUG_ERRCTL1_RFL_LOG_EN 0x00080000
0031 #define MCDEBUG_ERRCTL1_MBE_LOG_EN 0x00040000
0032 #define MCDEBUG_ERRCTL1_SBE_LOG_EN 0x00020000
0033 #define MCDEBUG_ERRSTA 0x730
0034 #define MCDEBUG_ERRSTA_RFL_STATUS 0x00000004
0035 #define MCDEBUG_ERRSTA_MBE_STATUS 0x00000002
0036 #define MCDEBUG_ERRSTA_SBE_STATUS 0x00000001
0037 #define MCDEBUG_ERRCNT1 0x734
0038 #define MCDEBUG_ERRCNT1_SBE_CNT_OVRFLO 0x00000080
0039 #define MCDEBUG_ERRLOG1A 0x738
0040 #define MCDEBUG_ERRLOG1A_MERR_TYPE_M 0x30000000
0041 #define MCDEBUG_ERRLOG1A_MERR_TYPE_NONE 0x00000000
0042 #define MCDEBUG_ERRLOG1A_MERR_TYPE_SBE 0x10000000
0043 #define MCDEBUG_ERRLOG1A_MERR_TYPE_MBE 0x20000000
0044 #define MCDEBUG_ERRLOG1A_MERR_TYPE_RFL 0x30000000
0045 #define MCDEBUG_ERRLOG1A_MERR_BA_M 0x00700000
0046 #define MCDEBUG_ERRLOG1A_MERR_BA_S 20
0047 #define MCDEBUG_ERRLOG1A_MERR_CS_M 0x00070000
0048 #define MCDEBUG_ERRLOG1A_MERR_CS_S 16
0049 #define MCDEBUG_ERRLOG1A_SYNDROME_M 0x0000ffff
0050 #define MCDRAM_RANKCFG 0x114
0051 #define MCDRAM_RANKCFG_EN 0x00000001
0052 #define MCDRAM_RANKCFG_TYPE_SIZE_M 0x000001c0
0053 #define MCDRAM_RANKCFG_TYPE_SIZE_S 6
0054
0055 #define PASEMI_EDAC_NR_CSROWS 8
0056 #define PASEMI_EDAC_NR_CHANS 1
0057 #define PASEMI_EDAC_ERROR_GRAIN 64
0058
0059 static int last_page_in_mmc;
0060 static int system_mmc_id;
0061
0062
0063 static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci)
0064 {
0065 struct pci_dev *pdev = to_pci_dev(mci->pdev);
0066 u32 tmp;
0067
0068 pci_read_config_dword(pdev, MCDEBUG_ERRSTA,
0069 &tmp);
0070
0071 tmp &= (MCDEBUG_ERRSTA_RFL_STATUS | MCDEBUG_ERRSTA_MBE_STATUS
0072 | MCDEBUG_ERRSTA_SBE_STATUS);
0073
0074 if (tmp) {
0075 if (tmp & MCDEBUG_ERRSTA_SBE_STATUS)
0076 pci_write_config_dword(pdev, MCDEBUG_ERRCNT1,
0077 MCDEBUG_ERRCNT1_SBE_CNT_OVRFLO);
0078 pci_write_config_dword(pdev, MCDEBUG_ERRSTA, tmp);
0079 }
0080
0081 return tmp;
0082 }
0083
0084 static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta)
0085 {
0086 struct pci_dev *pdev = to_pci_dev(mci->pdev);
0087 u32 errlog1a;
0088 u32 cs;
0089
0090 if (!errsta)
0091 return;
0092
0093 pci_read_config_dword(pdev, MCDEBUG_ERRLOG1A, &errlog1a);
0094
0095 cs = (errlog1a & MCDEBUG_ERRLOG1A_MERR_CS_M) >>
0096 MCDEBUG_ERRLOG1A_MERR_CS_S;
0097
0098
0099 if (errsta & (MCDEBUG_ERRSTA_MBE_STATUS |
0100 MCDEBUG_ERRSTA_RFL_STATUS)) {
0101 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
0102 mci->csrows[cs]->first_page, 0, 0,
0103 cs, 0, -1, mci->ctl_name, "");
0104 }
0105
0106
0107 if (errsta & MCDEBUG_ERRSTA_SBE_STATUS)
0108 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
0109 mci->csrows[cs]->first_page, 0, 0,
0110 cs, 0, -1, mci->ctl_name, "");
0111 }
0112
0113 static void pasemi_edac_check(struct mem_ctl_info *mci)
0114 {
0115 u32 errsta;
0116
0117 errsta = pasemi_edac_get_error_info(mci);
0118 if (errsta)
0119 pasemi_edac_process_error_info(mci, errsta);
0120 }
0121
0122 static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
0123 struct pci_dev *pdev,
0124 enum edac_type edac_mode)
0125 {
0126 struct csrow_info *csrow;
0127 struct dimm_info *dimm;
0128 u32 rankcfg;
0129 int index;
0130
0131 for (index = 0; index < mci->nr_csrows; index++) {
0132 csrow = mci->csrows[index];
0133 dimm = csrow->channels[0]->dimm;
0134
0135 pci_read_config_dword(pdev,
0136 MCDRAM_RANKCFG + (index * 12),
0137 &rankcfg);
0138
0139 if (!(rankcfg & MCDRAM_RANKCFG_EN))
0140 continue;
0141
0142 switch ((rankcfg & MCDRAM_RANKCFG_TYPE_SIZE_M) >>
0143 MCDRAM_RANKCFG_TYPE_SIZE_S) {
0144 case 0:
0145 dimm->nr_pages = 128 << (20 - PAGE_SHIFT);
0146 break;
0147 case 1:
0148 dimm->nr_pages = 256 << (20 - PAGE_SHIFT);
0149 break;
0150 case 2:
0151 case 3:
0152 dimm->nr_pages = 512 << (20 - PAGE_SHIFT);
0153 break;
0154 case 4:
0155 dimm->nr_pages = 1024 << (20 - PAGE_SHIFT);
0156 break;
0157 case 5:
0158 dimm->nr_pages = 2048 << (20 - PAGE_SHIFT);
0159 break;
0160 default:
0161 edac_mc_printk(mci, KERN_ERR,
0162 "Unrecognized Rank Config. rankcfg=%u\n",
0163 rankcfg);
0164 return -EINVAL;
0165 }
0166
0167 csrow->first_page = last_page_in_mmc;
0168 csrow->last_page = csrow->first_page + dimm->nr_pages - 1;
0169 last_page_in_mmc += dimm->nr_pages;
0170 csrow->page_mask = 0;
0171 dimm->grain = PASEMI_EDAC_ERROR_GRAIN;
0172 dimm->mtype = MEM_DDR;
0173 dimm->dtype = DEV_UNKNOWN;
0174 dimm->edac_mode = edac_mode;
0175 }
0176 return 0;
0177 }
0178
0179 static int pasemi_edac_probe(struct pci_dev *pdev,
0180 const struct pci_device_id *ent)
0181 {
0182 struct mem_ctl_info *mci = NULL;
0183 struct edac_mc_layer layers[2];
0184 u32 errctl1, errcor, scrub, mcen;
0185
0186 pci_read_config_dword(pdev, MCCFG_MCEN, &mcen);
0187 if (!(mcen & MCCFG_MCEN_MMC_EN))
0188 return -ENODEV;
0189
0190
0191
0192
0193
0194 pci_read_config_dword(pdev, MCDEBUG_ERRCTL1, &errctl1);
0195 errctl1 |= MCDEBUG_ERRCTL1_SBE_LOG_EN |
0196 MCDEBUG_ERRCTL1_MBE_LOG_EN |
0197 MCDEBUG_ERRCTL1_RFL_LOG_EN;
0198 pci_write_config_dword(pdev, MCDEBUG_ERRCTL1, errctl1);
0199
0200 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
0201 layers[0].size = PASEMI_EDAC_NR_CSROWS;
0202 layers[0].is_virt_csrow = true;
0203 layers[1].type = EDAC_MC_LAYER_CHANNEL;
0204 layers[1].size = PASEMI_EDAC_NR_CHANS;
0205 layers[1].is_virt_csrow = false;
0206 mci = edac_mc_alloc(system_mmc_id++, ARRAY_SIZE(layers), layers,
0207 0);
0208 if (mci == NULL)
0209 return -ENOMEM;
0210
0211 pci_read_config_dword(pdev, MCCFG_ERRCOR, &errcor);
0212 errcor |= MCCFG_ERRCOR_RNK_FAIL_DET_EN |
0213 MCCFG_ERRCOR_ECC_GEN_EN |
0214 MCCFG_ERRCOR_ECC_CRR_EN;
0215
0216 mci->pdev = &pdev->dev;
0217 mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR;
0218 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
0219 mci->edac_cap = (errcor & MCCFG_ERRCOR_ECC_GEN_EN) ?
0220 ((errcor & MCCFG_ERRCOR_ECC_CRR_EN) ?
0221 (EDAC_FLAG_EC | EDAC_FLAG_SECDED) : EDAC_FLAG_EC) :
0222 EDAC_FLAG_NONE;
0223 mci->mod_name = MODULE_NAME;
0224 mci->dev_name = pci_name(pdev);
0225 mci->ctl_name = "pasemi,pwrficient-mc";
0226 mci->edac_check = pasemi_edac_check;
0227 mci->ctl_page_to_phys = NULL;
0228 pci_read_config_dword(pdev, MCCFG_SCRUB, &scrub);
0229 mci->scrub_cap = SCRUB_FLAG_HW_PROG | SCRUB_FLAG_HW_SRC;
0230 mci->scrub_mode =
0231 ((errcor & MCCFG_ERRCOR_ECC_CRR_EN) ? SCRUB_FLAG_HW_SRC : 0) |
0232 ((scrub & MCCFG_SCRUB_RGLR_SCRB_EN) ? SCRUB_FLAG_HW_PROG : 0);
0233
0234 if (pasemi_edac_init_csrows(mci, pdev,
0235 (mci->edac_cap & EDAC_FLAG_SECDED) ?
0236 EDAC_SECDED :
0237 ((mci->edac_cap & EDAC_FLAG_EC) ?
0238 EDAC_EC : EDAC_NONE)))
0239 goto fail;
0240
0241
0242
0243
0244 pasemi_edac_get_error_info(mci);
0245
0246 if (edac_mc_add_mc(mci))
0247 goto fail;
0248
0249
0250 return 0;
0251
0252 fail:
0253 edac_mc_free(mci);
0254 return -ENODEV;
0255 }
0256
0257 static void pasemi_edac_remove(struct pci_dev *pdev)
0258 {
0259 struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev);
0260
0261 if (!mci)
0262 return;
0263
0264 edac_mc_free(mci);
0265 }
0266
0267
0268 static const struct pci_device_id pasemi_edac_pci_tbl[] = {
0269 { PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa00a) },
0270 { }
0271 };
0272
0273 MODULE_DEVICE_TABLE(pci, pasemi_edac_pci_tbl);
0274
0275 static struct pci_driver pasemi_edac_driver = {
0276 .name = MODULE_NAME,
0277 .probe = pasemi_edac_probe,
0278 .remove = pasemi_edac_remove,
0279 .id_table = pasemi_edac_pci_tbl,
0280 };
0281
0282 static int __init pasemi_edac_init(void)
0283 {
0284
0285 opstate_init();
0286
0287 return pci_register_driver(&pasemi_edac_driver);
0288 }
0289
0290 static void __exit pasemi_edac_exit(void)
0291 {
0292 pci_unregister_driver(&pasemi_edac_driver);
0293 }
0294
0295 module_init(pasemi_edac_init);
0296 module_exit(pasemi_edac_exit);
0297
0298 MODULE_LICENSE("GPL");
0299 MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>");
0300 MODULE_DESCRIPTION("MC support for PA Semi PWRficient memory controller");
0301 module_param(edac_op_state, int, 0444);
0302 MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
0303