blob: 4881ad0b0bd686aade7a528154038ee8fe4c61cd [file] [log] [blame]
Ralf Baechlef65aad42012-10-17 00:39:09 +02001/*
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
David Daneye1ced092012-11-15 13:58:59 -080015#include <asm/octeon/octeon.h>
16#include <asm/octeon/cvmx-lmcx-defs.h>
Ralf Baechlef65aad42012-10-17 00:39:09 +020017
18#include "edac_core.h"
19#include "edac_module.h"
Ralf Baechlef65aad42012-10-17 00:39:09 +020020
David Daneye1ced092012-11-15 13:58:59 -080021#define OCTEON_MAX_MC 4
Ralf Baechlef65aad42012-10-17 00:39:09 +020022
David Daneye1ced092012-11-15 13:58:59 -080023static void octeon_lmc_edac_poll(struct mem_ctl_info *mci)
Ralf Baechlef65aad42012-10-17 00:39:09 +020024{
David Daneye1ced092012-11-15 13:58:59 -080025 union cvmx_lmcx_mem_cfg0 cfg0;
26 bool do_clear = false;
Ralf Baechlef65aad42012-10-17 00:39:09 +020027 char msg[64];
28
David Daneye1ced092012-11-15 13:58:59 -080029 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);
Ralf Baechlef65aad42012-10-17 00:39:09 +020037 }
38
David Daneye1ced092012-11-15 13:58:59 -080039 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;
Ralf Baechlef65aad42012-10-17 00:39:09 +020044 }
45
David Daneye1ced092012-11-15 13:58:59 -080046 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);
Ralf Baechlef65aad42012-10-17 00:39:09 +020054}
55
David Daneye1ced092012-11-15 13:58:59 -080056static 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
Greg Kroah-Hartman9b3c6e82012-12-21 13:23:51 -080089static int octeon_lmc_edac_probe(struct platform_device *pdev)
Ralf Baechlef65aad42012-10-17 00:39:09 +020090{
91 struct mem_ctl_info *mci;
David Daneye1ced092012-11-15 13:58:59 -080092 struct edac_mc_layer layers[1];
93 int mc = pdev->id;
Ralf Baechlef65aad42012-10-17 00:39:09 +020094
Daniel Walker5331de02013-09-20 15:46:40 -070095 opstate_init();
96
David Daneye1ced092012-11-15 13:58:59 -080097 layers[0].type = EDAC_MC_LAYER_CHANNEL;
98 layers[0].size = 1;
99 layers[0].is_virt_csrow = false;
Ralf Baechlef65aad42012-10-17 00:39:09 +0200100
David Daneye1ced092012-11-15 13:58:59 -0800101 if (OCTEON_IS_MODEL(OCTEON_FAM_1_PLUS)) {
102 union cvmx_lmcx_mem_cfg0 cfg0;
Ralf Baechlef65aad42012-10-17 00:39:09 +0200103
David Daneye1ced092012-11-15 13:58:59 -0800104 cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(0));
105 if (!cfg0.s.ecc_ena) {
106 dev_info(&pdev->dev, "Disabled (ECC not enabled)\n");
107 return 0;
108 }
Ralf Baechlef65aad42012-10-17 00:39:09 +0200109
David Daneye1ced092012-11-15 13:58:59 -0800110 mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0);
111 if (!mci)
112 return -ENXIO;
113
114 mci->pdev = &pdev->dev;
115 mci->dev_name = dev_name(&pdev->dev);
116
117 mci->mod_name = "octeon-lmc";
118 mci->ctl_name = "octeon-lmc-err";
119 mci->edac_check = octeon_lmc_edac_poll;
120
121 if (edac_mc_add_mc(mci)) {
122 dev_err(&pdev->dev, "edac_mc_add_mc() failed\n");
123 edac_mc_free(mci);
124 return -ENXIO;
125 }
126
127 cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
128 cfg0.s.intr_ded_ena = 0; /* We poll */
129 cfg0.s.intr_sec_ena = 0;
130 cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), cfg0.u64);
131 } else {
132 /* OCTEON II */
133 union cvmx_lmcx_int_en en;
134 union cvmx_lmcx_config config;
135
136 config.u64 = cvmx_read_csr(CVMX_LMCX_CONFIG(0));
137 if (!config.s.ecc_ena) {
138 dev_info(&pdev->dev, "Disabled (ECC not enabled)\n");
139 return 0;
140 }
141
142 mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0);
143 if (!mci)
144 return -ENXIO;
145
146 mci->pdev = &pdev->dev;
147 mci->dev_name = dev_name(&pdev->dev);
148
149 mci->mod_name = "octeon-lmc";
150 mci->ctl_name = "co_lmc_err";
151 mci->edac_check = octeon_lmc_edac_poll_o2;
152
153 if (edac_mc_add_mc(mci)) {
154 dev_err(&pdev->dev, "edac_mc_add_mc() failed\n");
155 edac_mc_free(mci);
156 return -ENXIO;
157 }
158
159 en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
160 en.s.intr_ded_ena = 0; /* We poll */
161 en.s.intr_sec_ena = 0;
162 cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), en.u64);
Ralf Baechlef65aad42012-10-17 00:39:09 +0200163 }
David Daneye1ced092012-11-15 13:58:59 -0800164 platform_set_drvdata(pdev, mci);
Ralf Baechlef65aad42012-10-17 00:39:09 +0200165
166 return 0;
Ralf Baechlef65aad42012-10-17 00:39:09 +0200167}
168
David Daneye1ced092012-11-15 13:58:59 -0800169static int octeon_lmc_edac_remove(struct platform_device *pdev)
Ralf Baechlef65aad42012-10-17 00:39:09 +0200170{
171 struct mem_ctl_info *mci = platform_get_drvdata(pdev);
172
Ralf Baechlef65aad42012-10-17 00:39:09 +0200173 edac_mc_del_mc(&pdev->dev);
174 edac_mc_free(mci);
Ralf Baechlef65aad42012-10-17 00:39:09 +0200175 return 0;
176}
177
David Daneye1ced092012-11-15 13:58:59 -0800178static struct platform_driver octeon_lmc_edac_driver = {
179 .probe = octeon_lmc_edac_probe,
180 .remove = octeon_lmc_edac_remove,
Ralf Baechlef65aad42012-10-17 00:39:09 +0200181 .driver = {
David Daneye1ced092012-11-15 13:58:59 -0800182 .name = "octeon_lmc_edac",
Ralf Baechlef65aad42012-10-17 00:39:09 +0200183 }
184};
David Daneye1ced092012-11-15 13:58:59 -0800185module_platform_driver(octeon_lmc_edac_driver);
Ralf Baechlef65aad42012-10-17 00:39:09 +0200186
187MODULE_LICENSE("GPL");
188MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");