xref: /openbmc/linux/drivers/edac/edac_mc_sysfs.c (revision 861e6ed6)
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 {
4077a623c03SMauro Carvalho Chehab 	csrow->dev.type = &csrow_attr_type;
4082c1946b6STakashi Iwai 	csrow->dev.groups = csrow_dev_groups;
4097a623c03SMauro Carvalho Chehab 	device_initialize(&csrow->dev);
4107a623c03SMauro Carvalho Chehab 	csrow->dev.parent = &mci->dev;
411921a6899SBorislav Petkov 	csrow->mci = mci;
4127a623c03SMauro Carvalho Chehab 	dev_set_name(&csrow->dev, "csrow%d", index);
4137a623c03SMauro Carvalho Chehab 	dev_set_drvdata(&csrow->dev, csrow);
4147c9281d7SDouglas Thompson 
415956b9ba1SJoe Perches 	edac_dbg(0, "creating (virtual) csrow node %s\n",
416956b9ba1SJoe Perches 		 dev_name(&csrow->dev));
4178096cfafSDoug Thompson 
4182c1946b6STakashi Iwai 	return device_add(&csrow->dev);
4197c9281d7SDouglas Thompson }
4207c9281d7SDouglas Thompson 
4217a623c03SMauro Carvalho Chehab /* Create a CSROW object under specifed edac_mc_device */
4227a623c03SMauro Carvalho Chehab static int edac_create_csrow_objects(struct mem_ctl_info *mci)
4237a623c03SMauro Carvalho Chehab {
4242c1946b6STakashi Iwai 	int err, i;
4257a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4267c9281d7SDouglas Thompson 
4277a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->nr_csrows; i++) {
428de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
429e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
430e39f4ea9SMauro Carvalho Chehab 			continue;
431de3910ebSMauro Carvalho Chehab 		err = edac_create_csrow_object(mci, mci->csrows[i], i);
4323d958823SMauro Carvalho Chehab 		if (err < 0) {
4333d958823SMauro Carvalho Chehab 			edac_dbg(1,
4343d958823SMauro Carvalho Chehab 				 "failure: create csrow objects for csrow %d\n",
4353d958823SMauro Carvalho Chehab 				 i);
4367a623c03SMauro Carvalho Chehab 			goto error;
4377a623c03SMauro Carvalho Chehab 		}
4383d958823SMauro Carvalho Chehab 	}
4397a623c03SMauro Carvalho Chehab 	return 0;
4407a623c03SMauro Carvalho Chehab 
4417a623c03SMauro Carvalho Chehab error:
4427a623c03SMauro Carvalho Chehab 	for (--i; i >= 0; i--) {
443de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
444e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
445e39f4ea9SMauro Carvalho Chehab 			continue;
446de3910ebSMauro Carvalho Chehab 		put_device(&mci->csrows[i]->dev);
4477a623c03SMauro Carvalho Chehab 	}
4487a623c03SMauro Carvalho Chehab 
4497a623c03SMauro Carvalho Chehab 	return err;
4507a623c03SMauro Carvalho Chehab }
4517a623c03SMauro Carvalho Chehab 
4527a623c03SMauro Carvalho Chehab static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
4537a623c03SMauro Carvalho Chehab {
4542c1946b6STakashi Iwai 	int i;
4557a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4567a623c03SMauro Carvalho Chehab 
4577a623c03SMauro Carvalho Chehab 	for (i = mci->nr_csrows - 1; i >= 0; i--) {
458de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
459e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
460e39f4ea9SMauro Carvalho Chehab 			continue;
46144d22e24SLans Zhang 		device_unregister(&mci->csrows[i]->dev);
4627a623c03SMauro Carvalho Chehab 	}
4637a623c03SMauro Carvalho Chehab }
46419974710SMauro Carvalho Chehab #endif
46519974710SMauro Carvalho Chehab 
46619974710SMauro Carvalho Chehab /*
46719974710SMauro Carvalho Chehab  * Per-dimm (or per-rank) devices
46819974710SMauro Carvalho Chehab  */
46919974710SMauro Carvalho Chehab 
47019974710SMauro Carvalho Chehab #define to_dimm(k) container_of(k, struct dimm_info, dev)
47119974710SMauro Carvalho Chehab 
47219974710SMauro Carvalho Chehab /* show/store functions for DIMM Label attributes */
47319974710SMauro Carvalho Chehab static ssize_t dimmdev_location_show(struct device *dev,
47419974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
47519974710SMauro Carvalho Chehab {
47619974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
47719974710SMauro Carvalho Chehab 
4786e84d359SMauro Carvalho Chehab 	return edac_dimm_info_location(dimm, data, PAGE_SIZE);
47919974710SMauro Carvalho Chehab }
48019974710SMauro Carvalho Chehab 
48119974710SMauro Carvalho Chehab static ssize_t dimmdev_label_show(struct device *dev,
48219974710SMauro Carvalho Chehab 				  struct device_attribute *mattr, char *data)
48319974710SMauro Carvalho Chehab {
48419974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
48519974710SMauro Carvalho Chehab 
48619974710SMauro Carvalho Chehab 	/* if field has not been initialized, there is nothing to send */
48719974710SMauro Carvalho Chehab 	if (!dimm->label[0])
48819974710SMauro Carvalho Chehab 		return 0;
48919974710SMauro Carvalho Chehab 
4901ea62c59SToshi Kani 	return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label);
49119974710SMauro Carvalho Chehab }
49219974710SMauro Carvalho Chehab 
49319974710SMauro Carvalho Chehab static ssize_t dimmdev_label_store(struct device *dev,
49419974710SMauro Carvalho Chehab 				   struct device_attribute *mattr,
49519974710SMauro Carvalho Chehab 				   const char *data,
49619974710SMauro Carvalho Chehab 				   size_t count)
49719974710SMauro Carvalho Chehab {
49819974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
499438470b8SToshi Kani 	size_t copy_count = count;
50019974710SMauro Carvalho Chehab 
501438470b8SToshi Kani 	if (count == 0)
502438470b8SToshi Kani 		return -EINVAL;
50319974710SMauro Carvalho Chehab 
504438470b8SToshi Kani 	if (data[count - 1] == '\0' || data[count - 1] == '\n')
505438470b8SToshi Kani 		copy_count -= 1;
50619974710SMauro Carvalho Chehab 
507d0c9c930SToshi Kani 	if (copy_count == 0 || copy_count >= sizeof(dimm->label))
508438470b8SToshi Kani 		return -EINVAL;
509438470b8SToshi Kani 
510438470b8SToshi Kani 	strncpy(dimm->label, data, copy_count);
511438470b8SToshi Kani 	dimm->label[copy_count] = '\0';
512438470b8SToshi Kani 
513438470b8SToshi Kani 	return count;
51419974710SMauro Carvalho Chehab }
51519974710SMauro Carvalho Chehab 
51619974710SMauro Carvalho Chehab static ssize_t dimmdev_size_show(struct device *dev,
51719974710SMauro Carvalho Chehab 				 struct device_attribute *mattr, char *data)
51819974710SMauro Carvalho Chehab {
51919974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
52019974710SMauro Carvalho Chehab 
52119974710SMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
52219974710SMauro Carvalho Chehab }
52319974710SMauro Carvalho Chehab 
52419974710SMauro Carvalho Chehab static ssize_t dimmdev_mem_type_show(struct device *dev,
52519974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
52619974710SMauro Carvalho Chehab {
52719974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
52819974710SMauro Carvalho Chehab 
529d6dd77ebSTony Luck 	return sprintf(data, "%s\n", edac_mem_types[dimm->mtype]);
53019974710SMauro Carvalho Chehab }
53119974710SMauro Carvalho Chehab 
53219974710SMauro Carvalho Chehab static ssize_t dimmdev_dev_type_show(struct device *dev,
53319974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
53419974710SMauro Carvalho Chehab {
53519974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
53619974710SMauro Carvalho Chehab 
53719974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[dimm->dtype]);
53819974710SMauro Carvalho Chehab }
53919974710SMauro Carvalho Chehab 
54019974710SMauro Carvalho Chehab static ssize_t dimmdev_edac_mode_show(struct device *dev,
54119974710SMauro Carvalho Chehab 				      struct device_attribute *mattr,
54219974710SMauro Carvalho Chehab 				      char *data)
54319974710SMauro Carvalho Chehab {
54419974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
54519974710SMauro Carvalho Chehab 
54619974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
54719974710SMauro Carvalho Chehab }
54819974710SMauro Carvalho Chehab 
5494fb6fde7SAaron Miller static ssize_t dimmdev_ce_count_show(struct device *dev,
5504fb6fde7SAaron Miller 				      struct device_attribute *mattr,
5514fb6fde7SAaron Miller 				      char *data)
5524fb6fde7SAaron Miller {
5534fb6fde7SAaron Miller 	struct dimm_info *dimm = to_dimm(dev);
5544fb6fde7SAaron Miller 	u32 count;
5554fb6fde7SAaron Miller 	int off;
5564fb6fde7SAaron Miller 
5574fb6fde7SAaron Miller 	off = EDAC_DIMM_OFF(dimm->mci->layers,
5584fb6fde7SAaron Miller 			    dimm->mci->n_layers,
5594fb6fde7SAaron Miller 			    dimm->location[0],
5604fb6fde7SAaron Miller 			    dimm->location[1],
5614fb6fde7SAaron Miller 			    dimm->location[2]);
5624fb6fde7SAaron Miller 	count = dimm->mci->ce_per_layer[dimm->mci->n_layers-1][off];
5634fb6fde7SAaron Miller 	return sprintf(data, "%u\n", count);
5644fb6fde7SAaron Miller }
5654fb6fde7SAaron Miller 
5664fb6fde7SAaron Miller static ssize_t dimmdev_ue_count_show(struct device *dev,
5674fb6fde7SAaron Miller 				      struct device_attribute *mattr,
5684fb6fde7SAaron Miller 				      char *data)
5694fb6fde7SAaron Miller {
5704fb6fde7SAaron Miller 	struct dimm_info *dimm = to_dimm(dev);
5714fb6fde7SAaron Miller 	u32 count;
5724fb6fde7SAaron Miller 	int off;
5734fb6fde7SAaron Miller 
5744fb6fde7SAaron Miller 	off = EDAC_DIMM_OFF(dimm->mci->layers,
5754fb6fde7SAaron Miller 			    dimm->mci->n_layers,
5764fb6fde7SAaron Miller 			    dimm->location[0],
5774fb6fde7SAaron Miller 			    dimm->location[1],
5784fb6fde7SAaron Miller 			    dimm->location[2]);
5794fb6fde7SAaron Miller 	count = dimm->mci->ue_per_layer[dimm->mci->n_layers-1][off];
5804fb6fde7SAaron Miller 	return sprintf(data, "%u\n", count);
5814fb6fde7SAaron Miller }
5824fb6fde7SAaron Miller 
58319974710SMauro Carvalho Chehab /* dimm/rank attribute files */
58419974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
58519974710SMauro Carvalho Chehab 		   dimmdev_label_show, dimmdev_label_store);
58619974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
58719974710SMauro Carvalho Chehab static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
58819974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
58919974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
59019974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
5914fb6fde7SAaron Miller static DEVICE_ATTR(dimm_ce_count, S_IRUGO, dimmdev_ce_count_show, NULL);
5924fb6fde7SAaron Miller static DEVICE_ATTR(dimm_ue_count, S_IRUGO, dimmdev_ue_count_show, NULL);
59319974710SMauro Carvalho Chehab 
59419974710SMauro Carvalho Chehab /* attributes of the dimm<id>/rank<id> object */
59519974710SMauro Carvalho Chehab static struct attribute *dimm_attrs[] = {
59619974710SMauro Carvalho Chehab 	&dev_attr_dimm_label.attr,
59719974710SMauro Carvalho Chehab 	&dev_attr_dimm_location.attr,
59819974710SMauro Carvalho Chehab 	&dev_attr_size.attr,
59919974710SMauro Carvalho Chehab 	&dev_attr_dimm_mem_type.attr,
60019974710SMauro Carvalho Chehab 	&dev_attr_dimm_dev_type.attr,
60119974710SMauro Carvalho Chehab 	&dev_attr_dimm_edac_mode.attr,
6024fb6fde7SAaron Miller 	&dev_attr_dimm_ce_count.attr,
6034fb6fde7SAaron Miller 	&dev_attr_dimm_ue_count.attr,
60419974710SMauro Carvalho Chehab 	NULL,
60519974710SMauro Carvalho Chehab };
60619974710SMauro Carvalho Chehab 
6071c18be5aSArvind Yadav static const struct attribute_group dimm_attr_grp = {
60819974710SMauro Carvalho Chehab 	.attrs	= dimm_attrs,
60919974710SMauro Carvalho Chehab };
61019974710SMauro Carvalho Chehab 
61119974710SMauro Carvalho Chehab static const struct attribute_group *dimm_attr_groups[] = {
61219974710SMauro Carvalho Chehab 	&dimm_attr_grp,
61319974710SMauro Carvalho Chehab 	NULL
61419974710SMauro Carvalho Chehab };
61519974710SMauro Carvalho Chehab 
616de3910ebSMauro Carvalho Chehab static void dimm_attr_release(struct device *dev)
61719974710SMauro Carvalho Chehab {
618de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm = container_of(dev, struct dimm_info, dev);
619de3910ebSMauro Carvalho Chehab 
620956b9ba1SJoe Perches 	edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev));
621de3910ebSMauro Carvalho Chehab 	kfree(dimm);
62219974710SMauro Carvalho Chehab }
62319974710SMauro Carvalho Chehab 
624b2b3e736SBhumika Goyal static const struct device_type dimm_attr_type = {
62519974710SMauro Carvalho Chehab 	.groups		= dimm_attr_groups,
62619974710SMauro Carvalho Chehab 	.release	= dimm_attr_release,
62719974710SMauro Carvalho Chehab };
62819974710SMauro Carvalho Chehab 
62919974710SMauro Carvalho Chehab /* Create a DIMM object under specifed memory controller device */
63019974710SMauro Carvalho Chehab static int edac_create_dimm_object(struct mem_ctl_info *mci,
63119974710SMauro Carvalho Chehab 				   struct dimm_info *dimm,
63219974710SMauro Carvalho Chehab 				   int index)
63319974710SMauro Carvalho Chehab {
63419974710SMauro Carvalho Chehab 	int err;
63519974710SMauro Carvalho Chehab 	dimm->mci = mci;
63619974710SMauro Carvalho Chehab 
63719974710SMauro Carvalho Chehab 	dimm->dev.type = &dimm_attr_type;
63819974710SMauro Carvalho Chehab 	device_initialize(&dimm->dev);
63919974710SMauro Carvalho Chehab 
64019974710SMauro Carvalho Chehab 	dimm->dev.parent = &mci->dev;
6419713faecSMauro Carvalho Chehab 	if (mci->csbased)
64219974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "rank%d", index);
64319974710SMauro Carvalho Chehab 	else
64419974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "dimm%d", index);
64519974710SMauro Carvalho Chehab 	dev_set_drvdata(&dimm->dev, dimm);
64619974710SMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
64719974710SMauro Carvalho Chehab 
64819974710SMauro Carvalho Chehab 	err =  device_add(&dimm->dev);
64919974710SMauro Carvalho Chehab 
650956b9ba1SJoe Perches 	edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev));
65119974710SMauro Carvalho Chehab 
65219974710SMauro Carvalho Chehab 	return err;
65319974710SMauro Carvalho Chehab }
6547a623c03SMauro Carvalho Chehab 
6557a623c03SMauro Carvalho Chehab /*
6567a623c03SMauro Carvalho Chehab  * Memory controller device
6577a623c03SMauro Carvalho Chehab  */
6587a623c03SMauro Carvalho Chehab 
6597a623c03SMauro Carvalho Chehab #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
6607a623c03SMauro Carvalho Chehab 
6617a623c03SMauro Carvalho Chehab static ssize_t mci_reset_counters_store(struct device *dev,
6627a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
6637c9281d7SDouglas Thompson 					const char *data, size_t count)
6647c9281d7SDouglas Thompson {
6657a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
6667a623c03SMauro Carvalho Chehab 	int cnt, row, chan, i;
6675926ff50SMauro Carvalho Chehab 	mci->ue_mc = 0;
6685926ff50SMauro Carvalho Chehab 	mci->ce_mc = 0;
6697a623c03SMauro Carvalho Chehab 	mci->ue_noinfo_count = 0;
6707a623c03SMauro Carvalho Chehab 	mci->ce_noinfo_count = 0;
6717c9281d7SDouglas Thompson 
6727c9281d7SDouglas Thompson 	for (row = 0; row < mci->nr_csrows; row++) {
673de3910ebSMauro Carvalho Chehab 		struct csrow_info *ri = mci->csrows[row];
6747c9281d7SDouglas Thompson 
6757c9281d7SDouglas Thompson 		ri->ue_count = 0;
6767c9281d7SDouglas Thompson 		ri->ce_count = 0;
6777c9281d7SDouglas Thompson 
6787c9281d7SDouglas Thompson 		for (chan = 0; chan < ri->nr_channels; chan++)
679de3910ebSMauro Carvalho Chehab 			ri->channels[chan]->ce_count = 0;
6807c9281d7SDouglas Thompson 	}
6817c9281d7SDouglas Thompson 
6827a623c03SMauro Carvalho Chehab 	cnt = 1;
6837a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
6847a623c03SMauro Carvalho Chehab 		cnt *= mci->layers[i].size;
6857a623c03SMauro Carvalho Chehab 		memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32));
6867a623c03SMauro Carvalho Chehab 		memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32));
6877a623c03SMauro Carvalho Chehab 	}
6887a623c03SMauro Carvalho Chehab 
6897c9281d7SDouglas Thompson 	mci->start_time = jiffies;
6907c9281d7SDouglas Thompson 	return count;
6917c9281d7SDouglas Thompson }
6927c9281d7SDouglas Thompson 
69339094443SBorislav Petkov /* Memory scrubbing interface:
69439094443SBorislav Petkov  *
69539094443SBorislav Petkov  * A MC driver can limit the scrubbing bandwidth based on the CPU type.
69639094443SBorislav Petkov  * Therefore, ->set_sdram_scrub_rate should be made to return the actual
69739094443SBorislav Petkov  * bandwidth that is accepted or 0 when scrubbing is to be disabled.
69839094443SBorislav Petkov  *
69939094443SBorislav Petkov  * Negative value still means that an error has occurred while setting
70039094443SBorislav Petkov  * the scrub rate.
70139094443SBorislav Petkov  */
7027a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
7037a623c03SMauro Carvalho Chehab 					  struct device_attribute *mattr,
7047c9281d7SDouglas Thompson 					  const char *data, size_t count)
7057c9281d7SDouglas Thompson {
7067a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
707eba042a8SBorislav Petkov 	unsigned long bandwidth = 0;
70839094443SBorislav Petkov 	int new_bw = 0;
7097c9281d7SDouglas Thompson 
710c7f62fc8SJingoo Han 	if (kstrtoul(data, 10, &bandwidth) < 0)
711eba042a8SBorislav Petkov 		return -EINVAL;
712eba042a8SBorislav Petkov 
71339094443SBorislav Petkov 	new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
7144949603aSMarkus Trippelsdorf 	if (new_bw < 0) {
7154949603aSMarkus Trippelsdorf 		edac_printk(KERN_WARNING, EDAC_MC,
7164949603aSMarkus Trippelsdorf 			    "Error setting scrub rate to: %lu\n", bandwidth);
7174949603aSMarkus Trippelsdorf 		return -EINVAL;
7187c9281d7SDouglas Thompson 	}
7197c9281d7SDouglas Thompson 
7204949603aSMarkus Trippelsdorf 	return count;
7217c9281d7SDouglas Thompson }
722eba042a8SBorislav Petkov 
72339094443SBorislav Petkov /*
72439094443SBorislav Petkov  * ->get_sdram_scrub_rate() return value semantics same as above.
72539094443SBorislav Petkov  */
7267a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
7277a623c03SMauro Carvalho Chehab 					 struct device_attribute *mattr,
7287a623c03SMauro Carvalho Chehab 					 char *data)
72939094443SBorislav Petkov {
7307a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
73139094443SBorislav Petkov 	int bandwidth = 0;
73239094443SBorislav Petkov 
73339094443SBorislav Petkov 	bandwidth = mci->get_sdram_scrub_rate(mci);
73439094443SBorislav Petkov 	if (bandwidth < 0) {
735eba042a8SBorislav Petkov 		edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n");
73639094443SBorislav Petkov 		return bandwidth;
737eba042a8SBorislav Petkov 	}
73839094443SBorislav Petkov 
7397c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", bandwidth);
7407c9281d7SDouglas Thompson }
7417c9281d7SDouglas Thompson 
7427c9281d7SDouglas Thompson /* default attribute files for the MCI object */
7437a623c03SMauro Carvalho Chehab static ssize_t mci_ue_count_show(struct device *dev,
7447a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7457a623c03SMauro Carvalho Chehab 				 char *data)
7467c9281d7SDouglas Thompson {
7477a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7487a623c03SMauro Carvalho Chehab 
7495926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ue_mc);
7507c9281d7SDouglas Thompson }
7517c9281d7SDouglas Thompson 
7527a623c03SMauro Carvalho Chehab static ssize_t mci_ce_count_show(struct device *dev,
7537a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7547a623c03SMauro Carvalho Chehab 				 char *data)
7557c9281d7SDouglas Thompson {
7567a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7577a623c03SMauro Carvalho Chehab 
7585926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ce_mc);
7597c9281d7SDouglas Thompson }
7607c9281d7SDouglas Thompson 
7617a623c03SMauro Carvalho Chehab static ssize_t mci_ce_noinfo_show(struct device *dev,
7627a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7637a623c03SMauro Carvalho Chehab 				  char *data)
7647c9281d7SDouglas Thompson {
7657a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7667a623c03SMauro Carvalho Chehab 
7677c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ce_noinfo_count);
7687c9281d7SDouglas Thompson }
7697c9281d7SDouglas Thompson 
7707a623c03SMauro Carvalho Chehab static ssize_t mci_ue_noinfo_show(struct device *dev,
7717a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7727a623c03SMauro Carvalho Chehab 				  char *data)
7737c9281d7SDouglas Thompson {
7747a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7757a623c03SMauro Carvalho Chehab 
7767c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ue_noinfo_count);
7777c9281d7SDouglas Thompson }
7787c9281d7SDouglas Thompson 
7797a623c03SMauro Carvalho Chehab static ssize_t mci_seconds_show(struct device *dev,
7807a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
7817a623c03SMauro Carvalho Chehab 				char *data)
7827c9281d7SDouglas Thompson {
7837a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7847a623c03SMauro Carvalho Chehab 
7857c9281d7SDouglas Thompson 	return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
7867c9281d7SDouglas Thompson }
7877c9281d7SDouglas Thompson 
7887a623c03SMauro Carvalho Chehab static ssize_t mci_ctl_name_show(struct device *dev,
7897a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7907a623c03SMauro Carvalho Chehab 				 char *data)
7917c9281d7SDouglas Thompson {
7927a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7937a623c03SMauro Carvalho Chehab 
7947c9281d7SDouglas Thompson 	return sprintf(data, "%s\n", mci->ctl_name);
7957c9281d7SDouglas Thompson }
7967c9281d7SDouglas Thompson 
7977a623c03SMauro Carvalho Chehab static ssize_t mci_size_mb_show(struct device *dev,
7987a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
7997a623c03SMauro Carvalho Chehab 				char *data)
8007c9281d7SDouglas Thompson {
8017a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
802a895bf8bSMauro Carvalho Chehab 	int total_pages = 0, csrow_idx, j;
8037c9281d7SDouglas Thompson 
804a895bf8bSMauro Carvalho Chehab 	for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
805de3910ebSMauro Carvalho Chehab 		struct csrow_info *csrow = mci->csrows[csrow_idx];
8067c9281d7SDouglas Thompson 
807a895bf8bSMauro Carvalho Chehab 		for (j = 0; j < csrow->nr_channels; j++) {
808de3910ebSMauro Carvalho Chehab 			struct dimm_info *dimm = csrow->channels[j]->dimm;
8097c9281d7SDouglas Thompson 
810a895bf8bSMauro Carvalho Chehab 			total_pages += dimm->nr_pages;
811a895bf8bSMauro Carvalho Chehab 		}
8127c9281d7SDouglas Thompson 	}
8137c9281d7SDouglas Thompson 
8147c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
8157c9281d7SDouglas Thompson }
8167c9281d7SDouglas Thompson 
8178ad6c78aSMauro Carvalho Chehab static ssize_t mci_max_location_show(struct device *dev,
8188ad6c78aSMauro Carvalho Chehab 				     struct device_attribute *mattr,
8198ad6c78aSMauro Carvalho Chehab 				     char *data)
8208ad6c78aSMauro Carvalho Chehab {
8218ad6c78aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
8228ad6c78aSMauro Carvalho Chehab 	int i;
8238ad6c78aSMauro Carvalho Chehab 	char *p = data;
8248ad6c78aSMauro Carvalho Chehab 
8258ad6c78aSMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
8268ad6c78aSMauro Carvalho Chehab 		p += sprintf(p, "%s %d ",
8278ad6c78aSMauro Carvalho Chehab 			     edac_layer_name[mci->layers[i].type],
8288ad6c78aSMauro Carvalho Chehab 			     mci->layers[i].size - 1);
8298ad6c78aSMauro Carvalho Chehab 	}
8308ad6c78aSMauro Carvalho Chehab 
8318ad6c78aSMauro Carvalho Chehab 	return p - data;
8328ad6c78aSMauro Carvalho Chehab }
8338ad6c78aSMauro Carvalho Chehab 
8347c9281d7SDouglas Thompson /* default Control file */
835f11135d8SBorislav Petkov static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
8367c9281d7SDouglas Thompson 
8377c9281d7SDouglas Thompson /* default Attribute files */
838f11135d8SBorislav Petkov static DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
839f11135d8SBorislav Petkov static DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
840f11135d8SBorislav Petkov static DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
841f11135d8SBorislav Petkov static DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
842f11135d8SBorislav Petkov static DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
843f11135d8SBorislav Petkov static DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
844f11135d8SBorislav Petkov static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
845f11135d8SBorislav Petkov static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
8467c9281d7SDouglas Thompson 
8477c9281d7SDouglas Thompson /* memory scrubber attribute file */
848628ea92fSBen Dooks static DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show,
8492c1946b6STakashi Iwai 	    mci_sdram_scrub_rate_store); /* umode set later in is_visible */
8507c9281d7SDouglas Thompson 
8517a623c03SMauro Carvalho Chehab static struct attribute *mci_attrs[] = {
8527a623c03SMauro Carvalho Chehab 	&dev_attr_reset_counters.attr,
8537a623c03SMauro Carvalho Chehab 	&dev_attr_mc_name.attr,
8547a623c03SMauro Carvalho Chehab 	&dev_attr_size_mb.attr,
8557a623c03SMauro Carvalho Chehab 	&dev_attr_seconds_since_reset.attr,
8567a623c03SMauro Carvalho Chehab 	&dev_attr_ue_noinfo_count.attr,
8577a623c03SMauro Carvalho Chehab 	&dev_attr_ce_noinfo_count.attr,
8587a623c03SMauro Carvalho Chehab 	&dev_attr_ue_count.attr,
8597a623c03SMauro Carvalho Chehab 	&dev_attr_ce_count.attr,
8608ad6c78aSMauro Carvalho Chehab 	&dev_attr_max_location.attr,
8612c1946b6STakashi Iwai 	&dev_attr_sdram_scrub_rate.attr,
8627c9281d7SDouglas Thompson 	NULL
8637c9281d7SDouglas Thompson };
8647c9281d7SDouglas Thompson 
8652c1946b6STakashi Iwai static umode_t mci_attr_is_visible(struct kobject *kobj,
8662c1946b6STakashi Iwai 				   struct attribute *attr, int idx)
8672c1946b6STakashi Iwai {
8682c1946b6STakashi Iwai 	struct device *dev = kobj_to_dev(kobj);
8692c1946b6STakashi Iwai 	struct mem_ctl_info *mci = to_mci(dev);
8702c1946b6STakashi Iwai 	umode_t mode = 0;
8712c1946b6STakashi Iwai 
8722c1946b6STakashi Iwai 	if (attr != &dev_attr_sdram_scrub_rate.attr)
8732c1946b6STakashi Iwai 		return attr->mode;
8742c1946b6STakashi Iwai 	if (mci->get_sdram_scrub_rate)
8752c1946b6STakashi Iwai 		mode |= S_IRUGO;
8762c1946b6STakashi Iwai 	if (mci->set_sdram_scrub_rate)
8772c1946b6STakashi Iwai 		mode |= S_IWUSR;
8782c1946b6STakashi Iwai 	return mode;
8792c1946b6STakashi Iwai }
8802c1946b6STakashi Iwai 
8811c18be5aSArvind Yadav static const struct attribute_group mci_attr_grp = {
8827a623c03SMauro Carvalho Chehab 	.attrs	= mci_attrs,
8832c1946b6STakashi Iwai 	.is_visible = mci_attr_is_visible,
8847c9281d7SDouglas Thompson };
8857c9281d7SDouglas Thompson 
8867a623c03SMauro Carvalho Chehab static const struct attribute_group *mci_attr_groups[] = {
8877a623c03SMauro Carvalho Chehab 	&mci_attr_grp,
8887a623c03SMauro Carvalho Chehab 	NULL
889cc301b3aSMauro Carvalho Chehab };
890cc301b3aSMauro Carvalho Chehab 
891de3910ebSMauro Carvalho Chehab static void mci_attr_release(struct device *dev)
8927a623c03SMauro Carvalho Chehab {
893de3910ebSMauro Carvalho Chehab 	struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
894de3910ebSMauro Carvalho Chehab 
895956b9ba1SJoe Perches 	edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
896de3910ebSMauro Carvalho Chehab 	kfree(mci);
8977a623c03SMauro Carvalho Chehab }
8987a623c03SMauro Carvalho Chehab 
899b2b3e736SBhumika Goyal static const struct device_type mci_attr_type = {
9007a623c03SMauro Carvalho Chehab 	.groups		= mci_attr_groups,
9017a623c03SMauro Carvalho Chehab 	.release	= mci_attr_release,
902cc301b3aSMauro Carvalho Chehab };
903cc301b3aSMauro Carvalho Chehab 
9047c9281d7SDouglas Thompson /*
9057c9281d7SDouglas Thompson  * Create a new Memory Controller kobject instance,
9067c9281d7SDouglas Thompson  *	mc<id> under the 'mc' directory
9077c9281d7SDouglas Thompson  *
9087c9281d7SDouglas Thompson  * Return:
9097c9281d7SDouglas Thompson  *	0	Success
9107c9281d7SDouglas Thompson  *	!0	Failure
9117c9281d7SDouglas Thompson  */
9124e8d230dSTakashi Iwai int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
9134e8d230dSTakashi Iwai 				 const struct attribute_group **groups)
9147c9281d7SDouglas Thompson {
9157a623c03SMauro Carvalho Chehab 	int i, err;
9167c9281d7SDouglas Thompson 
917de3910ebSMauro Carvalho Chehab 	/* get the /sys/devices/system/edac subsys reference */
918de3910ebSMauro Carvalho Chehab 	mci->dev.type = &mci_attr_type;
919de3910ebSMauro Carvalho Chehab 	device_initialize(&mci->dev);
920de3910ebSMauro Carvalho Chehab 
921de3910ebSMauro Carvalho Chehab 	mci->dev.parent = mci_pdev;
9224e8d230dSTakashi Iwai 	mci->dev.groups = groups;
923de3910ebSMauro Carvalho Chehab 	dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
924de3910ebSMauro Carvalho Chehab 	dev_set_drvdata(&mci->dev, mci);
925de3910ebSMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
926de3910ebSMauro Carvalho Chehab 
927956b9ba1SJoe Perches 	edac_dbg(0, "creating device %s\n", dev_name(&mci->dev));
9287a623c03SMauro Carvalho Chehab 	err = device_add(&mci->dev);
9297a623c03SMauro Carvalho Chehab 	if (err < 0) {
9303d958823SMauro Carvalho Chehab 		edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev));
931861e6ed6SBorislav Petkov 		goto out;
9328096cfafSDoug Thompson 	}
93342a8e397SDouglas Thompson 
9347a623c03SMauro Carvalho Chehab 	/*
9357a623c03SMauro Carvalho Chehab 	 * Create the dimm/rank devices
9367c9281d7SDouglas Thompson 	 */
9377a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
938de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
9397a623c03SMauro Carvalho Chehab 		/* Only expose populated DIMMs */
9401bf1950cSJunjie Mao 		if (!dimm->nr_pages)
9417a623c03SMauro Carvalho Chehab 			continue;
9421bf1950cSJunjie Mao 
9437a623c03SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
944956b9ba1SJoe Perches 		edac_dbg(1, "creating dimm%d, located at ", i);
9457a623c03SMauro Carvalho Chehab 		if (edac_debug_level >= 1) {
9467a623c03SMauro Carvalho Chehab 			int lay;
9477a623c03SMauro Carvalho Chehab 			for (lay = 0; lay < mci->n_layers; lay++)
9487a623c03SMauro Carvalho Chehab 				printk(KERN_CONT "%s %d ",
9497a623c03SMauro Carvalho Chehab 					edac_layer_name[mci->layers[lay].type],
9507a623c03SMauro Carvalho Chehab 					dimm->location[lay]);
9517a623c03SMauro Carvalho Chehab 			printk(KERN_CONT "\n");
9527c9281d7SDouglas Thompson 		}
9537a623c03SMauro Carvalho Chehab #endif
95419974710SMauro Carvalho Chehab 		err = edac_create_dimm_object(mci, dimm, i);
95519974710SMauro Carvalho Chehab 		if (err) {
956956b9ba1SJoe Perches 			edac_dbg(1, "failure: create dimm %d obj\n", i);
9571bf1950cSJunjie Mao 			goto fail_unregister_dimm;
95819974710SMauro Carvalho Chehab 		}
9597c9281d7SDouglas Thompson 	}
9607a623c03SMauro Carvalho Chehab 
96119974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
9627a623c03SMauro Carvalho Chehab 	err = edac_create_csrow_objects(mci);
9637a623c03SMauro Carvalho Chehab 	if (err < 0)
9641bf1950cSJunjie Mao 		goto fail_unregister_dimm;
96519974710SMauro Carvalho Chehab #endif
9667c9281d7SDouglas Thompson 
9677ac8bf9bSBorislav Petkov 	edac_create_debugfs_nodes(mci);
9687c9281d7SDouglas Thompson 	return 0;
9697c9281d7SDouglas Thompson 
9701bf1950cSJunjie Mao fail_unregister_dimm:
9717c9281d7SDouglas Thompson 	for (i--; i >= 0; i--) {
972de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
9731bf1950cSJunjie Mao 		if (!dimm->nr_pages)
9747a623c03SMauro Carvalho Chehab 			continue;
9751bf1950cSJunjie Mao 
97644d22e24SLans Zhang 		device_unregister(&dimm->dev);
9777c9281d7SDouglas Thompson 	}
97844d22e24SLans Zhang 	device_unregister(&mci->dev);
97912e26969SBorislav Petkov 
980861e6ed6SBorislav Petkov out:
9817c9281d7SDouglas Thompson 	return err;
9827c9281d7SDouglas Thompson }
9837c9281d7SDouglas Thompson 
9847c9281d7SDouglas Thompson /*
9857c9281d7SDouglas Thompson  * remove a Memory Controller instance
9867c9281d7SDouglas Thompson  */
9877c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
9887c9281d7SDouglas Thompson {
9897a623c03SMauro Carvalho Chehab 	int i;
9907c9281d7SDouglas Thompson 
991956b9ba1SJoe Perches 	edac_dbg(0, "\n");
9927c9281d7SDouglas Thompson 
993452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
99430f84a89STan Xiaojun 	edac_debugfs_remove_recursive(mci->debugfs);
995452a6bf9SMauro Carvalho Chehab #endif
99619974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
9977a623c03SMauro Carvalho Chehab 	edac_delete_csrow_objects(mci);
99819974710SMauro Carvalho Chehab #endif
999a895bf8bSMauro Carvalho Chehab 
10007a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
1001de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
10027a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
10037a623c03SMauro Carvalho Chehab 			continue;
1004956b9ba1SJoe Perches 		edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev));
100544d22e24SLans Zhang 		device_unregister(&dimm->dev);
10067c9281d7SDouglas Thompson 	}
10077c9281d7SDouglas Thompson }
10087c9281d7SDouglas Thompson 
10097a623c03SMauro Carvalho Chehab void edac_unregister_sysfs(struct mem_ctl_info *mci)
10108096cfafSDoug Thompson {
1011956b9ba1SJoe Perches 	edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
101244d22e24SLans Zhang 	device_unregister(&mci->dev);
10137a623c03SMauro Carvalho Chehab }
10148096cfafSDoug Thompson 
1015de3910ebSMauro Carvalho Chehab static void mc_attr_release(struct device *dev)
10167a623c03SMauro Carvalho Chehab {
1017de3910ebSMauro Carvalho Chehab 	/*
1018de3910ebSMauro Carvalho Chehab 	 * There's no container structure here, as this is just the mci
1019de3910ebSMauro Carvalho Chehab 	 * parent device, used to create the /sys/devices/mc sysfs node.
1020de3910ebSMauro Carvalho Chehab 	 * So, there are no attributes on it.
1021de3910ebSMauro Carvalho Chehab 	 */
1022956b9ba1SJoe Perches 	edac_dbg(1, "Releasing device %s\n", dev_name(dev));
1023de3910ebSMauro Carvalho Chehab 	kfree(dev);
10247a623c03SMauro Carvalho Chehab }
10257a623c03SMauro Carvalho Chehab 
1026b2b3e736SBhumika Goyal static const struct device_type mc_attr_type = {
10277a623c03SMauro Carvalho Chehab 	.release	= mc_attr_release,
10287a623c03SMauro Carvalho Chehab };
10297a623c03SMauro Carvalho Chehab /*
10307a623c03SMauro Carvalho Chehab  * Init/exit code for the module. Basically, creates/removes /sys/class/rc
10317a623c03SMauro Carvalho Chehab  */
10327a623c03SMauro Carvalho Chehab int __init edac_mc_sysfs_init(void)
10337a623c03SMauro Carvalho Chehab {
10347a623c03SMauro Carvalho Chehab 	int err;
10358096cfafSDoug Thompson 
1036de3910ebSMauro Carvalho Chehab 	mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
10372d56b109SDenis Kirjanov 	if (!mci_pdev) {
10382d56b109SDenis Kirjanov 		err = -ENOMEM;
1039733476cfSBorislav Petkov 		goto out;
10402d56b109SDenis Kirjanov 	}
10418096cfafSDoug Thompson 
1042d4538000SBorislav Petkov 	mci_pdev->bus = edac_get_sysfs_subsys();
1043de3910ebSMauro Carvalho Chehab 	mci_pdev->type = &mc_attr_type;
1044de3910ebSMauro Carvalho Chehab 	device_initialize(mci_pdev);
1045de3910ebSMauro Carvalho Chehab 	dev_set_name(mci_pdev, "mc");
1046de3910ebSMauro Carvalho Chehab 
1047de3910ebSMauro Carvalho Chehab 	err = device_add(mci_pdev);
10487a623c03SMauro Carvalho Chehab 	if (err < 0)
10494708aa85SJohan Hovold 		goto out_put_device;
10508096cfafSDoug Thompson 
1051956b9ba1SJoe Perches 	edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
1052de3910ebSMauro Carvalho Chehab 
10538096cfafSDoug Thompson 	return 0;
10542d56b109SDenis Kirjanov 
10554708aa85SJohan Hovold  out_put_device:
10564708aa85SJohan Hovold 	put_device(mci_pdev);
10572d56b109SDenis Kirjanov  out:
10582d56b109SDenis Kirjanov 	return err;
10598096cfafSDoug Thompson }
10608096cfafSDoug Thompson 
1061c6b97bcfSAlexey Khoroshilov void edac_mc_sysfs_exit(void)
10628096cfafSDoug Thompson {
106344d22e24SLans Zhang 	device_unregister(mci_pdev);
10648096cfafSDoug Thompson }
1065