1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * edac_mc kernel module
3*4882a593Smuzhiyun * (C) 2005-2007 Linux Networx (http://lnxi.com)
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * This file may be distributed under the terms of the
6*4882a593Smuzhiyun * GNU General Public License.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Written Doug Thompson <norsk5@xmission.com> www.softwarebitmaker.com
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * (c) 2012-2013 - Mauro Carvalho Chehab
11*4882a593Smuzhiyun * The entire API were re-written, and ported to use struct device
12*4882a593Smuzhiyun *
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/ctype.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun #include <linux/edac.h>
18*4882a593Smuzhiyun #include <linux/bug.h>
19*4882a593Smuzhiyun #include <linux/pm_runtime.h>
20*4882a593Smuzhiyun #include <linux/uaccess.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include "edac_mc.h"
23*4882a593Smuzhiyun #include "edac_module.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /* MC EDAC Controls, setable by module parameter, and sysfs */
26*4882a593Smuzhiyun static int edac_mc_log_ue = 1;
27*4882a593Smuzhiyun static int edac_mc_log_ce = 1;
28*4882a593Smuzhiyun static int edac_mc_panic_on_ue;
29*4882a593Smuzhiyun static unsigned int edac_mc_poll_msec = 1000;
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun /* Getter functions for above */
edac_mc_get_log_ue(void)32*4882a593Smuzhiyun int edac_mc_get_log_ue(void)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun return edac_mc_log_ue;
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun
edac_mc_get_log_ce(void)37*4882a593Smuzhiyun int edac_mc_get_log_ce(void)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun return edac_mc_log_ce;
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun
edac_mc_get_panic_on_ue(void)42*4882a593Smuzhiyun int edac_mc_get_panic_on_ue(void)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun return edac_mc_panic_on_ue;
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /* this is temporary */
edac_mc_get_poll_msec(void)48*4882a593Smuzhiyun unsigned int edac_mc_get_poll_msec(void)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun return edac_mc_poll_msec;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun
edac_set_poll_msec(const char * val,const struct kernel_param * kp)53*4882a593Smuzhiyun static int edac_set_poll_msec(const char *val, const struct kernel_param *kp)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun unsigned int i;
56*4882a593Smuzhiyun int ret;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun if (!val)
59*4882a593Smuzhiyun return -EINVAL;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun ret = kstrtouint(val, 0, &i);
62*4882a593Smuzhiyun if (ret)
63*4882a593Smuzhiyun return ret;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun if (i < 1000)
66*4882a593Smuzhiyun return -EINVAL;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun *((unsigned int *)kp->arg) = i;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun /* notify edac_mc engine to reset the poll period */
71*4882a593Smuzhiyun edac_mc_reset_delay_period(i);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun return 0;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun /* Parameter declarations for above */
77*4882a593Smuzhiyun module_param(edac_mc_panic_on_ue, int, 0644);
78*4882a593Smuzhiyun MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on");
79*4882a593Smuzhiyun module_param(edac_mc_log_ue, int, 0644);
80*4882a593Smuzhiyun MODULE_PARM_DESC(edac_mc_log_ue,
81*4882a593Smuzhiyun "Log uncorrectable error to console: 0=off 1=on");
82*4882a593Smuzhiyun module_param(edac_mc_log_ce, int, 0644);
83*4882a593Smuzhiyun MODULE_PARM_DESC(edac_mc_log_ce,
84*4882a593Smuzhiyun "Log correctable error to console: 0=off 1=on");
85*4882a593Smuzhiyun module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_uint,
86*4882a593Smuzhiyun &edac_mc_poll_msec, 0644);
87*4882a593Smuzhiyun MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds");
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun static struct device *mci_pdev;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun /*
92*4882a593Smuzhiyun * various constants for Memory Controllers
93*4882a593Smuzhiyun */
94*4882a593Smuzhiyun static const char * const dev_types[] = {
95*4882a593Smuzhiyun [DEV_UNKNOWN] = "Unknown",
96*4882a593Smuzhiyun [DEV_X1] = "x1",
97*4882a593Smuzhiyun [DEV_X2] = "x2",
98*4882a593Smuzhiyun [DEV_X4] = "x4",
99*4882a593Smuzhiyun [DEV_X8] = "x8",
100*4882a593Smuzhiyun [DEV_X16] = "x16",
101*4882a593Smuzhiyun [DEV_X32] = "x32",
102*4882a593Smuzhiyun [DEV_X64] = "x64"
103*4882a593Smuzhiyun };
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun static const char * const edac_caps[] = {
106*4882a593Smuzhiyun [EDAC_UNKNOWN] = "Unknown",
107*4882a593Smuzhiyun [EDAC_NONE] = "None",
108*4882a593Smuzhiyun [EDAC_RESERVED] = "Reserved",
109*4882a593Smuzhiyun [EDAC_PARITY] = "PARITY",
110*4882a593Smuzhiyun [EDAC_EC] = "EC",
111*4882a593Smuzhiyun [EDAC_SECDED] = "SECDED",
112*4882a593Smuzhiyun [EDAC_S2ECD2ED] = "S2ECD2ED",
113*4882a593Smuzhiyun [EDAC_S4ECD4ED] = "S4ECD4ED",
114*4882a593Smuzhiyun [EDAC_S8ECD8ED] = "S8ECD8ED",
115*4882a593Smuzhiyun [EDAC_S16ECD16ED] = "S16ECD16ED"
116*4882a593Smuzhiyun };
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun #ifdef CONFIG_EDAC_LEGACY_SYSFS
119*4882a593Smuzhiyun /*
120*4882a593Smuzhiyun * EDAC sysfs CSROW data structures and methods
121*4882a593Smuzhiyun */
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun #define to_csrow(k) container_of(k, struct csrow_info, dev)
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun /*
126*4882a593Smuzhiyun * We need it to avoid namespace conflicts between the legacy API
127*4882a593Smuzhiyun * and the per-dimm/per-rank one
128*4882a593Smuzhiyun */
129*4882a593Smuzhiyun #define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \
130*4882a593Smuzhiyun static struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store)
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun struct dev_ch_attribute {
133*4882a593Smuzhiyun struct device_attribute attr;
134*4882a593Smuzhiyun unsigned int channel;
135*4882a593Smuzhiyun };
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \
138*4882a593Smuzhiyun static struct dev_ch_attribute dev_attr_legacy_##_name = \
139*4882a593Smuzhiyun { __ATTR(_name, _mode, _show, _store), (_var) }
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun #define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel)
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun /* Set of more default csrow<id> attribute show/store functions */
csrow_ue_count_show(struct device * dev,struct device_attribute * mattr,char * data)144*4882a593Smuzhiyun static ssize_t csrow_ue_count_show(struct device *dev,
145*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun return sprintf(data, "%u\n", csrow->ue_count);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
csrow_ce_count_show(struct device * dev,struct device_attribute * mattr,char * data)152*4882a593Smuzhiyun static ssize_t csrow_ce_count_show(struct device *dev,
153*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun return sprintf(data, "%u\n", csrow->ce_count);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
csrow_size_show(struct device * dev,struct device_attribute * mattr,char * data)160*4882a593Smuzhiyun static ssize_t csrow_size_show(struct device *dev,
161*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
164*4882a593Smuzhiyun int i;
165*4882a593Smuzhiyun u32 nr_pages = 0;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun for (i = 0; i < csrow->nr_channels; i++)
168*4882a593Smuzhiyun nr_pages += csrow->channels[i]->dimm->nr_pages;
169*4882a593Smuzhiyun return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages));
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
csrow_mem_type_show(struct device * dev,struct device_attribute * mattr,char * data)172*4882a593Smuzhiyun static ssize_t csrow_mem_type_show(struct device *dev,
173*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun return sprintf(data, "%s\n", edac_mem_types[csrow->channels[0]->dimm->mtype]);
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun
csrow_dev_type_show(struct device * dev,struct device_attribute * mattr,char * data)180*4882a593Smuzhiyun static ssize_t csrow_dev_type_show(struct device *dev,
181*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
csrow_edac_mode_show(struct device * dev,struct device_attribute * mattr,char * data)188*4882a593Smuzhiyun static ssize_t csrow_edac_mode_show(struct device *dev,
189*4882a593Smuzhiyun struct device_attribute *mattr,
190*4882a593Smuzhiyun char *data)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]);
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun /* show/store functions for DIMM Label attributes */
channel_dimm_label_show(struct device * dev,struct device_attribute * mattr,char * data)198*4882a593Smuzhiyun static ssize_t channel_dimm_label_show(struct device *dev,
199*4882a593Smuzhiyun struct device_attribute *mattr,
200*4882a593Smuzhiyun char *data)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
203*4882a593Smuzhiyun unsigned int chan = to_channel(mattr);
204*4882a593Smuzhiyun struct rank_info *rank = csrow->channels[chan];
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* if field has not been initialized, there is nothing to send */
207*4882a593Smuzhiyun if (!rank->dimm->label[0])
208*4882a593Smuzhiyun return 0;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun return snprintf(data, sizeof(rank->dimm->label) + 1, "%s\n",
211*4882a593Smuzhiyun rank->dimm->label);
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun
channel_dimm_label_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)214*4882a593Smuzhiyun static ssize_t channel_dimm_label_store(struct device *dev,
215*4882a593Smuzhiyun struct device_attribute *mattr,
216*4882a593Smuzhiyun const char *data, size_t count)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
219*4882a593Smuzhiyun unsigned int chan = to_channel(mattr);
220*4882a593Smuzhiyun struct rank_info *rank = csrow->channels[chan];
221*4882a593Smuzhiyun size_t copy_count = count;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun if (count == 0)
224*4882a593Smuzhiyun return -EINVAL;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun if (data[count - 1] == '\0' || data[count - 1] == '\n')
227*4882a593Smuzhiyun copy_count -= 1;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun if (copy_count == 0 || copy_count >= sizeof(rank->dimm->label))
230*4882a593Smuzhiyun return -EINVAL;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun strncpy(rank->dimm->label, data, copy_count);
233*4882a593Smuzhiyun rank->dimm->label[copy_count] = '\0';
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun return count;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun /* show function for dynamic chX_ce_count attribute */
channel_ce_count_show(struct device * dev,struct device_attribute * mattr,char * data)239*4882a593Smuzhiyun static ssize_t channel_ce_count_show(struct device *dev,
240*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun struct csrow_info *csrow = to_csrow(dev);
243*4882a593Smuzhiyun unsigned int chan = to_channel(mattr);
244*4882a593Smuzhiyun struct rank_info *rank = csrow->channels[chan];
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun return sprintf(data, "%u\n", rank->ce_count);
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun /* cwrow<id>/attribute files */
250*4882a593Smuzhiyun DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL);
251*4882a593Smuzhiyun DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL);
252*4882a593Smuzhiyun DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL);
253*4882a593Smuzhiyun DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL);
254*4882a593Smuzhiyun DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL);
255*4882a593Smuzhiyun DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun /* default attributes of the CSROW<id> object */
258*4882a593Smuzhiyun static struct attribute *csrow_attrs[] = {
259*4882a593Smuzhiyun &dev_attr_legacy_dev_type.attr,
260*4882a593Smuzhiyun &dev_attr_legacy_mem_type.attr,
261*4882a593Smuzhiyun &dev_attr_legacy_edac_mode.attr,
262*4882a593Smuzhiyun &dev_attr_legacy_size_mb.attr,
263*4882a593Smuzhiyun &dev_attr_legacy_ue_count.attr,
264*4882a593Smuzhiyun &dev_attr_legacy_ce_count.attr,
265*4882a593Smuzhiyun NULL,
266*4882a593Smuzhiyun };
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun static const struct attribute_group csrow_attr_grp = {
269*4882a593Smuzhiyun .attrs = csrow_attrs,
270*4882a593Smuzhiyun };
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun static const struct attribute_group *csrow_attr_groups[] = {
273*4882a593Smuzhiyun &csrow_attr_grp,
274*4882a593Smuzhiyun NULL
275*4882a593Smuzhiyun };
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun static const struct device_type csrow_attr_type = {
278*4882a593Smuzhiyun .groups = csrow_attr_groups,
279*4882a593Smuzhiyun };
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun /*
282*4882a593Smuzhiyun * possible dynamic channel DIMM Label attribute files
283*4882a593Smuzhiyun *
284*4882a593Smuzhiyun */
285*4882a593Smuzhiyun DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
286*4882a593Smuzhiyun channel_dimm_label_show, channel_dimm_label_store, 0);
287*4882a593Smuzhiyun DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
288*4882a593Smuzhiyun channel_dimm_label_show, channel_dimm_label_store, 1);
289*4882a593Smuzhiyun DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR,
290*4882a593Smuzhiyun channel_dimm_label_show, channel_dimm_label_store, 2);
291*4882a593Smuzhiyun DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR,
292*4882a593Smuzhiyun channel_dimm_label_show, channel_dimm_label_store, 3);
293*4882a593Smuzhiyun DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR,
294*4882a593Smuzhiyun channel_dimm_label_show, channel_dimm_label_store, 4);
295*4882a593Smuzhiyun DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR,
296*4882a593Smuzhiyun channel_dimm_label_show, channel_dimm_label_store, 5);
297*4882a593Smuzhiyun DEVICE_CHANNEL(ch6_dimm_label, S_IRUGO | S_IWUSR,
298*4882a593Smuzhiyun channel_dimm_label_show, channel_dimm_label_store, 6);
299*4882a593Smuzhiyun DEVICE_CHANNEL(ch7_dimm_label, S_IRUGO | S_IWUSR,
300*4882a593Smuzhiyun channel_dimm_label_show, channel_dimm_label_store, 7);
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun /* Total possible dynamic DIMM Label attribute file table */
303*4882a593Smuzhiyun static struct attribute *dynamic_csrow_dimm_attr[] = {
304*4882a593Smuzhiyun &dev_attr_legacy_ch0_dimm_label.attr.attr,
305*4882a593Smuzhiyun &dev_attr_legacy_ch1_dimm_label.attr.attr,
306*4882a593Smuzhiyun &dev_attr_legacy_ch2_dimm_label.attr.attr,
307*4882a593Smuzhiyun &dev_attr_legacy_ch3_dimm_label.attr.attr,
308*4882a593Smuzhiyun &dev_attr_legacy_ch4_dimm_label.attr.attr,
309*4882a593Smuzhiyun &dev_attr_legacy_ch5_dimm_label.attr.attr,
310*4882a593Smuzhiyun &dev_attr_legacy_ch6_dimm_label.attr.attr,
311*4882a593Smuzhiyun &dev_attr_legacy_ch7_dimm_label.attr.attr,
312*4882a593Smuzhiyun NULL
313*4882a593Smuzhiyun };
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun /* possible dynamic channel ce_count attribute files */
316*4882a593Smuzhiyun DEVICE_CHANNEL(ch0_ce_count, S_IRUGO,
317*4882a593Smuzhiyun channel_ce_count_show, NULL, 0);
318*4882a593Smuzhiyun DEVICE_CHANNEL(ch1_ce_count, S_IRUGO,
319*4882a593Smuzhiyun channel_ce_count_show, NULL, 1);
320*4882a593Smuzhiyun DEVICE_CHANNEL(ch2_ce_count, S_IRUGO,
321*4882a593Smuzhiyun channel_ce_count_show, NULL, 2);
322*4882a593Smuzhiyun DEVICE_CHANNEL(ch3_ce_count, S_IRUGO,
323*4882a593Smuzhiyun channel_ce_count_show, NULL, 3);
324*4882a593Smuzhiyun DEVICE_CHANNEL(ch4_ce_count, S_IRUGO,
325*4882a593Smuzhiyun channel_ce_count_show, NULL, 4);
326*4882a593Smuzhiyun DEVICE_CHANNEL(ch5_ce_count, S_IRUGO,
327*4882a593Smuzhiyun channel_ce_count_show, NULL, 5);
328*4882a593Smuzhiyun DEVICE_CHANNEL(ch6_ce_count, S_IRUGO,
329*4882a593Smuzhiyun channel_ce_count_show, NULL, 6);
330*4882a593Smuzhiyun DEVICE_CHANNEL(ch7_ce_count, S_IRUGO,
331*4882a593Smuzhiyun channel_ce_count_show, NULL, 7);
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun /* Total possible dynamic ce_count attribute file table */
334*4882a593Smuzhiyun static struct attribute *dynamic_csrow_ce_count_attr[] = {
335*4882a593Smuzhiyun &dev_attr_legacy_ch0_ce_count.attr.attr,
336*4882a593Smuzhiyun &dev_attr_legacy_ch1_ce_count.attr.attr,
337*4882a593Smuzhiyun &dev_attr_legacy_ch2_ce_count.attr.attr,
338*4882a593Smuzhiyun &dev_attr_legacy_ch3_ce_count.attr.attr,
339*4882a593Smuzhiyun &dev_attr_legacy_ch4_ce_count.attr.attr,
340*4882a593Smuzhiyun &dev_attr_legacy_ch5_ce_count.attr.attr,
341*4882a593Smuzhiyun &dev_attr_legacy_ch6_ce_count.attr.attr,
342*4882a593Smuzhiyun &dev_attr_legacy_ch7_ce_count.attr.attr,
343*4882a593Smuzhiyun NULL
344*4882a593Smuzhiyun };
345*4882a593Smuzhiyun
csrow_dev_is_visible(struct kobject * kobj,struct attribute * attr,int idx)346*4882a593Smuzhiyun static umode_t csrow_dev_is_visible(struct kobject *kobj,
347*4882a593Smuzhiyun struct attribute *attr, int idx)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun struct device *dev = kobj_to_dev(kobj);
350*4882a593Smuzhiyun struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun if (idx >= csrow->nr_channels)
353*4882a593Smuzhiyun return 0;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun if (idx >= ARRAY_SIZE(dynamic_csrow_ce_count_attr) - 1) {
356*4882a593Smuzhiyun WARN_ONCE(1, "idx: %d\n", idx);
357*4882a593Smuzhiyun return 0;
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun /* Only expose populated DIMMs */
361*4882a593Smuzhiyun if (!csrow->channels[idx]->dimm->nr_pages)
362*4882a593Smuzhiyun return 0;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun return attr->mode;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun static const struct attribute_group csrow_dev_dimm_group = {
369*4882a593Smuzhiyun .attrs = dynamic_csrow_dimm_attr,
370*4882a593Smuzhiyun .is_visible = csrow_dev_is_visible,
371*4882a593Smuzhiyun };
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun static const struct attribute_group csrow_dev_ce_count_group = {
374*4882a593Smuzhiyun .attrs = dynamic_csrow_ce_count_attr,
375*4882a593Smuzhiyun .is_visible = csrow_dev_is_visible,
376*4882a593Smuzhiyun };
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun static const struct attribute_group *csrow_dev_groups[] = {
379*4882a593Smuzhiyun &csrow_dev_dimm_group,
380*4882a593Smuzhiyun &csrow_dev_ce_count_group,
381*4882a593Smuzhiyun NULL
382*4882a593Smuzhiyun };
383*4882a593Smuzhiyun
csrow_release(struct device * dev)384*4882a593Smuzhiyun static void csrow_release(struct device *dev)
385*4882a593Smuzhiyun {
386*4882a593Smuzhiyun /*
387*4882a593Smuzhiyun * Nothing to do, just unregister sysfs here. The mci
388*4882a593Smuzhiyun * device owns the data and will also release it.
389*4882a593Smuzhiyun */
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun
nr_pages_per_csrow(struct csrow_info * csrow)392*4882a593Smuzhiyun static inline int nr_pages_per_csrow(struct csrow_info *csrow)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun int chan, nr_pages = 0;
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun for (chan = 0; chan < csrow->nr_channels; chan++)
397*4882a593Smuzhiyun nr_pages += csrow->channels[chan]->dimm->nr_pages;
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun return nr_pages;
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun /* Create a CSROW object under specifed edac_mc_device */
edac_create_csrow_object(struct mem_ctl_info * mci,struct csrow_info * csrow,int index)403*4882a593Smuzhiyun static int edac_create_csrow_object(struct mem_ctl_info *mci,
404*4882a593Smuzhiyun struct csrow_info *csrow, int index)
405*4882a593Smuzhiyun {
406*4882a593Smuzhiyun int err;
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun csrow->dev.type = &csrow_attr_type;
409*4882a593Smuzhiyun csrow->dev.groups = csrow_dev_groups;
410*4882a593Smuzhiyun csrow->dev.release = csrow_release;
411*4882a593Smuzhiyun device_initialize(&csrow->dev);
412*4882a593Smuzhiyun csrow->dev.parent = &mci->dev;
413*4882a593Smuzhiyun csrow->mci = mci;
414*4882a593Smuzhiyun dev_set_name(&csrow->dev, "csrow%d", index);
415*4882a593Smuzhiyun dev_set_drvdata(&csrow->dev, csrow);
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun err = device_add(&csrow->dev);
418*4882a593Smuzhiyun if (err) {
419*4882a593Smuzhiyun edac_dbg(1, "failure: create device %s\n", dev_name(&csrow->dev));
420*4882a593Smuzhiyun put_device(&csrow->dev);
421*4882a593Smuzhiyun return err;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun edac_dbg(0, "device %s created\n", dev_name(&csrow->dev));
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun return 0;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun /* Create a CSROW object under specifed edac_mc_device */
edac_create_csrow_objects(struct mem_ctl_info * mci)430*4882a593Smuzhiyun static int edac_create_csrow_objects(struct mem_ctl_info *mci)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun int err, i;
433*4882a593Smuzhiyun struct csrow_info *csrow;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun for (i = 0; i < mci->nr_csrows; i++) {
436*4882a593Smuzhiyun csrow = mci->csrows[i];
437*4882a593Smuzhiyun if (!nr_pages_per_csrow(csrow))
438*4882a593Smuzhiyun continue;
439*4882a593Smuzhiyun err = edac_create_csrow_object(mci, mci->csrows[i], i);
440*4882a593Smuzhiyun if (err < 0)
441*4882a593Smuzhiyun goto error;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun return 0;
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun error:
446*4882a593Smuzhiyun for (--i; i >= 0; i--) {
447*4882a593Smuzhiyun if (device_is_registered(&mci->csrows[i]->dev))
448*4882a593Smuzhiyun device_unregister(&mci->csrows[i]->dev);
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun return err;
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun
edac_delete_csrow_objects(struct mem_ctl_info * mci)454*4882a593Smuzhiyun static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun int i;
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun for (i = 0; i < mci->nr_csrows; i++) {
459*4882a593Smuzhiyun if (device_is_registered(&mci->csrows[i]->dev))
460*4882a593Smuzhiyun device_unregister(&mci->csrows[i]->dev);
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun #endif
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun /*
467*4882a593Smuzhiyun * Per-dimm (or per-rank) devices
468*4882a593Smuzhiyun */
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun #define to_dimm(k) container_of(k, struct dimm_info, dev)
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun /* show/store functions for DIMM Label attributes */
dimmdev_location_show(struct device * dev,struct device_attribute * mattr,char * data)473*4882a593Smuzhiyun static ssize_t dimmdev_location_show(struct device *dev,
474*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
475*4882a593Smuzhiyun {
476*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
477*4882a593Smuzhiyun ssize_t count;
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun count = edac_dimm_info_location(dimm, data, PAGE_SIZE);
480*4882a593Smuzhiyun count += scnprintf(data + count, PAGE_SIZE - count, "\n");
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun return count;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun
dimmdev_label_show(struct device * dev,struct device_attribute * mattr,char * data)485*4882a593Smuzhiyun static ssize_t dimmdev_label_show(struct device *dev,
486*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
487*4882a593Smuzhiyun {
488*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun /* if field has not been initialized, there is nothing to send */
491*4882a593Smuzhiyun if (!dimm->label[0])
492*4882a593Smuzhiyun return 0;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label);
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun
dimmdev_label_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)497*4882a593Smuzhiyun static ssize_t dimmdev_label_store(struct device *dev,
498*4882a593Smuzhiyun struct device_attribute *mattr,
499*4882a593Smuzhiyun const char *data,
500*4882a593Smuzhiyun size_t count)
501*4882a593Smuzhiyun {
502*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
503*4882a593Smuzhiyun size_t copy_count = count;
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun if (count == 0)
506*4882a593Smuzhiyun return -EINVAL;
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun if (data[count - 1] == '\0' || data[count - 1] == '\n')
509*4882a593Smuzhiyun copy_count -= 1;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun if (copy_count == 0 || copy_count >= sizeof(dimm->label))
512*4882a593Smuzhiyun return -EINVAL;
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun strncpy(dimm->label, data, copy_count);
515*4882a593Smuzhiyun dimm->label[copy_count] = '\0';
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun return count;
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun
dimmdev_size_show(struct device * dev,struct device_attribute * mattr,char * data)520*4882a593Smuzhiyun static ssize_t dimmdev_size_show(struct device *dev,
521*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
522*4882a593Smuzhiyun {
523*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun
dimmdev_mem_type_show(struct device * dev,struct device_attribute * mattr,char * data)528*4882a593Smuzhiyun static ssize_t dimmdev_mem_type_show(struct device *dev,
529*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
530*4882a593Smuzhiyun {
531*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun return sprintf(data, "%s\n", edac_mem_types[dimm->mtype]);
534*4882a593Smuzhiyun }
535*4882a593Smuzhiyun
dimmdev_dev_type_show(struct device * dev,struct device_attribute * mattr,char * data)536*4882a593Smuzhiyun static ssize_t dimmdev_dev_type_show(struct device *dev,
537*4882a593Smuzhiyun struct device_attribute *mattr, char *data)
538*4882a593Smuzhiyun {
539*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun return sprintf(data, "%s\n", dev_types[dimm->dtype]);
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun
dimmdev_edac_mode_show(struct device * dev,struct device_attribute * mattr,char * data)544*4882a593Smuzhiyun static ssize_t dimmdev_edac_mode_show(struct device *dev,
545*4882a593Smuzhiyun struct device_attribute *mattr,
546*4882a593Smuzhiyun char *data)
547*4882a593Smuzhiyun {
548*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun
dimmdev_ce_count_show(struct device * dev,struct device_attribute * mattr,char * data)553*4882a593Smuzhiyun static ssize_t dimmdev_ce_count_show(struct device *dev,
554*4882a593Smuzhiyun struct device_attribute *mattr,
555*4882a593Smuzhiyun char *data)
556*4882a593Smuzhiyun {
557*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun return sprintf(data, "%u\n", dimm->ce_count);
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun
dimmdev_ue_count_show(struct device * dev,struct device_attribute * mattr,char * data)562*4882a593Smuzhiyun static ssize_t dimmdev_ue_count_show(struct device *dev,
563*4882a593Smuzhiyun struct device_attribute *mattr,
564*4882a593Smuzhiyun char *data)
565*4882a593Smuzhiyun {
566*4882a593Smuzhiyun struct dimm_info *dimm = to_dimm(dev);
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun return sprintf(data, "%u\n", dimm->ue_count);
569*4882a593Smuzhiyun }
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun /* dimm/rank attribute files */
572*4882a593Smuzhiyun static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
573*4882a593Smuzhiyun dimmdev_label_show, dimmdev_label_store);
574*4882a593Smuzhiyun static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
575*4882a593Smuzhiyun static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
576*4882a593Smuzhiyun static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
577*4882a593Smuzhiyun static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
578*4882a593Smuzhiyun static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
579*4882a593Smuzhiyun static DEVICE_ATTR(dimm_ce_count, S_IRUGO, dimmdev_ce_count_show, NULL);
580*4882a593Smuzhiyun static DEVICE_ATTR(dimm_ue_count, S_IRUGO, dimmdev_ue_count_show, NULL);
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun /* attributes of the dimm<id>/rank<id> object */
583*4882a593Smuzhiyun static struct attribute *dimm_attrs[] = {
584*4882a593Smuzhiyun &dev_attr_dimm_label.attr,
585*4882a593Smuzhiyun &dev_attr_dimm_location.attr,
586*4882a593Smuzhiyun &dev_attr_size.attr,
587*4882a593Smuzhiyun &dev_attr_dimm_mem_type.attr,
588*4882a593Smuzhiyun &dev_attr_dimm_dev_type.attr,
589*4882a593Smuzhiyun &dev_attr_dimm_edac_mode.attr,
590*4882a593Smuzhiyun &dev_attr_dimm_ce_count.attr,
591*4882a593Smuzhiyun &dev_attr_dimm_ue_count.attr,
592*4882a593Smuzhiyun NULL,
593*4882a593Smuzhiyun };
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun static const struct attribute_group dimm_attr_grp = {
596*4882a593Smuzhiyun .attrs = dimm_attrs,
597*4882a593Smuzhiyun };
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun static const struct attribute_group *dimm_attr_groups[] = {
600*4882a593Smuzhiyun &dimm_attr_grp,
601*4882a593Smuzhiyun NULL
602*4882a593Smuzhiyun };
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun static const struct device_type dimm_attr_type = {
605*4882a593Smuzhiyun .groups = dimm_attr_groups,
606*4882a593Smuzhiyun };
607*4882a593Smuzhiyun
dimm_release(struct device * dev)608*4882a593Smuzhiyun static void dimm_release(struct device *dev)
609*4882a593Smuzhiyun {
610*4882a593Smuzhiyun /*
611*4882a593Smuzhiyun * Nothing to do, just unregister sysfs here. The mci
612*4882a593Smuzhiyun * device owns the data and will also release it.
613*4882a593Smuzhiyun */
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun /* Create a DIMM object under specifed memory controller device */
edac_create_dimm_object(struct mem_ctl_info * mci,struct dimm_info * dimm)617*4882a593Smuzhiyun static int edac_create_dimm_object(struct mem_ctl_info *mci,
618*4882a593Smuzhiyun struct dimm_info *dimm)
619*4882a593Smuzhiyun {
620*4882a593Smuzhiyun int err;
621*4882a593Smuzhiyun dimm->mci = mci;
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun dimm->dev.type = &dimm_attr_type;
624*4882a593Smuzhiyun dimm->dev.release = dimm_release;
625*4882a593Smuzhiyun device_initialize(&dimm->dev);
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun dimm->dev.parent = &mci->dev;
628*4882a593Smuzhiyun if (mci->csbased)
629*4882a593Smuzhiyun dev_set_name(&dimm->dev, "rank%d", dimm->idx);
630*4882a593Smuzhiyun else
631*4882a593Smuzhiyun dev_set_name(&dimm->dev, "dimm%d", dimm->idx);
632*4882a593Smuzhiyun dev_set_drvdata(&dimm->dev, dimm);
633*4882a593Smuzhiyun pm_runtime_forbid(&mci->dev);
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun err = device_add(&dimm->dev);
636*4882a593Smuzhiyun if (err) {
637*4882a593Smuzhiyun edac_dbg(1, "failure: create device %s\n", dev_name(&dimm->dev));
638*4882a593Smuzhiyun put_device(&dimm->dev);
639*4882a593Smuzhiyun return err;
640*4882a593Smuzhiyun }
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
643*4882a593Smuzhiyun char location[80];
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun edac_dimm_info_location(dimm, location, sizeof(location));
646*4882a593Smuzhiyun edac_dbg(0, "device %s created at location %s\n",
647*4882a593Smuzhiyun dev_name(&dimm->dev), location);
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun return 0;
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun /*
654*4882a593Smuzhiyun * Memory controller device
655*4882a593Smuzhiyun */
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
658*4882a593Smuzhiyun
mci_reset_counters_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)659*4882a593Smuzhiyun static ssize_t mci_reset_counters_store(struct device *dev,
660*4882a593Smuzhiyun struct device_attribute *mattr,
661*4882a593Smuzhiyun const char *data, size_t count)
662*4882a593Smuzhiyun {
663*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
664*4882a593Smuzhiyun struct dimm_info *dimm;
665*4882a593Smuzhiyun int row, chan;
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun mci->ue_mc = 0;
668*4882a593Smuzhiyun mci->ce_mc = 0;
669*4882a593Smuzhiyun mci->ue_noinfo_count = 0;
670*4882a593Smuzhiyun mci->ce_noinfo_count = 0;
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun for (row = 0; row < mci->nr_csrows; row++) {
673*4882a593Smuzhiyun struct csrow_info *ri = mci->csrows[row];
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun ri->ue_count = 0;
676*4882a593Smuzhiyun ri->ce_count = 0;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun for (chan = 0; chan < ri->nr_channels; chan++)
679*4882a593Smuzhiyun ri->channels[chan]->ce_count = 0;
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun
682*4882a593Smuzhiyun mci_for_each_dimm(mci, dimm) {
683*4882a593Smuzhiyun dimm->ue_count = 0;
684*4882a593Smuzhiyun dimm->ce_count = 0;
685*4882a593Smuzhiyun }
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun mci->start_time = jiffies;
688*4882a593Smuzhiyun return count;
689*4882a593Smuzhiyun }
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun /* Memory scrubbing interface:
692*4882a593Smuzhiyun *
693*4882a593Smuzhiyun * A MC driver can limit the scrubbing bandwidth based on the CPU type.
694*4882a593Smuzhiyun * Therefore, ->set_sdram_scrub_rate should be made to return the actual
695*4882a593Smuzhiyun * bandwidth that is accepted or 0 when scrubbing is to be disabled.
696*4882a593Smuzhiyun *
697*4882a593Smuzhiyun * Negative value still means that an error has occurred while setting
698*4882a593Smuzhiyun * the scrub rate.
699*4882a593Smuzhiyun */
mci_sdram_scrub_rate_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)700*4882a593Smuzhiyun static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
701*4882a593Smuzhiyun struct device_attribute *mattr,
702*4882a593Smuzhiyun const char *data, size_t count)
703*4882a593Smuzhiyun {
704*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
705*4882a593Smuzhiyun unsigned long bandwidth = 0;
706*4882a593Smuzhiyun int new_bw = 0;
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun if (kstrtoul(data, 10, &bandwidth) < 0)
709*4882a593Smuzhiyun return -EINVAL;
710*4882a593Smuzhiyun
711*4882a593Smuzhiyun new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
712*4882a593Smuzhiyun if (new_bw < 0) {
713*4882a593Smuzhiyun edac_printk(KERN_WARNING, EDAC_MC,
714*4882a593Smuzhiyun "Error setting scrub rate to: %lu\n", bandwidth);
715*4882a593Smuzhiyun return -EINVAL;
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun
718*4882a593Smuzhiyun return count;
719*4882a593Smuzhiyun }
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun /*
722*4882a593Smuzhiyun * ->get_sdram_scrub_rate() return value semantics same as above.
723*4882a593Smuzhiyun */
mci_sdram_scrub_rate_show(struct device * dev,struct device_attribute * mattr,char * data)724*4882a593Smuzhiyun static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
725*4882a593Smuzhiyun struct device_attribute *mattr,
726*4882a593Smuzhiyun char *data)
727*4882a593Smuzhiyun {
728*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
729*4882a593Smuzhiyun int bandwidth = 0;
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun bandwidth = mci->get_sdram_scrub_rate(mci);
732*4882a593Smuzhiyun if (bandwidth < 0) {
733*4882a593Smuzhiyun edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n");
734*4882a593Smuzhiyun return bandwidth;
735*4882a593Smuzhiyun }
736*4882a593Smuzhiyun
737*4882a593Smuzhiyun return sprintf(data, "%d\n", bandwidth);
738*4882a593Smuzhiyun }
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun /* default attribute files for the MCI object */
mci_ue_count_show(struct device * dev,struct device_attribute * mattr,char * data)741*4882a593Smuzhiyun static ssize_t mci_ue_count_show(struct device *dev,
742*4882a593Smuzhiyun struct device_attribute *mattr,
743*4882a593Smuzhiyun char *data)
744*4882a593Smuzhiyun {
745*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun return sprintf(data, "%d\n", mci->ue_mc);
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun
mci_ce_count_show(struct device * dev,struct device_attribute * mattr,char * data)750*4882a593Smuzhiyun static ssize_t mci_ce_count_show(struct device *dev,
751*4882a593Smuzhiyun struct device_attribute *mattr,
752*4882a593Smuzhiyun char *data)
753*4882a593Smuzhiyun {
754*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun return sprintf(data, "%d\n", mci->ce_mc);
757*4882a593Smuzhiyun }
758*4882a593Smuzhiyun
mci_ce_noinfo_show(struct device * dev,struct device_attribute * mattr,char * data)759*4882a593Smuzhiyun static ssize_t mci_ce_noinfo_show(struct device *dev,
760*4882a593Smuzhiyun struct device_attribute *mattr,
761*4882a593Smuzhiyun char *data)
762*4882a593Smuzhiyun {
763*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun return sprintf(data, "%d\n", mci->ce_noinfo_count);
766*4882a593Smuzhiyun }
767*4882a593Smuzhiyun
mci_ue_noinfo_show(struct device * dev,struct device_attribute * mattr,char * data)768*4882a593Smuzhiyun static ssize_t mci_ue_noinfo_show(struct device *dev,
769*4882a593Smuzhiyun struct device_attribute *mattr,
770*4882a593Smuzhiyun char *data)
771*4882a593Smuzhiyun {
772*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
773*4882a593Smuzhiyun
774*4882a593Smuzhiyun return sprintf(data, "%d\n", mci->ue_noinfo_count);
775*4882a593Smuzhiyun }
776*4882a593Smuzhiyun
mci_seconds_show(struct device * dev,struct device_attribute * mattr,char * data)777*4882a593Smuzhiyun static ssize_t mci_seconds_show(struct device *dev,
778*4882a593Smuzhiyun struct device_attribute *mattr,
779*4882a593Smuzhiyun char *data)
780*4882a593Smuzhiyun {
781*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
784*4882a593Smuzhiyun }
785*4882a593Smuzhiyun
mci_ctl_name_show(struct device * dev,struct device_attribute * mattr,char * data)786*4882a593Smuzhiyun static ssize_t mci_ctl_name_show(struct device *dev,
787*4882a593Smuzhiyun struct device_attribute *mattr,
788*4882a593Smuzhiyun char *data)
789*4882a593Smuzhiyun {
790*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun return sprintf(data, "%s\n", mci->ctl_name);
793*4882a593Smuzhiyun }
794*4882a593Smuzhiyun
mci_size_mb_show(struct device * dev,struct device_attribute * mattr,char * data)795*4882a593Smuzhiyun static ssize_t mci_size_mb_show(struct device *dev,
796*4882a593Smuzhiyun struct device_attribute *mattr,
797*4882a593Smuzhiyun char *data)
798*4882a593Smuzhiyun {
799*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
800*4882a593Smuzhiyun int total_pages = 0, csrow_idx, j;
801*4882a593Smuzhiyun
802*4882a593Smuzhiyun for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
803*4882a593Smuzhiyun struct csrow_info *csrow = mci->csrows[csrow_idx];
804*4882a593Smuzhiyun
805*4882a593Smuzhiyun for (j = 0; j < csrow->nr_channels; j++) {
806*4882a593Smuzhiyun struct dimm_info *dimm = csrow->channels[j]->dimm;
807*4882a593Smuzhiyun
808*4882a593Smuzhiyun total_pages += dimm->nr_pages;
809*4882a593Smuzhiyun }
810*4882a593Smuzhiyun }
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
813*4882a593Smuzhiyun }
814*4882a593Smuzhiyun
mci_max_location_show(struct device * dev,struct device_attribute * mattr,char * data)815*4882a593Smuzhiyun static ssize_t mci_max_location_show(struct device *dev,
816*4882a593Smuzhiyun struct device_attribute *mattr,
817*4882a593Smuzhiyun char *data)
818*4882a593Smuzhiyun {
819*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
820*4882a593Smuzhiyun int len = PAGE_SIZE;
821*4882a593Smuzhiyun char *p = data;
822*4882a593Smuzhiyun int i, n;
823*4882a593Smuzhiyun
824*4882a593Smuzhiyun for (i = 0; i < mci->n_layers; i++) {
825*4882a593Smuzhiyun n = scnprintf(p, len, "%s %d ",
826*4882a593Smuzhiyun edac_layer_name[mci->layers[i].type],
827*4882a593Smuzhiyun mci->layers[i].size - 1);
828*4882a593Smuzhiyun len -= n;
829*4882a593Smuzhiyun if (len <= 0)
830*4882a593Smuzhiyun goto out;
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun p += n;
833*4882a593Smuzhiyun }
834*4882a593Smuzhiyun
835*4882a593Smuzhiyun p += scnprintf(p, len, "\n");
836*4882a593Smuzhiyun out:
837*4882a593Smuzhiyun return p - data;
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun
840*4882a593Smuzhiyun /* default Control file */
841*4882a593Smuzhiyun static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
842*4882a593Smuzhiyun
843*4882a593Smuzhiyun /* default Attribute files */
844*4882a593Smuzhiyun static DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
845*4882a593Smuzhiyun static DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
846*4882a593Smuzhiyun static DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
847*4882a593Smuzhiyun static DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
848*4882a593Smuzhiyun static DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
849*4882a593Smuzhiyun static DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
850*4882a593Smuzhiyun static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
851*4882a593Smuzhiyun static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
852*4882a593Smuzhiyun
853*4882a593Smuzhiyun /* memory scrubber attribute file */
854*4882a593Smuzhiyun static DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show,
855*4882a593Smuzhiyun mci_sdram_scrub_rate_store); /* umode set later in is_visible */
856*4882a593Smuzhiyun
857*4882a593Smuzhiyun static struct attribute *mci_attrs[] = {
858*4882a593Smuzhiyun &dev_attr_reset_counters.attr,
859*4882a593Smuzhiyun &dev_attr_mc_name.attr,
860*4882a593Smuzhiyun &dev_attr_size_mb.attr,
861*4882a593Smuzhiyun &dev_attr_seconds_since_reset.attr,
862*4882a593Smuzhiyun &dev_attr_ue_noinfo_count.attr,
863*4882a593Smuzhiyun &dev_attr_ce_noinfo_count.attr,
864*4882a593Smuzhiyun &dev_attr_ue_count.attr,
865*4882a593Smuzhiyun &dev_attr_ce_count.attr,
866*4882a593Smuzhiyun &dev_attr_max_location.attr,
867*4882a593Smuzhiyun &dev_attr_sdram_scrub_rate.attr,
868*4882a593Smuzhiyun NULL
869*4882a593Smuzhiyun };
870*4882a593Smuzhiyun
mci_attr_is_visible(struct kobject * kobj,struct attribute * attr,int idx)871*4882a593Smuzhiyun static umode_t mci_attr_is_visible(struct kobject *kobj,
872*4882a593Smuzhiyun struct attribute *attr, int idx)
873*4882a593Smuzhiyun {
874*4882a593Smuzhiyun struct device *dev = kobj_to_dev(kobj);
875*4882a593Smuzhiyun struct mem_ctl_info *mci = to_mci(dev);
876*4882a593Smuzhiyun umode_t mode = 0;
877*4882a593Smuzhiyun
878*4882a593Smuzhiyun if (attr != &dev_attr_sdram_scrub_rate.attr)
879*4882a593Smuzhiyun return attr->mode;
880*4882a593Smuzhiyun if (mci->get_sdram_scrub_rate)
881*4882a593Smuzhiyun mode |= S_IRUGO;
882*4882a593Smuzhiyun if (mci->set_sdram_scrub_rate)
883*4882a593Smuzhiyun mode |= S_IWUSR;
884*4882a593Smuzhiyun return mode;
885*4882a593Smuzhiyun }
886*4882a593Smuzhiyun
887*4882a593Smuzhiyun static const struct attribute_group mci_attr_grp = {
888*4882a593Smuzhiyun .attrs = mci_attrs,
889*4882a593Smuzhiyun .is_visible = mci_attr_is_visible,
890*4882a593Smuzhiyun };
891*4882a593Smuzhiyun
892*4882a593Smuzhiyun static const struct attribute_group *mci_attr_groups[] = {
893*4882a593Smuzhiyun &mci_attr_grp,
894*4882a593Smuzhiyun NULL
895*4882a593Smuzhiyun };
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun static const struct device_type mci_attr_type = {
898*4882a593Smuzhiyun .groups = mci_attr_groups,
899*4882a593Smuzhiyun };
900*4882a593Smuzhiyun
901*4882a593Smuzhiyun /*
902*4882a593Smuzhiyun * Create a new Memory Controller kobject instance,
903*4882a593Smuzhiyun * mc<id> under the 'mc' directory
904*4882a593Smuzhiyun *
905*4882a593Smuzhiyun * Return:
906*4882a593Smuzhiyun * 0 Success
907*4882a593Smuzhiyun * !0 Failure
908*4882a593Smuzhiyun */
edac_create_sysfs_mci_device(struct mem_ctl_info * mci,const struct attribute_group ** groups)909*4882a593Smuzhiyun int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
910*4882a593Smuzhiyun const struct attribute_group **groups)
911*4882a593Smuzhiyun {
912*4882a593Smuzhiyun struct dimm_info *dimm;
913*4882a593Smuzhiyun int err;
914*4882a593Smuzhiyun
915*4882a593Smuzhiyun /* get the /sys/devices/system/edac subsys reference */
916*4882a593Smuzhiyun mci->dev.type = &mci_attr_type;
917*4882a593Smuzhiyun mci->dev.parent = mci_pdev;
918*4882a593Smuzhiyun mci->dev.groups = groups;
919*4882a593Smuzhiyun dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
920*4882a593Smuzhiyun dev_set_drvdata(&mci->dev, mci);
921*4882a593Smuzhiyun pm_runtime_forbid(&mci->dev);
922*4882a593Smuzhiyun
923*4882a593Smuzhiyun err = device_add(&mci->dev);
924*4882a593Smuzhiyun if (err < 0) {
925*4882a593Smuzhiyun edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev));
926*4882a593Smuzhiyun /* no put_device() here, free mci with _edac_mc_free() */
927*4882a593Smuzhiyun return err;
928*4882a593Smuzhiyun }
929*4882a593Smuzhiyun
930*4882a593Smuzhiyun edac_dbg(0, "device %s created\n", dev_name(&mci->dev));
931*4882a593Smuzhiyun
932*4882a593Smuzhiyun /*
933*4882a593Smuzhiyun * Create the dimm/rank devices
934*4882a593Smuzhiyun */
935*4882a593Smuzhiyun mci_for_each_dimm(mci, dimm) {
936*4882a593Smuzhiyun /* Only expose populated DIMMs */
937*4882a593Smuzhiyun if (!dimm->nr_pages)
938*4882a593Smuzhiyun continue;
939*4882a593Smuzhiyun
940*4882a593Smuzhiyun err = edac_create_dimm_object(mci, dimm);
941*4882a593Smuzhiyun if (err)
942*4882a593Smuzhiyun goto fail;
943*4882a593Smuzhiyun }
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun #ifdef CONFIG_EDAC_LEGACY_SYSFS
946*4882a593Smuzhiyun err = edac_create_csrow_objects(mci);
947*4882a593Smuzhiyun if (err < 0)
948*4882a593Smuzhiyun goto fail;
949*4882a593Smuzhiyun #endif
950*4882a593Smuzhiyun
951*4882a593Smuzhiyun edac_create_debugfs_nodes(mci);
952*4882a593Smuzhiyun return 0;
953*4882a593Smuzhiyun
954*4882a593Smuzhiyun fail:
955*4882a593Smuzhiyun edac_remove_sysfs_mci_device(mci);
956*4882a593Smuzhiyun
957*4882a593Smuzhiyun return err;
958*4882a593Smuzhiyun }
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun /*
961*4882a593Smuzhiyun * remove a Memory Controller instance
962*4882a593Smuzhiyun */
edac_remove_sysfs_mci_device(struct mem_ctl_info * mci)963*4882a593Smuzhiyun void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
964*4882a593Smuzhiyun {
965*4882a593Smuzhiyun struct dimm_info *dimm;
966*4882a593Smuzhiyun
967*4882a593Smuzhiyun if (!device_is_registered(&mci->dev))
968*4882a593Smuzhiyun return;
969*4882a593Smuzhiyun
970*4882a593Smuzhiyun edac_dbg(0, "\n");
971*4882a593Smuzhiyun
972*4882a593Smuzhiyun #ifdef CONFIG_EDAC_DEBUG
973*4882a593Smuzhiyun edac_debugfs_remove_recursive(mci->debugfs);
974*4882a593Smuzhiyun #endif
975*4882a593Smuzhiyun #ifdef CONFIG_EDAC_LEGACY_SYSFS
976*4882a593Smuzhiyun edac_delete_csrow_objects(mci);
977*4882a593Smuzhiyun #endif
978*4882a593Smuzhiyun
979*4882a593Smuzhiyun mci_for_each_dimm(mci, dimm) {
980*4882a593Smuzhiyun if (!device_is_registered(&dimm->dev))
981*4882a593Smuzhiyun continue;
982*4882a593Smuzhiyun edac_dbg(1, "unregistering device %s\n", dev_name(&dimm->dev));
983*4882a593Smuzhiyun device_unregister(&dimm->dev);
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun
986*4882a593Smuzhiyun /* only remove the device, but keep mci */
987*4882a593Smuzhiyun device_del(&mci->dev);
988*4882a593Smuzhiyun }
989*4882a593Smuzhiyun
mc_attr_release(struct device * dev)990*4882a593Smuzhiyun static void mc_attr_release(struct device *dev)
991*4882a593Smuzhiyun {
992*4882a593Smuzhiyun /*
993*4882a593Smuzhiyun * There's no container structure here, as this is just the mci
994*4882a593Smuzhiyun * parent device, used to create the /sys/devices/mc sysfs node.
995*4882a593Smuzhiyun * So, there are no attributes on it.
996*4882a593Smuzhiyun */
997*4882a593Smuzhiyun edac_dbg(1, "device %s released\n", dev_name(dev));
998*4882a593Smuzhiyun kfree(dev);
999*4882a593Smuzhiyun }
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun /*
1002*4882a593Smuzhiyun * Init/exit code for the module. Basically, creates/removes /sys/class/rc
1003*4882a593Smuzhiyun */
edac_mc_sysfs_init(void)1004*4882a593Smuzhiyun int __init edac_mc_sysfs_init(void)
1005*4882a593Smuzhiyun {
1006*4882a593Smuzhiyun int err;
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyun mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
1009*4882a593Smuzhiyun if (!mci_pdev)
1010*4882a593Smuzhiyun return -ENOMEM;
1011*4882a593Smuzhiyun
1012*4882a593Smuzhiyun mci_pdev->bus = edac_get_sysfs_subsys();
1013*4882a593Smuzhiyun mci_pdev->release = mc_attr_release;
1014*4882a593Smuzhiyun mci_pdev->init_name = "mc";
1015*4882a593Smuzhiyun
1016*4882a593Smuzhiyun err = device_register(mci_pdev);
1017*4882a593Smuzhiyun if (err < 0) {
1018*4882a593Smuzhiyun edac_dbg(1, "failure: create device %s\n", dev_name(mci_pdev));
1019*4882a593Smuzhiyun put_device(mci_pdev);
1020*4882a593Smuzhiyun return err;
1021*4882a593Smuzhiyun }
1022*4882a593Smuzhiyun
1023*4882a593Smuzhiyun edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
1024*4882a593Smuzhiyun
1025*4882a593Smuzhiyun return 0;
1026*4882a593Smuzhiyun }
1027*4882a593Smuzhiyun
edac_mc_sysfs_exit(void)1028*4882a593Smuzhiyun void edac_mc_sysfs_exit(void)
1029*4882a593Smuzhiyun {
1030*4882a593Smuzhiyun device_unregister(mci_pdev);
1031*4882a593Smuzhiyun }
1032