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
6 * Copyright (C) 2009 Wind River Systems,
7 * written by Ralf Baechle <ralf@linux-mips.org>
9 #include <linux/module.h>
10 #include <linux/init.h>
11 #include <linux/slab.h>
13 #include <linux/edac.h>
15 #include <asm/octeon/octeon.h>
16 #include <asm/octeon/cvmx-lmcx-defs.h>
18 #include "edac_core.h"
19 #include "edac_module.h"
21 #define OCTEON_MAX_MC 4
23 static void octeon_lmc_edac_poll(struct mem_ctl_info
*mci
)
25 union cvmx_lmcx_mem_cfg0 cfg0
;
26 bool do_clear
= false;
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
);
40 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED
, mci
, 1, 0, 0, 0,
42 cfg0
.s
.sec_err
= -1; /* Done, re-arm */
47 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED
, mci
, 1, 0, 0, 0,
49 cfg0
.s
.ded_err
= -1; /* Done, re-arm */
53 cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mci
->mc_idx
), cfg0
.u64
);
56 static void octeon_lmc_edac_poll_o2(struct mem_ctl_info
*mci
)
58 union cvmx_lmcx_int int_reg
;
59 bool do_clear
= false;
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
);
72 if (int_reg
.s
.sec_err
) {
73 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED
, mci
, 1, 0, 0, 0,
75 int_reg
.s
.sec_err
= -1; /* Done, re-arm */
79 if (int_reg
.s
.ded_err
) {
80 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED
, mci
, 1, 0, 0, 0,
82 int_reg
.s
.ded_err
= -1; /* Done, re-arm */
86 cvmx_write_csr(CVMX_LMCX_INT(mci
->mc_idx
), int_reg
.u64
);
89 static int octeon_lmc_edac_probe(struct platform_device
*pdev
)
91 struct mem_ctl_info
*mci
;
92 struct edac_mc_layer layers
[1];
95 layers
[0].type
= EDAC_MC_LAYER_CHANNEL
;
97 layers
[0].is_virt_csrow
= false;
99 if (OCTEON_IS_MODEL(OCTEON_FAM_1_PLUS
)) {
100 union cvmx_lmcx_mem_cfg0 cfg0
;
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");
108 mci
= edac_mc_alloc(mc
, ARRAY_SIZE(layers
), layers
, 0);
112 mci
->pdev
= &pdev
->dev
;
113 mci
->dev_name
= dev_name(&pdev
->dev
);
115 mci
->mod_name
= "octeon-lmc";
116 mci
->ctl_name
= "octeon-lmc-err";
117 mci
->edac_check
= octeon_lmc_edac_poll
;
119 if (edac_mc_add_mc(mci
)) {
120 dev_err(&pdev
->dev
, "edac_mc_add_mc() failed\n");
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
);
131 union cvmx_lmcx_int_en en
;
132 union cvmx_lmcx_config config
;
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");
140 mci
= edac_mc_alloc(mc
, ARRAY_SIZE(layers
), layers
, 0);
144 mci
->pdev
= &pdev
->dev
;
145 mci
->dev_name
= dev_name(&pdev
->dev
);
147 mci
->mod_name
= "octeon-lmc";
148 mci
->ctl_name
= "co_lmc_err";
149 mci
->edac_check
= octeon_lmc_edac_poll_o2
;
151 if (edac_mc_add_mc(mci
)) {
152 dev_err(&pdev
->dev
, "edac_mc_add_mc() failed\n");
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
);
162 platform_set_drvdata(pdev
, mci
);
167 static int octeon_lmc_edac_remove(struct platform_device
*pdev
)
169 struct mem_ctl_info
*mci
= platform_get_drvdata(pdev
);
171 edac_mc_del_mc(&pdev
->dev
);
176 static struct platform_driver octeon_lmc_edac_driver
= {
177 .probe
= octeon_lmc_edac_probe
,
178 .remove
= octeon_lmc_edac_remove
,
180 .name
= "octeon_lmc_edac",
183 module_platform_driver(octeon_lmc_edac_driver
);
185 MODULE_LICENSE("GPL");
186 MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");