xref: /openbmc/linux/drivers/edac/edac_mc_sysfs.c (revision 30f84a89)
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 
2220bcb7a8SDouglas Thompson #include "edac_core.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 
53096846e2SArthur Jones static int edac_set_poll_msec(const char *val, 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 mem_types[] = {
957c9281d7SDouglas Thompson 	[MEM_EMPTY] = "Empty",
967c9281d7SDouglas Thompson 	[MEM_RESERVED] = "Reserved",
977c9281d7SDouglas Thompson 	[MEM_UNKNOWN] = "Unknown",
987c9281d7SDouglas Thompson 	[MEM_FPM] = "FPM",
997c9281d7SDouglas Thompson 	[MEM_EDO] = "EDO",
1007c9281d7SDouglas Thompson 	[MEM_BEDO] = "BEDO",
1017c9281d7SDouglas Thompson 	[MEM_SDR] = "Unbuffered-SDR",
1027c9281d7SDouglas Thompson 	[MEM_RDR] = "Registered-SDR",
1037c9281d7SDouglas Thompson 	[MEM_DDR] = "Unbuffered-DDR",
1047c9281d7SDouglas Thompson 	[MEM_RDDR] = "Registered-DDR",
1051a9b85e6SDave Jiang 	[MEM_RMBS] = "RMBS",
1061a9b85e6SDave Jiang 	[MEM_DDR2] = "Unbuffered-DDR2",
1071a9b85e6SDave Jiang 	[MEM_FB_DDR2] = "FullyBuffered-DDR2",
1081d5f726cSBenjamin Herrenschmidt 	[MEM_RDDR2] = "Registered-DDR2",
109b1cfebc9SYang Shi 	[MEM_XDR] = "XDR",
110b1cfebc9SYang Shi 	[MEM_DDR3] = "Unbuffered-DDR3",
1117b827835SAristeu Rozanski 	[MEM_RDDR3] = "Registered-DDR3",
1127b827835SAristeu Rozanski 	[MEM_DDR4] = "Unbuffered-DDR4",
1137b827835SAristeu Rozanski 	[MEM_RDDR4] = "Registered-DDR4"
1147c9281d7SDouglas Thompson };
1157c9281d7SDouglas Thompson 
1168b7719e0SBorislav Petkov static const char * const dev_types[] = {
1177c9281d7SDouglas Thompson 	[DEV_UNKNOWN] = "Unknown",
1187c9281d7SDouglas Thompson 	[DEV_X1] = "x1",
1197c9281d7SDouglas Thompson 	[DEV_X2] = "x2",
1207c9281d7SDouglas Thompson 	[DEV_X4] = "x4",
1217c9281d7SDouglas Thompson 	[DEV_X8] = "x8",
1227c9281d7SDouglas Thompson 	[DEV_X16] = "x16",
1237c9281d7SDouglas Thompson 	[DEV_X32] = "x32",
1247c9281d7SDouglas Thompson 	[DEV_X64] = "x64"
1257c9281d7SDouglas Thompson };
1267c9281d7SDouglas Thompson 
1278b7719e0SBorislav Petkov static const char * const edac_caps[] = {
1287c9281d7SDouglas Thompson 	[EDAC_UNKNOWN] = "Unknown",
1297c9281d7SDouglas Thompson 	[EDAC_NONE] = "None",
1307c9281d7SDouglas Thompson 	[EDAC_RESERVED] = "Reserved",
1317c9281d7SDouglas Thompson 	[EDAC_PARITY] = "PARITY",
1327c9281d7SDouglas Thompson 	[EDAC_EC] = "EC",
1337c9281d7SDouglas Thompson 	[EDAC_SECDED] = "SECDED",
1347c9281d7SDouglas Thompson 	[EDAC_S2ECD2ED] = "S2ECD2ED",
1357c9281d7SDouglas Thompson 	[EDAC_S4ECD4ED] = "S4ECD4ED",
1367c9281d7SDouglas Thompson 	[EDAC_S8ECD8ED] = "S8ECD8ED",
1377c9281d7SDouglas Thompson 	[EDAC_S16ECD16ED] = "S16ECD16ED"
1387c9281d7SDouglas Thompson };
1397c9281d7SDouglas Thompson 
14019974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
1417a623c03SMauro Carvalho Chehab /*
1427a623c03SMauro Carvalho Chehab  * EDAC sysfs CSROW data structures and methods
1437c9281d7SDouglas Thompson  */
1447c9281d7SDouglas Thompson 
1457a623c03SMauro Carvalho Chehab #define to_csrow(k) container_of(k, struct csrow_info, dev)
1467a623c03SMauro Carvalho Chehab 
1477a623c03SMauro Carvalho Chehab /*
1487a623c03SMauro Carvalho Chehab  * We need it to avoid namespace conflicts between the legacy API
1497a623c03SMauro Carvalho Chehab  * and the per-dimm/per-rank one
1507a623c03SMauro Carvalho Chehab  */
1517a623c03SMauro Carvalho Chehab #define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \
152fbe2d361SStephen Hemminger 	static struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store)
1537a623c03SMauro Carvalho Chehab 
1547a623c03SMauro Carvalho Chehab struct dev_ch_attribute {
1557a623c03SMauro Carvalho Chehab 	struct device_attribute attr;
1567a623c03SMauro Carvalho Chehab 	int channel;
1577a623c03SMauro Carvalho Chehab };
1587a623c03SMauro Carvalho Chehab 
1597a623c03SMauro Carvalho Chehab #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \
160f11135d8SBorislav Petkov 	static struct dev_ch_attribute dev_attr_legacy_##_name = \
1617a623c03SMauro Carvalho Chehab 		{ __ATTR(_name, _mode, _show, _store), (_var) }
1627a623c03SMauro Carvalho Chehab 
1637a623c03SMauro Carvalho Chehab #define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel)
1647a623c03SMauro Carvalho Chehab 
1657c9281d7SDouglas Thompson /* Set of more default csrow<id> attribute show/store functions */
1667a623c03SMauro Carvalho Chehab static ssize_t csrow_ue_count_show(struct device *dev,
1677a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1687c9281d7SDouglas Thompson {
1697a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1707a623c03SMauro Carvalho Chehab 
1717c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", csrow->ue_count);
1727c9281d7SDouglas Thompson }
1737c9281d7SDouglas Thompson 
1747a623c03SMauro Carvalho Chehab static ssize_t csrow_ce_count_show(struct device *dev,
1757a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1767c9281d7SDouglas Thompson {
1777a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1787a623c03SMauro Carvalho Chehab 
1797c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", csrow->ce_count);
1807c9281d7SDouglas Thompson }
1817c9281d7SDouglas Thompson 
1827a623c03SMauro Carvalho Chehab static ssize_t csrow_size_show(struct device *dev,
1837a623c03SMauro Carvalho Chehab 			       struct device_attribute *mattr, char *data)
1847c9281d7SDouglas Thompson {
1857a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
186a895bf8bSMauro Carvalho Chehab 	int i;
187a895bf8bSMauro Carvalho Chehab 	u32 nr_pages = 0;
188a895bf8bSMauro Carvalho Chehab 
189a895bf8bSMauro Carvalho Chehab 	for (i = 0; i < csrow->nr_channels; i++)
190de3910ebSMauro Carvalho Chehab 		nr_pages += csrow->channels[i]->dimm->nr_pages;
191a895bf8bSMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages));
1927c9281d7SDouglas Thompson }
1937c9281d7SDouglas Thompson 
1947a623c03SMauro Carvalho Chehab static ssize_t csrow_mem_type_show(struct device *dev,
1957a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1967c9281d7SDouglas Thompson {
1977a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1987a623c03SMauro Carvalho Chehab 
199de3910ebSMauro Carvalho Chehab 	return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]);
2007c9281d7SDouglas Thompson }
2017c9281d7SDouglas Thompson 
2027a623c03SMauro Carvalho Chehab static ssize_t csrow_dev_type_show(struct device *dev,
2037a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
2047c9281d7SDouglas Thompson {
2057a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2067a623c03SMauro Carvalho Chehab 
207de3910ebSMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]);
2087c9281d7SDouglas Thompson }
2097c9281d7SDouglas Thompson 
2107a623c03SMauro Carvalho Chehab static ssize_t csrow_edac_mode_show(struct device *dev,
2117a623c03SMauro Carvalho Chehab 				    struct device_attribute *mattr,
2127a623c03SMauro Carvalho Chehab 				    char *data)
2137c9281d7SDouglas Thompson {
2147a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2157a623c03SMauro Carvalho Chehab 
216de3910ebSMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]);
2177c9281d7SDouglas Thompson }
2187c9281d7SDouglas Thompson 
2197c9281d7SDouglas Thompson /* show/store functions for DIMM Label attributes */
2207a623c03SMauro Carvalho Chehab static ssize_t channel_dimm_label_show(struct device *dev,
2217a623c03SMauro Carvalho Chehab 				       struct device_attribute *mattr,
2227a623c03SMauro Carvalho Chehab 				       char *data)
2237c9281d7SDouglas Thompson {
2247a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2257a623c03SMauro Carvalho Chehab 	unsigned chan = to_channel(mattr);
226de3910ebSMauro Carvalho Chehab 	struct rank_info *rank = csrow->channels[chan];
2277a623c03SMauro Carvalho Chehab 
228124682c7SArthur Jones 	/* if field has not been initialized, there is nothing to send */
2297a623c03SMauro Carvalho Chehab 	if (!rank->dimm->label[0])
230124682c7SArthur Jones 		return 0;
231124682c7SArthur Jones 
2321ea62c59SToshi Kani 	return snprintf(data, sizeof(rank->dimm->label) + 1, "%s\n",
2337a623c03SMauro Carvalho Chehab 			rank->dimm->label);
2347c9281d7SDouglas Thompson }
2357c9281d7SDouglas Thompson 
2367a623c03SMauro Carvalho Chehab static ssize_t channel_dimm_label_store(struct device *dev,
2377a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
2387a623c03SMauro Carvalho Chehab 					const char *data, size_t count)
2397c9281d7SDouglas Thompson {
2407a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2417a623c03SMauro Carvalho Chehab 	unsigned chan = to_channel(mattr);
242de3910ebSMauro Carvalho Chehab 	struct rank_info *rank = csrow->channels[chan];
243438470b8SToshi Kani 	size_t copy_count = count;
2447a623c03SMauro Carvalho Chehab 
245438470b8SToshi Kani 	if (count == 0)
246438470b8SToshi Kani 		return -EINVAL;
2477c9281d7SDouglas Thompson 
248438470b8SToshi Kani 	if (data[count - 1] == '\0' || data[count - 1] == '\n')
249438470b8SToshi Kani 		copy_count -= 1;
2507c9281d7SDouglas Thompson 
251d0c9c930SToshi Kani 	if (copy_count == 0 || copy_count >= sizeof(rank->dimm->label))
252438470b8SToshi Kani 		return -EINVAL;
253438470b8SToshi Kani 
254438470b8SToshi Kani 	strncpy(rank->dimm->label, data, copy_count);
255438470b8SToshi Kani 	rank->dimm->label[copy_count] = '\0';
256438470b8SToshi Kani 
257438470b8SToshi Kani 	return count;
2587c9281d7SDouglas Thompson }
2597c9281d7SDouglas Thompson 
2607c9281d7SDouglas Thompson /* show function for dynamic chX_ce_count attribute */
2617a623c03SMauro Carvalho Chehab static ssize_t channel_ce_count_show(struct device *dev,
2627a623c03SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
2637c9281d7SDouglas Thompson {
2647a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2657a623c03SMauro Carvalho Chehab 	unsigned chan = to_channel(mattr);
266de3910ebSMauro Carvalho Chehab 	struct rank_info *rank = csrow->channels[chan];
2677a623c03SMauro Carvalho Chehab 
2687a623c03SMauro Carvalho Chehab 	return sprintf(data, "%u\n", rank->ce_count);
2697c9281d7SDouglas Thompson }
2707c9281d7SDouglas Thompson 
2717a623c03SMauro Carvalho Chehab /* cwrow<id>/attribute files */
2727a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL);
2737a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL);
2747a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL);
2757a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL);
2767a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL);
2777a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL);
2787c9281d7SDouglas Thompson 
2797c9281d7SDouglas Thompson /* default attributes of the CSROW<id> object */
2807a623c03SMauro Carvalho Chehab static struct attribute *csrow_attrs[] = {
2817a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_dev_type.attr,
2827a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_mem_type.attr,
2837a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_edac_mode.attr,
2847a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_size_mb.attr,
2857a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ue_count.attr,
2867a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ce_count.attr,
2877c9281d7SDouglas Thompson 	NULL,
2887c9281d7SDouglas Thompson };
2897c9281d7SDouglas Thompson 
2907a623c03SMauro Carvalho Chehab static struct attribute_group csrow_attr_grp = {
2917a623c03SMauro Carvalho Chehab 	.attrs	= csrow_attrs,
2927c9281d7SDouglas Thompson };
2937c9281d7SDouglas Thompson 
2947a623c03SMauro Carvalho Chehab static const struct attribute_group *csrow_attr_groups[] = {
2957a623c03SMauro Carvalho Chehab 	&csrow_attr_grp,
2967a623c03SMauro Carvalho Chehab 	NULL
2977c9281d7SDouglas Thompson };
2987c9281d7SDouglas Thompson 
299de3910ebSMauro Carvalho Chehab static void csrow_attr_release(struct device *dev)
3007a623c03SMauro Carvalho Chehab {
301de3910ebSMauro Carvalho Chehab 	struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
302de3910ebSMauro Carvalho Chehab 
303956b9ba1SJoe Perches 	edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
304de3910ebSMauro Carvalho Chehab 	kfree(csrow);
3057a623c03SMauro Carvalho Chehab }
3067a623c03SMauro Carvalho Chehab 
3077a623c03SMauro Carvalho Chehab static struct device_type csrow_attr_type = {
3087a623c03SMauro Carvalho Chehab 	.groups		= csrow_attr_groups,
3097a623c03SMauro Carvalho Chehab 	.release	= csrow_attr_release,
3107a623c03SMauro Carvalho Chehab };
3117a623c03SMauro Carvalho Chehab 
3127a623c03SMauro Carvalho Chehab /*
3137a623c03SMauro Carvalho Chehab  * possible dynamic channel DIMM Label attribute files
3147a623c03SMauro Carvalho Chehab  *
3157a623c03SMauro Carvalho Chehab  */
3167a623c03SMauro Carvalho Chehab 
3177a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
3187a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 0);
3197a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
3207a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 1);
3217a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR,
3227a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 2);
3237a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR,
3247a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 3);
3257a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR,
3267a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 4);
3277a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR,
3287a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 5);
3297c9281d7SDouglas Thompson 
3307a623c03SMauro Carvalho Chehab /* Total possible dynamic DIMM Label attribute file table */
3312c1946b6STakashi Iwai static struct attribute *dynamic_csrow_dimm_attr[] = {
3322c1946b6STakashi Iwai 	&dev_attr_legacy_ch0_dimm_label.attr.attr,
3332c1946b6STakashi Iwai 	&dev_attr_legacy_ch1_dimm_label.attr.attr,
3342c1946b6STakashi Iwai 	&dev_attr_legacy_ch2_dimm_label.attr.attr,
3352c1946b6STakashi Iwai 	&dev_attr_legacy_ch3_dimm_label.attr.attr,
3362c1946b6STakashi Iwai 	&dev_attr_legacy_ch4_dimm_label.attr.attr,
3372c1946b6STakashi Iwai 	&dev_attr_legacy_ch5_dimm_label.attr.attr,
3382c1946b6STakashi Iwai 	NULL
3397a623c03SMauro Carvalho Chehab };
3407c9281d7SDouglas Thompson 
3417a623c03SMauro Carvalho Chehab /* possible dynamic channel ce_count attribute files */
342c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch0_ce_count, S_IRUGO,
3437a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 0);
344c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch1_ce_count, S_IRUGO,
3457a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 1);
346c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch2_ce_count, S_IRUGO,
3477a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 2);
348c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch3_ce_count, S_IRUGO,
3497a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 3);
350c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch4_ce_count, S_IRUGO,
3517a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 4);
352c8c64d16SSrivatsa S. Bhat DEVICE_CHANNEL(ch5_ce_count, S_IRUGO,
3537a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 5);
3547c9281d7SDouglas Thompson 
3557a623c03SMauro Carvalho Chehab /* Total possible dynamic ce_count attribute file table */
3562c1946b6STakashi Iwai static struct attribute *dynamic_csrow_ce_count_attr[] = {
3572c1946b6STakashi Iwai 	&dev_attr_legacy_ch0_ce_count.attr.attr,
3582c1946b6STakashi Iwai 	&dev_attr_legacy_ch1_ce_count.attr.attr,
3592c1946b6STakashi Iwai 	&dev_attr_legacy_ch2_ce_count.attr.attr,
3602c1946b6STakashi Iwai 	&dev_attr_legacy_ch3_ce_count.attr.attr,
3612c1946b6STakashi Iwai 	&dev_attr_legacy_ch4_ce_count.attr.attr,
3622c1946b6STakashi Iwai 	&dev_attr_legacy_ch5_ce_count.attr.attr,
3632c1946b6STakashi Iwai 	NULL
3642c1946b6STakashi Iwai };
3652c1946b6STakashi Iwai 
3662c1946b6STakashi Iwai static umode_t csrow_dev_is_visible(struct kobject *kobj,
3672c1946b6STakashi Iwai 				    struct attribute *attr, int idx)
3682c1946b6STakashi Iwai {
3692c1946b6STakashi Iwai 	struct device *dev = kobj_to_dev(kobj);
3702c1946b6STakashi Iwai 	struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
3712c1946b6STakashi Iwai 
3722c1946b6STakashi Iwai 	if (idx >= csrow->nr_channels)
3732c1946b6STakashi Iwai 		return 0;
3742c1946b6STakashi Iwai 	/* Only expose populated DIMMs */
3752c1946b6STakashi Iwai 	if (!csrow->channels[idx]->dimm->nr_pages)
3762c1946b6STakashi Iwai 		return 0;
3772c1946b6STakashi Iwai 	return attr->mode;
3782c1946b6STakashi Iwai }
3792c1946b6STakashi Iwai 
3802c1946b6STakashi Iwai 
3812c1946b6STakashi Iwai static const struct attribute_group csrow_dev_dimm_group = {
3822c1946b6STakashi Iwai 	.attrs = dynamic_csrow_dimm_attr,
3832c1946b6STakashi Iwai 	.is_visible = csrow_dev_is_visible,
3842c1946b6STakashi Iwai };
3852c1946b6STakashi Iwai 
3862c1946b6STakashi Iwai static const struct attribute_group csrow_dev_ce_count_group = {
3872c1946b6STakashi Iwai 	.attrs = dynamic_csrow_ce_count_attr,
3882c1946b6STakashi Iwai 	.is_visible = csrow_dev_is_visible,
3892c1946b6STakashi Iwai };
3902c1946b6STakashi Iwai 
3912c1946b6STakashi Iwai static const struct attribute_group *csrow_dev_groups[] = {
3922c1946b6STakashi Iwai 	&csrow_dev_dimm_group,
3932c1946b6STakashi Iwai 	&csrow_dev_ce_count_group,
3942c1946b6STakashi Iwai 	NULL
3957c9281d7SDouglas Thompson };
3967c9281d7SDouglas Thompson 
397e39f4ea9SMauro Carvalho Chehab static inline int nr_pages_per_csrow(struct csrow_info *csrow)
398e39f4ea9SMauro Carvalho Chehab {
399e39f4ea9SMauro Carvalho Chehab 	int chan, nr_pages = 0;
400e39f4ea9SMauro Carvalho Chehab 
401e39f4ea9SMauro Carvalho Chehab 	for (chan = 0; chan < csrow->nr_channels; chan++)
402de3910ebSMauro Carvalho Chehab 		nr_pages += csrow->channels[chan]->dimm->nr_pages;
403e39f4ea9SMauro Carvalho Chehab 
404e39f4ea9SMauro Carvalho Chehab 	return nr_pages;
405e39f4ea9SMauro Carvalho Chehab }
406e39f4ea9SMauro Carvalho Chehab 
4077c9281d7SDouglas Thompson /* Create a CSROW object under specifed edac_mc_device */
4088096cfafSDoug Thompson static int edac_create_csrow_object(struct mem_ctl_info *mci,
409079708b9SDouglas Thompson 				    struct csrow_info *csrow, int index)
4107c9281d7SDouglas Thompson {
4117a623c03SMauro Carvalho Chehab 	csrow->dev.type = &csrow_attr_type;
41288d84ac9SBorislav Petkov 	csrow->dev.bus = mci->bus;
4132c1946b6STakashi Iwai 	csrow->dev.groups = csrow_dev_groups;
4147a623c03SMauro Carvalho Chehab 	device_initialize(&csrow->dev);
4157a623c03SMauro Carvalho Chehab 	csrow->dev.parent = &mci->dev;
416921a6899SBorislav Petkov 	csrow->mci = mci;
4177a623c03SMauro Carvalho Chehab 	dev_set_name(&csrow->dev, "csrow%d", index);
4187a623c03SMauro Carvalho Chehab 	dev_set_drvdata(&csrow->dev, csrow);
4197c9281d7SDouglas Thompson 
420956b9ba1SJoe Perches 	edac_dbg(0, "creating (virtual) csrow node %s\n",
421956b9ba1SJoe Perches 		 dev_name(&csrow->dev));
4228096cfafSDoug Thompson 
4232c1946b6STakashi Iwai 	return device_add(&csrow->dev);
4247c9281d7SDouglas Thompson }
4257c9281d7SDouglas Thompson 
4267a623c03SMauro Carvalho Chehab /* Create a CSROW object under specifed edac_mc_device */
4277a623c03SMauro Carvalho Chehab static int edac_create_csrow_objects(struct mem_ctl_info *mci)
4287a623c03SMauro Carvalho Chehab {
4292c1946b6STakashi Iwai 	int err, i;
4307a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4317c9281d7SDouglas Thompson 
4327a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->nr_csrows; i++) {
433de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
434e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
435e39f4ea9SMauro Carvalho Chehab 			continue;
436de3910ebSMauro Carvalho Chehab 		err = edac_create_csrow_object(mci, mci->csrows[i], i);
4373d958823SMauro Carvalho Chehab 		if (err < 0) {
4383d958823SMauro Carvalho Chehab 			edac_dbg(1,
4393d958823SMauro Carvalho Chehab 				 "failure: create csrow objects for csrow %d\n",
4403d958823SMauro Carvalho Chehab 				 i);
4417a623c03SMauro Carvalho Chehab 			goto error;
4427a623c03SMauro Carvalho Chehab 		}
4433d958823SMauro Carvalho Chehab 	}
4447a623c03SMauro Carvalho Chehab 	return 0;
4457a623c03SMauro Carvalho Chehab 
4467a623c03SMauro Carvalho Chehab error:
4477a623c03SMauro Carvalho Chehab 	for (--i; i >= 0; i--) {
448de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
449e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
450e39f4ea9SMauro Carvalho Chehab 			continue;
451de3910ebSMauro Carvalho Chehab 		put_device(&mci->csrows[i]->dev);
4527a623c03SMauro Carvalho Chehab 	}
4537a623c03SMauro Carvalho Chehab 
4547a623c03SMauro Carvalho Chehab 	return err;
4557a623c03SMauro Carvalho Chehab }
4567a623c03SMauro Carvalho Chehab 
4577a623c03SMauro Carvalho Chehab static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
4587a623c03SMauro Carvalho Chehab {
4592c1946b6STakashi Iwai 	int i;
4607a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4617a623c03SMauro Carvalho Chehab 
4627a623c03SMauro Carvalho Chehab 	for (i = mci->nr_csrows - 1; i >= 0; i--) {
463de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
464e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
465e39f4ea9SMauro Carvalho Chehab 			continue;
46644d22e24SLans Zhang 		device_unregister(&mci->csrows[i]->dev);
4677a623c03SMauro Carvalho Chehab 	}
4687a623c03SMauro Carvalho Chehab }
46919974710SMauro Carvalho Chehab #endif
47019974710SMauro Carvalho Chehab 
47119974710SMauro Carvalho Chehab /*
47219974710SMauro Carvalho Chehab  * Per-dimm (or per-rank) devices
47319974710SMauro Carvalho Chehab  */
47419974710SMauro Carvalho Chehab 
47519974710SMauro Carvalho Chehab #define to_dimm(k) container_of(k, struct dimm_info, dev)
47619974710SMauro Carvalho Chehab 
47719974710SMauro Carvalho Chehab /* show/store functions for DIMM Label attributes */
47819974710SMauro Carvalho Chehab static ssize_t dimmdev_location_show(struct device *dev,
47919974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
48019974710SMauro Carvalho Chehab {
48119974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
48219974710SMauro Carvalho Chehab 
4836e84d359SMauro Carvalho Chehab 	return edac_dimm_info_location(dimm, data, PAGE_SIZE);
48419974710SMauro Carvalho Chehab }
48519974710SMauro Carvalho Chehab 
48619974710SMauro Carvalho Chehab static ssize_t dimmdev_label_show(struct device *dev,
48719974710SMauro Carvalho Chehab 				  struct device_attribute *mattr, char *data)
48819974710SMauro Carvalho Chehab {
48919974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
49019974710SMauro Carvalho Chehab 
49119974710SMauro Carvalho Chehab 	/* if field has not been initialized, there is nothing to send */
49219974710SMauro Carvalho Chehab 	if (!dimm->label[0])
49319974710SMauro Carvalho Chehab 		return 0;
49419974710SMauro Carvalho Chehab 
4951ea62c59SToshi Kani 	return snprintf(data, sizeof(dimm->label) + 1, "%s\n", dimm->label);
49619974710SMauro Carvalho Chehab }
49719974710SMauro Carvalho Chehab 
49819974710SMauro Carvalho Chehab static ssize_t dimmdev_label_store(struct device *dev,
49919974710SMauro Carvalho Chehab 				   struct device_attribute *mattr,
50019974710SMauro Carvalho Chehab 				   const char *data,
50119974710SMauro Carvalho Chehab 				   size_t count)
50219974710SMauro Carvalho Chehab {
50319974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
504438470b8SToshi Kani 	size_t copy_count = count;
50519974710SMauro Carvalho Chehab 
506438470b8SToshi Kani 	if (count == 0)
507438470b8SToshi Kani 		return -EINVAL;
50819974710SMauro Carvalho Chehab 
509438470b8SToshi Kani 	if (data[count - 1] == '\0' || data[count - 1] == '\n')
510438470b8SToshi Kani 		copy_count -= 1;
51119974710SMauro Carvalho Chehab 
512d0c9c930SToshi Kani 	if (copy_count == 0 || copy_count >= sizeof(dimm->label))
513438470b8SToshi Kani 		return -EINVAL;
514438470b8SToshi Kani 
515438470b8SToshi Kani 	strncpy(dimm->label, data, copy_count);
516438470b8SToshi Kani 	dimm->label[copy_count] = '\0';
517438470b8SToshi Kani 
518438470b8SToshi Kani 	return count;
51919974710SMauro Carvalho Chehab }
52019974710SMauro Carvalho Chehab 
52119974710SMauro Carvalho Chehab static ssize_t dimmdev_size_show(struct device *dev,
52219974710SMauro Carvalho Chehab 				 struct device_attribute *mattr, char *data)
52319974710SMauro Carvalho Chehab {
52419974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
52519974710SMauro Carvalho Chehab 
52619974710SMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
52719974710SMauro Carvalho Chehab }
52819974710SMauro Carvalho Chehab 
52919974710SMauro Carvalho Chehab static ssize_t dimmdev_mem_type_show(struct device *dev,
53019974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
53119974710SMauro Carvalho Chehab {
53219974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
53319974710SMauro Carvalho Chehab 
53419974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", mem_types[dimm->mtype]);
53519974710SMauro Carvalho Chehab }
53619974710SMauro Carvalho Chehab 
53719974710SMauro Carvalho Chehab static ssize_t dimmdev_dev_type_show(struct device *dev,
53819974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
53919974710SMauro Carvalho Chehab {
54019974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
54119974710SMauro Carvalho Chehab 
54219974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[dimm->dtype]);
54319974710SMauro Carvalho Chehab }
54419974710SMauro Carvalho Chehab 
54519974710SMauro Carvalho Chehab static ssize_t dimmdev_edac_mode_show(struct device *dev,
54619974710SMauro Carvalho Chehab 				      struct device_attribute *mattr,
54719974710SMauro Carvalho Chehab 				      char *data)
54819974710SMauro Carvalho Chehab {
54919974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
55019974710SMauro Carvalho Chehab 
55119974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
55219974710SMauro Carvalho Chehab }
55319974710SMauro Carvalho Chehab 
55419974710SMauro Carvalho Chehab /* dimm/rank attribute files */
55519974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
55619974710SMauro Carvalho Chehab 		   dimmdev_label_show, dimmdev_label_store);
55719974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
55819974710SMauro Carvalho Chehab static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
55919974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
56019974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
56119974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
56219974710SMauro Carvalho Chehab 
56319974710SMauro Carvalho Chehab /* attributes of the dimm<id>/rank<id> object */
56419974710SMauro Carvalho Chehab static struct attribute *dimm_attrs[] = {
56519974710SMauro Carvalho Chehab 	&dev_attr_dimm_label.attr,
56619974710SMauro Carvalho Chehab 	&dev_attr_dimm_location.attr,
56719974710SMauro Carvalho Chehab 	&dev_attr_size.attr,
56819974710SMauro Carvalho Chehab 	&dev_attr_dimm_mem_type.attr,
56919974710SMauro Carvalho Chehab 	&dev_attr_dimm_dev_type.attr,
57019974710SMauro Carvalho Chehab 	&dev_attr_dimm_edac_mode.attr,
57119974710SMauro Carvalho Chehab 	NULL,
57219974710SMauro Carvalho Chehab };
57319974710SMauro Carvalho Chehab 
57419974710SMauro Carvalho Chehab static struct attribute_group dimm_attr_grp = {
57519974710SMauro Carvalho Chehab 	.attrs	= dimm_attrs,
57619974710SMauro Carvalho Chehab };
57719974710SMauro Carvalho Chehab 
57819974710SMauro Carvalho Chehab static const struct attribute_group *dimm_attr_groups[] = {
57919974710SMauro Carvalho Chehab 	&dimm_attr_grp,
58019974710SMauro Carvalho Chehab 	NULL
58119974710SMauro Carvalho Chehab };
58219974710SMauro Carvalho Chehab 
583de3910ebSMauro Carvalho Chehab static void dimm_attr_release(struct device *dev)
58419974710SMauro Carvalho Chehab {
585de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm = container_of(dev, struct dimm_info, dev);
586de3910ebSMauro Carvalho Chehab 
587956b9ba1SJoe Perches 	edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev));
588de3910ebSMauro Carvalho Chehab 	kfree(dimm);
58919974710SMauro Carvalho Chehab }
59019974710SMauro Carvalho Chehab 
59119974710SMauro Carvalho Chehab static struct device_type dimm_attr_type = {
59219974710SMauro Carvalho Chehab 	.groups		= dimm_attr_groups,
59319974710SMauro Carvalho Chehab 	.release	= dimm_attr_release,
59419974710SMauro Carvalho Chehab };
59519974710SMauro Carvalho Chehab 
59619974710SMauro Carvalho Chehab /* Create a DIMM object under specifed memory controller device */
59719974710SMauro Carvalho Chehab static int edac_create_dimm_object(struct mem_ctl_info *mci,
59819974710SMauro Carvalho Chehab 				   struct dimm_info *dimm,
59919974710SMauro Carvalho Chehab 				   int index)
60019974710SMauro Carvalho Chehab {
60119974710SMauro Carvalho Chehab 	int err;
60219974710SMauro Carvalho Chehab 	dimm->mci = mci;
60319974710SMauro Carvalho Chehab 
60419974710SMauro Carvalho Chehab 	dimm->dev.type = &dimm_attr_type;
60588d84ac9SBorislav Petkov 	dimm->dev.bus = mci->bus;
60619974710SMauro Carvalho Chehab 	device_initialize(&dimm->dev);
60719974710SMauro Carvalho Chehab 
60819974710SMauro Carvalho Chehab 	dimm->dev.parent = &mci->dev;
6099713faecSMauro Carvalho Chehab 	if (mci->csbased)
61019974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "rank%d", index);
61119974710SMauro Carvalho Chehab 	else
61219974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "dimm%d", index);
61319974710SMauro Carvalho Chehab 	dev_set_drvdata(&dimm->dev, dimm);
61419974710SMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
61519974710SMauro Carvalho Chehab 
61619974710SMauro Carvalho Chehab 	err =  device_add(&dimm->dev);
61719974710SMauro Carvalho Chehab 
618956b9ba1SJoe Perches 	edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev));
61919974710SMauro Carvalho Chehab 
62019974710SMauro Carvalho Chehab 	return err;
62119974710SMauro Carvalho Chehab }
6227a623c03SMauro Carvalho Chehab 
6237a623c03SMauro Carvalho Chehab /*
6247a623c03SMauro Carvalho Chehab  * Memory controller device
6257a623c03SMauro Carvalho Chehab  */
6267a623c03SMauro Carvalho Chehab 
6277a623c03SMauro Carvalho Chehab #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
6287a623c03SMauro Carvalho Chehab 
6297a623c03SMauro Carvalho Chehab static ssize_t mci_reset_counters_store(struct device *dev,
6307a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
6317c9281d7SDouglas Thompson 					const char *data, size_t count)
6327c9281d7SDouglas Thompson {
6337a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
6347a623c03SMauro Carvalho Chehab 	int cnt, row, chan, i;
6355926ff50SMauro Carvalho Chehab 	mci->ue_mc = 0;
6365926ff50SMauro Carvalho Chehab 	mci->ce_mc = 0;
6377a623c03SMauro Carvalho Chehab 	mci->ue_noinfo_count = 0;
6387a623c03SMauro Carvalho Chehab 	mci->ce_noinfo_count = 0;
6397c9281d7SDouglas Thompson 
6407c9281d7SDouglas Thompson 	for (row = 0; row < mci->nr_csrows; row++) {
641de3910ebSMauro Carvalho Chehab 		struct csrow_info *ri = mci->csrows[row];
6427c9281d7SDouglas Thompson 
6437c9281d7SDouglas Thompson 		ri->ue_count = 0;
6447c9281d7SDouglas Thompson 		ri->ce_count = 0;
6457c9281d7SDouglas Thompson 
6467c9281d7SDouglas Thompson 		for (chan = 0; chan < ri->nr_channels; chan++)
647de3910ebSMauro Carvalho Chehab 			ri->channels[chan]->ce_count = 0;
6487c9281d7SDouglas Thompson 	}
6497c9281d7SDouglas Thompson 
6507a623c03SMauro Carvalho Chehab 	cnt = 1;
6517a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
6527a623c03SMauro Carvalho Chehab 		cnt *= mci->layers[i].size;
6537a623c03SMauro Carvalho Chehab 		memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32));
6547a623c03SMauro Carvalho Chehab 		memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32));
6557a623c03SMauro Carvalho Chehab 	}
6567a623c03SMauro Carvalho Chehab 
6577c9281d7SDouglas Thompson 	mci->start_time = jiffies;
6587c9281d7SDouglas Thompson 	return count;
6597c9281d7SDouglas Thompson }
6607c9281d7SDouglas Thompson 
66139094443SBorislav Petkov /* Memory scrubbing interface:
66239094443SBorislav Petkov  *
66339094443SBorislav Petkov  * A MC driver can limit the scrubbing bandwidth based on the CPU type.
66439094443SBorislav Petkov  * Therefore, ->set_sdram_scrub_rate should be made to return the actual
66539094443SBorislav Petkov  * bandwidth that is accepted or 0 when scrubbing is to be disabled.
66639094443SBorislav Petkov  *
66739094443SBorislav Petkov  * Negative value still means that an error has occurred while setting
66839094443SBorislav Petkov  * the scrub rate.
66939094443SBorislav Petkov  */
6707a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
6717a623c03SMauro Carvalho Chehab 					  struct device_attribute *mattr,
6727c9281d7SDouglas Thompson 					  const char *data, size_t count)
6737c9281d7SDouglas Thompson {
6747a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
675eba042a8SBorislav Petkov 	unsigned long bandwidth = 0;
67639094443SBorislav Petkov 	int new_bw = 0;
6777c9281d7SDouglas Thompson 
678c7f62fc8SJingoo Han 	if (kstrtoul(data, 10, &bandwidth) < 0)
679eba042a8SBorislav Petkov 		return -EINVAL;
680eba042a8SBorislav Petkov 
68139094443SBorislav Petkov 	new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
6824949603aSMarkus Trippelsdorf 	if (new_bw < 0) {
6834949603aSMarkus Trippelsdorf 		edac_printk(KERN_WARNING, EDAC_MC,
6844949603aSMarkus Trippelsdorf 			    "Error setting scrub rate to: %lu\n", bandwidth);
6854949603aSMarkus Trippelsdorf 		return -EINVAL;
6867c9281d7SDouglas Thompson 	}
6877c9281d7SDouglas Thompson 
6884949603aSMarkus Trippelsdorf 	return count;
6897c9281d7SDouglas Thompson }
690eba042a8SBorislav Petkov 
69139094443SBorislav Petkov /*
69239094443SBorislav Petkov  * ->get_sdram_scrub_rate() return value semantics same as above.
69339094443SBorislav Petkov  */
6947a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
6957a623c03SMauro Carvalho Chehab 					 struct device_attribute *mattr,
6967a623c03SMauro Carvalho Chehab 					 char *data)
69739094443SBorislav Petkov {
6987a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
69939094443SBorislav Petkov 	int bandwidth = 0;
70039094443SBorislav Petkov 
70139094443SBorislav Petkov 	bandwidth = mci->get_sdram_scrub_rate(mci);
70239094443SBorislav Petkov 	if (bandwidth < 0) {
703eba042a8SBorislav Petkov 		edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n");
70439094443SBorislav Petkov 		return bandwidth;
705eba042a8SBorislav Petkov 	}
70639094443SBorislav Petkov 
7077c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", bandwidth);
7087c9281d7SDouglas Thompson }
7097c9281d7SDouglas Thompson 
7107c9281d7SDouglas Thompson /* default attribute files for the MCI object */
7117a623c03SMauro Carvalho Chehab static ssize_t mci_ue_count_show(struct device *dev,
7127a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7137a623c03SMauro Carvalho Chehab 				 char *data)
7147c9281d7SDouglas Thompson {
7157a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7167a623c03SMauro Carvalho Chehab 
7175926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ue_mc);
7187c9281d7SDouglas Thompson }
7197c9281d7SDouglas Thompson 
7207a623c03SMauro Carvalho Chehab static ssize_t mci_ce_count_show(struct device *dev,
7217a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7227a623c03SMauro Carvalho Chehab 				 char *data)
7237c9281d7SDouglas Thompson {
7247a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7257a623c03SMauro Carvalho Chehab 
7265926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ce_mc);
7277c9281d7SDouglas Thompson }
7287c9281d7SDouglas Thompson 
7297a623c03SMauro Carvalho Chehab static ssize_t mci_ce_noinfo_show(struct device *dev,
7307a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7317a623c03SMauro Carvalho Chehab 				  char *data)
7327c9281d7SDouglas Thompson {
7337a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7347a623c03SMauro Carvalho Chehab 
7357c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ce_noinfo_count);
7367c9281d7SDouglas Thompson }
7377c9281d7SDouglas Thompson 
7387a623c03SMauro Carvalho Chehab static ssize_t mci_ue_noinfo_show(struct device *dev,
7397a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7407a623c03SMauro Carvalho Chehab 				  char *data)
7417c9281d7SDouglas Thompson {
7427a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7437a623c03SMauro Carvalho Chehab 
7447c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ue_noinfo_count);
7457c9281d7SDouglas Thompson }
7467c9281d7SDouglas Thompson 
7477a623c03SMauro Carvalho Chehab static ssize_t mci_seconds_show(struct device *dev,
7487a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
7497a623c03SMauro Carvalho Chehab 				char *data)
7507c9281d7SDouglas Thompson {
7517a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7527a623c03SMauro Carvalho Chehab 
7537c9281d7SDouglas Thompson 	return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
7547c9281d7SDouglas Thompson }
7557c9281d7SDouglas Thompson 
7567a623c03SMauro Carvalho Chehab static ssize_t mci_ctl_name_show(struct device *dev,
7577a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7587a623c03SMauro Carvalho Chehab 				 char *data)
7597c9281d7SDouglas Thompson {
7607a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7617a623c03SMauro Carvalho Chehab 
7627c9281d7SDouglas Thompson 	return sprintf(data, "%s\n", mci->ctl_name);
7637c9281d7SDouglas Thompson }
7647c9281d7SDouglas Thompson 
7657a623c03SMauro Carvalho Chehab static ssize_t mci_size_mb_show(struct device *dev,
7667a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
7677a623c03SMauro Carvalho Chehab 				char *data)
7687c9281d7SDouglas Thompson {
7697a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
770a895bf8bSMauro Carvalho Chehab 	int total_pages = 0, csrow_idx, j;
7717c9281d7SDouglas Thompson 
772a895bf8bSMauro Carvalho Chehab 	for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
773de3910ebSMauro Carvalho Chehab 		struct csrow_info *csrow = mci->csrows[csrow_idx];
7747c9281d7SDouglas Thompson 
775a895bf8bSMauro Carvalho Chehab 		for (j = 0; j < csrow->nr_channels; j++) {
776de3910ebSMauro Carvalho Chehab 			struct dimm_info *dimm = csrow->channels[j]->dimm;
7777c9281d7SDouglas Thompson 
778a895bf8bSMauro Carvalho Chehab 			total_pages += dimm->nr_pages;
779a895bf8bSMauro Carvalho Chehab 		}
7807c9281d7SDouglas Thompson 	}
7817c9281d7SDouglas Thompson 
7827c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
7837c9281d7SDouglas Thompson }
7847c9281d7SDouglas Thompson 
7858ad6c78aSMauro Carvalho Chehab static ssize_t mci_max_location_show(struct device *dev,
7868ad6c78aSMauro Carvalho Chehab 				     struct device_attribute *mattr,
7878ad6c78aSMauro Carvalho Chehab 				     char *data)
7888ad6c78aSMauro Carvalho Chehab {
7898ad6c78aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7908ad6c78aSMauro Carvalho Chehab 	int i;
7918ad6c78aSMauro Carvalho Chehab 	char *p = data;
7928ad6c78aSMauro Carvalho Chehab 
7938ad6c78aSMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
7948ad6c78aSMauro Carvalho Chehab 		p += sprintf(p, "%s %d ",
7958ad6c78aSMauro Carvalho Chehab 			     edac_layer_name[mci->layers[i].type],
7968ad6c78aSMauro Carvalho Chehab 			     mci->layers[i].size - 1);
7978ad6c78aSMauro Carvalho Chehab 	}
7988ad6c78aSMauro Carvalho Chehab 
7998ad6c78aSMauro Carvalho Chehab 	return p - data;
8008ad6c78aSMauro Carvalho Chehab }
8018ad6c78aSMauro Carvalho Chehab 
8027c9281d7SDouglas Thompson /* default Control file */
803f11135d8SBorislav Petkov static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
8047c9281d7SDouglas Thompson 
8057c9281d7SDouglas Thompson /* default Attribute files */
806f11135d8SBorislav Petkov static DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
807f11135d8SBorislav Petkov static DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
808f11135d8SBorislav Petkov static DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
809f11135d8SBorislav Petkov static DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
810f11135d8SBorislav Petkov static DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
811f11135d8SBorislav Petkov static DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
812f11135d8SBorislav Petkov static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
813f11135d8SBorislav Petkov static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
8147c9281d7SDouglas Thompson 
8157c9281d7SDouglas Thompson /* memory scrubber attribute file */
8162c1946b6STakashi Iwai DEVICE_ATTR(sdram_scrub_rate, 0, mci_sdram_scrub_rate_show,
8172c1946b6STakashi Iwai 	    mci_sdram_scrub_rate_store); /* umode set later in is_visible */
8187c9281d7SDouglas Thompson 
8197a623c03SMauro Carvalho Chehab static struct attribute *mci_attrs[] = {
8207a623c03SMauro Carvalho Chehab 	&dev_attr_reset_counters.attr,
8217a623c03SMauro Carvalho Chehab 	&dev_attr_mc_name.attr,
8227a623c03SMauro Carvalho Chehab 	&dev_attr_size_mb.attr,
8237a623c03SMauro Carvalho Chehab 	&dev_attr_seconds_since_reset.attr,
8247a623c03SMauro Carvalho Chehab 	&dev_attr_ue_noinfo_count.attr,
8257a623c03SMauro Carvalho Chehab 	&dev_attr_ce_noinfo_count.attr,
8267a623c03SMauro Carvalho Chehab 	&dev_attr_ue_count.attr,
8277a623c03SMauro Carvalho Chehab 	&dev_attr_ce_count.attr,
8288ad6c78aSMauro Carvalho Chehab 	&dev_attr_max_location.attr,
8292c1946b6STakashi Iwai 	&dev_attr_sdram_scrub_rate.attr,
8307c9281d7SDouglas Thompson 	NULL
8317c9281d7SDouglas Thompson };
8327c9281d7SDouglas Thompson 
8332c1946b6STakashi Iwai static umode_t mci_attr_is_visible(struct kobject *kobj,
8342c1946b6STakashi Iwai 				   struct attribute *attr, int idx)
8352c1946b6STakashi Iwai {
8362c1946b6STakashi Iwai 	struct device *dev = kobj_to_dev(kobj);
8372c1946b6STakashi Iwai 	struct mem_ctl_info *mci = to_mci(dev);
8382c1946b6STakashi Iwai 	umode_t mode = 0;
8392c1946b6STakashi Iwai 
8402c1946b6STakashi Iwai 	if (attr != &dev_attr_sdram_scrub_rate.attr)
8412c1946b6STakashi Iwai 		return attr->mode;
8422c1946b6STakashi Iwai 	if (mci->get_sdram_scrub_rate)
8432c1946b6STakashi Iwai 		mode |= S_IRUGO;
8442c1946b6STakashi Iwai 	if (mci->set_sdram_scrub_rate)
8452c1946b6STakashi Iwai 		mode |= S_IWUSR;
8462c1946b6STakashi Iwai 	return mode;
8472c1946b6STakashi Iwai }
8482c1946b6STakashi Iwai 
8497a623c03SMauro Carvalho Chehab static struct attribute_group mci_attr_grp = {
8507a623c03SMauro Carvalho Chehab 	.attrs	= mci_attrs,
8512c1946b6STakashi Iwai 	.is_visible = mci_attr_is_visible,
8527c9281d7SDouglas Thompson };
8537c9281d7SDouglas Thompson 
8547a623c03SMauro Carvalho Chehab static const struct attribute_group *mci_attr_groups[] = {
8557a623c03SMauro Carvalho Chehab 	&mci_attr_grp,
8567a623c03SMauro Carvalho Chehab 	NULL
857cc301b3aSMauro Carvalho Chehab };
858cc301b3aSMauro Carvalho Chehab 
859de3910ebSMauro Carvalho Chehab static void mci_attr_release(struct device *dev)
8607a623c03SMauro Carvalho Chehab {
861de3910ebSMauro Carvalho Chehab 	struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
862de3910ebSMauro Carvalho Chehab 
863956b9ba1SJoe Perches 	edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
864de3910ebSMauro Carvalho Chehab 	kfree(mci);
8657a623c03SMauro Carvalho Chehab }
8667a623c03SMauro Carvalho Chehab 
8677a623c03SMauro Carvalho Chehab static struct device_type mci_attr_type = {
8687a623c03SMauro Carvalho Chehab 	.groups		= mci_attr_groups,
8697a623c03SMauro Carvalho Chehab 	.release	= mci_attr_release,
870cc301b3aSMauro Carvalho Chehab };
871cc301b3aSMauro Carvalho Chehab 
8727c9281d7SDouglas Thompson /*
8737c9281d7SDouglas Thompson  * Create a new Memory Controller kobject instance,
8747c9281d7SDouglas Thompson  *	mc<id> under the 'mc' directory
8757c9281d7SDouglas Thompson  *
8767c9281d7SDouglas Thompson  * Return:
8777c9281d7SDouglas Thompson  *	0	Success
8787c9281d7SDouglas Thompson  *	!0	Failure
8797c9281d7SDouglas Thompson  */
8804e8d230dSTakashi Iwai int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
8814e8d230dSTakashi Iwai 				 const struct attribute_group **groups)
8827c9281d7SDouglas Thompson {
8837a623c03SMauro Carvalho Chehab 	int i, err;
8847c9281d7SDouglas Thompson 
8857a623c03SMauro Carvalho Chehab 	/*
8867a623c03SMauro Carvalho Chehab 	 * The memory controller needs its own bus, in order to avoid
8877a623c03SMauro Carvalho Chehab 	 * namespace conflicts at /sys/bus/edac.
88842a8e397SDouglas Thompson 	 */
88988d84ac9SBorislav Petkov 	mci->bus->name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx);
89088d84ac9SBorislav Petkov 	if (!mci->bus->name)
891de3910ebSMauro Carvalho Chehab 		return -ENOMEM;
89288d84ac9SBorislav Petkov 
89388d84ac9SBorislav Petkov 	edac_dbg(0, "creating bus %s\n", mci->bus->name);
89488d84ac9SBorislav Petkov 
89588d84ac9SBorislav Petkov 	err = bus_register(mci->bus);
8967a623c03SMauro Carvalho Chehab 	if (err < 0)
8971bf1950cSJunjie Mao 		goto fail_free_name;
8987a623c03SMauro Carvalho Chehab 
899de3910ebSMauro Carvalho Chehab 	/* get the /sys/devices/system/edac subsys reference */
900de3910ebSMauro Carvalho Chehab 	mci->dev.type = &mci_attr_type;
901de3910ebSMauro Carvalho Chehab 	device_initialize(&mci->dev);
902de3910ebSMauro Carvalho Chehab 
903de3910ebSMauro Carvalho Chehab 	mci->dev.parent = mci_pdev;
90488d84ac9SBorislav Petkov 	mci->dev.bus = mci->bus;
9054e8d230dSTakashi Iwai 	mci->dev.groups = groups;
906de3910ebSMauro Carvalho Chehab 	dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
907de3910ebSMauro Carvalho Chehab 	dev_set_drvdata(&mci->dev, mci);
908de3910ebSMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
909de3910ebSMauro Carvalho Chehab 
910956b9ba1SJoe Perches 	edac_dbg(0, "creating device %s\n", dev_name(&mci->dev));
9117a623c03SMauro Carvalho Chehab 	err = device_add(&mci->dev);
9127a623c03SMauro Carvalho Chehab 	if (err < 0) {
9133d958823SMauro Carvalho Chehab 		edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev));
9141bf1950cSJunjie Mao 		goto fail_unregister_bus;
9158096cfafSDoug Thompson 	}
91642a8e397SDouglas Thompson 
9177a623c03SMauro Carvalho Chehab 	/*
9187a623c03SMauro Carvalho Chehab 	 * Create the dimm/rank devices
9197c9281d7SDouglas Thompson 	 */
9207a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
921de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
9227a623c03SMauro Carvalho Chehab 		/* Only expose populated DIMMs */
9231bf1950cSJunjie Mao 		if (!dimm->nr_pages)
9247a623c03SMauro Carvalho Chehab 			continue;
9251bf1950cSJunjie Mao 
9267a623c03SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
927956b9ba1SJoe Perches 		edac_dbg(1, "creating dimm%d, located at ", i);
9287a623c03SMauro Carvalho Chehab 		if (edac_debug_level >= 1) {
9297a623c03SMauro Carvalho Chehab 			int lay;
9307a623c03SMauro Carvalho Chehab 			for (lay = 0; lay < mci->n_layers; lay++)
9317a623c03SMauro Carvalho Chehab 				printk(KERN_CONT "%s %d ",
9327a623c03SMauro Carvalho Chehab 					edac_layer_name[mci->layers[lay].type],
9337a623c03SMauro Carvalho Chehab 					dimm->location[lay]);
9347a623c03SMauro Carvalho Chehab 			printk(KERN_CONT "\n");
9357c9281d7SDouglas Thompson 		}
9367a623c03SMauro Carvalho Chehab #endif
93719974710SMauro Carvalho Chehab 		err = edac_create_dimm_object(mci, dimm, i);
93819974710SMauro Carvalho Chehab 		if (err) {
939956b9ba1SJoe Perches 			edac_dbg(1, "failure: create dimm %d obj\n", i);
9401bf1950cSJunjie Mao 			goto fail_unregister_dimm;
94119974710SMauro Carvalho Chehab 		}
9427c9281d7SDouglas Thompson 	}
9437a623c03SMauro Carvalho Chehab 
94419974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
9457a623c03SMauro Carvalho Chehab 	err = edac_create_csrow_objects(mci);
9467a623c03SMauro Carvalho Chehab 	if (err < 0)
9471bf1950cSJunjie Mao 		goto fail_unregister_dimm;
94819974710SMauro Carvalho Chehab #endif
9497c9281d7SDouglas Thompson 
9507ac8bf9bSBorislav Petkov 	edac_create_debugfs_nodes(mci);
9517c9281d7SDouglas Thompson 	return 0;
9527c9281d7SDouglas Thompson 
9531bf1950cSJunjie Mao fail_unregister_dimm:
9547c9281d7SDouglas Thompson 	for (i--; i >= 0; i--) {
955de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
9561bf1950cSJunjie Mao 		if (!dimm->nr_pages)
9577a623c03SMauro Carvalho Chehab 			continue;
9581bf1950cSJunjie Mao 
95944d22e24SLans Zhang 		device_unregister(&dimm->dev);
9607c9281d7SDouglas Thompson 	}
96144d22e24SLans Zhang 	device_unregister(&mci->dev);
9621bf1950cSJunjie Mao fail_unregister_bus:
96388d84ac9SBorislav Petkov 	bus_unregister(mci->bus);
9641bf1950cSJunjie Mao fail_free_name:
96588d84ac9SBorislav Petkov 	kfree(mci->bus->name);
9667c9281d7SDouglas Thompson 	return err;
9677c9281d7SDouglas Thompson }
9687c9281d7SDouglas Thompson 
9697c9281d7SDouglas Thompson /*
9707c9281d7SDouglas Thompson  * remove a Memory Controller instance
9717c9281d7SDouglas Thompson  */
9727c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
9737c9281d7SDouglas Thompson {
9747a623c03SMauro Carvalho Chehab 	int i;
9757c9281d7SDouglas Thompson 
976956b9ba1SJoe Perches 	edac_dbg(0, "\n");
9777c9281d7SDouglas Thompson 
978452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
97930f84a89STan Xiaojun 	edac_debugfs_remove_recursive(mci->debugfs);
980452a6bf9SMauro Carvalho Chehab #endif
98119974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
9827a623c03SMauro Carvalho Chehab 	edac_delete_csrow_objects(mci);
98319974710SMauro Carvalho Chehab #endif
984a895bf8bSMauro Carvalho Chehab 
9857a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
986de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
9877a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
9887a623c03SMauro Carvalho Chehab 			continue;
989956b9ba1SJoe Perches 		edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev));
99044d22e24SLans Zhang 		device_unregister(&dimm->dev);
9917c9281d7SDouglas Thompson 	}
9927c9281d7SDouglas Thompson }
9937c9281d7SDouglas Thompson 
9947a623c03SMauro Carvalho Chehab void edac_unregister_sysfs(struct mem_ctl_info *mci)
9958096cfafSDoug Thompson {
996956b9ba1SJoe Perches 	edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
99744d22e24SLans Zhang 	device_unregister(&mci->dev);
99888d84ac9SBorislav Petkov 	bus_unregister(mci->bus);
99988d84ac9SBorislav Petkov 	kfree(mci->bus->name);
10007a623c03SMauro Carvalho Chehab }
10018096cfafSDoug Thompson 
1002de3910ebSMauro Carvalho Chehab static void mc_attr_release(struct device *dev)
10037a623c03SMauro Carvalho Chehab {
1004de3910ebSMauro Carvalho Chehab 	/*
1005de3910ebSMauro Carvalho Chehab 	 * There's no container structure here, as this is just the mci
1006de3910ebSMauro Carvalho Chehab 	 * parent device, used to create the /sys/devices/mc sysfs node.
1007de3910ebSMauro Carvalho Chehab 	 * So, there are no attributes on it.
1008de3910ebSMauro Carvalho Chehab 	 */
1009956b9ba1SJoe Perches 	edac_dbg(1, "Releasing device %s\n", dev_name(dev));
1010de3910ebSMauro Carvalho Chehab 	kfree(dev);
10117a623c03SMauro Carvalho Chehab }
10127a623c03SMauro Carvalho Chehab 
10137a623c03SMauro Carvalho Chehab static struct device_type mc_attr_type = {
10147a623c03SMauro Carvalho Chehab 	.release	= mc_attr_release,
10157a623c03SMauro Carvalho Chehab };
10167a623c03SMauro Carvalho Chehab /*
10177a623c03SMauro Carvalho Chehab  * Init/exit code for the module. Basically, creates/removes /sys/class/rc
10187a623c03SMauro Carvalho Chehab  */
10197a623c03SMauro Carvalho Chehab int __init edac_mc_sysfs_init(void)
10207a623c03SMauro Carvalho Chehab {
10217a623c03SMauro Carvalho Chehab 	struct bus_type *edac_subsys;
10227a623c03SMauro Carvalho Chehab 	int err;
10238096cfafSDoug Thompson 
1024fe5ff8b8SKay Sievers 	/* get the /sys/devices/system/edac subsys reference */
1025fe5ff8b8SKay Sievers 	edac_subsys = edac_get_sysfs_subsys();
1026fe5ff8b8SKay Sievers 	if (edac_subsys == NULL) {
1027956b9ba1SJoe Perches 		edac_dbg(1, "no edac_subsys\n");
10282d56b109SDenis Kirjanov 		err = -EINVAL;
10292d56b109SDenis Kirjanov 		goto out;
10308096cfafSDoug Thompson 	}
10318096cfafSDoug Thompson 
1032de3910ebSMauro Carvalho Chehab 	mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
10332d56b109SDenis Kirjanov 	if (!mci_pdev) {
10342d56b109SDenis Kirjanov 		err = -ENOMEM;
10352d56b109SDenis Kirjanov 		goto out_put_sysfs;
10362d56b109SDenis Kirjanov 	}
10378096cfafSDoug Thompson 
1038de3910ebSMauro Carvalho Chehab 	mci_pdev->bus = edac_subsys;
1039de3910ebSMauro Carvalho Chehab 	mci_pdev->type = &mc_attr_type;
1040de3910ebSMauro Carvalho Chehab 	device_initialize(mci_pdev);
1041de3910ebSMauro Carvalho Chehab 	dev_set_name(mci_pdev, "mc");
1042de3910ebSMauro Carvalho Chehab 
1043de3910ebSMauro Carvalho Chehab 	err = device_add(mci_pdev);
10447a623c03SMauro Carvalho Chehab 	if (err < 0)
10452d56b109SDenis Kirjanov 		goto out_dev_free;
10468096cfafSDoug Thompson 
1047956b9ba1SJoe Perches 	edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
1048de3910ebSMauro Carvalho Chehab 
10498096cfafSDoug Thompson 	return 0;
10502d56b109SDenis Kirjanov 
10512d56b109SDenis Kirjanov  out_dev_free:
10522d56b109SDenis Kirjanov 	kfree(mci_pdev);
10532d56b109SDenis Kirjanov  out_put_sysfs:
10542d56b109SDenis Kirjanov 	edac_put_sysfs_subsys();
10552d56b109SDenis Kirjanov  out:
10562d56b109SDenis Kirjanov 	return err;
10578096cfafSDoug Thompson }
10588096cfafSDoug Thompson 
1059c6b97bcfSAlexey Khoroshilov void edac_mc_sysfs_exit(void)
10608096cfafSDoug Thompson {
106144d22e24SLans Zhang 	device_unregister(mci_pdev);
1062fe5ff8b8SKay Sievers 	edac_put_sysfs_subsys();
10638096cfafSDoug Thompson }
1064