17c9281d7SDouglas Thompson /* 27c9281d7SDouglas Thompson * edac_mc kernel module 342a8e397SDouglas Thompson * (C) 2005-2007 Linux Networx (http://lnxi.com) 442a8e397SDouglas Thompson * 57c9281d7SDouglas Thompson * This file may be distributed under the terms of the 67c9281d7SDouglas Thompson * GNU General Public License. 77c9281d7SDouglas Thompson * 842a8e397SDouglas Thompson * Written Doug Thompson <norsk5@xmission.com> www.softwarebitmaker.com 97c9281d7SDouglas Thompson * 107c9281d7SDouglas Thompson */ 117c9281d7SDouglas Thompson 127c9281d7SDouglas Thompson #include <linux/ctype.h> 138096cfafSDoug Thompson #include <linux/bug.h> 147c9281d7SDouglas Thompson 1520bcb7a8SDouglas Thompson #include "edac_core.h" 167c9281d7SDouglas Thompson #include "edac_module.h" 177c9281d7SDouglas Thompson 188096cfafSDoug Thompson 197c9281d7SDouglas Thompson /* MC EDAC Controls, setable by module parameter, and sysfs */ 204de78c68SDave Jiang static int edac_mc_log_ue = 1; 214de78c68SDave Jiang static int edac_mc_log_ce = 1; 22f044091cSDouglas Thompson static int edac_mc_panic_on_ue; 234de78c68SDave Jiang static int edac_mc_poll_msec = 1000; 247c9281d7SDouglas Thompson 257c9281d7SDouglas Thompson /* Getter functions for above */ 264de78c68SDave Jiang int edac_mc_get_log_ue(void) 277c9281d7SDouglas Thompson { 284de78c68SDave Jiang return edac_mc_log_ue; 297c9281d7SDouglas Thompson } 307c9281d7SDouglas Thompson 314de78c68SDave Jiang int edac_mc_get_log_ce(void) 327c9281d7SDouglas Thompson { 334de78c68SDave Jiang return edac_mc_log_ce; 347c9281d7SDouglas Thompson } 357c9281d7SDouglas Thompson 364de78c68SDave Jiang int edac_mc_get_panic_on_ue(void) 377c9281d7SDouglas Thompson { 384de78c68SDave Jiang return edac_mc_panic_on_ue; 397c9281d7SDouglas Thompson } 407c9281d7SDouglas Thompson 4181d87cb1SDave Jiang /* this is temporary */ 4281d87cb1SDave Jiang int edac_mc_get_poll_msec(void) 4381d87cb1SDave Jiang { 444de78c68SDave Jiang return edac_mc_poll_msec; 457c9281d7SDouglas Thompson } 467c9281d7SDouglas Thompson 477c9281d7SDouglas Thompson /* Parameter declarations for above */ 484de78c68SDave Jiang module_param(edac_mc_panic_on_ue, int, 0644); 494de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); 504de78c68SDave Jiang module_param(edac_mc_log_ue, int, 0644); 514de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ue, 524de78c68SDave Jiang "Log uncorrectable error to console: 0=off 1=on"); 534de78c68SDave Jiang module_param(edac_mc_log_ce, int, 0644); 544de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_log_ce, 554de78c68SDave Jiang "Log correctable error to console: 0=off 1=on"); 564de78c68SDave Jiang module_param(edac_mc_poll_msec, int, 0644); 574de78c68SDave Jiang MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); 587c9281d7SDouglas Thompson 597c9281d7SDouglas Thompson /* 607c9281d7SDouglas Thompson * various constants for Memory Controllers 617c9281d7SDouglas Thompson */ 627c9281d7SDouglas Thompson static const char *mem_types[] = { 637c9281d7SDouglas Thompson [MEM_EMPTY] = "Empty", 647c9281d7SDouglas Thompson [MEM_RESERVED] = "Reserved", 657c9281d7SDouglas Thompson [MEM_UNKNOWN] = "Unknown", 667c9281d7SDouglas Thompson [MEM_FPM] = "FPM", 677c9281d7SDouglas Thompson [MEM_EDO] = "EDO", 687c9281d7SDouglas Thompson [MEM_BEDO] = "BEDO", 697c9281d7SDouglas Thompson [MEM_SDR] = "Unbuffered-SDR", 707c9281d7SDouglas Thompson [MEM_RDR] = "Registered-SDR", 717c9281d7SDouglas Thompson [MEM_DDR] = "Unbuffered-DDR", 727c9281d7SDouglas Thompson [MEM_RDDR] = "Registered-DDR", 731a9b85e6SDave Jiang [MEM_RMBS] = "RMBS", 741a9b85e6SDave Jiang [MEM_DDR2] = "Unbuffered-DDR2", 751a9b85e6SDave Jiang [MEM_FB_DDR2] = "FullyBuffered-DDR2", 761a9b85e6SDave Jiang [MEM_RDDR2] = "Registered-DDR2" 777c9281d7SDouglas Thompson }; 787c9281d7SDouglas Thompson 797c9281d7SDouglas Thompson static const char *dev_types[] = { 807c9281d7SDouglas Thompson [DEV_UNKNOWN] = "Unknown", 817c9281d7SDouglas Thompson [DEV_X1] = "x1", 827c9281d7SDouglas Thompson [DEV_X2] = "x2", 837c9281d7SDouglas Thompson [DEV_X4] = "x4", 847c9281d7SDouglas Thompson [DEV_X8] = "x8", 857c9281d7SDouglas Thompson [DEV_X16] = "x16", 867c9281d7SDouglas Thompson [DEV_X32] = "x32", 877c9281d7SDouglas Thompson [DEV_X64] = "x64" 887c9281d7SDouglas Thompson }; 897c9281d7SDouglas Thompson 907c9281d7SDouglas Thompson static const char *edac_caps[] = { 917c9281d7SDouglas Thompson [EDAC_UNKNOWN] = "Unknown", 927c9281d7SDouglas Thompson [EDAC_NONE] = "None", 937c9281d7SDouglas Thompson [EDAC_RESERVED] = "Reserved", 947c9281d7SDouglas Thompson [EDAC_PARITY] = "PARITY", 957c9281d7SDouglas Thompson [EDAC_EC] = "EC", 967c9281d7SDouglas Thompson [EDAC_SECDED] = "SECDED", 977c9281d7SDouglas Thompson [EDAC_S2ECD2ED] = "S2ECD2ED", 987c9281d7SDouglas Thompson [EDAC_S4ECD4ED] = "S4ECD4ED", 997c9281d7SDouglas Thompson [EDAC_S8ECD8ED] = "S8ECD8ED", 1007c9281d7SDouglas Thompson [EDAC_S16ECD16ED] = "S16ECD16ED" 1017c9281d7SDouglas Thompson }; 1027c9281d7SDouglas Thompson 1037c9281d7SDouglas Thompson 1047c9281d7SDouglas Thompson 1057c9281d7SDouglas Thompson /* 1067c9281d7SDouglas Thompson * /sys/devices/system/edac/mc; 1077c9281d7SDouglas Thompson * data structures and methods 1087c9281d7SDouglas Thompson */ 1097c9281d7SDouglas Thompson static ssize_t memctrl_int_show(void *ptr, char *buffer) 1107c9281d7SDouglas Thompson { 1117c9281d7SDouglas Thompson int *value = (int *)ptr; 1127c9281d7SDouglas Thompson return sprintf(buffer, "%u\n", *value); 1137c9281d7SDouglas Thompson } 1147c9281d7SDouglas Thompson 1157c9281d7SDouglas Thompson static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) 1167c9281d7SDouglas Thompson { 1177c9281d7SDouglas Thompson int *value = (int *)ptr; 1187c9281d7SDouglas Thompson 1197c9281d7SDouglas Thompson if (isdigit(*buffer)) 1207c9281d7SDouglas Thompson *value = simple_strtoul(buffer, NULL, 0); 1217c9281d7SDouglas Thompson 1227c9281d7SDouglas Thompson return count; 1237c9281d7SDouglas Thompson } 1247c9281d7SDouglas Thompson 1257c9281d7SDouglas Thompson 1267c9281d7SDouglas Thompson /* EDAC sysfs CSROW data structures and methods 1277c9281d7SDouglas Thompson */ 1287c9281d7SDouglas Thompson 1297c9281d7SDouglas Thompson /* Set of more default csrow<id> attribute show/store functions */ 130e27e3dacSDouglas Thompson static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, 131e27e3dacSDouglas Thompson int private) 1327c9281d7SDouglas Thompson { 1337c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->ue_count); 1347c9281d7SDouglas Thompson } 1357c9281d7SDouglas Thompson 136e27e3dacSDouglas Thompson static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, 137e27e3dacSDouglas Thompson int private) 1387c9281d7SDouglas Thompson { 1397c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->ce_count); 1407c9281d7SDouglas Thompson } 1417c9281d7SDouglas Thompson 142e27e3dacSDouglas Thompson static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, 143e27e3dacSDouglas Thompson int private) 1447c9281d7SDouglas Thompson { 1457c9281d7SDouglas Thompson return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages)); 1467c9281d7SDouglas Thompson } 1477c9281d7SDouglas Thompson 148e27e3dacSDouglas Thompson static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, 149e27e3dacSDouglas Thompson int private) 1507c9281d7SDouglas Thompson { 1517c9281d7SDouglas Thompson return sprintf(data, "%s\n", mem_types[csrow->mtype]); 1527c9281d7SDouglas Thompson } 1537c9281d7SDouglas Thompson 154e27e3dacSDouglas Thompson static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, 155e27e3dacSDouglas Thompson int private) 1567c9281d7SDouglas Thompson { 1577c9281d7SDouglas Thompson return sprintf(data, "%s\n", dev_types[csrow->dtype]); 1587c9281d7SDouglas Thompson } 1597c9281d7SDouglas Thompson 160e27e3dacSDouglas Thompson static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, 161e27e3dacSDouglas Thompson int private) 1627c9281d7SDouglas Thompson { 1637c9281d7SDouglas Thompson return sprintf(data, "%s\n", edac_caps[csrow->edac_mode]); 1647c9281d7SDouglas Thompson } 1657c9281d7SDouglas Thompson 1667c9281d7SDouglas Thompson /* show/store functions for DIMM Label attributes */ 1677c9281d7SDouglas Thompson static ssize_t channel_dimm_label_show(struct csrow_info *csrow, 1687c9281d7SDouglas Thompson char *data, int channel) 1697c9281d7SDouglas Thompson { 1707c9281d7SDouglas Thompson return snprintf(data, EDAC_MC_LABEL_LEN, "%s", 1717c9281d7SDouglas Thompson csrow->channels[channel].label); 1727c9281d7SDouglas Thompson } 1737c9281d7SDouglas Thompson 1747c9281d7SDouglas Thompson static ssize_t channel_dimm_label_store(struct csrow_info *csrow, 1757c9281d7SDouglas Thompson const char *data, 176079708b9SDouglas Thompson size_t count, int channel) 1777c9281d7SDouglas Thompson { 1787c9281d7SDouglas Thompson ssize_t max_size = 0; 1797c9281d7SDouglas Thompson 1807c9281d7SDouglas Thompson max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); 1817c9281d7SDouglas Thompson strncpy(csrow->channels[channel].label, data, max_size); 1827c9281d7SDouglas Thompson csrow->channels[channel].label[max_size] = '\0'; 1837c9281d7SDouglas Thompson 1847c9281d7SDouglas Thompson return max_size; 1857c9281d7SDouglas Thompson } 1867c9281d7SDouglas Thompson 1877c9281d7SDouglas Thompson /* show function for dynamic chX_ce_count attribute */ 1887c9281d7SDouglas Thompson static ssize_t channel_ce_count_show(struct csrow_info *csrow, 189079708b9SDouglas Thompson char *data, int channel) 1907c9281d7SDouglas Thompson { 1917c9281d7SDouglas Thompson return sprintf(data, "%u\n", csrow->channels[channel].ce_count); 1927c9281d7SDouglas Thompson } 1937c9281d7SDouglas Thompson 1947c9281d7SDouglas Thompson /* csrow specific attribute structure */ 1957c9281d7SDouglas Thompson struct csrowdev_attribute { 1967c9281d7SDouglas Thompson struct attribute attr; 1977c9281d7SDouglas Thompson ssize_t(*show) (struct csrow_info *, char *, int); 1987c9281d7SDouglas Thompson ssize_t(*store) (struct csrow_info *, const char *, size_t, int); 1997c9281d7SDouglas Thompson int private; 2007c9281d7SDouglas Thompson }; 2017c9281d7SDouglas Thompson 2027c9281d7SDouglas Thompson #define to_csrow(k) container_of(k, struct csrow_info, kobj) 2037c9281d7SDouglas Thompson #define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) 2047c9281d7SDouglas Thompson 2057c9281d7SDouglas Thompson /* Set of show/store higher level functions for default csrow attributes */ 2067c9281d7SDouglas Thompson static ssize_t csrowdev_show(struct kobject *kobj, 207079708b9SDouglas Thompson struct attribute *attr, char *buffer) 2087c9281d7SDouglas Thompson { 2097c9281d7SDouglas Thompson struct csrow_info *csrow = to_csrow(kobj); 2107c9281d7SDouglas Thompson struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); 2117c9281d7SDouglas Thompson 2127c9281d7SDouglas Thompson if (csrowdev_attr->show) 2137c9281d7SDouglas Thompson return csrowdev_attr->show(csrow, 214079708b9SDouglas Thompson buffer, csrowdev_attr->private); 2157c9281d7SDouglas Thompson return -EIO; 2167c9281d7SDouglas Thompson } 2177c9281d7SDouglas Thompson 2187c9281d7SDouglas Thompson static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, 2197c9281d7SDouglas Thompson const char *buffer, size_t count) 2207c9281d7SDouglas Thompson { 2217c9281d7SDouglas Thompson struct csrow_info *csrow = to_csrow(kobj); 2227c9281d7SDouglas Thompson struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); 2237c9281d7SDouglas Thompson 2247c9281d7SDouglas Thompson if (csrowdev_attr->store) 2257c9281d7SDouglas Thompson return csrowdev_attr->store(csrow, 2267c9281d7SDouglas Thompson buffer, 227079708b9SDouglas Thompson count, csrowdev_attr->private); 2287c9281d7SDouglas Thompson return -EIO; 2297c9281d7SDouglas Thompson } 2307c9281d7SDouglas Thompson 2317c9281d7SDouglas Thompson static struct sysfs_ops csrowfs_ops = { 2327c9281d7SDouglas Thompson .show = csrowdev_show, 2337c9281d7SDouglas Thompson .store = csrowdev_store 2347c9281d7SDouglas Thompson }; 2357c9281d7SDouglas Thompson 2367c9281d7SDouglas Thompson #define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ 2377c9281d7SDouglas Thompson static struct csrowdev_attribute attr_##_name = { \ 2387c9281d7SDouglas Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 2397c9281d7SDouglas Thompson .show = _show, \ 2407c9281d7SDouglas Thompson .store = _store, \ 2417c9281d7SDouglas Thompson .private = _private, \ 2427c9281d7SDouglas Thompson }; 2437c9281d7SDouglas Thompson 2447c9281d7SDouglas Thompson /* default cwrow<id>/attribute files */ 2457c9281d7SDouglas Thompson CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0); 2467c9281d7SDouglas Thompson CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0); 2477c9281d7SDouglas Thompson CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0); 2487c9281d7SDouglas Thompson CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0); 2497c9281d7SDouglas Thompson CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0); 2507c9281d7SDouglas Thompson CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0); 2517c9281d7SDouglas Thompson 2527c9281d7SDouglas Thompson /* default attributes of the CSROW<id> object */ 2537c9281d7SDouglas Thompson static struct csrowdev_attribute *default_csrow_attr[] = { 2547c9281d7SDouglas Thompson &attr_dev_type, 2557c9281d7SDouglas Thompson &attr_mem_type, 2567c9281d7SDouglas Thompson &attr_edac_mode, 2577c9281d7SDouglas Thompson &attr_size_mb, 2587c9281d7SDouglas Thompson &attr_ue_count, 2597c9281d7SDouglas Thompson &attr_ce_count, 2607c9281d7SDouglas Thompson NULL, 2617c9281d7SDouglas Thompson }; 2627c9281d7SDouglas Thompson 2637c9281d7SDouglas Thompson /* possible dynamic channel DIMM Label attribute files */ 2647c9281d7SDouglas Thompson CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR, 265079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 0); 2667c9281d7SDouglas Thompson CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR, 267079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 1); 2687c9281d7SDouglas Thompson CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR, 269079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 2); 2707c9281d7SDouglas Thompson CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR, 271079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 3); 2727c9281d7SDouglas Thompson CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR, 273079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 4); 2747c9281d7SDouglas Thompson CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR, 275079708b9SDouglas Thompson channel_dimm_label_show, channel_dimm_label_store, 5); 2767c9281d7SDouglas Thompson 2777c9281d7SDouglas Thompson /* Total possible dynamic DIMM Label attribute file table */ 2787c9281d7SDouglas Thompson static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { 2797c9281d7SDouglas Thompson &attr_ch0_dimm_label, 2807c9281d7SDouglas Thompson &attr_ch1_dimm_label, 2817c9281d7SDouglas Thompson &attr_ch2_dimm_label, 2827c9281d7SDouglas Thompson &attr_ch3_dimm_label, 2837c9281d7SDouglas Thompson &attr_ch4_dimm_label, 2847c9281d7SDouglas Thompson &attr_ch5_dimm_label 2857c9281d7SDouglas Thompson }; 2867c9281d7SDouglas Thompson 2877c9281d7SDouglas Thompson /* possible dynamic channel ce_count attribute files */ 288079708b9SDouglas Thompson CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0); 289079708b9SDouglas Thompson CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1); 290079708b9SDouglas Thompson CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2); 291079708b9SDouglas Thompson CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3); 292079708b9SDouglas Thompson CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4); 293079708b9SDouglas Thompson CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5); 2947c9281d7SDouglas Thompson 2957c9281d7SDouglas Thompson /* Total possible dynamic ce_count attribute file table */ 2967c9281d7SDouglas Thompson static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { 2977c9281d7SDouglas Thompson &attr_ch0_ce_count, 2987c9281d7SDouglas Thompson &attr_ch1_ce_count, 2997c9281d7SDouglas Thompson &attr_ch2_ce_count, 3007c9281d7SDouglas Thompson &attr_ch3_ce_count, 3017c9281d7SDouglas Thompson &attr_ch4_ce_count, 3027c9281d7SDouglas Thompson &attr_ch5_ce_count 3037c9281d7SDouglas Thompson }; 3047c9281d7SDouglas Thompson 3057c9281d7SDouglas Thompson #define EDAC_NR_CHANNELS 6 3067c9281d7SDouglas Thompson 3077c9281d7SDouglas Thompson /* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ 3087c9281d7SDouglas Thompson static int edac_create_channel_files(struct kobject *kobj, int chan) 3097c9281d7SDouglas Thompson { 3107c9281d7SDouglas Thompson int err = -ENODEV; 3117c9281d7SDouglas Thompson 3127c9281d7SDouglas Thompson if (chan >= EDAC_NR_CHANNELS) 3137c9281d7SDouglas Thompson return err; 3147c9281d7SDouglas Thompson 3157c9281d7SDouglas Thompson /* create the DIMM label attribute file */ 3167c9281d7SDouglas Thompson err = sysfs_create_file(kobj, 317079708b9SDouglas Thompson (struct attribute *) 318079708b9SDouglas Thompson dynamic_csrow_dimm_attr[chan]); 3197c9281d7SDouglas Thompson 3207c9281d7SDouglas Thompson if (!err) { 3217c9281d7SDouglas Thompson /* create the CE Count attribute file */ 3227c9281d7SDouglas Thompson err = sysfs_create_file(kobj, 323079708b9SDouglas Thompson (struct attribute *) 324079708b9SDouglas Thompson dynamic_csrow_ce_count_attr[chan]); 3257c9281d7SDouglas Thompson } else { 326e27e3dacSDouglas Thompson debugf1("%s() dimm labels and ce_count files created", 327e27e3dacSDouglas Thompson __func__); 3287c9281d7SDouglas Thompson } 3297c9281d7SDouglas Thompson 3307c9281d7SDouglas Thompson return err; 3317c9281d7SDouglas Thompson } 3327c9281d7SDouglas Thompson 3337c9281d7SDouglas Thompson /* No memory to release for this kobj */ 3347c9281d7SDouglas Thompson static void edac_csrow_instance_release(struct kobject *kobj) 3357c9281d7SDouglas Thompson { 3368096cfafSDoug Thompson struct mem_ctl_info *mci; 3377c9281d7SDouglas Thompson struct csrow_info *cs; 3387c9281d7SDouglas Thompson 3398096cfafSDoug Thompson debugf1("%s()\n", __func__); 3408096cfafSDoug Thompson 3417c9281d7SDouglas Thompson cs = container_of(kobj, struct csrow_info, kobj); 3428096cfafSDoug Thompson mci = cs->mci; 3438096cfafSDoug Thompson 3448096cfafSDoug Thompson kobject_put(&mci->edac_mci_kobj); 3457c9281d7SDouglas Thompson } 3467c9281d7SDouglas Thompson 3477c9281d7SDouglas Thompson /* the kobj_type instance for a CSROW */ 3487c9281d7SDouglas Thompson static struct kobj_type ktype_csrow = { 3497c9281d7SDouglas Thompson .release = edac_csrow_instance_release, 3507c9281d7SDouglas Thompson .sysfs_ops = &csrowfs_ops, 3517c9281d7SDouglas Thompson .default_attrs = (struct attribute **)default_csrow_attr, 3527c9281d7SDouglas Thompson }; 3537c9281d7SDouglas Thompson 3547c9281d7SDouglas Thompson /* Create a CSROW object under specifed edac_mc_device */ 3558096cfafSDoug Thompson static int edac_create_csrow_object(struct mem_ctl_info *mci, 356079708b9SDouglas Thompson struct csrow_info *csrow, int index) 3577c9281d7SDouglas Thompson { 3588096cfafSDoug Thompson struct kobject *kobj_mci = &mci->edac_mci_kobj; 3598096cfafSDoug Thompson struct kobject *kobj; 3607c9281d7SDouglas Thompson int chan; 3618096cfafSDoug Thompson int err; 3627c9281d7SDouglas Thompson 3637c9281d7SDouglas Thompson /* generate ..../edac/mc/mc<id>/csrow<index> */ 3648096cfafSDoug Thompson memset(&csrow->kobj, 0, sizeof(csrow->kobj)); 3658096cfafSDoug Thompson csrow->mci = mci; /* include container up link */ 3668096cfafSDoug Thompson csrow->kobj.parent = kobj_mci; 3677c9281d7SDouglas Thompson csrow->kobj.ktype = &ktype_csrow; 3687c9281d7SDouglas Thompson 3697c9281d7SDouglas Thompson /* name this instance of csrow<id> */ 3707c9281d7SDouglas Thompson err = kobject_set_name(&csrow->kobj, "csrow%d", index); 3717c9281d7SDouglas Thompson if (err) 3728096cfafSDoug Thompson goto err_out; 3738096cfafSDoug Thompson 3748096cfafSDoug Thompson /* bump the mci instance's kobject's ref count */ 3758096cfafSDoug Thompson kobj = kobject_get(&mci->edac_mci_kobj); 3768096cfafSDoug Thompson if (!kobj) { 3778096cfafSDoug Thompson err = -ENODEV; 3788096cfafSDoug Thompson goto err_out; 3798096cfafSDoug Thompson } 3807c9281d7SDouglas Thompson 3817c9281d7SDouglas Thompson /* Instanstiate the csrow object */ 3827c9281d7SDouglas Thompson err = kobject_register(&csrow->kobj); 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 3878096cfafSDoug Thompson * call the kobject_unregister 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 */ 3988096cfafSDoug Thompson kobject_unregister(&csrow->kobj); 3998096cfafSDoug Thompson goto err_out; 4007c9281d7SDouglas Thompson } 4017c9281d7SDouglas Thompson } 4027c9281d7SDouglas Thompson 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 4397c9281d7SDouglas Thompson /* memory scrubbing */ 4407c9281d7SDouglas Thompson static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, 4417c9281d7SDouglas Thompson const char *data, size_t count) 4427c9281d7SDouglas Thompson { 4437c9281d7SDouglas Thompson u32 bandwidth = -1; 4447c9281d7SDouglas Thompson 4457c9281d7SDouglas Thompson if (mci->set_sdram_scrub_rate) { 4467c9281d7SDouglas Thompson 4477c9281d7SDouglas Thompson memctrl_int_store(&bandwidth, data, count); 4487c9281d7SDouglas Thompson 4497c9281d7SDouglas Thompson if (!(*mci->set_sdram_scrub_rate) (mci, &bandwidth)) { 4507c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4517c9281d7SDouglas Thompson "Scrub rate set successfully, applied: %d\n", 4527c9281d7SDouglas Thompson bandwidth); 4537c9281d7SDouglas Thompson } else { 4547c9281d7SDouglas Thompson /* FIXME: error codes maybe? */ 4557c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4567c9281d7SDouglas Thompson "Scrub rate set FAILED, could not apply: %d\n", 4577c9281d7SDouglas Thompson bandwidth); 4587c9281d7SDouglas Thompson } 4597c9281d7SDouglas Thompson } else { 4607c9281d7SDouglas Thompson /* FIXME: produce "not implemented" ERROR for user-side. */ 4617c9281d7SDouglas Thompson edac_printk(KERN_WARNING, EDAC_MC, 4627c9281d7SDouglas Thompson "Memory scrubbing 'set'control is not implemented!\n"); 4637c9281d7SDouglas Thompson } 4647c9281d7SDouglas Thompson return count; 4657c9281d7SDouglas Thompson } 4667c9281d7SDouglas Thompson 4677c9281d7SDouglas Thompson static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) 4687c9281d7SDouglas Thompson { 4697c9281d7SDouglas Thompson u32 bandwidth = -1; 4707c9281d7SDouglas Thompson 4717c9281d7SDouglas Thompson if (mci->get_sdram_scrub_rate) { 4727c9281d7SDouglas Thompson if (!(*mci->get_sdram_scrub_rate) (mci, &bandwidth)) { 4737c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4747c9281d7SDouglas Thompson "Scrub rate successfully, fetched: %d\n", 4757c9281d7SDouglas Thompson bandwidth); 4767c9281d7SDouglas Thompson } else { 4777c9281d7SDouglas Thompson /* FIXME: error codes maybe? */ 4787c9281d7SDouglas Thompson edac_printk(KERN_DEBUG, EDAC_MC, 4797c9281d7SDouglas Thompson "Scrub rate fetch FAILED, got: %d\n", 4807c9281d7SDouglas Thompson bandwidth); 4817c9281d7SDouglas Thompson } 4827c9281d7SDouglas Thompson } else { 4837c9281d7SDouglas Thompson /* FIXME: produce "not implemented" ERROR for user-side. */ 4847c9281d7SDouglas Thompson edac_printk(KERN_WARNING, EDAC_MC, 485e27e3dacSDouglas Thompson "Memory scrubbing 'get' control is not implemented\n"); 4867c9281d7SDouglas Thompson } 4877c9281d7SDouglas Thompson return sprintf(data, "%d\n", bandwidth); 4887c9281d7SDouglas Thompson } 4897c9281d7SDouglas Thompson 4907c9281d7SDouglas Thompson /* default attribute files for the MCI object */ 4917c9281d7SDouglas Thompson static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) 4927c9281d7SDouglas Thompson { 4937c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ue_count); 4947c9281d7SDouglas Thompson } 4957c9281d7SDouglas Thompson 4967c9281d7SDouglas Thompson static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) 4977c9281d7SDouglas Thompson { 4987c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ce_count); 4997c9281d7SDouglas Thompson } 5007c9281d7SDouglas Thompson 5017c9281d7SDouglas Thompson static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) 5027c9281d7SDouglas Thompson { 5037c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ce_noinfo_count); 5047c9281d7SDouglas Thompson } 5057c9281d7SDouglas Thompson 5067c9281d7SDouglas Thompson static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) 5077c9281d7SDouglas Thompson { 5087c9281d7SDouglas Thompson return sprintf(data, "%d\n", mci->ue_noinfo_count); 5097c9281d7SDouglas Thompson } 5107c9281d7SDouglas Thompson 5117c9281d7SDouglas Thompson static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) 5127c9281d7SDouglas Thompson { 5137c9281d7SDouglas Thompson return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ); 5147c9281d7SDouglas Thompson } 5157c9281d7SDouglas Thompson 5167c9281d7SDouglas Thompson static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) 5177c9281d7SDouglas Thompson { 5187c9281d7SDouglas Thompson return sprintf(data, "%s\n", mci->ctl_name); 5197c9281d7SDouglas Thompson } 5207c9281d7SDouglas Thompson 5217c9281d7SDouglas Thompson static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) 5227c9281d7SDouglas Thompson { 5237c9281d7SDouglas Thompson int total_pages, csrow_idx; 5247c9281d7SDouglas Thompson 5257c9281d7SDouglas Thompson for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows; 5267c9281d7SDouglas Thompson csrow_idx++) { 5277c9281d7SDouglas Thompson struct csrow_info *csrow = &mci->csrows[csrow_idx]; 5287c9281d7SDouglas Thompson 5297c9281d7SDouglas Thompson if (!csrow->nr_pages) 5307c9281d7SDouglas Thompson continue; 5317c9281d7SDouglas Thompson 5327c9281d7SDouglas Thompson total_pages += csrow->nr_pages; 5337c9281d7SDouglas Thompson } 5347c9281d7SDouglas Thompson 5357c9281d7SDouglas Thompson return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); 5367c9281d7SDouglas Thompson } 5377c9281d7SDouglas Thompson 5387c9281d7SDouglas Thompson #define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) 53942a8e397SDouglas Thompson #define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr) 5407c9281d7SDouglas Thompson 5417c9281d7SDouglas Thompson /* MCI show/store functions for top most object */ 5427c9281d7SDouglas Thompson static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, 5437c9281d7SDouglas Thompson char *buffer) 5447c9281d7SDouglas Thompson { 5457c9281d7SDouglas Thompson struct mem_ctl_info *mem_ctl_info = to_mci(kobj); 54642a8e397SDouglas Thompson struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 5477c9281d7SDouglas Thompson 5487c9281d7SDouglas Thompson if (mcidev_attr->show) 5497c9281d7SDouglas Thompson return mcidev_attr->show(mem_ctl_info, buffer); 5507c9281d7SDouglas Thompson 5517c9281d7SDouglas Thompson return -EIO; 5527c9281d7SDouglas Thompson } 5537c9281d7SDouglas Thompson 5547c9281d7SDouglas Thompson static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, 5557c9281d7SDouglas Thompson const char *buffer, size_t count) 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->store) 5617c9281d7SDouglas Thompson return mcidev_attr->store(mem_ctl_info, buffer, count); 5627c9281d7SDouglas Thompson 5637c9281d7SDouglas Thompson return -EIO; 5647c9281d7SDouglas Thompson } 5657c9281d7SDouglas Thompson 5668096cfafSDoug Thompson /* Intermediate show/store table */ 5677c9281d7SDouglas Thompson static struct sysfs_ops mci_ops = { 5687c9281d7SDouglas Thompson .show = mcidev_show, 5697c9281d7SDouglas Thompson .store = mcidev_store 5707c9281d7SDouglas Thompson }; 5717c9281d7SDouglas Thompson 5727c9281d7SDouglas Thompson #define MCIDEV_ATTR(_name,_mode,_show,_store) \ 57342a8e397SDouglas Thompson static struct mcidev_sysfs_attribute mci_attr_##_name = { \ 5747c9281d7SDouglas Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 5757c9281d7SDouglas Thompson .show = _show, \ 5767c9281d7SDouglas Thompson .store = _store, \ 5777c9281d7SDouglas Thompson }; 5787c9281d7SDouglas Thompson 5797c9281d7SDouglas Thompson /* default Control file */ 5807c9281d7SDouglas Thompson MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); 5817c9281d7SDouglas Thompson 5827c9281d7SDouglas Thompson /* default Attribute files */ 5837c9281d7SDouglas Thompson MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); 5847c9281d7SDouglas Thompson MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); 5857c9281d7SDouglas Thompson MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); 5867c9281d7SDouglas Thompson MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); 5877c9281d7SDouglas Thompson MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); 5887c9281d7SDouglas Thompson MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); 5897c9281d7SDouglas Thompson MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); 5907c9281d7SDouglas Thompson 5917c9281d7SDouglas Thompson /* memory scrubber attribute file */ 592079708b9SDouglas Thompson MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, 593e27e3dacSDouglas Thompson mci_sdram_scrub_rate_store); 5947c9281d7SDouglas Thompson 59542a8e397SDouglas Thompson static struct mcidev_sysfs_attribute *mci_attr[] = { 5967c9281d7SDouglas Thompson &mci_attr_reset_counters, 5977c9281d7SDouglas Thompson &mci_attr_mc_name, 5987c9281d7SDouglas Thompson &mci_attr_size_mb, 5997c9281d7SDouglas Thompson &mci_attr_seconds_since_reset, 6007c9281d7SDouglas Thompson &mci_attr_ue_noinfo_count, 6017c9281d7SDouglas Thompson &mci_attr_ce_noinfo_count, 6027c9281d7SDouglas Thompson &mci_attr_ue_count, 6037c9281d7SDouglas Thompson &mci_attr_ce_count, 6047c9281d7SDouglas Thompson &mci_attr_sdram_scrub_rate, 6057c9281d7SDouglas Thompson NULL 6067c9281d7SDouglas Thompson }; 6077c9281d7SDouglas Thompson 6088096cfafSDoug Thompson 6097c9281d7SDouglas Thompson /* 6107c9281d7SDouglas Thompson * Release of a MC controlling instance 6118096cfafSDoug Thompson * 6128096cfafSDoug Thompson * each MC control instance has the following resources upon entry: 6138096cfafSDoug Thompson * a) a ref count on the top memctl kobj 6148096cfafSDoug Thompson * b) a ref count on this module 6158096cfafSDoug Thompson * 6168096cfafSDoug Thompson * this function must decrement those ref counts and then 6178096cfafSDoug Thompson * issue a free on the instance's memory 6187c9281d7SDouglas Thompson */ 6198096cfafSDoug Thompson static void edac_mci_control_release(struct kobject *kobj) 6207c9281d7SDouglas Thompson { 6217c9281d7SDouglas Thompson struct mem_ctl_info *mci; 6227c9281d7SDouglas Thompson 6237c9281d7SDouglas Thompson mci = to_mci(kobj); 6248096cfafSDoug Thompson 6258096cfafSDoug Thompson debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx); 6268096cfafSDoug Thompson 6278096cfafSDoug Thompson /* decrement the module ref count */ 6288096cfafSDoug Thompson module_put(mci->owner); 6298096cfafSDoug Thompson 6308096cfafSDoug Thompson /* free the mci instance memory here */ 6318096cfafSDoug Thompson kfree(mci); 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 /* show/store, tables, etc for the MC kset */ 6418096cfafSDoug Thompson 6428096cfafSDoug Thompson 6438096cfafSDoug Thompson struct memctrl_dev_attribute { 6448096cfafSDoug Thompson struct attribute attr; 6458096cfafSDoug Thompson void *value; 6468096cfafSDoug Thompson ssize_t(*show) (void *, char *); 6478096cfafSDoug Thompson ssize_t(*store) (void *, const char *, size_t); 6488096cfafSDoug Thompson }; 6498096cfafSDoug Thompson 6508096cfafSDoug Thompson /* Set of show/store abstract level functions for memory control object */ 6518096cfafSDoug Thompson static ssize_t memctrl_dev_show(struct kobject *kobj, 6528096cfafSDoug Thompson struct attribute *attr, char *buffer) 6538096cfafSDoug Thompson { 6548096cfafSDoug Thompson struct memctrl_dev_attribute *memctrl_dev; 6558096cfafSDoug Thompson memctrl_dev = (struct memctrl_dev_attribute *)attr; 6568096cfafSDoug Thompson 6578096cfafSDoug Thompson if (memctrl_dev->show) 6588096cfafSDoug Thompson return memctrl_dev->show(memctrl_dev->value, buffer); 6598096cfafSDoug Thompson 6608096cfafSDoug Thompson return -EIO; 6618096cfafSDoug Thompson } 6628096cfafSDoug Thompson 6638096cfafSDoug Thompson static ssize_t memctrl_dev_store(struct kobject *kobj, struct attribute *attr, 6648096cfafSDoug Thompson const char *buffer, size_t count) 6658096cfafSDoug Thompson { 6668096cfafSDoug Thompson struct memctrl_dev_attribute *memctrl_dev; 6678096cfafSDoug Thompson memctrl_dev = (struct memctrl_dev_attribute *)attr; 6688096cfafSDoug Thompson 6698096cfafSDoug Thompson if (memctrl_dev->store) 6708096cfafSDoug Thompson return memctrl_dev->store(memctrl_dev->value, buffer, count); 6718096cfafSDoug Thompson 6728096cfafSDoug Thompson return -EIO; 6738096cfafSDoug Thompson } 6748096cfafSDoug Thompson 6758096cfafSDoug Thompson static struct sysfs_ops memctrlfs_ops = { 6768096cfafSDoug Thompson .show = memctrl_dev_show, 6778096cfafSDoug Thompson .store = memctrl_dev_store 6788096cfafSDoug Thompson }; 6798096cfafSDoug Thompson 6808096cfafSDoug Thompson #define MEMCTRL_ATTR(_name, _mode, _show, _store) \ 6818096cfafSDoug Thompson static struct memctrl_dev_attribute attr_##_name = { \ 6828096cfafSDoug Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 6838096cfafSDoug Thompson .value = &_name, \ 6848096cfafSDoug Thompson .show = _show, \ 6858096cfafSDoug Thompson .store = _store, \ 6868096cfafSDoug Thompson }; 6878096cfafSDoug Thompson 6888096cfafSDoug Thompson #define MEMCTRL_STRING_ATTR(_name, _data, _mode, _show, _store) \ 6898096cfafSDoug Thompson static struct memctrl_dev_attribute attr_##_name = { \ 6908096cfafSDoug Thompson .attr = {.name = __stringify(_name), .mode = _mode }, \ 6918096cfafSDoug Thompson .value = _data, \ 6928096cfafSDoug Thompson .show = _show, \ 6938096cfafSDoug Thompson .store = _store, \ 6948096cfafSDoug Thompson }; 6958096cfafSDoug Thompson 6968096cfafSDoug Thompson /* csrow<id> control files */ 6978096cfafSDoug Thompson MEMCTRL_ATTR(edac_mc_panic_on_ue, 6988096cfafSDoug Thompson S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store); 6998096cfafSDoug Thompson 7008096cfafSDoug Thompson MEMCTRL_ATTR(edac_mc_log_ue, 7018096cfafSDoug Thompson S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store); 7028096cfafSDoug Thompson 7038096cfafSDoug Thompson MEMCTRL_ATTR(edac_mc_log_ce, 7048096cfafSDoug Thompson S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store); 7058096cfafSDoug Thompson 7068096cfafSDoug Thompson MEMCTRL_ATTR(edac_mc_poll_msec, 7078096cfafSDoug Thompson S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store); 7088096cfafSDoug Thompson 7098096cfafSDoug Thompson /* Base Attributes of the memory ECC object */ 7108096cfafSDoug Thompson static struct memctrl_dev_attribute *memctrl_attr[] = { 7118096cfafSDoug Thompson &attr_edac_mc_panic_on_ue, 7128096cfafSDoug Thompson &attr_edac_mc_log_ue, 7138096cfafSDoug Thompson &attr_edac_mc_log_ce, 7148096cfafSDoug Thompson &attr_edac_mc_poll_msec, 7158096cfafSDoug Thompson NULL, 7168096cfafSDoug Thompson }; 7178096cfafSDoug Thompson 7188096cfafSDoug Thompson 7198096cfafSDoug Thompson /* the ktype for the mc_kset internal kobj */ 7208096cfafSDoug Thompson static struct kobj_type ktype_mc_set_attribs = { 7218096cfafSDoug Thompson .sysfs_ops = &memctrlfs_ops, 7228096cfafSDoug Thompson .default_attrs = (struct attribute **)memctrl_attr, 7238096cfafSDoug Thompson }; 7248096cfafSDoug Thompson 7258096cfafSDoug Thompson /* EDAC memory controller sysfs kset: 7268096cfafSDoug Thompson * /sys/devices/system/edac/mc 7278096cfafSDoug Thompson */ 7288096cfafSDoug Thompson static struct kset mc_kset = { 7298096cfafSDoug Thompson .kobj = {.name = "mc", .ktype = &ktype_mc_set_attribs }, 7308096cfafSDoug Thompson .ktype = &ktype_mci, 7318096cfafSDoug Thompson }; 7328096cfafSDoug Thompson 7338096cfafSDoug Thompson 7348096cfafSDoug Thompson /* 7358096cfafSDoug Thompson * edac_mc_register_sysfs_main_kobj 7368096cfafSDoug Thompson * 7378096cfafSDoug Thompson * setups and registers the main kobject for each mci 7388096cfafSDoug Thompson */ 7398096cfafSDoug Thompson int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci) 7408096cfafSDoug Thompson { 7418096cfafSDoug Thompson struct kobject *kobj_mci; 7428096cfafSDoug Thompson int err; 7438096cfafSDoug Thompson 7448096cfafSDoug Thompson debugf1("%s()\n", __func__); 7458096cfafSDoug Thompson 7468096cfafSDoug Thompson kobj_mci = &mci->edac_mci_kobj; 7478096cfafSDoug Thompson 7488096cfafSDoug Thompson /* Init the mci's kobject */ 7498096cfafSDoug Thompson memset(kobj_mci, 0, sizeof(*kobj_mci)); 7508096cfafSDoug Thompson 7518096cfafSDoug Thompson /* this instance become part of the mc_kset */ 7528096cfafSDoug Thompson kobj_mci->kset = &mc_kset; 7538096cfafSDoug Thompson 7548096cfafSDoug Thompson /* set the name of the mc<id> object */ 7558096cfafSDoug Thompson err = kobject_set_name(kobj_mci, "mc%d", mci->mc_idx); 7568096cfafSDoug Thompson if (err) 7578096cfafSDoug Thompson goto fail_out; 7588096cfafSDoug Thompson 7598096cfafSDoug Thompson /* Record which module 'owns' this control structure 7608096cfafSDoug Thompson * and bump the ref count of the module 7618096cfafSDoug Thompson */ 7628096cfafSDoug Thompson mci->owner = THIS_MODULE; 7638096cfafSDoug Thompson 7648096cfafSDoug Thompson /* bump ref count on this module */ 7658096cfafSDoug Thompson if (!try_module_get(mci->owner)) { 7668096cfafSDoug Thompson err = -ENODEV; 7678096cfafSDoug Thompson goto fail_out; 7688096cfafSDoug Thompson } 7698096cfafSDoug Thompson 7708096cfafSDoug Thompson /* register the mc<id> kobject to the mc_kset */ 7718096cfafSDoug Thompson err = kobject_register(kobj_mci); 7728096cfafSDoug Thompson if (err) { 7738096cfafSDoug Thompson debugf1("%s()Failed to register '.../edac/mc%d'\n", 7748096cfafSDoug Thompson __func__, mci->mc_idx); 7758096cfafSDoug Thompson goto kobj_reg_fail; 7768096cfafSDoug Thompson } 7778096cfafSDoug Thompson 7788096cfafSDoug Thompson /* At this point, to 'free' the control struct, 7798096cfafSDoug Thompson * edac_mc_unregister_sysfs_main_kobj() must be used 7808096cfafSDoug Thompson */ 7818096cfafSDoug Thompson 7828096cfafSDoug Thompson debugf1("%s() Registered '.../edac/mc%d' kobject\n", 7838096cfafSDoug Thompson __func__, mci->mc_idx); 7848096cfafSDoug Thompson 7858096cfafSDoug Thompson return 0; 7868096cfafSDoug Thompson 7878096cfafSDoug Thompson /* Error exit stack */ 7888096cfafSDoug Thompson 7898096cfafSDoug Thompson kobj_reg_fail: 7908096cfafSDoug Thompson module_put(mci->owner); 7918096cfafSDoug Thompson 7928096cfafSDoug Thompson fail_out: 7938096cfafSDoug Thompson return err; 7948096cfafSDoug Thompson } 7958096cfafSDoug Thompson 7968096cfafSDoug Thompson /* 7978096cfafSDoug Thompson * edac_mc_register_sysfs_main_kobj 7988096cfafSDoug Thompson * 7998096cfafSDoug Thompson * tears down and the main mci kobject from the mc_kset 8008096cfafSDoug Thompson */ 8018096cfafSDoug Thompson void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) 8028096cfafSDoug Thompson { 8038096cfafSDoug Thompson /* delete the kobj from the mc_kset */ 8048096cfafSDoug Thompson kobject_unregister(&mci->edac_mci_kobj); 8058096cfafSDoug Thompson } 8068096cfafSDoug Thompson 8077c9281d7SDouglas Thompson #define EDAC_DEVICE_SYMLINK "device" 8087c9281d7SDouglas Thompson 8097c9281d7SDouglas Thompson /* 8108096cfafSDoug Thompson * edac_create_mci_instance_attributes 81142a8e397SDouglas Thompson * create MC driver specific attributes at the topmost level 81242a8e397SDouglas Thompson * directory of this mci instance. 81342a8e397SDouglas Thompson */ 8148096cfafSDoug Thompson static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci) 81542a8e397SDouglas Thompson { 81642a8e397SDouglas Thompson int err; 81742a8e397SDouglas Thompson struct mcidev_sysfs_attribute *sysfs_attrib; 81842a8e397SDouglas Thompson 81942a8e397SDouglas Thompson /* point to the start of the array and iterate over it 82042a8e397SDouglas Thompson * adding each attribute listed to this mci instance's kobject 82142a8e397SDouglas Thompson */ 82242a8e397SDouglas Thompson sysfs_attrib = mci->mc_driver_sysfs_attributes; 82342a8e397SDouglas Thompson 8248096cfafSDoug Thompson while (sysfs_attrib && sysfs_attrib->attr.name) { 82542a8e397SDouglas Thompson err = sysfs_create_file(&mci->edac_mci_kobj, 82642a8e397SDouglas Thompson (struct attribute*) sysfs_attrib); 82742a8e397SDouglas Thompson if (err) { 82842a8e397SDouglas Thompson return err; 82942a8e397SDouglas Thompson } 83042a8e397SDouglas Thompson 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 */ 8428096cfafSDoug Thompson static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci) 8438096cfafSDoug Thompson { 8448096cfafSDoug Thompson struct mcidev_sysfs_attribute *sysfs_attrib; 8458096cfafSDoug Thompson 8468096cfafSDoug Thompson /* point to the start of the array and iterate over it 8478096cfafSDoug Thompson * adding each attribute listed to this mci instance's kobject 8488096cfafSDoug Thompson */ 8498096cfafSDoug Thompson sysfs_attrib = mci->mc_driver_sysfs_attributes; 8508096cfafSDoug Thompson 8518096cfafSDoug Thompson /* loop if there are attributes and until we hit a NULL entry */ 8528096cfafSDoug Thompson while (sysfs_attrib && sysfs_attrib->attr.name) { 8538096cfafSDoug Thompson sysfs_remove_file(&mci->edac_mci_kobj, 8548096cfafSDoug Thompson (struct attribute *) sysfs_attrib); 8558096cfafSDoug Thompson sysfs_attrib++; 8568096cfafSDoug Thompson } 8578096cfafSDoug Thompson } 8588096cfafSDoug Thompson 8598096cfafSDoug Thompson 8608096cfafSDoug Thompson /* 8617c9281d7SDouglas Thompson * Create a new Memory Controller kobject instance, 8627c9281d7SDouglas Thompson * mc<id> under the 'mc' directory 8637c9281d7SDouglas Thompson * 8647c9281d7SDouglas Thompson * Return: 8657c9281d7SDouglas Thompson * 0 Success 8667c9281d7SDouglas Thompson * !0 Failure 8677c9281d7SDouglas Thompson */ 8687c9281d7SDouglas Thompson int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) 8697c9281d7SDouglas Thompson { 8707c9281d7SDouglas Thompson int i; 8717c9281d7SDouglas Thompson int err; 8727c9281d7SDouglas Thompson struct csrow_info *csrow; 8738096cfafSDoug Thompson struct kobject *kobj_mci = &mci->edac_mci_kobj; 8747c9281d7SDouglas Thompson 8757c9281d7SDouglas Thompson debugf0("%s() idx=%d\n", __func__, mci->mc_idx); 8767c9281d7SDouglas Thompson 8777c9281d7SDouglas Thompson /* create a symlink for the device */ 8788096cfafSDoug Thompson err = sysfs_create_link(kobj_mci, &mci->dev->kobj, 8797c9281d7SDouglas Thompson EDAC_DEVICE_SYMLINK); 8808096cfafSDoug Thompson if (err) { 8818096cfafSDoug Thompson debugf1("%s() failure to create symlink\n", __func__); 8827c9281d7SDouglas Thompson goto fail0; 8838096cfafSDoug Thompson } 8847c9281d7SDouglas Thompson 88542a8e397SDouglas Thompson /* If the low level driver desires some attributes, 88642a8e397SDouglas Thompson * then create them now for the driver. 88742a8e397SDouglas Thompson */ 88842a8e397SDouglas Thompson if (mci->mc_driver_sysfs_attributes) { 8898096cfafSDoug Thompson err = edac_create_mci_instance_attributes(mci); 8908096cfafSDoug Thompson if (err) { 8918096cfafSDoug Thompson debugf1("%s() failure to create mci attributes\n", 8928096cfafSDoug Thompson __func__); 89342a8e397SDouglas Thompson goto fail0; 89442a8e397SDouglas Thompson } 8958096cfafSDoug Thompson } 89642a8e397SDouglas Thompson 8978096cfafSDoug Thompson /* Make directories for each CSROW object under the mc<id> kobject 8987c9281d7SDouglas Thompson */ 8997c9281d7SDouglas Thompson for (i = 0; i < mci->nr_csrows; i++) { 9007c9281d7SDouglas Thompson csrow = &mci->csrows[i]; 9017c9281d7SDouglas Thompson 9027c9281d7SDouglas Thompson /* Only expose populated CSROWs */ 9037c9281d7SDouglas Thompson if (csrow->nr_pages > 0) { 9048096cfafSDoug Thompson err = edac_create_csrow_object(mci, csrow, i); 9058096cfafSDoug Thompson if (err) { 9068096cfafSDoug Thompson debugf1("%s() failure: create csrow %d obj\n", 9078096cfafSDoug Thompson __func__, i); 9087c9281d7SDouglas Thompson goto fail1; 9097c9281d7SDouglas Thompson } 9107c9281d7SDouglas Thompson } 9118096cfafSDoug Thompson } 9127c9281d7SDouglas Thompson 9137c9281d7SDouglas Thompson return 0; 9147c9281d7SDouglas Thompson 9157c9281d7SDouglas Thompson /* CSROW error: backout what has already been registered, */ 9167c9281d7SDouglas Thompson fail1: 9177c9281d7SDouglas Thompson for (i--; i >= 0; i--) { 9187c9281d7SDouglas Thompson if (csrow->nr_pages > 0) { 9197c9281d7SDouglas Thompson kobject_unregister(&mci->csrows[i].kobj); 9207c9281d7SDouglas Thompson } 9217c9281d7SDouglas Thompson } 9227c9281d7SDouglas Thompson 9238096cfafSDoug Thompson /* remove the mci instance's attributes, if any */ 9248096cfafSDoug Thompson edac_remove_mci_instance_attributes(mci); 9258096cfafSDoug Thompson 9268096cfafSDoug Thompson /* remove the symlink */ 9278096cfafSDoug Thompson sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); 9288096cfafSDoug Thompson 9297c9281d7SDouglas Thompson fail0: 9307c9281d7SDouglas Thompson return err; 9317c9281d7SDouglas Thompson } 9327c9281d7SDouglas Thompson 9337c9281d7SDouglas Thompson /* 9347c9281d7SDouglas Thompson * remove a Memory Controller instance 9357c9281d7SDouglas Thompson */ 9367c9281d7SDouglas Thompson void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) 9377c9281d7SDouglas Thompson { 9387c9281d7SDouglas Thompson int i; 9397c9281d7SDouglas Thompson 9407c9281d7SDouglas Thompson debugf0("%s()\n", __func__); 9417c9281d7SDouglas Thompson 9427c9281d7SDouglas Thompson /* remove all csrow kobjects */ 9437c9281d7SDouglas Thompson for (i = 0; i < mci->nr_csrows; i++) { 9447c9281d7SDouglas Thompson if (mci->csrows[i].nr_pages > 0) { 9458096cfafSDoug Thompson debugf0("%s() unreg csrow-%d\n", __func__, i); 9467c9281d7SDouglas Thompson kobject_unregister(&mci->csrows[i].kobj); 9477c9281d7SDouglas Thompson } 9487c9281d7SDouglas Thompson } 9497c9281d7SDouglas Thompson 9508096cfafSDoug Thompson debugf0("%s() remove_link\n", __func__); 9518096cfafSDoug Thompson 9528096cfafSDoug Thompson /* remove the symlink */ 9537c9281d7SDouglas Thompson sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); 9548096cfafSDoug Thompson 9558096cfafSDoug Thompson debugf0("%s() remove_mci_instance\n", __func__); 9568096cfafSDoug Thompson 9578096cfafSDoug Thompson /* remove this mci instance's attribtes */ 9588096cfafSDoug Thompson edac_remove_mci_instance_attributes(mci); 9598096cfafSDoug Thompson 9608096cfafSDoug Thompson debugf0("%s() unregister this mci kobj\n", __func__); 9618096cfafSDoug Thompson 9628096cfafSDoug Thompson /* unregister this instance's kobject */ 9637c9281d7SDouglas Thompson kobject_unregister(&mci->edac_mci_kobj); 9647c9281d7SDouglas Thompson } 9658096cfafSDoug Thompson 9668096cfafSDoug Thompson 9678096cfafSDoug Thompson 9688096cfafSDoug Thompson 9698096cfafSDoug Thompson /* 9708096cfafSDoug Thompson * edac_setup_sysfs_mc_kset(void) 9718096cfafSDoug Thompson * 9728096cfafSDoug Thompson * Initialize the mc_kset for the 'mc' entry 9738096cfafSDoug Thompson * This requires creating the top 'mc' directory with a kset 9748096cfafSDoug Thompson * and its controls/attributes. 9758096cfafSDoug Thompson * 9768096cfafSDoug Thompson * To this 'mc' kset, instance 'mci' will be grouped as children. 9778096cfafSDoug Thompson * 9788096cfafSDoug Thompson * Return: 0 SUCCESS 9798096cfafSDoug Thompson * !0 FAILURE error code 9808096cfafSDoug Thompson */ 9818096cfafSDoug Thompson int edac_sysfs_setup_mc_kset(void) 9828096cfafSDoug Thompson { 9838096cfafSDoug Thompson int err = 0; 9848096cfafSDoug Thompson struct sysdev_class *edac_class; 9858096cfafSDoug Thompson 9868096cfafSDoug Thompson debugf1("%s()\n", __func__); 9878096cfafSDoug Thompson 9888096cfafSDoug Thompson /* get the /sys/devices/system/edac class reference */ 9898096cfafSDoug Thompson edac_class = edac_get_edac_class(); 9908096cfafSDoug Thompson if (edac_class == NULL) { 9918096cfafSDoug Thompson debugf1("%s() no edac_class error=%d\n", __func__, err); 9928096cfafSDoug Thompson goto fail_out; 9938096cfafSDoug Thompson } 9948096cfafSDoug Thompson 9958096cfafSDoug Thompson /* Init the MC's kobject */ 9968096cfafSDoug Thompson mc_kset.kobj.parent = &edac_class->kset.kobj; 9978096cfafSDoug Thompson 9988096cfafSDoug Thompson /* register the mc_kset */ 9998096cfafSDoug Thompson err = kset_register(&mc_kset); 10008096cfafSDoug Thompson if (err) { 10018096cfafSDoug Thompson debugf1("%s() Failed to register '.../edac/mc'\n", __func__); 10028096cfafSDoug Thompson goto fail_out; 10038096cfafSDoug Thompson } 10048096cfafSDoug Thompson 10058096cfafSDoug Thompson debugf1("%s() Registered '.../edac/mc' kobject\n", __func__); 10068096cfafSDoug Thompson 10078096cfafSDoug Thompson return 0; 10088096cfafSDoug Thompson 10098096cfafSDoug Thompson 10108096cfafSDoug Thompson /* error unwind stack */ 10118096cfafSDoug Thompson fail_out: 10128096cfafSDoug Thompson return err; 10138096cfafSDoug Thompson } 10148096cfafSDoug Thompson 10158096cfafSDoug Thompson /* 10168096cfafSDoug Thompson * edac_sysfs_teardown_mc_kset 10178096cfafSDoug Thompson * 10188096cfafSDoug Thompson * deconstruct the mc_ket for memory controllers 10198096cfafSDoug Thompson */ 10208096cfafSDoug Thompson void edac_sysfs_teardown_mc_kset(void) 10218096cfafSDoug Thompson { 10228096cfafSDoug Thompson kset_unregister(&mc_kset); 10238096cfafSDoug Thompson } 10248096cfafSDoug Thompson 1025