linux/drivers/edac/octeon_edac-lmc.c
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (C) 2009 Wind River Systems,
   7 *   written by Ralf Baechle <ralf@linux-mips.org>
   8 */
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/slab.h>
  12#include <linux/io.h>
  13#include <linux/edac.h>
  14
  15#include <asm/octeon/octeon.h>
  16#include <asm/octeon/cvmx-lmcx-defs.h>
  17
  18#include "edac_core.h"
  19#include "edac_module.h"
  20
  21#define OCTEON_MAX_MC 4
  22
  23static void octeon_lmc_edac_poll(struct mem_ctl_info *mci)
  24{
  25        union cvmx_lmcx_mem_cfg0 cfg0;
  26        bool do_clear = false;
  27        char msg[64];
  28
  29        cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx));
  30        if (cfg0.s.sec_err || cfg0.s.ded_err) {
  31                union cvmx_lmcx_fadr fadr;
  32                fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx));
  33                snprintf(msg, sizeof(msg),
  34                         "DIMM %d rank %d bank %d row %d col %d",
  35                         fadr.cn30xx.fdimm, fadr.cn30xx.fbunk,
  36                         fadr.cn30xx.fbank, fadr.cn30xx.frow, fadr.cn30xx.fcol);
  37        }
  38
  39        if (cfg0.s.sec_err) {
  40                edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
  41                                     -1, -1, -1, msg, "");
  42                cfg0.s.sec_err = -1;    /* Done, re-arm */
  43                do_clear = true;
  44        }
  45
  46        if (cfg0.s.ded_err) {
  47                edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
  48                                     -1, -1, -1, msg, "");
  49                cfg0.s.ded_err = -1;    /* Done, re-arm */
  50                do_clear = true;
  51        }
  52        if (do_clear)
  53                cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx), cfg0.u64);
  54}
  55
  56static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci)
  57{
  58        union cvmx_lmcx_int int_reg;
  59        bool do_clear = false;
  60        char msg[64];
  61
  62        int_reg.u64 = cvmx_read_csr(CVMX_LMCX_INT(mci->mc_idx));
  63        if (int_reg.s.sec_err || int_reg.s.ded_err) {
  64                union cvmx_lmcx_fadr fadr;
  65                fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx));
  66                snprintf(msg, sizeof(msg),
  67                         "DIMM %d rank %d bank %d row %d col %d",
  68                         fadr.cn61xx.fdimm, fadr.cn61xx.fbunk,
  69                         fadr.cn61xx.fbank, fadr.cn61xx.frow, fadr.cn61xx.fcol);
  70        }
  71
  72        if (int_reg.s.sec_err) {
  73                edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
  74                                     -1, -1, -1, msg, "");
  75                int_reg.s.sec_err = -1; /* Done, re-arm */
  76                do_clear = true;
  77        }
  78
  79        if (int_reg.s.ded_err) {
  80                edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
  81                                     -1, -1, -1, msg, "");
  82                int_reg.s.ded_err = -1; /* Done, re-arm */
  83                do_clear = true;
  84        }
  85        if (do_clear)
  86                cvmx_write_csr(CVMX_LMCX_INT(mci->mc_idx), int_reg.u64);
  87}
  88
  89static int octeon_lmc_edac_probe(struct platform_device *pdev)
  90{
  91        struct mem_ctl_info *mci;
  92        struct edac_mc_layer layers[1];
  93        int mc = pdev->id;
  94
  95        layers[0].type = EDAC_MC_LAYER_CHANNEL;
  96        layers[0].size = 1;
  97        layers[0].is_virt_csrow = false;
  98
  99        if (OCTEON_IS_MODEL(OCTEON_FAM_1_PLUS)) {
 100                union cvmx_lmcx_mem_cfg0 cfg0;
 101
 102                cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(0));
 103                if (!cfg0.s.ecc_ena) {
 104                        dev_info(&pdev->dev, "Disabled (ECC not enabled)\n");
 105                        return 0;
 106                }
 107
 108                mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0);
 109                if (!mci)
 110                        return -ENXIO;
 111
 112                mci->pdev = &pdev->dev;
 113                mci->dev_name = dev_name(&pdev->dev);
 114
 115                mci->mod_name = "octeon-lmc";
 116                mci->ctl_name = "octeon-lmc-err";
 117                mci->edac_check = octeon_lmc_edac_poll;
 118
 119                if (edac_mc_add_mc(mci)) {
 120                        dev_err(&pdev->dev, "edac_mc_add_mc() failed\n");
 121                        edac_mc_free(mci);
 122                        return -ENXIO;
 123                }
 124
 125                cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
 126                cfg0.s.intr_ded_ena = 0;        /* We poll */
 127                cfg0.s.intr_sec_ena = 0;
 128                cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), cfg0.u64);
 129        } else {
 130                /* OCTEON II */
 131                union cvmx_lmcx_int_en en;
 132                union cvmx_lmcx_config config;
 133
 134                config.u64 = cvmx_read_csr(CVMX_LMCX_CONFIG(0));
 135                if (!config.s.ecc_ena) {
 136                        dev_info(&pdev->dev, "Disabled (ECC not enabled)\n");
 137                        return 0;
 138                }
 139
 140                mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0);
 141                if (!mci)
 142                        return -ENXIO;
 143
 144                mci->pdev = &pdev->dev;
 145                mci->dev_name = dev_name(&pdev->dev);
 146
 147                mci->mod_name = "octeon-lmc";
 148                mci->ctl_name = "co_lmc_err";
 149                mci->edac_check = octeon_lmc_edac_poll_o2;
 150
 151                if (edac_mc_add_mc(mci)) {
 152                        dev_err(&pdev->dev, "edac_mc_add_mc() failed\n");
 153                        edac_mc_free(mci);
 154                        return -ENXIO;
 155                }
 156
 157                en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
 158                en.s.intr_ded_ena = 0;  /* We poll */
 159                en.s.intr_sec_ena = 0;
 160                cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), en.u64);
 161        }
 162        platform_set_drvdata(pdev, mci);
 163
 164        return 0;
 165}
 166
 167static int octeon_lmc_edac_remove(struct platform_device *pdev)
 168{
 169        struct mem_ctl_info *mci = platform_get_drvdata(pdev);
 170
 171        edac_mc_del_mc(&pdev->dev);
 172        edac_mc_free(mci);
 173        return 0;
 174}
 175
 176static struct platform_driver octeon_lmc_edac_driver = {
 177        .probe = octeon_lmc_edac_probe,
 178        .remove = octeon_lmc_edac_remove,
 179        .driver = {
 180                   .name = "octeon_lmc_edac",
 181        }
 182};
 183module_platform_driver(octeon_lmc_edac_driver);
 184
 185MODULE_LICENSE("GPL");
 186MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
 187