diff --git a/drivers/cxl/core/memscrub.c b/drivers/cxl/core/memscrub.c
index a1fb40f8307f..325084b22e7a 100644
--- a/drivers/cxl/core/memscrub.c
+++ b/drivers/cxl/core/memscrub.c
@@ -464,6 +464,8 @@ EXPORT_SYMBOL_NS_GPL(cxl_mem_patrol_scrub_init, CXL);
 #define CXL_MEMDEV_ECS_GET_FEAT_VERSION	0x01
 #define CXL_MEMDEV_ECS_SET_FEAT_VERSION	0x01
 
+#define CXL_DDR5_ECS	"cxl_ecs"
+
 static const uuid_t cxl_ecs_uuid =
 	UUID_INIT(0xe5b13f22, 0x2328, 0x4a14, 0xb8, 0xba, 0xb9, 0x69, 0x1e,     \
 		  0x89, 0x33, 0x86);
@@ -582,9 +584,8 @@ static int cxl_mem_ecs_get_attrs(struct device *dev, int fru_id,
 	return 0;
 }
 
-static int __maybe_unused
-cxl_mem_ecs_set_attrs(struct device *dev, int fru_id,
-		      struct cxl_memdev_ecs_params *params, u8 param_type)
+static int cxl_mem_ecs_set_attrs(struct device *dev, int fru_id,
+				 struct cxl_memdev_ecs_params *params, u8 param_type)
 {
 	struct cxl_memdev_ecs_feat_read_attrs *rd_attrs __free(kvfree) = NULL;
 	struct cxl_memdev_ecs_set_feat_pi *set_pi __free(kvfree) = NULL;
@@ -731,10 +732,247 @@ cxl_mem_ecs_set_attrs(struct device *dev, int fru_id,
 	return 0;
 }
 
+static int cxl_mem_ecs_log_entry_type_write(struct device *dev, int region_id, long val)
+{
+	struct cxl_memdev_ecs_params params;
+	int ret;
+
+	params.log_entry_type = val;
+	ret = cxl_mem_ecs_set_attrs(dev, region_id, &params,
+				    CXL_MEMDEV_ECS_PARAM_LOG_ENTRY_TYPE);
+	if (ret) {
+		dev_err(dev->parent, "Set CXL ECS params for log entry type fail ret=%d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int cxl_mem_ecs_threshold_write(struct device *dev, int region_id, long val)
+{
+	struct cxl_memdev_ecs_params params;
+	int ret;
+
+	params.threshold = val;
+	ret = cxl_mem_ecs_set_attrs(dev, region_id, &params,
+				    CXL_MEMDEV_ECS_PARAM_THRESHOLD);
+	if (ret) {
+		dev_err(dev->parent, "Set CXL ECS params for threshold fail ret=%d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int cxl_mem_ecs_mode_write(struct device *dev, int region_id, long val)
+{
+	struct cxl_memdev_ecs_params params;
+	int ret;
+
+	params.mode = val;
+	ret = cxl_mem_ecs_set_attrs(dev, region_id, &params,
+				    CXL_MEMDEV_ECS_PARAM_MODE);
+	if (ret) {
+		dev_err(dev->parent, "Set CXL ECS params for mode fail ret=%d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int cxl_mem_ecs_reset_counter_write(struct device *dev, int region_id, long val)
+{
+	struct cxl_memdev_ecs_params params;
+	int ret;
+
+	params.reset_counter = val;
+	ret = cxl_mem_ecs_set_attrs(dev, region_id, &params,
+				    CXL_MEMDEV_ECS_PARAM_RESET_COUNTER);
+	if (ret) {
+		dev_err(dev->parent, "Set CXL ECS params for reset ECC counter fail ret=%d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+enum cxl_mem_ecs_scrub_attributes {
+	cxl_ecs_log_entry_type,
+	cxl_ecs_log_entry_type_per_dram,
+	cxl_ecs_log_entry_type_per_memory_media,
+	cxl_ecs_mode,
+	cxl_ecs_mode_counts_codewords,
+	cxl_ecs_mode_counts_rows,
+	cxl_ecs_reset,
+	cxl_ecs_threshold,
+	cxl_ecs_threshold_available,
+	cxl_ecs_max_attrs,
+};
+
+static ssize_t cxl_mem_ecs_show_scrub_attr(struct device *dev, char *buf,
+					   int attr_id)
+{
+	struct cxl_ecs_context *cxl_ecs_ctx = dev_get_drvdata(dev);
+	int region_id = cxl_ecs_ctx->region_id;
+	struct cxl_memdev_ecs_params params;
+	int ret;
+
+	if (attr_id == cxl_ecs_log_entry_type ||
+	    attr_id == cxl_ecs_log_entry_type_per_dram ||
+	    attr_id == cxl_ecs_log_entry_type_per_memory_media ||
+	    attr_id == cxl_ecs_mode ||
+	    attr_id == cxl_ecs_mode_counts_codewords ||
+	    attr_id == cxl_ecs_mode_counts_rows ||
+	    attr_id == cxl_ecs_threshold) {
+		ret = cxl_mem_ecs_get_attrs(dev, region_id, &params);
+		if (ret) {
+			dev_err(dev->parent, "Get CXL ECS params fail ret=%d\n", ret);
+			return ret;
+		}
+	}
+
+	switch (attr_id) {
+	case cxl_ecs_log_entry_type:
+		return sprintf(buf, "%d\n", params.log_entry_type);
+	case cxl_ecs_log_entry_type_per_dram:
+		if (params.log_entry_type == ECS_LOG_ENTRY_TYPE_DRAM)
+			return sysfs_emit(buf, "1\n");
+		else
+			return sysfs_emit(buf, "0\n");
+	case cxl_ecs_log_entry_type_per_memory_media:
+		if (params.log_entry_type == ECS_LOG_ENTRY_TYPE_MEM_MEDIA_FRU)
+			return sysfs_emit(buf, "1\n");
+		else
+			return sysfs_emit(buf, "0\n");
+	case cxl_ecs_mode:
+		return sprintf(buf, "%d\n", params.mode);
+	case cxl_ecs_mode_counts_codewords:
+		if (params.mode == ECS_MODE_COUNTS_CODEWORDS)
+			return sysfs_emit(buf, "1\n");
+		else
+			return sysfs_emit(buf, "0\n");
+	case cxl_ecs_mode_counts_rows:
+		if (params.mode == ECS_MODE_COUNTS_ROWS)
+			return sysfs_emit(buf, "1\n");
+		else
+			return sysfs_emit(buf, "0\n");
+	case cxl_ecs_threshold:
+		return sprintf(buf, "%d\n", params.threshold);
+	case cxl_ecs_threshold_available:
+		return sysfs_emit(buf, "256,1024,4096\n");
+	}
+
+	return -ENOTSUPP;
+}
+
+static ssize_t cxl_mem_ecs_store_scrub_attr(struct device *dev, const char *buf,
+					    size_t count, int attr_id)
+{
+	struct cxl_ecs_context *cxl_ecs_ctx = dev_get_drvdata(dev);
+	int region_id = cxl_ecs_ctx->region_id;
+	long val;
+	int ret;
+
+	ret = kstrtol(buf, 10, &val);
+	if (ret < 0)
+		return ret;
+
+	switch (attr_id) {
+	case cxl_ecs_log_entry_type:
+		ret = cxl_mem_ecs_log_entry_type_write(dev, region_id, val);
+		if (ret)
+			return -ENOTSUPP;
+		break;
+	case cxl_ecs_mode:
+		ret = cxl_mem_ecs_mode_write(dev, region_id, val);
+		if (ret)
+			return -ENOTSUPP;
+		break;
+	case cxl_ecs_reset:
+		ret = cxl_mem_ecs_reset_counter_write(dev, region_id, val);
+		if (ret)
+			return -ENOTSUPP;
+		break;
+	case cxl_ecs_threshold:
+		ret = cxl_mem_ecs_threshold_write(dev, region_id, val);
+		if (ret)
+			return -ENOTSUPP;
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+
+	return count;
+}
+
+#define CXL_ECS_SCRUB_ATTR_RW(attr)						\
+static ssize_t attr##_show(struct device *dev,				\
+			   struct device_attribute *attr, char *buf)	\
+{									\
+	return cxl_mem_ecs_show_scrub_attr(dev, buf, (cxl_ecs_##attr));	\
+}									\
+static ssize_t attr##_store(struct device *dev,			\
+			    struct device_attribute *attr,		\
+			    const char *buf, size_t count)		\
+{									\
+	return cxl_mem_ecs_store_scrub_attr(dev, buf, count, (cxl_ecs_##attr));\
+}									\
+static DEVICE_ATTR_RW(attr)
+
+#define CXL_ECS_SCRUB_ATTR_RO(attr)						\
+static ssize_t attr##_show(struct device *dev,				\
+			   struct device_attribute *attr, char *buf)	\
+{									\
+	return cxl_mem_ecs_show_scrub_attr(dev, buf, (cxl_ecs_##attr));	\
+}									\
+static DEVICE_ATTR_RO(attr)
+
+#define CXL_ECS_SCRUB_ATTR_WO(attr)						\
+static ssize_t attr##_store(struct device *dev,			\
+			    struct device_attribute *attr,		\
+			    const char *buf, size_t count)		\
+{									\
+	return cxl_mem_ecs_store_scrub_attr(dev, buf, count, (cxl_ecs_##attr));\
+}									\
+static DEVICE_ATTR_WO(attr)
+
+CXL_ECS_SCRUB_ATTR_RW(log_entry_type);
+CXL_ECS_SCRUB_ATTR_RO(log_entry_type_per_dram);
+CXL_ECS_SCRUB_ATTR_RO(log_entry_type_per_memory_media);
+CXL_ECS_SCRUB_ATTR_RW(mode);
+CXL_ECS_SCRUB_ATTR_RO(mode_counts_codewords);
+CXL_ECS_SCRUB_ATTR_RO(mode_counts_rows);
+CXL_ECS_SCRUB_ATTR_WO(reset);
+CXL_ECS_SCRUB_ATTR_RW(threshold);
+CXL_ECS_SCRUB_ATTR_RO(threshold_available);
+
+static struct attribute *cxl_mem_ecs_scrub_attrs[] = {
+	&dev_attr_log_entry_type.attr,
+	&dev_attr_log_entry_type_per_dram.attr,
+	&dev_attr_log_entry_type_per_memory_media.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_mode_counts_codewords.attr,
+	&dev_attr_mode_counts_rows.attr,
+	&dev_attr_reset.attr,
+	&dev_attr_threshold.attr,
+	&dev_attr_threshold_available.attr,
+	NULL,
+};
+
+static struct attribute_group cxl_mem_ecs_attr_group = {
+	.attrs = cxl_mem_ecs_scrub_attrs,
+};
+
 int cxl_mem_ecs_init(struct cxl_memdev *cxlmd, int region_id)
 {
+	char scrub_name[CXL_MEMDEV_MAX_NAME_LENGTH];
 	struct cxl_mbox_supp_feat_entry feat_entry;
 	struct cxl_ecs_context *cxl_ecs_ctx;
+	struct device *cxl_scrub_dev;
 	int nmedia_frus;
 	int ret;
 
@@ -755,6 +993,15 @@ int cxl_mem_ecs_init(struct cxl_memdev *cxlmd, int region_id)
 		cxl_ecs_ctx->get_feat_size = feat_entry.get_feat_size;
 		cxl_ecs_ctx->set_feat_size = feat_entry.set_feat_size;
 		cxl_ecs_ctx->region_id = region_id;
+
+		snprintf(scrub_name, sizeof(scrub_name), "%s_%s_region%d",
+			 CXL_DDR5_ECS, dev_name(&cxlmd->dev), cxl_ecs_ctx->region_id);
+		cxl_scrub_dev = devm_scrub_device_register(&cxlmd->dev, scrub_name,
+							  cxl_ecs_ctx, NULL,
+							  cxl_ecs_ctx->region_id,
+							  &cxl_mem_ecs_attr_group);
+		if (IS_ERR(cxl_scrub_dev))
+			return PTR_ERR(cxl_scrub_dev);
 	}
 
 	return 0;
