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 * 107c9281d7SDouglas Thompson */ 117c9281d7SDouglas Thompson 127c9281d7SDouglas Thompson #include <linux/ctype.h> 138096cfafSDoug Thompson #include <linux/bug.h> 147c9281d7SDouglas Thompson 1520bcb7a8SDouglas Thompson #include "edac_core.h" 167c9281d7SDouglas Thompson #include "edac_module.h" 177c9281d7SDouglas Thompson 188096cfafSDoug Thompson 197c9281d7SDouglas Thompson /* MC EDAC Controls, setable by module parameter, and sysfs */ 204de78c68SDave Jiang static int edac_mc_log_ue = 1; 214de78c68SDave Jiang static int edac_mc_log_ce = 1; 22f044091cSDouglas Thompson static int edac_mc_panic_on_ue; 234de78c68SDave Jiang static int edac_mc_poll_msec = 1000; 247c9281d7SDouglas Thompson 257c9281d7SDouglas Thompson /* Getter functions for above */ 264de78c68SDave Jiang int edac_mc_get_log_ue(void) 277c9281d7SDouglas Thompson { 284de78c68SDave Jiang return edac_mc_log_ue; 297c9281d7SDouglas Thompson } 307c9281d7SDouglas Thompson 314de78c68SDave Jiang int edac_mc_get_log_ce(void) 327c9281d7SDouglas Thompson { 334de78c68SDave Jiang return edac_mc_log_ce; 347c9281d7SDouglas Thompson } 357c9281d7SDouglas Thompson 364de78c68SDave Jiang int edac_mc_get_panic_on_ue(void) 377c9281d7SDouglas Thompson { 384de78c68SDave Jiang return edac_mc_panic_on_ue; 397c9281d7SDouglas Thompson } 407c9281d7SDouglas Thompson 4181d87cb1SDave Jiang /* this is temporary */ 4281d87cb1SDave Jiang int edac_mc_get_poll_msec(void) 4381d87cb1SDave Jiang { 444de78c68SDave Jiang return edac_mc_poll_msec; 457c9281d7SDouglas Thompson } 467c9281d7SDouglas Thompson 47096846e2SArthur Jones static int edac_set_poll_msec(const char *val, struct kernel_param *kp) 48096846e2SArthur Jones { 49096846e2SArthur Jones long l; 50096846e2SArthur Jones int ret; 51096846e2SArthur Jones 52096846e2SArthur Jones if (!val) 53096846e2SArthur Jones return -EINVAL; 54096846e2SArthur Jones 55096846e2SArthur Jones ret = strict_strtol(val, 0, &l); 56096846e2SArthur Jones if (ret == -EINVAL || ((int)l != l)) 57096846e2SArthur Jones return -EINVAL; 58096846e2SArthur Jones *((int *)kp->arg) = l; 59096846e2SArthur Jones 60096846e2SArthur Jones /* notify edac_mc engine to reset the poll period */ 61096846e2SArthur Jones edac_mc_reset_delay_period(l); 62096846e2SArthur Jones 63096846e2SArthur Jones return 0; 64096846e2SArthur Jones } 65096846e2SArthur Jones 667c9281d7SDouglas Thompson /* Parameter declarations for above */ 674de78c68SDave Jiang module_param(edac_mc_panic_on_ue, int, 0644); 684de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); 694de78c68SDave Jiang module_param(edac_mc_log_ue, int, 0644); 704de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ue, 714de78c68SDave Jiang "Log uncorrectable error to console: 0=off 1=on"); 724de78c68SDave Jiang module_param(edac_mc_log_ce, int, 0644); 734de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ce, 744de78c68SDave Jiang "Log correctable error to console: 0=off 1=on"); 75096846e2SArthur Jones module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, 76096846e2SArthur Jones &edac_mc_poll_msec, 0644); 774de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); 787c9281d7SDouglas Thompson 797c9281d7SDouglas Thompson /* 807c9281d7SDouglas Thompson * various constants for Memory Controllers 817c9281d7SDouglas Thompson */ 827c9281d7SDouglas Thompson static const char *mem_types[] = { 837c9281d7SDouglas Thompson [MEM_EMPTY] = "Empty", 847c9281d7SDouglas Thompson [MEM_RESERVED] = "Reserved", 857c9281d7SDouglas Thompson [MEM_UNKNOWN] = "Unknown", 867c9281d7SDouglas Thompson [MEM_FPM] = "FPM", 877c9281d7SDouglas Thompson [MEM_EDO] = "EDO", 887c9281d7SDouglas Thompson [MEM_BEDO] = "BEDO", 897c9281d7SDouglas Thompson [MEM_SDR] = "Unbuffered-SDR", 907c9281d7SDouglas Thompson [MEM_RDR] = "Registered-SDR", 917c9281d7SDouglas Thompson [MEM_DDR] = "Unbuffered-DDR", 927c9281d7SDouglas Thompson [MEM_RDDR] = "Registered-DDR", 931a9b85e6SDave Jiang [MEM_RMBS] = "RMBS", 941a9b85e6SDave Jiang [MEM_DDR2] = "Unbuffered-DDR2", 951a9b85e6SDave Jiang [MEM_FB_DDR2] = "FullyBuffered-DDR2", 961d5f726cSBenjamin Herrenschmidt [MEM_RDDR2] = "Registered-DDR2", 97b1cfebc9SYang Shi [MEM_XDR] = "XDR", 98b1cfebc9SYang Shi [MEM_DDR3] = "Unbuffered-DDR3", 99b1cfebc9SYang Shi [MEM_RDDR3] = "Registered-DDR3" 1007c9281d7SDouglas Thompson }; 1017c9281d7SDouglas Thompson 1027c9281d7SDouglas Thompson static const char *dev_types[] = { 1037c9281d7SDouglas Thompson [DEV_UNKNOWN] = "Unknown", 1047c9281d7SDouglas Thompson [DEV_X1] = "x1", 1057c9281d7SDouglas Thompson [DEV_X2] = "x2", 1067c9281d7SDouglas Thompson [DEV_X4] = "x4", 1077c9281d7SDouglas Thompson [DEV_X8] = "x8", 1087c9281d7SDouglas Thompson [DEV_X16] = "x16", 1097c9281d7SDouglas Thompson [DEV_X32] = "x32", 1107c9281d7SDouglas Thompson [DEV_X64] = "x64" 1117c9281d7SDouglas Thompson }; 1127c9281d7SDouglas Thompson 1137c9281d7SDouglas Thompson static const char *edac_caps[] = { 1147c9281d7SDouglas Thompson [EDAC_UNKNOWN] = "Unknown", 1157c9281d7SDouglas Thompson [EDAC_NONE] = "None", 1167c9281d7SDouglas Thompson [EDAC_RESERVED] = "Reserved", 1177c9281d7SDouglas Thompson [EDAC_PARITY] = "PARITY", 1187c9281d7SDouglas Thompson [EDAC_EC] = "EC", 1197c9281d7SDouglas Thompson [EDAC_SECDED] = "SECDED", 1207c9281d7SDouglas Thompson [EDAC_S2ECD2ED] = "S2ECD2ED", 1217c9281d7SDouglas Thompson [EDAC_S4ECD4ED] = "S4ECD4ED", 1227c9281d7SDouglas Thompson [EDAC_S8ECD8ED] = "S8ECD8ED", 1237c9281d7SDouglas Thompson [EDAC_S16ECD16ED] = "S16ECD16ED" 1247c9281d7SDouglas Thompson }; 1257c9281d7SDouglas Thompson 1267c9281d7SDouglas Thompson 1277c9281d7SDouglas Thompson 1287c9281d7SDouglas Thompson static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) 1297c9281d7SDouglas Thompson { 1307c9281d7SDouglas Thompson int *value = (int *)ptr; 1317c9281d7SDouglas Thompson 1327c9281d7SDouglas Thompson if (isdigit(*buffer)) 1337c9281d7SDouglas Thompson *value = simple_strtoul(buffer, NULL, 0); 1347c9281d7SDouglas Thompson 1357c9281d7SDouglas Thompson return count; 1367c9281d7SDouglas Thompson } 1377c9281d7SDouglas Thompson 1387c9281d7SDouglas Thompson 1397c9281d7SDouglas Thompson /* EDAC sysfs CSROW data structures and methods 1407c9281d7SDouglas Thompson */ 1417c9281d7SDouglas Thompson 1427c9281d7SDouglas Thompson /* Set of more default csrow<id> attribute show/store functions */ 143e27e3dacSDouglas Thompson static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, 144e27e3dacSDouglas Thompson int private) 1457c9281d7SDouglas Thompson { 1467c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->ue_count); 1477c9281d7SDouglas Thompson } 1487c9281d7SDouglas Thompson 149e27e3dacSDouglas Thompson static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, 150e27e3dacSDouglas Thompson int private) 1517c9281d7SDouglas Thompson { 1527c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->ce_count); 1537c9281d7SDouglas Thompson } 1547c9281d7SDouglas Thompson 155e27e3dacSDouglas Thompson static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, 156e27e3dacSDouglas Thompson int private) 1577c9281d7SDouglas Thompson { 1587c9281d7SDouglas Thompson return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages)); 1597c9281d7SDouglas Thompson } 1607c9281d7SDouglas Thompson 161e27e3dacSDouglas Thompson static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, 162e27e3dacSDouglas Thompson int private) 1637c9281d7SDouglas Thompson { 1647c9281d7SDouglas Thompson return sprintf(data, "%s\n", mem_types[csrow->mtype]); 1657c9281d7SDouglas Thompson } 1667c9281d7SDouglas Thompson 167e27e3dacSDouglas Thompson static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, 168e27e3dacSDouglas Thompson int private) 1697c9281d7SDouglas Thompson { 1707c9281d7SDouglas Thompson return sprintf(data, "%s\n", dev_types[csrow->dtype]); 1717c9281d7SDouglas Thompson } 1727c9281d7SDouglas Thompson 173e27e3dacSDouglas Thompson static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, 174e27e3dacSDouglas Thompson int private) 1757c9281d7SDouglas Thompson { 1767c9281d7SDouglas Thompson return sprintf(data, "%s\n", edac_caps[csrow->edac_mode]); 1777c9281d7SDouglas Thompson } 1787c9281d7SDouglas Thompson 1797c9281d7SDouglas Thompson /* show/store functions for DIMM Label attributes */ 1807c9281d7SDouglas Thompson static ssize_t channel_dimm_label_show(struct csrow_info *csrow, 1817c9281d7SDouglas Thompson char *data, int channel) 1827c9281d7SDouglas Thompson { 183124682c7SArthur Jones /* if field has not been initialized, there is nothing to send */ 184124682c7SArthur Jones if (!csrow->channels[channel].label[0]) 185124682c7SArthur Jones return 0; 186124682c7SArthur Jones 187124682c7SArthur Jones return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", 1887c9281d7SDouglas Thompson csrow->channels[channel].label); 1897c9281d7SDouglas Thompson } 1907c9281d7SDouglas Thompson 1917c9281d7SDouglas Thompson static ssize_t channel_dimm_label_store(struct csrow_info *csrow, 1927c9281d7SDouglas Thompson const char *data, 193079708b9SDouglas Thompson size_t count, int channel) 1947c9281d7SDouglas Thompson { 1957c9281d7SDouglas Thompson ssize_t max_size = 0; 1967c9281d7SDouglas Thompson 1977c9281d7SDouglas Thompson max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); 1987c9281d7SDouglas Thompson strncpy(csrow->channels[channel].label, data, max_size); 1997c9281d7SDouglas Thompson csrow->channels[channel].label[max_size] = '\0'; 2007c9281d7SDouglas Thompson 2017c9281d7SDouglas Thompson return max_size; 2027c9281d7SDouglas Thompson } 2037c9281d7SDouglas Thompson 2047c9281d7SDouglas Thompson /* show function for dynamic chX_ce_count attribute */ 2057c9281d7SDouglas Thompson static ssize_t channel_ce_count_show(struct csrow_info *csrow, 206079708b9SDouglas Thompson char *data, int channel) 2077c9281d7SDouglas Thompson { 2087c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->channels[channel].ce_count); 2097c9281d7SDouglas Thompson } 2107c9281d7SDouglas Thompson 2117c9281d7SDouglas Thompson /* csrow specific attribute structure */ 2127c9281d7SDouglas Thompson struct csrowdev_attribute { 2137c9281d7SDouglas Thompson struct attribute attr; 2147c9281d7SDouglas Thompson ssize_t(*show) (struct csrow_info *, char *, int); 2157c9281d7SDouglas Thompson ssize_t(*store) (struct csrow_info *, const char *, size_t, int); 2167c9281d7SDouglas Thompson int private; 2177c9281d7SDouglas Thompson }; 2187c9281d7SDouglas Thompson 2197c9281d7SDouglas Thompson #define to_csrow(k) container_of(k, struct csrow_info, kobj) 2207c9281d7SDouglas Thompson #define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) 2217c9281d7SDouglas Thompson 2227c9281d7SDouglas Thompson /* Set of show/store higher level functions for default csrow attributes */ 2237c9281d7SDouglas Thompson static ssize_t csrowdev_show(struct kobject *kobj, 224079708b9SDouglas Thompson struct attribute *attr, char *buffer) 2257c9281d7SDouglas Thompson { 2267c9281d7SDouglas Thompson struct csrow_info *csrow = to_csrow(kobj); 2277c9281d7SDouglas Thompson struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); 2287c9281d7SDouglas Thompson 2297c9281d7SDouglas Thompson if (csrowdev_attr->show) 2307c9281d7SDouglas Thompson return csrowdev_attr->show(csrow, 231079708b9SDouglas Thompson buffer, csrowdev_attr->private); 2327c9281d7SDouglas Thompson return -EIO; 2337c9281d7SDouglas Thompson } 2347c9281d7SDouglas Thompson 2357c9281d7SDouglas Thompson static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, 2367c9281d7SDouglas Thompson const char *buffer, size_t count) 2377c9281d7SDouglas Thompson { 2387c9281d7SDouglas Thompson struct csrow_info *csrow = to_csrow(kobj); 2397c9281d7SDouglas Thompson struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); 2407c9281d7SDouglas Thompson 2417c9281d7SDouglas Thompson if (csrowdev_attr->store) 2427c9281d7SDouglas Thompson return csrowdev_attr->store(csrow, 2437c9281d7SDouglas Thompson buffer, 244079708b9SDouglas Thompson count, csrowdev_attr->private); 2457c9281d7SDouglas Thompson return -EIO; 2467c9281d7SDouglas Thompson } 2477c9281d7SDouglas Thompson 24852cf25d0SEmese Revfy static const struct sysfs_ops csrowfs_ops = { 2497c9281d7SDouglas Thompson .show = csrowdev_show, 2507c9281d7SDouglas Thompson .store = csrowdev_store 2517c9281d7SDouglas Thompson }; 2527c9281d7SDouglas Thompson 2537c9281d7SDouglas Thompson #define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ 2547c9281d7SDouglas Thompson static struct csrowdev_attribute attr_##_name = { \ 2557c9281d7SDouglas Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 2567c9281d7SDouglas Thompson .show = _show, \ 2577c9281d7SDouglas Thompson .store = _store, \ 2587c9281d7SDouglas Thompson .private = _private, \ 2597c9281d7SDouglas Thompson }; 2607c9281d7SDouglas Thompson 2617c9281d7SDouglas Thompson /* default cwrow<id>/attribute files */ 2627c9281d7SDouglas Thompson CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0); 2637c9281d7SDouglas Thompson CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0); 2647c9281d7SDouglas Thompson CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0); 2657c9281d7SDouglas Thompson CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0); 2667c9281d7SDouglas Thompson CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0); 2677c9281d7SDouglas Thompson CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0); 2687c9281d7SDouglas Thompson 2697c9281d7SDouglas Thompson /* default attributes of the CSROW<id> object */ 2707c9281d7SDouglas Thompson static struct csrowdev_attribute *default_csrow_attr[] = { 2717c9281d7SDouglas Thompson &attr_dev_type, 2727c9281d7SDouglas Thompson &attr_mem_type, 2737c9281d7SDouglas Thompson &attr_edac_mode, 2747c9281d7SDouglas Thompson &attr_size_mb, 2757c9281d7SDouglas Thompson &attr_ue_count, 2767c9281d7SDouglas Thompson &attr_ce_count, 2777c9281d7SDouglas Thompson NULL, 2787c9281d7SDouglas Thompson }; 2797c9281d7SDouglas Thompson 2807c9281d7SDouglas Thompson /* possible dynamic channel DIMM Label attribute files */ 2817c9281d7SDouglas Thompson CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR, 282079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 0); 2837c9281d7SDouglas Thompson CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR, 284079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 1); 2857c9281d7SDouglas Thompson CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR, 286079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 2); 2877c9281d7SDouglas Thompson CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR, 288079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 3); 2897c9281d7SDouglas Thompson CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR, 290079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 4); 2917c9281d7SDouglas Thompson CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR, 292079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 5); 2937c9281d7SDouglas Thompson 2947c9281d7SDouglas Thompson /* Total possible dynamic DIMM Label attribute file table */ 2957c9281d7SDouglas Thompson static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { 2967c9281d7SDouglas Thompson &attr_ch0_dimm_label, 2977c9281d7SDouglas Thompson &attr_ch1_dimm_label, 2987c9281d7SDouglas Thompson &attr_ch2_dimm_label, 2997c9281d7SDouglas Thompson &attr_ch3_dimm_label, 3007c9281d7SDouglas Thompson &attr_ch4_dimm_label, 3017c9281d7SDouglas Thompson &attr_ch5_dimm_label 3027c9281d7SDouglas Thompson }; 3037c9281d7SDouglas Thompson 3047c9281d7SDouglas Thompson /* possible dynamic channel ce_count attribute files */ 305079708b9SDouglas Thompson CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0); 306079708b9SDouglas Thompson CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1); 307079708b9SDouglas Thompson CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2); 308079708b9SDouglas Thompson CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3); 309079708b9SDouglas Thompson CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4); 310079708b9SDouglas Thompson CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5); 3117c9281d7SDouglas Thompson 3127c9281d7SDouglas Thompson /* Total possible dynamic ce_count attribute file table */ 3137c9281d7SDouglas Thompson static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { 3147c9281d7SDouglas Thompson &attr_ch0_ce_count, 3157c9281d7SDouglas Thompson &attr_ch1_ce_count, 3167c9281d7SDouglas Thompson &attr_ch2_ce_count, 3177c9281d7SDouglas Thompson &attr_ch3_ce_count, 3187c9281d7SDouglas Thompson &attr_ch4_ce_count, 3197c9281d7SDouglas Thompson &attr_ch5_ce_count 3207c9281d7SDouglas Thompson }; 3217c9281d7SDouglas Thompson 3227c9281d7SDouglas Thompson #define EDAC_NR_CHANNELS 6 3237c9281d7SDouglas Thompson 3247c9281d7SDouglas Thompson /* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ 3257c9281d7SDouglas Thompson static int edac_create_channel_files(struct kobject *kobj, int chan) 3267c9281d7SDouglas Thompson { 3277c9281d7SDouglas Thompson int err = -ENODEV; 3287c9281d7SDouglas Thompson 3297c9281d7SDouglas Thompson if (chan >= EDAC_NR_CHANNELS) 3307c9281d7SDouglas Thompson return err; 3317c9281d7SDouglas Thompson 3327c9281d7SDouglas Thompson /* create the DIMM label attribute file */ 3337c9281d7SDouglas Thompson err = sysfs_create_file(kobj, 334079708b9SDouglas Thompson (struct attribute *) 335079708b9SDouglas Thompson dynamic_csrow_dimm_attr[chan]); 3367c9281d7SDouglas Thompson 3377c9281d7SDouglas Thompson if (!err) { 3387c9281d7SDouglas Thompson /* create the CE Count attribute file */ 3397c9281d7SDouglas Thompson err = sysfs_create_file(kobj, 340079708b9SDouglas Thompson (struct attribute *) 341079708b9SDouglas Thompson dynamic_csrow_ce_count_attr[chan]); 3427c9281d7SDouglas Thompson } else { 343e27e3dacSDouglas Thompson debugf1("%s() dimm labels and ce_count files created", 344e27e3dacSDouglas Thompson __func__); 3457c9281d7SDouglas Thompson } 3467c9281d7SDouglas Thompson 3477c9281d7SDouglas Thompson return err; 3487c9281d7SDouglas Thompson } 3497c9281d7SDouglas Thompson 3507c9281d7SDouglas Thompson /* No memory to release for this kobj */ 3517c9281d7SDouglas Thompson static void edac_csrow_instance_release(struct kobject *kobj) 3527c9281d7SDouglas Thompson { 3538096cfafSDoug Thompson struct mem_ctl_info *mci; 3547c9281d7SDouglas Thompson struct csrow_info *cs; 3557c9281d7SDouglas Thompson 3568096cfafSDoug Thompson debugf1("%s()\n", __func__); 3578096cfafSDoug Thompson 3587c9281d7SDouglas Thompson cs = container_of(kobj, struct csrow_info, kobj); 3598096cfafSDoug Thompson mci = cs->mci; 3608096cfafSDoug Thompson 3618096cfafSDoug Thompson kobject_put(&mci->edac_mci_kobj); 3627c9281d7SDouglas Thompson } 3637c9281d7SDouglas Thompson 3647c9281d7SDouglas Thompson /* the kobj_type instance for a CSROW */ 3657c9281d7SDouglas Thompson static struct kobj_type ktype_csrow = { 3667c9281d7SDouglas Thompson .release = edac_csrow_instance_release, 3677c9281d7SDouglas Thompson .sysfs_ops = &csrowfs_ops, 3687c9281d7SDouglas Thompson .default_attrs = (struct attribute **)default_csrow_attr, 3697c9281d7SDouglas Thompson }; 3707c9281d7SDouglas Thompson 3717c9281d7SDouglas Thompson /* Create a CSROW object under specifed edac_mc_device */ 3728096cfafSDoug Thompson static int edac_create_csrow_object(struct mem_ctl_info *mci, 373079708b9SDouglas Thompson struct csrow_info *csrow, int index) 3747c9281d7SDouglas Thompson { 3758096cfafSDoug Thompson struct kobject *kobj_mci = &mci->edac_mci_kobj; 3768096cfafSDoug Thompson struct kobject *kobj; 3777c9281d7SDouglas Thompson int chan; 3788096cfafSDoug Thompson int err; 3797c9281d7SDouglas Thompson 3807c9281d7SDouglas Thompson /* generate ..../edac/mc/mc<id>/csrow<index> */ 3818096cfafSDoug Thompson memset(&csrow->kobj, 0, sizeof(csrow->kobj)); 3828096cfafSDoug Thompson csrow->mci = mci; /* include container up link */ 3838096cfafSDoug Thompson 3848096cfafSDoug Thompson /* bump the mci instance's kobject's ref count */ 3858096cfafSDoug Thompson kobj = kobject_get(&mci->edac_mci_kobj); 3868096cfafSDoug Thompson if (!kobj) { 3878096cfafSDoug Thompson err = -ENODEV; 3888096cfafSDoug Thompson goto err_out; 3898096cfafSDoug Thompson } 3907c9281d7SDouglas Thompson 3917c9281d7SDouglas Thompson /* Instanstiate the csrow object */ 392b2ed215aSGreg Kroah-Hartman err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci, 393b2ed215aSGreg Kroah-Hartman "csrow%d", index); 3948096cfafSDoug Thompson if (err) 3958096cfafSDoug Thompson goto err_release_top_kobj; 3968096cfafSDoug Thompson 3978096cfafSDoug Thompson /* At this point, to release a csrow kobj, one must 398c10997f6SGreg Kroah-Hartman * call the kobject_put and allow that tear down 3998096cfafSDoug Thompson * to work the releasing 4008096cfafSDoug Thompson */ 4018096cfafSDoug Thompson 4027c9281d7SDouglas Thompson /* Create the dyanmic attribute files on this csrow, 4037c9281d7SDouglas Thompson * namely, the DIMM labels and the channel ce_count 4047c9281d7SDouglas Thompson */ 4057c9281d7SDouglas Thompson for (chan = 0; chan < csrow->nr_channels; chan++) { 4067c9281d7SDouglas Thompson err = edac_create_channel_files(&csrow->kobj, chan); 4078096cfafSDoug Thompson if (err) { 4088096cfafSDoug Thompson /* special case the unregister here */ 409c10997f6SGreg Kroah-Hartman kobject_put(&csrow->kobj); 4108096cfafSDoug Thompson goto err_out; 4117c9281d7SDouglas Thompson } 4127c9281d7SDouglas Thompson } 413b2ed215aSGreg Kroah-Hartman kobject_uevent(&csrow->kobj, KOBJ_ADD); 4148096cfafSDoug Thompson return 0; 4158096cfafSDoug Thompson 4168096cfafSDoug Thompson /* error unwind stack */ 4178096cfafSDoug Thompson err_release_top_kobj: 4188096cfafSDoug Thompson kobject_put(&mci->edac_mci_kobj); 4198096cfafSDoug Thompson 4208096cfafSDoug Thompson err_out: 4217c9281d7SDouglas Thompson return err; 4227c9281d7SDouglas Thompson } 4237c9281d7SDouglas Thompson 4247c9281d7SDouglas Thompson /* default sysfs methods and data structures for the main MCI kobject */ 4257c9281d7SDouglas Thompson 4267c9281d7SDouglas Thompson static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, 4277c9281d7SDouglas Thompson const char *data, size_t count) 4287c9281d7SDouglas Thompson { 4297c9281d7SDouglas Thompson int row, chan; 4307c9281d7SDouglas Thompson 4317c9281d7SDouglas Thompson mci->ue_noinfo_count = 0; 4327c9281d7SDouglas Thompson mci->ce_noinfo_count = 0; 4337c9281d7SDouglas Thompson mci->ue_count = 0; 4347c9281d7SDouglas Thompson mci->ce_count = 0; 4357c9281d7SDouglas Thompson 4367c9281d7SDouglas Thompson for (row = 0; row < mci->nr_csrows; row++) { 4377c9281d7SDouglas Thompson struct csrow_info *ri = &mci->csrows[row]; 4387c9281d7SDouglas Thompson 4397c9281d7SDouglas Thompson ri->ue_count = 0; 4407c9281d7SDouglas Thompson ri->ce_count = 0; 4417c9281d7SDouglas Thompson 4427c9281d7SDouglas Thompson for (chan = 0; chan < ri->nr_channels; chan++) 4437c9281d7SDouglas Thompson ri->channels[chan].ce_count = 0; 4447c9281d7SDouglas Thompson } 4457c9281d7SDouglas Thompson 4467c9281d7SDouglas Thompson mci->start_time = jiffies; 4477c9281d7SDouglas Thompson return count; 4487c9281d7SDouglas Thompson } 4497c9281d7SDouglas Thompson 4507c9281d7SDouglas Thompson /* memory scrubbing */ 4517c9281d7SDouglas Thompson static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, 4527c9281d7SDouglas Thompson const char *data, size_t count) 4537c9281d7SDouglas Thompson { 4547c9281d7SDouglas Thompson u32 bandwidth = -1; 4557c9281d7SDouglas Thompson 4567c9281d7SDouglas Thompson if (mci->set_sdram_scrub_rate) { 4577c9281d7SDouglas Thompson 4587c9281d7SDouglas Thompson memctrl_int_store(&bandwidth, data, count); 4597c9281d7SDouglas Thompson 4607c9281d7SDouglas Thompson if (!(*mci->set_sdram_scrub_rate) (mci, &bandwidth)) { 4617c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4627c9281d7SDouglas Thompson "Scrub rate set successfully, applied: %d\n", 4637c9281d7SDouglas Thompson bandwidth); 4647c9281d7SDouglas Thompson } else { 4657c9281d7SDouglas Thompson /* FIXME: error codes maybe? */ 4667c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4677c9281d7SDouglas Thompson "Scrub rate set FAILED, could not apply: %d\n", 4687c9281d7SDouglas Thompson bandwidth); 4697c9281d7SDouglas Thompson } 4707c9281d7SDouglas Thompson } else { 4717c9281d7SDouglas Thompson /* FIXME: produce "not implemented" ERROR for user-side. */ 4727c9281d7SDouglas Thompson edac_printk(KERN_WARNING, EDAC_MC, 4737c9281d7SDouglas Thompson "Memory scrubbing 'set'control is not implemented!\n"); 4747c9281d7SDouglas Thompson } 4757c9281d7SDouglas Thompson return count; 4767c9281d7SDouglas Thompson } 4777c9281d7SDouglas Thompson 4787c9281d7SDouglas Thompson static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) 4797c9281d7SDouglas Thompson { 4807c9281d7SDouglas Thompson u32 bandwidth = -1; 4817c9281d7SDouglas Thompson 4827c9281d7SDouglas Thompson if (mci->get_sdram_scrub_rate) { 4837c9281d7SDouglas Thompson if (!(*mci->get_sdram_scrub_rate) (mci, &bandwidth)) { 4847c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4857c9281d7SDouglas Thompson "Scrub rate successfully, fetched: %d\n", 4867c9281d7SDouglas Thompson bandwidth); 4877c9281d7SDouglas Thompson } else { 4887c9281d7SDouglas Thompson /* FIXME: error codes maybe? */ 4897c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4907c9281d7SDouglas Thompson "Scrub rate fetch FAILED, got: %d\n", 4917c9281d7SDouglas Thompson bandwidth); 4927c9281d7SDouglas Thompson } 4937c9281d7SDouglas Thompson } else { 4947c9281d7SDouglas Thompson /* FIXME: produce "not implemented" ERROR for user-side. */ 4957c9281d7SDouglas Thompson edac_printk(KERN_WARNING, EDAC_MC, 496e27e3dacSDouglas Thompson "Memory scrubbing 'get' control is not implemented\n"); 4977c9281d7SDouglas Thompson } 4987c9281d7SDouglas Thompson return sprintf(data, "%d\n", bandwidth); 4997c9281d7SDouglas Thompson } 5007c9281d7SDouglas Thompson 5017c9281d7SDouglas Thompson /* default attribute files for the MCI object */ 5027c9281d7SDouglas Thompson static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) 5037c9281d7SDouglas Thompson { 5047c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ue_count); 5057c9281d7SDouglas Thompson } 5067c9281d7SDouglas Thompson 5077c9281d7SDouglas Thompson static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) 5087c9281d7SDouglas Thompson { 5097c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ce_count); 5107c9281d7SDouglas Thompson } 5117c9281d7SDouglas Thompson 5127c9281d7SDouglas Thompson static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) 5137c9281d7SDouglas Thompson { 5147c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ce_noinfo_count); 5157c9281d7SDouglas Thompson } 5167c9281d7SDouglas Thompson 5177c9281d7SDouglas Thompson static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) 5187c9281d7SDouglas Thompson { 5197c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ue_noinfo_count); 5207c9281d7SDouglas Thompson } 5217c9281d7SDouglas Thompson 5227c9281d7SDouglas Thompson static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) 5237c9281d7SDouglas Thompson { 5247c9281d7SDouglas Thompson return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ); 5257c9281d7SDouglas Thompson } 5267c9281d7SDouglas Thompson 5277c9281d7SDouglas Thompson static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) 5287c9281d7SDouglas Thompson { 5297c9281d7SDouglas Thompson return sprintf(data, "%s\n", mci->ctl_name); 5307c9281d7SDouglas Thompson } 5317c9281d7SDouglas Thompson 5327c9281d7SDouglas Thompson static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) 5337c9281d7SDouglas Thompson { 5347c9281d7SDouglas Thompson int total_pages, csrow_idx; 5357c9281d7SDouglas Thompson 5367c9281d7SDouglas Thompson for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows; 5377c9281d7SDouglas Thompson csrow_idx++) { 5387c9281d7SDouglas Thompson struct csrow_info *csrow = &mci->csrows[csrow_idx]; 5397c9281d7SDouglas Thompson 5407c9281d7SDouglas Thompson if (!csrow->nr_pages) 5417c9281d7SDouglas Thompson continue; 5427c9281d7SDouglas Thompson 5437c9281d7SDouglas Thompson total_pages += csrow->nr_pages; 5447c9281d7SDouglas Thompson } 5457c9281d7SDouglas Thompson 5467c9281d7SDouglas Thompson return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); 5477c9281d7SDouglas Thompson } 5487c9281d7SDouglas Thompson 5497c9281d7SDouglas Thompson #define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) 55042a8e397SDouglas Thompson #define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr) 5517c9281d7SDouglas Thompson 5527c9281d7SDouglas Thompson /* MCI show/store functions for top most object */ 5537c9281d7SDouglas Thompson static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, 5547c9281d7SDouglas Thompson char *buffer) 5557c9281d7SDouglas Thompson { 5567c9281d7SDouglas Thompson struct mem_ctl_info *mem_ctl_info = to_mci(kobj); 55742a8e397SDouglas Thompson struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 5587c9281d7SDouglas Thompson 5597c9281d7SDouglas Thompson if (mcidev_attr->show) 5607c9281d7SDouglas Thompson return mcidev_attr->show(mem_ctl_info, buffer); 5617c9281d7SDouglas Thompson 5627c9281d7SDouglas Thompson return -EIO; 5637c9281d7SDouglas Thompson } 5647c9281d7SDouglas Thompson 5657c9281d7SDouglas Thompson static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, 5667c9281d7SDouglas Thompson const char *buffer, size_t count) 5677c9281d7SDouglas Thompson { 5687c9281d7SDouglas Thompson struct mem_ctl_info *mem_ctl_info = to_mci(kobj); 56942a8e397SDouglas Thompson struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 5707c9281d7SDouglas Thompson 5717c9281d7SDouglas Thompson if (mcidev_attr->store) 5727c9281d7SDouglas Thompson return mcidev_attr->store(mem_ctl_info, buffer, count); 5737c9281d7SDouglas Thompson 5747c9281d7SDouglas Thompson return -EIO; 5757c9281d7SDouglas Thompson } 5767c9281d7SDouglas Thompson 5778096cfafSDoug Thompson /* Intermediate show/store table */ 57852cf25d0SEmese Revfy static const struct sysfs_ops mci_ops = { 5797c9281d7SDouglas Thompson .show = mcidev_show, 5807c9281d7SDouglas Thompson .store = mcidev_store 5817c9281d7SDouglas Thompson }; 5827c9281d7SDouglas Thompson 5837c9281d7SDouglas Thompson #define MCIDEV_ATTR(_name,_mode,_show,_store) \ 58442a8e397SDouglas Thompson static struct mcidev_sysfs_attribute mci_attr_##_name = { \ 5857c9281d7SDouglas Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 5867c9281d7SDouglas Thompson .show = _show, \ 5877c9281d7SDouglas Thompson .store = _store, \ 5887c9281d7SDouglas Thompson }; 5897c9281d7SDouglas Thompson 5907c9281d7SDouglas Thompson /* default Control file */ 5917c9281d7SDouglas Thompson MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); 5927c9281d7SDouglas Thompson 5937c9281d7SDouglas Thompson /* default Attribute files */ 5947c9281d7SDouglas Thompson MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); 5957c9281d7SDouglas Thompson MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); 5967c9281d7SDouglas Thompson MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); 5977c9281d7SDouglas Thompson MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); 5987c9281d7SDouglas Thompson MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); 5997c9281d7SDouglas Thompson MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); 6007c9281d7SDouglas Thompson MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); 6017c9281d7SDouglas Thompson 6027c9281d7SDouglas Thompson /* memory scrubber attribute file */ 603079708b9SDouglas Thompson MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, 604e27e3dacSDouglas Thompson mci_sdram_scrub_rate_store); 6057c9281d7SDouglas Thompson 60642a8e397SDouglas Thompson static struct mcidev_sysfs_attribute *mci_attr[] = { 6077c9281d7SDouglas Thompson &mci_attr_reset_counters, 6087c9281d7SDouglas Thompson &mci_attr_mc_name, 6097c9281d7SDouglas Thompson &mci_attr_size_mb, 6107c9281d7SDouglas Thompson &mci_attr_seconds_since_reset, 6117c9281d7SDouglas Thompson &mci_attr_ue_noinfo_count, 6127c9281d7SDouglas Thompson &mci_attr_ce_noinfo_count, 6137c9281d7SDouglas Thompson &mci_attr_ue_count, 6147c9281d7SDouglas Thompson &mci_attr_ce_count, 6157c9281d7SDouglas Thompson &mci_attr_sdram_scrub_rate, 6167c9281d7SDouglas Thompson NULL 6177c9281d7SDouglas Thompson }; 6187c9281d7SDouglas Thompson 6198096cfafSDoug Thompson 6207c9281d7SDouglas Thompson /* 6217c9281d7SDouglas Thompson * Release of a MC controlling instance 6228096cfafSDoug Thompson * 6238096cfafSDoug Thompson * each MC control instance has the following resources upon entry: 6248096cfafSDoug Thompson * a) a ref count on the top memctl kobj 6258096cfafSDoug Thompson * b) a ref count on this module 6268096cfafSDoug Thompson * 6278096cfafSDoug Thompson * this function must decrement those ref counts and then 6288096cfafSDoug Thompson * issue a free on the instance's memory 6297c9281d7SDouglas Thompson */ 6308096cfafSDoug Thompson static void edac_mci_control_release(struct kobject *kobj) 6317c9281d7SDouglas Thompson { 6327c9281d7SDouglas Thompson struct mem_ctl_info *mci; 6337c9281d7SDouglas Thompson 6347c9281d7SDouglas Thompson mci = to_mci(kobj); 6358096cfafSDoug Thompson 6368096cfafSDoug Thompson debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx); 6378096cfafSDoug Thompson 6388096cfafSDoug Thompson /* decrement the module ref count */ 6398096cfafSDoug Thompson module_put(mci->owner); 6408096cfafSDoug Thompson 6418096cfafSDoug Thompson /* free the mci instance memory here */ 6428096cfafSDoug Thompson kfree(mci); 6437c9281d7SDouglas Thompson } 6447c9281d7SDouglas Thompson 6457c9281d7SDouglas Thompson static struct kobj_type ktype_mci = { 6468096cfafSDoug Thompson .release = edac_mci_control_release, 6477c9281d7SDouglas Thompson .sysfs_ops = &mci_ops, 6487c9281d7SDouglas Thompson .default_attrs = (struct attribute **)mci_attr, 6497c9281d7SDouglas Thompson }; 6507c9281d7SDouglas Thompson 6518096cfafSDoug Thompson /* EDAC memory controller sysfs kset: 6528096cfafSDoug Thompson * /sys/devices/system/edac/mc 6538096cfafSDoug Thompson */ 654f9fc82adSArthur Jones static struct kset *mc_kset; 6558096cfafSDoug Thompson 6568096cfafSDoug Thompson /* 6578096cfafSDoug Thompson * edac_mc_register_sysfs_main_kobj 6588096cfafSDoug Thompson * 6598096cfafSDoug Thompson * setups and registers the main kobject for each mci 6608096cfafSDoug Thompson */ 6618096cfafSDoug Thompson int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci) 6628096cfafSDoug Thompson { 6638096cfafSDoug Thompson struct kobject *kobj_mci; 6648096cfafSDoug Thompson int err; 6658096cfafSDoug Thompson 6668096cfafSDoug Thompson debugf1("%s()\n", __func__); 6678096cfafSDoug Thompson 6688096cfafSDoug Thompson kobj_mci = &mci->edac_mci_kobj; 6698096cfafSDoug Thompson 6708096cfafSDoug Thompson /* Init the mci's kobject */ 6718096cfafSDoug Thompson memset(kobj_mci, 0, sizeof(*kobj_mci)); 6728096cfafSDoug Thompson 6738096cfafSDoug Thompson /* Record which module 'owns' this control structure 6748096cfafSDoug Thompson * and bump the ref count of the module 6758096cfafSDoug Thompson */ 6768096cfafSDoug Thompson mci->owner = THIS_MODULE; 6778096cfafSDoug Thompson 6788096cfafSDoug Thompson /* bump ref count on this module */ 6798096cfafSDoug Thompson if (!try_module_get(mci->owner)) { 6808096cfafSDoug Thompson err = -ENODEV; 6818096cfafSDoug Thompson goto fail_out; 6828096cfafSDoug Thompson } 6838096cfafSDoug Thompson 684b2ed215aSGreg Kroah-Hartman /* this instance become part of the mc_kset */ 685f9fc82adSArthur Jones kobj_mci->kset = mc_kset; 686b2ed215aSGreg Kroah-Hartman 6878096cfafSDoug Thompson /* register the mc<id> kobject to the mc_kset */ 688b2ed215aSGreg Kroah-Hartman err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL, 689b2ed215aSGreg Kroah-Hartman "mc%d", mci->mc_idx); 6908096cfafSDoug Thompson if (err) { 6918096cfafSDoug Thompson debugf1("%s()Failed to register '.../edac/mc%d'\n", 6928096cfafSDoug Thompson __func__, mci->mc_idx); 6938096cfafSDoug Thompson goto kobj_reg_fail; 6948096cfafSDoug Thompson } 695b2ed215aSGreg Kroah-Hartman kobject_uevent(kobj_mci, KOBJ_ADD); 6968096cfafSDoug Thompson 6978096cfafSDoug Thompson /* At this point, to 'free' the control struct, 6988096cfafSDoug Thompson * edac_mc_unregister_sysfs_main_kobj() must be used 6998096cfafSDoug Thompson */ 7008096cfafSDoug Thompson 7018096cfafSDoug Thompson debugf1("%s() Registered '.../edac/mc%d' kobject\n", 7028096cfafSDoug Thompson __func__, mci->mc_idx); 7038096cfafSDoug Thompson 7048096cfafSDoug Thompson return 0; 7058096cfafSDoug Thompson 7068096cfafSDoug Thompson /* Error exit stack */ 7078096cfafSDoug Thompson 7088096cfafSDoug Thompson kobj_reg_fail: 7098096cfafSDoug Thompson module_put(mci->owner); 7108096cfafSDoug Thompson 7118096cfafSDoug Thompson fail_out: 7128096cfafSDoug Thompson return err; 7138096cfafSDoug Thompson } 7148096cfafSDoug Thompson 7158096cfafSDoug Thompson /* 7168096cfafSDoug Thompson * edac_mc_register_sysfs_main_kobj 7178096cfafSDoug Thompson * 7188096cfafSDoug Thompson * tears down and the main mci kobject from the mc_kset 7198096cfafSDoug Thompson */ 7208096cfafSDoug Thompson void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) 7218096cfafSDoug Thompson { 7228096cfafSDoug Thompson /* delete the kobj from the mc_kset */ 723c10997f6SGreg Kroah-Hartman kobject_put(&mci->edac_mci_kobj); 7248096cfafSDoug Thompson } 7258096cfafSDoug Thompson 7267c9281d7SDouglas Thompson #define EDAC_DEVICE_SYMLINK "device" 7277c9281d7SDouglas Thompson 7287c9281d7SDouglas Thompson /* 7298096cfafSDoug Thompson * edac_create_mci_instance_attributes 73042a8e397SDouglas Thompson * create MC driver specific attributes at the topmost level 73142a8e397SDouglas Thompson * directory of this mci instance. 73242a8e397SDouglas Thompson */ 7338096cfafSDoug Thompson static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci) 73442a8e397SDouglas Thompson { 73542a8e397SDouglas Thompson int err; 73642a8e397SDouglas Thompson struct mcidev_sysfs_attribute *sysfs_attrib; 73742a8e397SDouglas Thompson 73842a8e397SDouglas Thompson /* point to the start of the array and iterate over it 73942a8e397SDouglas Thompson * adding each attribute listed to this mci instance's kobject 74042a8e397SDouglas Thompson */ 74142a8e397SDouglas Thompson sysfs_attrib = mci->mc_driver_sysfs_attributes; 74242a8e397SDouglas Thompson 7438096cfafSDoug Thompson while (sysfs_attrib && sysfs_attrib->attr.name) { 74442a8e397SDouglas Thompson err = sysfs_create_file(&mci->edac_mci_kobj, 74542a8e397SDouglas Thompson (struct attribute*) sysfs_attrib); 74642a8e397SDouglas Thompson if (err) { 74742a8e397SDouglas Thompson return err; 74842a8e397SDouglas Thompson } 74942a8e397SDouglas Thompson 75042a8e397SDouglas Thompson sysfs_attrib++; 75142a8e397SDouglas Thompson } 75242a8e397SDouglas Thompson 75342a8e397SDouglas Thompson return 0; 75442a8e397SDouglas Thompson } 75542a8e397SDouglas Thompson 75642a8e397SDouglas Thompson /* 7578096cfafSDoug Thompson * edac_remove_mci_instance_attributes 7588096cfafSDoug Thompson * remove MC driver specific attributes at the topmost level 7598096cfafSDoug Thompson * directory of this mci instance. 7608096cfafSDoug Thompson */ 7618096cfafSDoug Thompson static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci) 7628096cfafSDoug Thompson { 7638096cfafSDoug Thompson struct mcidev_sysfs_attribute *sysfs_attrib; 7648096cfafSDoug Thompson 7658096cfafSDoug Thompson /* point to the start of the array and iterate over it 7668096cfafSDoug Thompson * adding each attribute listed to this mci instance's kobject 7678096cfafSDoug Thompson */ 7688096cfafSDoug Thompson sysfs_attrib = mci->mc_driver_sysfs_attributes; 7698096cfafSDoug Thompson 7708096cfafSDoug Thompson /* loop if there are attributes and until we hit a NULL entry */ 7718096cfafSDoug Thompson while (sysfs_attrib && sysfs_attrib->attr.name) { 7728096cfafSDoug Thompson sysfs_remove_file(&mci->edac_mci_kobj, 7738096cfafSDoug Thompson (struct attribute *) sysfs_attrib); 7748096cfafSDoug Thompson sysfs_attrib++; 7758096cfafSDoug Thompson } 7768096cfafSDoug Thompson } 7778096cfafSDoug Thompson 7788096cfafSDoug Thompson 7798096cfafSDoug Thompson /* 7807c9281d7SDouglas Thompson * Create a new Memory Controller kobject instance, 7817c9281d7SDouglas Thompson * mc<id> under the 'mc' directory 7827c9281d7SDouglas Thompson * 7837c9281d7SDouglas Thompson * Return: 7847c9281d7SDouglas Thompson * 0 Success 7857c9281d7SDouglas Thompson * !0 Failure 7867c9281d7SDouglas Thompson */ 7877c9281d7SDouglas Thompson int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) 7887c9281d7SDouglas Thompson { 7897c9281d7SDouglas Thompson int i; 7907c9281d7SDouglas Thompson int err; 7917c9281d7SDouglas Thompson struct csrow_info *csrow; 7928096cfafSDoug Thompson struct kobject *kobj_mci = &mci->edac_mci_kobj; 7937c9281d7SDouglas Thompson 7947c9281d7SDouglas Thompson debugf0("%s() idx=%d\n", __func__, mci->mc_idx); 7957c9281d7SDouglas Thompson 7967c9281d7SDouglas Thompson /* create a symlink for the device */ 7978096cfafSDoug Thompson err = sysfs_create_link(kobj_mci, &mci->dev->kobj, 7987c9281d7SDouglas Thompson EDAC_DEVICE_SYMLINK); 7998096cfafSDoug Thompson if (err) { 8008096cfafSDoug Thompson debugf1("%s() failure to create symlink\n", __func__); 8017c9281d7SDouglas Thompson goto fail0; 8028096cfafSDoug Thompson } 8037c9281d7SDouglas Thompson 80442a8e397SDouglas Thompson /* If the low level driver desires some attributes, 80542a8e397SDouglas Thompson * then create them now for the driver. 80642a8e397SDouglas Thompson */ 80742a8e397SDouglas Thompson if (mci->mc_driver_sysfs_attributes) { 8088096cfafSDoug Thompson err = edac_create_mci_instance_attributes(mci); 8098096cfafSDoug Thompson if (err) { 8108096cfafSDoug Thompson debugf1("%s() failure to create mci attributes\n", 8118096cfafSDoug Thompson __func__); 81242a8e397SDouglas Thompson goto fail0; 81342a8e397SDouglas Thompson } 8148096cfafSDoug Thompson } 81542a8e397SDouglas Thompson 8168096cfafSDoug Thompson /* Make directories for each CSROW object under the mc<id> kobject 8177c9281d7SDouglas Thompson */ 8187c9281d7SDouglas Thompson for (i = 0; i < mci->nr_csrows; i++) { 8197c9281d7SDouglas Thompson csrow = &mci->csrows[i]; 8207c9281d7SDouglas Thompson 8217c9281d7SDouglas Thompson /* Only expose populated CSROWs */ 8227c9281d7SDouglas Thompson if (csrow->nr_pages > 0) { 8238096cfafSDoug Thompson err = edac_create_csrow_object(mci, csrow, i); 8248096cfafSDoug Thompson if (err) { 8258096cfafSDoug Thompson debugf1("%s() failure: create csrow %d obj\n", 8268096cfafSDoug Thompson __func__, i); 8277c9281d7SDouglas Thompson goto fail1; 8287c9281d7SDouglas Thompson } 8297c9281d7SDouglas Thompson } 8308096cfafSDoug Thompson } 8317c9281d7SDouglas Thompson 8327c9281d7SDouglas Thompson return 0; 8337c9281d7SDouglas Thompson 8347c9281d7SDouglas Thompson /* CSROW error: backout what has already been registered, */ 8357c9281d7SDouglas Thompson fail1: 8367c9281d7SDouglas Thompson for (i--; i >= 0; i--) { 8377c9281d7SDouglas Thompson if (csrow->nr_pages > 0) { 838c10997f6SGreg Kroah-Hartman kobject_put(&mci->csrows[i].kobj); 8397c9281d7SDouglas Thompson } 8407c9281d7SDouglas Thompson } 8417c9281d7SDouglas Thompson 8428096cfafSDoug Thompson /* remove the mci instance's attributes, if any */ 8438096cfafSDoug Thompson edac_remove_mci_instance_attributes(mci); 8448096cfafSDoug Thompson 8458096cfafSDoug Thompson /* remove the symlink */ 8468096cfafSDoug Thompson sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); 8478096cfafSDoug Thompson 8487c9281d7SDouglas Thompson fail0: 8497c9281d7SDouglas Thompson return err; 8507c9281d7SDouglas Thompson } 8517c9281d7SDouglas Thompson 8527c9281d7SDouglas Thompson /* 8537c9281d7SDouglas Thompson * remove a Memory Controller instance 8547c9281d7SDouglas Thompson */ 8557c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) 8567c9281d7SDouglas Thompson { 8577c9281d7SDouglas Thompson int i; 8587c9281d7SDouglas Thompson 8597c9281d7SDouglas Thompson debugf0("%s()\n", __func__); 8607c9281d7SDouglas Thompson 8617c9281d7SDouglas Thompson /* remove all csrow kobjects */ 8627c9281d7SDouglas Thompson for (i = 0; i < mci->nr_csrows; i++) { 8637c9281d7SDouglas Thompson if (mci->csrows[i].nr_pages > 0) { 8648096cfafSDoug Thompson debugf0("%s() unreg csrow-%d\n", __func__, i); 865c10997f6SGreg Kroah-Hartman kobject_put(&mci->csrows[i].kobj); 8667c9281d7SDouglas Thompson } 8677c9281d7SDouglas Thompson } 8687c9281d7SDouglas Thompson 8698096cfafSDoug Thompson debugf0("%s() remove_link\n", __func__); 8708096cfafSDoug Thompson 8718096cfafSDoug Thompson /* remove the symlink */ 8727c9281d7SDouglas Thompson sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); 8738096cfafSDoug Thompson 8748096cfafSDoug Thompson debugf0("%s() remove_mci_instance\n", __func__); 8758096cfafSDoug Thompson 8768096cfafSDoug Thompson /* remove this mci instance's attribtes */ 8778096cfafSDoug Thompson edac_remove_mci_instance_attributes(mci); 8788096cfafSDoug Thompson 8798096cfafSDoug Thompson debugf0("%s() unregister this mci kobj\n", __func__); 8808096cfafSDoug Thompson 8818096cfafSDoug Thompson /* unregister this instance's kobject */ 882c10997f6SGreg Kroah-Hartman kobject_put(&mci->edac_mci_kobj); 8837c9281d7SDouglas Thompson } 8848096cfafSDoug Thompson 8858096cfafSDoug Thompson 8868096cfafSDoug Thompson 8878096cfafSDoug Thompson 8888096cfafSDoug Thompson /* 8898096cfafSDoug Thompson * edac_setup_sysfs_mc_kset(void) 8908096cfafSDoug Thompson * 8918096cfafSDoug Thompson * Initialize the mc_kset for the 'mc' entry 8928096cfafSDoug Thompson * This requires creating the top 'mc' directory with a kset 8938096cfafSDoug Thompson * and its controls/attributes. 8948096cfafSDoug Thompson * 8958096cfafSDoug Thompson * To this 'mc' kset, instance 'mci' will be grouped as children. 8968096cfafSDoug Thompson * 8978096cfafSDoug Thompson * Return: 0 SUCCESS 8988096cfafSDoug Thompson * !0 FAILURE error code 8998096cfafSDoug Thompson */ 9008096cfafSDoug Thompson int edac_sysfs_setup_mc_kset(void) 9018096cfafSDoug Thompson { 9028096cfafSDoug Thompson int err = 0; 9038096cfafSDoug Thompson struct sysdev_class *edac_class; 9048096cfafSDoug Thompson 9058096cfafSDoug Thompson debugf1("%s()\n", __func__); 9068096cfafSDoug Thompson 9078096cfafSDoug Thompson /* get the /sys/devices/system/edac class reference */ 9088096cfafSDoug Thompson edac_class = edac_get_edac_class(); 9098096cfafSDoug Thompson if (edac_class == NULL) { 9108096cfafSDoug Thompson debugf1("%s() no edac_class error=%d\n", __func__, err); 9118096cfafSDoug Thompson goto fail_out; 9128096cfafSDoug Thompson } 9138096cfafSDoug Thompson 9148096cfafSDoug Thompson /* Init the MC's kobject */ 915f9fc82adSArthur Jones mc_kset = kset_create_and_add("mc", NULL, &edac_class->kset.kobj); 916f9fc82adSArthur Jones if (!mc_kset) { 917f9fc82adSArthur Jones err = -ENOMEM; 9188096cfafSDoug Thompson debugf1("%s() Failed to register '.../edac/mc'\n", __func__); 9198096cfafSDoug Thompson goto fail_out; 9208096cfafSDoug Thompson } 9218096cfafSDoug Thompson 9228096cfafSDoug Thompson debugf1("%s() Registered '.../edac/mc' kobject\n", __func__); 9238096cfafSDoug Thompson 9248096cfafSDoug Thompson return 0; 9258096cfafSDoug Thompson 9268096cfafSDoug Thompson 9278096cfafSDoug Thompson /* error unwind stack */ 9288096cfafSDoug Thompson fail_out: 9298096cfafSDoug Thompson return err; 9308096cfafSDoug Thompson } 9318096cfafSDoug Thompson 9328096cfafSDoug Thompson /* 9338096cfafSDoug Thompson * edac_sysfs_teardown_mc_kset 9348096cfafSDoug Thompson * 9358096cfafSDoug Thompson * deconstruct the mc_ket for memory controllers 9368096cfafSDoug Thompson */ 9378096cfafSDoug Thompson void edac_sysfs_teardown_mc_kset(void) 9388096cfafSDoug Thompson { 939f9fc82adSArthur Jones kset_unregister(mc_kset); 9408096cfafSDoug Thompson } 9418096cfafSDoug Thompson 942