xref: /openbmc/linux/drivers/edac/edac_mc_sysfs.c (revision 8b7719e0)
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  *
10e7100478SMauro Carvalho Chehab  * (c) 2012-2013 - 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  */
908b7719e0SBorislav Petkov static const char * const 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 
1108b7719e0SBorislav Petkov static const char * const 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 
1218b7719e0SBorislav Petkov static const char * const 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) \
146fbe2d361SStephen Hemminger 	static 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;
376921a6899SBorislav Petkov 	csrow->mci = mci;
3777a623c03SMauro Carvalho Chehab 	dev_set_name(&csrow->dev, "csrow%d", index);
3787a623c03SMauro Carvalho Chehab 	dev_set_drvdata(&csrow->dev, csrow);
3797c9281d7SDouglas Thompson 
380956b9ba1SJoe Perches 	edac_dbg(0, "creating (virtual) csrow node %s\n",
381956b9ba1SJoe Perches 		 dev_name(&csrow->dev));
3828096cfafSDoug Thompson 
3837a623c03SMauro Carvalho Chehab 	err = device_add(&csrow->dev);
3847a623c03SMauro Carvalho Chehab 	if (err < 0)
3857a623c03SMauro Carvalho Chehab 		return err;
3868096cfafSDoug Thompson 
3877c9281d7SDouglas Thompson 	for (chan = 0; chan < csrow->nr_channels; chan++) {
388e39f4ea9SMauro Carvalho Chehab 		/* Only expose populated DIMMs */
389de3910ebSMauro Carvalho Chehab 		if (!csrow->channels[chan]->dimm->nr_pages)
390e39f4ea9SMauro Carvalho Chehab 			continue;
3917a623c03SMauro Carvalho Chehab 		err = device_create_file(&csrow->dev,
3927a623c03SMauro Carvalho Chehab 					 dynamic_csrow_dimm_attr[chan]);
3937a623c03SMauro Carvalho Chehab 		if (err < 0)
3947a623c03SMauro Carvalho Chehab 			goto error;
3957a623c03SMauro Carvalho Chehab 		err = device_create_file(&csrow->dev,
3967a623c03SMauro Carvalho Chehab 					 dynamic_csrow_ce_count_attr[chan]);
3977a623c03SMauro Carvalho Chehab 		if (err < 0) {
3987a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
3997a623c03SMauro Carvalho Chehab 					   dynamic_csrow_dimm_attr[chan]);
4007a623c03SMauro Carvalho Chehab 			goto error;
4017c9281d7SDouglas Thompson 		}
4027c9281d7SDouglas Thompson 	}
4037a623c03SMauro Carvalho Chehab 
4048096cfafSDoug Thompson 	return 0;
4058096cfafSDoug Thompson 
4067a623c03SMauro Carvalho Chehab error:
4077a623c03SMauro Carvalho Chehab 	for (--chan; chan >= 0; chan--) {
4087a623c03SMauro Carvalho Chehab 		device_remove_file(&csrow->dev,
4097a623c03SMauro Carvalho Chehab 					dynamic_csrow_dimm_attr[chan]);
4107a623c03SMauro Carvalho Chehab 		device_remove_file(&csrow->dev,
4117a623c03SMauro Carvalho Chehab 					   dynamic_csrow_ce_count_attr[chan]);
4127a623c03SMauro Carvalho Chehab 	}
4137a623c03SMauro Carvalho Chehab 	put_device(&csrow->dev);
4148096cfafSDoug Thompson 
4157c9281d7SDouglas Thompson 	return err;
4167c9281d7SDouglas Thompson }
4177c9281d7SDouglas Thompson 
4187a623c03SMauro Carvalho Chehab /* Create a CSROW object under specifed edac_mc_device */
4197a623c03SMauro Carvalho Chehab static int edac_create_csrow_objects(struct mem_ctl_info *mci)
4207a623c03SMauro Carvalho Chehab {
4217a623c03SMauro Carvalho Chehab 	int err, i, chan;
4227a623c03SMauro Carvalho Chehab 	struct csrow_info *csrow;
4237c9281d7SDouglas Thompson 
4247a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->nr_csrows; i++) {
425de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
426e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
427e39f4ea9SMauro Carvalho Chehab 			continue;
428de3910ebSMauro Carvalho Chehab 		err = edac_create_csrow_object(mci, mci->csrows[i], i);
4293d958823SMauro Carvalho Chehab 		if (err < 0) {
4303d958823SMauro Carvalho Chehab 			edac_dbg(1,
4313d958823SMauro Carvalho Chehab 				 "failure: create csrow objects for csrow %d\n",
4323d958823SMauro Carvalho Chehab 				 i);
4337a623c03SMauro Carvalho Chehab 			goto error;
4347a623c03SMauro Carvalho Chehab 		}
4353d958823SMauro Carvalho Chehab 	}
4367a623c03SMauro Carvalho Chehab 	return 0;
4377a623c03SMauro Carvalho Chehab 
4387a623c03SMauro Carvalho Chehab error:
4397a623c03SMauro Carvalho Chehab 	for (--i; i >= 0; i--) {
440de3910ebSMauro Carvalho Chehab 		csrow = mci->csrows[i];
441e39f4ea9SMauro Carvalho Chehab 		if (!nr_pages_per_csrow(csrow))
442e39f4ea9SMauro Carvalho Chehab 			continue;
4437a623c03SMauro Carvalho Chehab 		for (chan = csrow->nr_channels - 1; chan >= 0; chan--) {
444de3910ebSMauro Carvalho Chehab 			if (!csrow->channels[chan]->dimm->nr_pages)
445e39f4ea9SMauro Carvalho Chehab 				continue;
4467a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
4477a623c03SMauro Carvalho Chehab 						dynamic_csrow_dimm_attr[chan]);
4487a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
4497a623c03SMauro Carvalho Chehab 						dynamic_csrow_ce_count_attr[chan]);
4507a623c03SMauro Carvalho Chehab 		}
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 {
4597a623c03SMauro Carvalho Chehab 	int i, chan;
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;
4667a623c03SMauro Carvalho Chehab 		for (chan = csrow->nr_channels - 1; chan >= 0; chan--) {
467de3910ebSMauro Carvalho Chehab 			if (!csrow->channels[chan]->dimm->nr_pages)
468e39f4ea9SMauro Carvalho Chehab 				continue;
469956b9ba1SJoe Perches 			edac_dbg(1, "Removing csrow %d channel %d sysfs nodes\n",
4707a623c03SMauro Carvalho Chehab 				 i, chan);
4717a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
4727a623c03SMauro Carvalho Chehab 						dynamic_csrow_dimm_attr[chan]);
4737a623c03SMauro Carvalho Chehab 			device_remove_file(&csrow->dev,
4747a623c03SMauro Carvalho Chehab 						dynamic_csrow_ce_count_attr[chan]);
4757a623c03SMauro Carvalho Chehab 		}
47644d22e24SLans Zhang 		device_unregister(&mci->csrows[i]->dev);
4777a623c03SMauro Carvalho Chehab 	}
4787a623c03SMauro Carvalho Chehab }
47919974710SMauro Carvalho Chehab #endif
48019974710SMauro Carvalho Chehab 
48119974710SMauro Carvalho Chehab /*
48219974710SMauro Carvalho Chehab  * Per-dimm (or per-rank) devices
48319974710SMauro Carvalho Chehab  */
48419974710SMauro Carvalho Chehab 
48519974710SMauro Carvalho Chehab #define to_dimm(k) container_of(k, struct dimm_info, dev)
48619974710SMauro Carvalho Chehab 
48719974710SMauro Carvalho Chehab /* show/store functions for DIMM Label attributes */
48819974710SMauro Carvalho Chehab static ssize_t dimmdev_location_show(struct device *dev,
48919974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
49019974710SMauro Carvalho Chehab {
49119974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
49219974710SMauro Carvalho Chehab 
4936e84d359SMauro Carvalho Chehab 	return edac_dimm_info_location(dimm, data, PAGE_SIZE);
49419974710SMauro Carvalho Chehab }
49519974710SMauro Carvalho Chehab 
49619974710SMauro Carvalho Chehab static ssize_t dimmdev_label_show(struct device *dev,
49719974710SMauro Carvalho Chehab 				  struct device_attribute *mattr, char *data)
49819974710SMauro Carvalho Chehab {
49919974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
50019974710SMauro Carvalho Chehab 
50119974710SMauro Carvalho Chehab 	/* if field has not been initialized, there is nothing to send */
50219974710SMauro Carvalho Chehab 	if (!dimm->label[0])
50319974710SMauro Carvalho Chehab 		return 0;
50419974710SMauro Carvalho Chehab 
50519974710SMauro Carvalho Chehab 	return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label);
50619974710SMauro Carvalho Chehab }
50719974710SMauro Carvalho Chehab 
50819974710SMauro Carvalho Chehab static ssize_t dimmdev_label_store(struct device *dev,
50919974710SMauro Carvalho Chehab 				   struct device_attribute *mattr,
51019974710SMauro Carvalho Chehab 				   const char *data,
51119974710SMauro Carvalho Chehab 				   size_t count)
51219974710SMauro Carvalho Chehab {
51319974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
51419974710SMauro Carvalho Chehab 
51519974710SMauro Carvalho Chehab 	ssize_t max_size = 0;
51619974710SMauro Carvalho Chehab 
51719974710SMauro Carvalho Chehab 	max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
51819974710SMauro Carvalho Chehab 	strncpy(dimm->label, data, max_size);
51919974710SMauro Carvalho Chehab 	dimm->label[max_size] = '\0';
52019974710SMauro Carvalho Chehab 
52119974710SMauro Carvalho Chehab 	return max_size;
52219974710SMauro Carvalho Chehab }
52319974710SMauro Carvalho Chehab 
52419974710SMauro Carvalho Chehab static ssize_t dimmdev_size_show(struct device *dev,
52519974710SMauro Carvalho Chehab 				 struct device_attribute *mattr, char *data)
52619974710SMauro Carvalho Chehab {
52719974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
52819974710SMauro Carvalho Chehab 
52919974710SMauro Carvalho Chehab 	return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
53019974710SMauro Carvalho Chehab }
53119974710SMauro Carvalho Chehab 
53219974710SMauro Carvalho Chehab static ssize_t dimmdev_mem_type_show(struct device *dev,
53319974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
53419974710SMauro Carvalho Chehab {
53519974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
53619974710SMauro Carvalho Chehab 
53719974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", mem_types[dimm->mtype]);
53819974710SMauro Carvalho Chehab }
53919974710SMauro Carvalho Chehab 
54019974710SMauro Carvalho Chehab static ssize_t dimmdev_dev_type_show(struct device *dev,
54119974710SMauro Carvalho Chehab 				     struct device_attribute *mattr, char *data)
54219974710SMauro Carvalho Chehab {
54319974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
54419974710SMauro Carvalho Chehab 
54519974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", dev_types[dimm->dtype]);
54619974710SMauro Carvalho Chehab }
54719974710SMauro Carvalho Chehab 
54819974710SMauro Carvalho Chehab static ssize_t dimmdev_edac_mode_show(struct device *dev,
54919974710SMauro Carvalho Chehab 				      struct device_attribute *mattr,
55019974710SMauro Carvalho Chehab 				      char *data)
55119974710SMauro Carvalho Chehab {
55219974710SMauro Carvalho Chehab 	struct dimm_info *dimm = to_dimm(dev);
55319974710SMauro Carvalho Chehab 
55419974710SMauro Carvalho Chehab 	return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
55519974710SMauro Carvalho Chehab }
55619974710SMauro Carvalho Chehab 
55719974710SMauro Carvalho Chehab /* dimm/rank attribute files */
55819974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
55919974710SMauro Carvalho Chehab 		   dimmdev_label_show, dimmdev_label_store);
56019974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
56119974710SMauro Carvalho Chehab static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
56219974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
56319974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
56419974710SMauro Carvalho Chehab static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
56519974710SMauro Carvalho Chehab 
56619974710SMauro Carvalho Chehab /* attributes of the dimm<id>/rank<id> object */
56719974710SMauro Carvalho Chehab static struct attribute *dimm_attrs[] = {
56819974710SMauro Carvalho Chehab 	&dev_attr_dimm_label.attr,
56919974710SMauro Carvalho Chehab 	&dev_attr_dimm_location.attr,
57019974710SMauro Carvalho Chehab 	&dev_attr_size.attr,
57119974710SMauro Carvalho Chehab 	&dev_attr_dimm_mem_type.attr,
57219974710SMauro Carvalho Chehab 	&dev_attr_dimm_dev_type.attr,
57319974710SMauro Carvalho Chehab 	&dev_attr_dimm_edac_mode.attr,
57419974710SMauro Carvalho Chehab 	NULL,
57519974710SMauro Carvalho Chehab };
57619974710SMauro Carvalho Chehab 
57719974710SMauro Carvalho Chehab static struct attribute_group dimm_attr_grp = {
57819974710SMauro Carvalho Chehab 	.attrs	= dimm_attrs,
57919974710SMauro Carvalho Chehab };
58019974710SMauro Carvalho Chehab 
58119974710SMauro Carvalho Chehab static const struct attribute_group *dimm_attr_groups[] = {
58219974710SMauro Carvalho Chehab 	&dimm_attr_grp,
58319974710SMauro Carvalho Chehab 	NULL
58419974710SMauro Carvalho Chehab };
58519974710SMauro Carvalho Chehab 
586de3910ebSMauro Carvalho Chehab static void dimm_attr_release(struct device *dev)
58719974710SMauro Carvalho Chehab {
588de3910ebSMauro Carvalho Chehab 	struct dimm_info *dimm = container_of(dev, struct dimm_info, dev);
589de3910ebSMauro Carvalho Chehab 
590956b9ba1SJoe Perches 	edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev));
591de3910ebSMauro Carvalho Chehab 	kfree(dimm);
59219974710SMauro Carvalho Chehab }
59319974710SMauro Carvalho Chehab 
59419974710SMauro Carvalho Chehab static struct device_type dimm_attr_type = {
59519974710SMauro Carvalho Chehab 	.groups		= dimm_attr_groups,
59619974710SMauro Carvalho Chehab 	.release	= dimm_attr_release,
59719974710SMauro Carvalho Chehab };
59819974710SMauro Carvalho Chehab 
59919974710SMauro Carvalho Chehab /* Create a DIMM object under specifed memory controller device */
60019974710SMauro Carvalho Chehab static int edac_create_dimm_object(struct mem_ctl_info *mci,
60119974710SMauro Carvalho Chehab 				   struct dimm_info *dimm,
60219974710SMauro Carvalho Chehab 				   int index)
60319974710SMauro Carvalho Chehab {
60419974710SMauro Carvalho Chehab 	int err;
60519974710SMauro Carvalho Chehab 	dimm->mci = mci;
60619974710SMauro Carvalho Chehab 
60719974710SMauro Carvalho Chehab 	dimm->dev.type = &dimm_attr_type;
60819974710SMauro Carvalho Chehab 	dimm->dev.bus = &mci->bus;
60919974710SMauro Carvalho Chehab 	device_initialize(&dimm->dev);
61019974710SMauro Carvalho Chehab 
61119974710SMauro Carvalho Chehab 	dimm->dev.parent = &mci->dev;
6129713faecSMauro Carvalho Chehab 	if (mci->csbased)
61319974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "rank%d", index);
61419974710SMauro Carvalho Chehab 	else
61519974710SMauro Carvalho Chehab 		dev_set_name(&dimm->dev, "dimm%d", index);
61619974710SMauro Carvalho Chehab 	dev_set_drvdata(&dimm->dev, dimm);
61719974710SMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
61819974710SMauro Carvalho Chehab 
61919974710SMauro Carvalho Chehab 	err =  device_add(&dimm->dev);
62019974710SMauro Carvalho Chehab 
621956b9ba1SJoe Perches 	edac_dbg(0, "creating rank/dimm device %s\n", dev_name(&dimm->dev));
62219974710SMauro Carvalho Chehab 
62319974710SMauro Carvalho Chehab 	return err;
62419974710SMauro Carvalho Chehab }
6257a623c03SMauro Carvalho Chehab 
6267a623c03SMauro Carvalho Chehab /*
6277a623c03SMauro Carvalho Chehab  * Memory controller device
6287a623c03SMauro Carvalho Chehab  */
6297a623c03SMauro Carvalho Chehab 
6307a623c03SMauro Carvalho Chehab #define to_mci(k) container_of(k, struct mem_ctl_info, dev)
6317a623c03SMauro Carvalho Chehab 
6327a623c03SMauro Carvalho Chehab static ssize_t mci_reset_counters_store(struct device *dev,
6337a623c03SMauro Carvalho Chehab 					struct device_attribute *mattr,
6347c9281d7SDouglas Thompson 					const char *data, size_t count)
6357c9281d7SDouglas Thompson {
6367a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
6377a623c03SMauro Carvalho Chehab 	int cnt, row, chan, i;
6385926ff50SMauro Carvalho Chehab 	mci->ue_mc = 0;
6395926ff50SMauro Carvalho Chehab 	mci->ce_mc = 0;
6407a623c03SMauro Carvalho Chehab 	mci->ue_noinfo_count = 0;
6417a623c03SMauro Carvalho Chehab 	mci->ce_noinfo_count = 0;
6427c9281d7SDouglas Thompson 
6437c9281d7SDouglas Thompson 	for (row = 0; row < mci->nr_csrows; row++) {
644de3910ebSMauro Carvalho Chehab 		struct csrow_info *ri = mci->csrows[row];
6457c9281d7SDouglas Thompson 
6467c9281d7SDouglas Thompson 		ri->ue_count = 0;
6477c9281d7SDouglas Thompson 		ri->ce_count = 0;
6487c9281d7SDouglas Thompson 
6497c9281d7SDouglas Thompson 		for (chan = 0; chan < ri->nr_channels; chan++)
650de3910ebSMauro Carvalho Chehab 			ri->channels[chan]->ce_count = 0;
6517c9281d7SDouglas Thompson 	}
6527c9281d7SDouglas Thompson 
6537a623c03SMauro Carvalho Chehab 	cnt = 1;
6547a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
6557a623c03SMauro Carvalho Chehab 		cnt *= mci->layers[i].size;
6567a623c03SMauro Carvalho Chehab 		memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32));
6577a623c03SMauro Carvalho Chehab 		memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32));
6587a623c03SMauro Carvalho Chehab 	}
6597a623c03SMauro Carvalho Chehab 
6607c9281d7SDouglas Thompson 	mci->start_time = jiffies;
6617c9281d7SDouglas Thompson 	return count;
6627c9281d7SDouglas Thompson }
6637c9281d7SDouglas Thompson 
66439094443SBorislav Petkov /* Memory scrubbing interface:
66539094443SBorislav Petkov  *
66639094443SBorislav Petkov  * A MC driver can limit the scrubbing bandwidth based on the CPU type.
66739094443SBorislav Petkov  * Therefore, ->set_sdram_scrub_rate should be made to return the actual
66839094443SBorislav Petkov  * bandwidth that is accepted or 0 when scrubbing is to be disabled.
66939094443SBorislav Petkov  *
67039094443SBorislav Petkov  * Negative value still means that an error has occurred while setting
67139094443SBorislav Petkov  * the scrub rate.
67239094443SBorislav Petkov  */
6737a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_store(struct device *dev,
6747a623c03SMauro Carvalho Chehab 					  struct device_attribute *mattr,
6757c9281d7SDouglas Thompson 					  const char *data, size_t count)
6767c9281d7SDouglas Thompson {
6777a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
678eba042a8SBorislav Petkov 	unsigned long bandwidth = 0;
67939094443SBorislav Petkov 	int new_bw = 0;
6807c9281d7SDouglas Thompson 
681eba042a8SBorislav Petkov 	if (strict_strtoul(data, 10, &bandwidth) < 0)
682eba042a8SBorislav Petkov 		return -EINVAL;
683eba042a8SBorislav Petkov 
68439094443SBorislav Petkov 	new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
6854949603aSMarkus Trippelsdorf 	if (new_bw < 0) {
6864949603aSMarkus Trippelsdorf 		edac_printk(KERN_WARNING, EDAC_MC,
6874949603aSMarkus Trippelsdorf 			    "Error setting scrub rate to: %lu\n", bandwidth);
6884949603aSMarkus Trippelsdorf 		return -EINVAL;
6897c9281d7SDouglas Thompson 	}
6907c9281d7SDouglas Thompson 
6914949603aSMarkus Trippelsdorf 	return count;
6927c9281d7SDouglas Thompson }
693eba042a8SBorislav Petkov 
69439094443SBorislav Petkov /*
69539094443SBorislav Petkov  * ->get_sdram_scrub_rate() return value semantics same as above.
69639094443SBorislav Petkov  */
6977a623c03SMauro Carvalho Chehab static ssize_t mci_sdram_scrub_rate_show(struct device *dev,
6987a623c03SMauro Carvalho Chehab 					 struct device_attribute *mattr,
6997a623c03SMauro Carvalho Chehab 					 char *data)
70039094443SBorislav Petkov {
7017a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
70239094443SBorislav Petkov 	int bandwidth = 0;
70339094443SBorislav Petkov 
70439094443SBorislav Petkov 	bandwidth = mci->get_sdram_scrub_rate(mci);
70539094443SBorislav Petkov 	if (bandwidth < 0) {
706eba042a8SBorislav Petkov 		edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n");
70739094443SBorislav Petkov 		return bandwidth;
708eba042a8SBorislav Petkov 	}
70939094443SBorislav Petkov 
7107c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", bandwidth);
7117c9281d7SDouglas Thompson }
7127c9281d7SDouglas Thompson 
7137c9281d7SDouglas Thompson /* default attribute files for the MCI object */
7147a623c03SMauro Carvalho Chehab static ssize_t mci_ue_count_show(struct device *dev,
7157a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7167a623c03SMauro Carvalho Chehab 				 char *data)
7177c9281d7SDouglas Thompson {
7187a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7197a623c03SMauro Carvalho Chehab 
7205926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ue_mc);
7217c9281d7SDouglas Thompson }
7227c9281d7SDouglas Thompson 
7237a623c03SMauro Carvalho Chehab static ssize_t mci_ce_count_show(struct device *dev,
7247a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7257a623c03SMauro Carvalho Chehab 				 char *data)
7267c9281d7SDouglas Thompson {
7277a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7287a623c03SMauro Carvalho Chehab 
7295926ff50SMauro Carvalho Chehab 	return sprintf(data, "%d\n", mci->ce_mc);
7307c9281d7SDouglas Thompson }
7317c9281d7SDouglas Thompson 
7327a623c03SMauro Carvalho Chehab static ssize_t mci_ce_noinfo_show(struct device *dev,
7337a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7347a623c03SMauro Carvalho Chehab 				  char *data)
7357c9281d7SDouglas Thompson {
7367a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7377a623c03SMauro Carvalho Chehab 
7387c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ce_noinfo_count);
7397c9281d7SDouglas Thompson }
7407c9281d7SDouglas Thompson 
7417a623c03SMauro Carvalho Chehab static ssize_t mci_ue_noinfo_show(struct device *dev,
7427a623c03SMauro Carvalho Chehab 				  struct device_attribute *mattr,
7437a623c03SMauro Carvalho Chehab 				  char *data)
7447c9281d7SDouglas Thompson {
7457a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7467a623c03SMauro Carvalho Chehab 
7477c9281d7SDouglas Thompson 	return sprintf(data, "%d\n", mci->ue_noinfo_count);
7487c9281d7SDouglas Thompson }
7497c9281d7SDouglas Thompson 
7507a623c03SMauro Carvalho Chehab static ssize_t mci_seconds_show(struct device *dev,
7517a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
7527a623c03SMauro Carvalho Chehab 				char *data)
7537c9281d7SDouglas Thompson {
7547a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7557a623c03SMauro Carvalho Chehab 
7567c9281d7SDouglas Thompson 	return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ);
7577c9281d7SDouglas Thompson }
7587c9281d7SDouglas Thompson 
7597a623c03SMauro Carvalho Chehab static ssize_t mci_ctl_name_show(struct device *dev,
7607a623c03SMauro Carvalho Chehab 				 struct device_attribute *mattr,
7617a623c03SMauro Carvalho Chehab 				 char *data)
7627c9281d7SDouglas Thompson {
7637a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7647a623c03SMauro Carvalho Chehab 
7657c9281d7SDouglas Thompson 	return sprintf(data, "%s\n", mci->ctl_name);
7667c9281d7SDouglas Thompson }
7677c9281d7SDouglas Thompson 
7687a623c03SMauro Carvalho Chehab static ssize_t mci_size_mb_show(struct device *dev,
7697a623c03SMauro Carvalho Chehab 				struct device_attribute *mattr,
7707a623c03SMauro Carvalho Chehab 				char *data)
7717c9281d7SDouglas Thompson {
7727a623c03SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
773a895bf8bSMauro Carvalho Chehab 	int total_pages = 0, csrow_idx, j;
7747c9281d7SDouglas Thompson 
775a895bf8bSMauro Carvalho Chehab 	for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) {
776de3910ebSMauro Carvalho Chehab 		struct csrow_info *csrow = mci->csrows[csrow_idx];
7777c9281d7SDouglas Thompson 
778a895bf8bSMauro Carvalho Chehab 		for (j = 0; j < csrow->nr_channels; j++) {
779de3910ebSMauro Carvalho Chehab 			struct dimm_info *dimm = csrow->channels[j]->dimm;
7807c9281d7SDouglas Thompson 
781a895bf8bSMauro Carvalho Chehab 			total_pages += dimm->nr_pages;
782a895bf8bSMauro Carvalho Chehab 		}
7837c9281d7SDouglas Thompson 	}
7847c9281d7SDouglas Thompson 
7857c9281d7SDouglas Thompson 	return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages));
7867c9281d7SDouglas Thompson }
7877c9281d7SDouglas Thompson 
7888ad6c78aSMauro Carvalho Chehab static ssize_t mci_max_location_show(struct device *dev,
7898ad6c78aSMauro Carvalho Chehab 				     struct device_attribute *mattr,
7908ad6c78aSMauro Carvalho Chehab 				     char *data)
7918ad6c78aSMauro Carvalho Chehab {
7928ad6c78aSMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
7938ad6c78aSMauro Carvalho Chehab 	int i;
7948ad6c78aSMauro Carvalho Chehab 	char *p = data;
7958ad6c78aSMauro Carvalho Chehab 
7968ad6c78aSMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
7978ad6c78aSMauro Carvalho Chehab 		p += sprintf(p, "%s %d ",
7988ad6c78aSMauro Carvalho Chehab 			     edac_layer_name[mci->layers[i].type],
7998ad6c78aSMauro Carvalho Chehab 			     mci->layers[i].size - 1);
8008ad6c78aSMauro Carvalho Chehab 	}
8018ad6c78aSMauro Carvalho Chehab 
8028ad6c78aSMauro Carvalho Chehab 	return p - data;
8038ad6c78aSMauro Carvalho Chehab }
8048ad6c78aSMauro Carvalho Chehab 
805452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
806452a6bf9SMauro Carvalho Chehab static ssize_t edac_fake_inject_write(struct file *file,
807452a6bf9SMauro Carvalho Chehab 				      const char __user *data,
808452a6bf9SMauro Carvalho Chehab 				      size_t count, loff_t *ppos)
809452a6bf9SMauro Carvalho Chehab {
810452a6bf9SMauro Carvalho Chehab 	struct device *dev = file->private_data;
811452a6bf9SMauro Carvalho Chehab 	struct mem_ctl_info *mci = to_mci(dev);
812452a6bf9SMauro Carvalho Chehab 	static enum hw_event_mc_err_type type;
81338ced28bSMauro Carvalho Chehab 	u16 errcount = mci->fake_inject_count;
81438ced28bSMauro Carvalho Chehab 
81538ced28bSMauro Carvalho Chehab 	if (!errcount)
81638ced28bSMauro Carvalho Chehab 		errcount = 1;
817452a6bf9SMauro Carvalho Chehab 
818452a6bf9SMauro Carvalho Chehab 	type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
819452a6bf9SMauro Carvalho Chehab 				   : HW_EVENT_ERR_CORRECTED;
820452a6bf9SMauro Carvalho Chehab 
821452a6bf9SMauro Carvalho Chehab 	printk(KERN_DEBUG
82238ced28bSMauro Carvalho Chehab 	       "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
82338ced28bSMauro Carvalho Chehab 		errcount,
824452a6bf9SMauro Carvalho Chehab 		(type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
82538ced28bSMauro Carvalho Chehab 		errcount > 1 ? "s" : "",
826452a6bf9SMauro Carvalho Chehab 		mci->fake_inject_layer[0],
827452a6bf9SMauro Carvalho Chehab 		mci->fake_inject_layer[1],
828452a6bf9SMauro Carvalho Chehab 		mci->fake_inject_layer[2]
829452a6bf9SMauro Carvalho Chehab 	       );
83038ced28bSMauro Carvalho Chehab 	edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
831452a6bf9SMauro Carvalho Chehab 			     mci->fake_inject_layer[0],
832452a6bf9SMauro Carvalho Chehab 			     mci->fake_inject_layer[1],
833452a6bf9SMauro Carvalho Chehab 			     mci->fake_inject_layer[2],
83403f7eae8SMauro Carvalho Chehab 			     "FAKE ERROR", "for EDAC testing only");
835452a6bf9SMauro Carvalho Chehab 
836452a6bf9SMauro Carvalho Chehab 	return count;
837452a6bf9SMauro Carvalho Chehab }
838452a6bf9SMauro Carvalho Chehab 
839452a6bf9SMauro Carvalho Chehab static const struct file_operations debug_fake_inject_fops = {
840db7312a2SWei Yongjun 	.open = simple_open,
841452a6bf9SMauro Carvalho Chehab 	.write = edac_fake_inject_write,
842452a6bf9SMauro Carvalho Chehab 	.llseek = generic_file_llseek,
843452a6bf9SMauro Carvalho Chehab };
844452a6bf9SMauro Carvalho Chehab #endif
845452a6bf9SMauro Carvalho Chehab 
8467c9281d7SDouglas Thompson /* default Control file */
8477a623c03SMauro Carvalho Chehab DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store);
8487c9281d7SDouglas Thompson 
8497c9281d7SDouglas Thompson /* default Attribute files */
8507a623c03SMauro Carvalho Chehab DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL);
8517a623c03SMauro Carvalho Chehab DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL);
8527a623c03SMauro Carvalho Chehab DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL);
8537a623c03SMauro Carvalho Chehab DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL);
8547a623c03SMauro Carvalho Chehab DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL);
8557a623c03SMauro Carvalho Chehab DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL);
8567a623c03SMauro Carvalho Chehab DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL);
8578ad6c78aSMauro Carvalho Chehab DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL);
8587c9281d7SDouglas Thompson 
8597c9281d7SDouglas Thompson /* memory scrubber attribute file */
860e7100478SMauro Carvalho Chehab DEVICE_ATTR(sdram_scrub_rate, 0, NULL, NULL);
8617c9281d7SDouglas Thompson 
8627a623c03SMauro Carvalho Chehab static struct attribute *mci_attrs[] = {
8637a623c03SMauro Carvalho Chehab 	&dev_attr_reset_counters.attr,
8647a623c03SMauro Carvalho Chehab 	&dev_attr_mc_name.attr,
8657a623c03SMauro Carvalho Chehab 	&dev_attr_size_mb.attr,
8667a623c03SMauro Carvalho Chehab 	&dev_attr_seconds_since_reset.attr,
8677a623c03SMauro Carvalho Chehab 	&dev_attr_ue_noinfo_count.attr,
8687a623c03SMauro Carvalho Chehab 	&dev_attr_ce_noinfo_count.attr,
8697a623c03SMauro Carvalho Chehab 	&dev_attr_ue_count.attr,
8707a623c03SMauro Carvalho Chehab 	&dev_attr_ce_count.attr,
8718ad6c78aSMauro Carvalho Chehab 	&dev_attr_max_location.attr,
8727c9281d7SDouglas Thompson 	NULL
8737c9281d7SDouglas Thompson };
8747c9281d7SDouglas Thompson 
8757a623c03SMauro Carvalho Chehab static struct attribute_group mci_attr_grp = {
8767a623c03SMauro Carvalho Chehab 	.attrs	= mci_attrs,
8777c9281d7SDouglas Thompson };
8787c9281d7SDouglas Thompson 
8797a623c03SMauro Carvalho Chehab static const struct attribute_group *mci_attr_groups[] = {
8807a623c03SMauro Carvalho Chehab 	&mci_attr_grp,
8817a623c03SMauro Carvalho Chehab 	NULL
882cc301b3aSMauro Carvalho Chehab };
883cc301b3aSMauro Carvalho Chehab 
884de3910ebSMauro Carvalho Chehab static void mci_attr_release(struct device *dev)
8857a623c03SMauro Carvalho Chehab {
886de3910ebSMauro Carvalho Chehab 	struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev);
887de3910ebSMauro Carvalho Chehab 
888956b9ba1SJoe Perches 	edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev));
889de3910ebSMauro Carvalho Chehab 	kfree(mci);
8907a623c03SMauro Carvalho Chehab }
8917a623c03SMauro Carvalho Chehab 
8927a623c03SMauro Carvalho Chehab static struct device_type mci_attr_type = {
8937a623c03SMauro Carvalho Chehab 	.groups		= mci_attr_groups,
8947a623c03SMauro Carvalho Chehab 	.release	= mci_attr_release,
895cc301b3aSMauro Carvalho Chehab };
896cc301b3aSMauro Carvalho Chehab 
897452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
898e7930ba4SRob Herring static struct dentry *edac_debugfs;
899e7930ba4SRob Herring 
900e7930ba4SRob Herring int __init edac_debugfs_init(void)
901e7930ba4SRob Herring {
902e7930ba4SRob Herring 	edac_debugfs = debugfs_create_dir("edac", NULL);
903e7930ba4SRob Herring 	if (IS_ERR(edac_debugfs)) {
904e7930ba4SRob Herring 		edac_debugfs = NULL;
905e7930ba4SRob Herring 		return -ENOMEM;
906e7930ba4SRob Herring 	}
907e7930ba4SRob Herring 	return 0;
908e7930ba4SRob Herring }
909e7930ba4SRob Herring 
910e7930ba4SRob Herring void __exit edac_debugfs_exit(void)
911e7930ba4SRob Herring {
912e7930ba4SRob Herring 	debugfs_remove(edac_debugfs);
913e7930ba4SRob Herring }
914e7930ba4SRob Herring 
915452a6bf9SMauro Carvalho Chehab int edac_create_debug_nodes(struct mem_ctl_info *mci)
916452a6bf9SMauro Carvalho Chehab {
917452a6bf9SMauro Carvalho Chehab 	struct dentry *d, *parent;
918452a6bf9SMauro Carvalho Chehab 	char name[80];
919452a6bf9SMauro Carvalho Chehab 	int i;
920452a6bf9SMauro Carvalho Chehab 
921e7930ba4SRob Herring 	if (!edac_debugfs)
922e7930ba4SRob Herring 		return -ENODEV;
923e7930ba4SRob Herring 
924e7930ba4SRob Herring 	d = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
925452a6bf9SMauro Carvalho Chehab 	if (!d)
926452a6bf9SMauro Carvalho Chehab 		return -ENOMEM;
927452a6bf9SMauro Carvalho Chehab 	parent = d;
928452a6bf9SMauro Carvalho Chehab 
929452a6bf9SMauro Carvalho Chehab 	for (i = 0; i < mci->n_layers; i++) {
930452a6bf9SMauro Carvalho Chehab 		sprintf(name, "fake_inject_%s",
931452a6bf9SMauro Carvalho Chehab 			     edac_layer_name[mci->layers[i].type]);
932452a6bf9SMauro Carvalho Chehab 		d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
933452a6bf9SMauro Carvalho Chehab 				      &mci->fake_inject_layer[i]);
934452a6bf9SMauro Carvalho Chehab 		if (!d)
935452a6bf9SMauro Carvalho Chehab 			goto nomem;
936452a6bf9SMauro Carvalho Chehab 	}
937452a6bf9SMauro Carvalho Chehab 
938452a6bf9SMauro Carvalho Chehab 	d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
939452a6bf9SMauro Carvalho Chehab 				&mci->fake_inject_ue);
940452a6bf9SMauro Carvalho Chehab 	if (!d)
941452a6bf9SMauro Carvalho Chehab 		goto nomem;
942452a6bf9SMauro Carvalho Chehab 
94338ced28bSMauro Carvalho Chehab 	d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
94438ced28bSMauro Carvalho Chehab 				&mci->fake_inject_count);
94538ced28bSMauro Carvalho Chehab 	if (!d)
94638ced28bSMauro Carvalho Chehab 		goto nomem;
94738ced28bSMauro Carvalho Chehab 
948452a6bf9SMauro Carvalho Chehab 	d = debugfs_create_file("fake_inject", S_IWUSR, parent,
949452a6bf9SMauro Carvalho Chehab 				&mci->dev,
950452a6bf9SMauro Carvalho Chehab 				&debug_fake_inject_fops);
951452a6bf9SMauro Carvalho Chehab 	if (!d)
952452a6bf9SMauro Carvalho Chehab 		goto nomem;
953452a6bf9SMauro Carvalho Chehab 
954e7930ba4SRob Herring 	mci->debugfs = parent;
955452a6bf9SMauro Carvalho Chehab 	return 0;
956452a6bf9SMauro Carvalho Chehab nomem:
957452a6bf9SMauro Carvalho Chehab 	debugfs_remove(mci->debugfs);
958452a6bf9SMauro Carvalho Chehab 	return -ENOMEM;
959452a6bf9SMauro Carvalho Chehab }
960452a6bf9SMauro Carvalho Chehab #endif
961452a6bf9SMauro Carvalho Chehab 
9627c9281d7SDouglas Thompson /*
9637c9281d7SDouglas Thompson  * Create a new Memory Controller kobject instance,
9647c9281d7SDouglas Thompson  *	mc<id> under the 'mc' directory
9657c9281d7SDouglas Thompson  *
9667c9281d7SDouglas Thompson  * Return:
9677c9281d7SDouglas Thompson  *	0	Success
9687c9281d7SDouglas Thompson  *	!0	Failure
9697c9281d7SDouglas Thompson  */
9707c9281d7SDouglas Thompson int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
9717c9281d7SDouglas Thompson {
9727a623c03SMauro Carvalho Chehab 	int i, err;
9737c9281d7SDouglas Thompson 
9747a623c03SMauro Carvalho Chehab 	/*
9757a623c03SMauro Carvalho Chehab 	 * The memory controller needs its own bus, in order to avoid
9767a623c03SMauro Carvalho Chehab 	 * namespace conflicts at /sys/bus/edac.
97742a8e397SDouglas Thompson 	 */
978de3910ebSMauro Carvalho Chehab 	mci->bus.name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx);
979de3910ebSMauro Carvalho Chehab 	if (!mci->bus.name)
980de3910ebSMauro Carvalho Chehab 		return -ENOMEM;
981956b9ba1SJoe Perches 	edac_dbg(0, "creating bus %s\n", mci->bus.name);
9827a623c03SMauro Carvalho Chehab 	err = bus_register(&mci->bus);
9837a623c03SMauro Carvalho Chehab 	if (err < 0)
9847a623c03SMauro Carvalho Chehab 		return err;
9857a623c03SMauro Carvalho Chehab 
986de3910ebSMauro Carvalho Chehab 	/* get the /sys/devices/system/edac subsys reference */
987de3910ebSMauro Carvalho Chehab 	mci->dev.type = &mci_attr_type;
988de3910ebSMauro Carvalho Chehab 	device_initialize(&mci->dev);
989de3910ebSMauro Carvalho Chehab 
990de3910ebSMauro Carvalho Chehab 	mci->dev.parent = mci_pdev;
991de3910ebSMauro Carvalho Chehab 	mci->dev.bus = &mci->bus;
992de3910ebSMauro Carvalho Chehab 	dev_set_name(&mci->dev, "mc%d", mci->mc_idx);
993de3910ebSMauro Carvalho Chehab 	dev_set_drvdata(&mci->dev, mci);
994de3910ebSMauro Carvalho Chehab 	pm_runtime_forbid(&mci->dev);
995de3910ebSMauro Carvalho Chehab 
996956b9ba1SJoe Perches 	edac_dbg(0, "creating device %s\n", dev_name(&mci->dev));
9977a623c03SMauro Carvalho Chehab 	err = device_add(&mci->dev);
9987a623c03SMauro Carvalho Chehab 	if (err < 0) {
9993d958823SMauro Carvalho Chehab 		edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev));
10007a623c03SMauro Carvalho Chehab 		bus_unregister(&mci->bus);
10017a623c03SMauro Carvalho Chehab 		kfree(mci->bus.name);
10027a623c03SMauro Carvalho Chehab 		return err;
10038096cfafSDoug Thompson 	}
100442a8e397SDouglas Thompson 
1005e7100478SMauro Carvalho Chehab 	if (mci->set_sdram_scrub_rate || mci->get_sdram_scrub_rate) {
1006e7100478SMauro Carvalho Chehab 		if (mci->get_sdram_scrub_rate) {
1007e7100478SMauro Carvalho Chehab 			dev_attr_sdram_scrub_rate.attr.mode |= S_IRUGO;
1008e7100478SMauro Carvalho Chehab 			dev_attr_sdram_scrub_rate.show = &mci_sdram_scrub_rate_show;
1009e7100478SMauro Carvalho Chehab 		}
1010e7100478SMauro Carvalho Chehab 		if (mci->set_sdram_scrub_rate) {
1011e7100478SMauro Carvalho Chehab 			dev_attr_sdram_scrub_rate.attr.mode |= S_IWUSR;
1012e7100478SMauro Carvalho Chehab 			dev_attr_sdram_scrub_rate.store = &mci_sdram_scrub_rate_store;
1013e7100478SMauro Carvalho Chehab 		}
1014e7100478SMauro Carvalho Chehab 		err = device_create_file(&mci->dev,
1015e7100478SMauro Carvalho Chehab 					 &dev_attr_sdram_scrub_rate);
1016e7100478SMauro Carvalho Chehab 		if (err) {
1017e7100478SMauro Carvalho Chehab 			edac_dbg(1, "failure: create sdram_scrub_rate\n");
1018e7100478SMauro Carvalho Chehab 			goto fail2;
1019e7100478SMauro Carvalho Chehab 		}
1020e7100478SMauro Carvalho Chehab 	}
10217a623c03SMauro Carvalho Chehab 	/*
10227a623c03SMauro Carvalho Chehab 	 * Create the dimm/rank devices
10237c9281d7SDouglas Thompson 	 */
10247a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
1025de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
10267a623c03SMauro Carvalho Chehab 		/* Only expose populated DIMMs */
10277a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
10287a623c03SMauro Carvalho Chehab 			continue;
10297a623c03SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
1030956b9ba1SJoe Perches 		edac_dbg(1, "creating dimm%d, located at ", i);
10317a623c03SMauro Carvalho Chehab 		if (edac_debug_level >= 1) {
10327a623c03SMauro Carvalho Chehab 			int lay;
10337a623c03SMauro Carvalho Chehab 			for (lay = 0; lay < mci->n_layers; lay++)
10347a623c03SMauro Carvalho Chehab 				printk(KERN_CONT "%s %d ",
10357a623c03SMauro Carvalho Chehab 					edac_layer_name[mci->layers[lay].type],
10367a623c03SMauro Carvalho Chehab 					dimm->location[lay]);
10377a623c03SMauro Carvalho Chehab 			printk(KERN_CONT "\n");
10387c9281d7SDouglas Thompson 		}
10397a623c03SMauro Carvalho Chehab #endif
104019974710SMauro Carvalho Chehab 		err = edac_create_dimm_object(mci, dimm, i);
104119974710SMauro Carvalho Chehab 		if (err) {
1042956b9ba1SJoe Perches 			edac_dbg(1, "failure: create dimm %d obj\n", i);
104319974710SMauro Carvalho Chehab 			goto fail;
104419974710SMauro Carvalho Chehab 		}
10457c9281d7SDouglas Thompson 	}
10467a623c03SMauro Carvalho Chehab 
104719974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
10487a623c03SMauro Carvalho Chehab 	err = edac_create_csrow_objects(mci);
10497a623c03SMauro Carvalho Chehab 	if (err < 0)
10507a623c03SMauro Carvalho Chehab 		goto fail;
105119974710SMauro Carvalho Chehab #endif
10527c9281d7SDouglas Thompson 
1053452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
1054452a6bf9SMauro Carvalho Chehab 	edac_create_debug_nodes(mci);
1055452a6bf9SMauro Carvalho Chehab #endif
10567c9281d7SDouglas Thompson 	return 0;
10577c9281d7SDouglas Thompson 
10587a623c03SMauro Carvalho Chehab fail:
10597c9281d7SDouglas Thompson 	for (i--; i >= 0; i--) {
1060de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
10617a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
10627a623c03SMauro Carvalho Chehab 			continue;
106344d22e24SLans Zhang 		device_unregister(&dimm->dev);
10647c9281d7SDouglas Thompson 	}
1065e7100478SMauro Carvalho Chehab fail2:
106644d22e24SLans Zhang 	device_unregister(&mci->dev);
10677a623c03SMauro Carvalho Chehab 	bus_unregister(&mci->bus);
10687a623c03SMauro Carvalho Chehab 	kfree(mci->bus.name);
10697c9281d7SDouglas Thompson 	return err;
10707c9281d7SDouglas Thompson }
10717c9281d7SDouglas Thompson 
10727c9281d7SDouglas Thompson /*
10737c9281d7SDouglas Thompson  * remove a Memory Controller instance
10747c9281d7SDouglas Thompson  */
10757c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
10767c9281d7SDouglas Thompson {
10777a623c03SMauro Carvalho Chehab 	int i;
10787c9281d7SDouglas Thompson 
1079956b9ba1SJoe Perches 	edac_dbg(0, "\n");
10807c9281d7SDouglas Thompson 
1081452a6bf9SMauro Carvalho Chehab #ifdef CONFIG_EDAC_DEBUG
1082452a6bf9SMauro Carvalho Chehab 	debugfs_remove(mci->debugfs);
1083452a6bf9SMauro Carvalho Chehab #endif
108419974710SMauro Carvalho Chehab #ifdef CONFIG_EDAC_LEGACY_SYSFS
10857a623c03SMauro Carvalho Chehab 	edac_delete_csrow_objects(mci);
108619974710SMauro Carvalho Chehab #endif
1087a895bf8bSMauro Carvalho Chehab 
10887a623c03SMauro Carvalho Chehab 	for (i = 0; i < mci->tot_dimms; i++) {
1089de3910ebSMauro Carvalho Chehab 		struct dimm_info *dimm = mci->dimms[i];
10907a623c03SMauro Carvalho Chehab 		if (dimm->nr_pages == 0)
10917a623c03SMauro Carvalho Chehab 			continue;
1092956b9ba1SJoe Perches 		edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev));
109344d22e24SLans Zhang 		device_unregister(&dimm->dev);
10947c9281d7SDouglas Thompson 	}
10957c9281d7SDouglas Thompson }
10967c9281d7SDouglas Thompson 
10977a623c03SMauro Carvalho Chehab void edac_unregister_sysfs(struct mem_ctl_info *mci)
10988096cfafSDoug Thompson {
1099956b9ba1SJoe Perches 	edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
110044d22e24SLans Zhang 	device_unregister(&mci->dev);
11017a623c03SMauro Carvalho Chehab 	bus_unregister(&mci->bus);
11027a623c03SMauro Carvalho Chehab 	kfree(mci->bus.name);
11037a623c03SMauro Carvalho Chehab }
11048096cfafSDoug Thompson 
1105de3910ebSMauro Carvalho Chehab static void mc_attr_release(struct device *dev)
11067a623c03SMauro Carvalho Chehab {
1107de3910ebSMauro Carvalho Chehab 	/*
1108de3910ebSMauro Carvalho Chehab 	 * There's no container structure here, as this is just the mci
1109de3910ebSMauro Carvalho Chehab 	 * parent device, used to create the /sys/devices/mc sysfs node.
1110de3910ebSMauro Carvalho Chehab 	 * So, there are no attributes on it.
1111de3910ebSMauro Carvalho Chehab 	 */
1112956b9ba1SJoe Perches 	edac_dbg(1, "Releasing device %s\n", dev_name(dev));
1113de3910ebSMauro Carvalho Chehab 	kfree(dev);
11147a623c03SMauro Carvalho Chehab }
11157a623c03SMauro Carvalho Chehab 
11167a623c03SMauro Carvalho Chehab static struct device_type mc_attr_type = {
11177a623c03SMauro Carvalho Chehab 	.release	= mc_attr_release,
11187a623c03SMauro Carvalho Chehab };
11197a623c03SMauro Carvalho Chehab /*
11207a623c03SMauro Carvalho Chehab  * Init/exit code for the module. Basically, creates/removes /sys/class/rc
11217a623c03SMauro Carvalho Chehab  */
11227a623c03SMauro Carvalho Chehab int __init edac_mc_sysfs_init(void)
11237a623c03SMauro Carvalho Chehab {
11247a623c03SMauro Carvalho Chehab 	struct bus_type *edac_subsys;
11257a623c03SMauro Carvalho Chehab 	int err;
11268096cfafSDoug Thompson 
1127fe5ff8b8SKay Sievers 	/* get the /sys/devices/system/edac subsys reference */
1128fe5ff8b8SKay Sievers 	edac_subsys = edac_get_sysfs_subsys();
1129fe5ff8b8SKay Sievers 	if (edac_subsys == NULL) {
1130956b9ba1SJoe Perches 		edac_dbg(1, "no edac_subsys\n");
11312d56b109SDenis Kirjanov 		err = -EINVAL;
11322d56b109SDenis Kirjanov 		goto out;
11338096cfafSDoug Thompson 	}
11348096cfafSDoug Thompson 
1135de3910ebSMauro Carvalho Chehab 	mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
11362d56b109SDenis Kirjanov 	if (!mci_pdev) {
11372d56b109SDenis Kirjanov 		err = -ENOMEM;
11382d56b109SDenis Kirjanov 		goto out_put_sysfs;
11392d56b109SDenis Kirjanov 	}
11408096cfafSDoug Thompson 
1141de3910ebSMauro Carvalho Chehab 	mci_pdev->bus = edac_subsys;
1142de3910ebSMauro Carvalho Chehab 	mci_pdev->type = &mc_attr_type;
1143de3910ebSMauro Carvalho Chehab 	device_initialize(mci_pdev);
1144de3910ebSMauro Carvalho Chehab 	dev_set_name(mci_pdev, "mc");
1145de3910ebSMauro Carvalho Chehab 
1146de3910ebSMauro Carvalho Chehab 	err = device_add(mci_pdev);
11477a623c03SMauro Carvalho Chehab 	if (err < 0)
11482d56b109SDenis Kirjanov 		goto out_dev_free;
11498096cfafSDoug Thompson 
1150956b9ba1SJoe Perches 	edac_dbg(0, "device %s created\n", dev_name(mci_pdev));
1151de3910ebSMauro Carvalho Chehab 
11528096cfafSDoug Thompson 	return 0;
11532d56b109SDenis Kirjanov 
11542d56b109SDenis Kirjanov  out_dev_free:
11552d56b109SDenis Kirjanov 	kfree(mci_pdev);
11562d56b109SDenis Kirjanov  out_put_sysfs:
11572d56b109SDenis Kirjanov 	edac_put_sysfs_subsys();
11582d56b109SDenis Kirjanov  out:
11592d56b109SDenis Kirjanov 	return err;
11608096cfafSDoug Thompson }
11618096cfafSDoug Thompson 
11627a623c03SMauro Carvalho Chehab void __exit edac_mc_sysfs_exit(void)
11638096cfafSDoug Thompson {
116444d22e24SLans Zhang 	device_unregister(mci_pdev);
1165fe5ff8b8SKay Sievers 	edac_put_sysfs_subsys();
11668096cfafSDoug Thompson }
1167