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> 135a0e3ad6STejun Heo #include <linux/slab.h> 148096cfafSDoug Thompson #include <linux/bug.h> 157c9281d7SDouglas Thompson 1620bcb7a8SDouglas Thompson #include "edac_core.h" 177c9281d7SDouglas Thompson #include "edac_module.h" 187c9281d7SDouglas Thompson 198096cfafSDoug Thompson 207c9281d7SDouglas Thompson /* MC EDAC Controls, setable by module parameter, and sysfs */ 214de78c68SDave Jiang static int edac_mc_log_ue = 1; 224de78c68SDave Jiang static int edac_mc_log_ce = 1; 23f044091cSDouglas Thompson static int edac_mc_panic_on_ue; 244de78c68SDave Jiang static int edac_mc_poll_msec = 1000; 257c9281d7SDouglas Thompson 267c9281d7SDouglas Thompson /* Getter functions for above */ 274de78c68SDave Jiang int edac_mc_get_log_ue(void) 287c9281d7SDouglas Thompson { 294de78c68SDave Jiang return edac_mc_log_ue; 307c9281d7SDouglas Thompson } 317c9281d7SDouglas Thompson 324de78c68SDave Jiang int edac_mc_get_log_ce(void) 337c9281d7SDouglas Thompson { 344de78c68SDave Jiang return edac_mc_log_ce; 357c9281d7SDouglas Thompson } 367c9281d7SDouglas Thompson 374de78c68SDave Jiang int edac_mc_get_panic_on_ue(void) 387c9281d7SDouglas Thompson { 394de78c68SDave Jiang return edac_mc_panic_on_ue; 407c9281d7SDouglas Thompson } 417c9281d7SDouglas Thompson 4281d87cb1SDave Jiang /* this is temporary */ 4381d87cb1SDave Jiang int edac_mc_get_poll_msec(void) 4481d87cb1SDave Jiang { 454de78c68SDave Jiang return edac_mc_poll_msec; 467c9281d7SDouglas Thompson } 477c9281d7SDouglas Thompson 48096846e2SArthur Jones static int edac_set_poll_msec(const char *val, struct kernel_param *kp) 49096846e2SArthur Jones { 50096846e2SArthur Jones long l; 51096846e2SArthur Jones int ret; 52096846e2SArthur Jones 53096846e2SArthur Jones if (!val) 54096846e2SArthur Jones return -EINVAL; 55096846e2SArthur Jones 56096846e2SArthur Jones ret = strict_strtol(val, 0, &l); 57096846e2SArthur Jones if (ret == -EINVAL || ((int)l != l)) 58096846e2SArthur Jones return -EINVAL; 59096846e2SArthur Jones *((int *)kp->arg) = l; 60096846e2SArthur Jones 61096846e2SArthur Jones /* notify edac_mc engine to reset the poll period */ 62096846e2SArthur Jones edac_mc_reset_delay_period(l); 63096846e2SArthur Jones 64096846e2SArthur Jones return 0; 65096846e2SArthur Jones } 66096846e2SArthur Jones 677c9281d7SDouglas Thompson /* Parameter declarations for above */ 684de78c68SDave Jiang module_param(edac_mc_panic_on_ue, int, 0644); 694de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); 704de78c68SDave Jiang module_param(edac_mc_log_ue, int, 0644); 714de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ue, 724de78c68SDave Jiang "Log uncorrectable error to console: 0=off 1=on"); 734de78c68SDave Jiang module_param(edac_mc_log_ce, int, 0644); 744de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ce, 754de78c68SDave Jiang "Log correctable error to console: 0=off 1=on"); 76096846e2SArthur Jones module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, 77096846e2SArthur Jones &edac_mc_poll_msec, 0644); 784de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); 797c9281d7SDouglas Thompson 807c9281d7SDouglas Thompson /* 817c9281d7SDouglas Thompson * various constants for Memory Controllers 827c9281d7SDouglas Thompson */ 837c9281d7SDouglas Thompson static const char *mem_types[] = { 847c9281d7SDouglas Thompson [MEM_EMPTY] = "Empty", 857c9281d7SDouglas Thompson [MEM_RESERVED] = "Reserved", 867c9281d7SDouglas Thompson [MEM_UNKNOWN] = "Unknown", 877c9281d7SDouglas Thompson [MEM_FPM] = "FPM", 887c9281d7SDouglas Thompson [MEM_EDO] = "EDO", 897c9281d7SDouglas Thompson [MEM_BEDO] = "BEDO", 907c9281d7SDouglas Thompson [MEM_SDR] = "Unbuffered-SDR", 917c9281d7SDouglas Thompson [MEM_RDR] = "Registered-SDR", 927c9281d7SDouglas Thompson [MEM_DDR] = "Unbuffered-DDR", 937c9281d7SDouglas Thompson [MEM_RDDR] = "Registered-DDR", 941a9b85e6SDave Jiang [MEM_RMBS] = "RMBS", 951a9b85e6SDave Jiang [MEM_DDR2] = "Unbuffered-DDR2", 961a9b85e6SDave Jiang [MEM_FB_DDR2] = "FullyBuffered-DDR2", 971d5f726cSBenjamin Herrenschmidt [MEM_RDDR2] = "Registered-DDR2", 98b1cfebc9SYang Shi [MEM_XDR] = "XDR", 99b1cfebc9SYang Shi [MEM_DDR3] = "Unbuffered-DDR3", 100b1cfebc9SYang Shi [MEM_RDDR3] = "Registered-DDR3" 1017c9281d7SDouglas Thompson }; 1027c9281d7SDouglas Thompson 1037c9281d7SDouglas Thompson static const char *dev_types[] = { 1047c9281d7SDouglas Thompson [DEV_UNKNOWN] = "Unknown", 1057c9281d7SDouglas Thompson [DEV_X1] = "x1", 1067c9281d7SDouglas Thompson [DEV_X2] = "x2", 1077c9281d7SDouglas Thompson [DEV_X4] = "x4", 1087c9281d7SDouglas Thompson [DEV_X8] = "x8", 1097c9281d7SDouglas Thompson [DEV_X16] = "x16", 1107c9281d7SDouglas Thompson [DEV_X32] = "x32", 1117c9281d7SDouglas Thompson [DEV_X64] = "x64" 1127c9281d7SDouglas Thompson }; 1137c9281d7SDouglas Thompson 1147c9281d7SDouglas Thompson static const char *edac_caps[] = { 1157c9281d7SDouglas Thompson [EDAC_UNKNOWN] = "Unknown", 1167c9281d7SDouglas Thompson [EDAC_NONE] = "None", 1177c9281d7SDouglas Thompson [EDAC_RESERVED] = "Reserved", 1187c9281d7SDouglas Thompson [EDAC_PARITY] = "PARITY", 1197c9281d7SDouglas Thompson [EDAC_EC] = "EC", 1207c9281d7SDouglas Thompson [EDAC_SECDED] = "SECDED", 1217c9281d7SDouglas Thompson [EDAC_S2ECD2ED] = "S2ECD2ED", 1227c9281d7SDouglas Thompson [EDAC_S4ECD4ED] = "S4ECD4ED", 1237c9281d7SDouglas Thompson [EDAC_S8ECD8ED] = "S8ECD8ED", 1247c9281d7SDouglas Thompson [EDAC_S16ECD16ED] = "S16ECD16ED" 1257c9281d7SDouglas Thompson }; 1267c9281d7SDouglas Thompson 1277c9281d7SDouglas Thompson 1287c9281d7SDouglas Thompson 1297c9281d7SDouglas Thompson static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) 1307c9281d7SDouglas Thompson { 1317c9281d7SDouglas Thompson int *value = (int *)ptr; 1327c9281d7SDouglas Thompson 1337c9281d7SDouglas Thompson if (isdigit(*buffer)) 1347c9281d7SDouglas Thompson *value = simple_strtoul(buffer, NULL, 0); 1357c9281d7SDouglas Thompson 1367c9281d7SDouglas Thompson return count; 1377c9281d7SDouglas Thompson } 1387c9281d7SDouglas Thompson 1397c9281d7SDouglas Thompson 1407c9281d7SDouglas Thompson /* EDAC sysfs CSROW data structures and methods 1417c9281d7SDouglas Thompson */ 1427c9281d7SDouglas Thompson 1437c9281d7SDouglas Thompson /* Set of more default csrow<id> attribute show/store functions */ 144e27e3dacSDouglas Thompson static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, 145e27e3dacSDouglas Thompson int private) 1467c9281d7SDouglas Thompson { 1477c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->ue_count); 1487c9281d7SDouglas Thompson } 1497c9281d7SDouglas Thompson 150e27e3dacSDouglas Thompson static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, 151e27e3dacSDouglas Thompson int private) 1527c9281d7SDouglas Thompson { 1537c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->ce_count); 1547c9281d7SDouglas Thompson } 1557c9281d7SDouglas Thompson 156e27e3dacSDouglas Thompson static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, 157e27e3dacSDouglas Thompson int private) 1587c9281d7SDouglas Thompson { 1597c9281d7SDouglas Thompson return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages)); 1607c9281d7SDouglas Thompson } 1617c9281d7SDouglas Thompson 162e27e3dacSDouglas Thompson static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, 163e27e3dacSDouglas Thompson int private) 1647c9281d7SDouglas Thompson { 1657c9281d7SDouglas Thompson return sprintf(data, "%s\n", mem_types[csrow->mtype]); 1667c9281d7SDouglas Thompson } 1677c9281d7SDouglas Thompson 168e27e3dacSDouglas Thompson static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, 169e27e3dacSDouglas Thompson int private) 1707c9281d7SDouglas Thompson { 1717c9281d7SDouglas Thompson return sprintf(data, "%s\n", dev_types[csrow->dtype]); 1727c9281d7SDouglas Thompson } 1737c9281d7SDouglas Thompson 174e27e3dacSDouglas Thompson static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, 175e27e3dacSDouglas Thompson int private) 1767c9281d7SDouglas Thompson { 1777c9281d7SDouglas Thompson return sprintf(data, "%s\n", edac_caps[csrow->edac_mode]); 1787c9281d7SDouglas Thompson } 1797c9281d7SDouglas Thompson 1807c9281d7SDouglas Thompson /* show/store functions for DIMM Label attributes */ 1817c9281d7SDouglas Thompson static ssize_t channel_dimm_label_show(struct csrow_info *csrow, 1827c9281d7SDouglas Thompson char *data, int channel) 1837c9281d7SDouglas Thompson { 184124682c7SArthur Jones /* if field has not been initialized, there is nothing to send */ 185124682c7SArthur Jones if (!csrow->channels[channel].label[0]) 186124682c7SArthur Jones return 0; 187124682c7SArthur Jones 188124682c7SArthur Jones return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", 1897c9281d7SDouglas Thompson csrow->channels[channel].label); 1907c9281d7SDouglas Thompson } 1917c9281d7SDouglas Thompson 1927c9281d7SDouglas Thompson static ssize_t channel_dimm_label_store(struct csrow_info *csrow, 1937c9281d7SDouglas Thompson const char *data, 194079708b9SDouglas Thompson size_t count, int channel) 1957c9281d7SDouglas Thompson { 1967c9281d7SDouglas Thompson ssize_t max_size = 0; 1977c9281d7SDouglas Thompson 1987c9281d7SDouglas Thompson max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); 1997c9281d7SDouglas Thompson strncpy(csrow->channels[channel].label, data, max_size); 2007c9281d7SDouglas Thompson csrow->channels[channel].label[max_size] = '\0'; 2017c9281d7SDouglas Thompson 2027c9281d7SDouglas Thompson return max_size; 2037c9281d7SDouglas Thompson } 2047c9281d7SDouglas Thompson 2057c9281d7SDouglas Thompson /* show function for dynamic chX_ce_count attribute */ 2067c9281d7SDouglas Thompson static ssize_t channel_ce_count_show(struct csrow_info *csrow, 207079708b9SDouglas Thompson char *data, int channel) 2087c9281d7SDouglas Thompson { 2097c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->channels[channel].ce_count); 2107c9281d7SDouglas Thompson } 2117c9281d7SDouglas Thompson 2127c9281d7SDouglas Thompson /* csrow specific attribute structure */ 2137c9281d7SDouglas Thompson struct csrowdev_attribute { 2147c9281d7SDouglas Thompson struct attribute attr; 2157c9281d7SDouglas Thompson ssize_t(*show) (struct csrow_info *, char *, int); 2167c9281d7SDouglas Thompson ssize_t(*store) (struct csrow_info *, const char *, size_t, int); 2177c9281d7SDouglas Thompson int private; 2187c9281d7SDouglas Thompson }; 2197c9281d7SDouglas Thompson 2207c9281d7SDouglas Thompson #define to_csrow(k) container_of(k, struct csrow_info, kobj) 2217c9281d7SDouglas Thompson #define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) 2227c9281d7SDouglas Thompson 2237c9281d7SDouglas Thompson /* Set of show/store higher level functions for default csrow attributes */ 2247c9281d7SDouglas Thompson static ssize_t csrowdev_show(struct kobject *kobj, 225079708b9SDouglas Thompson struct attribute *attr, char *buffer) 2267c9281d7SDouglas Thompson { 2277c9281d7SDouglas Thompson struct csrow_info *csrow = to_csrow(kobj); 2287c9281d7SDouglas Thompson struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); 2297c9281d7SDouglas Thompson 2307c9281d7SDouglas Thompson if (csrowdev_attr->show) 2317c9281d7SDouglas Thompson return csrowdev_attr->show(csrow, 232079708b9SDouglas Thompson buffer, csrowdev_attr->private); 2337c9281d7SDouglas Thompson return -EIO; 2347c9281d7SDouglas Thompson } 2357c9281d7SDouglas Thompson 2367c9281d7SDouglas Thompson static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, 2377c9281d7SDouglas Thompson const char *buffer, size_t count) 2387c9281d7SDouglas Thompson { 2397c9281d7SDouglas Thompson struct csrow_info *csrow = to_csrow(kobj); 2407c9281d7SDouglas Thompson struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); 2417c9281d7SDouglas Thompson 2427c9281d7SDouglas Thompson if (csrowdev_attr->store) 2437c9281d7SDouglas Thompson return csrowdev_attr->store(csrow, 2447c9281d7SDouglas Thompson buffer, 245079708b9SDouglas Thompson count, csrowdev_attr->private); 2467c9281d7SDouglas Thompson return -EIO; 2477c9281d7SDouglas Thompson } 2487c9281d7SDouglas Thompson 24952cf25d0SEmese Revfy static const struct sysfs_ops csrowfs_ops = { 2507c9281d7SDouglas Thompson .show = csrowdev_show, 2517c9281d7SDouglas Thompson .store = csrowdev_store 2527c9281d7SDouglas Thompson }; 2537c9281d7SDouglas Thompson 2547c9281d7SDouglas Thompson #define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ 2557c9281d7SDouglas Thompson static struct csrowdev_attribute attr_##_name = { \ 2567c9281d7SDouglas Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 2577c9281d7SDouglas Thompson .show = _show, \ 2587c9281d7SDouglas Thompson .store = _store, \ 2597c9281d7SDouglas Thompson .private = _private, \ 2607c9281d7SDouglas Thompson }; 2617c9281d7SDouglas Thompson 2627c9281d7SDouglas Thompson /* default cwrow<id>/attribute files */ 2637c9281d7SDouglas Thompson CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0); 2647c9281d7SDouglas Thompson CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0); 2657c9281d7SDouglas Thompson CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0); 2667c9281d7SDouglas Thompson CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0); 2677c9281d7SDouglas Thompson CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0); 2687c9281d7SDouglas Thompson CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0); 2697c9281d7SDouglas Thompson 2707c9281d7SDouglas Thompson /* default attributes of the CSROW<id> object */ 2717c9281d7SDouglas Thompson static struct csrowdev_attribute *default_csrow_attr[] = { 2727c9281d7SDouglas Thompson &attr_dev_type, 2737c9281d7SDouglas Thompson &attr_mem_type, 2747c9281d7SDouglas Thompson &attr_edac_mode, 2757c9281d7SDouglas Thompson &attr_size_mb, 2767c9281d7SDouglas Thompson &attr_ue_count, 2777c9281d7SDouglas Thompson &attr_ce_count, 2787c9281d7SDouglas Thompson NULL, 2797c9281d7SDouglas Thompson }; 2807c9281d7SDouglas Thompson 2817c9281d7SDouglas Thompson /* possible dynamic channel DIMM Label attribute files */ 2827c9281d7SDouglas Thompson CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR, 283079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 0); 2847c9281d7SDouglas Thompson CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR, 285079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 1); 2867c9281d7SDouglas Thompson CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR, 287079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 2); 2887c9281d7SDouglas Thompson CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR, 289079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 3); 2907c9281d7SDouglas Thompson CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR, 291079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 4); 2927c9281d7SDouglas Thompson CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR, 293079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 5); 2947c9281d7SDouglas Thompson 2957c9281d7SDouglas Thompson /* Total possible dynamic DIMM Label attribute file table */ 2967c9281d7SDouglas Thompson static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { 2977c9281d7SDouglas Thompson &attr_ch0_dimm_label, 2987c9281d7SDouglas Thompson &attr_ch1_dimm_label, 2997c9281d7SDouglas Thompson &attr_ch2_dimm_label, 3007c9281d7SDouglas Thompson &attr_ch3_dimm_label, 3017c9281d7SDouglas Thompson &attr_ch4_dimm_label, 3027c9281d7SDouglas Thompson &attr_ch5_dimm_label 3037c9281d7SDouglas Thompson }; 3047c9281d7SDouglas Thompson 3057c9281d7SDouglas Thompson /* possible dynamic channel ce_count attribute files */ 306079708b9SDouglas Thompson CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0); 307079708b9SDouglas Thompson CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1); 308079708b9SDouglas Thompson CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2); 309079708b9SDouglas Thompson CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3); 310079708b9SDouglas Thompson CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4); 311079708b9SDouglas Thompson CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5); 3127c9281d7SDouglas Thompson 3137c9281d7SDouglas Thompson /* Total possible dynamic ce_count attribute file table */ 3147c9281d7SDouglas Thompson static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { 3157c9281d7SDouglas Thompson &attr_ch0_ce_count, 3167c9281d7SDouglas Thompson &attr_ch1_ce_count, 3177c9281d7SDouglas Thompson &attr_ch2_ce_count, 3187c9281d7SDouglas Thompson &attr_ch3_ce_count, 3197c9281d7SDouglas Thompson &attr_ch4_ce_count, 3207c9281d7SDouglas Thompson &attr_ch5_ce_count 3217c9281d7SDouglas Thompson }; 3227c9281d7SDouglas Thompson 3237c9281d7SDouglas Thompson #define EDAC_NR_CHANNELS 6 3247c9281d7SDouglas Thompson 3257c9281d7SDouglas Thompson /* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ 3267c9281d7SDouglas Thompson static int edac_create_channel_files(struct kobject *kobj, int chan) 3277c9281d7SDouglas Thompson { 3287c9281d7SDouglas Thompson int err = -ENODEV; 3297c9281d7SDouglas Thompson 3307c9281d7SDouglas Thompson if (chan >= EDAC_NR_CHANNELS) 3317c9281d7SDouglas Thompson return err; 3327c9281d7SDouglas Thompson 3337c9281d7SDouglas Thompson /* create the DIMM label attribute file */ 3347c9281d7SDouglas Thompson err = sysfs_create_file(kobj, 335079708b9SDouglas Thompson (struct attribute *) 336079708b9SDouglas Thompson dynamic_csrow_dimm_attr[chan]); 3377c9281d7SDouglas Thompson 3387c9281d7SDouglas Thompson if (!err) { 3397c9281d7SDouglas Thompson /* create the CE Count attribute file */ 3407c9281d7SDouglas Thompson err = sysfs_create_file(kobj, 341079708b9SDouglas Thompson (struct attribute *) 342079708b9SDouglas Thompson dynamic_csrow_ce_count_attr[chan]); 3437c9281d7SDouglas Thompson } else { 344e27e3dacSDouglas Thompson debugf1("%s() dimm labels and ce_count files created", 345e27e3dacSDouglas Thompson __func__); 3467c9281d7SDouglas Thompson } 3477c9281d7SDouglas Thompson 3487c9281d7SDouglas Thompson return err; 3497c9281d7SDouglas Thompson } 3507c9281d7SDouglas Thompson 3517c9281d7SDouglas Thompson /* No memory to release for this kobj */ 3527c9281d7SDouglas Thompson static void edac_csrow_instance_release(struct kobject *kobj) 3537c9281d7SDouglas Thompson { 3548096cfafSDoug Thompson struct mem_ctl_info *mci; 3557c9281d7SDouglas Thompson struct csrow_info *cs; 3567c9281d7SDouglas Thompson 3578096cfafSDoug Thompson debugf1("%s()\n", __func__); 3588096cfafSDoug Thompson 3597c9281d7SDouglas Thompson cs = container_of(kobj, struct csrow_info, kobj); 3608096cfafSDoug Thompson mci = cs->mci; 3618096cfafSDoug Thompson 3628096cfafSDoug Thompson kobject_put(&mci->edac_mci_kobj); 3637c9281d7SDouglas Thompson } 3647c9281d7SDouglas Thompson 3657c9281d7SDouglas Thompson /* the kobj_type instance for a CSROW */ 3667c9281d7SDouglas Thompson static struct kobj_type ktype_csrow = { 3677c9281d7SDouglas Thompson .release = edac_csrow_instance_release, 3687c9281d7SDouglas Thompson .sysfs_ops = &csrowfs_ops, 3697c9281d7SDouglas Thompson .default_attrs = (struct attribute **)default_csrow_attr, 3707c9281d7SDouglas Thompson }; 3717c9281d7SDouglas Thompson 3727c9281d7SDouglas Thompson /* Create a CSROW object under specifed edac_mc_device */ 3738096cfafSDoug Thompson static int edac_create_csrow_object(struct mem_ctl_info *mci, 374079708b9SDouglas Thompson struct csrow_info *csrow, int index) 3757c9281d7SDouglas Thompson { 3768096cfafSDoug Thompson struct kobject *kobj_mci = &mci->edac_mci_kobj; 3778096cfafSDoug Thompson struct kobject *kobj; 3787c9281d7SDouglas Thompson int chan; 3798096cfafSDoug Thompson int err; 3807c9281d7SDouglas Thompson 3817c9281d7SDouglas Thompson /* generate ..../edac/mc/mc<id>/csrow<index> */ 3828096cfafSDoug Thompson memset(&csrow->kobj, 0, sizeof(csrow->kobj)); 3838096cfafSDoug Thompson csrow->mci = mci; /* include container up link */ 3848096cfafSDoug Thompson 3858096cfafSDoug Thompson /* bump the mci instance's kobject's ref count */ 3868096cfafSDoug Thompson kobj = kobject_get(&mci->edac_mci_kobj); 3878096cfafSDoug Thompson if (!kobj) { 3888096cfafSDoug Thompson err = -ENODEV; 3898096cfafSDoug Thompson goto err_out; 3908096cfafSDoug Thompson } 3917c9281d7SDouglas Thompson 3927c9281d7SDouglas Thompson /* Instanstiate the csrow object */ 393b2ed215aSGreg Kroah-Hartman err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci, 394b2ed215aSGreg Kroah-Hartman "csrow%d", index); 3958096cfafSDoug Thompson if (err) 3968096cfafSDoug Thompson goto err_release_top_kobj; 3978096cfafSDoug Thompson 3988096cfafSDoug Thompson /* At this point, to release a csrow kobj, one must 399c10997f6SGreg Kroah-Hartman * call the kobject_put and allow that tear down 4008096cfafSDoug Thompson * to work the releasing 4018096cfafSDoug Thompson */ 4028096cfafSDoug Thompson 4037c9281d7SDouglas Thompson /* Create the dyanmic attribute files on this csrow, 4047c9281d7SDouglas Thompson * namely, the DIMM labels and the channel ce_count 4057c9281d7SDouglas Thompson */ 4067c9281d7SDouglas Thompson for (chan = 0; chan < csrow->nr_channels; chan++) { 4077c9281d7SDouglas Thompson err = edac_create_channel_files(&csrow->kobj, chan); 4088096cfafSDoug Thompson if (err) { 4098096cfafSDoug Thompson /* special case the unregister here */ 410c10997f6SGreg Kroah-Hartman kobject_put(&csrow->kobj); 4118096cfafSDoug Thompson goto err_out; 4127c9281d7SDouglas Thompson } 4137c9281d7SDouglas Thompson } 414b2ed215aSGreg Kroah-Hartman kobject_uevent(&csrow->kobj, KOBJ_ADD); 4158096cfafSDoug Thompson return 0; 4168096cfafSDoug Thompson 4178096cfafSDoug Thompson /* error unwind stack */ 4188096cfafSDoug Thompson err_release_top_kobj: 4198096cfafSDoug Thompson kobject_put(&mci->edac_mci_kobj); 4208096cfafSDoug Thompson 4218096cfafSDoug Thompson err_out: 4227c9281d7SDouglas Thompson return err; 4237c9281d7SDouglas Thompson } 4247c9281d7SDouglas Thompson 4257c9281d7SDouglas Thompson /* default sysfs methods and data structures for the main MCI kobject */ 4267c9281d7SDouglas Thompson 4277c9281d7SDouglas Thompson static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, 4287c9281d7SDouglas Thompson const char *data, size_t count) 4297c9281d7SDouglas Thompson { 4307c9281d7SDouglas Thompson int row, chan; 4317c9281d7SDouglas Thompson 4327c9281d7SDouglas Thompson mci->ue_noinfo_count = 0; 4337c9281d7SDouglas Thompson mci->ce_noinfo_count = 0; 4347c9281d7SDouglas Thompson mci->ue_count = 0; 4357c9281d7SDouglas Thompson mci->ce_count = 0; 4367c9281d7SDouglas Thompson 4377c9281d7SDouglas Thompson for (row = 0; row < mci->nr_csrows; row++) { 4387c9281d7SDouglas Thompson struct csrow_info *ri = &mci->csrows[row]; 4397c9281d7SDouglas Thompson 4407c9281d7SDouglas Thompson ri->ue_count = 0; 4417c9281d7SDouglas Thompson ri->ce_count = 0; 4427c9281d7SDouglas Thompson 4437c9281d7SDouglas Thompson for (chan = 0; chan < ri->nr_channels; chan++) 4447c9281d7SDouglas Thompson ri->channels[chan].ce_count = 0; 4457c9281d7SDouglas Thompson } 4467c9281d7SDouglas Thompson 4477c9281d7SDouglas Thompson mci->start_time = jiffies; 4487c9281d7SDouglas Thompson return count; 4497c9281d7SDouglas Thompson } 4507c9281d7SDouglas Thompson 4517c9281d7SDouglas Thompson /* memory scrubbing */ 4527c9281d7SDouglas Thompson static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, 4537c9281d7SDouglas Thompson const char *data, size_t count) 4547c9281d7SDouglas Thompson { 4557c9281d7SDouglas Thompson u32 bandwidth = -1; 4567c9281d7SDouglas Thompson 4577c9281d7SDouglas Thompson if (mci->set_sdram_scrub_rate) { 4587c9281d7SDouglas Thompson 4597c9281d7SDouglas Thompson memctrl_int_store(&bandwidth, data, count); 4607c9281d7SDouglas Thompson 4617c9281d7SDouglas Thompson if (!(*mci->set_sdram_scrub_rate) (mci, &bandwidth)) { 4627c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4637c9281d7SDouglas Thompson "Scrub rate set successfully, applied: %d\n", 4647c9281d7SDouglas Thompson bandwidth); 4657c9281d7SDouglas Thompson } else { 4667c9281d7SDouglas Thompson /* FIXME: error codes maybe? */ 4677c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4687c9281d7SDouglas Thompson "Scrub rate set FAILED, could not apply: %d\n", 4697c9281d7SDouglas Thompson bandwidth); 4707c9281d7SDouglas Thompson } 4717c9281d7SDouglas Thompson } else { 4727c9281d7SDouglas Thompson /* FIXME: produce "not implemented" ERROR for user-side. */ 4737c9281d7SDouglas Thompson edac_printk(KERN_WARNING, EDAC_MC, 4747c9281d7SDouglas Thompson "Memory scrubbing 'set'control is not implemented!\n"); 4757c9281d7SDouglas Thompson } 4767c9281d7SDouglas Thompson return count; 4777c9281d7SDouglas Thompson } 4787c9281d7SDouglas Thompson 4797c9281d7SDouglas Thompson static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) 4807c9281d7SDouglas Thompson { 4817c9281d7SDouglas Thompson u32 bandwidth = -1; 4827c9281d7SDouglas Thompson 4837c9281d7SDouglas Thompson if (mci->get_sdram_scrub_rate) { 4847c9281d7SDouglas Thompson if (!(*mci->get_sdram_scrub_rate) (mci, &bandwidth)) { 4857c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4867c9281d7SDouglas Thompson "Scrub rate successfully, fetched: %d\n", 4877c9281d7SDouglas Thompson bandwidth); 4887c9281d7SDouglas Thompson } else { 4897c9281d7SDouglas Thompson /* FIXME: error codes maybe? */ 4907c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4917c9281d7SDouglas Thompson "Scrub rate fetch FAILED, got: %d\n", 4927c9281d7SDouglas Thompson bandwidth); 4937c9281d7SDouglas Thompson } 4947c9281d7SDouglas Thompson } else { 4957c9281d7SDouglas Thompson /* FIXME: produce "not implemented" ERROR for user-side. */ 4967c9281d7SDouglas Thompson edac_printk(KERN_WARNING, EDAC_MC, 497e27e3dacSDouglas Thompson "Memory scrubbing 'get' control is not implemented\n"); 4987c9281d7SDouglas Thompson } 4997c9281d7SDouglas Thompson return sprintf(data, "%d\n", bandwidth); 5007c9281d7SDouglas Thompson } 5017c9281d7SDouglas Thompson 5027c9281d7SDouglas Thompson /* default attribute files for the MCI object */ 5037c9281d7SDouglas Thompson static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) 5047c9281d7SDouglas Thompson { 5057c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ue_count); 5067c9281d7SDouglas Thompson } 5077c9281d7SDouglas Thompson 5087c9281d7SDouglas Thompson static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) 5097c9281d7SDouglas Thompson { 5107c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ce_count); 5117c9281d7SDouglas Thompson } 5127c9281d7SDouglas Thompson 5137c9281d7SDouglas Thompson static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) 5147c9281d7SDouglas Thompson { 5157c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ce_noinfo_count); 5167c9281d7SDouglas Thompson } 5177c9281d7SDouglas Thompson 5187c9281d7SDouglas Thompson static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) 5197c9281d7SDouglas Thompson { 5207c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ue_noinfo_count); 5217c9281d7SDouglas Thompson } 5227c9281d7SDouglas Thompson 5237c9281d7SDouglas Thompson static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) 5247c9281d7SDouglas Thompson { 5257c9281d7SDouglas Thompson return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ); 5267c9281d7SDouglas Thompson } 5277c9281d7SDouglas Thompson 5287c9281d7SDouglas Thompson static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) 5297c9281d7SDouglas Thompson { 5307c9281d7SDouglas Thompson return sprintf(data, "%s\n", mci->ctl_name); 5317c9281d7SDouglas Thompson } 5327c9281d7SDouglas Thompson 5337c9281d7SDouglas Thompson static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) 5347c9281d7SDouglas Thompson { 5357c9281d7SDouglas Thompson int total_pages, csrow_idx; 5367c9281d7SDouglas Thompson 5377c9281d7SDouglas Thompson for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows; 5387c9281d7SDouglas Thompson csrow_idx++) { 5397c9281d7SDouglas Thompson struct csrow_info *csrow = &mci->csrows[csrow_idx]; 5407c9281d7SDouglas Thompson 5417c9281d7SDouglas Thompson if (!csrow->nr_pages) 5427c9281d7SDouglas Thompson continue; 5437c9281d7SDouglas Thompson 5447c9281d7SDouglas Thompson total_pages += csrow->nr_pages; 5457c9281d7SDouglas Thompson } 5467c9281d7SDouglas Thompson 5477c9281d7SDouglas Thompson return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); 5487c9281d7SDouglas Thompson } 5497c9281d7SDouglas Thompson 5507c9281d7SDouglas Thompson #define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) 55142a8e397SDouglas Thompson #define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr) 5527c9281d7SDouglas Thompson 5537c9281d7SDouglas Thompson /* MCI show/store functions for top most object */ 5547c9281d7SDouglas Thompson static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, 5557c9281d7SDouglas Thompson char *buffer) 5567c9281d7SDouglas Thompson { 5577c9281d7SDouglas Thompson struct mem_ctl_info *mem_ctl_info = to_mci(kobj); 55842a8e397SDouglas Thompson struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 5597c9281d7SDouglas Thompson 5607c9281d7SDouglas Thompson if (mcidev_attr->show) 5617c9281d7SDouglas Thompson return mcidev_attr->show(mem_ctl_info, buffer); 5627c9281d7SDouglas Thompson 5637c9281d7SDouglas Thompson return -EIO; 5647c9281d7SDouglas Thompson } 5657c9281d7SDouglas Thompson 5667c9281d7SDouglas Thompson static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, 5677c9281d7SDouglas Thompson const char *buffer, size_t count) 5687c9281d7SDouglas Thompson { 5697c9281d7SDouglas Thompson struct mem_ctl_info *mem_ctl_info = to_mci(kobj); 57042a8e397SDouglas Thompson struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 5717c9281d7SDouglas Thompson 5727c9281d7SDouglas Thompson if (mcidev_attr->store) 5737c9281d7SDouglas Thompson return mcidev_attr->store(mem_ctl_info, buffer, count); 5747c9281d7SDouglas Thompson 5757c9281d7SDouglas Thompson return -EIO; 5767c9281d7SDouglas Thompson } 5777c9281d7SDouglas Thompson 5788096cfafSDoug Thompson /* Intermediate show/store table */ 57952cf25d0SEmese Revfy static const struct sysfs_ops mci_ops = { 5807c9281d7SDouglas Thompson .show = mcidev_show, 5817c9281d7SDouglas Thompson .store = mcidev_store 5827c9281d7SDouglas Thompson }; 5837c9281d7SDouglas Thompson 5847c9281d7SDouglas Thompson #define MCIDEV_ATTR(_name,_mode,_show,_store) \ 58542a8e397SDouglas Thompson static struct mcidev_sysfs_attribute mci_attr_##_name = { \ 5867c9281d7SDouglas Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 5877c9281d7SDouglas Thompson .show = _show, \ 5887c9281d7SDouglas Thompson .store = _store, \ 5897c9281d7SDouglas Thompson }; 5907c9281d7SDouglas Thompson 5917c9281d7SDouglas Thompson /* default Control file */ 5927c9281d7SDouglas Thompson MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); 5937c9281d7SDouglas Thompson 5947c9281d7SDouglas Thompson /* default Attribute files */ 5957c9281d7SDouglas Thompson MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); 5967c9281d7SDouglas Thompson MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); 5977c9281d7SDouglas Thompson MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); 5987c9281d7SDouglas Thompson MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); 5997c9281d7SDouglas Thompson MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); 6007c9281d7SDouglas Thompson MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); 6017c9281d7SDouglas Thompson MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); 6027c9281d7SDouglas Thompson 6037c9281d7SDouglas Thompson /* memory scrubber attribute file */ 604079708b9SDouglas Thompson MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, 605e27e3dacSDouglas Thompson mci_sdram_scrub_rate_store); 6067c9281d7SDouglas Thompson 60742a8e397SDouglas Thompson static struct mcidev_sysfs_attribute *mci_attr[] = { 6087c9281d7SDouglas Thompson &mci_attr_reset_counters, 6097c9281d7SDouglas Thompson &mci_attr_mc_name, 6107c9281d7SDouglas Thompson &mci_attr_size_mb, 6117c9281d7SDouglas Thompson &mci_attr_seconds_since_reset, 6127c9281d7SDouglas Thompson &mci_attr_ue_noinfo_count, 6137c9281d7SDouglas Thompson &mci_attr_ce_noinfo_count, 6147c9281d7SDouglas Thompson &mci_attr_ue_count, 6157c9281d7SDouglas Thompson &mci_attr_ce_count, 6167c9281d7SDouglas Thompson &mci_attr_sdram_scrub_rate, 6177c9281d7SDouglas Thompson NULL 6187c9281d7SDouglas Thompson }; 6197c9281d7SDouglas Thompson 6208096cfafSDoug Thompson 6217c9281d7SDouglas Thompson /* 6227c9281d7SDouglas Thompson * Release of a MC controlling instance 6238096cfafSDoug Thompson * 6248096cfafSDoug Thompson * each MC control instance has the following resources upon entry: 6258096cfafSDoug Thompson * a) a ref count on the top memctl kobj 6268096cfafSDoug Thompson * b) a ref count on this module 6278096cfafSDoug Thompson * 6288096cfafSDoug Thompson * this function must decrement those ref counts and then 6298096cfafSDoug Thompson * issue a free on the instance's memory 6307c9281d7SDouglas Thompson */ 6318096cfafSDoug Thompson static void edac_mci_control_release(struct kobject *kobj) 6327c9281d7SDouglas Thompson { 6337c9281d7SDouglas Thompson struct mem_ctl_info *mci; 6347c9281d7SDouglas Thompson 6357c9281d7SDouglas Thompson mci = to_mci(kobj); 6368096cfafSDoug Thompson 6378096cfafSDoug Thompson debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx); 6388096cfafSDoug Thompson 6398096cfafSDoug Thompson /* decrement the module ref count */ 6408096cfafSDoug Thompson module_put(mci->owner); 6418096cfafSDoug Thompson 6428096cfafSDoug Thompson /* free the mci instance memory here */ 6438096cfafSDoug Thompson kfree(mci); 6447c9281d7SDouglas Thompson } 6457c9281d7SDouglas Thompson 6467c9281d7SDouglas Thompson static struct kobj_type ktype_mci = { 6478096cfafSDoug Thompson .release = edac_mci_control_release, 6487c9281d7SDouglas Thompson .sysfs_ops = &mci_ops, 6497c9281d7SDouglas Thompson .default_attrs = (struct attribute **)mci_attr, 6507c9281d7SDouglas Thompson }; 6517c9281d7SDouglas Thompson 6528096cfafSDoug Thompson /* EDAC memory controller sysfs kset: 6538096cfafSDoug Thompson * /sys/devices/system/edac/mc 6548096cfafSDoug Thompson */ 655f9fc82adSArthur Jones static struct kset *mc_kset; 6568096cfafSDoug Thompson 6578096cfafSDoug Thompson /* 6588096cfafSDoug Thompson * edac_mc_register_sysfs_main_kobj 6598096cfafSDoug Thompson * 6608096cfafSDoug Thompson * setups and registers the main kobject for each mci 6618096cfafSDoug Thompson */ 6628096cfafSDoug Thompson int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci) 6638096cfafSDoug Thompson { 6648096cfafSDoug Thompson struct kobject *kobj_mci; 6658096cfafSDoug Thompson int err; 6668096cfafSDoug Thompson 6678096cfafSDoug Thompson debugf1("%s()\n", __func__); 6688096cfafSDoug Thompson 6698096cfafSDoug Thompson kobj_mci = &mci->edac_mci_kobj; 6708096cfafSDoug Thompson 6718096cfafSDoug Thompson /* Init the mci's kobject */ 6728096cfafSDoug Thompson memset(kobj_mci, 0, sizeof(*kobj_mci)); 6738096cfafSDoug Thompson 6748096cfafSDoug Thompson /* Record which module 'owns' this control structure 6758096cfafSDoug Thompson * and bump the ref count of the module 6768096cfafSDoug Thompson */ 6778096cfafSDoug Thompson mci->owner = THIS_MODULE; 6788096cfafSDoug Thompson 6798096cfafSDoug Thompson /* bump ref count on this module */ 6808096cfafSDoug Thompson if (!try_module_get(mci->owner)) { 6818096cfafSDoug Thompson err = -ENODEV; 6828096cfafSDoug Thompson goto fail_out; 6838096cfafSDoug Thompson } 6848096cfafSDoug Thompson 685b2ed215aSGreg Kroah-Hartman /* this instance become part of the mc_kset */ 686f9fc82adSArthur Jones kobj_mci->kset = mc_kset; 687b2ed215aSGreg Kroah-Hartman 6888096cfafSDoug Thompson /* register the mc<id> kobject to the mc_kset */ 689b2ed215aSGreg Kroah-Hartman err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL, 690b2ed215aSGreg Kroah-Hartman "mc%d", mci->mc_idx); 6918096cfafSDoug Thompson if (err) { 6928096cfafSDoug Thompson debugf1("%s()Failed to register '.../edac/mc%d'\n", 6938096cfafSDoug Thompson __func__, mci->mc_idx); 6948096cfafSDoug Thompson goto kobj_reg_fail; 6958096cfafSDoug Thompson } 696b2ed215aSGreg Kroah-Hartman kobject_uevent(kobj_mci, KOBJ_ADD); 6978096cfafSDoug Thompson 6988096cfafSDoug Thompson /* At this point, to 'free' the control struct, 6998096cfafSDoug Thompson * edac_mc_unregister_sysfs_main_kobj() must be used 7008096cfafSDoug Thompson */ 7018096cfafSDoug Thompson 7028096cfafSDoug Thompson debugf1("%s() Registered '.../edac/mc%d' kobject\n", 7038096cfafSDoug Thompson __func__, mci->mc_idx); 7048096cfafSDoug Thompson 7058096cfafSDoug Thompson return 0; 7068096cfafSDoug Thompson 7078096cfafSDoug Thompson /* Error exit stack */ 7088096cfafSDoug Thompson 7098096cfafSDoug Thompson kobj_reg_fail: 7108096cfafSDoug Thompson module_put(mci->owner); 7118096cfafSDoug Thompson 7128096cfafSDoug Thompson fail_out: 7138096cfafSDoug Thompson return err; 7148096cfafSDoug Thompson } 7158096cfafSDoug Thompson 7168096cfafSDoug Thompson /* 7178096cfafSDoug Thompson * edac_mc_register_sysfs_main_kobj 7188096cfafSDoug Thompson * 7198096cfafSDoug Thompson * tears down and the main mci kobject from the mc_kset 7208096cfafSDoug Thompson */ 7218096cfafSDoug Thompson void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) 7228096cfafSDoug Thompson { 7238096cfafSDoug Thompson /* delete the kobj from the mc_kset */ 724c10997f6SGreg Kroah-Hartman kobject_put(&mci->edac_mci_kobj); 7258096cfafSDoug Thompson } 7268096cfafSDoug Thompson 7277c9281d7SDouglas Thompson #define EDAC_DEVICE_SYMLINK "device" 7287c9281d7SDouglas Thompson 7297c9281d7SDouglas Thompson /* 7308096cfafSDoug Thompson * edac_create_mci_instance_attributes 7319fa2fc2eSMauro Carvalho Chehab * create MC driver specific attributes bellow an specified kobj 7329fa2fc2eSMauro Carvalho Chehab * This routine calls itself recursively, in order to create an entire 7339fa2fc2eSMauro Carvalho Chehab * object tree. 73442a8e397SDouglas Thompson */ 7359fa2fc2eSMauro Carvalho Chehab static int edac_create_mci_instance_attributes( 7369fa2fc2eSMauro Carvalho Chehab struct mcidev_sysfs_attribute *sysfs_attrib, 7379fa2fc2eSMauro Carvalho Chehab struct kobject *kobj) 73842a8e397SDouglas Thompson { 73942a8e397SDouglas Thompson int err; 74042a8e397SDouglas Thompson 7419fa2fc2eSMauro Carvalho Chehab while (sysfs_attrib) { 7429fa2fc2eSMauro Carvalho Chehab if (sysfs_attrib->grp) { 7439fa2fc2eSMauro Carvalho Chehab struct kobject *newkobj = &sysfs_attrib->grp->kobj; 7449fa2fc2eSMauro Carvalho Chehab debugf0("%s() grp %s\n", __func__, 7459fa2fc2eSMauro Carvalho Chehab sysfs_attrib->grp->name); 74642a8e397SDouglas Thompson 7479fa2fc2eSMauro Carvalho Chehab err = kobject_init_and_add(newkobj, NULL, 7489fa2fc2eSMauro Carvalho Chehab kobj, 7499fa2fc2eSMauro Carvalho Chehab sysfs_attrib->grp->name); 7509fa2fc2eSMauro Carvalho Chehab if (err) 7519fa2fc2eSMauro Carvalho Chehab return err; 7529fa2fc2eSMauro Carvalho Chehab 7539fa2fc2eSMauro Carvalho Chehab err = edac_create_mci_instance_attributes( 7549fa2fc2eSMauro Carvalho Chehab sysfs_attrib->grp->mcidev_attr, newkobj); 7559fa2fc2eSMauro Carvalho Chehab if (err) 7569fa2fc2eSMauro Carvalho Chehab return err; 7579fa2fc2eSMauro Carvalho Chehab } else if (sysfs_attrib->attr.name) { 7589fa2fc2eSMauro Carvalho Chehab debugf0("%s() file %s\n", __func__, 7599fa2fc2eSMauro Carvalho Chehab sysfs_attrib->attr.name); 7609fa2fc2eSMauro Carvalho Chehab 7619fa2fc2eSMauro Carvalho Chehab err = sysfs_create_file(kobj, &sysfs_attrib->attr); 7629fa2fc2eSMauro Carvalho Chehab } else 7639fa2fc2eSMauro Carvalho Chehab break; 7649fa2fc2eSMauro Carvalho Chehab 76542a8e397SDouglas Thompson if (err) { 76642a8e397SDouglas Thompson return err; 76742a8e397SDouglas Thompson } 76842a8e397SDouglas Thompson sysfs_attrib++; 76942a8e397SDouglas Thompson } 77042a8e397SDouglas Thompson 77142a8e397SDouglas Thompson return 0; 77242a8e397SDouglas Thompson } 77342a8e397SDouglas Thompson 77442a8e397SDouglas Thompson /* 7758096cfafSDoug Thompson * edac_remove_mci_instance_attributes 7768096cfafSDoug Thompson * remove MC driver specific attributes at the topmost level 7778096cfafSDoug Thompson * directory of this mci instance. 7788096cfafSDoug Thompson */ 7799fa2fc2eSMauro Carvalho Chehab static void edac_remove_mci_instance_attributes( 7809fa2fc2eSMauro Carvalho Chehab struct mcidev_sysfs_attribute *sysfs_attrib, 7819fa2fc2eSMauro Carvalho Chehab struct kobject *kobj) 7828096cfafSDoug Thompson { 7838096cfafSDoug Thompson /* loop if there are attributes and until we hit a NULL entry */ 7849fa2fc2eSMauro Carvalho Chehab while (sysfs_attrib) { 7859fa2fc2eSMauro Carvalho Chehab if (sysfs_attrib->grp) { 7869fa2fc2eSMauro Carvalho Chehab struct kobject *newkobj = &sysfs_attrib->grp->kobj; 7879fa2fc2eSMauro Carvalho Chehab 7889fa2fc2eSMauro Carvalho Chehab debugf0("%s() grp %s\n", __func__, 7899fa2fc2eSMauro Carvalho Chehab sysfs_attrib->grp->name); 7909fa2fc2eSMauro Carvalho Chehab 7919fa2fc2eSMauro Carvalho Chehab edac_remove_mci_instance_attributes( 7929fa2fc2eSMauro Carvalho Chehab sysfs_attrib->grp->mcidev_attr, newkobj); 7939fa2fc2eSMauro Carvalho Chehab 7949fa2fc2eSMauro Carvalho Chehab kobject_put(newkobj); 7959fa2fc2eSMauro Carvalho Chehab } else if (sysfs_attrib->attr.name) { 7969fa2fc2eSMauro Carvalho Chehab debugf0("%s() file %s\n", __func__, 7979fa2fc2eSMauro Carvalho Chehab sysfs_attrib->attr.name); 7989fa2fc2eSMauro Carvalho Chehab sysfs_remove_file(kobj, &sysfs_attrib->attr); 7999fa2fc2eSMauro Carvalho Chehab } else 8009fa2fc2eSMauro Carvalho Chehab break; 8018096cfafSDoug Thompson sysfs_attrib++; 8028096cfafSDoug Thompson } 8038096cfafSDoug Thompson } 8048096cfafSDoug Thompson 8058096cfafSDoug Thompson 8068096cfafSDoug Thompson /* 8077c9281d7SDouglas Thompson * Create a new Memory Controller kobject instance, 8087c9281d7SDouglas Thompson * mc<id> under the 'mc' directory 8097c9281d7SDouglas Thompson * 8107c9281d7SDouglas Thompson * Return: 8117c9281d7SDouglas Thompson * 0 Success 8127c9281d7SDouglas Thompson * !0 Failure 8137c9281d7SDouglas Thompson */ 8147c9281d7SDouglas Thompson int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) 8157c9281d7SDouglas Thompson { 8167c9281d7SDouglas Thompson int i; 8177c9281d7SDouglas Thompson int err; 8187c9281d7SDouglas Thompson struct csrow_info *csrow; 8198096cfafSDoug Thompson struct kobject *kobj_mci = &mci->edac_mci_kobj; 8207c9281d7SDouglas Thompson 8217c9281d7SDouglas Thompson debugf0("%s() idx=%d\n", __func__, mci->mc_idx); 8227c9281d7SDouglas Thompson 8237c9281d7SDouglas Thompson /* create a symlink for the device */ 8248096cfafSDoug Thompson err = sysfs_create_link(kobj_mci, &mci->dev->kobj, 8257c9281d7SDouglas Thompson EDAC_DEVICE_SYMLINK); 8268096cfafSDoug Thompson if (err) { 8278096cfafSDoug Thompson debugf1("%s() failure to create symlink\n", __func__); 8287c9281d7SDouglas Thompson goto fail0; 8298096cfafSDoug Thompson } 8307c9281d7SDouglas Thompson 83142a8e397SDouglas Thompson /* If the low level driver desires some attributes, 83242a8e397SDouglas Thompson * then create them now for the driver. 83342a8e397SDouglas Thompson */ 83442a8e397SDouglas Thompson if (mci->mc_driver_sysfs_attributes) { 8359fa2fc2eSMauro Carvalho Chehab err = edac_create_mci_instance_attributes( 8369fa2fc2eSMauro Carvalho Chehab mci->mc_driver_sysfs_attributes, 8379fa2fc2eSMauro Carvalho Chehab &mci->edac_mci_kobj); 8388096cfafSDoug Thompson if (err) { 8398096cfafSDoug Thompson debugf1("%s() failure to create mci attributes\n", 8408096cfafSDoug Thompson __func__); 84142a8e397SDouglas Thompson goto fail0; 84242a8e397SDouglas Thompson } 8438096cfafSDoug Thompson } 84442a8e397SDouglas Thompson 8458096cfafSDoug Thompson /* Make directories for each CSROW object under the mc<id> kobject 8467c9281d7SDouglas Thompson */ 8477c9281d7SDouglas Thompson for (i = 0; i < mci->nr_csrows; i++) { 8487c9281d7SDouglas Thompson csrow = &mci->csrows[i]; 8497c9281d7SDouglas Thompson 8507c9281d7SDouglas Thompson /* Only expose populated CSROWs */ 8517c9281d7SDouglas Thompson if (csrow->nr_pages > 0) { 8528096cfafSDoug Thompson err = edac_create_csrow_object(mci, csrow, i); 8538096cfafSDoug Thompson if (err) { 8548096cfafSDoug Thompson debugf1("%s() failure: create csrow %d obj\n", 8558096cfafSDoug Thompson __func__, i); 8567c9281d7SDouglas Thompson goto fail1; 8577c9281d7SDouglas Thompson } 8587c9281d7SDouglas Thompson } 8598096cfafSDoug Thompson } 8607c9281d7SDouglas Thompson 8617c9281d7SDouglas Thompson return 0; 8627c9281d7SDouglas Thompson 8637c9281d7SDouglas Thompson /* CSROW error: backout what has already been registered, */ 8647c9281d7SDouglas Thompson fail1: 8657c9281d7SDouglas Thompson for (i--; i >= 0; i--) { 8667c9281d7SDouglas Thompson if (csrow->nr_pages > 0) { 867c10997f6SGreg Kroah-Hartman kobject_put(&mci->csrows[i].kobj); 8687c9281d7SDouglas Thompson } 8697c9281d7SDouglas Thompson } 8707c9281d7SDouglas Thompson 8718096cfafSDoug Thompson /* remove the mci instance's attributes, if any */ 8729fa2fc2eSMauro Carvalho Chehab edac_remove_mci_instance_attributes( 8739fa2fc2eSMauro Carvalho Chehab mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj); 8748096cfafSDoug Thompson 8758096cfafSDoug Thompson /* remove the symlink */ 8768096cfafSDoug Thompson sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); 8778096cfafSDoug Thompson 8787c9281d7SDouglas Thompson fail0: 8797c9281d7SDouglas Thompson return err; 8807c9281d7SDouglas Thompson } 8817c9281d7SDouglas Thompson 8827c9281d7SDouglas Thompson /* 8837c9281d7SDouglas Thompson * remove a Memory Controller instance 8847c9281d7SDouglas Thompson */ 8857c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) 8867c9281d7SDouglas Thompson { 8877c9281d7SDouglas Thompson int i; 8887c9281d7SDouglas Thompson 8897c9281d7SDouglas Thompson debugf0("%s()\n", __func__); 8907c9281d7SDouglas Thompson 8917c9281d7SDouglas Thompson /* remove all csrow kobjects */ 8927c9281d7SDouglas Thompson for (i = 0; i < mci->nr_csrows; i++) { 8937c9281d7SDouglas Thompson if (mci->csrows[i].nr_pages > 0) { 8948096cfafSDoug Thompson debugf0("%s() unreg csrow-%d\n", __func__, i); 895c10997f6SGreg Kroah-Hartman kobject_put(&mci->csrows[i].kobj); 8967c9281d7SDouglas Thompson } 8977c9281d7SDouglas Thompson } 8987c9281d7SDouglas Thompson 8998096cfafSDoug Thompson debugf0("%s() remove_link\n", __func__); 9008096cfafSDoug Thompson 9018096cfafSDoug Thompson /* remove the symlink */ 9027c9281d7SDouglas Thompson sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); 9038096cfafSDoug Thompson 9048096cfafSDoug Thompson debugf0("%s() remove_mci_instance\n", __func__); 9058096cfafSDoug Thompson 9068096cfafSDoug Thompson /* remove this mci instance's attribtes */ 9079fa2fc2eSMauro Carvalho Chehab edac_remove_mci_instance_attributes(mci->mc_driver_sysfs_attributes, 9089fa2fc2eSMauro Carvalho Chehab &mci->edac_mci_kobj); 9098096cfafSDoug Thompson debugf0("%s() unregister this mci kobj\n", __func__); 9108096cfafSDoug Thompson 9118096cfafSDoug Thompson /* unregister this instance's kobject */ 912c10997f6SGreg Kroah-Hartman kobject_put(&mci->edac_mci_kobj); 9137c9281d7SDouglas Thompson } 9148096cfafSDoug Thompson 9158096cfafSDoug Thompson 9168096cfafSDoug Thompson 9178096cfafSDoug Thompson 9188096cfafSDoug Thompson /* 9198096cfafSDoug Thompson * edac_setup_sysfs_mc_kset(void) 9208096cfafSDoug Thompson * 9218096cfafSDoug Thompson * Initialize the mc_kset for the 'mc' entry 9228096cfafSDoug Thompson * This requires creating the top 'mc' directory with a kset 9238096cfafSDoug Thompson * and its controls/attributes. 9248096cfafSDoug Thompson * 9258096cfafSDoug Thompson * To this 'mc' kset, instance 'mci' will be grouped as children. 9268096cfafSDoug Thompson * 9278096cfafSDoug Thompson * Return: 0 SUCCESS 9288096cfafSDoug Thompson * !0 FAILURE error code 9298096cfafSDoug Thompson */ 9308096cfafSDoug Thompson int edac_sysfs_setup_mc_kset(void) 9318096cfafSDoug Thompson { 9328096cfafSDoug Thompson int err = 0; 9338096cfafSDoug Thompson struct sysdev_class *edac_class; 9348096cfafSDoug Thompson 9358096cfafSDoug Thompson debugf1("%s()\n", __func__); 9368096cfafSDoug Thompson 9378096cfafSDoug Thompson /* get the /sys/devices/system/edac class reference */ 9388096cfafSDoug Thompson edac_class = edac_get_edac_class(); 9398096cfafSDoug Thompson if (edac_class == NULL) { 9408096cfafSDoug Thompson debugf1("%s() no edac_class error=%d\n", __func__, err); 9418096cfafSDoug Thompson goto fail_out; 9428096cfafSDoug Thompson } 9438096cfafSDoug Thompson 9448096cfafSDoug Thompson /* Init the MC's kobject */ 945f9fc82adSArthur Jones mc_kset = kset_create_and_add("mc", NULL, &edac_class->kset.kobj); 946f9fc82adSArthur Jones if (!mc_kset) { 947f9fc82adSArthur Jones err = -ENOMEM; 9488096cfafSDoug Thompson debugf1("%s() Failed to register '.../edac/mc'\n", __func__); 9498096cfafSDoug Thompson goto fail_out; 9508096cfafSDoug Thompson } 9518096cfafSDoug Thompson 9528096cfafSDoug Thompson debugf1("%s() Registered '.../edac/mc' kobject\n", __func__); 9538096cfafSDoug Thompson 9548096cfafSDoug Thompson return 0; 9558096cfafSDoug Thompson 9568096cfafSDoug Thompson 9578096cfafSDoug Thompson /* error unwind stack */ 9588096cfafSDoug Thompson fail_out: 9598096cfafSDoug Thompson return err; 9608096cfafSDoug Thompson } 9618096cfafSDoug Thompson 9628096cfafSDoug Thompson /* 9638096cfafSDoug Thompson * edac_sysfs_teardown_mc_kset 9648096cfafSDoug Thompson * 9658096cfafSDoug Thompson * deconstruct the mc_ket for memory controllers 9668096cfafSDoug Thompson */ 9678096cfafSDoug Thompson void edac_sysfs_teardown_mc_kset(void) 9688096cfafSDoug Thompson { 969f9fc82adSArthur Jones kset_unregister(mc_kset); 9708096cfafSDoug Thompson } 9718096cfafSDoug Thompson 972