blob: 8eaa1d6a8a9ff4ccc5aa0f67768ad021cfe96836 [file] [log] [blame]
Douglas Thompson7c9281d2007-07-19 01:49:33 -07001/*
2 * edac_mc kernel module
3 * (C) 2005, 2006 Linux Networx (http://lnxi.com)
4 * This file may be distributed under the terms of the
5 * GNU General Public License.
6 *
7 * Written Doug Thompson <norsk5@xmission.com>
8 *
9 */
10
11#include <linux/module.h>
12#include <linux/sysdev.h>
13#include <linux/ctype.h>
14
Douglas Thompson20bcb7a2007-07-19 01:49:47 -070015#include "edac_core.h"
Douglas Thompson7c9281d2007-07-19 01:49:33 -070016#include "edac_module.h"
17
18/* MC EDAC Controls, setable by module parameter, and sysfs */
Dave Jiang4de78c62007-07-19 01:49:54 -070019static int edac_mc_log_ue = 1;
20static int edac_mc_log_ce = 1;
21static int edac_mc_panic_on_ue = 0;
22static int edac_mc_poll_msec = 1000;
Douglas Thompson7c9281d2007-07-19 01:49:33 -070023
24/* Getter functions for above */
Dave Jiang4de78c62007-07-19 01:49:54 -070025int edac_mc_get_log_ue(void)
Douglas Thompson7c9281d2007-07-19 01:49:33 -070026{
Dave Jiang4de78c62007-07-19 01:49:54 -070027 return edac_mc_log_ue;
Douglas Thompson7c9281d2007-07-19 01:49:33 -070028}
29
Dave Jiang4de78c62007-07-19 01:49:54 -070030int edac_mc_get_log_ce(void)
Douglas Thompson7c9281d2007-07-19 01:49:33 -070031{
Dave Jiang4de78c62007-07-19 01:49:54 -070032 return edac_mc_log_ce;
Douglas Thompson7c9281d2007-07-19 01:49:33 -070033}
34
Dave Jiang4de78c62007-07-19 01:49:54 -070035int edac_mc_get_panic_on_ue(void)
Douglas Thompson7c9281d2007-07-19 01:49:33 -070036{
Dave Jiang4de78c62007-07-19 01:49:54 -070037 return edac_mc_panic_on_ue;
Douglas Thompson7c9281d2007-07-19 01:49:33 -070038}
39
Dave Jiang81d87cb2007-07-19 01:49:52 -070040/* this is temporary */
41int edac_mc_get_poll_msec(void)
42{
Dave Jiang4de78c62007-07-19 01:49:54 -070043 return edac_mc_poll_msec;
Douglas Thompson7c9281d2007-07-19 01:49:33 -070044}
45
46/* Parameter declarations for above */
Dave Jiang4de78c62007-07-19 01:49:54 -070047module_param(edac_mc_panic_on_ue, int, 0644);
48MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on");
49module_param(edac_mc_log_ue, int, 0644);
50MODULE_PARM_DESC(edac_mc_log_ue,
Douglas Thompson079708b2007-07-19 01:49:58 -070051 "Log uncorrectable error to console: 0=off 1=on");
Dave Jiang4de78c62007-07-19 01:49:54 -070052module_param(edac_mc_log_ce, int, 0644);
53MODULE_PARM_DESC(edac_mc_log_ce,
Douglas Thompson079708b2007-07-19 01:49:58 -070054 "Log correctable error to console: 0=off 1=on");
Dave Jiang4de78c62007-07-19 01:49:54 -070055module_param(edac_mc_poll_msec, int, 0644);
56MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds");
Douglas Thompson7c9281d2007-07-19 01:49:33 -070057
Douglas Thompson7c9281d2007-07-19 01:49:33 -070058/*
59 * various constants for Memory Controllers
60 */
61static const char *mem_types[] = {
62 [MEM_EMPTY] = "Empty",
63 [MEM_RESERVED] = "Reserved",
64 [MEM_UNKNOWN] = "Unknown",
65 [MEM_FPM] = "FPM",
66 [MEM_EDO] = "EDO",
67 [MEM_BEDO] = "BEDO",
68 [MEM_SDR] = "Unbuffered-SDR",
69 [MEM_RDR] = "Registered-SDR",
70 [MEM_DDR] = "Unbuffered-DDR",
71 [MEM_RDDR] = "Registered-DDR",
Dave Jiang1a9b85e2007-07-19 01:49:38 -070072 [MEM_RMBS] = "RMBS",
73 [MEM_DDR2] = "Unbuffered-DDR2",
74 [MEM_FB_DDR2] = "FullyBuffered-DDR2",
75 [MEM_RDDR2] = "Registered-DDR2"
Douglas Thompson7c9281d2007-07-19 01:49:33 -070076};
77
78static const char *dev_types[] = {
79 [DEV_UNKNOWN] = "Unknown",
80 [DEV_X1] = "x1",
81 [DEV_X2] = "x2",
82 [DEV_X4] = "x4",
83 [DEV_X8] = "x8",
84 [DEV_X16] = "x16",
85 [DEV_X32] = "x32",
86 [DEV_X64] = "x64"
87};
88
89static const char *edac_caps[] = {
90 [EDAC_UNKNOWN] = "Unknown",
91 [EDAC_NONE] = "None",
92 [EDAC_RESERVED] = "Reserved",
93 [EDAC_PARITY] = "PARITY",
94 [EDAC_EC] = "EC",
95 [EDAC_SECDED] = "SECDED",
96 [EDAC_S2ECD2ED] = "S2ECD2ED",
97 [EDAC_S4ECD4ED] = "S4ECD4ED",
98 [EDAC_S8ECD8ED] = "S8ECD8ED",
99 [EDAC_S16ECD16ED] = "S16ECD16ED"
100};
101
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700102/* sysfs object:
103 * /sys/devices/system/edac/mc
104 */
105static struct kobject edac_memctrl_kobj;
106
107/* We use these to wait for the reference counts on edac_memctrl_kobj and
108 * edac_pci_kobj to reach 0.
109 */
110static struct completion edac_memctrl_kobj_complete;
111
112/*
113 * /sys/devices/system/edac/mc;
114 * data structures and methods
115 */
116static ssize_t memctrl_int_show(void *ptr, char *buffer)
117{
Douglas Thompson079708b2007-07-19 01:49:58 -0700118 int *value = (int *)ptr;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700119 return sprintf(buffer, "%u\n", *value);
120}
121
122static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count)
123{
Douglas Thompson079708b2007-07-19 01:49:58 -0700124 int *value = (int *)ptr;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700125
126 if (isdigit(*buffer))
127 *value = simple_strtoul(buffer, NULL, 0);
128
129 return count;
130}
131
132struct memctrl_dev_attribute {
133 struct attribute attr;
134 void *value;
Douglas Thompson079708b2007-07-19 01:49:58 -0700135 ssize_t(*show) (void *, char *);
136 ssize_t(*store) (void *, const char *, size_t);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700137};
138
139/* Set of show/store abstract level functions for memory control object */
140static ssize_t memctrl_dev_show(struct kobject *kobj,
Douglas Thompson079708b2007-07-19 01:49:58 -0700141 struct attribute *attr, char *buffer)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700142{
143 struct memctrl_dev_attribute *memctrl_dev;
Douglas Thompson079708b2007-07-19 01:49:58 -0700144 memctrl_dev = (struct memctrl_dev_attribute *)attr;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700145
146 if (memctrl_dev->show)
147 return memctrl_dev->show(memctrl_dev->value, buffer);
148
149 return -EIO;
150}
151
152static ssize_t memctrl_dev_store(struct kobject *kobj, struct attribute *attr,
Douglas Thompson079708b2007-07-19 01:49:58 -0700153 const char *buffer, size_t count)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700154{
155 struct memctrl_dev_attribute *memctrl_dev;
Douglas Thompson079708b2007-07-19 01:49:58 -0700156 memctrl_dev = (struct memctrl_dev_attribute *)attr;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700157
158 if (memctrl_dev->store)
159 return memctrl_dev->store(memctrl_dev->value, buffer, count);
160
161 return -EIO;
162}
163
164static struct sysfs_ops memctrlfs_ops = {
Douglas Thompson079708b2007-07-19 01:49:58 -0700165 .show = memctrl_dev_show,
166 .store = memctrl_dev_store
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700167};
168
169#define MEMCTRL_ATTR(_name,_mode,_show,_store) \
170static struct memctrl_dev_attribute attr_##_name = { \
171 .attr = {.name = __stringify(_name), .mode = _mode }, \
172 .value = &_name, \
173 .show = _show, \
174 .store = _store, \
175};
176
177#define MEMCTRL_STRING_ATTR(_name,_data,_mode,_show,_store) \
178static struct memctrl_dev_attribute attr_##_name = { \
179 .attr = {.name = __stringify(_name), .mode = _mode }, \
180 .value = _data, \
181 .show = _show, \
182 .store = _store, \
183};
184
185/* csrow<id> control files */
Dave Jiang4de78c62007-07-19 01:49:54 -0700186MEMCTRL_ATTR(edac_mc_panic_on_ue,
Douglas Thompson079708b2007-07-19 01:49:58 -0700187 S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
Dave Jiang4de78c62007-07-19 01:49:54 -0700188
189MEMCTRL_ATTR(edac_mc_log_ue,
Douglas Thompson079708b2007-07-19 01:49:58 -0700190 S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
Dave Jiang4de78c62007-07-19 01:49:54 -0700191
192MEMCTRL_ATTR(edac_mc_log_ce,
Douglas Thompson079708b2007-07-19 01:49:58 -0700193 S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
Dave Jiang4de78c62007-07-19 01:49:54 -0700194
195MEMCTRL_ATTR(edac_mc_poll_msec,
Douglas Thompson079708b2007-07-19 01:49:58 -0700196 S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700197
198/* Base Attributes of the memory ECC object */
199static struct memctrl_dev_attribute *memctrl_attr[] = {
Dave Jiang4de78c62007-07-19 01:49:54 -0700200 &attr_edac_mc_panic_on_ue,
201 &attr_edac_mc_log_ue,
202 &attr_edac_mc_log_ce,
203 &attr_edac_mc_poll_msec,
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700204 NULL,
205};
206
207/* Main MC kobject release() function */
208static void edac_memctrl_master_release(struct kobject *kobj)
209{
210 debugf1("%s()\n", __func__);
211 complete(&edac_memctrl_kobj_complete);
212}
213
214static struct kobj_type ktype_memctrl = {
215 .release = edac_memctrl_master_release,
216 .sysfs_ops = &memctrlfs_ops,
Douglas Thompson079708b2007-07-19 01:49:58 -0700217 .default_attrs = (struct attribute **)memctrl_attr,
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700218};
219
220/* Initialize the main sysfs entries for edac:
221 * /sys/devices/system/edac
222 *
223 * and children
224 *
225 * Return: 0 SUCCESS
226 * !0 FAILURE
227 */
228int edac_sysfs_memctrl_setup(void)
229{
230 int err = 0;
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700231 struct sysdev_class *edac_class;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700232
233 debugf1("%s()\n", __func__);
234
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700235 /* get the /sys/devices/system/edac class reference */
236 edac_class = edac_get_edac_class();
237 if (edac_class == NULL) {
238 debugf1("%s() no edac_class error=%d\n", __func__, err);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700239 return err;
240 }
241
242 /* Init the MC's kobject */
Douglas Thompson079708b2007-07-19 01:49:58 -0700243 memset(&edac_memctrl_kobj, 0, sizeof(edac_memctrl_kobj));
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700244 edac_memctrl_kobj.parent = &edac_class->kset.kobj;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700245 edac_memctrl_kobj.ktype = &ktype_memctrl;
246
247 /* generate sysfs "..../edac/mc" */
Douglas Thompson079708b2007-07-19 01:49:58 -0700248 err = kobject_set_name(&edac_memctrl_kobj, "mc");
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700249 if (err) {
Douglas Thompson079708b2007-07-19 01:49:58 -0700250 debugf1("%s() Failed to set name '.../edac/mc'\n", __func__);
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700251 return err;
252 }
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700253
254 /* FIXME: maybe new sysdev_create_subdir() */
255 err = kobject_register(&edac_memctrl_kobj);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700256 if (err) {
Douglas Thompson079708b2007-07-19 01:49:58 -0700257 debugf1("%s() Failed to register '.../edac/mc'\n", __func__);
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700258 return err;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700259 }
260
Douglas Thompson079708b2007-07-19 01:49:58 -0700261 debugf1("%s() Registered '.../edac/mc' kobject\n", __func__);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700262 return 0;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700263}
264
265/*
266 * MC teardown:
267 * the '..../edac/mc' kobject followed by '..../edac' itself
268 */
269void edac_sysfs_memctrl_teardown(void)
270{
271 debugf0("MC: " __FILE__ ": %s()\n", __func__);
272
273 /* Unregister the MC's kobject and wait for reference count to reach 0.
274 */
275 init_completion(&edac_memctrl_kobj_complete);
276 kobject_unregister(&edac_memctrl_kobj);
277 wait_for_completion(&edac_memctrl_kobj_complete);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700278}
279
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700280/* EDAC sysfs CSROW data structures and methods
281 */
282
283/* Set of more default csrow<id> attribute show/store functions */
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700284static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data,
Douglas Thompson079708b2007-07-19 01:49:58 -0700285 int private)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700286{
Douglas Thompson079708b2007-07-19 01:49:58 -0700287 return sprintf(data, "%u\n", csrow->ue_count);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700288}
289
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700290static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data,
Douglas Thompson079708b2007-07-19 01:49:58 -0700291 int private)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700292{
Douglas Thompson079708b2007-07-19 01:49:58 -0700293 return sprintf(data, "%u\n", csrow->ce_count);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700294}
295
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700296static ssize_t csrow_size_show(struct csrow_info *csrow, char *data,
Douglas Thompson079708b2007-07-19 01:49:58 -0700297 int private)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700298{
Douglas Thompson079708b2007-07-19 01:49:58 -0700299 return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages));
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700300}
301
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700302static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data,
Douglas Thompson079708b2007-07-19 01:49:58 -0700303 int private)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700304{
Douglas Thompson079708b2007-07-19 01:49:58 -0700305 return sprintf(data, "%s\n", mem_types[csrow->mtype]);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700306}
307
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700308static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data,
Douglas Thompson079708b2007-07-19 01:49:58 -0700309 int private)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700310{
Douglas Thompson079708b2007-07-19 01:49:58 -0700311 return sprintf(data, "%s\n", dev_types[csrow->dtype]);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700312}
313
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700314static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data,
Douglas Thompson079708b2007-07-19 01:49:58 -0700315 int private)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700316{
Douglas Thompson079708b2007-07-19 01:49:58 -0700317 return sprintf(data, "%s\n", edac_caps[csrow->edac_mode]);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700318}
319
320/* show/store functions for DIMM Label attributes */
321static ssize_t channel_dimm_label_show(struct csrow_info *csrow,
Douglas Thompson079708b2007-07-19 01:49:58 -0700322 char *data, int channel)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700323{
Douglas Thompson079708b2007-07-19 01:49:58 -0700324 return snprintf(data, EDAC_MC_LABEL_LEN, "%s",
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700325 csrow->channels[channel].label);
326}
327
328static ssize_t channel_dimm_label_store(struct csrow_info *csrow,
Douglas Thompson079708b2007-07-19 01:49:58 -0700329 const char *data,
330 size_t count, int channel)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700331{
332 ssize_t max_size = 0;
333
Douglas Thompson079708b2007-07-19 01:49:58 -0700334 max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700335 strncpy(csrow->channels[channel].label, data, max_size);
336 csrow->channels[channel].label[max_size] = '\0';
337
338 return max_size;
339}
340
341/* show function for dynamic chX_ce_count attribute */
342static ssize_t channel_ce_count_show(struct csrow_info *csrow,
Douglas Thompson079708b2007-07-19 01:49:58 -0700343 char *data, int channel)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700344{
345 return sprintf(data, "%u\n", csrow->channels[channel].ce_count);
346}
347
348/* csrow specific attribute structure */
349struct csrowdev_attribute {
350 struct attribute attr;
Douglas Thompson079708b2007-07-19 01:49:58 -0700351 ssize_t(*show) (struct csrow_info *, char *, int);
352 ssize_t(*store) (struct csrow_info *, const char *, size_t, int);
353 int private;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700354};
355
356#define to_csrow(k) container_of(k, struct csrow_info, kobj)
357#define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr)
358
359/* Set of show/store higher level functions for default csrow attributes */
360static ssize_t csrowdev_show(struct kobject *kobj,
Douglas Thompson079708b2007-07-19 01:49:58 -0700361 struct attribute *attr, char *buffer)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700362{
363 struct csrow_info *csrow = to_csrow(kobj);
364 struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
365
366 if (csrowdev_attr->show)
367 return csrowdev_attr->show(csrow,
Douglas Thompson079708b2007-07-19 01:49:58 -0700368 buffer, csrowdev_attr->private);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700369 return -EIO;
370}
371
372static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr,
Douglas Thompson079708b2007-07-19 01:49:58 -0700373 const char *buffer, size_t count)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700374{
375 struct csrow_info *csrow = to_csrow(kobj);
Douglas Thompson079708b2007-07-19 01:49:58 -0700376 struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700377
378 if (csrowdev_attr->store)
379 return csrowdev_attr->store(csrow,
Douglas Thompson079708b2007-07-19 01:49:58 -0700380 buffer,
381 count, csrowdev_attr->private);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700382 return -EIO;
383}
384
385static struct sysfs_ops csrowfs_ops = {
Douglas Thompson079708b2007-07-19 01:49:58 -0700386 .show = csrowdev_show,
387 .store = csrowdev_store
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700388};
389
390#define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \
391static struct csrowdev_attribute attr_##_name = { \
392 .attr = {.name = __stringify(_name), .mode = _mode }, \
393 .show = _show, \
394 .store = _store, \
395 .private = _private, \
396};
397
398/* default cwrow<id>/attribute files */
Douglas Thompson079708b2007-07-19 01:49:58 -0700399CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0);
400CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0);
401CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0);
402CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0);
403CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0);
404CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700405
406/* default attributes of the CSROW<id> object */
407static struct csrowdev_attribute *default_csrow_attr[] = {
408 &attr_dev_type,
409 &attr_mem_type,
410 &attr_edac_mode,
411 &attr_size_mb,
412 &attr_ue_count,
413 &attr_ce_count,
414 NULL,
415};
416
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700417/* possible dynamic channel DIMM Label attribute files */
Douglas Thompson079708b2007-07-19 01:49:58 -0700418CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR,
419 channel_dimm_label_show, channel_dimm_label_store, 0);
420CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR,
421 channel_dimm_label_show, channel_dimm_label_store, 1);
422CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR,
423 channel_dimm_label_show, channel_dimm_label_store, 2);
424CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR,
425 channel_dimm_label_show, channel_dimm_label_store, 3);
426CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR,
427 channel_dimm_label_show, channel_dimm_label_store, 4);
428CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR,
429 channel_dimm_label_show, channel_dimm_label_store, 5);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700430
431/* Total possible dynamic DIMM Label attribute file table */
432static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = {
Douglas Thompson079708b2007-07-19 01:49:58 -0700433 &attr_ch0_dimm_label,
434 &attr_ch1_dimm_label,
435 &attr_ch2_dimm_label,
436 &attr_ch3_dimm_label,
437 &attr_ch4_dimm_label,
438 &attr_ch5_dimm_label
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700439};
440
441/* possible dynamic channel ce_count attribute files */
Douglas Thompson079708b2007-07-19 01:49:58 -0700442CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0);
443CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1);
444CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2);
445CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3);
446CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4);
447CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700448
449/* Total possible dynamic ce_count attribute file table */
450static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = {
Douglas Thompson079708b2007-07-19 01:49:58 -0700451 &attr_ch0_ce_count,
452 &attr_ch1_ce_count,
453 &attr_ch2_ce_count,
454 &attr_ch3_ce_count,
455 &attr_ch4_ce_count,
456 &attr_ch5_ce_count
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700457};
458
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700459#define EDAC_NR_CHANNELS 6
460
461/* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */
462static int edac_create_channel_files(struct kobject *kobj, int chan)
463{
Douglas Thompson079708b2007-07-19 01:49:58 -0700464 int err = -ENODEV;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700465
466 if (chan >= EDAC_NR_CHANNELS)
467 return err;
468
469 /* create the DIMM label attribute file */
470 err = sysfs_create_file(kobj,
Douglas Thompson079708b2007-07-19 01:49:58 -0700471 (struct attribute *)
472 dynamic_csrow_dimm_attr[chan]);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700473
474 if (!err) {
475 /* create the CE Count attribute file */
476 err = sysfs_create_file(kobj,
Douglas Thompson079708b2007-07-19 01:49:58 -0700477 (struct attribute *)
478 dynamic_csrow_ce_count_attr[chan]);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700479 } else {
Douglas Thompsone27e3da2007-07-19 01:49:36 -0700480 debugf1("%s() dimm labels and ce_count files created",
481 __func__);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700482 }
483
484 return err;
485}
486
487/* No memory to release for this kobj */
488static void edac_csrow_instance_release(struct kobject *kobj)
489{
490 struct csrow_info *cs;
491
492 cs = container_of(kobj, struct csrow_info, kobj);
493 complete(&cs->kobj_complete);
494}
495
496/* the kobj_type instance for a CSROW */
497static struct kobj_type ktype_csrow = {
498 .release = edac_csrow_instance_release,
499 .sysfs_ops = &csrowfs_ops,
Douglas Thompson079708b2007-07-19 01:49:58 -0700500 .default_attrs = (struct attribute **)default_csrow_attr,
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700501};
502
503/* Create a CSROW object under specifed edac_mc_device */
Douglas Thompson079708b2007-07-19 01:49:58 -0700504static int edac_create_csrow_object(struct kobject *edac_mci_kobj,
505 struct csrow_info *csrow, int index)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700506{
507 int err = 0;
508 int chan;
509
510 memset(&csrow->kobj, 0, sizeof(csrow->kobj));
511
512 /* generate ..../edac/mc/mc<id>/csrow<index> */
513
514 csrow->kobj.parent = edac_mci_kobj;
515 csrow->kobj.ktype = &ktype_csrow;
516
517 /* name this instance of csrow<id> */
Douglas Thompson079708b2007-07-19 01:49:58 -0700518 err = kobject_set_name(&csrow->kobj, "csrow%d", index);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700519 if (err)
520 goto error_exit;
521
522 /* Instanstiate the csrow object */
523 err = kobject_register(&csrow->kobj);
524 if (!err) {
525 /* Create the dyanmic attribute files on this csrow,
526 * namely, the DIMM labels and the channel ce_count
527 */
528 for (chan = 0; chan < csrow->nr_channels; chan++) {
Douglas Thompson079708b2007-07-19 01:49:58 -0700529 err = edac_create_channel_files(&csrow->kobj, chan);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700530 if (err)
531 break;
532 }
533 }
534
Douglas Thompson079708b2007-07-19 01:49:58 -0700535 error_exit:
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700536 return err;
537}
538
539/* default sysfs methods and data structures for the main MCI kobject */
540
541static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
Douglas Thompson079708b2007-07-19 01:49:58 -0700542 const char *data, size_t count)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700543{
544 int row, chan;
545
546 mci->ue_noinfo_count = 0;
547 mci->ce_noinfo_count = 0;
548 mci->ue_count = 0;
549 mci->ce_count = 0;
550
551 for (row = 0; row < mci->nr_csrows; row++) {
552 struct csrow_info *ri = &mci->csrows[row];
553
554 ri->ue_count = 0;
555 ri->ce_count = 0;
556
557 for (chan = 0; chan < ri->nr_channels; chan++)
558 ri->channels[chan].ce_count = 0;
559 }
560
561 mci->start_time = jiffies;
562 return count;
563}
564
565/* memory scrubbing */
566static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
Douglas Thompson079708b2007-07-19 01:49:58 -0700567 const char *data, size_t count)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700568{
569 u32 bandwidth = -1;
570
571 if (mci->set_sdram_scrub_rate) {
572
573 memctrl_int_store(&bandwidth, data, count);
574
Douglas Thompson079708b2007-07-19 01:49:58 -0700575 if (!(*mci->set_sdram_scrub_rate) (mci, &bandwidth)) {
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700576 edac_printk(KERN_DEBUG, EDAC_MC,
Douglas Thompson079708b2007-07-19 01:49:58 -0700577 "Scrub rate set successfully, applied: %d\n",
578 bandwidth);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700579 } else {
580 /* FIXME: error codes maybe? */
581 edac_printk(KERN_DEBUG, EDAC_MC,
Douglas Thompson079708b2007-07-19 01:49:58 -0700582 "Scrub rate set FAILED, could not apply: %d\n",
583 bandwidth);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700584 }
585 } else {
586 /* FIXME: produce "not implemented" ERROR for user-side. */
587 edac_printk(KERN_WARNING, EDAC_MC,
Douglas Thompson079708b2007-07-19 01:49:58 -0700588 "Memory scrubbing 'set'control is not implemented!\n");
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700589 }
590 return count;
591}
592
593static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
594{
595 u32 bandwidth = -1;
596
597 if (mci->get_sdram_scrub_rate) {
Douglas Thompson079708b2007-07-19 01:49:58 -0700598 if (!(*mci->get_sdram_scrub_rate) (mci, &bandwidth)) {
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700599 edac_printk(KERN_DEBUG, EDAC_MC,
Douglas Thompson079708b2007-07-19 01:49:58 -0700600 "Scrub rate successfully, fetched: %d\n",
601 bandwidth);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700602 } else {
603 /* FIXME: error codes maybe? */
604 edac_printk(KERN_DEBUG, EDAC_MC,
Douglas Thompson079708b2007-07-19 01:49:58 -0700605 "Scrub rate fetch FAILED, got: %d\n",
606 bandwidth);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700607 }
608 } else {
609 /* FIXME: produce "not implemented" ERROR for user-side. */
610 edac_printk(KERN_WARNING, EDAC_MC,
Douglas Thompson079708b2007-07-19 01:49:58 -0700611 "Memory scrubbing 'get' control is not implemented\n");
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700612 }
613 return sprintf(data, "%d\n", bandwidth);
614}
615
616/* default attribute files for the MCI object */
617static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data)
618{
Douglas Thompson079708b2007-07-19 01:49:58 -0700619 return sprintf(data, "%d\n", mci->ue_count);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700620}
621
622static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data)
623{
Douglas Thompson079708b2007-07-19 01:49:58 -0700624 return sprintf(data, "%d\n", mci->ce_count);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700625}
626
627static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data)
628{
Douglas Thompson079708b2007-07-19 01:49:58 -0700629 return sprintf(data, "%d\n", mci->ce_noinfo_count);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700630}
631
632static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data)
633{
Douglas Thompson079708b2007-07-19 01:49:58 -0700634 return sprintf(data, "%d\n", mci->ue_noinfo_count);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700635}
636
637static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data)
638{
Douglas Thompson079708b2007-07-19 01:49:58 -0700639 return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700640}
641
642static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data)
643{
Douglas Thompson079708b2007-07-19 01:49:58 -0700644 return sprintf(data, "%s\n", mci->ctl_name);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700645}
646
647static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data)
648{
649 int total_pages, csrow_idx;
650
651 for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows;
Douglas Thompson079708b2007-07-19 01:49:58 -0700652 csrow_idx++) {
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700653 struct csrow_info *csrow = &mci->csrows[csrow_idx];
654
655 if (!csrow->nr_pages)
656 continue;
657
658 total_pages += csrow->nr_pages;
659 }
660
Douglas Thompson079708b2007-07-19 01:49:58 -0700661 return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700662}
663
664struct mcidev_attribute {
665 struct attribute attr;
Douglas Thompson079708b2007-07-19 01:49:58 -0700666 ssize_t(*show) (struct mem_ctl_info *, char *);
667 ssize_t(*store) (struct mem_ctl_info *, const char *, size_t);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700668};
669
670#define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj)
671#define to_mcidev_attr(a) container_of(a, struct mcidev_attribute, attr)
672
673/* MCI show/store functions for top most object */
674static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr,
Douglas Thompson079708b2007-07-19 01:49:58 -0700675 char *buffer)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700676{
677 struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
Douglas Thompson079708b2007-07-19 01:49:58 -0700678 struct mcidev_attribute *mcidev_attr = to_mcidev_attr(attr);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700679
680 if (mcidev_attr->show)
681 return mcidev_attr->show(mem_ctl_info, buffer);
682
683 return -EIO;
684}
685
686static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr,
Douglas Thompson079708b2007-07-19 01:49:58 -0700687 const char *buffer, size_t count)
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700688{
689 struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
Douglas Thompson079708b2007-07-19 01:49:58 -0700690 struct mcidev_attribute *mcidev_attr = to_mcidev_attr(attr);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700691
692 if (mcidev_attr->store)
693 return mcidev_attr->store(mem_ctl_info, buffer, count);
694
695 return -EIO;
696}
697
698static struct sysfs_ops mci_ops = {
699 .show = mcidev_show,
700 .store = mcidev_store
701};
702
703#define MCIDEV_ATTR(_name,_mode,_show,_store) \
704static struct mcidev_attribute mci_attr_##_name = { \
705 .attr = {.name = __stringify(_name), .mode = _mode }, \
706 .show = _show, \
707 .store = _store, \
708};
709
710/* default Control file */
Douglas Thompson079708b2007-07-19 01:49:58 -0700711MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700712
713/* default Attribute files */
Douglas Thompson079708b2007-07-19 01:49:58 -0700714MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
715MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
716MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
717MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
718MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
719MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
720MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700721
722/* memory scrubber attribute file */
Douglas Thompson079708b2007-07-19 01:49:58 -0700723MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show,
724 mci_sdram_scrub_rate_store);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700725
726static struct mcidev_attribute *mci_attr[] = {
727 &mci_attr_reset_counters,
728 &mci_attr_mc_name,
729 &mci_attr_size_mb,
730 &mci_attr_seconds_since_reset,
731 &mci_attr_ue_noinfo_count,
732 &mci_attr_ce_noinfo_count,
733 &mci_attr_ue_count,
734 &mci_attr_ce_count,
735 &mci_attr_sdram_scrub_rate,
736 NULL
737};
738
739/*
740 * Release of a MC controlling instance
741 */
742static void edac_mci_instance_release(struct kobject *kobj)
743{
744 struct mem_ctl_info *mci;
745
746 mci = to_mci(kobj);
747 debugf0("%s() idx=%d\n", __func__, mci->mc_idx);
748 complete(&mci->kobj_complete);
749}
750
751static struct kobj_type ktype_mci = {
752 .release = edac_mci_instance_release,
753 .sysfs_ops = &mci_ops,
Douglas Thompson079708b2007-07-19 01:49:58 -0700754 .default_attrs = (struct attribute **)mci_attr,
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700755};
756
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700757#define EDAC_DEVICE_SYMLINK "device"
758
759/*
760 * Create a new Memory Controller kobject instance,
761 * mc<id> under the 'mc' directory
762 *
763 * Return:
764 * 0 Success
765 * !0 Failure
766 */
767int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
768{
769 int i;
770 int err;
771 struct csrow_info *csrow;
Douglas Thompson079708b2007-07-19 01:49:58 -0700772 struct kobject *edac_mci_kobj = &mci->edac_mci_kobj;
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700773
774 debugf0("%s() idx=%d\n", __func__, mci->mc_idx);
775 memset(edac_mci_kobj, 0, sizeof(*edac_mci_kobj));
776
777 /* set the name of the mc<id> object */
Douglas Thompson079708b2007-07-19 01:49:58 -0700778 err = kobject_set_name(edac_mci_kobj, "mc%d", mci->mc_idx);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700779 if (err)
780 return err;
781
782 /* link to our parent the '..../edac/mc' object */
783 edac_mci_kobj->parent = &edac_memctrl_kobj;
784 edac_mci_kobj->ktype = &ktype_mci;
785
786 /* register the mc<id> kobject */
787 err = kobject_register(edac_mci_kobj);
788 if (err)
789 return err;
790
791 /* create a symlink for the device */
792 err = sysfs_create_link(edac_mci_kobj, &mci->dev->kobj,
793 EDAC_DEVICE_SYMLINK);
794 if (err)
795 goto fail0;
796
797 /* Make directories for each CSROW object
798 * under the mc<id> kobject
799 */
800 for (i = 0; i < mci->nr_csrows; i++) {
801 csrow = &mci->csrows[i];
802
803 /* Only expose populated CSROWs */
804 if (csrow->nr_pages > 0) {
Douglas Thompson079708b2007-07-19 01:49:58 -0700805 err = edac_create_csrow_object(edac_mci_kobj, csrow, i);
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700806 if (err)
807 goto fail1;
808 }
809 }
810
811 return 0;
812
813 /* CSROW error: backout what has already been registered, */
Douglas Thompson079708b2007-07-19 01:49:58 -0700814 fail1:
815 for (i--; i >= 0; i--) {
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700816 if (csrow->nr_pages > 0) {
817 init_completion(&csrow->kobj_complete);
818 kobject_unregister(&mci->csrows[i].kobj);
819 wait_for_completion(&csrow->kobj_complete);
820 }
821 }
822
Douglas Thompson079708b2007-07-19 01:49:58 -0700823 fail0:
Douglas Thompson7c9281d2007-07-19 01:49:33 -0700824 init_completion(&mci->kobj_complete);
825 kobject_unregister(edac_mci_kobj);
826 wait_for_completion(&mci->kobj_complete);
827 return err;
828}
829
830/*
831 * remove a Memory Controller instance
832 */
833void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
834{
835 int i;
836
837 debugf0("%s()\n", __func__);
838
839 /* remove all csrow kobjects */
840 for (i = 0; i < mci->nr_csrows; i++) {
841 if (mci->csrows[i].nr_pages > 0) {
842 init_completion(&mci->csrows[i].kobj_complete);
843 kobject_unregister(&mci->csrows[i].kobj);
844 wait_for_completion(&mci->csrows[i].kobj_complete);
845 }
846 }
847
848 sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
849 init_completion(&mci->kobj_complete);
850 kobject_unregister(&mci->edac_mci_kobj);
851 wait_for_completion(&mci->kobj_complete);
852}