xref: /openbmc/linux/drivers/edac/edac_mc_sysfs.c (revision 25836ce1)
17c9281d7SDouglas Thompson /*
27c9281d7SDouglas Thompson  * edac_mc kernel module
342a8e397SDouglas Thompson  * (C) 2005-2007 Linux Networx (http://lnxi.com)
442a8e397SDouglas Thompson  *
57c9281d7SDouglas Thompson  * This file may be distributed under the terms of the
67c9281d7SDouglas Thompson  * GNU General Public License.
77c9281d7SDouglas Thompson  *
842a8e397SDouglas Thompson  * Written Doug Thompson <norsk5@xmission.com> www.softwarebitmaker.com
97c9281d7SDouglas Thompson  *
1037e59f87SMauro Carvalho Chehab  * (c) 2012-2013 - Mauro Carvalho Chehab
117a623c03SMauro Carvalho Chehab  *	The entire API were re-written, and ported to use struct device
127a623c03SMauro Carvalho Chehab  *
137c9281d7SDouglas Thompson  */
147c9281d7SDouglas Thompson 
157c9281d7SDouglas Thompson #include <linux/ctype.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
1730e1f7a8SBorislav Petkov #include <linux/edac.h>
188096cfafSDoug Thompson #include <linux/bug.h>
197a623c03SMauro Carvalho Chehab #include <linux/pm_runtime.h>
20452a6bf9SMauro Carvalho Chehab #include <linux/uaccess.h>
217c9281d7SDouglas Thompson 
2278d88e8aSMauro Carvalho Chehab #include "edac_mc.h"
237c9281d7SDouglas Thompson #include "edac_module.h"
247c9281d7SDouglas Thompson 
257c9281d7SDouglas Thompson /* MC EDAC Controls, setable by module parameter, and sysfs */
264de78c68SDave Jiang static int edac_mc_log_ue = 1;
274de78c68SDave Jiang static int edac_mc_log_ce = 1;
28f044091cSDouglas Thompson static int edac_mc_panic_on_ue;
29d8655e76SEiichi Tsukata static unsigned int edac_mc_poll_msec = 1000;
307c9281d7SDouglas Thompson 
317c9281d7SDouglas Thompson /* Getter functions for above */
edac_mc_get_log_ue(void)324de78c68SDave Jiang int edac_mc_get_log_ue(void)
337c9281d7SDouglas Thompson {
344de78c68SDave Jiang 	return edac_mc_log_ue;
357c9281d7SDouglas Thompson }
367c9281d7SDouglas Thompson 
edac_mc_get_log_ce(void)374de78c68SDave Jiang int edac_mc_get_log_ce(void)
387c9281d7SDouglas Thompson {
394de78c68SDave Jiang 	return edac_mc_log_ce;
407c9281d7SDouglas Thompson }
417c9281d7SDouglas Thompson 
edac_mc_get_panic_on_ue(void)424de78c68SDave Jiang int edac_mc_get_panic_on_ue(void)
437c9281d7SDouglas Thompson {
444de78c68SDave Jiang 	return edac_mc_panic_on_ue;
457c9281d7SDouglas Thompson }
467c9281d7SDouglas Thompson 
4781d87cb1SDave Jiang /* this is temporary */
edac_mc_get_poll_msec(void)48d8655e76SEiichi Tsukata unsigned int edac_mc_get_poll_msec(void)
4981d87cb1SDave Jiang {
504de78c68SDave Jiang 	return edac_mc_poll_msec;
517c9281d7SDouglas Thompson }
527c9281d7SDouglas Thompson 
edac_set_poll_msec(const char * val,const struct kernel_param * kp)53e4dca7b7SKees Cook static int edac_set_poll_msec(const char *val, const struct kernel_param *kp)
54096846e2SArthur Jones {
55d8655e76SEiichi Tsukata 	unsigned int i;
56096846e2SArthur Jones 	int ret;
57096846e2SArthur Jones 
58096846e2SArthur Jones 	if (!val)
59096846e2SArthur Jones 		return -EINVAL;
60096846e2SArthur Jones 
61d8655e76SEiichi Tsukata 	ret = kstrtouint(val, 0, &i);
62c542b53dSJingoo Han 	if (ret)
63c542b53dSJingoo Han 		return ret;
649da21b15SBorislav Petkov 
65d8655e76SEiichi Tsukata 	if (i < 1000)
66096846e2SArthur Jones 		return -EINVAL;
679da21b15SBorislav Petkov 
68d8655e76SEiichi Tsukata 	*((unsigned int *)kp->arg) = i;
69096846e2SArthur Jones 
70096846e2SArthur Jones 	/* notify edac_mc engine to reset the poll period */
71d8655e76SEiichi Tsukata 	edac_mc_reset_delay_period(i);
72096846e2SArthur Jones 
73096846e2SArthur Jones 	return 0;
74096846e2SArthur Jones }
75096846e2SArthur Jones 
767c9281d7SDouglas Thompson /* Parameter declarations for above */
774de78c68SDave Jiang module_param(edac_mc_panic_on_ue, int, 0644);
784de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on");
794de78c68SDave Jiang module_param(edac_mc_log_ue, int, 0644);
804de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ue,
814de78c68SDave Jiang 		 "Log uncorrectable error to console: 0=off 1=on");
824de78c68SDave Jiang module_param(edac_mc_log_ce, int, 0644);
834de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ce,
844de78c68SDave Jiang 		 "Log correctable error to console: 0=off 1=on");
85d8655e76SEiichi Tsukata module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_uint,
86096846e2SArthur Jones 		  &edac_mc_poll_msec, 0644);
874de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds");
887c9281d7SDouglas Thompson 
89de3910ebSMauro Carvalho Chehab static struct device *mci_pdev;
907a623c03SMauro Carvalho Chehab 
917c9281d7SDouglas Thompson /*
927c9281d7SDouglas Thompson  * various constants for Memory Controllers
937c9281d7SDouglas Thompson  */
948b7719e0SBorislav Petkov static const char * const dev_types[] = {
957c9281d7SDouglas Thompson 	[DEV_UNKNOWN] = "Unknown",
967c9281d7SDouglas Thompson 	[DEV_X1] = "x1",
977c9281d7SDouglas Thompson 	[DEV_X2] = "x2",
987c9281d7SDouglas Thompson 	[DEV_X4] = "x4",
997c9281d7SDouglas Thompson 	[DEV_X8] = "x8",
1007c9281d7SDouglas Thompson 	[DEV_X16] = "x16",
1017c9281d7SDouglas Thompson 	[DEV_X32] = "x32",
1027c9281d7SDouglas Thompson 	[DEV_X64] = "x64"
1037c9281d7SDouglas Thompson };
1047c9281d7SDouglas Thompson 
1058b7719e0SBorislav Petkov static const char * const edac_caps[] = {
1067c9281d7SDouglas Thompson 	[EDAC_UNKNOWN] = "Unknown",
1077c9281d7SDouglas Thompson 	[EDAC_NONE] = "None",
1087c9281d7SDouglas Thompson 	[EDAC_RESERVED] = "Reserved",
1097c9281d7SDouglas Thompson 	[EDAC_PARITY] = "PARITY",
1107c9281d7SDouglas Thompson 	[EDAC_EC] = "EC",
1117c9281d7SDouglas Thompson 	[EDAC_SECDED] = "SECDED",
1127c9281d7SDouglas Thompson 	[EDAC_S2ECD2ED] = "S2ECD2ED",
1137c9281d7SDouglas Thompson 	[EDAC_S4ECD4ED] = "S4ECD4ED",
1147c9281d7SDouglas Thompson 	[EDAC_S8ECD8ED] = "S8ECD8ED",
1157c9281d7SDouglas Thompson 	[EDAC_S16ECD16ED] = "S16ECD16ED"
1167c9281d7SDouglas Thompson };
1177c9281d7SDouglas Thompson 
11819974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
1197a623c03SMauro Carvalho Chehab /*
1207a623c03SMauro Carvalho Chehab  * EDAC sysfs CSROW data structures and methods
1217c9281d7SDouglas Thompson  */
1227c9281d7SDouglas Thompson 
1237a623c03SMauro Carvalho Chehab #define to_csrow(k) container_of(k, struct csrow_info, dev)
1247a623c03SMauro Carvalho Chehab 
1257a623c03SMauro Carvalho Chehab /*
1267a623c03SMauro Carvalho Chehab  * We need it to avoid namespace conflicts between the legacy API
1277a623c03SMauro Carvalho Chehab  * and the per-dimm/per-rank one
1287a623c03SMauro Carvalho Chehab  */
1297a623c03SMauro Carvalho Chehab #define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \
130fbe2d361SStephen Hemminger 	static struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store)
1317a623c03SMauro Carvalho Chehab 
1327a623c03SMauro Carvalho Chehab struct dev_ch_attribute {
1337a623c03SMauro Carvalho Chehab 	struct device_attribute attr;
134d55c79acSRobert Richter 	unsigned int channel;
1357a623c03SMauro Carvalho Chehab };
1367a623c03SMauro Carvalho Chehab 
1377a623c03SMauro Carvalho Chehab #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \
138f11135d8SBorislav Petkov 	static struct dev_ch_attribute dev_attr_legacy_##_name = \
1397a623c03SMauro Carvalho Chehab 		{ __ATTR(_name, _mode, _show, _store), (_var) }
1407a623c03SMauro Carvalho Chehab 
1417a623c03SMauro Carvalho Chehab #define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel)
1427a623c03SMauro Carvalho Chehab 
1437c9281d7SDouglas Thompson /* Set of more default csrow<id> attribute show/store functions */
csrow_ue_count_show(struct device * dev,struct device_attribute * mattr,char * data)1447a623c03SMauro Carvalho Chehab static ssize_t csrow_ue_count_show(struct device *dev,
1457a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1467c9281d7SDouglas Thompson {
1477a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1487a623c03SMauro Carvalho Chehab 
1497c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", csrow->ue_count);
1507c9281d7SDouglas Thompson }
1517c9281d7SDouglas Thompson 
csrow_ce_count_show(struct device * dev,struct device_attribute * mattr,char * data)1527a623c03SMauro Carvalho Chehab static ssize_t csrow_ce_count_show(struct device *dev,
1537a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1547c9281d7SDouglas Thompson {
1557a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1567a623c03SMauro Carvalho Chehab 
1577c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", csrow->ce_count);
1587c9281d7SDouglas Thompson }
1597c9281d7SDouglas Thompson 
csrow_size_show(struct device * dev,struct device_attribute * mattr,char * data)1607a623c03SMauro Carvalho Chehab static ssize_t csrow_size_show(struct device *dev,
1617a623c03SMauro Carvalho Chehab 			       struct device_attribute *mattr, char *data)
1627c9281d7SDouglas Thompson {
1637a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
164a895bf8bSMauro Carvalho Chehab 	int i;
165a895bf8bSMauro Carvalho Chehab 	u32 nr_pages = 0;
166a895bf8bSMauro Carvalho Chehab 
167a895bf8bSMauro Carvalho Chehab 	for (i = 0; i < csrow->nr_channels; i++)
168de3910ebSMauro Carvalho Chehab 		nr_pages += csrow->channels[i]->dimm->nr_pages;
169a895bf8bSMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages));
1707c9281d7SDouglas Thompson }
1717c9281d7SDouglas Thompson 
csrow_mem_type_show(struct device * dev,struct device_attribute * mattr,char * data)1727a623c03SMauro Carvalho Chehab static ssize_t csrow_mem_type_show(struct device *dev,
1737a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1747c9281d7SDouglas Thompson {
1757a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1767a623c03SMauro Carvalho Chehab 
177d6dd77ebSTony Luck 	return sprintf(data, "%s\n", edac_mem_types[csrow->channels[0]->dimm->mtype]);
1787c9281d7SDouglas Thompson }
1797c9281d7SDouglas Thompson 
csrow_dev_type_show(struct device * dev,struct device_attribute * mattr,char * data)1807a623c03SMauro Carvalho Chehab static ssize_t csrow_dev_type_show(struct device *dev,
1817a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1827c9281d7SDouglas Thompson {
1837a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1847a623c03SMauro Carvalho Chehab 
185de3910ebSMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]);
1867c9281d7SDouglas Thompson }
1877c9281d7SDouglas Thompson 
csrow_edac_mode_show(struct device * dev,struct device_attribute * mattr,char * data)1887a623c03SMauro Carvalho Chehab static ssize_t csrow_edac_mode_show(struct device *dev,
1897a623c03SMauro Carvalho Chehab 				    struct device_attribute *mattr,
1907a623c03SMauro Carvalho Chehab 				    char *data)
1917c9281d7SDouglas Thompson {
1927a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1937a623c03SMauro Carvalho Chehab 
194de3910ebSMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]);
1957c9281d7SDouglas Thompson }
1967c9281d7SDouglas Thompson 
1977c9281d7SDouglas Thompson /* show/store functions for DIMM Label attributes */
channel_dimm_label_show(struct device * dev,struct device_attribute * mattr,char * data)1987a623c03SMauro Carvalho Chehab static ssize_t channel_dimm_label_show(struct device *dev,
1997a623c03SMauro Carvalho Chehab 				       struct device_attribute *mattr,
2007a623c03SMauro Carvalho Chehab 				       char *data)
2017c9281d7SDouglas Thompson {
2027a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
203d55c79acSRobert Richter 	unsigned int chan = to_channel(mattr);
204de3910ebSMauro Carvalho Chehab 	struct rank_info *rank = csrow->channels[chan];
2057a623c03SMauro Carvalho Chehab 
206124682c7SArthur Jones 	/* if field has not been initialized, there is nothing to send */
2077a623c03SMauro Carvalho Chehab 	if (!rank->dimm->label[0])
208124682c7SArthur Jones 		return 0;
209124682c7SArthur Jones 
2101ea62c59SToshi Kani 	return snprintf(data, sizeof(rank->dimm->label) + 1, "%s\n",
2117a623c03SMauro Carvalho Chehab 			rank->dimm->label);
2127c9281d7SDouglas Thompson }
2137c9281d7SDouglas Thompson 
channel_dimm_label_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)2147a623c03SMauro Carvalho Chehab static ssize_t channel_dimm_label_store(struct device *dev,
2157a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
2167a623c03SMauro Carvalho Chehab 					const char *data, size_t count)
2177c9281d7SDouglas Thompson {
2187a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
219d55c79acSRobert Richter 	unsigned int chan = to_channel(mattr);
220de3910ebSMauro Carvalho Chehab 	struct rank_info *rank = csrow->channels[chan];
221438470b8SToshi Kani 	size_t copy_count = count;
2227a623c03SMauro Carvalho Chehab 
223438470b8SToshi Kani 	if (count == 0)
224438470b8SToshi Kani 		return -EINVAL;
2257c9281d7SDouglas Thompson 
226438470b8SToshi Kani 	if (data[count - 1] == '\0' || data[count - 1] == '\n')
227438470b8SToshi Kani 		copy_count -= 1;
2287c9281d7SDouglas Thompson 
229d0c9c930SToshi Kani 	if (copy_count == 0 || copy_count >= sizeof(rank->dimm->label))
230438470b8SToshi Kani 		return -EINVAL;
231438470b8SToshi Kani 
232438470b8SToshi Kani 	strncpy(rank->dimm->label, data, copy_count);
233438470b8SToshi Kani 	rank->dimm->label[copy_count] = '\0';
234438470b8SToshi Kani 
235438470b8SToshi Kani 	return count;
2367c9281d7SDouglas Thompson }
2377c9281d7SDouglas Thompson 
2387c9281d7SDouglas Thompson /* show function for dynamic chX_ce_count attribute */
channel_ce_count_show(struct device * dev,struct device_attribute * mattr,char * data)2397a623c03SMauro Carvalho Chehab static ssize_t channel_ce_count_show(struct device *dev,
2407a623c03SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
2417c9281d7SDouglas Thompson {
2427a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
243d55c79acSRobert Richter 	unsigned int chan = to_channel(mattr);
244de3910ebSMauro Carvalho Chehab 	struct rank_info *rank = csrow->channels[chan];
2457a623c03SMauro Carvalho Chehab 
2467a623c03SMauro Carvalho Chehab 	return sprintf(data, "%u\n", rank->ce_count);
2477c9281d7SDouglas Thompson }
2487c9281d7SDouglas Thompson 
2497a623c03SMauro Carvalho Chehab /* cwrow<id>/attribute files */
2507a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL);
2517a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL);
2527a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL);
2537a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL);
2547a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL);
2557a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL);
2567c9281d7SDouglas Thompson 
2577c9281d7SDouglas Thompson /* default attributes of the CSROW<id> object */
2587a623c03SMauro Carvalho Chehab static struct attribute *csrow_attrs[] = {
2597a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_dev_type.attr,
2607a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_mem_type.attr,
2617a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_edac_mode.attr,
2627a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_size_mb.attr,
2637a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ue_count.attr,
2647a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ce_count.attr,
2657c9281d7SDouglas Thompson 	NULL,
2667c9281d7SDouglas Thompson };
2677c9281d7SDouglas Thompson 
2681c18be5aSArvind Yadav static const struct attribute_group csrow_attr_grp = {
2697a623c03SMauro Carvalho Chehab 	.attrs	= csrow_attrs,
2707c9281d7SDouglas Thompson };
2717c9281d7SDouglas Thompson 
2727a623c03SMauro Carvalho Chehab static const struct attribute_group *csrow_attr_groups[] = {
2737a623c03SMauro Carvalho Chehab 	&csrow_attr_grp,
2747a623c03SMauro Carvalho Chehab 	NULL
2757c9281d7SDouglas Thompson };
2767c9281d7SDouglas Thompson 
277b2b3e736SBhumika Goyal static const struct device_type csrow_attr_type = {
2787a623c03SMauro Carvalho Chehab 	.groups		= csrow_attr_groups,
2797a623c03SMauro Carvalho Chehab };
2807a623c03SMauro Carvalho Chehab 
2817a623c03SMauro Carvalho Chehab /*
2827a623c03SMauro Carvalho Chehab  * possible dynamic channel DIMM Label attribute files
2837a623c03SMauro Carvalho Chehab  *
2847a623c03SMauro Carvalho Chehab  */
2857a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
2867a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 0);
2877a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
2887a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 1);
2897a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR,
2907a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 2);
2917a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR,
2927a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 3);
2937a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR,
2947a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 4);
2957a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR,
2967a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 5);
297bba14295SBorislav Petkov DEVICE_CHANNEL(ch6_dimm_label, S_IRUGO | S_IWUSR,
298bba14295SBorislav Petkov 	channel_dimm_label_show, channel_dimm_label_store, 6);
299bba14295SBorislav Petkov DEVICE_CHANNEL(ch7_dimm_label, S_IRUGO | S_IWUSR,
300bba14295SBorislav Petkov 	channel_dimm_label_show, channel_dimm_label_store, 7);
301*25836ce1SYazen Ghannam DEVICE_CHANNEL(ch8_dimm_label, S_IRUGO | S_IWUSR,
302*25836ce1SYazen Ghannam 	channel_dimm_label_show, channel_dimm_label_store, 8);
303*25836ce1SYazen Ghannam DEVICE_CHANNEL(ch9_dimm_label, S_IRUGO | S_IWUSR,
304*25836ce1SYazen Ghannam 	channel_dimm_label_show, channel_dimm_label_store, 9);
305*25836ce1SYazen Ghannam DEVICE_CHANNEL(ch10_dimm_label, S_IRUGO | S_IWUSR,
306*25836ce1SYazen Ghannam 	channel_dimm_label_show, channel_dimm_label_store, 10);
307*25836ce1SYazen Ghannam DEVICE_CHANNEL(ch11_dimm_label, S_IRUGO | S_IWUSR,
308*25836ce1SYazen Ghannam 	channel_dimm_label_show, channel_dimm_label_store, 11);
3097c9281d7SDouglas Thompson 
3107a623c03SMauro Carvalho Chehab /* Total possible dynamic DIMM Label attribute file table */
3112c1946b6STakashi Iwai static struct attribute *dynamic_csrow_dimm_attr[] = {
3122c1946b6STakashi Iwai 	&dev_attr_legacy_ch0_dimm_label.attr.attr,
3132c1946b6STakashi Iwai 	&dev_attr_legacy_ch1_dimm_label.attr.attr,
3142c1946b6STakashi Iwai 	&dev_attr_legacy_ch2_dimm_label.attr.attr,
3152c1946b6STakashi Iwai 	&dev_attr_legacy_ch3_dimm_label.attr.attr,
3162c1946b6STakashi Iwai 	&dev_attr_legacy_ch4_dimm_label.attr.attr,
3172c1946b6STakashi Iwai 	&dev_attr_legacy_ch5_dimm_label.attr.attr,
318bba14295SBorislav Petkov 	&dev_attr_legacy_ch6_dimm_label.attr.attr,
319bba14295SBorislav Petkov 	&dev_attr_legacy_ch7_dimm_label.attr.attr,
320*25836ce1SYazen Ghannam 	&dev_attr_legacy_ch8_dimm_label.attr.attr,
321*25836ce1SYazen Ghannam 	&dev_attr_legacy_ch9_dimm_label.attr.attr,
322*25836ce1SYazen Ghannam 	&dev_attr_legacy_ch10_dimm_label.attr.attr,
323*25836ce1SYazen Ghannam 	&dev_attr_legacy_ch11_dimm_label.attr.attr,
3242c1946b6STakashi Iwai 	NULL
3257a623c03SMauro Carvalho Chehab };
3267c9281d7SDouglas Thompson 
3277a623c03SMauro Carvalho Chehab /* possible dynamic channel ce_count attribute files */
328c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch0_ce_count, S_IRUGO,
3297a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 0);
330c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch1_ce_count, S_IRUGO,
3317a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 1);
332c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch2_ce_count, S_IRUGO,
3337a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 2);
334c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch3_ce_count, S_IRUGO,
3357a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 3);
336c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch4_ce_count, S_IRUGO,
3377a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 4);
338c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch5_ce_count, S_IRUGO,
3397a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 5);
340bba14295SBorislav Petkov DEVICE_CHANNEL(ch6_ce_count, S_IRUGO,
341bba14295SBorislav Petkov 		   channel_ce_count_show, NULL, 6);
342bba14295SBorislav Petkov DEVICE_CHANNEL(ch7_ce_count, S_IRUGO,
343bba14295SBorislav Petkov 		   channel_ce_count_show, NULL, 7);
344*25836ce1SYazen Ghannam DEVICE_CHANNEL(ch8_ce_count, S_IRUGO,
345*25836ce1SYazen Ghannam 		   channel_ce_count_show, NULL, 8);
346*25836ce1SYazen Ghannam DEVICE_CHANNEL(ch9_ce_count, S_IRUGO,
347*25836ce1SYazen Ghannam 		   channel_ce_count_show, NULL, 9);
348*25836ce1SYazen Ghannam DEVICE_CHANNEL(ch10_ce_count, S_IRUGO,
349*25836ce1SYazen Ghannam 		   channel_ce_count_show, NULL, 10);
350*25836ce1SYazen Ghannam DEVICE_CHANNEL(ch11_ce_count, S_IRUGO,
351*25836ce1SYazen Ghannam 		   channel_ce_count_show, NULL, 11);
3527c9281d7SDouglas Thompson 
3537a623c03SMauro Carvalho Chehab /* Total possible dynamic ce_count attribute file table */
3542c1946b6STakashi Iwai static struct attribute *dynamic_csrow_ce_count_attr[] = {
3552c1946b6STakashi Iwai 	&dev_attr_legacy_ch0_ce_count.attr.attr,
3562c1946b6STakashi Iwai 	&dev_attr_legacy_ch1_ce_count.attr.attr,
3572c1946b6STakashi Iwai 	&dev_attr_legacy_ch2_ce_count.attr.attr,
3582c1946b6STakashi Iwai 	&dev_attr_legacy_ch3_ce_count.attr.attr,
3592c1946b6STakashi Iwai 	&dev_attr_legacy_ch4_ce_count.attr.attr,
3602c1946b6STakashi Iwai 	&dev_attr_legacy_ch5_ce_count.attr.attr,
361bba14295SBorislav Petkov 	&dev_attr_legacy_ch6_ce_count.attr.attr,
362bba14295SBorislav Petkov 	&dev_attr_legacy_ch7_ce_count.attr.attr,
363*25836ce1SYazen Ghannam 	&dev_attr_legacy_ch8_ce_count.attr.attr,
364*25836ce1SYazen Ghannam 	&dev_attr_legacy_ch9_ce_count.attr.attr,
365*25836ce1SYazen Ghannam 	&dev_attr_legacy_ch10_ce_count.attr.attr,
366*25836ce1SYazen Ghannam 	&dev_attr_legacy_ch11_ce_count.attr.attr,
3672c1946b6STakashi Iwai 	NULL
3682c1946b6STakashi Iwai };
3692c1946b6STakashi Iwai 
csrow_dev_is_visible(struct kobject * kobj,struct attribute * attr,int idx)3702c1946b6STakashi Iwai static umode_t csrow_dev_is_visible(struct kobject *kobj,
3712c1946b6STakashi Iwai 				    struct attribute *attr, int idx)
3722c1946b6STakashi Iwai {
3732c1946b6STakashi Iwai 	struct device *dev = kobj_to_dev(kobj);
3742c1946b6STakashi Iwai 	struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
3752c1946b6STakashi Iwai 
3762c1946b6STakashi Iwai 	if (idx >= csrow->nr_channels)
3772c1946b6STakashi Iwai 		return 0;
378bba14295SBorislav Petkov 
379bba14295SBorislav Petkov 	if (idx >= ARRAY_SIZE(dynamic_csrow_ce_count_attr) - 1) {
380bba14295SBorislav Petkov 		WARN_ONCE(1, "idx: %d\n", idx);
381bba14295SBorislav Petkov 		return 0;
382bba14295SBorislav Petkov 	}
383bba14295SBorislav Petkov 
3842c1946b6STakashi Iwai 	/* Only expose populated DIMMs */
3852c1946b6STakashi Iwai 	if (!csrow->channels[idx]->dimm->nr_pages)
3862c1946b6STakashi Iwai 		return 0;
387bba14295SBorislav Petkov 
3882c1946b6STakashi Iwai 	return attr->mode;
3892c1946b6STakashi Iwai }
3902c1946b6STakashi Iwai 
3912c1946b6STakashi Iwai 
3922c1946b6STakashi Iwai static const struct attribute_group csrow_dev_dimm_group = {
3932c1946b6STakashi Iwai 	.attrs = dynamic_csrow_dimm_attr,
3942c1946b6STakashi Iwai 	.is_visible = csrow_dev_is_visible,
3952c1946b6STakashi Iwai };
3962c1946b6STakashi Iwai 
3972c1946b6STakashi Iwai static const struct attribute_group csrow_dev_ce_count_group = {
3982c1946b6STakashi Iwai 	.attrs = dynamic_csrow_ce_count_attr,
3992c1946b6STakashi Iwai 	.is_visible = csrow_dev_is_visible,
4002c1946b6STakashi Iwai };
4012c1946b6STakashi Iwai 
4022c1946b6STakashi Iwai static const struct attribute_group *csrow_dev_groups[] = {
4032c1946b6STakashi Iwai 	&csrow_dev_dimm_group,
4042c1946b6STakashi Iwai 	&csrow_dev_ce_count_group,
4052c1946b6STakashi Iwai 	NULL
4067c9281d7SDouglas Thompson };
4077c9281d7SDouglas Thompson 
csrow_release(struct device * dev)408bea1bfd5SRobert Richter static void csrow_release(struct device *dev)
409bea1bfd5SRobert Richter {
410bea1bfd5SRobert Richter 	/*
411bea1bfd5SRobert Richter 	 * Nothing to do, just unregister sysfs here. The mci
412bea1bfd5SRobert Richter 	 * device owns the data and will also release it.
413bea1bfd5SRobert Richter 	 */
414bea1bfd5SRobert Richter }
415bea1bfd5SRobert Richter 
nr_pages_per_csrow(struct csrow_info * csrow)416e39f4ea9SMauro Carvalho Chehab static inline int nr_pages_per_csrow(struct csrow_info *csrow)
417e39f4ea9SMauro Carvalho Chehab {
418e39f4ea9SMauro Carvalho Chehab 	int chan, nr_pages = 0;
419e39f4ea9SMauro Carvalho Chehab 
420e39f4ea9SMauro Carvalho Chehab 	for (chan = 0; chan < csrow->nr_channels; chan++)
421de3910ebSMauro Carvalho Chehab 		nr_pages += csrow->channels[chan]->dimm->nr_pages;
422e39f4ea9SMauro Carvalho Chehab 
423e39f4ea9SMauro Carvalho Chehab 	return nr_pages;
424e39f4ea9SMauro Carvalho Chehab }
425e39f4ea9SMauro Carvalho Chehab 
4267c9281d7SDouglas Thompson /* Create a CSROW object under specifed edac_mc_device */
edac_create_csrow_object(struct mem_ctl_info * mci,struct csrow_info * csrow,int index)4278096cfafSDoug Thompson static int edac_create_csrow_object(struct mem_ctl_info *mci,
428079708b9SDouglas Thompson 				    struct csrow_info *csrow, int index)
4297c9281d7SDouglas Thompson {
430585fb3d9SPan Bian 	int err;
431585fb3d9SPan Bian 
4327a623c03SMauro Carvalho Chehab 	csrow->dev.type = &csrow_attr_type;
4332c1946b6STakashi Iwai 	csrow->dev.groups = csrow_dev_groups;
434bea1bfd5SRobert Richter 	csrow->dev.release = csrow_release;
4357a623c03SMauro Carvalho Chehab 	device_initialize(&csrow->dev);
4367a623c03SMauro Carvalho Chehab 	csrow->dev.parent = &mci->dev;
437921a6899SBorislav Petkov 	csrow->mci = mci;
4387a623c03SMauro Carvalho Chehab 	dev_set_name(&csrow->dev, "csrow%d", index);
4397a623c03SMauro Carvalho Chehab 	dev_set_drvdata(&csrow->dev, csrow);
4407c9281d7SDouglas Thompson 
441585fb3d9SPan Bian 	err = device_add(&csrow->dev);
442e701f412SRobert Richter 	if (err) {
443e701f412SRobert Richter 		edac_dbg(1, "failure: create device %s\n", dev_name(&csrow->dev));
444585fb3d9SPan Bian 		put_device(&csrow->dev);
445585fb3d9SPan Bian 		return err;
4467c9281d7SDouglas Thompson 	}
4477c9281d7SDouglas Thompson 
448e701f412SRobert Richter 	edac_dbg(0, "device %s created\n", dev_name(&csrow->dev));
449e701f412SRobert Richter 
450e701f412SRobert Richter 	return 0;
451e701f412SRobert Richter }
452e701f412SRobert Richter 
4537a623c03SMauro Carvalho Chehab /* Create a CSROW object under specifed edac_mc_device */
edac_create_csrow_objects(struct mem_ctl_info * mci)4547a623c03SMauro Carvalho Chehab static int edac_create_csrow_objects(struct mem_ctl_info *mci)
4557a623c03SMauro Carvalho Chehab {
4562c1946b6STakashi Iwai 	int err, i;
4577a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4587c9281d7SDouglas Thompson 
4597a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->nr_csrows; i++) {
460de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
461e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
462e39f4ea9SMauro Carvalho Chehab 			continue;
463de3910ebSMauro Carvalho Chehab 		err = edac_create_csrow_object(mci, mci->csrows[i], i);
464e701f412SRobert Richter 		if (err < 0)
4657a623c03SMauro Carvalho Chehab 			goto error;
4667a623c03SMauro Carvalho Chehab 	}
4677a623c03SMauro Carvalho Chehab 	return 0;
4687a623c03SMauro Carvalho Chehab 
4697a623c03SMauro Carvalho Chehab error:
4707a623c03SMauro Carvalho Chehab 	for (--i; i >= 0; i--) {
471bea1bfd5SRobert Richter 		if (device_is_registered(&mci->csrows[i]->dev))
4724d59588cSRobert Richter 			device_unregister(&mci->csrows[i]->dev);
4737a623c03SMauro Carvalho Chehab 	}
4747a623c03SMauro Carvalho Chehab 
4757a623c03SMauro Carvalho Chehab 	return err;
4767a623c03SMauro Carvalho Chehab }
4777a623c03SMauro Carvalho Chehab 
edac_delete_csrow_objects(struct mem_ctl_info * mci)4787a623c03SMauro Carvalho Chehab static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
4797a623c03SMauro Carvalho Chehab {
4802c1946b6STakashi Iwai 	int i;
4817a623c03SMauro Carvalho Chehab 
482bea1bfd5SRobert Richter 	for (i = 0; i < mci->nr_csrows; i++) {
483bea1bfd5SRobert Richter 		if (device_is_registered(&mci->csrows[i]->dev))
48444d22e24SLans Zhang 			device_unregister(&mci->csrows[i]->dev);
4857a623c03SMauro Carvalho Chehab 	}
4867a623c03SMauro Carvalho Chehab }
487bea1bfd5SRobert Richter 
48819974710SMauro Carvalho Chehab #endif
48919974710SMauro Carvalho Chehab 
49019974710SMauro Carvalho Chehab /*
49119974710SMauro Carvalho Chehab  * Per-dimm (or per-rank) devices
49219974710SMauro Carvalho Chehab  */
49319974710SMauro Carvalho Chehab 
49419974710SMauro Carvalho Chehab #define to_dimm(k) container_of(k, struct dimm_info, dev)
49519974710SMauro Carvalho Chehab 
49619974710SMauro Carvalho Chehab /* show/store functions for DIMM Label attributes */
dimmdev_location_show(struct device * dev,struct device_attribute * mattr,char * data)49719974710SMauro Carvalho Chehab static ssize_t dimmdev_location_show(struct device *dev,
49819974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
49919974710SMauro Carvalho Chehab {
50019974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
501e6bbde8bSXiongfeng Wang 	ssize_t count;
50219974710SMauro Carvalho Chehab 
503e6bbde8bSXiongfeng Wang 	count = edac_dimm_info_location(dimm, data, PAGE_SIZE);
504e6bbde8bSXiongfeng Wang 	count += scnprintf(data + count, PAGE_SIZE - count, "\n");
505e6bbde8bSXiongfeng Wang 
506e6bbde8bSXiongfeng Wang 	return count;
50719974710SMauro Carvalho Chehab }
50819974710SMauro Carvalho Chehab 
dimmdev_label_show(struct device * dev,struct device_attribute * mattr,char * data)50919974710SMauro Carvalho Chehab static ssize_t dimmdev_label_show(struct device *dev,
51019974710SMauro Carvalho Chehab 				  struct device_attribute *mattr, char *data)
51119974710SMauro Carvalho Chehab {
51219974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
51319974710SMauro Carvalho Chehab 
51419974710SMauro Carvalho Chehab 	/* if field has not been initialized, there is nothing to send */
51519974710SMauro Carvalho Chehab 	if (!dimm->label[0])
51619974710SMauro Carvalho Chehab 		return 0;
51719974710SMauro Carvalho Chehab 
5181ea62c59SToshi Kani 	return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label);
51919974710SMauro Carvalho Chehab }
52019974710SMauro Carvalho Chehab 
dimmdev_label_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)52119974710SMauro Carvalho Chehab static ssize_t dimmdev_label_store(struct device *dev,
52219974710SMauro Carvalho Chehab 				   struct device_attribute *mattr,
52319974710SMauro Carvalho Chehab 				   const char *data,
52419974710SMauro Carvalho Chehab 				   size_t count)
52519974710SMauro Carvalho Chehab {
52619974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
527438470b8SToshi Kani 	size_t copy_count = count;
52819974710SMauro Carvalho Chehab 
529438470b8SToshi Kani 	if (count == 0)
530438470b8SToshi Kani 		return -EINVAL;
53119974710SMauro Carvalho Chehab 
532438470b8SToshi Kani 	if (data[count - 1] == '\0' || data[count - 1] == '\n')
533438470b8SToshi Kani 		copy_count -= 1;
53419974710SMauro Carvalho Chehab 
535d0c9c930SToshi Kani 	if (copy_count == 0 || copy_count >= sizeof(dimm->label))
536438470b8SToshi Kani 		return -EINVAL;
537438470b8SToshi Kani 
538438470b8SToshi Kani 	strncpy(dimm->label, data, copy_count);
539438470b8SToshi Kani 	dimm->label[copy_count] = '\0';
540438470b8SToshi Kani 
541438470b8SToshi Kani 	return count;
54219974710SMauro Carvalho Chehab }
54319974710SMauro Carvalho Chehab 
dimmdev_size_show(struct device * dev,struct device_attribute * mattr,char * data)54419974710SMauro Carvalho Chehab static ssize_t dimmdev_size_show(struct device *dev,
54519974710SMauro Carvalho Chehab 				 struct device_attribute *mattr, char *data)
54619974710SMauro Carvalho Chehab {
54719974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
54819974710SMauro Carvalho Chehab 
54919974710SMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
55019974710SMauro Carvalho Chehab }
55119974710SMauro Carvalho Chehab 
dimmdev_mem_type_show(struct device * dev,struct device_attribute * mattr,char * data)55219974710SMauro Carvalho Chehab static ssize_t dimmdev_mem_type_show(struct device *dev,
55319974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
55419974710SMauro Carvalho Chehab {
55519974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
55619974710SMauro Carvalho Chehab 
557d6dd77ebSTony Luck 	return sprintf(data, "%s\n", edac_mem_types[dimm->mtype]);
55819974710SMauro Carvalho Chehab }
55919974710SMauro Carvalho Chehab 
dimmdev_dev_type_show(struct device * dev,struct device_attribute * mattr,char * data)56019974710SMauro Carvalho Chehab static ssize_t dimmdev_dev_type_show(struct device *dev,
56119974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
56219974710SMauro Carvalho Chehab {
56319974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
56419974710SMauro Carvalho Chehab 
56519974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[dimm->dtype]);
56619974710SMauro Carvalho Chehab }
56719974710SMauro Carvalho Chehab 
dimmdev_edac_mode_show(struct device * dev,struct device_attribute * mattr,char * data)56819974710SMauro Carvalho Chehab static ssize_t dimmdev_edac_mode_show(struct device *dev,
56919974710SMauro Carvalho Chehab 				      struct device_attribute *mattr,
57019974710SMauro Carvalho Chehab 				      char *data)
57119974710SMauro Carvalho Chehab {
57219974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
57319974710SMauro Carvalho Chehab 
57419974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
57519974710SMauro Carvalho Chehab }
57619974710SMauro Carvalho Chehab 
dimmdev_ce_count_show(struct device * dev,struct device_attribute * mattr,char * data)5774fb6fde7SAaron Miller static ssize_t dimmdev_ce_count_show(struct device *dev,
5784fb6fde7SAaron Miller 				      struct device_attribute *mattr,
5794fb6fde7SAaron Miller 				      char *data)
5804fb6fde7SAaron Miller {
5814fb6fde7SAaron Miller 	struct dimm_info *dimm = to_dimm(dev);
5824fb6fde7SAaron Miller 
5834aa92c86SRobert Richter 	return sprintf(data, "%u\n", dimm->ce_count);
5844fb6fde7SAaron Miller }
5854fb6fde7SAaron Miller 
dimmdev_ue_count_show(struct device * dev,struct device_attribute * mattr,char * data)5864fb6fde7SAaron Miller static ssize_t dimmdev_ue_count_show(struct device *dev,
5874fb6fde7SAaron Miller 				      struct device_attribute *mattr,
5884fb6fde7SAaron Miller 				      char *data)
5894fb6fde7SAaron Miller {
5904fb6fde7SAaron Miller 	struct dimm_info *dimm = to_dimm(dev);
5914fb6fde7SAaron Miller 
5924aa92c86SRobert Richter 	return sprintf(data, "%u\n", dimm->ue_count);
5934fb6fde7SAaron Miller }
5944fb6fde7SAaron Miller 
59519974710SMauro Carvalho Chehab /* dimm/rank attribute files */
59619974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
59719974710SMauro Carvalho Chehab 		   dimmdev_label_show, dimmdev_label_store);
59819974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
59919974710SMauro Carvalho Chehab static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
60019974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
60119974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
60219974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
6034fb6fde7SAaron Miller static DEVICE_ATTR(dimm_ce_count, S_IRUGO, dimmdev_ce_count_show, NULL);
6044fb6fde7SAaron Miller static DEVICE_ATTR(dimm_ue_count, S_IRUGO, dimmdev_ue_count_show, NULL);
60519974710SMauro Carvalho Chehab 
60619974710SMauro Carvalho Chehab /* attributes of the dimm<id>/rank<id> object */
60719974710SMauro Carvalho Chehab static struct attribute *dimm_attrs[] = {
60819974710SMauro Carvalho Chehab 	&dev_attr_dimm_label.attr,
60919974710SMauro Carvalho Chehab 	&dev_attr_dimm_location.attr,
61019974710SMauro Carvalho Chehab 	&dev_attr_size.attr,
61119974710SMauro Carvalho Chehab 	&dev_attr_dimm_mem_type.attr,
61219974710SMauro Carvalho Chehab 	&dev_attr_dimm_dev_type.attr,
61319974710SMauro Carvalho Chehab 	&dev_attr_dimm_edac_mode.attr,
6144fb6fde7SAaron Miller 	&dev_attr_dimm_ce_count.attr,
6154fb6fde7SAaron Miller 	&dev_attr_dimm_ue_count.attr,
61619974710SMauro Carvalho Chehab 	NULL,
61719974710SMauro Carvalho Chehab };
61819974710SMauro Carvalho Chehab 
6191c18be5aSArvind Yadav static const struct attribute_group dimm_attr_grp = {
62019974710SMauro Carvalho Chehab 	.attrs	= dimm_attrs,
62119974710SMauro Carvalho Chehab };
62219974710SMauro Carvalho Chehab 
62319974710SMauro Carvalho Chehab static const struct attribute_group *dimm_attr_groups[] = {
62419974710SMauro Carvalho Chehab 	&dimm_attr_grp,
62519974710SMauro Carvalho Chehab 	NULL
62619974710SMauro Carvalho Chehab };
62719974710SMauro Carvalho Chehab 
628b2b3e736SBhumika Goyal static const struct device_type dimm_attr_type = {
62919974710SMauro Carvalho Chehab 	.groups		= dimm_attr_groups,
63019974710SMauro Carvalho Chehab };
63119974710SMauro Carvalho Chehab 
dimm_release(struct device * dev)632bea1bfd5SRobert Richter static void dimm_release(struct device *dev)
633bea1bfd5SRobert Richter {
634bea1bfd5SRobert Richter 	/*
635bea1bfd5SRobert Richter 	 * Nothing to do, just unregister sysfs here. The mci
636bea1bfd5SRobert Richter 	 * device owns the data and will also release it.
637bea1bfd5SRobert Richter 	 */
638bea1bfd5SRobert Richter }
639bea1bfd5SRobert Richter 
64019974710SMauro Carvalho Chehab /* Create a DIMM object under specifed memory controller device */
edac_create_dimm_object(struct mem_ctl_info * mci,struct dimm_info * dimm)64119974710SMauro Carvalho Chehab static int edac_create_dimm_object(struct mem_ctl_info *mci,
642c498afafSRobert Richter 				   struct dimm_info *dimm)
64319974710SMauro Carvalho Chehab {
64419974710SMauro Carvalho Chehab 	int err;
64519974710SMauro Carvalho Chehab 	dimm->mci = mci;
64619974710SMauro Carvalho Chehab 
64719974710SMauro Carvalho Chehab 	dimm->dev.type = &dimm_attr_type;
648bea1bfd5SRobert Richter 	dimm->dev.release = dimm_release;
64919974710SMauro Carvalho Chehab 	device_initialize(&dimm->dev);
65019974710SMauro Carvalho Chehab 
65119974710SMauro Carvalho Chehab 	dimm->dev.parent = &mci->dev;
6529713faecSMauro Carvalho Chehab 	if (mci->csbased)
653c498afafSRobert Richter 		dev_set_name(&dimm->dev, "rank%d", dimm->idx);
65419974710SMauro Carvalho Chehab 	else
655c498afafSRobert Richter 		dev_set_name(&dimm->dev, "dimm%d", dimm->idx);
65619974710SMauro Carvalho Chehab 	dev_set_drvdata(&dimm->dev, dimm);
65719974710SMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
65819974710SMauro Carvalho Chehab 
65919974710SMauro Carvalho Chehab 	err = device_add(&dimm->dev);
660e701f412SRobert Richter 	if (err) {
661e701f412SRobert Richter 		edac_dbg(1, "failure: create device %s\n", dev_name(&dimm->dev));
6627adc05d2SGreg KH 		put_device(&dimm->dev);
66319974710SMauro Carvalho Chehab 		return err;
66419974710SMauro Carvalho Chehab 	}
6657a623c03SMauro Carvalho Chehab 
666e701f412SRobert Richter 	if (IS_ENABLED(CONFIG_EDAC_DEBUG)) {
667e701f412SRobert Richter 		char location[80];
668e701f412SRobert Richter 
669e701f412SRobert Richter 		edac_dimm_info_location(dimm, location, sizeof(location));
670e701f412SRobert Richter 		edac_dbg(0, "device %s created at location %s\n",
671e701f412SRobert Richter 			dev_name(&dimm->dev), location);
672e701f412SRobert Richter 	}
673e701f412SRobert Richter 
674e701f412SRobert Richter 	return 0;
675e701f412SRobert Richter }
676e701f412SRobert Richter 
6777a623c03SMauro Carvalho Chehab /*
6787a623c03SMauro Carvalho Chehab  * Memory controller device
6797a623c03SMauro Carvalho Chehab  */
6807a623c03SMauro Carvalho Chehab 
6817a623c03SMauro Carvalho Chehab #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
6827a623c03SMauro Carvalho Chehab 
mci_reset_counters_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)6837a623c03SMauro Carvalho Chehab static ssize_t mci_reset_counters_store(struct device *dev,
6847a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
6857c9281d7SDouglas Thompson 					const char *data, size_t count)
6867c9281d7SDouglas Thompson {
6877a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
6884aa92c86SRobert Richter 	struct dimm_info *dimm;
6894aa92c86SRobert Richter 	int row, chan;
6904aa92c86SRobert Richter 
6915926ff50SMauro Carvalho Chehab 	mci->ue_mc = 0;
6925926ff50SMauro Carvalho Chehab 	mci->ce_mc = 0;
6937a623c03SMauro Carvalho Chehab 	mci->ue_noinfo_count = 0;
6947a623c03SMauro Carvalho Chehab 	mci->ce_noinfo_count = 0;
6957c9281d7SDouglas Thompson 
6967c9281d7SDouglas Thompson 	for (row = 0; row < mci->nr_csrows; row++) {
697de3910ebSMauro Carvalho Chehab 		struct csrow_info *ri = mci->csrows[row];
6987c9281d7SDouglas Thompson 
6997c9281d7SDouglas Thompson 		ri->ue_count = 0;
7007c9281d7SDouglas Thompson 		ri->ce_count = 0;
7017c9281d7SDouglas Thompson 
7027c9281d7SDouglas Thompson 		for (chan = 0; chan < ri->nr_channels; chan++)
703de3910ebSMauro Carvalho Chehab 			ri->channels[chan]->ce_count = 0;
7047c9281d7SDouglas Thompson 	}
7057c9281d7SDouglas Thompson 
7064aa92c86SRobert Richter 	mci_for_each_dimm(mci, dimm) {
7074aa92c86SRobert Richter 		dimm->ue_count = 0;
7084aa92c86SRobert Richter 		dimm->ce_count = 0;
7097a623c03SMauro Carvalho Chehab 	}
7107a623c03SMauro Carvalho Chehab 
7117c9281d7SDouglas Thompson 	mci->start_time = jiffies;
7127c9281d7SDouglas Thompson 	return count;
7137c9281d7SDouglas Thompson }
7147c9281d7SDouglas Thompson 
71539094443SBorislav Petkov /* Memory scrubbing interface:
71639094443SBorislav Petkov  *
71739094443SBorislav Petkov  * A MC driver can limit the scrubbing bandwidth based on the CPU type.
71839094443SBorislav Petkov  * Therefore, ->set_sdram_scrub_rate should be made to return the actual
71939094443SBorislav Petkov  * bandwidth that is accepted or 0 when scrubbing is to be disabled.
72039094443SBorislav Petkov  *
72139094443SBorislav Petkov  * Negative value still means that an error has occurred while setting
72239094443SBorislav Petkov  * the scrub rate.
72339094443SBorislav Petkov  */
mci_sdram_scrub_rate_store(struct device * dev,struct device_attribute * mattr,const char * data,size_t count)7247a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
7257a623c03SMauro Carvalho Chehab 					  struct device_attribute *mattr,
7267c9281d7SDouglas Thompson 					  const char *data, size_t count)
7277c9281d7SDouglas Thompson {
7287a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
729eba042a8SBorislav Petkov 	unsigned long bandwidth = 0;
73039094443SBorislav Petkov 	int new_bw = 0;
7317c9281d7SDouglas Thompson 
732c7f62fc8SJingoo Han 	if (kstrtoul(data, 10, &bandwidth) < 0)
733eba042a8SBorislav Petkov 		return -EINVAL;
734eba042a8SBorislav Petkov 
73539094443SBorislav Petkov 	new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
7364949603aSMarkus Trippelsdorf 	if (new_bw < 0) {
7374949603aSMarkus Trippelsdorf 		edac_printk(KERN_WARNING, EDAC_MC,
7384949603aSMarkus Trippelsdorf 			    "Error setting scrub rate to: %lu\n", bandwidth);
7394949603aSMarkus Trippelsdorf 		return -EINVAL;
7407c9281d7SDouglas Thompson 	}
7417c9281d7SDouglas Thompson 
7424949603aSMarkus Trippelsdorf 	return count;
7437c9281d7SDouglas Thompson }
744eba042a8SBorislav Petkov 
74539094443SBorislav Petkov /*
74639094443SBorislav Petkov  * ->get_sdram_scrub_rate() return value semantics same as above.
74739094443SBorislav Petkov  */
mci_sdram_scrub_rate_show(struct device * dev,struct device_attribute * mattr,char * data)7487a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
7497a623c03SMauro Carvalho Chehab 					 struct device_attribute *mattr,
7507a623c03SMauro Carvalho Chehab 					 char *data)
75139094443SBorislav Petkov {
7527a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
75339094443SBorislav Petkov 	int bandwidth = 0;
75439094443SBorislav Petkov 
75539094443SBorislav Petkov 	bandwidth = mci->get_sdram_scrub_rate(mci);
75639094443SBorislav Petkov 	if (bandwidth < 0) {
757eba042a8SBorislav Petkov 		edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n");
75839094443SBorislav Petkov 		return bandwidth;
759eba042a8SBorislav Petkov 	}
76039094443SBorislav Petkov 
7617c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", bandwidth);
7627c9281d7SDouglas Thompson }
7637c9281d7SDouglas Thompson 
7647c9281d7SDouglas Thompson /* default attribute files for the MCI object */
mci_ue_count_show(struct device * dev,struct device_attribute * mattr,char * data)7657a623c03SMauro Carvalho Chehab static ssize_t mci_ue_count_show(struct device *dev,
7667a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7677a623c03SMauro Carvalho Chehab 				 char *data)
7687c9281d7SDouglas Thompson {
7697a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7707a623c03SMauro Carvalho Chehab 
77134417f27SEric Badger 	return sprintf(data, "%u\n", mci->ue_mc);
7727c9281d7SDouglas Thompson }
7737c9281d7SDouglas Thompson 
mci_ce_count_show(struct device * dev,struct device_attribute * mattr,char * data)7747a623c03SMauro Carvalho Chehab static ssize_t mci_ce_count_show(struct device *dev,
7757a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7767a623c03SMauro Carvalho Chehab 				 char *data)
7777c9281d7SDouglas Thompson {
7787a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7797a623c03SMauro Carvalho Chehab 
78034417f27SEric Badger 	return sprintf(data, "%u\n", mci->ce_mc);
7817c9281d7SDouglas Thompson }
7827c9281d7SDouglas Thompson 
mci_ce_noinfo_show(struct device * dev,struct device_attribute * mattr,char * data)7837a623c03SMauro Carvalho Chehab static ssize_t mci_ce_noinfo_show(struct device *dev,
7847a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7857a623c03SMauro Carvalho Chehab 				  char *data)
7867c9281d7SDouglas Thompson {
7877a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7887a623c03SMauro Carvalho Chehab 
78934417f27SEric Badger 	return sprintf(data, "%u\n", mci->ce_noinfo_count);
7907c9281d7SDouglas Thompson }
7917c9281d7SDouglas Thompson 
mci_ue_noinfo_show(struct device * dev,struct device_attribute * mattr,char * data)7927a623c03SMauro Carvalho Chehab static ssize_t mci_ue_noinfo_show(struct device *dev,
7937a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7947a623c03SMauro Carvalho Chehab 				  char *data)
7957c9281d7SDouglas Thompson {
7967a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7977a623c03SMauro Carvalho Chehab 
79834417f27SEric Badger 	return sprintf(data, "%u\n", mci->ue_noinfo_count);
7997c9281d7SDouglas Thompson }
8007c9281d7SDouglas Thompson 
mci_seconds_show(struct device * dev,struct device_attribute * mattr,char * data)8017a623c03SMauro Carvalho Chehab static ssize_t mci_seconds_show(struct device *dev,
8027a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
8037a623c03SMauro Carvalho Chehab 				char *data)
8047c9281d7SDouglas Thompson {
8057a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
8067a623c03SMauro Carvalho Chehab 
8077c9281d7SDouglas Thompson 	return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
8087c9281d7SDouglas Thompson }
8097c9281d7SDouglas Thompson 
mci_ctl_name_show(struct device * dev,struct device_attribute * mattr,char * data)8107a623c03SMauro Carvalho Chehab static ssize_t mci_ctl_name_show(struct device *dev,
8117a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
8127a623c03SMauro Carvalho Chehab 				 char *data)
8137c9281d7SDouglas Thompson {
8147a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
8157a623c03SMauro Carvalho Chehab 
8167c9281d7SDouglas Thompson 	return sprintf(data, "%s\n", mci->ctl_name);
8177c9281d7SDouglas Thompson }
8187c9281d7SDouglas Thompson 
mci_size_mb_show(struct device * dev,struct device_attribute * mattr,char * data)8197a623c03SMauro Carvalho Chehab static ssize_t mci_size_mb_show(struct device *dev,
8207a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
8217a623c03SMauro Carvalho Chehab 				char *data)
8227c9281d7SDouglas Thompson {
8237a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
824a895bf8bSMauro Carvalho Chehab 	int total_pages = 0, csrow_idx, j;
8257c9281d7SDouglas Thompson 
826a895bf8bSMauro Carvalho Chehab 	for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
827de3910ebSMauro Carvalho Chehab 		struct csrow_info *csrow = mci->csrows[csrow_idx];
8287c9281d7SDouglas Thompson 
829a895bf8bSMauro Carvalho Chehab 		for (j = 0; j < csrow->nr_channels; j++) {
830de3910ebSMauro Carvalho Chehab 			struct dimm_info *dimm = csrow->channels[j]->dimm;
8317c9281d7SDouglas Thompson 
832a895bf8bSMauro Carvalho Chehab 			total_pages += dimm->nr_pages;
833a895bf8bSMauro Carvalho Chehab 		}
8347c9281d7SDouglas Thompson 	}
8357c9281d7SDouglas Thompson 
8367c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
8377c9281d7SDouglas Thompson }
8387c9281d7SDouglas Thompson 
mci_max_location_show(struct device * dev,struct device_attribute * mattr,char * data)8398ad6c78aSMauro Carvalho Chehab static ssize_t mci_max_location_show(struct device *dev,
8408ad6c78aSMauro Carvalho Chehab 				     struct device_attribute *mattr,
8418ad6c78aSMauro Carvalho Chehab 				     char *data)
8428ad6c78aSMauro Carvalho Chehab {
8438ad6c78aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
844e6bbde8bSXiongfeng Wang 	int len = PAGE_SIZE;
8458ad6c78aSMauro Carvalho Chehab 	char *p = data;
846e6bbde8bSXiongfeng Wang 	int i, n;
8478ad6c78aSMauro Carvalho Chehab 
8488ad6c78aSMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
849e6bbde8bSXiongfeng Wang 		n = scnprintf(p, len, "%s %d ",
8508ad6c78aSMauro Carvalho Chehab 			      edac_layer_name[mci->layers[i].type],
8518ad6c78aSMauro Carvalho Chehab 			      mci->layers[i].size - 1);
852e6bbde8bSXiongfeng Wang 		len -= n;
853e6bbde8bSXiongfeng Wang 		if (len <= 0)
854e6bbde8bSXiongfeng Wang 			goto out;
855e6bbde8bSXiongfeng Wang 
856e6bbde8bSXiongfeng Wang 		p += n;
8578ad6c78aSMauro Carvalho Chehab 	}
8588ad6c78aSMauro Carvalho Chehab 
859e6bbde8bSXiongfeng Wang 	p += scnprintf(p, len, "\n");
860e6bbde8bSXiongfeng Wang out:
8618ad6c78aSMauro Carvalho Chehab 	return p - data;
8628ad6c78aSMauro Carvalho Chehab }
8638ad6c78aSMauro Carvalho Chehab 
8647c9281d7SDouglas Thompson /* default Control file */
865f11135d8SBorislav Petkov static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
8667c9281d7SDouglas Thompson 
8677c9281d7SDouglas Thompson /* default Attribute files */
868f11135d8SBorislav Petkov static DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
869f11135d8SBorislav Petkov static DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
870f11135d8SBorislav Petkov static DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
871f11135d8SBorislav Petkov static DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
872f11135d8SBorislav Petkov static DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
873f11135d8SBorislav Petkov static DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
874f11135d8SBorislav Petkov static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
875f11135d8SBorislav Petkov static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
8767c9281d7SDouglas Thompson 
8777c9281d7SDouglas Thompson /* memory scrubber attribute file */
878628ea92fSBen Dooks static DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show,
8792c1946b6STakashi Iwai 	    mci_sdram_scrub_rate_store); /* umode set later in is_visible */
8807c9281d7SDouglas Thompson 
8817a623c03SMauro Carvalho Chehab static struct attribute *mci_attrs[] = {
8827a623c03SMauro Carvalho Chehab 	&dev_attr_reset_counters.attr,
8837a623c03SMauro Carvalho Chehab 	&dev_attr_mc_name.attr,
8847a623c03SMauro Carvalho Chehab 	&dev_attr_size_mb.attr,
8857a623c03SMauro Carvalho Chehab 	&dev_attr_seconds_since_reset.attr,
8867a623c03SMauro Carvalho Chehab 	&dev_attr_ue_noinfo_count.attr,
8877a623c03SMauro Carvalho Chehab 	&dev_attr_ce_noinfo_count.attr,
8887a623c03SMauro Carvalho Chehab 	&dev_attr_ue_count.attr,
8897a623c03SMauro Carvalho Chehab 	&dev_attr_ce_count.attr,
8908ad6c78aSMauro Carvalho Chehab 	&dev_attr_max_location.attr,
8912c1946b6STakashi Iwai 	&dev_attr_sdram_scrub_rate.attr,
8927c9281d7SDouglas Thompson 	NULL
8937c9281d7SDouglas Thompson };
8947c9281d7SDouglas Thompson 
mci_attr_is_visible(struct kobject * kobj,struct attribute * attr,int idx)8952c1946b6STakashi Iwai static umode_t mci_attr_is_visible(struct kobject *kobj,
8962c1946b6STakashi Iwai 				   struct attribute *attr, int idx)
8972c1946b6STakashi Iwai {
8982c1946b6STakashi Iwai 	struct device *dev = kobj_to_dev(kobj);
8992c1946b6STakashi Iwai 	struct mem_ctl_info *mci = to_mci(dev);
9002c1946b6STakashi Iwai 	umode_t mode = 0;
9012c1946b6STakashi Iwai 
9022c1946b6STakashi Iwai 	if (attr != &dev_attr_sdram_scrub_rate.attr)
9032c1946b6STakashi Iwai 		return attr->mode;
9042c1946b6STakashi Iwai 	if (mci->get_sdram_scrub_rate)
9052c1946b6STakashi Iwai 		mode |= S_IRUGO;
9062c1946b6STakashi Iwai 	if (mci->set_sdram_scrub_rate)
9072c1946b6STakashi Iwai 		mode |= S_IWUSR;
9082c1946b6STakashi Iwai 	return mode;
9092c1946b6STakashi Iwai }
9102c1946b6STakashi Iwai 
9111c18be5aSArvind Yadav static const struct attribute_group mci_attr_grp = {
9127a623c03SMauro Carvalho Chehab 	.attrs	= mci_attrs,
9132c1946b6STakashi Iwai 	.is_visible = mci_attr_is_visible,
9147c9281d7SDouglas Thompson };
9157c9281d7SDouglas Thompson 
9167a623c03SMauro Carvalho Chehab static const struct attribute_group *mci_attr_groups[] = {
9177a623c03SMauro Carvalho Chehab 	&mci_attr_grp,
9187a623c03SMauro Carvalho Chehab 	NULL
919cc301b3aSMauro Carvalho Chehab };
920cc301b3aSMauro Carvalho Chehab 
921b2b3e736SBhumika Goyal static const struct device_type mci_attr_type = {
9227a623c03SMauro Carvalho Chehab 	.groups		= mci_attr_groups,
923cc301b3aSMauro Carvalho Chehab };
924cc301b3aSMauro Carvalho Chehab 
9257c9281d7SDouglas Thompson /*
9267c9281d7SDouglas Thompson  * Create a new Memory Controller kobject instance,
9277c9281d7SDouglas Thompson  *	mc<id> under the 'mc' directory
9287c9281d7SDouglas Thompson  *
9297c9281d7SDouglas Thompson  * Return:
9307c9281d7SDouglas Thompson  *	0	Success
9317c9281d7SDouglas Thompson  *	!0	Failure
9327c9281d7SDouglas Thompson  */
edac_create_sysfs_mci_device(struct mem_ctl_info * mci,const struct attribute_group ** groups)9334e8d230dSTakashi Iwai int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
9344e8d230dSTakashi Iwai 				 const struct attribute_group **groups)
9357c9281d7SDouglas Thompson {
936c498afafSRobert Richter 	struct dimm_info *dimm;
937c498afafSRobert Richter 	int err;
9387c9281d7SDouglas Thompson 
939de3910ebSMauro Carvalho Chehab 	/* get the /sys/devices/system/edac subsys reference */
940de3910ebSMauro Carvalho Chehab 	mci->dev.type = &mci_attr_type;
941de3910ebSMauro Carvalho Chehab 	mci->dev.parent = mci_pdev;
9424e8d230dSTakashi Iwai 	mci->dev.groups = groups;
943de3910ebSMauro Carvalho Chehab 	dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
944de3910ebSMauro Carvalho Chehab 	dev_set_drvdata(&mci->dev, mci);
945de3910ebSMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
946de3910ebSMauro Carvalho Chehab 
9477a623c03SMauro Carvalho Chehab 	err = device_add(&mci->dev);
9487a623c03SMauro Carvalho Chehab 	if (err < 0) {
9493d958823SMauro Carvalho Chehab 		edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev));
950bea1bfd5SRobert Richter 		/* no put_device() here, free mci with _edac_mc_free() */
951644110e1SRobert Richter 		return err;
9528096cfafSDoug Thompson 	}
95342a8e397SDouglas Thompson 
954e701f412SRobert Richter 	edac_dbg(0, "device %s created\n", dev_name(&mci->dev));
955e701f412SRobert Richter 
9567a623c03SMauro Carvalho Chehab 	/*
9577a623c03SMauro Carvalho Chehab 	 * Create the dimm/rank devices
9587c9281d7SDouglas Thompson 	 */
959c498afafSRobert Richter 	mci_for_each_dimm(mci, dimm) {
9607a623c03SMauro Carvalho Chehab 		/* Only expose populated DIMMs */
9611bf1950cSJunjie Mao 		if (!dimm->nr_pages)
9627a623c03SMauro Carvalho Chehab 			continue;
9631bf1950cSJunjie Mao 
964c498afafSRobert Richter 		err = edac_create_dimm_object(mci, dimm);
965e701f412SRobert Richter 		if (err)
966bea1bfd5SRobert Richter 			goto fail;
96719974710SMauro Carvalho Chehab 	}
9687a623c03SMauro Carvalho Chehab 
96919974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
9707a623c03SMauro Carvalho Chehab 	err = edac_create_csrow_objects(mci);
9717a623c03SMauro Carvalho Chehab 	if (err < 0)
972bea1bfd5SRobert Richter 		goto fail;
97319974710SMauro Carvalho Chehab #endif
9747c9281d7SDouglas Thompson 
9757ac8bf9bSBorislav Petkov 	edac_create_debugfs_nodes(mci);
9767c9281d7SDouglas Thompson 	return 0;
9777c9281d7SDouglas Thompson 
978bea1bfd5SRobert Richter fail:
979bea1bfd5SRobert Richter 	edac_remove_sysfs_mci_device(mci);
98012e26969SBorislav Petkov 
9817c9281d7SDouglas Thompson 	return err;
9827c9281d7SDouglas Thompson }
9837c9281d7SDouglas Thompson 
9847c9281d7SDouglas Thompson /*
9857c9281d7SDouglas Thompson  * remove a Memory Controller instance
9867c9281d7SDouglas Thompson  */
edac_remove_sysfs_mci_device(struct mem_ctl_info * mci)9877c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
9887c9281d7SDouglas Thompson {
989c498afafSRobert Richter 	struct dimm_info *dimm;
9907c9281d7SDouglas Thompson 
991bea1bfd5SRobert Richter 	if (!device_is_registered(&mci->dev))
992bea1bfd5SRobert Richter 		return;
993bea1bfd5SRobert Richter 
994956b9ba1SJoe Perches 	edac_dbg(0, "\n");
9957c9281d7SDouglas Thompson 
996452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
99730f84a89STan Xiaojun 	edac_debugfs_remove_recursive(mci->debugfs);
998452a6bf9SMauro Carvalho Chehab #endif
99919974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
10007a623c03SMauro Carvalho Chehab 	edac_delete_csrow_objects(mci);
100119974710SMauro Carvalho Chehab #endif
1002a895bf8bSMauro Carvalho Chehab 
1003c498afafSRobert Richter 	mci_for_each_dimm(mci, dimm) {
1004bea1bfd5SRobert Richter 		if (!device_is_registered(&dimm->dev))
10057a623c03SMauro Carvalho Chehab 			continue;
1006e701f412SRobert Richter 		edac_dbg(1, "unregistering device %s\n", dev_name(&dimm->dev));
100744d22e24SLans Zhang 		device_unregister(&dimm->dev);
10087c9281d7SDouglas Thompson 	}
10097c9281d7SDouglas Thompson 
1010bea1bfd5SRobert Richter 	/* only remove the device, but keep mci */
1011bea1bfd5SRobert Richter 	device_del(&mci->dev);
10127a623c03SMauro Carvalho Chehab }
10138096cfafSDoug Thompson 
mc_attr_release(struct device * dev)1014de3910ebSMauro Carvalho Chehab static void mc_attr_release(struct device *dev)
10157a623c03SMauro Carvalho Chehab {
1016de3910ebSMauro Carvalho Chehab 	/*
1017de3910ebSMauro Carvalho Chehab 	 * There's no container structure here, as this is just the mci
1018de3910ebSMauro Carvalho Chehab 	 * parent device, used to create the /sys/devices/mc sysfs node.
1019de3910ebSMauro Carvalho Chehab 	 * So, there are no attributes on it.
1020de3910ebSMauro Carvalho Chehab 	 */
1021e701f412SRobert Richter 	edac_dbg(1, "device %s released\n", dev_name(dev));
1022de3910ebSMauro Carvalho Chehab 	kfree(dev);
10237a623c03SMauro Carvalho Chehab }
10247a623c03SMauro Carvalho Chehab 
10257a623c03SMauro Carvalho Chehab /*
10267a623c03SMauro Carvalho Chehab  * Init/exit code for the module. Basically, creates/removes /sys/class/rc
10277a623c03SMauro Carvalho Chehab  */
edac_mc_sysfs_init(void)10287a623c03SMauro Carvalho Chehab int __init edac_mc_sysfs_init(void)
10297a623c03SMauro Carvalho Chehab {
10307a623c03SMauro Carvalho Chehab 	int err;
10318096cfafSDoug Thompson 
1032de3910ebSMauro Carvalho Chehab 	mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
1033644110e1SRobert Richter 	if (!mci_pdev)
1034644110e1SRobert Richter 		return -ENOMEM;
10358096cfafSDoug Thompson 
1036d4538000SBorislav Petkov 	mci_pdev->bus = edac_get_sysfs_subsys();
1037bea1bfd5SRobert Richter 	mci_pdev->release = mc_attr_release;
1038bea1bfd5SRobert Richter 	mci_pdev->init_name = "mc";
1039de3910ebSMauro Carvalho Chehab 
1040bea1bfd5SRobert Richter 	err = device_register(mci_pdev);
1041644110e1SRobert Richter 	if (err < 0) {
1042e701f412SRobert Richter 		edac_dbg(1, "failure: create device %s\n", dev_name(mci_pdev));
1043644110e1SRobert Richter 		put_device(mci_pdev);
1044644110e1SRobert Richter 		return err;
1045644110e1SRobert Richter 	}
10468096cfafSDoug Thompson 
1047956b9ba1SJoe Perches 	edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
1048de3910ebSMauro Carvalho Chehab 
10498096cfafSDoug Thompson 	return 0;
10508096cfafSDoug Thompson }
10518096cfafSDoug Thompson 
edac_mc_sysfs_exit(void)1052c6b97bcfSAlexey Khoroshilov void edac_mc_sysfs_exit(void)
10538096cfafSDoug Thompson {
105444d22e24SLans Zhang 	device_unregister(mci_pdev);
10558096cfafSDoug Thompson }
1056