cell_edac.c 7.72 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * Cell MIC driver for ECC counting
 *
 * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
 *                <benh@kernel.crashing.org>
 *
 * This file may be distributed under the terms of the
 * GNU General Public License.
 */
#undef DEBUG

12
#include <linux/edac.h>
13 14 15 16 17
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/stop_machine.h>
#include <linux/io.h>
18
#include <linux/of_address.h>
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include <asm/machdep.h>
#include <asm/cell-regs.h>

#include "edac_core.h"

struct cell_edac_priv
{
	struct cbe_mic_tm_regs __iomem	*regs;
	int				node;
	int				chanmask;
#ifdef DEBUG
	u64				prev_fir;
#endif
};

static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
{
	struct cell_edac_priv		*priv = mci->pvt_info;
37
	struct csrow_info		*csrow = mci->csrows[0];
38
	unsigned long			address, pfn, offset, syndrome;
39

40
	dev_dbg(mci->pdev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n",
41 42 43 44 45 46 47 48
		priv->node, chan, ar);

	/* Address decoding is likely a bit bogus, to dbl check */
	address = (ar & 0xffffffffe0000000ul) >> 29;
	if (priv->chanmask == 0x3)
		address = (address << 1) | chan;
	pfn = address >> PAGE_SHIFT;
	offset = address & ~PAGE_MASK;
49
	syndrome = (ar & 0x000000001fe00000ul) >> 21;
50

51
	/* TODO: Decoding of the error address */
52
	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
53
			     csrow->first_page + pfn, offset, syndrome,
54
			     0, chan, -1, "", "");
55 56 57 58 59
}

static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
{
	struct cell_edac_priv		*priv = mci->pvt_info;
60
	struct csrow_info		*csrow = mci->csrows[0];
61 62
	unsigned long			address, pfn, offset;

63
	dev_dbg(mci->pdev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n",
64 65 66 67 68 69 70 71 72
		priv->node, chan, ar);

	/* Address decoding is likely a bit bogus, to dbl check */
	address = (ar & 0xffffffffe0000000ul) >> 29;
	if (priv->chanmask == 0x3)
		address = (address << 1) | chan;
	pfn = address >> PAGE_SHIFT;
	offset = address & ~PAGE_MASK;

73
	/* TODO: Decoding of the error address */
74
	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
75
			     csrow->first_page + pfn, offset, 0,
76
			     0, chan, -1, "", "");
77 78 79 80 81 82 83 84 85 86
}

static void cell_edac_check(struct mem_ctl_info *mci)
{
	struct cell_edac_priv		*priv = mci->pvt_info;
	u64				fir, addreg, clear = 0;

	fir = in_be64(&priv->regs->mic_fir);
#ifdef DEBUG
	if (fir != priv->prev_fir) {
87
		dev_dbg(mci->pdev, "fir change : 0x%016lx\n", fir);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
		priv->prev_fir = fir;
	}
#endif
	if ((priv->chanmask & 0x1) && (fir & CBE_MIC_FIR_ECC_SINGLE_0_ERR)) {
		addreg = in_be64(&priv->regs->mic_df_ecc_address_0);
		clear |= CBE_MIC_FIR_ECC_SINGLE_0_RESET;
		cell_edac_count_ce(mci, 0, addreg);
	}
	if ((priv->chanmask & 0x2) && (fir & CBE_MIC_FIR_ECC_SINGLE_1_ERR)) {
		addreg = in_be64(&priv->regs->mic_df_ecc_address_1);
		clear |= CBE_MIC_FIR_ECC_SINGLE_1_RESET;
		cell_edac_count_ce(mci, 1, addreg);
	}
	if ((priv->chanmask & 0x1) && (fir & CBE_MIC_FIR_ECC_MULTI_0_ERR)) {
		addreg = in_be64(&priv->regs->mic_df_ecc_address_0);
		clear |= CBE_MIC_FIR_ECC_MULTI_0_RESET;
		cell_edac_count_ue(mci, 0, addreg);
	}
	if ((priv->chanmask & 0x2) && (fir & CBE_MIC_FIR_ECC_MULTI_1_ERR)) {
		addreg = in_be64(&priv->regs->mic_df_ecc_address_1);
		clear |= CBE_MIC_FIR_ECC_MULTI_1_RESET;
		cell_edac_count_ue(mci, 1, addreg);
	}

	/* The procedure for clearing FIR bits is a bit ... weird */
	if (clear) {
		fir &= ~(CBE_MIC_FIR_ECC_ERR_MASK | CBE_MIC_FIR_ECC_SET_MASK);
		fir |= CBE_MIC_FIR_ECC_RESET_MASK;
		fir &= ~clear;
		out_be64(&priv->regs->mic_fir, fir);
		(void)in_be64(&priv->regs->mic_fir);

		mb();	/* sync up */
#ifdef DEBUG
		fir = in_be64(&priv->regs->mic_fir);
123
		dev_dbg(mci->pdev, "fir clear  : 0x%016lx\n", fir);
124 125 126 127
#endif
	}
}

