aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/tile_edac.c
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2011-03-01 13:01:49 -0500
committerChris Metcalf <cmetcalf@tilera.com>2011-03-10 13:30:14 -0500
commit5c7707554858eca8903706b6df7cba5c0f802244 (patch)
tree6ebb5ef933b1cf4dbca374cf2a03e249383cd41e /drivers/edac/tile_edac.c
parentd91c641233ae09fcccec75313b7f55992668bf8d (diff)
drivers/edac: provide support for tile architecture
Add tile support for the EDAC driver, which provides unified system error (memory, PCI, etc.) reporting. For now, the TILEPro port reports memory correctable error (CE) only. Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Diffstat (limited to 'drivers/edac/tile_edac.c')
-rw-r--r--drivers/edac/tile_edac.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c
new file mode 100644
index 000000000000..1d5cf06f6c6b
--- /dev/null
+++ b/drivers/edac/tile_edac.c
@@ -0,0 +1,254 @@
1/*
2 * Copyright 2011 Tilera Corporation. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation, version 2.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11 * NON INFRINGEMENT. See the GNU General Public License for
12 * more details.
13 * Tilera-specific EDAC driver.
14 *
15 * This source code is derived from the following driver:
16 *
17 * Cell MIC driver for ECC counting
18 *
19 * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
20 * <benh@kernel.crashing.org>
21 *
22 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/platform_device.h>
27#include <linux/io.h>
28#include <linux/uaccess.h>
29#include <linux/edac.h>
30#include <hv/hypervisor.h>
31#include <hv/drv_mshim_intf.h>
32
33#include "edac_core.h"
34
35#define DRV_NAME "tile-edac"
36
37/* Number of cs_rows needed per memory controller on TILEPro. */
38#define TILE_EDAC_NR_CSROWS 1
39
40/* Number of channels per memory controller on TILEPro. */
41#define TILE_EDAC_NR_CHANS 1
42
43/* Granularity of reported error in bytes on TILEPro. */
44#define TILE_EDAC_ERROR_GRAIN 8
45
46/* TILE processor has multiple independent memory controllers. */
47struct platform_device *mshim_pdev[TILE_MAX_MSHIMS];
48
49struct tile_edac_priv {
50 int hv_devhdl; /* Hypervisor device handle. */
51 int node; /* Memory controller instance #. */
52 unsigned int ce_count; /*
53 * Correctable-error counter
54 * kept by the driver.
55 */
56};
57
58static void tile_edac_check(struct mem_ctl_info *mci)
59{
60 struct tile_edac_priv *priv = mci->pvt_info;
61 struct mshim_mem_error mem_error;
62
63 if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_error,
64 sizeof(struct mshim_mem_error), MSHIM_MEM_ERROR_OFF) !=
65 sizeof(struct mshim_mem_error)) {
66 pr_err(DRV_NAME ": MSHIM_MEM_ERROR_OFF pread failure.\n");
67 return;
68 }
69
70 /* Check if the current error count is different from the saved one. */
71 if (mem_error.sbe_count != priv->ce_count) {
72 dev_dbg(mci->dev, "ECC CE err on node %d\n", priv->node);
73 priv->ce_count = mem_error.sbe_count;
74 edac_mc_handle_ce(mci, 0, 0, 0, 0, 0, mci->ctl_name);
75 }
76}
77
78/*
79 * Initialize the 'csrows' table within the mci control structure with the
80 * addressing of memory.
81 */
82static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
83{
84 struct csrow_info *csrow = &mci->csrows[0];
85 struct tile_edac_priv *priv = mci->pvt_info;
86 struct mshim_mem_info mem_info;
87
88 if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info,
89 sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) !=
90 sizeof(struct mshim_mem_info)) {
91 pr_err(DRV_NAME ": MSHIM_MEM_INFO_OFF pread failure.\n");
92 return -1;
93 }
94
95 if (mem_info.mem_ecc)
96 csrow->edac_mode = EDAC_SECDED;
97 else
98 csrow->edac_mode = EDAC_NONE;
99 switch (mem_info.mem_type) {
100 case DDR2:
101 csrow->mtype = MEM_DDR2;
102 break;
103
104 case DDR3:
105 csrow->mtype = MEM_DDR3;
106 break;
107
108 default:
109 return -1;
110 }
111
112 csrow->first_page = 0;
113 csrow->nr_pages = mem_info.mem_size >> PAGE_SHIFT;
114 csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
115 csrow->grain = TILE_EDAC_ERROR_GRAIN;
116 csrow->dtype = DEV_UNKNOWN;
117
118 return 0;
119}
120
121static int __devinit tile_edac_mc_probe(struct platform_device *pdev)
122{
123 char hv_file[32];
124 int hv_devhdl;
125 struct mem_ctl_info *mci;
126 struct tile_edac_priv *priv;
127 int rc;
128
129 sprintf(hv_file, "mshim/%d", pdev->id);
130 hv_devhdl = hv_dev_open((HV_VirtAddr)hv_file, 0);
131 if (hv_devhdl < 0)
132 return -EINVAL;
133
134 /* A TILE MC has a single channel and one chip-select row. */
135 mci = edac_mc_alloc(sizeof(struct tile_edac_priv),
136 TILE_EDAC_NR_CSROWS, TILE_EDAC_NR_CHANS, pdev->id);
137 if (mci == NULL)
138 return -ENOMEM;
139 priv = mci->pvt_info;
140 priv->node = pdev->id;
141 priv->hv_devhdl = hv_devhdl;
142
143 mci->dev = &pdev->dev;
144 mci->mtype_cap = MEM_FLAG_DDR2;
145 mci->edac_ctl_cap = EDAC_FLAG_SECDED;
146
147 mci->mod_name = DRV_NAME;
148 mci->ctl_name = "TILEPro_Memory_Controller";
149 mci->dev_name = dev_name(&pdev->dev);
150 mci->edac_check = tile_edac_check;
151
152 /*
153 * Initialize the MC control structure 'csrows' table
154 * with the mapping and control information.
155 */
156 if (tile_edac_init_csrows(mci)) {
157 /* No csrows found. */
158 mci->edac_cap = EDAC_FLAG_NONE;
159 } else {
160 mci->edac_cap = EDAC_FLAG_SECDED;
161 }
162
163 platform_set_drvdata(pdev, mci);
164
165 /* Register with EDAC core */
166 rc = edac_mc_add_mc(mci);
167 if (rc) {
168 dev_err(&pdev->dev, "failed to register with EDAC core\n");
169 edac_mc_free(mci);
170 return rc;
171 }
172
173 return 0;
174}
175
176static int __devexit tile_edac_mc_remove(struct platform_device *pdev)
177{
178 struct mem_ctl_info *mci = platform_get_drvdata(pdev);
179
180 edac_mc_del_mc(&pdev->dev);
181 if (mci)
182 edac_mc_free(mci);
183 return 0;
184}
185
186static struct platform_driver tile_edac_mc_driver = {
187 .driver = {
188 .name = DRV_NAME,
189 .owner = THIS_MODULE,
190 },
191 .probe = tile_edac_mc_probe,
192 .remove = __devexit_p(tile_edac_mc_remove),
193};
194
195/*
196 * Driver init routine.
197 */
198static int __init tile_edac_init(void)
199{
200 char hv_file[32];
201 struct platform_device *pdev;
202 int i, err, num = 0;
203
204 /* Only support POLL mode. */
205 edac_op_state = EDAC_OPSTATE_POLL;
206
207 err = platform_driver_register(&tile_edac_mc_driver);
208 if (err)
209 return err;
210
211 for (i = 0; i < TILE_MAX_MSHIMS; i++) {
212 /*
213 * Not all memory controllers are configured such as in the
214 * case of a simulator. So we register only those mshims
215 * that are configured by the hypervisor.
216 */
217 sprintf(hv_file, "mshim/%d", i);
218 if (hv_dev_open((HV_VirtAddr)hv_file, 0) < 0)
219 continue;
220
221 pdev = platform_device_register_simple(DRV_NAME, i, NULL, 0);
222 if (IS_ERR(pdev))
223 continue;
224 mshim_pdev[i] = pdev;
225 num++;
226 }
227
228 if (num == 0) {
229 platform_driver_unregister(&tile_edac_mc_driver);
230 return -ENODEV;
231 }
232 return 0;
233}
234
235/*
236 * Driver cleanup routine.
237 */
238static void __exit tile_edac_exit(void)
239{
240 int i;
241
242 for (i = 0; i < TILE_MAX_MSHIMS; i++) {
243 struct platform_device *pdev = mshim_pdev[i];
244 if (!pdev)
245 continue;
246
247 platform_set_drvdata(pdev, NULL);
248 platform_device_unregister(pdev);
249 }
250 platform_driver_unregister(&tile_edac_mc_driver);
251}
252
253module_init(tile_edac_init);
254module_exit(tile_edac_exit);