root/drivers/edac/pasemi_edac.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pasemi_edac_get_error_info
  2. pasemi_edac_process_error_info
  3. pasemi_edac_check
  4. pasemi_edac_init_csrows
  5. pasemi_edac_probe
  6. pasemi_edac_remove
  7. pasemi_edac_init
  8. pasemi_edac_exit

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

/* [<][>][^][v][top][bottom][index][help] */