aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2008-02-07 03:14:53 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-07 11:42:23 -0500
commit48764e4143c06672fc072eb482fdc4c75ee0f968 (patch)
treea308089e6a6e5e680f24854dd7e6a7db0707373c /drivers
parent1d5f726cbf1b736338bbcd83ebc6ad758079261f (diff)
drivers-edac: add Cell MC driver
Adds driver for the Cell memory controller when used without a Hypervisor such as on the IBM Cell blades. There might still be some improvements to do to this such as finding if it's possible to properly obtain more details about the address of the error but it's good enough already to report CE counts which is our main priority at the moment. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk Signed-off-by: Doug Thompson <dougthompson@xmission.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/edac/Kconfig7
-rw-r--r--drivers/edac/Makefile1
-rw-r--r--drivers/edac/cell_edac.c258
3 files changed, 266 insertions, 0 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 98b6b4fb4257..411f021bef99 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -131,5 +131,12 @@ config EDAC_PASEMI
131 Support for error detection and correction on PA Semi 131 Support for error detection and correction on PA Semi
132 PWRficient. 132 PWRficient.
133 133
134config EDAC_CELL
135 tristate "Cell Broadband Engine memory controller"
136 depends on EDAC_MM_EDAC && PPC_CELL_NATIVE
137 help
138 Support for error detection and correction on the
139 Cell Broadband Engine internal memory controller
140 on platform without a hypervisor
134 141
135endif # EDAC 142endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 02c09f0ff157..c07426349a2c 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -28,4 +28,5 @@ obj-$(CONFIG_EDAC_I3000) += i3000_edac.o
28obj-$(CONFIG_EDAC_I82860) += i82860_edac.o 28obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
29obj-$(CONFIG_EDAC_R82600) += r82600_edac.o 29obj-$(CONFIG_EDAC_R82600) += r82600_edac.o
30obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o 30obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o
31obj-$(CONFIG_EDAC_CELL) += cell_edac.o
31 32
diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c
new file mode 100644
index 000000000000..b54112ffd282
--- /dev/null
+++ b/drivers/edac/cell_edac.c
@@ -0,0 +1,258 @@
1/*
2 * Cell MIC driver for ECC counting
3 *
4 * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
5 * <benh@kernel.crashing.org>
6 *
7 * This file may be distributed under the terms of the
8 * GNU General Public License.
9 */
10#undef DEBUG
11
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/platform_device.h>
15#include <linux/stop_machine.h>
16#include <linux/io.h>
17#include <asm/machdep.h>
18#include <asm/cell-regs.h>
19
20#include "edac_core.h"
21
22struct cell_edac_priv
23{
24 struct cbe_mic_tm_regs __iomem *regs;
25 int node;
26 int chanmask;
27#ifdef DEBUG
28 u64 prev_fir;
29#endif
30};
31
32static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
33{
34 struct cell_edac_priv *priv = mci->pvt_info;
35 struct csrow_info *csrow = &mci->csrows[0];
36 unsigned long address, pfn, offset;
37
38 dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016lx\n",
39 priv->node, chan, ar);
40
41 /* Address decoding is likely a bit bogus, to dbl check */
42 address = (ar & 0xffffffffe0000000ul) >> 29;
43 if (priv->chanmask == 0x3)
44 address = (address << 1) | chan;
45 pfn = address >> PAGE_SHIFT;
46 offset = address & ~PAGE_MASK;
47
48 /* TODO: Decoding of the error addresss */
49 edac_mc_handle_ce(mci, csrow->first_page + pfn, offset,
50 0, 0, chan, "");
51}
52
53static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
54{
55 struct cell_edac_priv *priv = mci->pvt_info;
56 struct csrow_info *csrow = &mci->csrows[0];
57 unsigned long address, pfn, offset;
58
59 dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016lx\n",
60 priv->node, chan, ar);
61
62 /* Address decoding is likely a bit bogus, to dbl check */
63 address = (ar & 0xffffffffe0000000ul) >> 29;
64 if (priv->chanmask == 0x3)
65 address = (address << 1) | chan;
66 pfn = address >> PAGE_SHIFT;
67 offset = address & ~PAGE_MASK;
68
69 /* TODO: Decoding of the error addresss */
70 edac_mc_handle_ue(mci, csrow->first_page + pfn, offset, 0, "");
71}
72
73static void cell_edac_check(struct mem_ctl_info *mci)
74{
75 struct cell_edac_priv *priv = mci->pvt_info;
76 u64 fir, addreg, clear = 0;
77
78 fir = in_be64(&priv->regs->mic_fir);
79#ifdef DEBUG
80 if (fir != priv->prev_fir) {
81 dev_dbg(mci->dev, "fir change : 0x%016lx\n", fir);
82 priv->prev_fir = fir;
83 }
84#endif
85 if ((priv->chanmask & 0x1) && (fir & CBE_MIC_FIR_ECC_SINGLE_0_ERR)) {
86 addreg = in_be64(&priv->regs->mic_df_ecc_address_0);
87 clear |= CBE_MIC_FIR_ECC_SINGLE_0_RESET;
88 cell_edac_count_ce(mci, 0, addreg);
89 }
90 if ((priv->chanmask & 0x2) && (fir & CBE_MIC_FIR_ECC_SINGLE_1_ERR)) {
91 addreg = in_be64(&priv->regs->mic_df_ecc_address_1);
92 clear |= CBE_MIC_FIR_ECC_SINGLE_1_RESET;
93 cell_edac_count_ce(mci, 1, addreg);
94 }
95 if ((priv->chanmask & 0x1) && (fir & CBE_MIC_FIR_ECC_MULTI_0_ERR)) {
96 addreg = in_be64(&priv->regs->mic_df_ecc_address_0);
97 clear |= CBE_MIC_FIR_ECC_MULTI_0_RESET;
98 cell_edac_count_ue(mci, 0, addreg);
99 }
100 if ((priv->chanmask & 0x2) && (fir & CBE_MIC_FIR_ECC_MULTI_1_ERR)) {
101 addreg = in_be64(&priv->regs->mic_df_ecc_address_1);
102 clear |= CBE_MIC_FIR_ECC_MULTI_1_RESET;
103 cell_edac_count_ue(mci, 1, addreg);
104 }
105
106 /* The procedure for clearing FIR bits is a bit ... weird */
107 if (clear) {
108 fir &= ~(CBE_MIC_FIR_ECC_ERR_MASK | CBE_MIC_FIR_ECC_SET_MASK);
109 fir |= CBE_MIC_FIR_ECC_RESET_MASK;
110 fir &= ~clear;
111 out_be64(&priv->regs->mic_fir, fir);
112 (void)in_be64(&priv->regs->mic_fir);
113
114 mb(); /* sync up */
115#ifdef DEBUG
116 fir = in_be64(&priv->regs->mic_fir);
117 dev_dbg(mci->dev, "fir clear : 0x%016lx\n", fir);
118#endif
119 }
120}
121
122static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
123{
124 struct csrow_info *csrow = &mci->csrows[0];
125 struct cell_edac_priv *priv = mci->pvt_info;
126 struct device_node *np;
127
128 for (np = NULL;
129 (np = of_find_node_by_name(np, "memory")) != NULL;) {
130 struct resource r;
131
132 /* We "know" that the Cell firmware only creates one entry
133 * in the "memory" nodes. If that changes, this code will
134 * need to be adapted.
135 */
136 if (of_address_to_resource(np, 0, &r))
137 continue;
138 if (of_node_to_nid(np) != priv->node)
139 continue;
140 csrow->first_page = r.start >> PAGE_SHIFT;
141 csrow->nr_pages = (r.end - r.start + 1) >> PAGE_SHIFT;
142 csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
143 csrow->mtype = MEM_XDR;
144 csrow->edac_mode = EDAC_FLAG_EC | EDAC_FLAG_SECDED;
145 dev_dbg(mci->dev,
146 "Initialized on node %d, chanmask=0x%x,"
147 " first_page=0x%lx, nr_pages=0x%x\n",
148 priv->node, priv->chanmask,
149 csrow->first_page, csrow->nr_pages);
150 break;
151 }
152}
153
154static int __devinit cell_edac_probe(struct platform_device *pdev)
155{
156 struct cbe_mic_tm_regs __iomem *regs;
157 struct mem_ctl_info *mci;
158 struct cell_edac_priv *priv;
159 u64 reg;
160 int rc, chanmask;
161
162 regs = cbe_get_cpu_mic_tm_regs(cbe_node_to_cpu(pdev->id));
163 if (regs == NULL)
164 return -ENODEV;
165
166 /* Get channel population */
167 reg = in_be64(&regs->mic_mnt_cfg);
168 dev_dbg(&pdev->dev, "MIC_MNT_CFG = 0x%016lx\n", reg);
169 chanmask = 0;
170 if (reg & CBE_MIC_MNT_CFG_CHAN_0_POP)
171 chanmask |= 0x1;
172 if (reg & CBE_MIC_MNT_CFG_CHAN_1_POP)
173 chanmask |= 0x2;
174 if (chanmask == 0) {
175 dev_warn(&pdev->dev,
176 "Yuck ! No channel populated ? Aborting !\n");
177 return -ENODEV;
178 }
179 dev_dbg(&pdev->dev, "Initial FIR = 0x%016lx\n",
180 in_be64(&regs->mic_fir));
181
182 /* Allocate & init EDAC MC data structure */
183 mci = edac_mc_alloc(sizeof(struct cell_edac_priv), 1,
184 chanmask == 3 ? 2 : 1, pdev->id);
185 if (mci == NULL)
186 return -ENOMEM;
187 priv = mci->pvt_info;
188 priv->regs = regs;
189 priv->node = pdev->id;
190 priv->chanmask = chanmask;
191 mci->dev = &pdev->dev;
192 mci->mtype_cap = MEM_FLAG_XDR;
193 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
194 mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED;
195 mci->mod_name = "cell_edac";
196 mci->ctl_name = "MIC";
197 mci->dev_name = pdev->dev.bus_id;
198 mci->edac_check = cell_edac_check;
199 cell_edac_init_csrows(mci);
200
201 /* Register with EDAC core */
202 rc = edac_mc_add_mc(mci);
203 if (rc) {
204 dev_err(&pdev->dev, "failed to register with EDAC core\n");
205 edac_mc_free(mci);
206 return rc;
207 }
208
209 return 0;
210}
211
212static int __devexit cell_edac_remove(struct platform_device *pdev)
213{
214 struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev);
215 if (mci)
216 edac_mc_free(mci);
217 return 0;
218}
219
220static struct platform_driver cell_edac_driver = {
221 .driver = {
222 .name = "cbe-mic",
223 .owner = THIS_MODULE,
224 },
225 .probe = cell_edac_probe,
226 .remove = cell_edac_remove,
227};
228
229static int __init cell_edac_init(void)
230{
231 /* Sanity check registers data structure */
232 BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
233 mic_df_ecc_address_0) != 0xf8);
234 BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
235 mic_df_ecc_address_1) != 0x1b8);
236 BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
237 mic_df_config) != 0x218);
238 BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
239 mic_fir) != 0x230);
240 BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
241 mic_mnt_cfg) != 0x210);
242 BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
243 mic_exc) != 0x208);
244
245 return platform_driver_register(&cell_edac_driver);
246}
247
248static void __exit cell_edac_exit(void)
249{
250 platform_driver_unregister(&cell_edac_driver);
251}
252
253module_init(cell_edac_init);
254module_exit(cell_edac_exit);
255
256MODULE_LICENSE("GPL");
257MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
258MODULE_DESCRIPTION("ECC counting for Cell MIC");