xref: /openbmc/linux/drivers/edac/edac_mc_sysfs.c (revision 585fb3d9)
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;
294de78c68SDave Jiang static int edac_mc_poll_msec = 1000;
307c9281d7SDouglas Thompson 
317c9281d7SDouglas Thompson /* Getter functions for above */
324de78c68SDave Jiang int edac_mc_get_log_ue(void)
337c9281d7SDouglas Thompson {
344de78c68SDave Jiang 	return edac_mc_log_ue;
357c9281d7SDouglas Thompson }
367c9281d7SDouglas Thompson 
374de78c68SDave Jiang int edac_mc_get_log_ce(void)
387c9281d7SDouglas Thompson {
394de78c68SDave Jiang 	return edac_mc_log_ce;
407c9281d7SDouglas Thompson }
417c9281d7SDouglas Thompson 
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 */
4881d87cb1SDave Jiang int edac_mc_get_poll_msec(void)
4981d87cb1SDave Jiang {
504de78c68SDave Jiang 	return edac_mc_poll_msec;
517c9281d7SDouglas Thompson }
527c9281d7SDouglas Thompson 
53e4dca7b7SKees Cook static int edac_set_poll_msec(const char *val, const struct kernel_param *kp)
54096846e2SArthur Jones {
559da21b15SBorislav Petkov 	unsigned long l;
56096846e2SArthur Jones 	int ret;
57096846e2SArthur Jones 
58096846e2SArthur Jones 	if (!val)
59096846e2SArthur Jones 		return -EINVAL;
60096846e2SArthur Jones 
619da21b15SBorislav Petkov 	ret = kstrtoul(val, 0, &l);
62c542b53dSJingoo Han 	if (ret)
63c542b53dSJingoo Han 		return ret;
649da21b15SBorislav Petkov 
659da21b15SBorislav Petkov 	if (l < 1000)
66096846e2SArthur Jones 		return -EINVAL;
679da21b15SBorislav Petkov 
689da21b15SBorislav Petkov 	*((unsigned long *)kp->arg) = l;
69096846e2SArthur Jones 
70096846e2SArthur Jones 	/* notify edac_mc engine to reset the poll period */
71096846e2SArthur Jones 	edac_mc_reset_delay_period(l);
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");
85096846e2SArthur Jones module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int,
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;
1347a623c03SMauro Carvalho Chehab 	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 */
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 
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 
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 
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 
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 
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 */
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);
2037a623c03SMauro Carvalho Chehab 	unsigned 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 
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);
2197a623c03SMauro Carvalho Chehab 	unsigned 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 */
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);
2437a623c03SMauro Carvalho Chehab 	unsigned 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 
277de3910ebSMauro Carvalho Chehab static void csrow_attr_release(struct device *dev)
2787a623c03SMauro Carvalho Chehab {
279de3910ebSMauro Carvalho Chehab 	struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
280de3910ebSMauro Carvalho Chehab 
281956b9ba1SJoe Perches 	edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
282de3910ebSMauro Carvalho Chehab 	kfree(csrow);
2837a623c03SMauro Carvalho Chehab }
2847a623c03SMauro Carvalho Chehab 
285b2b3e736SBhumika Goyal static const struct device_type csrow_attr_type = {
2867a623c03SMauro Carvalho Chehab 	.groups		= csrow_attr_groups,
2877a623c03SMauro Carvalho Chehab 	.release	= csrow_attr_release,
2887a623c03SMauro Carvalho Chehab };
2897a623c03SMauro Carvalho Chehab 
2907a623c03SMauro Carvalho Chehab /*
2917a623c03SMauro Carvalho Chehab  * possible dynamic channel DIMM Label attribute files
2927a623c03SMauro Carvalho Chehab  *
2937a623c03SMauro Carvalho Chehab  */
2947a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
2957a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 0);
2967a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
2977a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 1);
2987a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR,
2997a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 2);
3007a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR,
3017a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 3);
3027a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR,
3037a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 4);
3047a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR,
3057a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 5);
306bba14295SBorislav Petkov DEVICE_CHANNEL(ch6_dimm_label, S_IRUGO | S_IWUSR,
307bba14295SBorislav Petkov 	channel_dimm_label_show, channel_dimm_label_store, 6);
308bba14295SBorislav Petkov DEVICE_CHANNEL(ch7_dimm_label, S_IRUGO | S_IWUSR,
309bba14295SBorislav Petkov 	channel_dimm_label_show, channel_dimm_label_store, 7);
3107c9281d7SDouglas Thompson 
3117a623c03SMauro Carvalho Chehab /* Total possible dynamic DIMM Label attribute file table */
3122c1946b6STakashi Iwai static struct attribute *dynamic_csrow_dimm_attr[] = {
3132c1946b6STakashi Iwai 	&dev_attr_legacy_ch0_dimm_label.attr.attr,
3142c1946b6STakashi Iwai 	&dev_attr_legacy_ch1_dimm_label.attr.attr,
3152c1946b6STakashi Iwai 	&dev_attr_legacy_ch2_dimm_label.attr.attr,
3162c1946b6STakashi Iwai 	&dev_attr_legacy_ch3_dimm_label.attr.attr,
3172c1946b6STakashi Iwai 	&dev_attr_legacy_ch4_dimm_label.attr.attr,
3182c1946b6STakashi Iwai 	&dev_attr_legacy_ch5_dimm_label.attr.attr,
319bba14295SBorislav Petkov 	&dev_attr_legacy_ch6_dimm_label.attr.attr,
320bba14295SBorislav Petkov 	&dev_attr_legacy_ch7_dimm_label.attr.attr,
3212c1946b6STakashi Iwai 	NULL
3227a623c03SMauro Carvalho Chehab };
3237c9281d7SDouglas Thompson 
3247a623c03SMauro Carvalho Chehab /* possible dynamic channel ce_count attribute files */
325c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch0_ce_count, S_IRUGO,
3267a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 0);
327c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch1_ce_count, S_IRUGO,
3287a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 1);
329c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch2_ce_count, S_IRUGO,
3307a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 2);
331c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch3_ce_count, S_IRUGO,
3327a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 3);
333c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch4_ce_count, S_IRUGO,
3347a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 4);
335c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch5_ce_count, S_IRUGO,
3367a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 5);
337bba14295SBorislav Petkov DEVICE_CHANNEL(ch6_ce_count, S_IRUGO,
338bba14295SBorislav Petkov 		   channel_ce_count_show, NULL, 6);
339bba14295SBorislav Petkov DEVICE_CHANNEL(ch7_ce_count, S_IRUGO,
340bba14295SBorislav Petkov 		   channel_ce_count_show, NULL, 7);
3417c9281d7SDouglas Thompson 
3427a623c03SMauro Carvalho Chehab /* Total possible dynamic ce_count attribute file table */
3432c1946b6STakashi Iwai static struct attribute *dynamic_csrow_ce_count_attr[] = {
3442c1946b6STakashi Iwai 	&dev_attr_legacy_ch0_ce_count.attr.attr,
3452c1946b6STakashi Iwai 	&dev_attr_legacy_ch1_ce_count.attr.attr,
3462c1946b6STakashi Iwai 	&dev_attr_legacy_ch2_ce_count.attr.attr,
3472c1946b6STakashi Iwai 	&dev_attr_legacy_ch3_ce_count.attr.attr,
3482c1946b6STakashi Iwai 	&dev_attr_legacy_ch4_ce_count.attr.attr,
3492c1946b6STakashi Iwai 	&dev_attr_legacy_ch5_ce_count.attr.attr,
350bba14295SBorislav Petkov 	&dev_attr_legacy_ch6_ce_count.attr.attr,
351bba14295SBorislav Petkov 	&dev_attr_legacy_ch7_ce_count.attr.attr,
3522c1946b6STakashi Iwai 	NULL
3532c1946b6STakashi Iwai };
3542c1946b6STakashi Iwai 
3552c1946b6STakashi Iwai static umode_t csrow_dev_is_visible(struct kobject *kobj,
3562c1946b6STakashi Iwai 				    struct attribute *attr, int idx)
3572c1946b6STakashi Iwai {
3582c1946b6STakashi Iwai 	struct device *dev = kobj_to_dev(kobj);
3592c1946b6STakashi Iwai 	struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
3602c1946b6STakashi Iwai 
3612c1946b6STakashi Iwai 	if (idx >= csrow->nr_channels)
3622c1946b6STakashi Iwai 		return 0;
363bba14295SBorislav Petkov 
364bba14295SBorislav Petkov 	if (idx >= ARRAY_SIZE(dynamic_csrow_ce_count_attr) - 1) {
365bba14295SBorislav Petkov 		WARN_ONCE(1, "idx: %d\n", idx);
366bba14295SBorislav Petkov 		return 0;
367bba14295SBorislav Petkov 	}
368bba14295SBorislav Petkov 
3692c1946b6STakashi Iwai 	/* Only expose populated DIMMs */
3702c1946b6STakashi Iwai 	if (!csrow->channels[idx]->dimm->nr_pages)
3712c1946b6STakashi Iwai 		return 0;
372bba14295SBorislav Petkov 
3732c1946b6STakashi Iwai 	return attr->mode;
3742c1946b6STakashi Iwai }
3752c1946b6STakashi Iwai 
3762c1946b6STakashi Iwai 
3772c1946b6STakashi Iwai static const struct attribute_group csrow_dev_dimm_group = {
3782c1946b6STakashi Iwai 	.attrs = dynamic_csrow_dimm_attr,
3792c1946b6STakashi Iwai 	.is_visible = csrow_dev_is_visible,
3802c1946b6STakashi Iwai };
3812c1946b6STakashi Iwai 
3822c1946b6STakashi Iwai static const struct attribute_group csrow_dev_ce_count_group = {
3832c1946b6STakashi Iwai 	.attrs = dynamic_csrow_ce_count_attr,
3842c1946b6STakashi Iwai 	.is_visible = csrow_dev_is_visible,
3852c1946b6STakashi Iwai };
3862c1946b6STakashi Iwai 
3872c1946b6STakashi Iwai static const struct attribute_group *csrow_dev_groups[] = {
3882c1946b6STakashi Iwai 	&csrow_dev_dimm_group,
3892c1946b6STakashi Iwai 	&csrow_dev_ce_count_group,
3902c1946b6STakashi Iwai 	NULL
3917c9281d7SDouglas Thompson };
3927c9281d7SDouglas Thompson 
393e39f4ea9SMauro Carvalho Chehab static inline int nr_pages_per_csrow(struct csrow_info *csrow)
394e39f4ea9SMauro Carvalho Chehab {
395e39f4ea9SMauro Carvalho Chehab 	int chan, nr_pages = 0;
396e39f4ea9SMauro Carvalho Chehab 
397e39f4ea9SMauro Carvalho Chehab 	for (chan = 0; chan < csrow->nr_channels; chan++)
398de3910ebSMauro Carvalho Chehab 		nr_pages += csrow->channels[chan]->dimm->nr_pages;
399e39f4ea9SMauro Carvalho Chehab 
400e39f4ea9SMauro Carvalho Chehab 	return nr_pages;
401e39f4ea9SMauro Carvalho Chehab }
402e39f4ea9SMauro Carvalho Chehab 
4037c9281d7SDouglas Thompson /* Create a CSROW object under specifed edac_mc_device */
4048096cfafSDoug Thompson static int edac_create_csrow_object(struct mem_ctl_info *mci,
405079708b9SDouglas Thompson 				    struct csrow_info *csrow, int index)
4067c9281d7SDouglas Thompson {
407585fb3d9SPan Bian 	int err;
408585fb3d9SPan Bian 
4097a623c03SMauro Carvalho Chehab 	csrow->dev.type = &csrow_attr_type;
4102c1946b6STakashi Iwai 	csrow->dev.groups = csrow_dev_groups;
4117a623c03SMauro Carvalho Chehab 	device_initialize(&csrow->dev);
4127a623c03SMauro Carvalho Chehab 	csrow->dev.parent = &mci->dev;
413921a6899SBorislav Petkov 	csrow->mci = mci;
4147a623c03SMauro Carvalho Chehab 	dev_set_name(&csrow->dev, "csrow%d", index);
4157a623c03SMauro Carvalho Chehab 	dev_set_drvdata(&csrow->dev, csrow);
4167c9281d7SDouglas Thompson 
417956b9ba1SJoe Perches 	edac_dbg(0, "creating (virtual) csrow node %s\n",
418956b9ba1SJoe Perches 		 dev_name(&csrow->dev));
4198096cfafSDoug Thompson 
420585fb3d9SPan Bian 	err = device_add(&csrow->dev);
421585fb3d9SPan Bian 	if (err)
422585fb3d9SPan Bian 		put_device(&csrow->dev);
423585fb3d9SPan Bian 
424585fb3d9SPan Bian 	return err;
4257c9281d7SDouglas Thompson }
4267c9281d7SDouglas Thompson 
4277a623c03SMauro Carvalho Chehab /* Create a CSROW object under specifed edac_mc_device */
4287a623c03SMauro Carvalho Chehab static int edac_create_csrow_objects(struct mem_ctl_info *mci)
4297a623c03SMauro Carvalho Chehab {
4302c1946b6STakashi Iwai 	int err, i;
4317a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4327c9281d7SDouglas Thompson 
4337a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->nr_csrows; i++) {
434de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
435e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
436e39f4ea9SMauro Carvalho Chehab 			continue;
437de3910ebSMauro Carvalho Chehab 		err = edac_create_csrow_object(mci, mci->csrows[i], i);
4383d958823SMauro Carvalho Chehab 		if (err < 0) {
4393d958823SMauro Carvalho Chehab 			edac_dbg(1,
4403d958823SMauro Carvalho Chehab 				 "failure: create csrow objects for csrow %d\n",
4413d958823SMauro Carvalho Chehab 				 i);
4427a623c03SMauro Carvalho Chehab 			goto error;
4437a623c03SMauro Carvalho Chehab 		}
4443d958823SMauro Carvalho Chehab 	}
4457a623c03SMauro Carvalho Chehab 	return 0;
4467a623c03SMauro Carvalho Chehab 
4477a623c03SMauro Carvalho Chehab error:
4487a623c03SMauro Carvalho Chehab 	for (--i; i >= 0; i--) {
449de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
450e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
451e39f4ea9SMauro Carvalho Chehab 			continue;
452de3910ebSMauro Carvalho Chehab 		put_device(&mci->csrows[i]->dev);
4537a623c03SMauro Carvalho Chehab 	}
4547a623c03SMauro Carvalho Chehab 
4557a623c03SMauro Carvalho Chehab 	return err;
4567a623c03SMauro Carvalho Chehab }
4577a623c03SMauro Carvalho Chehab 
4587a623c03SMauro Carvalho Chehab static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
4597a623c03SMauro Carvalho Chehab {
4602c1946b6STakashi Iwai 	int i;
4617a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4627a623c03SMauro Carvalho Chehab 
4637a623c03SMauro Carvalho Chehab 	for (i = mci->nr_csrows - 1; i >= 0; i--) {
464de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
465e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
466e39f4ea9SMauro Carvalho Chehab 			continue;
46744d22e24SLans Zhang 		device_unregister(&mci->csrows[i]->dev);
4687a623c03SMauro Carvalho Chehab 	}
4697a623c03SMauro Carvalho Chehab }
47019974710SMauro Carvalho Chehab #endif
47119974710SMauro Carvalho Chehab 
47219974710SMauro Carvalho Chehab /*
47319974710SMauro Carvalho Chehab  * Per-dimm (or per-rank) devices
47419974710SMauro Carvalho Chehab  */
47519974710SMauro Carvalho Chehab 
47619974710SMauro Carvalho Chehab #define to_dimm(k) container_of(k, struct dimm_info, dev)
47719974710SMauro Carvalho Chehab 
47819974710SMauro Carvalho Chehab /* show/store functions for DIMM Label attributes */
47919974710SMauro Carvalho Chehab static ssize_t dimmdev_location_show(struct device *dev,
48019974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
48119974710SMauro Carvalho Chehab {
48219974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
48319974710SMauro Carvalho Chehab 
4846e84d359SMauro Carvalho Chehab 	return edac_dimm_info_location(dimm, data, PAGE_SIZE);
48519974710SMauro Carvalho Chehab }
48619974710SMauro Carvalho Chehab 
48719974710SMauro Carvalho Chehab static ssize_t dimmdev_label_show(struct device *dev,
48819974710SMauro Carvalho Chehab 				  struct device_attribute *mattr, char *data)
48919974710SMauro Carvalho Chehab {
49019974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
49119974710SMauro Carvalho Chehab 
49219974710SMauro Carvalho Chehab 	/* if field has not been initialized, there is nothing to send */
49319974710SMauro Carvalho Chehab 	if (!dimm->label[0])
49419974710SMauro Carvalho Chehab 		return 0;
49519974710SMauro Carvalho Chehab 
4961ea62c59SToshi Kani 	return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label);
49719974710SMauro Carvalho Chehab }
49819974710SMauro Carvalho Chehab 
49919974710SMauro Carvalho Chehab static ssize_t dimmdev_label_store(struct device *dev,
50019974710SMauro Carvalho Chehab 				   struct device_attribute *mattr,
50119974710SMauro Carvalho Chehab 				   const char *data,
50219974710SMauro Carvalho Chehab 				   size_t count)
50319974710SMauro Carvalho Chehab {
50419974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
505438470b8SToshi Kani 	size_t copy_count = count;
50619974710SMauro Carvalho Chehab 
507438470b8SToshi Kani 	if (count == 0)
508438470b8SToshi Kani 		return -EINVAL;
50919974710SMauro Carvalho Chehab 
510438470b8SToshi Kani 	if (data[count - 1] == '\0' || data[count - 1] == '\n')
511438470b8SToshi Kani 		copy_count -= 1;
51219974710SMauro Carvalho Chehab 
513d0c9c930SToshi Kani 	if (copy_count == 0 || copy_count >= sizeof(dimm->label))
514438470b8SToshi Kani 		return -EINVAL;
515438470b8SToshi Kani 
516438470b8SToshi Kani 	strncpy(dimm->label, data, copy_count);
517438470b8SToshi Kani 	dimm->label[copy_count] = '\0';
518438470b8SToshi Kani 
519438470b8SToshi Kani 	return count;
52019974710SMauro Carvalho Chehab }
52119974710SMauro Carvalho Chehab 
52219974710SMauro Carvalho Chehab static ssize_t dimmdev_size_show(struct device *dev,
52319974710SMauro Carvalho Chehab 				 struct device_attribute *mattr, char *data)
52419974710SMauro Carvalho Chehab {
52519974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
52619974710SMauro Carvalho Chehab 
52719974710SMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
52819974710SMauro Carvalho Chehab }
52919974710SMauro Carvalho Chehab 
53019974710SMauro Carvalho Chehab static ssize_t dimmdev_mem_type_show(struct device *dev,
53119974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
53219974710SMauro Carvalho Chehab {
53319974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
53419974710SMauro Carvalho Chehab 
535d6dd77ebSTony Luck 	return sprintf(data, "%s\n", edac_mem_types[dimm->mtype]);
53619974710SMauro Carvalho Chehab }
53719974710SMauro Carvalho Chehab 
53819974710SMauro Carvalho Chehab static ssize_t dimmdev_dev_type_show(struct device *dev,
53919974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
54019974710SMauro Carvalho Chehab {
54119974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
54219974710SMauro Carvalho Chehab 
54319974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[dimm->dtype]);
54419974710SMauro Carvalho Chehab }
54519974710SMauro Carvalho Chehab 
54619974710SMauro Carvalho Chehab static ssize_t dimmdev_edac_mode_show(struct device *dev,
54719974710SMauro Carvalho Chehab 				      struct device_attribute *mattr,
54819974710SMauro Carvalho Chehab 				      char *data)
54919974710SMauro Carvalho Chehab {
55019974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
55119974710SMauro Carvalho Chehab 
55219974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
55319974710SMauro Carvalho Chehab }
55419974710SMauro Carvalho Chehab 
5554fb6fde7SAaron Miller static ssize_t dimmdev_ce_count_show(struct device *dev,
5564fb6fde7SAaron Miller 				      struct device_attribute *mattr,
5574fb6fde7SAaron Miller 				      char *data)
5584fb6fde7SAaron Miller {
5594fb6fde7SAaron Miller 	struct dimm_info *dimm = to_dimm(dev);
5604fb6fde7SAaron Miller 	u32 count;
5614fb6fde7SAaron Miller 	int off;
5624fb6fde7SAaron Miller 
5634fb6fde7SAaron Miller 	off = EDAC_DIMM_OFF(dimm->mci->layers,
5644fb6fde7SAaron Miller 			    dimm->mci->n_layers,
5654fb6fde7SAaron Miller 			    dimm->location[0],
5664fb6fde7SAaron Miller 			    dimm->location[1],
5674fb6fde7SAaron Miller 			    dimm->location[2]);
5684fb6fde7SAaron Miller 	count = dimm->mci->ce_per_layer[dimm->mci->n_layers-1][off];
5694fb6fde7SAaron Miller 	return sprintf(data, "%u\n", count);
5704fb6fde7SAaron Miller }
5714fb6fde7SAaron Miller 
5724fb6fde7SAaron Miller static ssize_t dimmdev_ue_count_show(struct device *dev,
5734fb6fde7SAaron Miller 				      struct device_attribute *mattr,
5744fb6fde7SAaron Miller 				      char *data)
5754fb6fde7SAaron Miller {
5764fb6fde7SAaron Miller 	struct dimm_info *dimm = to_dimm(dev);
5774fb6fde7SAaron Miller 	u32 count;
5784fb6fde7SAaron Miller 	int off;
5794fb6fde7SAaron Miller 
5804fb6fde7SAaron Miller 	off = EDAC_DIMM_OFF(dimm->mci->layers,
5814fb6fde7SAaron Miller 			    dimm->mci->n_layers,
5824fb6fde7SAaron Miller 			    dimm->location[0],
5834fb6fde7SAaron Miller 			    dimm->location[1],
5844fb6fde7SAaron Miller 			    dimm->location[2]);
5854fb6fde7SAaron Miller 	count = dimm->mci->ue_per_layer[dimm->mci->n_layers-1][off];
5864fb6fde7SAaron Miller 	return sprintf(data, "%u\n", count);
5874fb6fde7SAaron Miller }
5884fb6fde7SAaron Miller 
58919974710SMauro Carvalho Chehab /* dimm/rank attribute files */
59019974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
59119974710SMauro Carvalho Chehab 		   dimmdev_label_show, dimmdev_label_store);
59219974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
59319974710SMauro Carvalho Chehab static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
59419974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
59519974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
59619974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
5974fb6fde7SAaron Miller static DEVICE_ATTR(dimm_ce_count, S_IRUGO, dimmdev_ce_count_show, NULL);
5984fb6fde7SAaron Miller static DEVICE_ATTR(dimm_ue_count, S_IRUGO, dimmdev_ue_count_show, NULL);
59919974710SMauro Carvalho Chehab 
60019974710SMauro Carvalho Chehab /* attributes of the dimm<id>/rank<id> object */
60119974710SMauro Carvalho Chehab static struct attribute *dimm_attrs[] = {
60219974710SMauro Carvalho Chehab 	&dev_attr_dimm_label.attr,
60319974710SMauro Carvalho Chehab 	&dev_attr_dimm_location.attr,
60419974710SMauro Carvalho Chehab 	&dev_attr_size.attr,
60519974710SMauro Carvalho Chehab 	&dev_attr_dimm_mem_type.attr,
60619974710SMauro Carvalho Chehab 	&dev_attr_dimm_dev_type.attr,
60719974710SMauro Carvalho Chehab 	&dev_attr_dimm_edac_mode.attr,
6084fb6fde7SAaron Miller 	&dev_attr_dimm_ce_count.attr,
6094fb6fde7SAaron Miller 	&dev_attr_dimm_ue_count.attr,
61019974710SMauro Carvalho Chehab 	NULL,
61119974710SMauro Carvalho Chehab };
61219974710SMauro Carvalho Chehab 
6131c18be5aSArvind Yadav static const struct attribute_group dimm_attr_grp = {
61419974710SMauro Carvalho Chehab 	.attrs	= dimm_attrs,
61519974710SMauro Carvalho Chehab };
61619974710SMauro Carvalho Chehab 
61719974710SMauro Carvalho Chehab static const struct attribute_group *dimm_attr_groups[] = {
61819974710SMauro Carvalho Chehab 	&dimm_attr_grp,
61919974710SMauro Carvalho Chehab 	NULL
62019974710SMauro Carvalho Chehab };
62119974710SMauro Carvalho Chehab 
622de3910ebSMauro Carvalho Chehab static void dimm_attr_release(struct device *dev)
62319974710SMauro Carvalho Chehab {
624de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm = container_of(dev, struct dimm_info, dev);
625de3910ebSMauro Carvalho Chehab 
626956b9ba1SJoe Perches 	edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev));
627de3910ebSMauro Carvalho Chehab 	kfree(dimm);
62819974710SMauro Carvalho Chehab }
62919974710SMauro Carvalho Chehab 
630b2b3e736SBhumika Goyal static const struct device_type dimm_attr_type = {
63119974710SMauro Carvalho Chehab 	.groups		= dimm_attr_groups,
63219974710SMauro Carvalho Chehab 	.release	= dimm_attr_release,
63319974710SMauro Carvalho Chehab };
63419974710SMauro Carvalho Chehab 
63519974710SMauro Carvalho Chehab /* Create a DIMM object under specifed memory controller device */
63619974710SMauro Carvalho Chehab static int edac_create_dimm_object(struct mem_ctl_info *mci,
63719974710SMauro Carvalho Chehab 				   struct dimm_info *dimm,
63819974710SMauro Carvalho Chehab 				   int index)
63919974710SMauro Carvalho Chehab {
64019974710SMauro Carvalho Chehab 	int err;
64119974710SMauro Carvalho Chehab 	dimm->mci = mci;
64219974710SMauro Carvalho Chehab 
64319974710SMauro Carvalho Chehab 	dimm->dev.type = &dimm_attr_type;
64419974710SMauro Carvalho Chehab 	device_initialize(&dimm->dev);
64519974710SMauro Carvalho Chehab 
64619974710SMauro Carvalho Chehab 	dimm->dev.parent = &mci->dev;
6479713faecSMauro Carvalho Chehab 	if (mci->csbased)
64819974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "rank%d", index);
64919974710SMauro Carvalho Chehab 	else
65019974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "dimm%d", index);
65119974710SMauro Carvalho Chehab 	dev_set_drvdata(&dimm->dev, dimm);
65219974710SMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
65319974710SMauro Carvalho Chehab 
65419974710SMauro Carvalho Chehab 	err =  device_add(&dimm->dev);
65519974710SMauro Carvalho Chehab 
656956b9ba1SJoe Perches 	edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev));
65719974710SMauro Carvalho Chehab 
65819974710SMauro Carvalho Chehab 	return err;
65919974710SMauro Carvalho Chehab }
6607a623c03SMauro Carvalho Chehab 
6617a623c03SMauro Carvalho Chehab /*
6627a623c03SMauro Carvalho Chehab  * Memory controller device
6637a623c03SMauro Carvalho Chehab  */
6647a623c03SMauro Carvalho Chehab 
6657a623c03SMauro Carvalho Chehab #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
6667a623c03SMauro Carvalho Chehab 
6677a623c03SMauro Carvalho Chehab static ssize_t mci_reset_counters_store(struct device *dev,
6687a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
6697c9281d7SDouglas Thompson 					const char *data, size_t count)
6707c9281d7SDouglas Thompson {
6717a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
6727a623c03SMauro Carvalho Chehab 	int cnt, row, chan, i;
6735926ff50SMauro Carvalho Chehab 	mci->ue_mc = 0;
6745926ff50SMauro Carvalho Chehab 	mci->ce_mc = 0;
6757a623c03SMauro Carvalho Chehab 	mci->ue_noinfo_count = 0;
6767a623c03SMauro Carvalho Chehab 	mci->ce_noinfo_count = 0;
6777c9281d7SDouglas Thompson 
6787c9281d7SDouglas Thompson 	for (row = 0; row < mci->nr_csrows; row++) {
679de3910ebSMauro Carvalho Chehab 		struct csrow_info *ri = mci->csrows[row];
6807c9281d7SDouglas Thompson 
6817c9281d7SDouglas Thompson 		ri->ue_count = 0;
6827c9281d7SDouglas Thompson 		ri->ce_count = 0;
6837c9281d7SDouglas Thompson 
6847c9281d7SDouglas Thompson 		for (chan = 0; chan < ri->nr_channels; chan++)
685de3910ebSMauro Carvalho Chehab 			ri->channels[chan]->ce_count = 0;
6867c9281d7SDouglas Thompson 	}
6877c9281d7SDouglas Thompson 
6887a623c03SMauro Carvalho Chehab 	cnt = 1;
6897a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
6907a623c03SMauro Carvalho Chehab 		cnt *= mci->layers[i].size;
6917a623c03SMauro Carvalho Chehab 		memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32));
6927a623c03SMauro Carvalho Chehab 		memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32));
6937a623c03SMauro Carvalho Chehab 	}
6947a623c03SMauro Carvalho Chehab 
6957c9281d7SDouglas Thompson 	mci->start_time = jiffies;
6967c9281d7SDouglas Thompson 	return count;
6977c9281d7SDouglas Thompson }
6987c9281d7SDouglas Thompson 
69939094443SBorislav Petkov /* Memory scrubbing interface:
70039094443SBorislav Petkov  *
70139094443SBorislav Petkov  * A MC driver can limit the scrubbing bandwidth based on the CPU type.
70239094443SBorislav Petkov  * Therefore, ->set_sdram_scrub_rate should be made to return the actual
70339094443SBorislav Petkov  * bandwidth that is accepted or 0 when scrubbing is to be disabled.
70439094443SBorislav Petkov  *
70539094443SBorislav Petkov  * Negative value still means that an error has occurred while setting
70639094443SBorislav Petkov  * the scrub rate.
70739094443SBorislav Petkov  */
7087a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
7097a623c03SMauro Carvalho Chehab 					  struct device_attribute *mattr,
7107c9281d7SDouglas Thompson 					  const char *data, size_t count)
7117c9281d7SDouglas Thompson {
7127a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
713eba042a8SBorislav Petkov 	unsigned long bandwidth = 0;
71439094443SBorislav Petkov 	int new_bw = 0;
7157c9281d7SDouglas Thompson 
716c7f62fc8SJingoo Han 	if (kstrtoul(data, 10, &bandwidth) < 0)
717eba042a8SBorislav Petkov 		return -EINVAL;
718eba042a8SBorislav Petkov 
71939094443SBorislav Petkov 	new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
7204949603aSMarkus Trippelsdorf 	if (new_bw < 0) {
7214949603aSMarkus Trippelsdorf 		edac_printk(KERN_WARNING, EDAC_MC,
7224949603aSMarkus Trippelsdorf 			    "Error setting scrub rate to: %lu\n", bandwidth);
7234949603aSMarkus Trippelsdorf 		return -EINVAL;
7247c9281d7SDouglas Thompson 	}
7257c9281d7SDouglas Thompson 
7264949603aSMarkus Trippelsdorf 	return count;
7277c9281d7SDouglas Thompson }
728eba042a8SBorislav Petkov 
72939094443SBorislav Petkov /*
73039094443SBorislav Petkov  * ->get_sdram_scrub_rate() return value semantics same as above.
73139094443SBorislav Petkov  */
7327a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
7337a623c03SMauro Carvalho Chehab 					 struct device_attribute *mattr,
7347a623c03SMauro Carvalho Chehab 					 char *data)
73539094443SBorislav Petkov {
7367a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
73739094443SBorislav Petkov 	int bandwidth = 0;
73839094443SBorislav Petkov 
73939094443SBorislav Petkov 	bandwidth = mci->get_sdram_scrub_rate(mci);
74039094443SBorislav Petkov 	if (bandwidth < 0) {
741eba042a8SBorislav Petkov 		edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n");
74239094443SBorislav Petkov 		return bandwidth;
743eba042a8SBorislav Petkov 	}
74439094443SBorislav Petkov 
7457c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", bandwidth);
7467c9281d7SDouglas Thompson }
7477c9281d7SDouglas Thompson 
7487c9281d7SDouglas Thompson /* default attribute files for the MCI object */
7497a623c03SMauro Carvalho Chehab static ssize_t mci_ue_count_show(struct device *dev,
7507a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7517a623c03SMauro Carvalho Chehab 				 char *data)
7527c9281d7SDouglas Thompson {
7537a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7547a623c03SMauro Carvalho Chehab 
7555926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ue_mc);
7567c9281d7SDouglas Thompson }
7577c9281d7SDouglas Thompson 
7587a623c03SMauro Carvalho Chehab static ssize_t mci_ce_count_show(struct device *dev,
7597a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7607a623c03SMauro Carvalho Chehab 				 char *data)
7617c9281d7SDouglas Thompson {
7627a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7637a623c03SMauro Carvalho Chehab 
7645926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ce_mc);
7657c9281d7SDouglas Thompson }
7667c9281d7SDouglas Thompson 
7677a623c03SMauro Carvalho Chehab static ssize_t mci_ce_noinfo_show(struct device *dev,
7687a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7697a623c03SMauro Carvalho Chehab 				  char *data)
7707c9281d7SDouglas Thompson {
7717a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7727a623c03SMauro Carvalho Chehab 
7737c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ce_noinfo_count);
7747c9281d7SDouglas Thompson }
7757c9281d7SDouglas Thompson 
7767a623c03SMauro Carvalho Chehab static ssize_t mci_ue_noinfo_show(struct device *dev,
7777a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7787a623c03SMauro Carvalho Chehab 				  char *data)
7797c9281d7SDouglas Thompson {
7807a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7817a623c03SMauro Carvalho Chehab 
7827c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ue_noinfo_count);
7837c9281d7SDouglas Thompson }
7847c9281d7SDouglas Thompson 
7857a623c03SMauro Carvalho Chehab static ssize_t mci_seconds_show(struct device *dev,
7867a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
7877a623c03SMauro Carvalho Chehab 				char *data)
7887c9281d7SDouglas Thompson {
7897a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7907a623c03SMauro Carvalho Chehab 
7917c9281d7SDouglas Thompson 	return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
7927c9281d7SDouglas Thompson }
7937c9281d7SDouglas Thompson 
7947a623c03SMauro Carvalho Chehab static ssize_t mci_ctl_name_show(struct device *dev,
7957a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7967a623c03SMauro Carvalho Chehab 				 char *data)
7977c9281d7SDouglas Thompson {
7987a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7997a623c03SMauro Carvalho Chehab 
8007c9281d7SDouglas Thompson 	return sprintf(data, "%s\n", mci->ctl_name);
8017c9281d7SDouglas Thompson }
8027c9281d7SDouglas Thompson 
8037a623c03SMauro Carvalho Chehab static ssize_t mci_size_mb_show(struct device *dev,
8047a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
8057a623c03SMauro Carvalho Chehab 				char *data)
8067c9281d7SDouglas Thompson {
8077a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
808a895bf8bSMauro Carvalho Chehab 	int total_pages = 0, csrow_idx, j;
8097c9281d7SDouglas Thompson 
810a895bf8bSMauro Carvalho Chehab 	for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
811de3910ebSMauro Carvalho Chehab 		struct csrow_info *csrow = mci->csrows[csrow_idx];
8127c9281d7SDouglas Thompson 
813a895bf8bSMauro Carvalho Chehab 		for (j = 0; j < csrow->nr_channels; j++) {
814de3910ebSMauro Carvalho Chehab 			struct dimm_info *dimm = csrow->channels[j]->dimm;
8157c9281d7SDouglas Thompson 
816a895bf8bSMauro Carvalho Chehab 			total_pages += dimm->nr_pages;
817a895bf8bSMauro Carvalho Chehab 		}
8187c9281d7SDouglas Thompson 	}
8197c9281d7SDouglas Thompson 
8207c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
8217c9281d7SDouglas Thompson }
8227c9281d7SDouglas Thompson 
8238ad6c78aSMauro Carvalho Chehab static ssize_t mci_max_location_show(struct device *dev,
8248ad6c78aSMauro Carvalho Chehab 				     struct device_attribute *mattr,
8258ad6c78aSMauro Carvalho Chehab 				     char *data)
8268ad6c78aSMauro Carvalho Chehab {
8278ad6c78aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
8288ad6c78aSMauro Carvalho Chehab 	int i;
8298ad6c78aSMauro Carvalho Chehab 	char *p = data;
8308ad6c78aSMauro Carvalho Chehab 
8318ad6c78aSMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
8328ad6c78aSMauro Carvalho Chehab 		p += sprintf(p, "%s %d ",
8338ad6c78aSMauro Carvalho Chehab 			     edac_layer_name[mci->layers[i].type],
8348ad6c78aSMauro Carvalho Chehab 			     mci->layers[i].size - 1);
8358ad6c78aSMauro Carvalho Chehab 	}
8368ad6c78aSMauro Carvalho Chehab 
8378ad6c78aSMauro Carvalho Chehab 	return p - data;
8388ad6c78aSMauro Carvalho Chehab }
8398ad6c78aSMauro Carvalho Chehab 
8407c9281d7SDouglas Thompson /* default Control file */
841f11135d8SBorislav Petkov static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
8427c9281d7SDouglas Thompson 
8437c9281d7SDouglas Thompson /* default Attribute files */
844f11135d8SBorislav Petkov static DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
845f11135d8SBorislav Petkov static DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
846f11135d8SBorislav Petkov static DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
847f11135d8SBorislav Petkov static DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
848f11135d8SBorislav Petkov static DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
849f11135d8SBorislav Petkov static DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
850f11135d8SBorislav Petkov static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
851f11135d8SBorislav Petkov static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
8527c9281d7SDouglas Thompson 
8537c9281d7SDouglas Thompson /* memory scrubber attribute file */
854628ea92fSBen Dooks static DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show,
8552c1946b6STakashi Iwai 	    mci_sdram_scrub_rate_store); /* umode set later in is_visible */
8567c9281d7SDouglas Thompson 
8577a623c03SMauro Carvalho Chehab static struct attribute *mci_attrs[] = {
8587a623c03SMauro Carvalho Chehab 	&dev_attr_reset_counters.attr,
8597a623c03SMauro Carvalho Chehab 	&dev_attr_mc_name.attr,
8607a623c03SMauro Carvalho Chehab 	&dev_attr_size_mb.attr,
8617a623c03SMauro Carvalho Chehab 	&dev_attr_seconds_since_reset.attr,
8627a623c03SMauro Carvalho Chehab 	&dev_attr_ue_noinfo_count.attr,
8637a623c03SMauro Carvalho Chehab 	&dev_attr_ce_noinfo_count.attr,
8647a623c03SMauro Carvalho Chehab 	&dev_attr_ue_count.attr,
8657a623c03SMauro Carvalho Chehab 	&dev_attr_ce_count.attr,
8668ad6c78aSMauro Carvalho Chehab 	&dev_attr_max_location.attr,
8672c1946b6STakashi Iwai 	&dev_attr_sdram_scrub_rate.attr,
8687c9281d7SDouglas Thompson 	NULL
8697c9281d7SDouglas Thompson };
8707c9281d7SDouglas Thompson 
8712c1946b6STakashi Iwai static umode_t mci_attr_is_visible(struct kobject *kobj,
8722c1946b6STakashi Iwai 				   struct attribute *attr, int idx)
8732c1946b6STakashi Iwai {
8742c1946b6STakashi Iwai 	struct device *dev = kobj_to_dev(kobj);
8752c1946b6STakashi Iwai 	struct mem_ctl_info *mci = to_mci(dev);
8762c1946b6STakashi Iwai 	umode_t mode = 0;
8772c1946b6STakashi Iwai 
8782c1946b6STakashi Iwai 	if (attr != &dev_attr_sdram_scrub_rate.attr)
8792c1946b6STakashi Iwai 		return attr->mode;
8802c1946b6STakashi Iwai 	if (mci->get_sdram_scrub_rate)
8812c1946b6STakashi Iwai 		mode |= S_IRUGO;
8822c1946b6STakashi Iwai 	if (mci->set_sdram_scrub_rate)
8832c1946b6STakashi Iwai 		mode |= S_IWUSR;
8842c1946b6STakashi Iwai 	return mode;
8852c1946b6STakashi Iwai }
8862c1946b6STakashi Iwai 
8871c18be5aSArvind Yadav static const struct attribute_group mci_attr_grp = {
8887a623c03SMauro Carvalho Chehab 	.attrs	= mci_attrs,
8892c1946b6STakashi Iwai 	.is_visible = mci_attr_is_visible,
8907c9281d7SDouglas Thompson };
8917c9281d7SDouglas Thompson 
8927a623c03SMauro Carvalho Chehab static const struct attribute_group *mci_attr_groups[] = {
8937a623c03SMauro Carvalho Chehab 	&mci_attr_grp,
8947a623c03SMauro Carvalho Chehab 	NULL
895cc301b3aSMauro Carvalho Chehab };
896cc301b3aSMauro Carvalho Chehab 
897de3910ebSMauro Carvalho Chehab static void mci_attr_release(struct device *dev)
8987a623c03SMauro Carvalho Chehab {
899de3910ebSMauro Carvalho Chehab 	struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
900de3910ebSMauro Carvalho Chehab 
901956b9ba1SJoe Perches 	edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
902de3910ebSMauro Carvalho Chehab 	kfree(mci);
9037a623c03SMauro Carvalho Chehab }
9047a623c03SMauro Carvalho Chehab 
905b2b3e736SBhumika Goyal static const struct device_type mci_attr_type = {
9067a623c03SMauro Carvalho Chehab 	.groups		= mci_attr_groups,
9077a623c03SMauro Carvalho Chehab 	.release	= mci_attr_release,
908cc301b3aSMauro Carvalho Chehab };
909cc301b3aSMauro Carvalho Chehab 
9107c9281d7SDouglas Thompson /*
9117c9281d7SDouglas Thompson  * Create a new Memory Controller kobject instance,
9127c9281d7SDouglas Thompson  *	mc<id> under the 'mc' directory
9137c9281d7SDouglas Thompson  *
9147c9281d7SDouglas Thompson  * Return:
9157c9281d7SDouglas Thompson  *	0	Success
9167c9281d7SDouglas Thompson  *	!0	Failure
9177c9281d7SDouglas Thompson  */
9184e8d230dSTakashi Iwai int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
9194e8d230dSTakashi Iwai 				 const struct attribute_group **groups)
9207c9281d7SDouglas Thompson {
9217a623c03SMauro Carvalho Chehab 	int i, err;
9227c9281d7SDouglas Thompson 
923de3910ebSMauro Carvalho Chehab 	/* get the /sys/devices/system/edac subsys reference */
924de3910ebSMauro Carvalho Chehab 	mci->dev.type = &mci_attr_type;
925de3910ebSMauro Carvalho Chehab 	device_initialize(&mci->dev);
926de3910ebSMauro Carvalho Chehab 
927de3910ebSMauro Carvalho Chehab 	mci->dev.parent = mci_pdev;
9284e8d230dSTakashi Iwai 	mci->dev.groups = groups;
929de3910ebSMauro Carvalho Chehab 	dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
930de3910ebSMauro Carvalho Chehab 	dev_set_drvdata(&mci->dev, mci);
931de3910ebSMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
932de3910ebSMauro Carvalho Chehab 
933956b9ba1SJoe Perches 	edac_dbg(0, "creating device %s\n", dev_name(&mci->dev));
9347a623c03SMauro Carvalho Chehab 	err = device_add(&mci->dev);
9357a623c03SMauro Carvalho Chehab 	if (err < 0) {
9363d958823SMauro Carvalho Chehab 		edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev));
937861e6ed6SBorislav Petkov 		goto out;
9388096cfafSDoug Thompson 	}
93942a8e397SDouglas Thompson 
9407a623c03SMauro Carvalho Chehab 	/*
9417a623c03SMauro Carvalho Chehab 	 * Create the dimm/rank devices
9427c9281d7SDouglas Thompson 	 */
9437a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
944de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
9457a623c03SMauro Carvalho Chehab 		/* Only expose populated DIMMs */
9461bf1950cSJunjie Mao 		if (!dimm->nr_pages)
9477a623c03SMauro Carvalho Chehab 			continue;
9481bf1950cSJunjie Mao 
9497a623c03SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
950956b9ba1SJoe Perches 		edac_dbg(1, "creating dimm%d, located at ", i);
9517a623c03SMauro Carvalho Chehab 		if (edac_debug_level >= 1) {
9527a623c03SMauro Carvalho Chehab 			int lay;
9537a623c03SMauro Carvalho Chehab 			for (lay = 0; lay < mci->n_layers; lay++)
9547a623c03SMauro Carvalho Chehab 				printk(KERN_CONT "%s %d ",
9557a623c03SMauro Carvalho Chehab 					edac_layer_name[mci->layers[lay].type],
9567a623c03SMauro Carvalho Chehab 					dimm->location[lay]);
9577a623c03SMauro Carvalho Chehab 			printk(KERN_CONT "\n");
9587c9281d7SDouglas Thompson 		}
9597a623c03SMauro Carvalho Chehab #endif
96019974710SMauro Carvalho Chehab 		err = edac_create_dimm_object(mci, dimm, i);
96119974710SMauro Carvalho Chehab 		if (err) {
962956b9ba1SJoe Perches 			edac_dbg(1, "failure: create dimm %d obj\n", i);
9631bf1950cSJunjie Mao 			goto fail_unregister_dimm;
96419974710SMauro Carvalho Chehab 		}
9657c9281d7SDouglas Thompson 	}
9667a623c03SMauro Carvalho Chehab 
96719974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
9687a623c03SMauro Carvalho Chehab 	err = edac_create_csrow_objects(mci);
9697a623c03SMauro Carvalho Chehab 	if (err < 0)
9701bf1950cSJunjie Mao 		goto fail_unregister_dimm;
97119974710SMauro Carvalho Chehab #endif
9727c9281d7SDouglas Thompson 
9737ac8bf9bSBorislav Petkov 	edac_create_debugfs_nodes(mci);
9747c9281d7SDouglas Thompson 	return 0;
9757c9281d7SDouglas Thompson 
9761bf1950cSJunjie Mao fail_unregister_dimm:
9777c9281d7SDouglas Thompson 	for (i--; i >= 0; i--) {
978de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
9791bf1950cSJunjie Mao 		if (!dimm->nr_pages)
9807a623c03SMauro Carvalho Chehab 			continue;
9811bf1950cSJunjie Mao 
98244d22e24SLans Zhang 		device_unregister(&dimm->dev);
9837c9281d7SDouglas Thompson 	}
98444d22e24SLans Zhang 	device_unregister(&mci->dev);
98512e26969SBorislav Petkov 
986861e6ed6SBorislav Petkov out:
9877c9281d7SDouglas Thompson 	return err;
9887c9281d7SDouglas Thompson }
9897c9281d7SDouglas Thompson 
9907c9281d7SDouglas Thompson /*
9917c9281d7SDouglas Thompson  * remove a Memory Controller instance
9927c9281d7SDouglas Thompson  */
9937c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
9947c9281d7SDouglas Thompson {
9957a623c03SMauro Carvalho Chehab 	int i;
9967c9281d7SDouglas Thompson 
997956b9ba1SJoe Perches 	edac_dbg(0, "\n");
9987c9281d7SDouglas Thompson 
999452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
100030f84a89STan Xiaojun 	edac_debugfs_remove_recursive(mci->debugfs);
1001452a6bf9SMauro Carvalho Chehab #endif
100219974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
10037a623c03SMauro Carvalho Chehab 	edac_delete_csrow_objects(mci);
100419974710SMauro Carvalho Chehab #endif
1005a895bf8bSMauro Carvalho Chehab 
10067a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
1007de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
10087a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
10097a623c03SMauro Carvalho Chehab 			continue;
1010956b9ba1SJoe Perches 		edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev));
101144d22e24SLans Zhang 		device_unregister(&dimm->dev);
10127c9281d7SDouglas Thompson 	}
10137c9281d7SDouglas Thompson }
10147c9281d7SDouglas Thompson 
10157a623c03SMauro Carvalho Chehab void edac_unregister_sysfs(struct mem_ctl_info *mci)
10168096cfafSDoug Thompson {
1017956b9ba1SJoe Perches 	edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
101844d22e24SLans Zhang 	device_unregister(&mci->dev);
10197a623c03SMauro Carvalho Chehab }
10208096cfafSDoug Thompson 
1021de3910ebSMauro Carvalho Chehab static void mc_attr_release(struct device *dev)
10227a623c03SMauro Carvalho Chehab {
1023de3910ebSMauro Carvalho Chehab 	/*
1024de3910ebSMauro Carvalho Chehab 	 * There's no container structure here, as this is just the mci
1025de3910ebSMauro Carvalho Chehab 	 * parent device, used to create the /sys/devices/mc sysfs node.
1026de3910ebSMauro Carvalho Chehab 	 * So, there are no attributes on it.
1027de3910ebSMauro Carvalho Chehab 	 */
1028956b9ba1SJoe Perches 	edac_dbg(1, "Releasing device %s\n", dev_name(dev));
1029de3910ebSMauro Carvalho Chehab 	kfree(dev);
10307a623c03SMauro Carvalho Chehab }
10317a623c03SMauro Carvalho Chehab 
1032b2b3e736SBhumika Goyal static const struct device_type mc_attr_type = {
10337a623c03SMauro Carvalho Chehab 	.release	= mc_attr_release,
10347a623c03SMauro Carvalho Chehab };
10357a623c03SMauro Carvalho Chehab /*
10367a623c03SMauro Carvalho Chehab  * Init/exit code for the module. Basically, creates/removes /sys/class/rc
10377a623c03SMauro Carvalho Chehab  */
10387a623c03SMauro Carvalho Chehab int __init edac_mc_sysfs_init(void)
10397a623c03SMauro Carvalho Chehab {
10407a623c03SMauro Carvalho Chehab 	int err;
10418096cfafSDoug Thompson 
1042de3910ebSMauro Carvalho Chehab 	mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
10432d56b109SDenis Kirjanov 	if (!mci_pdev) {
10442d56b109SDenis Kirjanov 		err = -ENOMEM;
1045733476cfSBorislav Petkov 		goto out;
10462d56b109SDenis Kirjanov 	}
10478096cfafSDoug Thompson 
1048d4538000SBorislav Petkov 	mci_pdev->bus = edac_get_sysfs_subsys();
1049de3910ebSMauro Carvalho Chehab 	mci_pdev->type = &mc_attr_type;
1050de3910ebSMauro Carvalho Chehab 	device_initialize(mci_pdev);
1051de3910ebSMauro Carvalho Chehab 	dev_set_name(mci_pdev, "mc");
1052de3910ebSMauro Carvalho Chehab 
1053de3910ebSMauro Carvalho Chehab 	err = device_add(mci_pdev);
10547a623c03SMauro Carvalho Chehab 	if (err < 0)
10554708aa85SJohan Hovold 		goto out_put_device;
10568096cfafSDoug Thompson 
1057956b9ba1SJoe Perches 	edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
1058de3910ebSMauro Carvalho Chehab 
10598096cfafSDoug Thompson 	return 0;
10602d56b109SDenis Kirjanov 
10614708aa85SJohan Hovold  out_put_device:
10624708aa85SJohan Hovold 	put_device(mci_pdev);
10632d56b109SDenis Kirjanov  out:
10642d56b109SDenis Kirjanov 	return err;
10658096cfafSDoug Thompson }
10668096cfafSDoug Thompson 
1067c6b97bcfSAlexey Khoroshilov void edac_mc_sysfs_exit(void)
10688096cfafSDoug Thompson {
106944d22e24SLans Zhang 	device_unregister(mci_pdev);
10708096cfafSDoug Thompson }
1071