Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2006-2007 PA Semi, Inc
0004  *
0005  * Author: Egor Martovetsky <egor@pasemi.com>
0006  * Maintained by: Olof Johansson <olof@lixom.net>
0007  *
0008  * Driver for the PWRficient onchip memory controllers
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     /* uncorrectable/multi-bit errors */
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     /* correctable/single-bit errors */
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      * We should think about enabling other error detection later on
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      * Clear status
0243      */
0244     pasemi_edac_get_error_info(mci);
0245 
0246     if (edac_mc_add_mc(mci))
0247         goto fail;
0248 
0249     /* get this far and it's successful */
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        /* Ensure that the OPSTATE is set correctly for POLL or NMI */
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