Blame view

drivers/edac/edac_mc_sysfs.c 26 KB
7c9281d76   Douglas Thompson   drivers/edac: spl...
1
2
  /*
   * edac_mc kernel module
42a8e397a   Douglas Thompson   drivers/edac: add...
3
4
   * (C) 2005-2007 Linux Networx (http://lnxi.com)
   *
7c9281d76   Douglas Thompson   drivers/edac: spl...
5
6
7
   * This file may be distributed under the terms of the
   * GNU General Public License.
   *
42a8e397a   Douglas Thompson   drivers/edac: add...
8
   * Written Doug Thompson <norsk5@xmission.com> www.softwarebitmaker.com
7c9281d76   Douglas Thompson   drivers/edac: spl...
9
10
   *
   */
7c9281d76   Douglas Thompson   drivers/edac: spl...
11
  #include <linux/ctype.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
12
  #include <linux/slab.h>
8096cfafb   Doug Thompson   drivers/edac: fix...
13
  #include <linux/bug.h>
7c9281d76   Douglas Thompson   drivers/edac: spl...
14

20bcb7a81   Douglas Thompson   drivers/edac: mod...
15
  #include "edac_core.h"
7c9281d76   Douglas Thompson   drivers/edac: spl...
16
  #include "edac_module.h"
8096cfafb   Doug Thompson   drivers/edac: fix...
17

7c9281d76   Douglas Thompson   drivers/edac: spl...
18
  /* MC EDAC Controls, setable by module parameter, and sysfs */
4de78c687   Dave Jiang   drivers/edac: mod...
19
20
  static int edac_mc_log_ue = 1;
  static int edac_mc_log_ce = 1;
f044091ca   Douglas Thompson   drivers/edac: rem...
21
  static int edac_mc_panic_on_ue;
4de78c687   Dave Jiang   drivers/edac: mod...
22
  static int edac_mc_poll_msec = 1000;
7c9281d76   Douglas Thompson   drivers/edac: spl...
23
24
  
  /* Getter functions for above */
4de78c687   Dave Jiang   drivers/edac: mod...
25
  int edac_mc_get_log_ue(void)
7c9281d76   Douglas Thompson   drivers/edac: spl...
26
  {
4de78c687   Dave Jiang   drivers/edac: mod...
27
  	return edac_mc_log_ue;
7c9281d76   Douglas Thompson   drivers/edac: spl...
28
  }
4de78c687   Dave Jiang   drivers/edac: mod...
29
  int edac_mc_get_log_ce(void)
7c9281d76   Douglas Thompson   drivers/edac: spl...
30
  {
4de78c687   Dave Jiang   drivers/edac: mod...
31
  	return edac_mc_log_ce;
7c9281d76   Douglas Thompson   drivers/edac: spl...
32
  }
4de78c687   Dave Jiang   drivers/edac: mod...
33
  int edac_mc_get_panic_on_ue(void)
7c9281d76   Douglas Thompson   drivers/edac: spl...
34
  {
4de78c687   Dave Jiang   drivers/edac: mod...
35
  	return edac_mc_panic_on_ue;
7c9281d76   Douglas Thompson   drivers/edac: spl...
36
  }
81d87cb13   Dave Jiang   drivers/edac: mod...
37
38
39
  /* this is temporary */
  int edac_mc_get_poll_msec(void)
  {
4de78c687   Dave Jiang   drivers/edac: mod...
40
  	return edac_mc_poll_msec;
7c9281d76   Douglas Thompson   drivers/edac: spl...
41
  }
096846e2b   Arthur Jones   edac: core fix wo...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  static int edac_set_poll_msec(const char *val, struct kernel_param *kp)
  {
  	long l;
  	int ret;
  
  	if (!val)
  		return -EINVAL;
  
  	ret = strict_strtol(val, 0, &l);
  	if (ret == -EINVAL || ((int)l != l))
  		return -EINVAL;
  	*((int *)kp->arg) = l;
  
  	/* notify edac_mc engine to reset the poll period */
  	edac_mc_reset_delay_period(l);
  
  	return 0;
  }
7c9281d76   Douglas Thompson   drivers/edac: spl...
60
  /* Parameter declarations for above */
4de78c687   Dave Jiang   drivers/edac: mod...
61
62
63
64
  module_param(edac_mc_panic_on_ue, int, 0644);
  MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on");
  module_param(edac_mc_log_ue, int, 0644);
  MODULE_PARM_DESC(edac_mc_log_ue,
079708b91   Douglas Thompson   drivers/edac: cor...
65
  		 "Log uncorrectable error to console: 0=off 1=on");
4de78c687   Dave Jiang   drivers/edac: mod...
66
67
  module_param(edac_mc_log_ce, int, 0644);
  MODULE_PARM_DESC(edac_mc_log_ce,
079708b91   Douglas Thompson   drivers/edac: cor...
68
  		 "Log correctable error to console: 0=off 1=on");
096846e2b   Arthur Jones   edac: core fix wo...
69
70
  module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int,
  		  &edac_mc_poll_msec, 0644);
4de78c687   Dave Jiang   drivers/edac: mod...
71
  MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds");
7c9281d76   Douglas Thompson   drivers/edac: spl...
72

7c9281d76   Douglas Thompson   drivers/edac: spl...
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  /*
   * various constants for Memory Controllers
   */
  static const char *mem_types[] = {
  	[MEM_EMPTY] = "Empty",
  	[MEM_RESERVED] = "Reserved",
  	[MEM_UNKNOWN] = "Unknown",
  	[MEM_FPM] = "FPM",
  	[MEM_EDO] = "EDO",
  	[MEM_BEDO] = "BEDO",
  	[MEM_SDR] = "Unbuffered-SDR",
  	[MEM_RDR] = "Registered-SDR",
  	[MEM_DDR] = "Unbuffered-DDR",
  	[MEM_RDDR] = "Registered-DDR",
1a9b85e6b   Dave Jiang   drivers/edac: mc ...
87
88
89
  	[MEM_RMBS] = "RMBS",
  	[MEM_DDR2] = "Unbuffered-DDR2",
  	[MEM_FB_DDR2] = "FullyBuffered-DDR2",
1d5f726cb   Benjamin Herrenschmidt   drivers-edac: add...
90
  	[MEM_RDDR2] = "Registered-DDR2",
b1cfebc92   Yang Shi   edac: add DDR3 me...
91
92
93
  	[MEM_XDR] = "XDR",
  	[MEM_DDR3] = "Unbuffered-DDR3",
  	[MEM_RDDR3] = "Registered-DDR3"
7c9281d76   Douglas Thompson   drivers/edac: spl...
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
  };
  
  static const char *dev_types[] = {
  	[DEV_UNKNOWN] = "Unknown",
  	[DEV_X1] = "x1",
  	[DEV_X2] = "x2",
  	[DEV_X4] = "x4",
  	[DEV_X8] = "x8",
  	[DEV_X16] = "x16",
  	[DEV_X32] = "x32",
  	[DEV_X64] = "x64"
  };
  
  static const char *edac_caps[] = {
  	[EDAC_UNKNOWN] = "Unknown",
  	[EDAC_NONE] = "None",
  	[EDAC_RESERVED] = "Reserved",
  	[EDAC_PARITY] = "PARITY",
  	[EDAC_EC] = "EC",
  	[EDAC_SECDED] = "SECDED",
  	[EDAC_S2ECD2ED] = "S2ECD2ED",
  	[EDAC_S4ECD4ED] = "S4ECD4ED",
  	[EDAC_S8ECD8ED] = "S8ECD8ED",
  	[EDAC_S16ECD16ED] = "S16ECD16ED"
  };
