|
@@ -130,6 +130,7 @@ static const char *edac_caps[] = {
|
|
|
[EDAC_S16ECD16ED] = "S16ECD16ED"
|
|
|
};
|
|
|
|
|
|
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
|
|
|
/*
|
|
|
* EDAC sysfs CSROW data structures and methods
|
|
|
*/
|
|
@@ -443,6 +444,159 @@ static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
|
|
|
device_del(&mci->csrows[i].dev);
|
|
|
}
|
|
|
}
|
|
|
+#endif
|
|
|
+
|
|
|
+/*
|
|
|
+ * Per-dimm (or per-rank) devices
|
|
|
+ */
|
|
|
+
|
|
|
+#define to_dimm(k) container_of(k, struct dimm_info, dev)
|
|
|
+
|
|
|
+/* show/store functions for DIMM Label attributes */
|
|
|
+static ssize_t dimmdev_location_show(struct device *dev,
|
|
|
+ struct device_attribute *mattr, char *data)
|
|
|
+{
|
|
|
+ struct dimm_info *dimm = to_dimm(dev);
|
|
|
+ struct mem_ctl_info *mci = dimm->mci;
|
|
|
+ int i;
|
|
|
+ char *p = data;
|
|
|
+
|
|
|
+ for (i = 0; i < mci->n_layers; i++) {
|
|
|
+ p += sprintf(p, "%s %d ",
|
|
|
+ edac_layer_name[mci->layers[i].type],
|
|
|
+ dimm->location[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return p - data;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dimmdev_label_show(struct device *dev,
|
|
|
+ struct device_attribute *mattr, char *data)
|
|
|
+{
|
|
|
+ struct dimm_info *dimm = to_dimm(dev);
|
|
|
+
|
|
|
+ /* if field has not been initialized, there is nothing to send */
|
|
|
+ if (!dimm->label[0])
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dimmdev_label_store(struct device *dev,
|
|
|
+ struct device_attribute *mattr,
|
|
|
+ const char *data,
|
|
|
+ size_t count)
|
|
|
+{
|
|
|
+ struct dimm_info *dimm = to_dimm(dev);
|
|
|
+
|
|
|
+ ssize_t max_size = 0;
|
|
|
+
|
|
|
+ max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
|
|
|
+ strncpy(dimm->label, data, max_size);
|
|
|
+ dimm->label[max_size] = '\0';
|
|
|
+
|
|
|
+ return max_size;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dimmdev_size_show(struct device *dev,
|
|
|
+ struct device_attribute *mattr, char *data)
|
|
|
+{
|
|
|
+ struct dimm_info *dimm = to_dimm(dev);
|
|
|
+
|
|
|
+ return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dimmdev_mem_type_show(struct device *dev,
|
|
|
+ struct device_attribute *mattr, char *data)
|
|
|
+{
|
|
|
+ struct dimm_info *dimm = to_dimm(dev);
|
|
|
+
|
|
|
+ return sprintf(data, "%s\n", mem_types[dimm->mtype]);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dimmdev_dev_type_show(struct device *dev,
|
|
|
+ struct device_attribute *mattr, char *data)
|
|
|
+{
|
|
|
+ struct dimm_info *dimm = to_dimm(dev);
|
|
|
+
|
|
|
+ return sprintf(data, "%s\n", dev_types[dimm->dtype]);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dimmdev_edac_mode_show(struct device *dev,
|
|
|
+ struct device_attribute *mattr,
|
|
|
+ char *data)
|
|
|
+{
|
|
|
+ struct dimm_info *dimm = to_dimm(dev);
|
|
|
+
|
|
|
+ return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
|
|
|
+}
|
|
|
+
|
|
|
+/* dimm/rank attribute files */
|
|
|
+static DEVICE_ATTR(dimm_label, S_IRUGO | S_IWUSR,
|
|
|
+ dimmdev_label_show, dimmdev_label_store);
|
|
|
+static DEVICE_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
|
|
|
+static DEVICE_ATTR(size, S_IRUGO, dimmdev_size_show, NULL);
|
|
|
+static DEVICE_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
|
|
|
+static DEVICE_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
|
|
|
+static DEVICE_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
|
|
|
+
|
|
|
+/* attributes of the dimm<id>/rank<id> object */
|
|
|
+static struct attribute *dimm_attrs[] = {
|
|
|
+ &dev_attr_dimm_label.attr,
|
|
|
+ &dev_attr_dimm_location.attr,
|
|
|
+ &dev_attr_size.attr,
|
|
|
+ &dev_attr_dimm_mem_type.attr,
|
|
|
+ &dev_attr_dimm_dev_type.attr,
|
|
|
+ &dev_attr_dimm_edac_mode.attr,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+static struct attribute_group dimm_attr_grp = {
|
|
|
+ .attrs = dimm_attrs,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct attribute_group *dimm_attr_groups[] = {
|
|
|
+ &dimm_attr_grp,
|
|
|
+ NULL
|
|
|
+};
|
|
|
+
|
|
|
+static void dimm_attr_release(struct device *device)
|
|
|
+{
|
|
|
+ debugf1("Releasing dimm device %s\n", dev_name(device));
|
|
|
+}
|
|
|
+
|
|
|
+static struct device_type dimm_attr_type = {
|
|
|
+ .groups = dimm_attr_groups,
|
|
|
+ .release = dimm_attr_release,
|
|
|
+};
|
|
|
+
|
|
|
+/* Create a DIMM object under specifed memory controller device */
|
|
|
+static int edac_create_dimm_object(struct mem_ctl_info *mci,
|
|
|
+ struct dimm_info *dimm,
|
|
|
+ int index)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ dimm->mci = mci;
|
|
|
+
|
|
|
+ dimm->dev.type = &dimm_attr_type;
|
|
|
+ dimm->dev.bus = &mci->bus;
|
|
|
+ device_initialize(&dimm->dev);
|
|
|
+
|
|
|
+ dimm->dev.parent = &mci->dev;
|
|
|
+ if (mci->mem_is_per_rank)
|
|
|
+ dev_set_name(&dimm->dev, "rank%d", index);
|
|
|
+ else
|
|
|
+ dev_set_name(&dimm->dev, "dimm%d", index);
|
|
|
+ dev_set_drvdata(&dimm->dev, dimm);
|
|
|
+ pm_runtime_forbid(&mci->dev);
|
|
|
+
|
|
|
+ err = device_add(&dimm->dev);
|
|
|
+
|
|
|
+ debugf0("%s(): creating rank/dimm device %s\n", __func__,
|
|
|
+ dev_name(&dimm->dev));
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
* Memory controller device
|
|
@@ -660,7 +814,6 @@ static struct device_type mci_attr_type = {
|
|
|
.release = mci_attr_release,
|
|
|
};
|
|
|
|
|
|
-
|
|
|
/*
|
|
|
* Create a new Memory Controller kobject instance,
|
|
|
* mc<id> under the 'mc' directory
|
|
@@ -725,11 +878,19 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
|
|
|
printk(KERN_CONT "\n");
|
|
|
}
|
|
|
#endif
|
|
|
+ err = edac_create_dimm_object(mci, dimm, i);
|
|
|
+ if (err) {
|
|
|
+ debugf1("%s() failure: create dimm %d obj\n",
|
|
|
+ __func__, i);
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
|
|
|
err = edac_create_csrow_objects(mci);
|
|
|
if (err < 0)
|
|
|
goto fail;
|
|
|
+#endif
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -757,7 +918,9 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
|
|
|
|
|
|
debugf0("%s()\n", __func__);
|
|
|
|
|
|
+#ifdef CONFIG_EDAC_LEGACY_SYSFS
|
|
|
edac_delete_csrow_objects(mci);
|
|
|
+#endif
|
|
|
|
|
|
for (i = 0; i < mci->tot_dimms; i++) {
|
|
|
struct dimm_info *dimm = &mci->dimms[i];
|