xref: /openbmc/linux/drivers/edac/edac_mc_sysfs.c (revision e7930ba4)
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  *
107a623c03SMauro Carvalho Chehab  * (c) 2012 - Mauro Carvalho Chehab <mchehab@redhat.com>
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 {
55096846e2SArthur Jones 	long l;
56096846e2SArthur Jones 	int ret;
57096846e2SArthur Jones 
58096846e2SArthur Jones 	if (!val)
59096846e2SArthur Jones 		return -EINVAL;
60096846e2SArthur Jones 
61096846e2SArthur Jones 	ret = strict_strtol(val, 0, &l);
62096846e2SArthur Jones 	if (ret == -EINVAL || ((int)l != l))
63096846e2SArthur Jones 		return -EINVAL;
64096846e2SArthur Jones 	*((int *)kp->arg) = l;
65096846e2SArthur Jones 
66096846e2SArthur Jones 	/* notify edac_mc engine to reset the poll period */
67096846e2SArthur Jones 	edac_mc_reset_delay_period(l);
68096846e2SArthur Jones 
69096846e2SArthur Jones 	return 0;
70096846e2SArthur Jones }
71096846e2SArthur Jones 
727c9281d7SDouglas Thompson /* Parameter declarations for above */
734de78c68SDave Jiang module_param(edac_mc_panic_on_ue, int, 0644);
744de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on");
754de78c68SDave Jiang module_param(edac_mc_log_ue, int, 0644);
764de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ue,
774de78c68SDave Jiang 		 "Log uncorrectable error to console: 0=off 1=on");
784de78c68SDave Jiang module_param(edac_mc_log_ce, int, 0644);
794de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ce,
804de78c68SDave Jiang 		 "Log correctable error to console: 0=off 1=on");
81096846e2SArthur Jones module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int,
82096846e2SArthur Jones 		  &edac_mc_poll_msec, 0644);
834de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds");
847c9281d7SDouglas Thompson 
85de3910ebSMauro Carvalho Chehab static struct device *mci_pdev;
867a623c03SMauro Carvalho Chehab 
877c9281d7SDouglas Thompson /*
887c9281d7SDouglas Thompson  * various constants for Memory Controllers
897c9281d7SDouglas Thompson  */
907c9281d7SDouglas Thompson static const char *mem_types[] = {
917c9281d7SDouglas Thompson 	[MEM_EMPTY] = "Empty",
927c9281d7SDouglas Thompson 	[MEM_RESERVED] = "Reserved",
937c9281d7SDouglas Thompson 	[MEM_UNKNOWN] = "Unknown",
947c9281d7SDouglas Thompson 	[MEM_FPM] = "FPM",
957c9281d7SDouglas Thompson 	[MEM_EDO] = "EDO",
967c9281d7SDouglas Thompson 	[MEM_BEDO] = "BEDO",
977c9281d7SDouglas Thompson 	[MEM_SDR] = "Unbuffered-SDR",
987c9281d7SDouglas Thompson 	[MEM_RDR] = "Registered-SDR",
997c9281d7SDouglas Thompson 	[MEM_DDR] = "Unbuffered-DDR",
1007c9281d7SDouglas Thompson 	[MEM_RDDR] = "Registered-DDR",
1011a9b85e6SDave Jiang 	[MEM_RMBS] = "RMBS",
1021a9b85e6SDave Jiang 	[MEM_DDR2] = "Unbuffered-DDR2",
1031a9b85e6SDave Jiang 	[MEM_FB_DDR2] = "FullyBuffered-DDR2",
1041d5f726cSBenjamin Herrenschmidt 	[MEM_RDDR2] = "Registered-DDR2",
105b1cfebc9SYang Shi 	[MEM_XDR] = "XDR",
106b1cfebc9SYang Shi 	[MEM_DDR3] = "Unbuffered-DDR3",
107b1cfebc9SYang Shi 	[MEM_RDDR3] = "Registered-DDR3"
1087c9281d7SDouglas Thompson };
1097c9281d7SDouglas Thompson 
1107c9281d7SDouglas Thompson static const char *dev_types[] = {
1117c9281d7SDouglas Thompson 	[DEV_UNKNOWN] = "Unknown",
1127c9281d7SDouglas Thompson 	[DEV_X1] = "x1",
1137c9281d7SDouglas Thompson 	[DEV_X2] = "x2",
1147c9281d7SDouglas Thompson 	[DEV_X4] = "x4",
1157c9281d7SDouglas Thompson 	[DEV_X8] = "x8",
1167c9281d7SDouglas Thompson 	[DEV_X16] = "x16",
1177c9281d7SDouglas Thompson 	[DEV_X32] = "x32",
1187c9281d7SDouglas Thompson 	[DEV_X64] = "x64"
1197c9281d7SDouglas Thompson };
1207c9281d7SDouglas Thompson 
1217c9281d7SDouglas Thompson static const char *edac_caps[] = {
1227c9281d7SDouglas Thompson 	[EDAC_UNKNOWN] = "Unknown",
1237c9281d7SDouglas Thompson 	[EDAC_NONE] = "None",
1247c9281d7SDouglas Thompson 	[EDAC_RESERVED] = "Reserved",
1257c9281d7SDouglas Thompson 	[EDAC_PARITY] = "PARITY",
1267c9281d7SDouglas Thompson 	[EDAC_EC] = "EC",
1277c9281d7SDouglas Thompson 	[EDAC_SECDED] = "SECDED",
1287c9281d7SDouglas Thompson 	[EDAC_S2ECD2ED] = "S2ECD2ED",
1297c9281d7SDouglas Thompson 	[EDAC_S4ECD4ED] = "S4ECD4ED",
1307c9281d7SDouglas Thompson 	[EDAC_S8ECD8ED] = "S8ECD8ED",
1317c9281d7SDouglas Thompson 	[EDAC_S16ECD16ED] = "S16ECD16ED"
1327c9281d7SDouglas Thompson };
1337c9281d7SDouglas Thompson 
13419974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
1357a623c03SMauro Carvalho Chehab /*
1367a623c03SMauro Carvalho Chehab  * EDAC sysfs CSROW data structures and methods
1377c9281d7SDouglas Thompson  */
1387c9281d7SDouglas Thompson 
1397a623c03SMauro Carvalho Chehab #define to_csrow(k) container_of(k, struct csrow_info, dev)
1407a623c03SMauro Carvalho Chehab 
1417a623c03SMauro Carvalho Chehab /*
1427a623c03SMauro Carvalho Chehab  * We need it to avoid namespace conflicts between the legacy API
1437a623c03SMauro Carvalho Chehab  * and the per-dimm/per-rank one
1447a623c03SMauro Carvalho Chehab  */
1457a623c03SMauro Carvalho Chehab #define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \
1467a623c03SMauro Carvalho Chehab 	struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store)
1477a623c03SMauro Carvalho Chehab 
1487a623c03SMauro Carvalho Chehab struct dev_ch_attribute {
1497a623c03SMauro Carvalho Chehab 	struct device_attribute attr;
1507a623c03SMauro Carvalho Chehab 	int channel;
1517a623c03SMauro Carvalho Chehab };
1527a623c03SMauro Carvalho Chehab 
1537a623c03SMauro Carvalho Chehab #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \
1547a623c03SMauro Carvalho Chehab 	struct dev_ch_attribute dev_attr_legacy_##_name = \
1557a623c03SMauro Carvalho Chehab 		{ __ATTR(_name, _mode, _show, _store), (_var) }
1567a623c03SMauro Carvalho Chehab 
1577a623c03SMauro Carvalho Chehab #define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel)
1587a623c03SMauro Carvalho Chehab 
1597c9281d7SDouglas Thompson /* Set of more default csrow<id> attribute show/store functions */
1607a623c03SMauro Carvalho Chehab static ssize_t csrow_ue_count_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);
1647a623c03SMauro Carvalho Chehab 
1657c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", csrow->ue_count);
1667c9281d7SDouglas Thompson }
1677c9281d7SDouglas Thompson 
1687a623c03SMauro Carvalho Chehab static ssize_t csrow_ce_count_show(struct device *dev,
1697a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1707c9281d7SDouglas Thompson {
1717a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1727a623c03SMauro Carvalho Chehab 
1737c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", csrow->ce_count);
1747c9281d7SDouglas Thompson }
1757c9281d7SDouglas Thompson 
1767a623c03SMauro Carvalho Chehab static ssize_t csrow_size_show(struct device *dev,
1777a623c03SMauro Carvalho Chehab 			       struct device_attribute *mattr, char *data)
1787c9281d7SDouglas Thompson {
1797a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
180a895bf8bSMauro Carvalho Chehab 	int i;
181a895bf8bSMauro Carvalho Chehab 	u32 nr_pages = 0;
182a895bf8bSMauro Carvalho Chehab 
183a895bf8bSMauro Carvalho Chehab 	for (i = 0; i < csrow->nr_channels; i++)
184de3910ebSMauro Carvalho Chehab 		nr_pages += csrow->channels[i]->dimm->nr_pages;
185a895bf8bSMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages));
1867c9281d7SDouglas Thompson }
1877c9281d7SDouglas Thompson 
1887a623c03SMauro Carvalho Chehab static ssize_t csrow_mem_type_show(struct device *dev,
1897a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1907c9281d7SDouglas Thompson {
1917a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
1927a623c03SMauro Carvalho Chehab 
193de3910ebSMauro Carvalho Chehab 	return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]);
1947c9281d7SDouglas Thompson }
1957c9281d7SDouglas Thompson 
1967a623c03SMauro Carvalho Chehab static ssize_t csrow_dev_type_show(struct device *dev,
1977a623c03SMauro Carvalho Chehab 				   struct device_attribute *mattr, char *data)
1987c9281d7SDouglas Thompson {
1997a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2007a623c03SMauro Carvalho Chehab 
201de3910ebSMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]);
2027c9281d7SDouglas Thompson }
2037c9281d7SDouglas Thompson 
2047a623c03SMauro Carvalho Chehab static ssize_t csrow_edac_mode_show(struct device *dev,
2057a623c03SMauro Carvalho Chehab 				    struct device_attribute *mattr,
2067a623c03SMauro Carvalho Chehab 				    char *data)
2077c9281d7SDouglas Thompson {
2087a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2097a623c03SMauro Carvalho Chehab 
210de3910ebSMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]);
2117c9281d7SDouglas Thompson }
2127c9281d7SDouglas Thompson 
2137c9281d7SDouglas Thompson /* show/store functions for DIMM Label attributes */
2147a623c03SMauro Carvalho Chehab static ssize_t channel_dimm_label_show(struct device *dev,
2157a623c03SMauro Carvalho Chehab 				       struct device_attribute *mattr,
2167a623c03SMauro Carvalho Chehab 				       char *data)
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];
2217a623c03SMauro Carvalho Chehab 
222124682c7SArthur Jones 	/* if field has not been initialized, there is nothing to send */
2237a623c03SMauro Carvalho Chehab 	if (!rank->dimm->label[0])
224124682c7SArthur Jones 		return 0;
225124682c7SArthur Jones 
226124682c7SArthur Jones 	return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n",
2277a623c03SMauro Carvalho Chehab 			rank->dimm->label);
2287c9281d7SDouglas Thompson }
2297c9281d7SDouglas Thompson 
2307a623c03SMauro Carvalho Chehab static ssize_t channel_dimm_label_store(struct device *dev,
2317a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
2327a623c03SMauro Carvalho Chehab 					const char *data, size_t count)
2337c9281d7SDouglas Thompson {
2347a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2357a623c03SMauro Carvalho Chehab 	unsigned chan = to_channel(mattr);
236de3910ebSMauro Carvalho Chehab 	struct rank_info *rank = csrow->channels[chan];
2377a623c03SMauro Carvalho Chehab 
2387c9281d7SDouglas Thompson 	ssize_t max_size = 0;
2397c9281d7SDouglas Thompson 
2407c9281d7SDouglas Thompson 	max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
2417a623c03SMauro Carvalho Chehab 	strncpy(rank->dimm->label, data, max_size);
2427a623c03SMauro Carvalho Chehab 	rank->dimm->label[max_size] = '\0';
2437c9281d7SDouglas Thompson 
2447c9281d7SDouglas Thompson 	return max_size;
2457c9281d7SDouglas Thompson }
2467c9281d7SDouglas Thompson 
2477c9281d7SDouglas Thompson /* show function for dynamic chX_ce_count attribute */
2487a623c03SMauro Carvalho Chehab static ssize_t channel_ce_count_show(struct device *dev,
2497a623c03SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
2507c9281d7SDouglas Thompson {
2517a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow = to_csrow(dev);
2527a623c03SMauro Carvalho Chehab 	unsigned chan = to_channel(mattr);
253de3910ebSMauro Carvalho Chehab 	struct rank_info *rank = csrow->channels[chan];
2547a623c03SMauro Carvalho Chehab 
2557a623c03SMauro Carvalho Chehab 	return sprintf(data, "%u\n", rank->ce_count);
2567c9281d7SDouglas Thompson }
2577c9281d7SDouglas Thompson 
2587a623c03SMauro Carvalho Chehab /* cwrow<id>/attribute files */
2597a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL);
2607a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL);
2617a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL);
2627a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL);
2637a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL);
2647a623c03SMauro Carvalho Chehab DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL);
2657c9281d7SDouglas Thompson 
2667c9281d7SDouglas Thompson /* default attributes of the CSROW<id> object */
2677a623c03SMauro Carvalho Chehab static struct attribute *csrow_attrs[] = {
2687a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_dev_type.attr,
2697a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_mem_type.attr,
2707a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_edac_mode.attr,
2717a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_size_mb.attr,
2727a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ue_count.attr,
2737a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ce_count.attr,
2747c9281d7SDouglas Thompson 	NULL,
2757c9281d7SDouglas Thompson };
2767c9281d7SDouglas Thompson 
2777a623c03SMauro Carvalho Chehab static struct attribute_group csrow_attr_grp = {
2787a623c03SMauro Carvalho Chehab 	.attrs	= csrow_attrs,
2797c9281d7SDouglas Thompson };
2807c9281d7SDouglas Thompson 
2817a623c03SMauro Carvalho Chehab static const struct attribute_group *csrow_attr_groups[] = {
2827a623c03SMauro Carvalho Chehab 	&csrow_attr_grp,
2837a623c03SMauro Carvalho Chehab 	NULL
2847c9281d7SDouglas Thompson };
2857c9281d7SDouglas Thompson 
286de3910ebSMauro Carvalho Chehab static void csrow_attr_release(struct device *dev)
2877a623c03SMauro Carvalho Chehab {
288de3910ebSMauro Carvalho Chehab 	struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
289de3910ebSMauro Carvalho Chehab 
290956b9ba1SJoe Perches 	edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
291de3910ebSMauro Carvalho Chehab 	kfree(csrow);
2927a623c03SMauro Carvalho Chehab }
2937a623c03SMauro Carvalho Chehab 
2947a623c03SMauro Carvalho Chehab static struct device_type csrow_attr_type = {
2957a623c03SMauro Carvalho Chehab 	.groups		= csrow_attr_groups,
2967a623c03SMauro Carvalho Chehab 	.release	= csrow_attr_release,
2977a623c03SMauro Carvalho Chehab };
2987a623c03SMauro Carvalho Chehab 
2997a623c03SMauro Carvalho Chehab /*
3007a623c03SMauro Carvalho Chehab  * possible dynamic channel DIMM Label attribute files
3017a623c03SMauro Carvalho Chehab  *
3027a623c03SMauro Carvalho Chehab  */
3037a623c03SMauro Carvalho Chehab 
3047c9281d7SDouglas Thompson #define EDAC_NR_CHANNELS	6
3057c9281d7SDouglas Thompson 
3067a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
3077a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 0);
3087a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
3097a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 1);
3107a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR,
3117a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 2);
3127a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR,
3137a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 3);
3147a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR,
3157a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 4);
3167a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR,
3177a623c03SMauro Carvalho Chehab 	channel_dimm_label_show, channel_dimm_label_store, 5);
3187c9281d7SDouglas Thompson 
3197a623c03SMauro Carvalho Chehab /* Total possible dynamic DIMM Label attribute file table */
3207a623c03SMauro Carvalho Chehab static struct device_attribute *dynamic_csrow_dimm_attr[] = {
3217a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch0_dimm_label.attr,
3227a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch1_dimm_label.attr,
3237a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch2_dimm_label.attr,
3247a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch3_dimm_label.attr,
3257a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch4_dimm_label.attr,
3267a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch5_dimm_label.attr
3277a623c03SMauro Carvalho Chehab };
3287c9281d7SDouglas Thompson 
3297a623c03SMauro Carvalho Chehab /* possible dynamic channel ce_count attribute files */
3307a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch0_ce_count, S_IRUGO | S_IWUSR,
3317a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 0);
3327a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch1_ce_count, S_IRUGO | S_IWUSR,
3337a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 1);
3347a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch2_ce_count, S_IRUGO | S_IWUSR,
3357a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 2);
3367a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch3_ce_count, S_IRUGO | S_IWUSR,
3377a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 3);
3387a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch4_ce_count, S_IRUGO | S_IWUSR,
3397a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 4);
3407a623c03SMauro Carvalho Chehab DEVICE_CHANNEL(ch5_ce_count, S_IRUGO | S_IWUSR,
3417a623c03SMauro Carvalho Chehab 		   channel_ce_count_show, NULL, 5);
3427c9281d7SDouglas Thompson 
3437a623c03SMauro Carvalho Chehab /* Total possible dynamic ce_count attribute file table */
3447a623c03SMauro Carvalho Chehab static struct device_attribute *dynamic_csrow_ce_count_attr[] = {
3457a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch0_ce_count.attr,
3467a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch1_ce_count.attr,
3477a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch2_ce_count.attr,
3487a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch3_ce_count.attr,
3497a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch4_ce_count.attr,
3507a623c03SMauro Carvalho Chehab 	&dev_attr_legacy_ch5_ce_count.attr
3517c9281d7SDouglas Thompson };
3527c9281d7SDouglas Thompson 
353e39f4ea9SMauro Carvalho Chehab static inline int nr_pages_per_csrow(struct csrow_info *csrow)
354e39f4ea9SMauro Carvalho Chehab {
355e39f4ea9SMauro Carvalho Chehab 	int chan, nr_pages = 0;
356e39f4ea9SMauro Carvalho Chehab 
357e39f4ea9SMauro Carvalho Chehab 	for (chan = 0; chan < csrow->nr_channels; chan++)
358de3910ebSMauro Carvalho Chehab 		nr_pages += csrow->channels[chan]->dimm->nr_pages;
359e39f4ea9SMauro Carvalho Chehab 
360e39f4ea9SMauro Carvalho Chehab 	return nr_pages;
361e39f4ea9SMauro Carvalho Chehab }
362e39f4ea9SMauro Carvalho Chehab 
3637c9281d7SDouglas Thompson /* Create a CSROW object under specifed edac_mc_device */
3648096cfafSDoug Thompson static int edac_create_csrow_object(struct mem_ctl_info *mci,
365079708b9SDouglas Thompson 				    struct csrow_info *csrow, int index)
3667c9281d7SDouglas Thompson {
3677a623c03SMauro Carvalho Chehab 	int err, chan;
3687c9281d7SDouglas Thompson 
3697a623c03SMauro Carvalho Chehab 	if (csrow->nr_channels >= EDAC_NR_CHANNELS)
3707a623c03SMauro Carvalho Chehab 		return -ENODEV;
3718096cfafSDoug Thompson 
3727a623c03SMauro Carvalho Chehab 	csrow->dev.type = &csrow_attr_type;
3737a623c03SMauro Carvalho Chehab 	csrow->dev.bus = &mci->bus;
3747a623c03SMauro Carvalho Chehab 	device_initialize(&csrow->dev);
3757a623c03SMauro Carvalho Chehab 	csrow->dev.parent = &mci->dev;
3767a623c03SMauro Carvalho Chehab 	dev_set_name(&csrow->dev, "csrow%d", index);
3777a623c03SMauro Carvalho Chehab 	dev_set_drvdata(&csrow->dev, csrow);
3787c9281d7SDouglas Thompson 
379956b9ba1SJoe Perches 	edac_dbg(0, "creating (virtual) csrow node %s\n",
380956b9ba1SJoe Perches 		 dev_name(&csrow->dev));
3818096cfafSDoug Thompson 
3827a623c03SMauro Carvalho Chehab 	err = device_add(&csrow->dev);
3837a623c03SMauro Carvalho Chehab 	if (err < 0)
3847a623c03SMauro Carvalho Chehab 		return err;
3858096cfafSDoug Thompson 
3867c9281d7SDouglas Thompson 	for (chan = 0; chan < csrow->nr_channels; chan++) {
387e39f4ea9SMauro Carvalho Chehab 		/* Only expose populated DIMMs */
388de3910ebSMauro Carvalho Chehab 		if (!csrow->channels[chan]->dimm->nr_pages)
389e39f4ea9SMauro Carvalho Chehab 			continue;
3907a623c03SMauro Carvalho Chehab 		err = device_create_file(&csrow->dev,
3917a623c03SMauro Carvalho Chehab 					 dynamic_csrow_dimm_attr[chan]);
3927a623c03SMauro Carvalho Chehab 		if (err < 0)
3937a623c03SMauro Carvalho Chehab 			goto error;
3947a623c03SMauro Carvalho Chehab 		err = device_create_file(&csrow->dev,
3957a623c03SMauro Carvalho Chehab 					 dynamic_csrow_ce_count_attr[chan]);
3967a623c03SMauro Carvalho Chehab 		if (err < 0) {
3977a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
3987a623c03SMauro Carvalho Chehab 					   dynamic_csrow_dimm_attr[chan]);
3997a623c03SMauro Carvalho Chehab 			goto error;
4007c9281d7SDouglas Thompson 		}
4017c9281d7SDouglas Thompson 	}
4027a623c03SMauro Carvalho Chehab 
4038096cfafSDoug Thompson 	return 0;
4048096cfafSDoug Thompson 
4057a623c03SMauro Carvalho Chehab error:
4067a623c03SMauro Carvalho Chehab 	for (--chan; chan >= 0; chan--) {
4077a623c03SMauro Carvalho Chehab 		device_remove_file(&csrow->dev,
4087a623c03SMauro Carvalho Chehab 					dynamic_csrow_dimm_attr[chan]);
4097a623c03SMauro Carvalho Chehab 		device_remove_file(&csrow->dev,
4107a623c03SMauro Carvalho Chehab 					   dynamic_csrow_ce_count_attr[chan]);
4117a623c03SMauro Carvalho Chehab 	}
4127a623c03SMauro Carvalho Chehab 	put_device(&csrow->dev);
4138096cfafSDoug Thompson 
4147c9281d7SDouglas Thompson 	return err;
4157c9281d7SDouglas Thompson }
4167c9281d7SDouglas Thompson 
4177a623c03SMauro Carvalho Chehab /* Create a CSROW object under specifed edac_mc_device */
4187a623c03SMauro Carvalho Chehab static int edac_create_csrow_objects(struct mem_ctl_info *mci)
4197a623c03SMauro Carvalho Chehab {
4207a623c03SMauro Carvalho Chehab 	int err, i, chan;
4217a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4227c9281d7SDouglas Thompson 
4237a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->nr_csrows; i++) {
424de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
425e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
426e39f4ea9SMauro Carvalho Chehab 			continue;
427de3910ebSMauro Carvalho Chehab 		err = edac_create_csrow_object(mci, mci->csrows[i], i);
4287a623c03SMauro Carvalho Chehab 		if (err < 0)
4297a623c03SMauro Carvalho Chehab 			goto error;
4307a623c03SMauro Carvalho Chehab 	}
4317a623c03SMauro Carvalho Chehab 	return 0;
4327a623c03SMauro Carvalho Chehab 
4337a623c03SMauro Carvalho Chehab error:
4347a623c03SMauro Carvalho Chehab 	for (--i; i >= 0; i--) {
435de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
436e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
437e39f4ea9SMauro Carvalho Chehab 			continue;
4387a623c03SMauro Carvalho Chehab 		for (chan = csrow->nr_channels - 1; chan >= 0; chan--) {
439de3910ebSMauro Carvalho Chehab 			if (!csrow->channels[chan]->dimm->nr_pages)
440e39f4ea9SMauro Carvalho Chehab 				continue;
4417a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
4427a623c03SMauro Carvalho Chehab 						dynamic_csrow_dimm_attr[chan]);
4437a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
4447a623c03SMauro Carvalho Chehab 						dynamic_csrow_ce_count_attr[chan]);
4457a623c03SMauro Carvalho Chehab 		}
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 {
4547a623c03SMauro Carvalho Chehab 	int i, chan;
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;
4617a623c03SMauro Carvalho Chehab 		for (chan = csrow->nr_channels - 1; chan >= 0; chan--) {
462de3910ebSMauro Carvalho Chehab 			if (!csrow->channels[chan]->dimm->nr_pages)
463e39f4ea9SMauro Carvalho Chehab 				continue;
464956b9ba1SJoe Perches 			edac_dbg(1, "Removing csrow %d channel %d sysfs nodes\n",
4657a623c03SMauro Carvalho Chehab 				 i, chan);
4667a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
4677a623c03SMauro Carvalho Chehab 						dynamic_csrow_dimm_attr[chan]);
4687a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
4697a623c03SMauro Carvalho Chehab 						dynamic_csrow_ce_count_attr[chan]);
4707a623c03SMauro Carvalho Chehab 		}
471de3910ebSMauro Carvalho Chehab 		put_device(&mci->csrows[i]->dev);
472de3910ebSMauro Carvalho Chehab 		device_del(&mci->csrows[i]->dev);
4737a623c03SMauro Carvalho Chehab 	}
4747a623c03SMauro Carvalho Chehab }
47519974710SMauro Carvalho Chehab #endif
47619974710SMauro Carvalho Chehab 
47719974710SMauro Carvalho Chehab /*
47819974710SMauro Carvalho Chehab  * Per-dimm (or per-rank) devices
47919974710SMauro Carvalho Chehab  */
48019974710SMauro Carvalho Chehab 
48119974710SMauro Carvalho Chehab #define to_dimm(k) container_of(k, struct dimm_info, dev)
48219974710SMauro Carvalho Chehab 
48319974710SMauro Carvalho Chehab /* show/store functions for DIMM Label attributes */
48419974710SMauro Carvalho Chehab static ssize_t dimmdev_location_show(struct device *dev,
48519974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
48619974710SMauro Carvalho Chehab {
48719974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
48819974710SMauro Carvalho Chehab 
4896e84d359SMauro Carvalho Chehab 	return edac_dimm_info_location(dimm, data, PAGE_SIZE);
49019974710SMauro Carvalho Chehab }
49119974710SMauro Carvalho Chehab 
49219974710SMauro Carvalho Chehab static ssize_t dimmdev_label_show(struct device *dev,
49319974710SMauro Carvalho Chehab 				  struct device_attribute *mattr, char *data)
49419974710SMauro Carvalho Chehab {
49519974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
49619974710SMauro Carvalho Chehab 
49719974710SMauro Carvalho Chehab 	/* if field has not been initialized, there is nothing to send */
49819974710SMauro Carvalho Chehab 	if (!dimm->label[0])
49919974710SMauro Carvalho Chehab 		return 0;
50019974710SMauro Carvalho Chehab 
50119974710SMauro Carvalho Chehab 	return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label);
50219974710SMauro Carvalho Chehab }
50319974710SMauro Carvalho Chehab 
50419974710SMauro Carvalho Chehab static ssize_t dimmdev_label_store(struct device *dev,
50519974710SMauro Carvalho Chehab 				   struct device_attribute *mattr,
50619974710SMauro Carvalho Chehab 				   const char *data,
50719974710SMauro Carvalho Chehab 				   size_t count)
50819974710SMauro Carvalho Chehab {
50919974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
51019974710SMauro Carvalho Chehab 
51119974710SMauro Carvalho Chehab 	ssize_t max_size = 0;
51219974710SMauro Carvalho Chehab 
51319974710SMauro Carvalho Chehab 	max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
51419974710SMauro Carvalho Chehab 	strncpy(dimm->label, data, max_size);
51519974710SMauro Carvalho Chehab 	dimm->label[max_size] = '\0';
51619974710SMauro Carvalho Chehab 
51719974710SMauro Carvalho Chehab 	return max_size;
51819974710SMauro Carvalho Chehab }
51919974710SMauro Carvalho Chehab 
52019974710SMauro Carvalho Chehab static ssize_t dimmdev_size_show(struct device *dev,
52119974710SMauro Carvalho Chehab 				 struct device_attribute *mattr, char *data)
52219974710SMauro Carvalho Chehab {
52319974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
52419974710SMauro Carvalho Chehab 
52519974710SMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
52619974710SMauro Carvalho Chehab }
52719974710SMauro Carvalho Chehab 
52819974710SMauro Carvalho Chehab static ssize_t dimmdev_mem_type_show(struct device *dev,
52919974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
53019974710SMauro Carvalho Chehab {
53119974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
53219974710SMauro Carvalho Chehab 
53319974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", mem_types[dimm->mtype]);
53419974710SMauro Carvalho Chehab }
53519974710SMauro Carvalho Chehab 
53619974710SMauro Carvalho Chehab static ssize_t dimmdev_dev_type_show(struct device *dev,
53719974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
53819974710SMauro Carvalho Chehab {
53919974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
54019974710SMauro Carvalho Chehab 
54119974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[dimm->dtype]);
54219974710SMauro Carvalho Chehab }
54319974710SMauro Carvalho Chehab 
54419974710SMauro Carvalho Chehab static ssize_t dimmdev_edac_mode_show(struct device *dev,
54519974710SMauro Carvalho Chehab 				      struct device_attribute *mattr,
54619974710SMauro Carvalho Chehab 				      char *data)
54719974710SMauro Carvalho Chehab {
54819974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
54919974710SMauro Carvalho Chehab 
55019974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
55119974710SMauro Carvalho Chehab }
55219974710SMauro Carvalho Chehab 
55319974710SMauro Carvalho Chehab /* dimm/rank attribute files */
55419974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
55519974710SMauro Carvalho Chehab 		   dimmdev_label_show, dimmdev_label_store);
55619974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
55719974710SMauro Carvalho Chehab static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
55819974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
55919974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
56019974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
56119974710SMauro Carvalho Chehab 
56219974710SMauro Carvalho Chehab /* attributes of the dimm<id>/rank<id> object */
56319974710SMauro Carvalho Chehab static struct attribute *dimm_attrs[] = {
56419974710SMauro Carvalho Chehab 	&dev_attr_dimm_label.attr,
56519974710SMauro Carvalho Chehab 	&dev_attr_dimm_location.attr,
56619974710SMauro Carvalho Chehab 	&dev_attr_size.attr,
56719974710SMauro Carvalho Chehab 	&dev_attr_dimm_mem_type.attr,
56819974710SMauro Carvalho Chehab 	&dev_attr_dimm_dev_type.attr,
56919974710SMauro Carvalho Chehab 	&dev_attr_dimm_edac_mode.attr,
57019974710SMauro Carvalho Chehab 	NULL,
57119974710SMauro Carvalho Chehab };
57219974710SMauro Carvalho Chehab 
57319974710SMauro Carvalho Chehab static struct attribute_group dimm_attr_grp = {
57419974710SMauro Carvalho Chehab 	.attrs	= dimm_attrs,
57519974710SMauro Carvalho Chehab };
57619974710SMauro Carvalho Chehab 
57719974710SMauro Carvalho Chehab static const struct attribute_group *dimm_attr_groups[] = {
57819974710SMauro Carvalho Chehab 	&dimm_attr_grp,
57919974710SMauro Carvalho Chehab 	NULL
58019974710SMauro Carvalho Chehab };
58119974710SMauro Carvalho Chehab 
582de3910ebSMauro Carvalho Chehab static void dimm_attr_release(struct device *dev)
58319974710SMauro Carvalho Chehab {
584de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm = container_of(dev, struct dimm_info, dev);
585de3910ebSMauro Carvalho Chehab 
586956b9ba1SJoe Perches 	edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev));
587de3910ebSMauro Carvalho Chehab 	kfree(dimm);
58819974710SMauro Carvalho Chehab }
58919974710SMauro Carvalho Chehab 
59019974710SMauro Carvalho Chehab static struct device_type dimm_attr_type = {
59119974710SMauro Carvalho Chehab 	.groups		= dimm_attr_groups,
59219974710SMauro Carvalho Chehab 	.release	= dimm_attr_release,
59319974710SMauro Carvalho Chehab };
59419974710SMauro Carvalho Chehab 
59519974710SMauro Carvalho Chehab /* Create a DIMM object under specifed memory controller device */
59619974710SMauro Carvalho Chehab static int edac_create_dimm_object(struct mem_ctl_info *mci,
59719974710SMauro Carvalho Chehab 				   struct dimm_info *dimm,
59819974710SMauro Carvalho Chehab 				   int index)
59919974710SMauro Carvalho Chehab {
60019974710SMauro Carvalho Chehab 	int err;
60119974710SMauro Carvalho Chehab 	dimm->mci = mci;
60219974710SMauro Carvalho Chehab 
60319974710SMauro Carvalho Chehab 	dimm->dev.type = &dimm_attr_type;
60419974710SMauro Carvalho Chehab 	dimm->dev.bus = &mci->bus;
60519974710SMauro Carvalho Chehab 	device_initialize(&dimm->dev);
60619974710SMauro Carvalho Chehab 
60719974710SMauro Carvalho Chehab 	dimm->dev.parent = &mci->dev;
60819974710SMauro Carvalho Chehab 	if (mci->mem_is_per_rank)
60919974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "rank%d", index);
61019974710SMauro Carvalho Chehab 	else
61119974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "dimm%d", index);
61219974710SMauro Carvalho Chehab 	dev_set_drvdata(&dimm->dev, dimm);
61319974710SMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
61419974710SMauro Carvalho Chehab 
61519974710SMauro Carvalho Chehab 	err =  device_add(&dimm->dev);
61619974710SMauro Carvalho Chehab 
617956b9ba1SJoe Perches 	edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev));
61819974710SMauro Carvalho Chehab 
61919974710SMauro Carvalho Chehab 	return err;
62019974710SMauro Carvalho Chehab }
6217a623c03SMauro Carvalho Chehab 
6227a623c03SMauro Carvalho Chehab /*
6237a623c03SMauro Carvalho Chehab  * Memory controller device
6247a623c03SMauro Carvalho Chehab  */
6257a623c03SMauro Carvalho Chehab 
6267a623c03SMauro Carvalho Chehab #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
6277a623c03SMauro Carvalho Chehab 
6287a623c03SMauro Carvalho Chehab static ssize_t mci_reset_counters_store(struct device *dev,
6297a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
6307c9281d7SDouglas Thompson 					const char *data, size_t count)
6317c9281d7SDouglas Thompson {
6327a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
6337a623c03SMauro Carvalho Chehab 	int cnt, row, chan, i;
6345926ff50SMauro Carvalho Chehab 	mci->ue_mc = 0;
6355926ff50SMauro Carvalho Chehab 	mci->ce_mc = 0;
6367a623c03SMauro Carvalho Chehab 	mci->ue_noinfo_count = 0;
6377a623c03SMauro Carvalho Chehab 	mci->ce_noinfo_count = 0;
6387c9281d7SDouglas Thompson 
6397c9281d7SDouglas Thompson 	for (row = 0; row < mci->nr_csrows; row++) {
640de3910ebSMauro Carvalho Chehab 		struct csrow_info *ri = mci->csrows[row];
6417c9281d7SDouglas Thompson 
6427c9281d7SDouglas Thompson 		ri->ue_count = 0;
6437c9281d7SDouglas Thompson 		ri->ce_count = 0;
6447c9281d7SDouglas Thompson 
6457c9281d7SDouglas Thompson 		for (chan = 0; chan < ri->nr_channels; chan++)
646de3910ebSMauro Carvalho Chehab 			ri->channels[chan]->ce_count = 0;
6477c9281d7SDouglas Thompson 	}
6487c9281d7SDouglas Thompson 
6497a623c03SMauro Carvalho Chehab 	cnt = 1;
6507a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
6517a623c03SMauro Carvalho Chehab 		cnt *= mci->layers[i].size;
6527a623c03SMauro Carvalho Chehab 		memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32));
6537a623c03SMauro Carvalho Chehab 		memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32));
6547a623c03SMauro Carvalho Chehab 	}
6557a623c03SMauro Carvalho Chehab 
6567c9281d7SDouglas Thompson 	mci->start_time = jiffies;
6577c9281d7SDouglas Thompson 	return count;
6587c9281d7SDouglas Thompson }
6597c9281d7SDouglas Thompson 
66039094443SBorislav Petkov /* Memory scrubbing interface:
66139094443SBorislav Petkov  *
66239094443SBorislav Petkov  * A MC driver can limit the scrubbing bandwidth based on the CPU type.
66339094443SBorislav Petkov  * Therefore, ->set_sdram_scrub_rate should be made to return the actual
66439094443SBorislav Petkov  * bandwidth that is accepted or 0 when scrubbing is to be disabled.
66539094443SBorislav Petkov  *
66639094443SBorislav Petkov  * Negative value still means that an error has occurred while setting
66739094443SBorislav Petkov  * the scrub rate.
66839094443SBorislav Petkov  */
6697a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
6707a623c03SMauro Carvalho Chehab 					  struct device_attribute *mattr,
6717c9281d7SDouglas Thompson 					  const char *data, size_t count)
6727c9281d7SDouglas Thompson {
6737a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
674eba042a8SBorislav Petkov 	unsigned long bandwidth = 0;
67539094443SBorislav Petkov 	int new_bw = 0;
6767c9281d7SDouglas Thompson 
67739094443SBorislav Petkov 	if (!mci->set_sdram_scrub_rate)
6785e8e19bfSBorislav Petkov 		return -ENODEV;
679eba042a8SBorislav Petkov 
680eba042a8SBorislav Petkov 	if (strict_strtoul(data, 10, &bandwidth) < 0)
681eba042a8SBorislav Petkov 		return -EINVAL;
682eba042a8SBorislav Petkov 
68339094443SBorislav Petkov 	new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
6844949603aSMarkus Trippelsdorf 	if (new_bw < 0) {
6854949603aSMarkus Trippelsdorf 		edac_printk(KERN_WARNING, EDAC_MC,
6864949603aSMarkus Trippelsdorf 			    "Error setting scrub rate to: %lu\n", bandwidth);
6874949603aSMarkus Trippelsdorf 		return -EINVAL;
6887c9281d7SDouglas Thompson 	}
6897c9281d7SDouglas Thompson 
6904949603aSMarkus Trippelsdorf 	return count;
6917c9281d7SDouglas Thompson }
692eba042a8SBorislav Petkov 
69339094443SBorislav Petkov /*
69439094443SBorislav Petkov  * ->get_sdram_scrub_rate() return value semantics same as above.
69539094443SBorislav Petkov  */
6967a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
6977a623c03SMauro Carvalho Chehab 					 struct device_attribute *mattr,
6987a623c03SMauro Carvalho Chehab 					 char *data)
69939094443SBorislav Petkov {
7007a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
70139094443SBorislav Petkov 	int bandwidth = 0;
70239094443SBorislav Petkov 
70339094443SBorislav Petkov 	if (!mci->get_sdram_scrub_rate)
7045e8e19bfSBorislav Petkov 		return -ENODEV;
70539094443SBorislav Petkov 
70639094443SBorislav Petkov 	bandwidth = mci->get_sdram_scrub_rate(mci);
70739094443SBorislav Petkov 	if (bandwidth < 0) {
708eba042a8SBorislav Petkov 		edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n");
70939094443SBorislav Petkov 		return bandwidth;
710eba042a8SBorislav Petkov 	}
71139094443SBorislav Petkov 
7127c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", bandwidth);
7137c9281d7SDouglas Thompson }
7147c9281d7SDouglas Thompson 
7157c9281d7SDouglas Thompson /* default attribute files for the MCI object */
7167a623c03SMauro Carvalho Chehab static ssize_t mci_ue_count_show(struct device *dev,
7177a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7187a623c03SMauro Carvalho Chehab 				 char *data)
7197c9281d7SDouglas Thompson {
7207a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7217a623c03SMauro Carvalho Chehab 
7225926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ue_mc);
7237c9281d7SDouglas Thompson }
7247c9281d7SDouglas Thompson 
7257a623c03SMauro Carvalho Chehab static ssize_t mci_ce_count_show(struct device *dev,
7267a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7277a623c03SMauro Carvalho Chehab 				 char *data)
7287c9281d7SDouglas Thompson {
7297a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7307a623c03SMauro Carvalho Chehab 
7315926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ce_mc);
7327c9281d7SDouglas Thompson }
7337c9281d7SDouglas Thompson 
7347a623c03SMauro Carvalho Chehab static ssize_t mci_ce_noinfo_show(struct device *dev,
7357a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7367a623c03SMauro Carvalho Chehab 				  char *data)
7377c9281d7SDouglas Thompson {
7387a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7397a623c03SMauro Carvalho Chehab 
7407c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ce_noinfo_count);
7417c9281d7SDouglas Thompson }
7427c9281d7SDouglas Thompson 
7437a623c03SMauro Carvalho Chehab static ssize_t mci_ue_noinfo_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 
7497c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ue_noinfo_count);
7507c9281d7SDouglas Thompson }
7517c9281d7SDouglas Thompson 
7527a623c03SMauro Carvalho Chehab static ssize_t mci_seconds_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 
7587c9281d7SDouglas Thompson 	return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
7597c9281d7SDouglas Thompson }
7607c9281d7SDouglas Thompson 
7617a623c03SMauro Carvalho Chehab static ssize_t mci_ctl_name_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, "%s\n", mci->ctl_name);
7687c9281d7SDouglas Thompson }
7697c9281d7SDouglas Thompson 
7707a623c03SMauro Carvalho Chehab static ssize_t mci_size_mb_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);
775a895bf8bSMauro Carvalho Chehab 	int total_pages = 0, csrow_idx, j;
7767c9281d7SDouglas Thompson 
777a895bf8bSMauro Carvalho Chehab 	for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
778de3910ebSMauro Carvalho Chehab 		struct csrow_info *csrow = mci->csrows[csrow_idx];
7797c9281d7SDouglas Thompson 
780a895bf8bSMauro Carvalho Chehab 		for (j = 0; j < csrow->nr_channels; j++) {
781de3910ebSMauro Carvalho Chehab 			struct dimm_info *dimm = csrow->channels[j]->dimm;
7827c9281d7SDouglas Thompson 
783a895bf8bSMauro Carvalho Chehab 			total_pages += dimm->nr_pages;
784a895bf8bSMauro Carvalho Chehab 		}
7857c9281d7SDouglas Thompson 	}
7867c9281d7SDouglas Thompson 
7877c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
7887c9281d7SDouglas Thompson }
7897c9281d7SDouglas Thompson 
7908ad6c78aSMauro Carvalho Chehab static ssize_t mci_max_location_show(struct device *dev,
7918ad6c78aSMauro Carvalho Chehab 				     struct device_attribute *mattr,
7928ad6c78aSMauro Carvalho Chehab 				     char *data)
7938ad6c78aSMauro Carvalho Chehab {
7948ad6c78aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7958ad6c78aSMauro Carvalho Chehab 	int i;
7968ad6c78aSMauro Carvalho Chehab 	char *p = data;
7978ad6c78aSMauro Carvalho Chehab 
7988ad6c78aSMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
7998ad6c78aSMauro Carvalho Chehab 		p += sprintf(p, "%s %d ",
8008ad6c78aSMauro Carvalho Chehab 			     edac_layer_name[mci->layers[i].type],
8018ad6c78aSMauro Carvalho Chehab 			     mci->layers[i].size - 1);
8028ad6c78aSMauro Carvalho Chehab 	}
8038ad6c78aSMauro Carvalho Chehab 
8048ad6c78aSMauro Carvalho Chehab 	return p - data;
8058ad6c78aSMauro Carvalho Chehab }
8068ad6c78aSMauro Carvalho Chehab 
807452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
808452a6bf9SMauro Carvalho Chehab static ssize_t edac_fake_inject_write(struct file *file,
809452a6bf9SMauro Carvalho Chehab 				      const char __user *data,
810452a6bf9SMauro Carvalho Chehab 				      size_t count, loff_t *ppos)
811452a6bf9SMauro Carvalho Chehab {
812452a6bf9SMauro Carvalho Chehab 	struct device *dev = file->private_data;
813452a6bf9SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
814452a6bf9SMauro Carvalho Chehab 	static enum hw_event_mc_err_type type;
815452a6bf9SMauro Carvalho Chehab 
816452a6bf9SMauro Carvalho Chehab 	type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
817452a6bf9SMauro Carvalho Chehab 				   : HW_EVENT_ERR_CORRECTED;
818452a6bf9SMauro Carvalho Chehab 
819452a6bf9SMauro Carvalho Chehab 	printk(KERN_DEBUG
820452a6bf9SMauro Carvalho Chehab 	       "Generating a %s fake error to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
821452a6bf9SMauro Carvalho Chehab 		(type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
822452a6bf9SMauro Carvalho Chehab 		mci->fake_inject_layer[0],
823452a6bf9SMauro Carvalho Chehab 		mci->fake_inject_layer[1],
824452a6bf9SMauro Carvalho Chehab 		mci->fake_inject_layer[2]
825452a6bf9SMauro Carvalho Chehab 	       );
8269eb07a7fSMauro Carvalho Chehab 	edac_mc_handle_error(type, mci, 1, 0, 0, 0,
827452a6bf9SMauro Carvalho Chehab 			     mci->fake_inject_layer[0],
828452a6bf9SMauro Carvalho Chehab 			     mci->fake_inject_layer[1],
829452a6bf9SMauro Carvalho Chehab 			     mci->fake_inject_layer[2],
83003f7eae8SMauro Carvalho Chehab 			     "FAKE ERROR", "for EDAC testing only");
831452a6bf9SMauro Carvalho Chehab 
832452a6bf9SMauro Carvalho Chehab 	return count;
833452a6bf9SMauro Carvalho Chehab }
834452a6bf9SMauro Carvalho Chehab 
835452a6bf9SMauro Carvalho Chehab static int debugfs_open(struct inode *inode, struct file *file)
836452a6bf9SMauro Carvalho Chehab {
837452a6bf9SMauro Carvalho Chehab 	file->private_data = inode->i_private;
838452a6bf9SMauro Carvalho Chehab 	return 0;
839452a6bf9SMauro Carvalho Chehab }
840452a6bf9SMauro Carvalho Chehab 
841452a6bf9SMauro Carvalho Chehab static const struct file_operations debug_fake_inject_fops = {
842452a6bf9SMauro Carvalho Chehab 	.open = debugfs_open,
843452a6bf9SMauro Carvalho Chehab 	.write = edac_fake_inject_write,
844452a6bf9SMauro Carvalho Chehab 	.llseek = generic_file_llseek,
845452a6bf9SMauro Carvalho Chehab };
846452a6bf9SMauro Carvalho Chehab #endif
847452a6bf9SMauro Carvalho Chehab 
8487c9281d7SDouglas Thompson /* default Control file */
8497a623c03SMauro Carvalho Chehab DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
8507c9281d7SDouglas Thompson 
8517c9281d7SDouglas Thompson /* default Attribute files */
8527a623c03SMauro Carvalho Chehab DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
8537a623c03SMauro Carvalho Chehab DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
8547a623c03SMauro Carvalho Chehab DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
8557a623c03SMauro Carvalho Chehab DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
8567a623c03SMauro Carvalho Chehab DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
8577a623c03SMauro Carvalho Chehab DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
8587a623c03SMauro Carvalho Chehab DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
8598ad6c78aSMauro Carvalho Chehab DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
8607c9281d7SDouglas Thompson 
8617c9281d7SDouglas Thompson /* memory scrubber attribute file */
8627a623c03SMauro Carvalho Chehab DEVICE_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show,
863e27e3dacSDouglas Thompson 	mci_sdram_scrub_rate_store);
8647c9281d7SDouglas Thompson 
8657a623c03SMauro Carvalho Chehab static struct attribute *mci_attrs[] = {
8667a623c03SMauro Carvalho Chehab 	&dev_attr_reset_counters.attr,
8677a623c03SMauro Carvalho Chehab 	&dev_attr_mc_name.attr,
8687a623c03SMauro Carvalho Chehab 	&dev_attr_size_mb.attr,
8697a623c03SMauro Carvalho Chehab 	&dev_attr_seconds_since_reset.attr,
8707a623c03SMauro Carvalho Chehab 	&dev_attr_ue_noinfo_count.attr,
8717a623c03SMauro Carvalho Chehab 	&dev_attr_ce_noinfo_count.attr,
8727a623c03SMauro Carvalho Chehab 	&dev_attr_ue_count.attr,
8737a623c03SMauro Carvalho Chehab 	&dev_attr_ce_count.attr,
8747a623c03SMauro Carvalho Chehab 	&dev_attr_sdram_scrub_rate.attr,
8758ad6c78aSMauro Carvalho Chehab 	&dev_attr_max_location.attr,
8767c9281d7SDouglas Thompson 	NULL
8777c9281d7SDouglas Thompson };
8787c9281d7SDouglas Thompson 
8797a623c03SMauro Carvalho Chehab static struct attribute_group mci_attr_grp = {
8807a623c03SMauro Carvalho Chehab 	.attrs	= mci_attrs,
8817c9281d7SDouglas Thompson };
8827c9281d7SDouglas Thompson 
8837a623c03SMauro Carvalho Chehab static const struct attribute_group *mci_attr_groups[] = {
8847a623c03SMauro Carvalho Chehab 	&mci_attr_grp,
8857a623c03SMauro Carvalho Chehab 	NULL
886cc301b3aSMauro Carvalho Chehab };
887cc301b3aSMauro Carvalho Chehab 
888de3910ebSMauro Carvalho Chehab static void mci_attr_release(struct device *dev)
8897a623c03SMauro Carvalho Chehab {
890de3910ebSMauro Carvalho Chehab 	struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
891de3910ebSMauro Carvalho Chehab 
892956b9ba1SJoe Perches 	edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
893de3910ebSMauro Carvalho Chehab 	kfree(mci);
8947a623c03SMauro Carvalho Chehab }
8957a623c03SMauro Carvalho Chehab 
8967a623c03SMauro Carvalho Chehab static struct device_type mci_attr_type = {
8977a623c03SMauro Carvalho Chehab 	.groups		= mci_attr_groups,
8987a623c03SMauro Carvalho Chehab 	.release	= mci_attr_release,
899cc301b3aSMauro Carvalho Chehab };
900cc301b3aSMauro Carvalho Chehab 
901452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
902e7930ba4SRob Herring static struct dentry *edac_debugfs;
903e7930ba4SRob Herring 
904e7930ba4SRob Herring int __init edac_debugfs_init(void)
905e7930ba4SRob Herring {
906e7930ba4SRob Herring 	edac_debugfs = debugfs_create_dir("edac", NULL);
907e7930ba4SRob Herring 	if (IS_ERR(edac_debugfs)) {
908e7930ba4SRob Herring 		edac_debugfs = NULL;
909e7930ba4SRob Herring 		return -ENOMEM;
910e7930ba4SRob Herring 	}
911e7930ba4SRob Herring 	return 0;
912e7930ba4SRob Herring }
913e7930ba4SRob Herring 
914e7930ba4SRob Herring void __exit edac_debugfs_exit(void)
915e7930ba4SRob Herring {
916e7930ba4SRob Herring 	debugfs_remove(edac_debugfs);
917e7930ba4SRob Herring }
918e7930ba4SRob Herring 
919452a6bf9SMauro Carvalho Chehab int edac_create_debug_nodes(struct mem_ctl_info *mci)
920452a6bf9SMauro Carvalho Chehab {
921452a6bf9SMauro Carvalho Chehab 	struct dentry *d, *parent;
922452a6bf9SMauro Carvalho Chehab 	char name[80];
923452a6bf9SMauro Carvalho Chehab 	int i;
924452a6bf9SMauro Carvalho Chehab 
925e7930ba4SRob Herring 	if (!edac_debugfs)
926e7930ba4SRob Herring 		return -ENODEV;
927e7930ba4SRob Herring 
928e7930ba4SRob Herring 	d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
929452a6bf9SMauro Carvalho Chehab 	if (!d)
930452a6bf9SMauro Carvalho Chehab 		return -ENOMEM;
931452a6bf9SMauro Carvalho Chehab 	parent = d;
932452a6bf9SMauro Carvalho Chehab 
933452a6bf9SMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
934452a6bf9SMauro Carvalho Chehab 		sprintf(name, "fake_inject_%s",
935452a6bf9SMauro Carvalho Chehab 			     edac_layer_name[mci->layers[i].type]);
936452a6bf9SMauro Carvalho Chehab 		d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
937452a6bf9SMauro Carvalho Chehab 				      &mci->fake_inject_layer[i]);
938452a6bf9SMauro Carvalho Chehab 		if (!d)
939452a6bf9SMauro Carvalho Chehab 			goto nomem;
940452a6bf9SMauro Carvalho Chehab 	}
941452a6bf9SMauro Carvalho Chehab 
942452a6bf9SMauro Carvalho Chehab 	d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
943452a6bf9SMauro Carvalho Chehab 				&mci->fake_inject_ue);
944452a6bf9SMauro Carvalho Chehab 	if (!d)
945452a6bf9SMauro Carvalho Chehab 		goto nomem;
946452a6bf9SMauro Carvalho Chehab 
947452a6bf9SMauro Carvalho Chehab 	d = debugfs_create_file("fake_inject", S_IWUSR, parent,
948452a6bf9SMauro Carvalho Chehab 				&mci->dev,
949452a6bf9SMauro Carvalho Chehab 				&debug_fake_inject_fops);
950452a6bf9SMauro Carvalho Chehab 	if (!d)
951452a6bf9SMauro Carvalho Chehab 		goto nomem;
952452a6bf9SMauro Carvalho Chehab 
953e7930ba4SRob Herring 	mci->debugfs = parent;
954452a6bf9SMauro Carvalho Chehab 	return 0;
955452a6bf9SMauro Carvalho Chehab nomem:
956452a6bf9SMauro Carvalho Chehab 	debugfs_remove(mci->debugfs);
957452a6bf9SMauro Carvalho Chehab 	return -ENOMEM;
958452a6bf9SMauro Carvalho Chehab }
959452a6bf9SMauro Carvalho Chehab #endif
960452a6bf9SMauro Carvalho Chehab 
9617c9281d7SDouglas Thompson /*
9627c9281d7SDouglas Thompson  * Create a new Memory Controller kobject instance,
9637c9281d7SDouglas Thompson  *	mc<id> under the 'mc' directory
9647c9281d7SDouglas Thompson  *
9657c9281d7SDouglas Thompson  * Return:
9667c9281d7SDouglas Thompson  *	0	Success
9677c9281d7SDouglas Thompson  *	!0	Failure
9687c9281d7SDouglas Thompson  */
9697c9281d7SDouglas Thompson int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
9707c9281d7SDouglas Thompson {
9717a623c03SMauro Carvalho Chehab 	int i, err;
9727c9281d7SDouglas Thompson 
9737a623c03SMauro Carvalho Chehab 	/*
9747a623c03SMauro Carvalho Chehab 	 * The memory controller needs its own bus, in order to avoid
9757a623c03SMauro Carvalho Chehab 	 * namespace conflicts at /sys/bus/edac.
97642a8e397SDouglas Thompson 	 */
977de3910ebSMauro Carvalho Chehab 	mci->bus.name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx);
978de3910ebSMauro Carvalho Chehab 	if (!mci->bus.name)
979de3910ebSMauro Carvalho Chehab 		return -ENOMEM;
980956b9ba1SJoe Perches 	edac_dbg(0, "creating bus %s\n", mci->bus.name);
9817a623c03SMauro Carvalho Chehab 	err = bus_register(&mci->bus);
9827a623c03SMauro Carvalho Chehab 	if (err < 0)
9837a623c03SMauro Carvalho Chehab 		return err;
9847a623c03SMauro Carvalho Chehab 
985de3910ebSMauro Carvalho Chehab 	/* get the /sys/devices/system/edac subsys reference */
986de3910ebSMauro Carvalho Chehab 	mci->dev.type = &mci_attr_type;
987de3910ebSMauro Carvalho Chehab 	device_initialize(&mci->dev);
988de3910ebSMauro Carvalho Chehab 
989de3910ebSMauro Carvalho Chehab 	mci->dev.parent = mci_pdev;
990de3910ebSMauro Carvalho Chehab 	mci->dev.bus = &mci->bus;
991de3910ebSMauro Carvalho Chehab 	dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
992de3910ebSMauro Carvalho Chehab 	dev_set_drvdata(&mci->dev, mci);
993de3910ebSMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
994de3910ebSMauro Carvalho Chehab 
995956b9ba1SJoe Perches 	edac_dbg(0, "creating device %s\n", dev_name(&mci->dev));
9967a623c03SMauro Carvalho Chehab 	err = device_add(&mci->dev);
9977a623c03SMauro Carvalho Chehab 	if (err < 0) {
9987a623c03SMauro Carvalho Chehab 		bus_unregister(&mci->bus);
9997a623c03SMauro Carvalho Chehab 		kfree(mci->bus.name);
10007a623c03SMauro Carvalho Chehab 		return err;
10018096cfafSDoug Thompson 	}
100242a8e397SDouglas Thompson 
10037a623c03SMauro Carvalho Chehab 	/*
10047a623c03SMauro Carvalho Chehab 	 * Create the dimm/rank devices
10057c9281d7SDouglas Thompson 	 */
10067a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
1007de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
10087a623c03SMauro Carvalho Chehab 		/* Only expose populated DIMMs */
10097a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
10107a623c03SMauro Carvalho Chehab 			continue;
10117a623c03SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
1012956b9ba1SJoe Perches 		edac_dbg(1, "creating dimm%d, located at ", i);
10137a623c03SMauro Carvalho Chehab 		if (edac_debug_level >= 1) {
10147a623c03SMauro Carvalho Chehab 			int lay;
10157a623c03SMauro Carvalho Chehab 			for (lay = 0; lay < mci->n_layers; lay++)
10167a623c03SMauro Carvalho Chehab 				printk(KERN_CONT "%s %d ",
10177a623c03SMauro Carvalho Chehab 					edac_layer_name[mci->layers[lay].type],
10187a623c03SMauro Carvalho Chehab 					dimm->location[lay]);
10197a623c03SMauro Carvalho Chehab 			printk(KERN_CONT "\n");
10207c9281d7SDouglas Thompson 		}
10217a623c03SMauro Carvalho Chehab #endif
102219974710SMauro Carvalho Chehab 		err = edac_create_dimm_object(mci, dimm, i);
102319974710SMauro Carvalho Chehab 		if (err) {
1024956b9ba1SJoe Perches 			edac_dbg(1, "failure: create dimm %d obj\n", i);
102519974710SMauro Carvalho Chehab 			goto fail;
102619974710SMauro Carvalho Chehab 		}
10277c9281d7SDouglas Thompson 	}
10287a623c03SMauro Carvalho Chehab 
102919974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
10307a623c03SMauro Carvalho Chehab 	err = edac_create_csrow_objects(mci);
10317a623c03SMauro Carvalho Chehab 	if (err < 0)
10327a623c03SMauro Carvalho Chehab 		goto fail;
103319974710SMauro Carvalho Chehab #endif
10347c9281d7SDouglas Thompson 
1035452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
1036452a6bf9SMauro Carvalho Chehab 	edac_create_debug_nodes(mci);
1037452a6bf9SMauro Carvalho Chehab #endif
10387c9281d7SDouglas Thompson 	return 0;
10397c9281d7SDouglas Thompson 
10407a623c03SMauro Carvalho Chehab fail:
10417c9281d7SDouglas Thompson 	for (i--; i >= 0; i--) {
1042de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
10437a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
10447a623c03SMauro Carvalho Chehab 			continue;
10457a623c03SMauro Carvalho Chehab 		put_device(&dimm->dev);
10467a623c03SMauro Carvalho Chehab 		device_del(&dimm->dev);
10477c9281d7SDouglas Thompson 	}
10487a623c03SMauro Carvalho Chehab 	put_device(&mci->dev);
10497a623c03SMauro Carvalho Chehab 	device_del(&mci->dev);
10507a623c03SMauro Carvalho Chehab 	bus_unregister(&mci->bus);
10517a623c03SMauro Carvalho Chehab 	kfree(mci->bus.name);
10527c9281d7SDouglas Thompson 	return err;
10537c9281d7SDouglas Thompson }
10547c9281d7SDouglas Thompson 
10557c9281d7SDouglas Thompson /*
10567c9281d7SDouglas Thompson  * remove a Memory Controller instance
10577c9281d7SDouglas Thompson  */
10587c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
10597c9281d7SDouglas Thompson {
10607a623c03SMauro Carvalho Chehab 	int i;
10617c9281d7SDouglas Thompson 
1062956b9ba1SJoe Perches 	edac_dbg(0, "\n");
10637c9281d7SDouglas Thompson 
1064452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
1065452a6bf9SMauro Carvalho Chehab 	debugfs_remove(mci->debugfs);
1066452a6bf9SMauro Carvalho Chehab #endif
106719974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
10687a623c03SMauro Carvalho Chehab 	edac_delete_csrow_objects(mci);
106919974710SMauro Carvalho Chehab #endif
1070a895bf8bSMauro Carvalho Chehab 
10717a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
1072de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
10737a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
10747a623c03SMauro Carvalho Chehab 			continue;
1075956b9ba1SJoe Perches 		edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev));
10767a623c03SMauro Carvalho Chehab 		put_device(&dimm->dev);
10777a623c03SMauro Carvalho Chehab 		device_del(&dimm->dev);
10787c9281d7SDouglas Thompson 	}
10797c9281d7SDouglas Thompson }
10807c9281d7SDouglas Thompson 
10817a623c03SMauro Carvalho Chehab void edac_unregister_sysfs(struct mem_ctl_info *mci)
10828096cfafSDoug Thompson {
1083956b9ba1SJoe Perches 	edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
10847a623c03SMauro Carvalho Chehab 	put_device(&mci->dev);
10857a623c03SMauro Carvalho Chehab 	device_del(&mci->dev);
10867a623c03SMauro Carvalho Chehab 	bus_unregister(&mci->bus);
10877a623c03SMauro Carvalho Chehab 	kfree(mci->bus.name);
10887a623c03SMauro Carvalho Chehab }
10898096cfafSDoug Thompson 
1090de3910ebSMauro Carvalho Chehab static void mc_attr_release(struct device *dev)
10917a623c03SMauro Carvalho Chehab {
1092de3910ebSMauro Carvalho Chehab 	/*
1093de3910ebSMauro Carvalho Chehab 	 * There's no container structure here, as this is just the mci
1094de3910ebSMauro Carvalho Chehab 	 * parent device, used to create the /sys/devices/mc sysfs node.
1095de3910ebSMauro Carvalho Chehab 	 * So, there are no attributes on it.
1096de3910ebSMauro Carvalho Chehab 	 */
1097956b9ba1SJoe Perches 	edac_dbg(1, "Releasing device %s\n", dev_name(dev));
1098de3910ebSMauro Carvalho Chehab 	kfree(dev);
10997a623c03SMauro Carvalho Chehab }
11007a623c03SMauro Carvalho Chehab 
11017a623c03SMauro Carvalho Chehab static struct device_type mc_attr_type = {
11027a623c03SMauro Carvalho Chehab 	.release	= mc_attr_release,
11037a623c03SMauro Carvalho Chehab };
11047a623c03SMauro Carvalho Chehab /*
11057a623c03SMauro Carvalho Chehab  * Init/exit code for the module. Basically, creates/removes /sys/class/rc
11067a623c03SMauro Carvalho Chehab  */
11077a623c03SMauro Carvalho Chehab int __init edac_mc_sysfs_init(void)
11087a623c03SMauro Carvalho Chehab {
11097a623c03SMauro Carvalho Chehab 	struct bus_type *edac_subsys;
11107a623c03SMauro Carvalho Chehab 	int err;
11118096cfafSDoug Thompson 
1112fe5ff8b8SKay Sievers 	/* get the /sys/devices/system/edac subsys reference */
1113fe5ff8b8SKay Sievers 	edac_subsys = edac_get_sysfs_subsys();
1114fe5ff8b8SKay Sievers 	if (edac_subsys == NULL) {
1115956b9ba1SJoe Perches 		edac_dbg(1, "no edac_subsys\n");
11167a623c03SMauro Carvalho Chehab 		return -EINVAL;
11178096cfafSDoug Thompson 	}
11188096cfafSDoug Thompson 
1119de3910ebSMauro Carvalho Chehab 	mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
11208096cfafSDoug Thompson 
1121de3910ebSMauro Carvalho Chehab 	mci_pdev->bus = edac_subsys;
1122de3910ebSMauro Carvalho Chehab 	mci_pdev->type = &mc_attr_type;
1123de3910ebSMauro Carvalho Chehab 	device_initialize(mci_pdev);
1124de3910ebSMauro Carvalho Chehab 	dev_set_name(mci_pdev, "mc");
1125de3910ebSMauro Carvalho Chehab 
1126de3910ebSMauro Carvalho Chehab 	err = device_add(mci_pdev);
11277a623c03SMauro Carvalho Chehab 	if (err < 0)
11287a623c03SMauro Carvalho Chehab 		return err;
11298096cfafSDoug Thompson 
1130956b9ba1SJoe Perches 	edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
1131de3910ebSMauro Carvalho Chehab 
11328096cfafSDoug Thompson 	return 0;
11338096cfafSDoug Thompson }
11348096cfafSDoug Thompson 
11357a623c03SMauro Carvalho Chehab void __exit edac_mc_sysfs_exit(void)
11368096cfafSDoug Thompson {
1137de3910ebSMauro Carvalho Chehab 	put_device(mci_pdev);
1138de3910ebSMauro Carvalho Chehab 	device_del(mci_pdev);
1139fe5ff8b8SKay Sievers 	edac_put_sysfs_subsys();
11408096cfafSDoug Thompson }
1141