7c9281d76   Douglas Thompson   drivers/edac: spl...
119
120
121
122
  /* EDAC sysfs CSROW data structures and methods
   */
  
  /* Set of more default csrow<id> attribute show/store functions */
e27e3dac6   Douglas Thompson   drivers/edac: add...
123
  static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data,
052dfb45c   Douglas Thompson   drivers/edac: cle...
124
  				int private)
7c9281d76   Douglas Thompson   drivers/edac: spl...
125
  {
079708b91   Douglas Thompson   drivers/edac: cor...
126
127
  	return sprintf(data, "%u
  ", csrow->ue_count);
7c9281d76   Douglas Thompson   drivers/edac: spl...
128
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
129
  static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data,
052dfb45c   Douglas Thompson   drivers/edac: cle...
130
  				int private)
7c9281d76   Douglas Thompson   drivers/edac: spl...
131
  {
079708b91   Douglas Thompson   drivers/edac: cor...
132
133
  	return sprintf(data, "%u
  ", csrow->ce_count);
7c9281d76   Douglas Thompson   drivers/edac: spl...
134
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
135
  static ssize_t csrow_size_show(struct csrow_info *csrow, char *data,
052dfb45c   Douglas Thompson   drivers/edac: cle...
136
  				int private)
7c9281d76   Douglas Thompson   drivers/edac: spl...
137
  {
079708b91   Douglas Thompson   drivers/edac: cor...
138
139
  	return sprintf(data, "%u
  ", PAGES_TO_MiB(csrow->nr_pages));
7c9281d76   Douglas Thompson   drivers/edac: spl...
140
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
141
  static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data,
052dfb45c   Douglas Thompson   drivers/edac: cle...
142
  				int private)
7c9281d76   Douglas Thompson   drivers/edac: spl...
143
  {
079708b91   Douglas Thompson   drivers/edac: cor...
144
145
  	return sprintf(data, "%s
  ", mem_types[csrow->mtype]);
7c9281d76   Douglas Thompson   drivers/edac: spl...
146
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
147
  static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data,
052dfb45c   Douglas Thompson   drivers/edac: cle...
148
  				int private)
7c9281d76   Douglas Thompson   drivers/edac: spl...
149
  {
079708b91   Douglas Thompson   drivers/edac: cor...
150
151
  	return sprintf(data, "%s
  ", dev_types[csrow->dtype]);
7c9281d76   Douglas Thompson   drivers/edac: spl...
152
  }
e27e3dac6   Douglas Thompson   drivers/edac: add...
153
  static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data,
052dfb45c   Douglas Thompson   drivers/edac: cle...
154
  				int private)
7c9281d76   Douglas Thompson   drivers/edac: spl...
155
  {
079708b91   Douglas Thompson   drivers/edac: cor...
156
157
  	return sprintf(data, "%s
  ", edac_caps[csrow->edac_mode]);
7c9281d76   Douglas Thompson   drivers/edac: spl...
158
159
160
161
  }
  
  /* show/store functions for DIMM Label attributes */
  static ssize_t channel_dimm_label_show(struct csrow_info *csrow,
052dfb45c   Douglas Thompson   drivers/edac: cle...
162
  				char *data, int channel)
7c9281d76   Douglas Thompson   drivers/edac: spl...
163
  {
124682c78   Arthur Jones   edac: core fix ad...
164
165
166
167
168
169
  	/* if field has not been initialized, there is nothing to send */
  	if (!csrow->channels[channel].label[0])
  		return 0;
  
  	return snprintf(data, EDAC_MC_LABEL_LEN, "%s
  ",
7c9281d76   Douglas Thompson   drivers/edac: spl...
170
171
172
173
  			csrow->channels[channel].label);
  }
  
  static ssize_t channel_dimm_label_store(struct csrow_info *csrow,
079708b91   Douglas Thompson   drivers/edac: cor...
174
175
  					const char *data,
  					size_t count, int channel)
7c9281d76   Douglas Thompson   drivers/edac: spl...
176
177
  {
  	ssize_t max_size = 0;
079708b91   Douglas Thompson   drivers/edac: cor...
178
  	max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
7c9281d76   Douglas Thompson   drivers/edac: spl...
179
180
181
182
183
184
185
186
  	strncpy(csrow->channels[channel].label, data, max_size);
  	csrow->channels[channel].label[max_size] = '\0';
  
  	return max_size;
  }
  
  /* show function for dynamic chX_ce_count attribute */
  static ssize_t channel_ce_count_show(struct csrow_info *csrow,
052dfb45c   Douglas Thompson   drivers/edac: cle...
187
  				char *data, int channel)
7c9281d76   Douglas Thompson   drivers/edac: spl...
188
189
190
191
192
193
194
195
  {
  	return sprintf(data, "%u
  ", csrow->channels[channel].ce_count);
  }
  
  /* csrow specific attribute structure */
  struct csrowdev_attribute {
  	struct attribute attr;
079708b91   Douglas Thompson   drivers/edac: cor...
196
197
198
  	 ssize_t(*show) (struct csrow_info *, char *, int);
  	 ssize_t(*store) (struct csrow_info *, const char *, size_t, int);
  	int private;
7c9281d76   Douglas Thompson   drivers/edac: spl...
199
200
201
202
203
204
205
  };
  
  #define to_csrow(k) container_of(k, struct csrow_info, kobj)
  #define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr)
  
  /* Set of show/store higher level functions for default csrow attributes */
  static ssize_t csrowdev_show(struct kobject *kobj,
052dfb45c   Douglas Thompson   drivers/edac: cle...
206
  			struct attribute *attr, char *buffer)
7c9281d76   Douglas Thompson   drivers/edac: spl...
207
208
209
210
211
212
  {
  	struct csrow_info *csrow = to_csrow(kobj);
  	struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
  
  	if (csrowdev_attr->show)
  		return csrowdev_attr->show(csrow,
052dfb45c   Douglas Thompson   drivers/edac: cle...
213
  					buffer, csrowdev_attr->private);
7c9281d76   Douglas Thompson   drivers/edac: spl...
214
215
216
217
  	return -EIO;
  }
  
  static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr,
052dfb45c   Douglas Thompson   drivers/edac: cle...
218
  			const char *buffer, size_t count)
7c9281d76   Douglas Thompson   drivers/edac: spl...
219
220
  {
  	struct csrow_info *csrow = to_csrow(kobj);
079708b91   Douglas Thompson   drivers/edac: cor...
221
  	struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr);
7c9281d76   Douglas Thompson   drivers/edac: spl...
222
223
224
  
  	if (csrowdev_attr->store)
  		return csrowdev_attr->store(csrow,
052dfb45c   Douglas Thompson   drivers/edac: cle...
225
226
  					buffer,
  					count, csrowdev_attr->private);
7c9281d76   Douglas Thompson   drivers/edac: spl...
227
228
  	return -EIO;
  }
52cf25d0a   Emese Revfy   Driver core: Cons...
229
  static const struct sysfs_ops csrowfs_ops = {
079708b91   Douglas Thompson   drivers/edac: cor...
230
231
  	.show = csrowdev_show,
  	.store = csrowdev_store
7c9281d76   Douglas Thompson   drivers/edac: spl...
232
233
234
235
236
237
238
239
240
241
242
  };
  
  #define CSROWDEV_ATTR(_name,_mode,_show,_store,_private)	\
  static struct csrowdev_attribute attr_##_name = {			\
  	.attr = {.name = __stringify(_name), .mode = _mode },	\
  	.show   = _show,					\
  	.store  = _store,					\
  	.private = _private,					\
  };
  
  /* default cwrow<id>/attribute files */
079708b91   Douglas Thompson   drivers/edac: cor...
243
244
245
246
247
248
  CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0);
  CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0);
  CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0);
  CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0);
  CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0);
  CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0);
