On Fri, Nov 01, 2024 at 09:17:29AM +0000, shiju.jose@xxxxxxxxxx wrote: > From: Shiju Jose <shiju.jose@xxxxxxxxxx> > > Add generic EDAC memory repair control, eg. PPR(Post Package Repair), > memory sparing etc, control driver in order to control memory repairs > in the system. Supports sPPR(soft PPR), hPPR(hard PPR), soft/hard memory > sparing, memory sparing at cacheline/row/bank/rank granularity etc. > Device with memory repair features registers with EDAC device driver, > which retrieves memory repair descriptor from EDAC memory repair driver and > exposes the sysfs repair control attributes to userspace in > /sys/bus/edac/devices/<dev-name>/mem_repairX/. > > The common memory repair control interface abstracts the control of > arbitrary memory repair functionality into a standardized set of functions. > The sysfs memory repair attribute nodes are only available if the client > driver has implemented the corresponding attribute callback function and > provided operations to the EDAC device driver during registration. > > Signed-off-by: Shiju Jose <shiju.jose@xxxxxxxxxx> > --- > .../ABI/testing/sysfs-edac-memory-repair | 168 ++++++++ > drivers/edac/Makefile | 2 +- > drivers/edac/edac_device.c | 32 ++ > drivers/edac/mem_repair.c | 367 ++++++++++++++++++ > include/linux/edac.h | 87 +++++ > 5 files changed, 655 insertions(+), 1 deletion(-) > create mode 100644 Documentation/ABI/testing/sysfs-edac-memory-repair > create mode 100755 drivers/edac/mem_repair.c > > diff --git a/Documentation/ABI/testing/sysfs-edac-memory-repair b/Documentation/ABI/testing/sysfs-edac-memory-repair > new file mode 100644 > index 000000000000..393206b8d418 ... @@ -610,6 +611,7 @@ int edac_dev_register(struct device *parent, char *name, > const struct edac_dev_feature *ras_features) > { > const struct attribute_group **ras_attr_groups; > + int mem_repair_cnt = 0, mem_repair_inst = 0; > int scrub_cnt = 0, scrub_inst = 0; > struct edac_dev_data *dev_data; > struct edac_dev_feat_ctx *ctx; > @@ -626,6 +628,10 @@ int edac_dev_register(struct device *parent, char *name, > attr_gcnt++; > scrub_cnt++; > break; > + case RAS_FEAT_MEM_REPAIR: > + attr_gcnt++; > + mem_repair_cnt++; > + break; > case RAS_FEAT_ECS: > attr_gcnt += ras_features[feat].ecs_info.num_media_frus; > break; > @@ -652,6 +658,14 @@ int edac_dev_register(struct device *parent, char *name, > } > } > > + if (mem_repair_cnt) { > + ctx->mem_repair = kcalloc(mem_repair_cnt, sizeof(*ctx->mem_repair), GFP_KERNEL); > + if (!ctx->mem_repair) { > + ret = -ENOMEM; > + goto groups_free; If the function returns here, we will have a leak from memory pointed by ctx->scrub. Fan > + } > + } > + > attr_gcnt = 0; > for (feat = 0; feat < num_features; feat++, ras_features++) { > switch (ras_features->ft_type) { > @@ -686,6 +700,23 @@ int edac_dev_register(struct device *parent, char *name, > > attr_gcnt += ras_features->ecs_info.num_media_frus; > break; > + case RAS_FEAT_MEM_REPAIR: > + if (!ras_features->mem_repair_ops || > + mem_repair_inst != ras_features->instance) > + goto data_mem_free; > + > + dev_data = &ctx->mem_repair[mem_repair_inst]; > + dev_data->instance = mem_repair_inst; > + dev_data->mem_repair_ops = ras_features->mem_repair_ops; > + dev_data->private = ras_features->ctx; > + ret = edac_mem_repair_get_desc(parent, &ras_attr_groups[attr_gcnt], > + ras_features->instance); > + if (ret) > + goto data_mem_free; > + > + mem_repair_inst++; > + attr_gcnt++; > + break; > default: > ret = -EINVAL; > goto data_mem_free; > @@ -712,6 +743,7 @@ int edac_dev_register(struct device *parent, char *name, > return devm_add_action_or_reset(parent, edac_dev_unreg, &ctx->dev); > > data_mem_free: > + kfree(ctx->mem_repair); > kfree(ctx->scrub); > groups_free: > kfree(ras_attr_groups); > diff --git a/drivers/edac/mem_repair.c b/drivers/edac/mem_repair.c > new file mode 100755 > index 000000000000..93246ad0c9eb > --- /dev/null > +++ b/drivers/edac/mem_repair.c > @@ -0,0 +1,367 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * The generic EDAC memory repair driver is designed to control the memory > + * devices with memory repair features, such as Post Package Repair (PPR), > + * memory sparing etc. The common sysfs memory repair interface abstracts > + * the control of various arbitrary memory repair functionalities into a > + * unified set of functions. > + * > + * Copyright (c) 2024 HiSilicon Limited. > + */ > + > +#include <linux/edac.h> > + > +enum edac_mem_repair_attributes { > + MEM_REPAIR_TYPE, > + MEM_REPAIR_PERSIST_MODE_AVAIL, > + MEM_REPAIR_PERSIST_MODE, > + MEM_REPAIR_DPA_SUPPORT, > + MEM_REPAIR_SAFE_IN_USE, > + MEM_REPAIR_HPA, > + MEM_REPAIR_DPA, > + MEM_REPAIR_NIBBLE_MASK, > + MEM_REPAIR_BANK_GROUP, > + MEM_REPAIR_BANK, > + MEM_REPAIR_RANK, > + MEM_REPAIR_ROW, > + MEM_REPAIR_COLUMN, > + MEM_REPAIR_CHANNEL, > + MEM_REPAIR_SUB_CHANNEL, > + MEM_REPAIR_QUERY, > + MEM_DO_REPAIR, > + MEM_REPAIR_MAX_ATTRS > +}; > + > +struct edac_mem_repair_dev_attr { > + struct device_attribute dev_attr; > + u8 instance; > +}; > + > +struct edac_mem_repair_context { > + char name[EDAC_FEAT_NAME_LEN]; > + struct edac_mem_repair_dev_attr mem_repair_dev_attr[MEM_REPAIR_MAX_ATTRS]; > + struct attribute *mem_repair_attrs[MEM_REPAIR_MAX_ATTRS + 1]; > + struct attribute_group group; > +}; > + > +#define TO_MEM_REPAIR_DEV_ATTR(_dev_attr) \ > + container_of(_dev_attr, struct edac_mem_repair_dev_attr, dev_attr) > + > +#define EDAC_MEM_REPAIR_ATTR_SHOW(attrib, cb, type, format) \ > +static ssize_t attrib##_show(struct device *ras_feat_dev, \ > + struct device_attribute *attr, char *buf) \ > +{ \ > + u8 inst = TO_MEM_REPAIR_DEV_ATTR(attr)->instance; \ > + struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); \ > + const struct edac_mem_repair_ops *ops = \ > + ctx->mem_repair[inst].mem_repair_ops; \ > + type data; \ > + int ret; \ > + \ > + ret = ops->cb(ras_feat_dev->parent, ctx->mem_repair[inst].private, \ > + &data); \ > + if (ret) \ > + return ret; \ > + \ > + return sysfs_emit(buf, format, data); \ > +} > + > +EDAC_MEM_REPAIR_ATTR_SHOW(repair_type, get_repair_type, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(persist_mode, get_persist_mode, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(dpa_support, get_dpa_support, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(repair_safe_when_in_use, get_repair_safe_when_in_use, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(hpa, get_hpa, u64, "0x%llx\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(dpa, get_dpa, u64, "0x%llx\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(nibble_mask, get_nibble_mask, u64, "0x%llx\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(bank_group, get_bank_group, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(bank, get_bank, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(rank, get_rank, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(row, get_row, u64, "0x%llx\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(column, get_column, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(channel, get_channel, u32, "%u\n") > +EDAC_MEM_REPAIR_ATTR_SHOW(sub_channel, get_sub_channel, u32, "%u\n") > + > +#define EDAC_MEM_REPAIR_ATTR_STORE(attrib, cb, type, conv_func) \ > +static ssize_t attrib##_store(struct device *ras_feat_dev, \ > + struct device_attribute *attr, \ > + const char *buf, size_t len) \ > +{ \ > + u8 inst = TO_MEM_REPAIR_DEV_ATTR(attr)->instance; \ > + struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); \ > + const struct edac_mem_repair_ops *ops = \ > + ctx->mem_repair[inst].mem_repair_ops; \ > + type data; \ > + int ret; \ > + \ > + ret = conv_func(buf, 0, &data); \ > + if (ret < 0) \ > + return ret; \ > + \ > + ret = ops->cb(ras_feat_dev->parent, ctx->mem_repair[inst].private, \ > + data); \ > + if (ret) \ > + return ret; \ > + \ > + return len; \ > +} > + > +EDAC_MEM_REPAIR_ATTR_STORE(persist_mode, set_persist_mode, unsigned long, kstrtoul) > +EDAC_MEM_REPAIR_ATTR_STORE(hpa, set_hpa, u64, kstrtou64) > +EDAC_MEM_REPAIR_ATTR_STORE(dpa, set_dpa, u64, kstrtou64) > +EDAC_MEM_REPAIR_ATTR_STORE(nibble_mask, set_nibble_mask, u64, kstrtou64) > +EDAC_MEM_REPAIR_ATTR_STORE(bank_group, set_bank_group, unsigned long, kstrtoul) > +EDAC_MEM_REPAIR_ATTR_STORE(bank, set_bank, unsigned long, kstrtoul) > +EDAC_MEM_REPAIR_ATTR_STORE(rank, set_rank, unsigned long, kstrtoul) > +EDAC_MEM_REPAIR_ATTR_STORE(row, set_row, u64, kstrtou64) > +EDAC_MEM_REPAIR_ATTR_STORE(column, set_column, unsigned long, kstrtoul) > +EDAC_MEM_REPAIR_ATTR_STORE(channel, set_channel, unsigned long, kstrtoul) > +EDAC_MEM_REPAIR_ATTR_STORE(sub_channel, set_sub_channel, unsigned long, kstrtoul) > + > +#define EDAC_MEM_REPAIR_DO_OP(attrib, cb) \ > +static ssize_t attrib##_store(struct device *ras_feat_dev, \ > + struct device_attribute *attr, \ > + const char *buf, size_t len) \ > +{ \ > + u8 inst = TO_MEM_REPAIR_DEV_ATTR(attr)->instance; \ > + struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); \ > + const struct edac_mem_repair_ops *ops = ctx->mem_repair[inst].mem_repair_ops; \ > + int ret; \ > + \ > + ret = ops->cb(ras_feat_dev->parent, ctx->mem_repair[inst].private); \ > + if (ret) \ > + return ret; \ > + \ > + return len; \ > +} > + > +EDAC_MEM_REPAIR_DO_OP(query, do_query) > +EDAC_MEM_REPAIR_DO_OP(repair, do_repair) > + > +static ssize_t persist_mode_avail_show(struct device *ras_feat_dev, > + struct device_attribute *attr, char *buf) > +{ > + struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + u8 inst = TO_MEM_REPAIR_DEV_ATTR(attr)->instance; > + const struct edac_mem_repair_ops *ops = ctx->mem_repair[inst].mem_repair_ops; > + > + return ops->get_persist_mode_avail(ras_feat_dev->parent, > + ctx->mem_repair[inst].private, buf); > +} > + > +static umode_t mem_repair_attr_visible(struct kobject *kobj, struct attribute *a, int attr_id) > +{ > + struct device *ras_feat_dev = kobj_to_dev(kobj); > + struct device_attribute *dev_attr = container_of(a, struct device_attribute, attr); > + struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + u8 inst = TO_MEM_REPAIR_DEV_ATTR(dev_attr)->instance; > + const struct edac_mem_repair_ops *ops = ctx->mem_repair[inst].mem_repair_ops; > + > + switch (attr_id) { > + case MEM_REPAIR_TYPE: > + if (ops->get_repair_type) > + return a->mode; > + break; > + case MEM_REPAIR_PERSIST_MODE_AVAIL: > + if (ops->get_persist_mode_avail) > + return a->mode; > + break; > + case MEM_REPAIR_PERSIST_MODE: > + if (ops->get_persist_mode) { > + if (ops->set_persist_mode) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_DPA_SUPPORT: > + if (ops->get_dpa_support) > + return a->mode; > + break; > + case MEM_REPAIR_SAFE_IN_USE: > + if (ops->get_repair_safe_when_in_use) > + return a->mode; > + break; > + case MEM_REPAIR_HPA: > + if (ops->get_hpa) { > + if (ops->set_hpa) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_DPA: > + if (ops->get_dpa) { > + if (ops->set_dpa) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_NIBBLE_MASK: > + if (ops->get_nibble_mask) { > + if (ops->set_nibble_mask) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_BANK_GROUP: > + if (ops->get_bank_group) { > + if (ops->set_bank_group) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_BANK: > + if (ops->get_bank) { > + if (ops->set_bank) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_RANK: > + if (ops->get_rank) { > + if (ops->set_rank) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_ROW: > + if (ops->get_row) { > + if (ops->set_row) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_COLUMN: > + if (ops->get_column) { > + if (ops->set_column) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_CHANNEL: > + if (ops->get_channel) { > + if (ops->set_channel) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_SUB_CHANNEL: > + if (ops->get_sub_channel) { > + if (ops->set_sub_channel) > + return a->mode; > + else > + return 0444; > + } > + break; > + case MEM_REPAIR_QUERY: > + if (ops->do_query) > + return a->mode; > + break; > + case MEM_DO_REPAIR: > + if (ops->do_repair) > + return a->mode; > + break; > + default: > + break; > + } > + > + return 0; > +} > + > +#define EDAC_MEM_REPAIR_ATTR_RO(_name, _instance) \ > + ((struct edac_mem_repair_dev_attr) { .dev_attr = __ATTR_RO(_name), \ > + .instance = _instance }) > + > +#define EDAC_MEM_REPAIR_ATTR_WO(_name, _instance) \ > + ((struct edac_mem_repair_dev_attr) { .dev_attr = __ATTR_WO(_name), \ > + .instance = _instance }) > + > +#define EDAC_MEM_REPAIR_ATTR_RW(_name, _instance) \ > + ((struct edac_mem_repair_dev_attr) { .dev_attr = __ATTR_RW(_name), \ > + .instance = _instance }) > + > +static int mem_repair_create_desc(struct device *dev, > + const struct attribute_group **attr_groups, > + u8 instance) > +{ > + struct edac_mem_repair_context *ctx; > + struct attribute_group *group; > + int i; > + struct edac_mem_repair_dev_attr dev_attr[] = { > + [MEM_REPAIR_TYPE] = EDAC_MEM_REPAIR_ATTR_RO(repair_type, > + instance), > + [MEM_REPAIR_PERSIST_MODE_AVAIL] = > + EDAC_MEM_REPAIR_ATTR_RO(persist_mode_avail, > + instance), > + [MEM_REPAIR_PERSIST_MODE] = > + EDAC_MEM_REPAIR_ATTR_RW(persist_mode, instance), > + [MEM_REPAIR_DPA_SUPPORT] = > + EDAC_MEM_REPAIR_ATTR_RO(dpa_support, instance), > + [MEM_REPAIR_SAFE_IN_USE] = > + EDAC_MEM_REPAIR_ATTR_RO(repair_safe_when_in_use, > + instance), > + [MEM_REPAIR_HPA] = EDAC_MEM_REPAIR_ATTR_RW(hpa, instance), > + [MEM_REPAIR_DPA] = EDAC_MEM_REPAIR_ATTR_RW(dpa, instance), > + [MEM_REPAIR_NIBBLE_MASK] = > + EDAC_MEM_REPAIR_ATTR_RW(nibble_mask, instance), > + [MEM_REPAIR_BANK_GROUP] = > + EDAC_MEM_REPAIR_ATTR_RW(bank_group, instance), > + [MEM_REPAIR_BANK] = EDAC_MEM_REPAIR_ATTR_RW(bank, instance), > + [MEM_REPAIR_RANK] = EDAC_MEM_REPAIR_ATTR_RW(rank, instance), > + [MEM_REPAIR_ROW] = EDAC_MEM_REPAIR_ATTR_RW(row, instance), > + [MEM_REPAIR_COLUMN] = EDAC_MEM_REPAIR_ATTR_RW(column, instance), > + [MEM_REPAIR_CHANNEL] = EDAC_MEM_REPAIR_ATTR_RW(channel, instance), > + [MEM_REPAIR_SUB_CHANNEL] = > + EDAC_MEM_REPAIR_ATTR_RW(sub_channel, instance), > + [MEM_REPAIR_QUERY] = EDAC_MEM_REPAIR_ATTR_WO(query, instance), > + [MEM_DO_REPAIR] = EDAC_MEM_REPAIR_ATTR_WO(repair, instance) > + }; > + > + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); > + if (!ctx) > + return -ENOMEM; > + > + for (i = 0; i < MEM_REPAIR_MAX_ATTRS; i++) { > + memcpy(&ctx->mem_repair_dev_attr[i].dev_attr, > + &dev_attr[i], sizeof(dev_attr[i])); > + ctx->mem_repair_attrs[i] = > + &ctx->mem_repair_dev_attr[i].dev_attr.attr; > + } > + > + sprintf(ctx->name, "%s%d", "mem_repair", instance); > + group = &ctx->group; > + group->name = ctx->name; > + group->attrs = ctx->mem_repair_attrs; > + group->is_visible = mem_repair_attr_visible; > + attr_groups[0] = group; > + > + return 0; > +} > + > +/** > + * edac_mem_repair_get_desc - get EDAC memory repair descriptors > + * @dev: client device with memory repair feature > + * @attr_groups: pointer to attribute group container > + * @instance: device's memory repair instance number. > + * > + * Return: > + * * %0 - Success. > + * * %-EINVAL - Invalid parameters passed. > + * * %-ENOMEM - Dynamic memory allocation failed. > + */ > +int edac_mem_repair_get_desc(struct device *dev, > + const struct attribute_group **attr_groups, u8 instance) > +{ > + if (!dev || !attr_groups) > + return -EINVAL; > + > + return mem_repair_create_desc(dev, attr_groups, instance); > +} > diff --git a/include/linux/edac.h b/include/linux/edac.h > index 04385b1a9283..b52730d63088 100644 > --- a/include/linux/edac.h > +++ b/include/linux/edac.h > @@ -670,6 +670,7 @@ static inline struct dimm_info *edac_get_dimm(struct mem_ctl_info *mci, > enum edac_dev_feat { > RAS_FEAT_SCRUB, > RAS_FEAT_ECS, > + RAS_FEAT_MEM_REPAIR, > RAS_FEAT_MAX > }; > > @@ -731,11 +732,95 @@ int edac_ecs_get_desc(struct device *ecs_dev, > const struct attribute_group **attr_groups, > u16 num_media_frus); > > +enum edac_mem_repair_type { > + EDAC_TYPE_SPPR, > + EDAC_TYPE_HPPR, > + EDAC_TYPE_CACHELINE_MEM_SPARING, > + EDAC_TYPE_ROW_MEM_SPARING, > + EDAC_TYPE_BANK_MEM_SPARING, > + EDAC_TYPE_RANK_MEM_SPARING, > +}; > + > +enum edac_mem_repair_persist_mode { > + EDAC_MEM_REPAIR_SOFT, /* soft memory repair */ > + EDAC_MEM_REPAIR_HARD, /* hard memory repair */ > +}; > + > +/** > + * struct edac_mem_repair_ops - memory repair device operations > + * (all elements optional) > + * @get_repair_type: get the memory repair type, listed in enum edac_mem_repair_type. > + * @get_persist_mode_avail: get the persist modes supported in the device. > + * @get_persist_mode: get the persist mode of the memory repair instance. > + * @set_persist_mode: set the persist mode for the memory repair instance. > + * @get_dpa_support: get dpa support flag. > + * @get_repair_safe_when_in_use: get whether memory media is accessible and > + * data is retained during repair operation. > + * @get_hpa: get HPA for memory repair. > + * @set_hpa: set HPA for memory repair. > + * @get_dpa: get DPA for memory repair. > + * @set_dpa: set DPA for memory repair. > + * @get_nibble_mask: get nibble mask for memory repair. > + * @set_nibble_mask: set nibble mask for memory repair. > + * @get_bank_group: get bank group for memory repair. > + * @set_bank_group: set bank group for memory repair. > + * @get_bank: get bank for memory repair. > + * @set_bank: set bank for memory repair. > + * @get_rank: get rank for memory repair. > + * @set_rank: set rank for memory repair. > + * @get_row: get row for memory repair. > + * @set_row: set row for memory repair. > + * @get_column: get column for memory repair. > + * @set_column: set column for memory repair. > + * @get_channel: get channel for memory repair. > + * @set_channel: set channel for memory repair. > + * @get_sub_channel: get sub channel for memory repair. > + * @set_sub_channel: set sub channel for memory repair. > + * @do_query: Query memory repair operation for the HPA/DPA/other attrs set > + * is supported or not. > + * @do_repair: start memory repair operation for the HPA/DPA/other attrs set. > + */ > +struct edac_mem_repair_ops { > + int (*get_repair_type)(struct device *dev, void *drv_data, u32 *val); > + int (*get_persist_mode_avail)(struct device *dev, void *drv_data, char *buf); > + int (*get_persist_mode)(struct device *dev, void *drv_data, u32 *mode); > + int (*set_persist_mode)(struct device *dev, void *drv_data, u32 mode); > + int (*get_dpa_support)(struct device *dev, void *drv_data, u32 *val); > + int (*get_repair_safe_when_in_use)(struct device *dev, void *drv_data, u32 *val); > + int (*get_hpa)(struct device *dev, void *drv_data, u64 *hpa); > + int (*set_hpa)(struct device *dev, void *drv_data, u64 hpa); > + int (*get_dpa)(struct device *dev, void *drv_data, u64 *dpa); > + int (*set_dpa)(struct device *dev, void *drv_data, u64 dpa); > + int (*get_nibble_mask)(struct device *dev, void *drv_data, u64 *val); > + int (*set_nibble_mask)(struct device *dev, void *drv_data, u64 val); > + int (*get_bank_group)(struct device *dev, void *drv_data, u32 *val); > + int (*set_bank_group)(struct device *dev, void *drv_data, u32 val); > + int (*get_bank)(struct device *dev, void *drv_data, u32 *val); > + int (*set_bank)(struct device *dev, void *drv_data, u32 val); > + int (*get_rank)(struct device *dev, void *drv_data, u32 *val); > + int (*set_rank)(struct device *dev, void *drv_data, u32 val); > + int (*get_row)(struct device *dev, void *drv_data, u64 *val); > + int (*set_row)(struct device *dev, void *drv_data, u64 val); > + int (*get_column)(struct device *dev, void *drv_data, u32 *val); > + int (*set_column)(struct device *dev, void *drv_data, u32 val); > + int (*get_channel)(struct device *dev, void *drv_data, u32 *val); > + int (*set_channel)(struct device *dev, void *drv_data, u32 val); > + int (*get_sub_channel)(struct device *dev, void *drv_data, u32 *val); > + int (*set_sub_channel)(struct device *dev, void *drv_data, u32 val); > + int (*do_query)(struct device *dev, void *drv_data); > + int (*do_repair)(struct device *dev, void *drv_data); > +}; > + > +int edac_mem_repair_get_desc(struct device *dev, > + const struct attribute_group **attr_groups, > + u8 instance); > + > /* EDAC device feature information structure */ > struct edac_dev_data { > union { > const struct edac_scrub_ops *scrub_ops; > const struct edac_ecs_ops *ecs_ops; > + const struct edac_mem_repair_ops *mem_repair_ops; > }; > u8 instance; > void *private; > @@ -746,6 +831,7 @@ struct edac_dev_feat_ctx { > void *private; > struct edac_dev_data *scrub; > struct edac_dev_data ecs; > + struct edac_dev_data *mem_repair; > }; > > struct edac_dev_feature { > @@ -754,6 +840,7 @@ struct edac_dev_feature { > union { > const struct edac_scrub_ops *scrub_ops; > const struct edac_ecs_ops *ecs_ops; > + const struct edac_mem_repair_ops *mem_repair_ops; > }; > void *ctx; > struct edac_ecs_ex_info ecs_info; > -- > 2.34.1 > -- Fan Ni