128
static void cell_edac_init_csrows(struct mem_ctl_info *mci)
129
{
130
	struct csrow_info		*csrow = mci->csrows[0];
131
	struct dimm_info		*dimm;
132 133
	struct cell_edac_priv		*priv = mci->pvt_info;
	struct device_node		*np;
134
	int				j;
135
	u32				nr_pages;
136

137
	for_each_node_by_name(np, "memory") {
138 139 140 141 142 143 144 145 146 147 148
		struct resource r;

		/* We "know" that the Cell firmware only creates one entry
		 * in the "memory" nodes. If that changes, this code will
		 * need to be adapted.
		 */
		if (of_address_to_resource(np, 0, &r))
			continue;
		if (of_node_to_nid(np) != priv->node)
			continue;
		csrow->first_page = r.start >> PAGE_SHIFT;
149 150
		nr_pages = resource_size(&r) >> PAGE_SHIFT;
		csrow->last_page = csrow->first_page + nr_pages - 1;
151 152

		for (j = 0; j < csrow->nr_channels; j++) {
153
			dimm = csrow->channels[j]->dimm;
154 155
			dimm->mtype = MEM_XDR;
			dimm->edac_mode = EDAC_SECDED;
156
			dimm->nr_pages = nr_pages / csrow->nr_channels;
157
		}
158
		dev_dbg(mci->pdev,
159 160 161
			"Initialized on node %d, chanmask=0x%x,"
			" first_page=0x%lx, nr_pages=0x%x\n",
			priv->node, priv->chanmask,
162
			csrow->first_page, nr_pages);
163 164
		break;
	}
165
	of_node_put(np);
166 167
}

168
static int cell_edac_probe(struct platform_device *pdev)
169 170 171
{
	struct cbe_mic_tm_regs __iomem	*regs;
	struct mem_ctl_info		*mci;
172
	struct edac_mc_layer		layers[2];
173 174
	struct cell_edac_priv		*priv;
	u64				reg;
175
	int				rc, chanmask, num_chans;
176 177 178 179 180

	regs = cbe_get_cpu_mic_tm_regs(cbe_node_to_cpu(pdev->id));
	if (regs == NULL)
		return -ENODEV;

181 182
	edac_op_state = EDAC_OPSTATE_POLL;

183 184
	/* Get channel population */
	reg = in_be64(&regs->mic_mnt_cfg);
185
	dev_dbg(&pdev->dev, "MIC_MNT_CFG = 0x%016llx\n", reg);
186 187 188 189 190 191 192 193 194 195
	chanmask = 0;
	if (reg & CBE_MIC_MNT_CFG_CHAN_0_POP)
		chanmask |= 0x1;
	if (reg & CBE_MIC_MNT_CFG_CHAN_1_POP)
		chanmask |= 0x2;
	if (chanmask == 0) {
		dev_warn(&pdev->dev,
			 "Yuck ! No channel populated ? Aborting !\n");
		return -ENODEV;
	}
196
	dev_dbg(&pdev->dev, "Initial FIR = 0x%016llx\n",
197 198 199
		in_be64(&regs->mic_fir));

	/* Allocate & init EDAC MC data structure */
200 201 202 203 204 205 206 207
	num_chans = chanmask == 3 ? 2 : 1;

	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
	layers[0].size = 1;
	layers[0].is_virt_csrow = true;
	layers[1].type = EDAC_MC_LAYER_CHANNEL;
	layers[1].size = num_chans;
	layers[1].is_virt_csrow = false;
208
	mci = edac_mc_alloc(pdev->id, ARRAY_SIZE(layers), layers,
209
			    sizeof(struct cell_edac_priv));
210 211 212 213 214 215
	if (mci == NULL)
		return -ENOMEM;
	priv = mci->pvt_info;
	priv->regs = regs;
	priv->node = pdev->id;
	priv->chanmask = chanmask;
216
	mci->pdev = &pdev->dev;
217 218 219 220 221
	mci->mtype_cap = MEM_FLAG_XDR;
	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
	mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED;
	mci->mod_name = "cell_edac";
	mci->ctl_name = "MIC";
222
	mci->dev_name = dev_name(&pdev->dev);
223 224 225 226 227 228 229 230 231 232 233 234 235 236
	mci->edac_check = cell_edac_check;
	cell_edac_init_csrows(mci);

	/* Register with EDAC core */
	rc = edac_mc_add_mc(mci);
	if (rc) {
		dev_err(&pdev->dev, "failed to register with EDAC core\n");
		edac_mc_free(mci);
		return rc;
	}

	return 0;
}

237
static int cell_edac_remove(struct platform_device *pdev)
238 239 240 241 242 243 244 245 246 247 248 249
{
	struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev);
	if (mci)
		edac_mc_free(mci);
	return 0;
}

static struct platform_driver cell_edac_driver = {
	.driver		= {
		.name	= "cbe-mic",
	},
	.probe		= cell_edac_probe,
250
	.remove		= cell_edac_remove,
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
};

static int __init cell_edac_init(void)
{
	/* Sanity check registers data structure */
	BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
			      mic_df_ecc_address_0) != 0xf8);
	BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
			      mic_df_ecc_address_1) != 0x1b8);
	BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
			      mic_df_config) != 0x218);
	BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
			      mic_fir) != 0x230);
	BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
			      mic_mnt_cfg) != 0x210);
	BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
			      mic_exc) != 0x208);

	return platform_driver_register(&cell_edac_driver);
}

static void __exit cell_edac_exit(void)
{
	platform_driver_unregister(&cell_edac_driver);
}

module_init(cell_edac_init);
module_exit(cell_edac_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("ECC counting for Cell MIC");