7c9281d76   Douglas Thompson   drivers/edac: spl...
249
250
251
252
253
254
255
256
257
258
259
  
  /* default attributes of the CSROW<id> object */
  static struct csrowdev_attribute *default_csrow_attr[] = {
  	&attr_dev_type,
  	&attr_mem_type,
  	&attr_edac_mode,
  	&attr_size_mb,
  	&attr_ue_count,
  	&attr_ce_count,
  	NULL,
  };
7c9281d76   Douglas Thompson   drivers/edac: spl...
260
  /* possible dynamic channel DIMM Label attribute files */
079708b91   Douglas Thompson   drivers/edac: cor...
261
  CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
262
  	channel_dimm_label_show, channel_dimm_label_store, 0);
079708b91   Douglas Thompson   drivers/edac: cor...
263
  CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
264
  	channel_dimm_label_show, channel_dimm_label_store, 1);
079708b91   Douglas Thompson   drivers/edac: cor...
265
  CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
266
  	channel_dimm_label_show, channel_dimm_label_store, 2);
079708b91   Douglas Thompson   drivers/edac: cor...
267
  CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
268
  	channel_dimm_label_show, channel_dimm_label_store, 3);
079708b91   Douglas Thompson   drivers/edac: cor...
269
  CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
270
  	channel_dimm_label_show, channel_dimm_label_store, 4);
079708b91   Douglas Thompson   drivers/edac: cor...
271
  CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR,
052dfb45c   Douglas Thompson   drivers/edac: cle...
272
  	channel_dimm_label_show, channel_dimm_label_store, 5);
7c9281d76   Douglas Thompson   drivers/edac: spl...
273
274
275
  
  /* Total possible dynamic DIMM Label attribute file table */
  static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = {
079708b91   Douglas Thompson   drivers/edac: cor...
276
277
278
279
280
281
  	&attr_ch0_dimm_label,
  	&attr_ch1_dimm_label,
  	&attr_ch2_dimm_label,
  	&attr_ch3_dimm_label,
  	&attr_ch4_dimm_label,
  	&attr_ch5_dimm_label
7c9281d76   Douglas Thompson   drivers/edac: spl...
282
283
284
  };
  
  /* possible dynamic channel ce_count attribute files */
079708b91   Douglas Thompson   drivers/edac: cor...
285
286
287
288
289
290
  CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0);
  CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1);
  CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2);
  CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3);
  CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4);
  CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5);
7c9281d76   Douglas Thompson   drivers/edac: spl...
291
292
293
  
  /* Total possible dynamic ce_count attribute file table */
  static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = {
079708b91   Douglas Thompson   drivers/edac: cor...
294
295
296
297
298
299
  	&attr_ch0_ce_count,
  	&attr_ch1_ce_count,
  	&attr_ch2_ce_count,
  	&attr_ch3_ce_count,
  	&attr_ch4_ce_count,
  	&attr_ch5_ce_count
7c9281d76   Douglas Thompson   drivers/edac: spl...
300
  };
