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> 1430e1f7a8SBorislav Petkov #include <linux/edac.h> 158096cfafSDoug Thompson #include <linux/bug.h> 167c9281d7SDouglas Thompson 1720bcb7a8SDouglas Thompson #include "edac_core.h" 187c9281d7SDouglas Thompson #include "edac_module.h" 197c9281d7SDouglas Thompson 208096cfafSDoug Thompson 217c9281d7SDouglas Thompson /* MC EDAC Controls, setable by module parameter, and sysfs */ 224de78c68SDave Jiang static int edac_mc_log_ue = 1; 234de78c68SDave Jiang static int edac_mc_log_ce = 1; 24f044091cSDouglas Thompson static int edac_mc_panic_on_ue; 254de78c68SDave Jiang static int edac_mc_poll_msec = 1000; 267c9281d7SDouglas Thompson 277c9281d7SDouglas Thompson /* Getter functions for above */ 284de78c68SDave Jiang int edac_mc_get_log_ue(void) 297c9281d7SDouglas Thompson { 304de78c68SDave Jiang return edac_mc_log_ue; 317c9281d7SDouglas Thompson } 327c9281d7SDouglas Thompson 334de78c68SDave Jiang int edac_mc_get_log_ce(void) 347c9281d7SDouglas Thompson { 354de78c68SDave Jiang return edac_mc_log_ce; 367c9281d7SDouglas Thompson } 377c9281d7SDouglas Thompson 384de78c68SDave Jiang int edac_mc_get_panic_on_ue(void) 397c9281d7SDouglas Thompson { 404de78c68SDave Jiang return edac_mc_panic_on_ue; 417c9281d7SDouglas Thompson } 427c9281d7SDouglas Thompson 4381d87cb1SDave Jiang /* this is temporary */ 4481d87cb1SDave Jiang int edac_mc_get_poll_msec(void) 4581d87cb1SDave Jiang { 464de78c68SDave Jiang return edac_mc_poll_msec; 477c9281d7SDouglas Thompson } 487c9281d7SDouglas Thompson 49096846e2SArthur Jones static int edac_set_poll_msec(const char *val, struct kernel_param *kp) 50096846e2SArthur Jones { 51096846e2SArthur Jones long l; 52096846e2SArthur Jones int ret; 53096846e2SArthur Jones 54096846e2SArthur Jones if (!val) 55096846e2SArthur Jones return -EINVAL; 56096846e2SArthur Jones 57096846e2SArthur Jones ret = strict_strtol(val, 0, &l); 58096846e2SArthur Jones if (ret == -EINVAL || ((int)l != l)) 59096846e2SArthur Jones return -EINVAL; 60096846e2SArthur Jones *((int *)kp->arg) = l; 61096846e2SArthur Jones 62096846e2SArthur Jones /* notify edac_mc engine to reset the poll period */ 63096846e2SArthur Jones edac_mc_reset_delay_period(l); 64096846e2SArthur Jones 65096846e2SArthur Jones return 0; 66096846e2SArthur Jones } 67096846e2SArthur Jones 687c9281d7SDouglas Thompson /* Parameter declarations for above */ 694de78c68SDave Jiang module_param(edac_mc_panic_on_ue, int, 0644); 704de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); 714de78c68SDave Jiang module_param(edac_mc_log_ue, int, 0644); 724de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ue, 734de78c68SDave Jiang "Log uncorrectable error to console: 0=off 1=on"); 744de78c68SDave Jiang module_param(edac_mc_log_ce, int, 0644); 754de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ce, 764de78c68SDave Jiang "Log correctable error to console: 0=off 1=on"); 77096846e2SArthur Jones module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, 78096846e2SArthur Jones &edac_mc_poll_msec, 0644); 794de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); 807c9281d7SDouglas Thompson 817c9281d7SDouglas Thompson /* 827c9281d7SDouglas Thompson * various constants for Memory Controllers 837c9281d7SDouglas Thompson */ 847c9281d7SDouglas Thompson static const char *mem_types[] = { 857c9281d7SDouglas Thompson [MEM_EMPTY] = "Empty", 867c9281d7SDouglas Thompson [MEM_RESERVED] = "Reserved", 877c9281d7SDouglas Thompson [MEM_UNKNOWN] = "Unknown", 887c9281d7SDouglas Thompson [MEM_FPM] = "FPM", 897c9281d7SDouglas Thompson [MEM_EDO] = "EDO", 907c9281d7SDouglas Thompson [MEM_BEDO] = "BEDO", 917c9281d7SDouglas Thompson [MEM_SDR] = "Unbuffered-SDR", 927c9281d7SDouglas Thompson [MEM_RDR] = "Registered-SDR", 937c9281d7SDouglas Thompson [MEM_DDR] = "Unbuffered-DDR", 947c9281d7SDouglas Thompson [MEM_RDDR] = "Registered-DDR", 951a9b85e6SDave Jiang [MEM_RMBS] = "RMBS", 961a9b85e6SDave Jiang [MEM_DDR2] = "Unbuffered-DDR2", 971a9b85e6SDave Jiang [MEM_FB_DDR2] = "FullyBuffered-DDR2", 981d5f726cSBenjamin Herrenschmidt [MEM_RDDR2] = "Registered-DDR2", 99b1cfebc9SYang Shi [MEM_XDR] = "XDR", 100b1cfebc9SYang Shi [MEM_DDR3] = "Unbuffered-DDR3", 101b1cfebc9SYang Shi [MEM_RDDR3] = "Registered-DDR3" 1027c9281d7SDouglas Thompson }; 1037c9281d7SDouglas Thompson 1047c9281d7SDouglas Thompson static const char *dev_types[] = { 1057c9281d7SDouglas Thompson [DEV_UNKNOWN] = "Unknown", 1067c9281d7SDouglas Thompson [DEV_X1] = "x1", 1077c9281d7SDouglas Thompson [DEV_X2] = "x2", 1087c9281d7SDouglas Thompson [DEV_X4] = "x4", 1097c9281d7SDouglas Thompson [DEV_X8] = "x8", 1107c9281d7SDouglas Thompson [DEV_X16] = "x16", 1117c9281d7SDouglas Thompson [DEV_X32] = "x32", 1127c9281d7SDouglas Thompson [DEV_X64] = "x64" 1137c9281d7SDouglas Thompson }; 1147c9281d7SDouglas Thompson 1157c9281d7SDouglas Thompson static const char *edac_caps[] = { 1167c9281d7SDouglas Thompson [EDAC_UNKNOWN] = "Unknown", 1177c9281d7SDouglas Thompson [EDAC_NONE] = "None", 1187c9281d7SDouglas Thompson [EDAC_RESERVED] = "Reserved", 1197c9281d7SDouglas Thompson [EDAC_PARITY] = "PARITY", 1207c9281d7SDouglas Thompson [EDAC_EC] = "EC", 1217c9281d7SDouglas Thompson [EDAC_SECDED] = "SECDED", 1227c9281d7SDouglas Thompson [EDAC_S2ECD2ED] = "S2ECD2ED", 1237c9281d7SDouglas Thompson [EDAC_S4ECD4ED] = "S4ECD4ED", 1247c9281d7SDouglas Thompson [EDAC_S8ECD8ED] = "S8ECD8ED", 1257c9281d7SDouglas Thompson [EDAC_S16ECD16ED] = "S16ECD16ED" 1267c9281d7SDouglas Thompson }; 1277c9281d7SDouglas Thompson 1287c9281d7SDouglas Thompson /* EDAC sysfs CSROW data structures and methods 1297c9281d7SDouglas Thompson */ 1307c9281d7SDouglas Thompson 1317c9281d7SDouglas Thompson /* Set of more default csrow<id> attribute show/store functions */ 132e27e3dacSDouglas Thompson static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, 133e27e3dacSDouglas Thompson int private) 1347c9281d7SDouglas Thompson { 1357c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->ue_count); 1367c9281d7SDouglas Thompson } 1377c9281d7SDouglas Thompson 138e27e3dacSDouglas Thompson static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, 139e27e3dacSDouglas Thompson int private) 1407c9281d7SDouglas Thompson { 1417c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->ce_count); 1427c9281d7SDouglas Thompson } 1437c9281d7SDouglas Thompson 144e27e3dacSDouglas Thompson static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, 145e27e3dacSDouglas Thompson int private) 1467c9281d7SDouglas Thompson { 1477c9281d7SDouglas Thompson return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages)); 1487c9281d7SDouglas Thompson } 1497c9281d7SDouglas Thompson 150e27e3dacSDouglas Thompson static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, 151e27e3dacSDouglas Thompson int private) 1527c9281d7SDouglas Thompson { 1537c9281d7SDouglas Thompson return sprintf(data, "%s\n", mem_types[csrow->mtype]); 1547c9281d7SDouglas Thompson } 1557c9281d7SDouglas Thompson 156e27e3dacSDouglas Thompson static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, 157e27e3dacSDouglas Thompson int private) 1587c9281d7SDouglas Thompson { 1597c9281d7SDouglas Thompson return sprintf(data, "%s\n", dev_types[csrow->dtype]); 1607c9281d7SDouglas Thompson } 1617c9281d7SDouglas Thompson 162e27e3dacSDouglas Thompson static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, 163e27e3dacSDouglas Thompson int private) 1647c9281d7SDouglas Thompson { 1657c9281d7SDouglas Thompson return sprintf(data, "%s\n", edac_caps[csrow->edac_mode]); 1667c9281d7SDouglas Thompson } 1677c9281d7SDouglas Thompson 1687c9281d7SDouglas Thompson /* show/store functions for DIMM Label attributes */ 1697c9281d7SDouglas Thompson static ssize_t channel_dimm_label_show(struct csrow_info *csrow, 1707c9281d7SDouglas Thompson char *data, int channel) 1717c9281d7SDouglas Thompson { 172124682c7SArthur Jones /* if field has not been initialized, there is nothing to send */ 173124682c7SArthur Jones if (!csrow->channels[channel].label[0]) 174124682c7SArthur Jones return 0; 175124682c7SArthur Jones 176124682c7SArthur Jones return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", 1777c9281d7SDouglas Thompson csrow->channels[channel].label); 1787c9281d7SDouglas Thompson } 1797c9281d7SDouglas Thompson 1807c9281d7SDouglas Thompson static ssize_t channel_dimm_label_store(struct csrow_info *csrow, 1817c9281d7SDouglas Thompson const char *data, 182079708b9SDouglas Thompson size_t count, int channel) 1837c9281d7SDouglas Thompson { 1847c9281d7SDouglas Thompson ssize_t max_size = 0; 1857c9281d7SDouglas Thompson 1867c9281d7SDouglas Thompson max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); 1877c9281d7SDouglas Thompson strncpy(csrow->channels[channel].label, data, max_size); 1887c9281d7SDouglas Thompson csrow->channels[channel].label[max_size] = '\0'; 1897c9281d7SDouglas Thompson 1907c9281d7SDouglas Thompson return max_size; 1917c9281d7SDouglas Thompson } 1927c9281d7SDouglas Thompson 1937c9281d7SDouglas Thompson /* show function for dynamic chX_ce_count attribute */ 1947c9281d7SDouglas Thompson static ssize_t channel_ce_count_show(struct csrow_info *csrow, 195079708b9SDouglas Thompson char *data, int channel) 1967c9281d7SDouglas Thompson { 1977c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->channels[channel].ce_count); 1987c9281d7SDouglas Thompson } 1997c9281d7SDouglas Thompson 2007c9281d7SDouglas Thompson /* csrow specific attribute structure */ 2017c9281d7SDouglas Thompson struct csrowdev_attribute { 2027c9281d7SDouglas Thompson struct attribute attr; 2037c9281d7SDouglas Thompson ssize_t(*show) (struct csrow_info *, char *, int); 2047c9281d7SDouglas Thompson ssize_t(*store) (struct csrow_info *, const char *, size_t, int); 2057c9281d7SDouglas Thompson int private; 2067c9281d7SDouglas Thompson }; 2077c9281d7SDouglas Thompson 2087c9281d7SDouglas Thompson #define to_csrow(k) container_of(k, struct csrow_info, kobj) 2097c9281d7SDouglas Thompson #define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) 2107c9281d7SDouglas Thompson 2117c9281d7SDouglas Thompson /* Set of show/store higher level functions for default csrow attributes */ 2127c9281d7SDouglas Thompson static ssize_t csrowdev_show(struct kobject *kobj, 213079708b9SDouglas Thompson struct attribute *attr, char *buffer) 2147c9281d7SDouglas Thompson { 2157c9281d7SDouglas Thompson struct csrow_info *csrow = to_csrow(kobj); 2167c9281d7SDouglas Thompson struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); 2177c9281d7SDouglas Thompson 2187c9281d7SDouglas Thompson if (csrowdev_attr->show) 2197c9281d7SDouglas Thompson return csrowdev_attr->show(csrow, 220079708b9SDouglas Thompson buffer, csrowdev_attr->private); 2217c9281d7SDouglas Thompson return -EIO; 2227c9281d7SDouglas Thompson } 2237c9281d7SDouglas Thompson 2247c9281d7SDouglas Thompson static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, 2257c9281d7SDouglas Thompson const char *buffer, size_t count) 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->store) 2317c9281d7SDouglas Thompson return csrowdev_attr->store(csrow, 2327c9281d7SDouglas Thompson buffer, 233079708b9SDouglas Thompson count, csrowdev_attr->private); 2347c9281d7SDouglas Thompson return -EIO; 2357c9281d7SDouglas Thompson } 2367c9281d7SDouglas Thompson 23752cf25d0SEmese Revfy static const struct sysfs_ops csrowfs_ops = { 2387c9281d7SDouglas Thompson .show = csrowdev_show, 2397c9281d7SDouglas Thompson .store = csrowdev_store 2407c9281d7SDouglas Thompson }; 2417c9281d7SDouglas Thompson 2427c9281d7SDouglas Thompson #define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ 2437c9281d7SDouglas Thompson static struct csrowdev_attribute attr_##_name = { \ 2447c9281d7SDouglas Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 2457c9281d7SDouglas Thompson .show = _show, \ 2467c9281d7SDouglas Thompson .store = _store, \ 2477c9281d7SDouglas Thompson .private = _private, \ 2487c9281d7SDouglas Thompson }; 2497c9281d7SDouglas Thompson 2507c9281d7SDouglas Thompson /* default cwrow<id>/attribute files */ 2517c9281d7SDouglas Thompson CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0); 2527c9281d7SDouglas Thompson CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0); 2537c9281d7SDouglas Thompson CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0); 2547c9281d7SDouglas Thompson CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0); 2557c9281d7SDouglas Thompson CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0); 2567c9281d7SDouglas Thompson CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0); 2577c9281d7SDouglas Thompson 2587c9281d7SDouglas Thompson /* default attributes of the CSROW<id> object */ 2597c9281d7SDouglas Thompson static struct csrowdev_attribute *default_csrow_attr[] = { 2607c9281d7SDouglas Thompson &attr_dev_type, 2617c9281d7SDouglas Thompson &attr_mem_type, 2627c9281d7SDouglas Thompson &attr_edac_mode, 2637c9281d7SDouglas Thompson &attr_size_mb, 2647c9281d7SDouglas Thompson &attr_ue_count, 2657c9281d7SDouglas Thompson &attr_ce_count, 2667c9281d7SDouglas Thompson NULL, 2677c9281d7SDouglas Thompson }; 2687c9281d7SDouglas Thompson 2697c9281d7SDouglas Thompson /* possible dynamic channel DIMM Label attribute files */ 2707c9281d7SDouglas Thompson CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR, 271079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 0); 2727c9281d7SDouglas Thompson CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR, 273079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 1); 2747c9281d7SDouglas Thompson CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR, 275079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 2); 2767c9281d7SDouglas Thompson CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR, 277079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 3); 2787c9281d7SDouglas Thompson CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR, 279079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 4); 2807c9281d7SDouglas Thompson CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR, 281079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 5); 2827c9281d7SDouglas Thompson 2837c9281d7SDouglas Thompson /* Total possible dynamic DIMM Label attribute file table */ 2847c9281d7SDouglas Thompson static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { 2857c9281d7SDouglas Thompson &attr_ch0_dimm_label, 2867c9281d7SDouglas Thompson &attr_ch1_dimm_label, 2877c9281d7SDouglas Thompson &attr_ch2_dimm_label, 2887c9281d7SDouglas Thompson &attr_ch3_dimm_label, 2897c9281d7SDouglas Thompson &attr_ch4_dimm_label, 2907c9281d7SDouglas Thompson &attr_ch5_dimm_label 2917c9281d7SDouglas Thompson }; 2927c9281d7SDouglas Thompson 2937c9281d7SDouglas Thompson /* possible dynamic channel ce_count attribute files */ 294079708b9SDouglas Thompson CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0); 295079708b9SDouglas Thompson CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1); 296079708b9SDouglas Thompson CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2); 297079708b9SDouglas Thompson CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3); 298079708b9SDouglas Thompson CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4); 299079708b9SDouglas Thompson CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5); 3007c9281d7SDouglas Thompson 3017c9281d7SDouglas Thompson /* Total possible dynamic ce_count attribute file table */ 3027c9281d7SDouglas Thompson static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { 3037c9281d7SDouglas Thompson &attr_ch0_ce_count, 3047c9281d7SDouglas Thompson &attr_ch1_ce_count, 3057c9281d7SDouglas Thompson &attr_ch2_ce_count, 3067c9281d7SDouglas Thompson &attr_ch3_ce_count, 3077c9281d7SDouglas Thompson &attr_ch4_ce_count, 3087c9281d7SDouglas Thompson &attr_ch5_ce_count 3097c9281d7SDouglas Thompson }; 3107c9281d7SDouglas Thompson 3117c9281d7SDouglas Thompson #define EDAC_NR_CHANNELS 6 3127c9281d7SDouglas Thompson 3137c9281d7SDouglas Thompson /* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ 3147c9281d7SDouglas Thompson static int edac_create_channel_files(struct kobject *kobj, int chan) 3157c9281d7SDouglas Thompson { 3167c9281d7SDouglas Thompson int err = -ENODEV; 3177c9281d7SDouglas Thompson 3187c9281d7SDouglas Thompson if (chan >= EDAC_NR_CHANNELS) 3197c9281d7SDouglas Thompson return err; 3207c9281d7SDouglas Thompson 3217c9281d7SDouglas Thompson /* create the DIMM label attribute file */ 3227c9281d7SDouglas Thompson err = sysfs_create_file(kobj, 323079708b9SDouglas Thompson (struct attribute *) 324079708b9SDouglas Thompson dynamic_csrow_dimm_attr[chan]); 3257c9281d7SDouglas Thompson 3267c9281d7SDouglas Thompson if (!err) { 3277c9281d7SDouglas Thompson /* create the CE Count attribute file */ 3287c9281d7SDouglas Thompson err = sysfs_create_file(kobj, 329079708b9SDouglas Thompson (struct attribute *) 330079708b9SDouglas Thompson dynamic_csrow_ce_count_attr[chan]); 3317c9281d7SDouglas Thompson } else { 332e27e3dacSDouglas Thompson debugf1("%s() dimm labels and ce_count files created", 333e27e3dacSDouglas Thompson __func__); 3347c9281d7SDouglas Thompson } 3357c9281d7SDouglas Thompson 3367c9281d7SDouglas Thompson return err; 3377c9281d7SDouglas Thompson } 3387c9281d7SDouglas Thompson 3397c9281d7SDouglas Thompson /* No memory to release for this kobj */ 3407c9281d7SDouglas Thompson static void edac_csrow_instance_release(struct kobject *kobj) 3417c9281d7SDouglas Thompson { 3428096cfafSDoug Thompson struct mem_ctl_info *mci; 3437c9281d7SDouglas Thompson struct csrow_info *cs; 3447c9281d7SDouglas Thompson 3458096cfafSDoug Thompson debugf1("%s()\n", __func__); 3468096cfafSDoug Thompson 3477c9281d7SDouglas Thompson cs = container_of(kobj, struct csrow_info, kobj); 3488096cfafSDoug Thompson mci = cs->mci; 3498096cfafSDoug Thompson 3508096cfafSDoug Thompson kobject_put(&mci->edac_mci_kobj); 3517c9281d7SDouglas Thompson } 3527c9281d7SDouglas Thompson 3537c9281d7SDouglas Thompson /* the kobj_type instance for a CSROW */ 3547c9281d7SDouglas Thompson static struct kobj_type ktype_csrow = { 3557c9281d7SDouglas Thompson .release = edac_csrow_instance_release, 3567c9281d7SDouglas Thompson .sysfs_ops = &csrowfs_ops, 3577c9281d7SDouglas Thompson .default_attrs = (struct attribute **)default_csrow_attr, 3587c9281d7SDouglas Thompson }; 3597c9281d7SDouglas Thompson 3607c9281d7SDouglas Thompson /* Create a CSROW object under specifed edac_mc_device */ 3618096cfafSDoug Thompson static int edac_create_csrow_object(struct mem_ctl_info *mci, 362079708b9SDouglas Thompson struct csrow_info *csrow, int index) 3637c9281d7SDouglas Thompson { 3648096cfafSDoug Thompson struct kobject *kobj_mci = &mci->edac_mci_kobj; 3658096cfafSDoug Thompson struct kobject *kobj; 3667c9281d7SDouglas Thompson int chan; 3678096cfafSDoug Thompson int err; 3687c9281d7SDouglas Thompson 3697c9281d7SDouglas Thompson /* generate ..../edac/mc/mc<id>/csrow<index> */ 3708096cfafSDoug Thompson memset(&csrow->kobj, 0, sizeof(csrow->kobj)); 3718096cfafSDoug Thompson csrow->mci = mci; /* include container up link */ 3728096cfafSDoug Thompson 3738096cfafSDoug Thompson /* bump the mci instance's kobject's ref count */ 3748096cfafSDoug Thompson kobj = kobject_get(&mci->edac_mci_kobj); 3758096cfafSDoug Thompson if (!kobj) { 3768096cfafSDoug Thompson err = -ENODEV; 3778096cfafSDoug Thompson goto err_out; 3788096cfafSDoug Thompson } 3797c9281d7SDouglas Thompson 3807c9281d7SDouglas Thompson /* Instanstiate the csrow object */ 381b2ed215aSGreg Kroah-Hartman err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci, 382b2ed215aSGreg Kroah-Hartman "csrow%d", index); 3838096cfafSDoug Thompson if (err) 3848096cfafSDoug Thompson goto err_release_top_kobj; 3858096cfafSDoug Thompson 3868096cfafSDoug Thompson /* At this point, to release a csrow kobj, one must 387c10997f6SGreg Kroah-Hartman * call the kobject_put and allow that tear down 3888096cfafSDoug Thompson * to work the releasing 3898096cfafSDoug Thompson */ 3908096cfafSDoug Thompson 3917c9281d7SDouglas Thompson /* Create the dyanmic attribute files on this csrow, 3927c9281d7SDouglas Thompson * namely, the DIMM labels and the channel ce_count 3937c9281d7SDouglas Thompson */ 3947c9281d7SDouglas Thompson for (chan = 0; chan < csrow->nr_channels; chan++) { 3957c9281d7SDouglas Thompson err = edac_create_channel_files(&csrow->kobj, chan); 3968096cfafSDoug Thompson if (err) { 3978096cfafSDoug Thompson /* special case the unregister here */ 398c10997f6SGreg Kroah-Hartman kobject_put(&csrow->kobj); 3998096cfafSDoug Thompson goto err_out; 4007c9281d7SDouglas Thompson } 4017c9281d7SDouglas Thompson } 402b2ed215aSGreg Kroah-Hartman kobject_uevent(&csrow->kobj, KOBJ_ADD); 4038096cfafSDoug Thompson return 0; 4048096cfafSDoug Thompson 4058096cfafSDoug Thompson /* error unwind stack */ 4068096cfafSDoug Thompson err_release_top_kobj: 4078096cfafSDoug Thompson kobject_put(&mci->edac_mci_kobj); 4088096cfafSDoug Thompson 4098096cfafSDoug Thompson err_out: 4107c9281d7SDouglas Thompson return err; 4117c9281d7SDouglas Thompson } 4127c9281d7SDouglas Thompson 4137c9281d7SDouglas Thompson /* default sysfs methods and data structures for the main MCI kobject */ 4147c9281d7SDouglas Thompson 4157c9281d7SDouglas Thompson static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, 4167c9281d7SDouglas Thompson const char *data, size_t count) 4177c9281d7SDouglas Thompson { 4187c9281d7SDouglas Thompson int row, chan; 4197c9281d7SDouglas Thompson 4207c9281d7SDouglas Thompson mci->ue_noinfo_count = 0; 4217c9281d7SDouglas Thompson mci->ce_noinfo_count = 0; 4227c9281d7SDouglas Thompson mci->ue_count = 0; 4237c9281d7SDouglas Thompson mci->ce_count = 0; 4247c9281d7SDouglas Thompson 4257c9281d7SDouglas Thompson for (row = 0; row < mci->nr_csrows; row++) { 4267c9281d7SDouglas Thompson struct csrow_info *ri = &mci->csrows[row]; 4277c9281d7SDouglas Thompson 4287c9281d7SDouglas Thompson ri->ue_count = 0; 4297c9281d7SDouglas Thompson ri->ce_count = 0; 4307c9281d7SDouglas Thompson 4317c9281d7SDouglas Thompson for (chan = 0; chan < ri->nr_channels; chan++) 4327c9281d7SDouglas Thompson ri->channels[chan].ce_count = 0; 4337c9281d7SDouglas Thompson } 4347c9281d7SDouglas Thompson 4357c9281d7SDouglas Thompson mci->start_time = jiffies; 4367c9281d7SDouglas Thompson return count; 4377c9281d7SDouglas Thompson } 4387c9281d7SDouglas Thompson 43939094443SBorislav Petkov /* Memory scrubbing interface: 44039094443SBorislav Petkov * 44139094443SBorislav Petkov * A MC driver can limit the scrubbing bandwidth based on the CPU type. 44239094443SBorislav Petkov * Therefore, ->set_sdram_scrub_rate should be made to return the actual 44339094443SBorislav Petkov * bandwidth that is accepted or 0 when scrubbing is to be disabled. 44439094443SBorislav Petkov * 44539094443SBorislav Petkov * Negative value still means that an error has occurred while setting 44639094443SBorislav Petkov * the scrub rate. 44739094443SBorislav Petkov */ 4487c9281d7SDouglas Thompson static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, 4497c9281d7SDouglas Thompson const char *data, size_t count) 4507c9281d7SDouglas Thompson { 451eba042a8SBorislav Petkov unsigned long bandwidth = 0; 45239094443SBorislav Petkov int new_bw = 0; 4537c9281d7SDouglas Thompson 45439094443SBorislav Petkov if (!mci->set_sdram_scrub_rate) 455eba042a8SBorislav Petkov return -EINVAL; 456eba042a8SBorislav Petkov 457eba042a8SBorislav Petkov if (strict_strtoul(data, 10, &bandwidth) < 0) 458eba042a8SBorislav Petkov return -EINVAL; 459eba042a8SBorislav Petkov 46039094443SBorislav Petkov new_bw = mci->set_sdram_scrub_rate(mci, bandwidth); 4614949603aSMarkus Trippelsdorf if (new_bw < 0) { 4624949603aSMarkus Trippelsdorf edac_printk(KERN_WARNING, EDAC_MC, 4634949603aSMarkus Trippelsdorf "Error setting scrub rate to: %lu\n", bandwidth); 4644949603aSMarkus Trippelsdorf return -EINVAL; 4657c9281d7SDouglas Thompson } 4667c9281d7SDouglas Thompson 4674949603aSMarkus Trippelsdorf return count; 4687c9281d7SDouglas Thompson } 469eba042a8SBorislav Petkov 47039094443SBorislav Petkov /* 47139094443SBorislav Petkov * ->get_sdram_scrub_rate() return value semantics same as above. 47239094443SBorislav Petkov */ 47339094443SBorislav Petkov static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) 47439094443SBorislav Petkov { 47539094443SBorislav Petkov int bandwidth = 0; 47639094443SBorislav Petkov 47739094443SBorislav Petkov if (!mci->get_sdram_scrub_rate) 47839094443SBorislav Petkov return -EINVAL; 47939094443SBorislav Petkov 48039094443SBorislav Petkov bandwidth = mci->get_sdram_scrub_rate(mci); 48139094443SBorislav Petkov if (bandwidth < 0) { 482eba042a8SBorislav Petkov edac_printk(KERN_DEBUG, EDAC_MC, "Error reading scrub rate\n"); 48339094443SBorislav Petkov return bandwidth; 484eba042a8SBorislav Petkov } 48539094443SBorislav Petkov 4867c9281d7SDouglas Thompson return sprintf(data, "%d\n", bandwidth); 4877c9281d7SDouglas Thompson } 4887c9281d7SDouglas Thompson 4897c9281d7SDouglas Thompson /* default attribute files for the MCI object */ 4907c9281d7SDouglas Thompson static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) 4917c9281d7SDouglas Thompson { 4927c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ue_count); 4937c9281d7SDouglas Thompson } 4947c9281d7SDouglas Thompson 4957c9281d7SDouglas Thompson static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) 4967c9281d7SDouglas Thompson { 4977c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ce_count); 4987c9281d7SDouglas Thompson } 4997c9281d7SDouglas Thompson 5007c9281d7SDouglas Thompson static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) 5017c9281d7SDouglas Thompson { 5027c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ce_noinfo_count); 5037c9281d7SDouglas Thompson } 5047c9281d7SDouglas Thompson 5057c9281d7SDouglas Thompson static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) 5067c9281d7SDouglas Thompson { 5077c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ue_noinfo_count); 5087c9281d7SDouglas Thompson } 5097c9281d7SDouglas Thompson 5107c9281d7SDouglas Thompson static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) 5117c9281d7SDouglas Thompson { 5127c9281d7SDouglas Thompson return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ); 5137c9281d7SDouglas Thompson } 5147c9281d7SDouglas Thompson 5157c9281d7SDouglas Thompson static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) 5167c9281d7SDouglas Thompson { 5177c9281d7SDouglas Thompson return sprintf(data, "%s\n", mci->ctl_name); 5187c9281d7SDouglas Thompson } 5197c9281d7SDouglas Thompson 5207c9281d7SDouglas Thompson static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) 5217c9281d7SDouglas Thompson { 5227c9281d7SDouglas Thompson int total_pages, csrow_idx; 5237c9281d7SDouglas Thompson 5247c9281d7SDouglas Thompson for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows; 5257c9281d7SDouglas Thompson csrow_idx++) { 5267c9281d7SDouglas Thompson struct csrow_info *csrow = &mci->csrows[csrow_idx]; 5277c9281d7SDouglas Thompson 5287c9281d7SDouglas Thompson if (!csrow->nr_pages) 5297c9281d7SDouglas Thompson continue; 5307c9281d7SDouglas Thompson 5317c9281d7SDouglas Thompson total_pages += csrow->nr_pages; 5327c9281d7SDouglas Thompson } 5337c9281d7SDouglas Thompson 5347c9281d7SDouglas Thompson return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); 5357c9281d7SDouglas Thompson } 5367c9281d7SDouglas Thompson 5377c9281d7SDouglas Thompson #define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) 53842a8e397SDouglas Thompson #define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr) 5397c9281d7SDouglas Thompson 5407c9281d7SDouglas Thompson /* MCI show/store functions for top most object */ 5417c9281d7SDouglas Thompson static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, 5427c9281d7SDouglas Thompson char *buffer) 5437c9281d7SDouglas Thompson { 5447c9281d7SDouglas Thompson struct mem_ctl_info *mem_ctl_info = to_mci(kobj); 54542a8e397SDouglas Thompson struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 5467c9281d7SDouglas Thompson 547cc301b3aSMauro Carvalho Chehab debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); 548cc301b3aSMauro Carvalho Chehab 5497c9281d7SDouglas Thompson if (mcidev_attr->show) 5507c9281d7SDouglas Thompson return mcidev_attr->show(mem_ctl_info, buffer); 5517c9281d7SDouglas Thompson 5527c9281d7SDouglas Thompson return -EIO; 5537c9281d7SDouglas Thompson } 5547c9281d7SDouglas Thompson 5557c9281d7SDouglas Thompson static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, 5567c9281d7SDouglas Thompson const char *buffer, size_t count) 5577c9281d7SDouglas Thompson { 5587c9281d7SDouglas Thompson struct mem_ctl_info *mem_ctl_info = to_mci(kobj); 55942a8e397SDouglas Thompson struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 5607c9281d7SDouglas Thompson 561cc301b3aSMauro Carvalho Chehab debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); 562cc301b3aSMauro Carvalho Chehab 5637c9281d7SDouglas Thompson if (mcidev_attr->store) 5647c9281d7SDouglas Thompson return mcidev_attr->store(mem_ctl_info, buffer, count); 5657c9281d7SDouglas Thompson 5667c9281d7SDouglas Thompson return -EIO; 5677c9281d7SDouglas Thompson } 5687c9281d7SDouglas Thompson 5698096cfafSDoug Thompson /* Intermediate show/store table */ 57052cf25d0SEmese Revfy static const struct sysfs_ops mci_ops = { 5717c9281d7SDouglas Thompson .show = mcidev_show, 5727c9281d7SDouglas Thompson .store = mcidev_store 5737c9281d7SDouglas Thompson }; 5747c9281d7SDouglas Thompson 5757c9281d7SDouglas Thompson #define MCIDEV_ATTR(_name,_mode,_show,_store) \ 57642a8e397SDouglas Thompson static struct mcidev_sysfs_attribute mci_attr_##_name = { \ 5777c9281d7SDouglas Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 5787c9281d7SDouglas Thompson .show = _show, \ 5797c9281d7SDouglas Thompson .store = _store, \ 5807c9281d7SDouglas Thompson }; 5817c9281d7SDouglas Thompson 5827c9281d7SDouglas Thompson /* default Control file */ 5837c9281d7SDouglas Thompson MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); 5847c9281d7SDouglas Thompson 5857c9281d7SDouglas Thompson /* default Attribute files */ 5867c9281d7SDouglas Thompson MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); 5877c9281d7SDouglas Thompson MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); 5887c9281d7SDouglas Thompson MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); 5897c9281d7SDouglas Thompson MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); 5907c9281d7SDouglas Thompson MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); 5917c9281d7SDouglas Thompson MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); 5927c9281d7SDouglas Thompson MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); 5937c9281d7SDouglas Thompson 5947c9281d7SDouglas Thompson /* memory scrubber attribute file */ 595079708b9SDouglas Thompson MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, 596e27e3dacSDouglas Thompson mci_sdram_scrub_rate_store); 5977c9281d7SDouglas Thompson 59842a8e397SDouglas Thompson static struct mcidev_sysfs_attribute *mci_attr[] = { 5997c9281d7SDouglas Thompson &mci_attr_reset_counters, 6007c9281d7SDouglas Thompson &mci_attr_mc_name, 6017c9281d7SDouglas Thompson &mci_attr_size_mb, 6027c9281d7SDouglas Thompson &mci_attr_seconds_since_reset, 6037c9281d7SDouglas Thompson &mci_attr_ue_noinfo_count, 6047c9281d7SDouglas Thompson &mci_attr_ce_noinfo_count, 6057c9281d7SDouglas Thompson &mci_attr_ue_count, 6067c9281d7SDouglas Thompson &mci_attr_ce_count, 6077c9281d7SDouglas Thompson &mci_attr_sdram_scrub_rate, 6087c9281d7SDouglas Thompson NULL 6097c9281d7SDouglas Thompson }; 6107c9281d7SDouglas Thompson 6118096cfafSDoug Thompson 6127c9281d7SDouglas Thompson /* 6137c9281d7SDouglas Thompson * Release of a MC controlling instance 6148096cfafSDoug Thompson * 6158096cfafSDoug Thompson * each MC control instance has the following resources upon entry: 6168096cfafSDoug Thompson * a) a ref count on the top memctl kobj 6178096cfafSDoug Thompson * b) a ref count on this module 6188096cfafSDoug Thompson * 6198096cfafSDoug Thompson * this function must decrement those ref counts and then 6208096cfafSDoug Thompson * issue a free on the instance's memory 6217c9281d7SDouglas Thompson */ 6228096cfafSDoug Thompson static void edac_mci_control_release(struct kobject *kobj) 6237c9281d7SDouglas Thompson { 6247c9281d7SDouglas Thompson struct mem_ctl_info *mci; 6257c9281d7SDouglas Thompson 6267c9281d7SDouglas Thompson mci = to_mci(kobj); 6278096cfafSDoug Thompson 6288096cfafSDoug Thompson debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx); 6298096cfafSDoug Thompson 6308096cfafSDoug Thompson /* decrement the module ref count */ 6318096cfafSDoug Thompson module_put(mci->owner); 6327c9281d7SDouglas Thompson } 6337c9281d7SDouglas Thompson 6347c9281d7SDouglas Thompson static struct kobj_type ktype_mci = { 6358096cfafSDoug Thompson .release = edac_mci_control_release, 6367c9281d7SDouglas Thompson .sysfs_ops = &mci_ops, 6377c9281d7SDouglas Thompson .default_attrs = (struct attribute **)mci_attr, 6387c9281d7SDouglas Thompson }; 6397c9281d7SDouglas Thompson 6408096cfafSDoug Thompson /* EDAC memory controller sysfs kset: 6418096cfafSDoug Thompson * /sys/devices/system/edac/mc 6428096cfafSDoug Thompson */ 643f9fc82adSArthur Jones static struct kset *mc_kset; 6448096cfafSDoug Thompson 6458096cfafSDoug Thompson /* 6468096cfafSDoug Thompson * edac_mc_register_sysfs_main_kobj 6478096cfafSDoug Thompson * 6488096cfafSDoug Thompson * setups and registers the main kobject for each mci 6498096cfafSDoug Thompson */ 6508096cfafSDoug Thompson int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci) 6518096cfafSDoug Thompson { 6528096cfafSDoug Thompson struct kobject *kobj_mci; 6538096cfafSDoug Thompson int err; 6548096cfafSDoug Thompson 6558096cfafSDoug Thompson debugf1("%s()\n", __func__); 6568096cfafSDoug Thompson 6578096cfafSDoug Thompson kobj_mci = &mci->edac_mci_kobj; 6588096cfafSDoug Thompson 6598096cfafSDoug Thompson /* Init the mci's kobject */ 6608096cfafSDoug Thompson memset(kobj_mci, 0, sizeof(*kobj_mci)); 6618096cfafSDoug Thompson 6628096cfafSDoug Thompson /* Record which module 'owns' this control structure 6638096cfafSDoug Thompson * and bump the ref count of the module 6648096cfafSDoug Thompson */ 6658096cfafSDoug Thompson mci->owner = THIS_MODULE; 6668096cfafSDoug Thompson 6678096cfafSDoug Thompson /* bump ref count on this module */ 6688096cfafSDoug Thompson if (!try_module_get(mci->owner)) { 6698096cfafSDoug Thompson err = -ENODEV; 6708096cfafSDoug Thompson goto fail_out; 6718096cfafSDoug Thompson } 6728096cfafSDoug Thompson 673b2ed215aSGreg Kroah-Hartman /* this instance become part of the mc_kset */ 674f9fc82adSArthur Jones kobj_mci->kset = mc_kset; 675b2ed215aSGreg Kroah-Hartman 6768096cfafSDoug Thompson /* register the mc<id> kobject to the mc_kset */ 677b2ed215aSGreg Kroah-Hartman err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL, 678b2ed215aSGreg Kroah-Hartman "mc%d", mci->mc_idx); 6798096cfafSDoug Thompson if (err) { 6808096cfafSDoug Thompson debugf1("%s()Failed to register '.../edac/mc%d'\n", 6818096cfafSDoug Thompson __func__, mci->mc_idx); 6828096cfafSDoug Thompson goto kobj_reg_fail; 6838096cfafSDoug Thompson } 684b2ed215aSGreg Kroah-Hartman kobject_uevent(kobj_mci, KOBJ_ADD); 6858096cfafSDoug Thompson 6868096cfafSDoug Thompson /* At this point, to 'free' the control struct, 6878096cfafSDoug Thompson * edac_mc_unregister_sysfs_main_kobj() must be used 6888096cfafSDoug Thompson */ 6898096cfafSDoug Thompson 6908096cfafSDoug Thompson debugf1("%s() Registered '.../edac/mc%d' kobject\n", 6918096cfafSDoug Thompson __func__, mci->mc_idx); 6928096cfafSDoug Thompson 6938096cfafSDoug Thompson return 0; 6948096cfafSDoug Thompson 6958096cfafSDoug Thompson /* Error exit stack */ 6968096cfafSDoug Thompson 6978096cfafSDoug Thompson kobj_reg_fail: 6988096cfafSDoug Thompson module_put(mci->owner); 6998096cfafSDoug Thompson 7008096cfafSDoug Thompson fail_out: 7018096cfafSDoug Thompson return err; 7028096cfafSDoug Thompson } 7038096cfafSDoug Thompson 7048096cfafSDoug Thompson /* 7058096cfafSDoug Thompson * edac_mc_register_sysfs_main_kobj 7068096cfafSDoug Thompson * 7078096cfafSDoug Thompson * tears down and the main mci kobject from the mc_kset 7088096cfafSDoug Thompson */ 7098096cfafSDoug Thompson void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) 7108096cfafSDoug Thompson { 711bbc560aeSMauro Carvalho Chehab debugf1("%s()\n", __func__); 712bbc560aeSMauro Carvalho Chehab 7138096cfafSDoug Thompson /* delete the kobj from the mc_kset */ 714c10997f6SGreg Kroah-Hartman kobject_put(&mci->edac_mci_kobj); 7158096cfafSDoug Thompson } 7168096cfafSDoug Thompson 7177c9281d7SDouglas Thompson #define EDAC_DEVICE_SYMLINK "device" 7187c9281d7SDouglas Thompson 719b968759eSMauro Carvalho Chehab #define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci) 720cc301b3aSMauro Carvalho Chehab 721cc301b3aSMauro Carvalho Chehab /* MCI show/store functions for top most object */ 722cc301b3aSMauro Carvalho Chehab static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr, 723cc301b3aSMauro Carvalho Chehab char *buffer) 724cc301b3aSMauro Carvalho Chehab { 725cc301b3aSMauro Carvalho Chehab struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); 726cc301b3aSMauro Carvalho Chehab struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 727cc301b3aSMauro Carvalho Chehab 728cc301b3aSMauro Carvalho Chehab debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); 729cc301b3aSMauro Carvalho Chehab 730cc301b3aSMauro Carvalho Chehab if (mcidev_attr->show) 731cc301b3aSMauro Carvalho Chehab return mcidev_attr->show(mem_ctl_info, buffer); 732cc301b3aSMauro Carvalho Chehab 733cc301b3aSMauro Carvalho Chehab return -EIO; 734cc301b3aSMauro Carvalho Chehab } 735cc301b3aSMauro Carvalho Chehab 736cc301b3aSMauro Carvalho Chehab static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr, 737cc301b3aSMauro Carvalho Chehab const char *buffer, size_t count) 738cc301b3aSMauro Carvalho Chehab { 739cc301b3aSMauro Carvalho Chehab struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); 740cc301b3aSMauro Carvalho Chehab struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 741cc301b3aSMauro Carvalho Chehab 742cc301b3aSMauro Carvalho Chehab debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); 743cc301b3aSMauro Carvalho Chehab 744cc301b3aSMauro Carvalho Chehab if (mcidev_attr->store) 745cc301b3aSMauro Carvalho Chehab return mcidev_attr->store(mem_ctl_info, buffer, count); 746cc301b3aSMauro Carvalho Chehab 747cc301b3aSMauro Carvalho Chehab return -EIO; 748cc301b3aSMauro Carvalho Chehab } 749cc301b3aSMauro Carvalho Chehab 750cc301b3aSMauro Carvalho Chehab /* No memory to release for this kobj */ 751cc301b3aSMauro Carvalho Chehab static void edac_inst_grp_release(struct kobject *kobj) 752cc301b3aSMauro Carvalho Chehab { 753b968759eSMauro Carvalho Chehab struct mcidev_sysfs_group_kobj *grp; 754cc301b3aSMauro Carvalho Chehab struct mem_ctl_info *mci; 755cc301b3aSMauro Carvalho Chehab 756cc301b3aSMauro Carvalho Chehab debugf1("%s()\n", __func__); 757cc301b3aSMauro Carvalho Chehab 758b968759eSMauro Carvalho Chehab grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); 759cc301b3aSMauro Carvalho Chehab mci = grp->mci; 760cc301b3aSMauro Carvalho Chehab } 761cc301b3aSMauro Carvalho Chehab 762cc301b3aSMauro Carvalho Chehab /* Intermediate show/store table */ 763cc301b3aSMauro Carvalho Chehab static struct sysfs_ops inst_grp_ops = { 764cc301b3aSMauro Carvalho Chehab .show = inst_grp_show, 765cc301b3aSMauro Carvalho Chehab .store = inst_grp_store 766cc301b3aSMauro Carvalho Chehab }; 767cc301b3aSMauro Carvalho Chehab 768cc301b3aSMauro Carvalho Chehab /* the kobj_type instance for a instance group */ 769cc301b3aSMauro Carvalho Chehab static struct kobj_type ktype_inst_grp = { 770cc301b3aSMauro Carvalho Chehab .release = edac_inst_grp_release, 771cc301b3aSMauro Carvalho Chehab .sysfs_ops = &inst_grp_ops, 772cc301b3aSMauro Carvalho Chehab }; 773cc301b3aSMauro Carvalho Chehab 774cc301b3aSMauro Carvalho Chehab 7757c9281d7SDouglas Thompson /* 7768096cfafSDoug Thompson * edac_create_mci_instance_attributes 7779fa2fc2eSMauro Carvalho Chehab * create MC driver specific attributes bellow an specified kobj 7789fa2fc2eSMauro Carvalho Chehab * This routine calls itself recursively, in order to create an entire 7799fa2fc2eSMauro Carvalho Chehab * object tree. 78042a8e397SDouglas Thompson */ 781cc301b3aSMauro Carvalho Chehab static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, 7821288c18fSMauro Carvalho Chehab const struct mcidev_sysfs_attribute *sysfs_attrib, 7839fa2fc2eSMauro Carvalho Chehab struct kobject *kobj) 78442a8e397SDouglas Thompson { 78542a8e397SDouglas Thompson int err; 78642a8e397SDouglas Thompson 787b6a280bbSBorislav Petkov debugf4("%s()\n", __func__); 788cc301b3aSMauro Carvalho Chehab 7899fa2fc2eSMauro Carvalho Chehab while (sysfs_attrib) { 790b6a280bbSBorislav Petkov debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); 7919fa2fc2eSMauro Carvalho Chehab if (sysfs_attrib->grp) { 792b968759eSMauro Carvalho Chehab struct mcidev_sysfs_group_kobj *grp_kobj; 793b968759eSMauro Carvalho Chehab 794b968759eSMauro Carvalho Chehab grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL); 795b968759eSMauro Carvalho Chehab if (!grp_kobj) 796b968759eSMauro Carvalho Chehab return -ENOMEM; 797b968759eSMauro Carvalho Chehab 798b968759eSMauro Carvalho Chehab grp_kobj->grp = sysfs_attrib->grp; 799b968759eSMauro Carvalho Chehab grp_kobj->mci = mci; 8006fe1108fSMauro Carvalho Chehab list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); 801c419d921SMauro Carvalho Chehab 802cc301b3aSMauro Carvalho Chehab debugf0("%s() grp %s, mci %p\n", __func__, 803cc301b3aSMauro Carvalho Chehab sysfs_attrib->grp->name, mci); 80442a8e397SDouglas Thompson 805b968759eSMauro Carvalho Chehab err = kobject_init_and_add(&grp_kobj->kobj, 806b968759eSMauro Carvalho Chehab &ktype_inst_grp, 807c419d921SMauro Carvalho Chehab &mci->edac_mci_kobj, 8089fa2fc2eSMauro Carvalho Chehab sysfs_attrib->grp->name); 8096fe1108fSMauro Carvalho Chehab if (err < 0) { 8106fe1108fSMauro Carvalho Chehab printk(KERN_ERR "kobject_init_and_add failed: %d\n", err); 8119fa2fc2eSMauro Carvalho Chehab return err; 8126fe1108fSMauro Carvalho Chehab } 813cc301b3aSMauro Carvalho Chehab err = edac_create_mci_instance_attributes(mci, 814b968759eSMauro Carvalho Chehab grp_kobj->grp->mcidev_attr, 815b968759eSMauro Carvalho Chehab &grp_kobj->kobj); 816cc301b3aSMauro Carvalho Chehab 8176fe1108fSMauro Carvalho Chehab if (err < 0) 8189fa2fc2eSMauro Carvalho Chehab return err; 8199fa2fc2eSMauro Carvalho Chehab } else if (sysfs_attrib->attr.name) { 820b6a280bbSBorislav Petkov debugf4("%s() file %s\n", __func__, 8219fa2fc2eSMauro Carvalho Chehab sysfs_attrib->attr.name); 8229fa2fc2eSMauro Carvalho Chehab 8239fa2fc2eSMauro Carvalho Chehab err = sysfs_create_file(kobj, &sysfs_attrib->attr); 8246fe1108fSMauro Carvalho Chehab if (err < 0) { 8256fe1108fSMauro Carvalho Chehab printk(KERN_ERR "sysfs_create_file failed: %d\n", err); 8266fe1108fSMauro Carvalho Chehab return err; 8276fe1108fSMauro Carvalho Chehab } 8289fa2fc2eSMauro Carvalho Chehab } else 8299fa2fc2eSMauro Carvalho Chehab break; 8309fa2fc2eSMauro Carvalho Chehab 83142a8e397SDouglas Thompson sysfs_attrib++; 83242a8e397SDouglas Thompson } 83342a8e397SDouglas Thompson 83442a8e397SDouglas Thompson return 0; 83542a8e397SDouglas Thompson } 83642a8e397SDouglas Thompson 83742a8e397SDouglas Thompson /* 8388096cfafSDoug Thompson * edac_remove_mci_instance_attributes 8398096cfafSDoug Thompson * remove MC driver specific attributes at the topmost level 8408096cfafSDoug Thompson * directory of this mci instance. 8418096cfafSDoug Thompson */ 842b968759eSMauro Carvalho Chehab static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, 8431288c18fSMauro Carvalho Chehab const struct mcidev_sysfs_attribute *sysfs_attrib, 844b968759eSMauro Carvalho Chehab struct kobject *kobj, int count) 8458096cfafSDoug Thompson { 846b968759eSMauro Carvalho Chehab struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; 847b968759eSMauro Carvalho Chehab 848cc301b3aSMauro Carvalho Chehab debugf1("%s()\n", __func__); 849cc301b3aSMauro Carvalho Chehab 850b968759eSMauro Carvalho Chehab /* 851b968759eSMauro Carvalho Chehab * loop if there are attributes and until we hit a NULL entry 85225985edcSLucas De Marchi * Remove first all the attributes 853b968759eSMauro Carvalho Chehab */ 8549fa2fc2eSMauro Carvalho Chehab while (sysfs_attrib) { 855b6a280bbSBorislav Petkov debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); 8569fa2fc2eSMauro Carvalho Chehab if (sysfs_attrib->grp) { 857b6a280bbSBorislav Petkov debugf4("%s() seeking for group %s\n", 8586fe1108fSMauro Carvalho Chehab __func__, sysfs_attrib->grp->name); 8596fe1108fSMauro Carvalho Chehab list_for_each_entry(grp_kobj, 8606fe1108fSMauro Carvalho Chehab &mci->grp_kobj_list, list) { 861b6a280bbSBorislav Petkov debugf4("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp); 8626fe1108fSMauro Carvalho Chehab if (grp_kobj->grp == sysfs_attrib->grp) { 863b968759eSMauro Carvalho Chehab edac_remove_mci_instance_attributes(mci, 864b968759eSMauro Carvalho Chehab grp_kobj->grp->mcidev_attr, 865b968759eSMauro Carvalho Chehab &grp_kobj->kobj, count + 1); 866b6a280bbSBorislav Petkov debugf4("%s() group %s\n", __func__, 8676fe1108fSMauro Carvalho Chehab sysfs_attrib->grp->name); 8686fe1108fSMauro Carvalho Chehab kobject_put(&grp_kobj->kobj); 8696fe1108fSMauro Carvalho Chehab } 8706fe1108fSMauro Carvalho Chehab } 871b6a280bbSBorislav Petkov debugf4("%s() end of seeking for group %s\n", 8726fe1108fSMauro Carvalho Chehab __func__, sysfs_attrib->grp->name); 8739fa2fc2eSMauro Carvalho Chehab } else if (sysfs_attrib->attr.name) { 874b6a280bbSBorislav Petkov debugf4("%s() file %s\n", __func__, 8759fa2fc2eSMauro Carvalho Chehab sysfs_attrib->attr.name); 8769fa2fc2eSMauro Carvalho Chehab sysfs_remove_file(kobj, &sysfs_attrib->attr); 8779fa2fc2eSMauro Carvalho Chehab } else 8789fa2fc2eSMauro Carvalho Chehab break; 8798096cfafSDoug Thompson sysfs_attrib++; 8808096cfafSDoug Thompson } 881b968759eSMauro Carvalho Chehab 8826fe1108fSMauro Carvalho Chehab /* Remove the group objects */ 8836fe1108fSMauro Carvalho Chehab if (count) 8846fe1108fSMauro Carvalho Chehab return; 8856fe1108fSMauro Carvalho Chehab list_for_each_entry_safe(grp_kobj, tmp, 8866fe1108fSMauro Carvalho Chehab &mci->grp_kobj_list, list) { 8876fe1108fSMauro Carvalho Chehab list_del(&grp_kobj->list); 8886fe1108fSMauro Carvalho Chehab kfree(grp_kobj); 889b968759eSMauro Carvalho Chehab } 8908096cfafSDoug Thompson } 8918096cfafSDoug Thompson 8928096cfafSDoug Thompson 8938096cfafSDoug Thompson /* 8947c9281d7SDouglas Thompson * Create a new Memory Controller kobject instance, 8957c9281d7SDouglas Thompson * mc<id> under the 'mc' directory 8967c9281d7SDouglas Thompson * 8977c9281d7SDouglas Thompson * Return: 8987c9281d7SDouglas Thompson * 0 Success 8997c9281d7SDouglas Thompson * !0 Failure 9007c9281d7SDouglas Thompson */ 9017c9281d7SDouglas Thompson int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) 9027c9281d7SDouglas Thompson { 9037c9281d7SDouglas Thompson int i; 9047c9281d7SDouglas Thompson int err; 9057c9281d7SDouglas Thompson struct csrow_info *csrow; 9068096cfafSDoug Thompson struct kobject *kobj_mci = &mci->edac_mci_kobj; 9077c9281d7SDouglas Thompson 9087c9281d7SDouglas Thompson debugf0("%s() idx=%d\n", __func__, mci->mc_idx); 9097c9281d7SDouglas Thompson 910b968759eSMauro Carvalho Chehab INIT_LIST_HEAD(&mci->grp_kobj_list); 911b968759eSMauro Carvalho Chehab 9127c9281d7SDouglas Thompson /* create a symlink for the device */ 9138096cfafSDoug Thompson err = sysfs_create_link(kobj_mci, &mci->dev->kobj, 9147c9281d7SDouglas Thompson EDAC_DEVICE_SYMLINK); 9158096cfafSDoug Thompson if (err) { 9168096cfafSDoug Thompson debugf1("%s() failure to create symlink\n", __func__); 9177c9281d7SDouglas Thompson goto fail0; 9188096cfafSDoug Thompson } 9197c9281d7SDouglas Thompson 92042a8e397SDouglas Thompson /* If the low level driver desires some attributes, 92142a8e397SDouglas Thompson * then create them now for the driver. 92242a8e397SDouglas Thompson */ 92342a8e397SDouglas Thompson if (mci->mc_driver_sysfs_attributes) { 924cc301b3aSMauro Carvalho Chehab err = edac_create_mci_instance_attributes(mci, 9259fa2fc2eSMauro Carvalho Chehab mci->mc_driver_sysfs_attributes, 9269fa2fc2eSMauro Carvalho Chehab &mci->edac_mci_kobj); 9278096cfafSDoug Thompson if (err) { 9288096cfafSDoug Thompson debugf1("%s() failure to create mci attributes\n", 9298096cfafSDoug Thompson __func__); 93042a8e397SDouglas Thompson goto fail0; 93142a8e397SDouglas Thompson } 9328096cfafSDoug Thompson } 93342a8e397SDouglas Thompson 9348096cfafSDoug Thompson /* Make directories for each CSROW object under the mc<id> kobject 9357c9281d7SDouglas Thompson */ 9367c9281d7SDouglas Thompson for (i = 0; i < mci->nr_csrows; i++) { 9377c9281d7SDouglas Thompson csrow = &mci->csrows[i]; 9387c9281d7SDouglas Thompson 9397c9281d7SDouglas Thompson /* Only expose populated CSROWs */ 9407c9281d7SDouglas Thompson if (csrow->nr_pages > 0) { 9418096cfafSDoug Thompson err = edac_create_csrow_object(mci, csrow, i); 9428096cfafSDoug Thompson if (err) { 9438096cfafSDoug Thompson debugf1("%s() failure: create csrow %d obj\n", 9448096cfafSDoug Thompson __func__, i); 9457c9281d7SDouglas Thompson goto fail1; 9467c9281d7SDouglas Thompson } 9477c9281d7SDouglas Thompson } 9488096cfafSDoug Thompson } 9497c9281d7SDouglas Thompson 9507c9281d7SDouglas Thompson return 0; 9517c9281d7SDouglas Thompson 9527c9281d7SDouglas Thompson /* CSROW error: backout what has already been registered, */ 9537c9281d7SDouglas Thompson fail1: 9547c9281d7SDouglas Thompson for (i--; i >= 0; i--) { 9557c9281d7SDouglas Thompson if (csrow->nr_pages > 0) { 956c10997f6SGreg Kroah-Hartman kobject_put(&mci->csrows[i].kobj); 9577c9281d7SDouglas Thompson } 9587c9281d7SDouglas Thompson } 9597c9281d7SDouglas Thompson 9608096cfafSDoug Thompson /* remove the mci instance's attributes, if any */ 961b968759eSMauro Carvalho Chehab edac_remove_mci_instance_attributes(mci, 962b968759eSMauro Carvalho Chehab mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0); 9638096cfafSDoug Thompson 9648096cfafSDoug Thompson /* remove the symlink */ 9658096cfafSDoug Thompson sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); 9668096cfafSDoug Thompson 9677c9281d7SDouglas Thompson fail0: 9687c9281d7SDouglas Thompson return err; 9697c9281d7SDouglas Thompson } 9707c9281d7SDouglas Thompson 9717c9281d7SDouglas Thompson /* 9727c9281d7SDouglas Thompson * remove a Memory Controller instance 9737c9281d7SDouglas Thompson */ 9747c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) 9757c9281d7SDouglas Thompson { 9767c9281d7SDouglas Thompson int i; 9777c9281d7SDouglas Thompson 9787c9281d7SDouglas Thompson debugf0("%s()\n", __func__); 9797c9281d7SDouglas Thompson 9807c9281d7SDouglas Thompson /* remove all csrow kobjects */ 981b6a280bbSBorislav Petkov debugf4("%s() unregister this mci kobj\n", __func__); 9827c9281d7SDouglas Thompson for (i = 0; i < mci->nr_csrows; i++) { 9837c9281d7SDouglas Thompson if (mci->csrows[i].nr_pages > 0) { 9848096cfafSDoug Thompson debugf0("%s() unreg csrow-%d\n", __func__, i); 985c10997f6SGreg Kroah-Hartman kobject_put(&mci->csrows[i].kobj); 9867c9281d7SDouglas Thompson } 9877c9281d7SDouglas Thompson } 9887c9281d7SDouglas Thompson 9898096cfafSDoug Thompson /* remove this mci instance's attribtes */ 9906fe1108fSMauro Carvalho Chehab if (mci->mc_driver_sysfs_attributes) { 991b6a280bbSBorislav Petkov debugf4("%s() unregister mci private attributes\n", __func__); 992b968759eSMauro Carvalho Chehab edac_remove_mci_instance_attributes(mci, 993b968759eSMauro Carvalho Chehab mci->mc_driver_sysfs_attributes, 994b968759eSMauro Carvalho Chehab &mci->edac_mci_kobj, 0); 9956fe1108fSMauro Carvalho Chehab } 9966fe1108fSMauro Carvalho Chehab 9976fe1108fSMauro Carvalho Chehab /* remove the symlink */ 998b6a280bbSBorislav Petkov debugf4("%s() remove_link\n", __func__); 9996fe1108fSMauro Carvalho Chehab sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); 10008096cfafSDoug Thompson 10018096cfafSDoug Thompson /* unregister this instance's kobject */ 1002b6a280bbSBorislav Petkov debugf4("%s() remove_mci_instance\n", __func__); 1003c10997f6SGreg Kroah-Hartman kobject_put(&mci->edac_mci_kobj); 10047c9281d7SDouglas Thompson } 10058096cfafSDoug Thompson 10068096cfafSDoug Thompson 10078096cfafSDoug Thompson 10088096cfafSDoug Thompson 10098096cfafSDoug Thompson /* 10108096cfafSDoug Thompson * edac_setup_sysfs_mc_kset(void) 10118096cfafSDoug Thompson * 10128096cfafSDoug Thompson * Initialize the mc_kset for the 'mc' entry 10138096cfafSDoug Thompson * This requires creating the top 'mc' directory with a kset 10148096cfafSDoug Thompson * and its controls/attributes. 10158096cfafSDoug Thompson * 10168096cfafSDoug Thompson * To this 'mc' kset, instance 'mci' will be grouped as children. 10178096cfafSDoug Thompson * 10188096cfafSDoug Thompson * Return: 0 SUCCESS 10198096cfafSDoug Thompson * !0 FAILURE error code 10208096cfafSDoug Thompson */ 10218096cfafSDoug Thompson int edac_sysfs_setup_mc_kset(void) 10228096cfafSDoug Thompson { 1023ca755e0aSBorislav Petkov int err = -EINVAL; 1024fe5ff8b8SKay Sievers struct bus_type *edac_subsys; 10258096cfafSDoug Thompson 10268096cfafSDoug Thompson debugf1("%s()\n", __func__); 10278096cfafSDoug Thompson 1028fe5ff8b8SKay Sievers /* get the /sys/devices/system/edac subsys reference */ 1029fe5ff8b8SKay Sievers edac_subsys = edac_get_sysfs_subsys(); 1030fe5ff8b8SKay Sievers if (edac_subsys == NULL) { 1031fe5ff8b8SKay Sievers debugf1("%s() no edac_subsys error=%d\n", __func__, err); 10328096cfafSDoug Thompson goto fail_out; 10338096cfafSDoug Thompson } 10348096cfafSDoug Thompson 10358096cfafSDoug Thompson /* Init the MC's kobject */ 1036fe5ff8b8SKay Sievers mc_kset = kset_create_and_add("mc", NULL, &edac_subsys->dev_root->kobj); 1037f9fc82adSArthur Jones if (!mc_kset) { 1038f9fc82adSArthur Jones err = -ENOMEM; 10398096cfafSDoug Thompson debugf1("%s() Failed to register '.../edac/mc'\n", __func__); 104030e1f7a8SBorislav Petkov goto fail_kset; 10418096cfafSDoug Thompson } 10428096cfafSDoug Thompson 10438096cfafSDoug Thompson debugf1("%s() Registered '.../edac/mc' kobject\n", __func__); 10448096cfafSDoug Thompson 10458096cfafSDoug Thompson return 0; 10468096cfafSDoug Thompson 104730e1f7a8SBorislav Petkov fail_kset: 1048fe5ff8b8SKay Sievers edac_put_sysfs_subsys(); 10498096cfafSDoug Thompson 10508096cfafSDoug Thompson fail_out: 10518096cfafSDoug Thompson return err; 10528096cfafSDoug Thompson } 10538096cfafSDoug Thompson 10548096cfafSDoug Thompson /* 10558096cfafSDoug Thompson * edac_sysfs_teardown_mc_kset 10568096cfafSDoug Thompson * 10578096cfafSDoug Thompson * deconstruct the mc_ket for memory controllers 10588096cfafSDoug Thompson */ 10598096cfafSDoug Thompson void edac_sysfs_teardown_mc_kset(void) 10608096cfafSDoug Thompson { 1061f9fc82adSArthur Jones kset_unregister(mc_kset); 1062fe5ff8b8SKay Sievers edac_put_sysfs_subsys(); 10638096cfafSDoug Thompson } 10648096cfafSDoug Thompson 1065