7c9281d76   Douglas Thompson   drivers/edac: spl...
301
302
303
304
305
  #define EDAC_NR_CHANNELS	6
  
  /* Create dynamic CHANNEL files, indexed by 'chan',  under specifed CSROW */
  static int edac_create_channel_files(struct kobject *kobj, int chan)
  {
079708b91   Douglas Thompson   drivers/edac: cor...
306
  	int err = -ENODEV;
7c9281d76   Douglas Thompson   drivers/edac: spl...
307
308
309
310
311
312
  
  	if (chan >= EDAC_NR_CHANNELS)
  		return err;
  
  	/* create the DIMM label attribute file */
  	err = sysfs_create_file(kobj,
079708b91   Douglas Thompson   drivers/edac: cor...
313
314
  				(struct attribute *)
  				dynamic_csrow_dimm_attr[chan]);
7c9281d76   Douglas Thompson   drivers/edac: spl...
315
316
317
318
  
  	if (!err) {
  		/* create the CE Count attribute file */
  		err = sysfs_create_file(kobj,
079708b91   Douglas Thompson   drivers/edac: cor...
319
320
  					(struct attribute *)
  					dynamic_csrow_ce_count_attr[chan]);
7c9281d76   Douglas Thompson   drivers/edac: spl...
321
  	} else {
e27e3dac6   Douglas Thompson   drivers/edac: add...
322
323
  		debugf1("%s()  dimm labels and ce_count files created",
  			__func__);
7c9281d76   Douglas Thompson   drivers/edac: spl...
324
325
326
327
328
329
330
331
  	}
  
  	return err;
  }
  
  /* No memory to release for this kobj */
  static void edac_csrow_instance_release(struct kobject *kobj)
  {
8096cfafb   Doug Thompson   drivers/edac: fix...
332
  	struct mem_ctl_info *mci;
7c9281d76   Douglas Thompson   drivers/edac: spl...
333
  	struct csrow_info *cs;
8096cfafb   Doug Thompson   drivers/edac: fix...
334
335
  	debugf1("%s()
  ", __func__);
7c9281d76   Douglas Thompson   drivers/edac: spl...
336
  	cs = container_of(kobj, struct csrow_info, kobj);
8096cfafb   Doug Thompson   drivers/edac: fix...
337
338
339
  	mci = cs->mci;
  
  	kobject_put(&mci->edac_mci_kobj);
7c9281d76   Douglas Thompson   drivers/edac: spl...
340
341
342
343
344
345
  }
  
  /* the kobj_type instance for a CSROW */
  static struct kobj_type ktype_csrow = {
  	.release = edac_csrow_instance_release,
  	.sysfs_ops = &csrowfs_ops,
079708b91   Douglas Thompson   drivers/edac: cor...
346
  	.default_attrs = (struct attribute **)default_csrow_attr,
7c9281d76   Douglas Thompson   drivers/edac: spl...
347
348
349
  };
  
  /* Create a CSROW object under specifed edac_mc_device */
8096cfafb   Doug Thompson   drivers/edac: fix...
350
351
  static int edac_create_csrow_object(struct mem_ctl_info *mci,
  					struct csrow_info *csrow, int index)
7c9281d76   Douglas Thompson   drivers/edac: spl...
352
  {
8096cfafb   Doug Thompson   drivers/edac: fix...
353
354
  	struct kobject *kobj_mci = &mci->edac_mci_kobj;
  	struct kobject *kobj;
7c9281d76   Douglas Thompson   drivers/edac: spl...
355
  	int chan;
8096cfafb   Doug Thompson   drivers/edac: fix...
356
  	int err;
7c9281d76   Douglas Thompson   drivers/edac: spl...
357
358
  
  	/* generate ..../edac/mc/mc<id>/csrow<index>   */
8096cfafb   Doug Thompson   drivers/edac: fix...
359
360
  	memset(&csrow->kobj, 0, sizeof(csrow->kobj));
  	csrow->mci = mci;	/* include container up link */
8096cfafb   Doug Thompson   drivers/edac: fix...
361
362
363
364
365
366
367
  
  	/* bump the mci instance's kobject's ref count */
  	kobj = kobject_get(&mci->edac_mci_kobj);
  	if (!kobj) {
  		err = -ENODEV;
  		goto err_out;
  	}
7c9281d76   Douglas Thompson   drivers/edac: spl...
368
369
  
  	/* Instanstiate the csrow object */
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
370
371
  	err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci,
  				   "csrow%d", index);
8096cfafb   Doug Thompson   drivers/edac: fix...
372
373
374
375
  	if (err)
  		goto err_release_top_kobj;
  
  	/* At this point, to release a csrow kobj, one must
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
376
  	 * call the kobject_put and allow that tear down
8096cfafb   Doug Thompson   drivers/edac: fix...
377
378
379
380
381
382
383
384
385
386
  	 * to work the releasing
  	 */
  
  	/* Create the dyanmic attribute files on this csrow,
  	 * namely, the DIMM labels and the channel ce_count
  	 */
  	for (chan = 0; chan < csrow->nr_channels; chan++) {
  		err = edac_create_channel_files(&csrow->kobj, chan);
  		if (err) {
  			/* special case the unregister here */
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
387
  			kobject_put(&csrow->kobj);
8096cfafb   Doug Thompson   drivers/edac: fix...
388
  			goto err_out;
7c9281d76   Douglas Thompson   drivers/edac: spl...
389
390
  		}
  	}
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
391
  	kobject_uevent(&csrow->kobj, KOBJ_ADD);
8096cfafb   Doug Thompson   drivers/edac: fix...
392
393
394
395
396
397
398
  	return 0;
  
  	/* error unwind stack */
  err_release_top_kobj:
  	kobject_put(&mci->edac_mci_kobj);
  
  err_out:
7c9281d76   Douglas Thompson   drivers/edac: spl...
399
400
401
402
403
404
  	return err;
  }
  
  /* default sysfs methods and data structures for the main MCI kobject */
  
  static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
079708b91   Douglas Thompson   drivers/edac: cor...
405
  					const char *data, size_t count)
7c9281d76   Douglas Thompson   drivers/edac: spl...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
  {
  	int row, chan;
  
  	mci->ue_noinfo_count = 0;
  	mci->ce_noinfo_count = 0;
  	mci->ue_count = 0;
  	mci->ce_count = 0;
  
  	for (row = 0; row < mci->nr_csrows; row++) {
  		struct csrow_info *ri = &mci->csrows[row];
  
  		ri->ue_count = 0;
  		ri->ce_count = 0;
  
  		for (chan = 0; chan < ri->nr_channels; chan++)
  			ri->channels[chan].ce_count = 0;
  	}
  
  	mci->start_time = jiffies;
  	return count;
  }
  
  /* memory scrubbing */
  static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
eba042a81   Borislav Petkov   edac, mc: Improve...
430
  					  const char *data, size_t count)
7c9281d76   Douglas Thompson   drivers/edac: spl...
431
  {
eba042a81   Borislav Petkov   edac, mc: Improve...
432
433
  	unsigned long bandwidth = 0;
  	int err;
7c9281d76   Douglas Thompson   drivers/edac: spl...
434

eba042a81   Borislav Petkov   edac, mc: Improve...
435
436
437
438
439
440
  	if (!mci->set_sdram_scrub_rate) {
  		edac_printk(KERN_WARNING, EDAC_MC,
  			    "Memory scrub rate setting not implemented!
  ");
  		return -EINVAL;
  	}
7c9281d76   Douglas Thompson   drivers/edac: spl...
441

eba042a81   Borislav Petkov   edac, mc: Improve...
442
443
  	if (strict_strtoul(data, 10, &bandwidth) < 0)
  		return -EINVAL;
7c9281d76   Douglas Thompson   drivers/edac: spl...
444

eba042a81   Borislav Petkov   edac, mc: Improve...
445
446
447
448
449
450
451
452
453
454
455
456
  	err = mci->set_sdram_scrub_rate(mci, (u32)bandwidth);
  	if (err) {
  		edac_printk(KERN_DEBUG, EDAC_MC,
  			    "Failed setting scrub rate to %lu
  ", bandwidth);
  		return -EINVAL;
  	}
  	else {
  		edac_printk(KERN_DEBUG, EDAC_MC,
  			    "Scrub rate set to: %lu
  ", bandwidth);
  		return count;
7c9281d76   Douglas Thompson   drivers/edac: spl...
457
  	}
7c9281d76   Douglas Thompson   drivers/edac: spl...
458
459
460
461
  }
  
  static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
  {
eba042a81   Borislav Petkov   edac, mc: Improve...
462
463
464
465
  	u32 bandwidth = 0;
  	int err;
  
  	if (!mci->get_sdram_scrub_rate) {
7c9281d76   Douglas Thompson   drivers/edac: spl...
466
  		edac_printk(KERN_WARNING, EDAC_MC,
eba042a81   Borislav Petkov   edac, mc: Improve...
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
  			    "Memory scrub rate reading not implemented
  ");
  		return -EINVAL;
  	}
  
  	err = mci->get_sdram_scrub_rate(mci, &bandwidth);
  	if (err) {
  		edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate
  ");
  		return err;
  	}
  	else {
  		edac_printk(KERN_DEBUG, EDAC_MC,
  			    "Read scrub rate: %d
  ", bandwidth);
  		return sprintf(data, "%d
  ", bandwidth);
7c9281d76   Douglas Thompson   drivers/edac: spl...
484
  	}
7c9281d76   Douglas Thompson   drivers/edac: spl...
485
486
487
488
489
  }
  
  /* default attribute files for the MCI object */
  static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data)
  {
079708b91   Douglas Thompson   drivers/edac: cor...
490
491
  	return sprintf(data, "%d
  ", mci->ue_count);
7c9281d76   Douglas Thompson   drivers/edac: spl...
492
493
494
495
  }
  
  static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data)
  {
079708b91   Douglas Thompson   drivers/edac: cor...
496
497
  	return sprintf(data, "%d
  ", mci->ce_count);
7c9281d76   Douglas Thompson   drivers/edac: spl...
498
499
500
501
  }
  
  static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data)
  {
079708b91   Douglas Thompson   drivers/edac: cor...
502
503
  	return sprintf(data, "%d
  ", mci->ce_noinfo_count);
7c9281d76   Douglas Thompson   drivers/edac: spl...
504
505
506
507
  }
  
  static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data)
  {
079708b91   Douglas Thompson   drivers/edac: cor...
508
509
  	return sprintf(data, "%d
  ", mci->ue_noinfo_count);
7c9281d76   Douglas Thompson   drivers/edac: spl...
510
511
512
513
  }
  
  static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data)
  {
079708b91   Douglas Thompson   drivers/edac: cor...
514
515
  	return sprintf(data, "%ld
  ", (jiffies - mci->start_time) / HZ);
7c9281d76   Douglas Thompson   drivers/edac: spl...
516
517
518
519
  }
  
  static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data)
  {
079708b91   Douglas Thompson   drivers/edac: cor...
520
521
  	return sprintf(data, "%s
  ", mci->ctl_name);
7c9281d76   Douglas Thompson   drivers/edac: spl...
522
523
524
525
526
527
528
  }
  
  static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data)
  {
  	int total_pages, csrow_idx;
  
  	for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows;
052dfb45c   Douglas Thompson   drivers/edac: cle...
529
  		csrow_idx++) {
7c9281d76   Douglas Thompson   drivers/edac: spl...
530
531
532
533
534
535
536
  		struct csrow_info *csrow = &mci->csrows[csrow_idx];
  
  		if (!csrow->nr_pages)
  			continue;
  
  		total_pages += csrow->nr_pages;
  	}
079708b91   Douglas Thompson   drivers/edac: cor...
537
538
  	return sprintf(data, "%u
  ", PAGES_TO_MiB(total_pages));
7c9281d76   Douglas Thompson   drivers/edac: spl...
539
  }
7c9281d76   Douglas Thompson   drivers/edac: spl...
540
  #define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj)
42a8e397a   Douglas Thompson   drivers/edac: add...
541
  #define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr)
7c9281d76   Douglas Thompson   drivers/edac: spl...
542
543
544
  
  /* MCI show/store functions for top most object */
  static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr,
052dfb45c   Douglas Thompson   drivers/edac: cle...
545
  			char *buffer)
7c9281d76   Douglas Thompson   drivers/edac: spl...
546
547
  {
  	struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
42a8e397a   Douglas Thompson   drivers/edac: add...
548
  	struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
7c9281d76   Douglas Thompson   drivers/edac: spl...
549

cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
550
551
  	debugf1("%s() mem_ctl_info %p
  ", __func__, mem_ctl_info);
7c9281d76   Douglas Thompson   drivers/edac: spl...
552
553
554
555
556
557
558
  	if (mcidev_attr->show)
  		return mcidev_attr->show(mem_ctl_info, buffer);
  
  	return -EIO;
  }
  
  static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr,
052dfb45c   Douglas Thompson   drivers/edac: cle...
559
  			const char *buffer, size_t count)
7c9281d76   Douglas Thompson   drivers/edac: spl...
560
561
  {
  	struct mem_ctl_info *mem_ctl_info = to_mci(kobj);
42a8e397a   Douglas Thompson   drivers/edac: add...
562
  	struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
7c9281d76   Douglas Thompson   drivers/edac: spl...
563

cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
564
565
  	debugf1("%s() mem_ctl_info %p
  ", __func__, mem_ctl_info);
7c9281d76   Douglas Thompson   drivers/edac: spl...
566
567
568
569
570
  	if (mcidev_attr->store)
  		return mcidev_attr->store(mem_ctl_info, buffer, count);
  
  	return -EIO;
  }
8096cfafb   Doug Thompson   drivers/edac: fix...
571
  /* Intermediate show/store table */
52cf25d0a   Emese Revfy   Driver core: Cons...
572
  static const struct sysfs_ops mci_ops = {
7c9281d76   Douglas Thompson   drivers/edac: spl...
573
574
575
576
577
  	.show = mcidev_show,
  	.store = mcidev_store
  };
  
  #define MCIDEV_ATTR(_name,_mode,_show,_store)			\
42a8e397a   Douglas Thompson   drivers/edac: add...
578
  static struct mcidev_sysfs_attribute mci_attr_##_name = {			\
7c9281d76   Douglas Thompson   drivers/edac: spl...
579
580
581
582
583
584
  	.attr = {.name = __stringify(_name), .mode = _mode },	\
  	.show   = _show,					\
  	.store  = _store,					\
  };
  
  /* default Control file */
079708b91   Douglas Thompson   drivers/edac: cor...
585
  MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
7c9281d76   Douglas Thompson   drivers/edac: spl...
586
587
  
  /* default Attribute files */
079708b91   Douglas Thompson   drivers/edac: cor...
588
589
590
591
592
593
594
  MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
  MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
  MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
  MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
  MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
  MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
  MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
7c9281d76   Douglas Thompson   drivers/edac: spl...
595
596
  
  /* memory scrubber attribute file */
079708b91   Douglas Thompson   drivers/edac: cor...
597
  MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show,
052dfb45c   Douglas Thompson   drivers/edac: cle...
598
  	mci_sdram_scrub_rate_store);
7c9281d76   Douglas Thompson   drivers/edac: spl...
599

42a8e397a   Douglas Thompson   drivers/edac: add...
600
  static struct mcidev_sysfs_attribute *mci_attr[] = {
7c9281d76   Douglas Thompson   drivers/edac: spl...
601
602
603
604
605
606
607
608
609
610
611
  	&mci_attr_reset_counters,
  	&mci_attr_mc_name,
  	&mci_attr_size_mb,
  	&mci_attr_seconds_since_reset,
  	&mci_attr_ue_noinfo_count,
  	&mci_attr_ce_noinfo_count,
  	&mci_attr_ue_count,
  	&mci_attr_ce_count,
  	&mci_attr_sdram_scrub_rate,
  	NULL
  };
8096cfafb   Doug Thompson   drivers/edac: fix...
612

7c9281d76   Douglas Thompson   drivers/edac: spl...
613
614
  /*
   * Release of a MC controlling instance
8096cfafb   Doug Thompson   drivers/edac: fix...
615
616
617
618
619
620
621
   *
   *	each MC control instance has the following resources upon entry:
   *		a) a ref count on the top memctl kobj
   *		b) a ref count on this module
   *
   *	this function must decrement those ref counts and then
   *	issue a free on the instance's memory
7c9281d76   Douglas Thompson   drivers/edac: spl...
622
   */
8096cfafb   Doug Thompson   drivers/edac: fix...
623
  static void edac_mci_control_release(struct kobject *kobj)
7c9281d76   Douglas Thompson   drivers/edac: spl...
624
625
626
627
  {
  	struct mem_ctl_info *mci;
  
  	mci = to_mci(kobj);
8096cfafb   Doug Thompson   drivers/edac: fix...
628
629
630
631
632
633
634
635
636
  
  	debugf0("%s() mci instance idx=%d releasing
  ", __func__, mci->mc_idx);
  
  	/* decrement the module ref count */
  	module_put(mci->owner);
  
  	/* free the mci instance memory here */
  	kfree(mci);
7c9281d76   Douglas Thompson   drivers/edac: spl...
637
638
639
  }
  
  static struct kobj_type ktype_mci = {
8096cfafb   Doug Thompson   drivers/edac: fix...
640
  	.release = edac_mci_control_release,
7c9281d76   Douglas Thompson   drivers/edac: spl...
641
  	.sysfs_ops = &mci_ops,
079708b91   Douglas Thompson   drivers/edac: cor...
642
  	.default_attrs = (struct attribute **)mci_attr,
7c9281d76   Douglas Thompson   drivers/edac: spl...
643
  };
8096cfafb   Doug Thompson   drivers/edac: fix...
644
645
646
  /* EDAC memory controller sysfs kset:
   *	/sys/devices/system/edac/mc
   */
f9fc82adc   Arthur Jones   edac: core fix st...
647
  static struct kset *mc_kset;
8096cfafb   Doug Thompson   drivers/edac: fix...
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
  
  /*
   * edac_mc_register_sysfs_main_kobj
   *
   *	setups and registers the main kobject for each mci
   */
  int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci)
  {
  	struct kobject *kobj_mci;
  	int err;
  
  	debugf1("%s()
  ", __func__);
  
  	kobj_mci = &mci->edac_mci_kobj;
  
  	/* Init the mci's kobject */
  	memset(kobj_mci, 0, sizeof(*kobj_mci));
8096cfafb   Doug Thompson   drivers/edac: fix...
666
667
668
669
670
671
672
673
674
675
  	/* Record which module 'owns' this control structure
  	 * and bump the ref count of the module
  	 */
  	mci->owner = THIS_MODULE;
  
  	/* bump ref count on this module */
  	if (!try_module_get(mci->owner)) {
  		err = -ENODEV;
  		goto fail_out;
  	}
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
676
  	/* this instance become part of the mc_kset */
f9fc82adc   Arthur Jones   edac: core fix st...
677
  	kobj_mci->kset = mc_kset;
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
678

8096cfafb   Doug Thompson   drivers/edac: fix...
679
  	/* register the mc<id> kobject to the mc_kset */
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
680
681
  	err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL,
  				   "mc%d", mci->mc_idx);
8096cfafb   Doug Thompson   drivers/edac: fix...
682
683
684
685
686
687
  	if (err) {
  		debugf1("%s()Failed to register '.../edac/mc%d'
  ",
  			__func__, mci->mc_idx);
  		goto kobj_reg_fail;
  	}
b2ed215a3   Greg Kroah-Hartman   Kobject: change d...
688
  	kobject_uevent(kobj_mci, KOBJ_ADD);
8096cfafb   Doug Thompson   drivers/edac: fix...
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
  
  	/* At this point, to 'free' the control struct,
  	 * edac_mc_unregister_sysfs_main_kobj() must be used
  	 */
  
  	debugf1("%s() Registered '.../edac/mc%d' kobject
  ",
  		__func__, mci->mc_idx);
  
  	return 0;
  
  	/* Error exit stack */
  
  kobj_reg_fail:
  	module_put(mci->owner);
  
  fail_out:
  	return err;
  }
  
  /*
   * edac_mc_register_sysfs_main_kobj
   *
   *	tears down and the main mci kobject from the mc_kset
   */
  void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci)
  {
  	/* delete the kobj from the mc_kset */
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
717
  	kobject_put(&mci->edac_mci_kobj);
8096cfafb   Doug Thompson   drivers/edac: fix...
718
  }
7c9281d76   Douglas Thompson   drivers/edac: spl...
719
  #define EDAC_DEVICE_SYMLINK	"device"
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
720
  #define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci)
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
  
  /* MCI show/store functions for top most object */
  static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr,
  			char *buffer)
  {
  	struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj);
  	struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
  
  	debugf1("%s() mem_ctl_info %p
  ", __func__, mem_ctl_info);
  
  	if (mcidev_attr->show)
  		return mcidev_attr->show(mem_ctl_info, buffer);
  
  	return -EIO;
  }
  
  static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr,
  			const char *buffer, size_t count)
  {
  	struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj);
  	struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr);
  
  	debugf1("%s() mem_ctl_info %p
  ", __func__, mem_ctl_info);
  
  	if (mcidev_attr->store)
  		return mcidev_attr->store(mem_ctl_info, buffer, count);
  
  	return -EIO;
  }
  
  /* No memory to release for this kobj */
  static void edac_inst_grp_release(struct kobject *kobj)
  {
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
756
  	struct mcidev_sysfs_group_kobj *grp;
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
757
758
759
760
  	struct mem_ctl_info *mci;
  
  	debugf1("%s()
  ", __func__);
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
761
  	grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj);
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  	mci = grp->mci;
  
  	kobject_put(&mci->edac_mci_kobj);
  }
  
  /* Intermediate show/store table */
  static struct sysfs_ops inst_grp_ops = {
  	.show = inst_grp_show,
  	.store = inst_grp_store
  };
  
  /* the kobj_type instance for a instance group */
  static struct kobj_type ktype_inst_grp = {
  	.release = edac_inst_grp_release,
  	.sysfs_ops = &inst_grp_ops,
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
777
  };
7c9281d76   Douglas Thompson   drivers/edac: spl...
778
  /*
8096cfafb   Doug Thompson   drivers/edac: fix...
779
   * edac_create_mci_instance_attributes
9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
780
781
782
   *	create MC driver specific attributes bellow an specified kobj
   * This routine calls itself recursively, in order to create an entire
   * object tree.
42a8e397a   Douglas Thompson   drivers/edac: add...
783
   */
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
784
  static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci,
9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
785
786
  				struct mcidev_sysfs_attribute *sysfs_attrib,
  				struct kobject *kobj)
42a8e397a   Douglas Thompson   drivers/edac: add...
787
788
  {
  	int err;
42a8e397a   Douglas Thompson   drivers/edac: add...
789

cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
790
791
  	debugf1("%s()
  ", __func__);
9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
792
793
  	while (sysfs_attrib) {
  		if (sysfs_attrib->grp) {
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
794
795
796
797
798
799
800
801
802
803
  			struct mcidev_sysfs_group_kobj *grp_kobj;
  
  			grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL);
  			if (!grp_kobj)
  				return -ENOMEM;
  
  			list_add_tail(&grp_kobj->list, &mci->grp_kobj_list);
  
  			grp_kobj->grp = sysfs_attrib->grp;
  			grp_kobj->mci = mci;
c419d921e   Mauro Carvalho Chehab   edac: Don't creat...
804

cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
805
806
807
  			debugf0("%s() grp %s, mci %p
  ", __func__,
  				sysfs_attrib->grp->name, mci);
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
808
809
  			err = kobject_init_and_add(&grp_kobj->kobj,
  						&ktype_inst_grp,
c419d921e   Mauro Carvalho Chehab   edac: Don't creat...
810
  						&mci->edac_mci_kobj,
9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
811
812
813
  						sysfs_attrib->grp->name);
  			if (err)
  				return err;
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
814
  			err = edac_create_mci_instance_attributes(mci,
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
815
816
  					grp_kobj->grp->mcidev_attr,
  					&grp_kobj->kobj);
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
817

9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
818
819
820
821
822
823
824
825
826
827
  			if (err)
  				return err;
  		} else if (sysfs_attrib->attr.name) {
  			debugf0("%s() file %s
  ", __func__,
  				sysfs_attrib->attr.name);
  
  			err = sysfs_create_file(kobj, &sysfs_attrib->attr);
  		} else
  			break;
42a8e397a   Douglas Thompson   drivers/edac: add...
828

42a8e397a   Douglas Thompson   drivers/edac: add...
829
830
831
  		if (err) {
  			return err;
  		}
42a8e397a   Douglas Thompson   drivers/edac: add...
832
833
834
835
836
837
838
  		sysfs_attrib++;
  	}
  
  	return 0;
  }
  
  /*
8096cfafb   Doug Thompson   drivers/edac: fix...
839
840
841
842
   * edac_remove_mci_instance_attributes
   *	remove MC driver specific attributes at the topmost level
   *	directory of this mci instance.
   */
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
843
  static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci,
9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
844
  				struct mcidev_sysfs_attribute *sysfs_attrib,
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
845
  				struct kobject *kobj, int count)
8096cfafb   Doug Thompson   drivers/edac: fix...
846
  {
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
847
  	struct mcidev_sysfs_group_kobj *grp_kobj, *tmp;
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
848
849
  	debugf1("%s()
  ", __func__);
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
850
851
852
853
  	/*
  	 * loop if there are attributes and until we hit a NULL entry
  	 * Remove first all the atributes
  	 */
9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
854
855
  	while (sysfs_attrib) {
  		if (sysfs_attrib->grp) {
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
856
857
858
859
860
861
862
  			list_for_each_entry(grp_kobj, &mci->grp_kobj_list,
  					    list)
  				if (grp_kobj->grp == sysfs_attrib->grp)
  					edac_remove_mci_instance_attributes(mci,
  						    grp_kobj->grp->mcidev_attr,
  						    &grp_kobj->kobj, count + 1);
  		} else if (sysfs_attrib->attr.name) {
9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
863
864
865
866
867
868
  			debugf0("%s() file %s
  ", __func__,
  				sysfs_attrib->attr.name);
  			sysfs_remove_file(kobj, &sysfs_attrib->attr);
  		} else
  			break;
8096cfafb   Doug Thompson   drivers/edac: fix...
869
870
  		sysfs_attrib++;
  	}
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
871
872
873
874
875
876
877
878
879
880
881
  
  	/*
  	 * Now that all attributes got removed, it is save to remove all groups
  	 */
  	if (!count)
  		list_for_each_entry_safe(grp_kobj, tmp, &mci->grp_kobj_list,
  					 list) {
  			debugf0("%s() grp %s
  ", __func__, grp_kobj->grp->name);
  			kobject_put(&grp_kobj->kobj);
  		}
8096cfafb   Doug Thompson   drivers/edac: fix...
882
883
884
885
  }
  
  
  /*
7c9281d76   Douglas Thompson   drivers/edac: spl...
886
887
888
889
890
891
892
893
894
895
896
897
   * Create a new Memory Controller kobject instance,
   *	mc<id> under the 'mc' directory
   *
   * Return:
   *	0	Success
   *	!0	Failure
   */
  int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
  {
  	int i;
  	int err;
  	struct csrow_info *csrow;
8096cfafb   Doug Thompson   drivers/edac: fix...
898
  	struct kobject *kobj_mci = &mci->edac_mci_kobj;
7c9281d76   Douglas Thompson   drivers/edac: spl...
899
900
901
  
  	debugf0("%s() idx=%d
  ", __func__, mci->mc_idx);
7c9281d76   Douglas Thompson   drivers/edac: spl...
902

b968759ee   Mauro Carvalho Chehab   edac: Create an u...
903
  	INIT_LIST_HEAD(&mci->grp_kobj_list);
7c9281d76   Douglas Thompson   drivers/edac: spl...
904
  	/* create a symlink for the device */
8096cfafb   Doug Thompson   drivers/edac: fix...
905
  	err = sysfs_create_link(kobj_mci, &mci->dev->kobj,
7c9281d76   Douglas Thompson   drivers/edac: spl...
906
  				EDAC_DEVICE_SYMLINK);
8096cfafb   Doug Thompson   drivers/edac: fix...
907
908
909
  	if (err) {
  		debugf1("%s() failure to create symlink
  ", __func__);
7c9281d76   Douglas Thompson   drivers/edac: spl...
910
  		goto fail0;
8096cfafb   Doug Thompson   drivers/edac: fix...
911
  	}
7c9281d76   Douglas Thompson   drivers/edac: spl...
912

42a8e397a   Douglas Thompson   drivers/edac: add...
913
914
915
916
  	/* If the low level driver desires some attributes,
  	 * then create them now for the driver.
  	 */
  	if (mci->mc_driver_sysfs_attributes) {
cc301b3ae   Mauro Carvalho Chehab   edac: store/show ...
917
  		err = edac_create_mci_instance_attributes(mci,
9fa2fc2e2   Mauro Carvalho Chehab   edac_core: Allow ...
918
919
  					mci->mc_driver_sysfs_attributes,
  					&mci->edac_mci_kobj);
8096cfafb   Doug Thompson   drivers/edac: fix...
920
921
922
923
  		if (err) {
  			debugf1("%s() failure to create mci attributes
  ",
  				__func__);
42a8e397a   Douglas Thompson   drivers/edac: add...
924
  			goto fail0;
8096cfafb   Doug Thompson   drivers/edac: fix...
925
  		}
42a8e397a   Douglas Thompson   drivers/edac: add...
926
  	}
8096cfafb   Doug Thompson   drivers/edac: fix...
927
  	/* Make directories for each CSROW object under the mc<id> kobject
7c9281d76   Douglas Thompson   drivers/edac: spl...
928
929
930
931
932
933
  	 */
  	for (i = 0; i < mci->nr_csrows; i++) {
  		csrow = &mci->csrows[i];
  
  		/* Only expose populated CSROWs */
  		if (csrow->nr_pages > 0) {
8096cfafb   Doug Thompson   drivers/edac: fix...
934
935
936
937
938
  			err = edac_create_csrow_object(mci, csrow, i);
  			if (err) {
  				debugf1("%s() failure: create csrow %d obj
  ",
  					__func__, i);
7c9281d76   Douglas Thompson   drivers/edac: spl...
939
  				goto fail1;
8096cfafb   Doug Thompson   drivers/edac: fix...
940
  			}
7c9281d76   Douglas Thompson   drivers/edac: spl...
941
942
943
944
945
946
  		}
  	}
  
  	return 0;
  
  	/* CSROW error: backout what has already been registered,  */
052dfb45c   Douglas Thompson   drivers/edac: cle...
947
  fail1:
079708b91   Douglas Thompson   drivers/edac: cor...
948
  	for (i--; i >= 0; i--) {
7c9281d76   Douglas Thompson   drivers/edac: spl...
949
  		if (csrow->nr_pages > 0) {
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
950
  			kobject_put(&mci->csrows[i].kobj);
7c9281d76   Douglas Thompson   drivers/edac: spl...
951
952
  		}
  	}
8096cfafb   Doug Thompson   drivers/edac: fix...
953
  	/* remove the mci instance's attributes, if any */
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
954
955
  	edac_remove_mci_instance_attributes(mci,
  		mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0);
8096cfafb   Doug Thompson   drivers/edac: fix...
956
957
958
  
  	/* remove the symlink */
  	sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK);
052dfb45c   Douglas Thompson   drivers/edac: cle...
959
  fail0:
7c9281d76   Douglas Thompson   drivers/edac: spl...
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
  	return err;
  }
  
  /*
   * remove a Memory Controller instance
   */
  void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
  {
  	int i;
  
  	debugf0("%s()
  ", __func__);
  
  	/* remove all csrow kobjects */
  	for (i = 0; i < mci->nr_csrows; i++) {
  		if (mci->csrows[i].nr_pages > 0) {
8096cfafb   Doug Thompson   drivers/edac: fix...
976
977
  			debugf0("%s()  unreg csrow-%d
  ", __func__, i);
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
978
  			kobject_put(&mci->csrows[i].kobj);
7c9281d76   Douglas Thompson   drivers/edac: spl...
979
980
  		}
  	}
8096cfafb   Doug Thompson   drivers/edac: fix...
981
982
983
984
  	debugf0("%s()  remove_link
  ", __func__);
  
  	/* remove the symlink */
7c9281d76   Douglas Thompson   drivers/edac: spl...
985
  	sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK);
8096cfafb   Doug Thompson   drivers/edac: fix...
986
987
988
989
990
  
  	debugf0("%s()  remove_mci_instance
  ", __func__);
  
  	/* remove this mci instance's attribtes */
b968759ee   Mauro Carvalho Chehab   edac: Create an u...
991
992
993
  	edac_remove_mci_instance_attributes(mci,
  					    mci->mc_driver_sysfs_attributes,
  					    &mci->edac_mci_kobj, 0);
8096cfafb   Doug Thompson   drivers/edac: fix...
994
995
996
997
  	debugf0("%s()  unregister this mci kobj
  ", __func__);
  
  	/* unregister this instance's kobject */
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
998
  	kobject_put(&mci->edac_mci_kobj);
7c9281d76   Douglas Thompson   drivers/edac: spl...
999
  }
8096cfafb   Doug Thompson   drivers/edac: fix...
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
  
  
  
  
  /*
   * edac_setup_sysfs_mc_kset(void)
   *
   * Initialize the mc_kset for the 'mc' entry
   *	This requires creating the top 'mc' directory with a kset
   *	and its controls/attributes.
   *
   *	To this 'mc' kset, instance 'mci' will be grouped as children.
   *
   * Return:  0 SUCCESS
   *         !0 FAILURE error code
   */
  int edac_sysfs_setup_mc_kset(void)
  {
  	int err = 0;
  	struct sysdev_class *edac_class;
  
  	debugf1("%s()
  ", __func__);
  
  	/* get the /sys/devices/system/edac class reference */
  	edac_class = edac_get_edac_class();
  	if (edac_class == NULL) {
  		debugf1("%s() no edac_class error=%d
  ", __func__, err);
  		goto fail_out;
  	}
  
  	/* Init the MC's kobject */
f9fc82adc   Arthur Jones   edac: core fix st...
1033
1034
1035
  	mc_kset = kset_create_and_add("mc", NULL, &edac_class->kset.kobj);
  	if (!mc_kset) {
  		err = -ENOMEM;
8096cfafb   Doug Thompson   drivers/edac: fix...
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
  		debugf1("%s() Failed to register '.../edac/mc'
  ", __func__);
  		goto fail_out;
  	}
  
  	debugf1("%s() Registered '.../edac/mc' kobject
  ", __func__);
  
  	return 0;
  
  
  	/* error unwind stack */
  fail_out:
  	return err;
  }
  
  /*
   * edac_sysfs_teardown_mc_kset
   *
   *	deconstruct the mc_ket for memory controllers
   */
  void edac_sysfs_teardown_mc_kset(void)
  {
f9fc82adc   Arthur Jones   edac: core fix st...
1059
  	kset_unregister(mc_kset);
8096cfafb   Doug Thompson   drivers/edac: fix...
1060
  }