File Index
Files and directories
Sign in
FILE

mem_repair.c

File preview with syntax highlighting.

linux-6.19.14/drivers/edac/mem_repair.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * The generic EDAC memory repair driver is designed to control the memory
  4 * devices with memory repair features, such as Post Package Repair (PPR),
  5 * memory sparing etc. The common sysfs memory repair interface abstracts
  6 * the control of various arbitrary memory repair functionalities into a
  7 * unified set of functions.
  8 *
  9 * Copyright (c) 2024-2025 HiSilicon Limited.
 10 */
 11
 12#include <linux/edac.h>
 13
 14enum edac_mem_repair_attributes {
 15	MR_TYPE,
 16	MR_PERSIST_MODE,
 17	MR_SAFE_IN_USE,
 18	MR_HPA,
 19	MR_MIN_HPA,
 20	MR_MAX_HPA,
 21	MR_DPA,
 22	MR_MIN_DPA,
 23	MR_MAX_DPA,
 24	MR_NIBBLE_MASK,
 25	MR_BANK_GROUP,
 26	MR_BANK,
 27	MR_RANK,
 28	MR_ROW,
 29	MR_COLUMN,
 30	MR_CHANNEL,
 31	MR_SUB_CHANNEL,
 32	MEM_DO_REPAIR,
 33	MR_MAX_ATTRS
 34};
 35
 36struct edac_mem_repair_dev_attr {
 37	struct device_attribute dev_attr;
 38	u8 instance;
 39};
 40
 41struct edac_mem_repair_context {
 42	char name[EDAC_FEAT_NAME_LEN];
 43	struct edac_mem_repair_dev_attr mem_repair_dev_attr[MR_MAX_ATTRS];
 44	struct attribute *mem_repair_attrs[MR_MAX_ATTRS + 1];
 45	struct attribute_group group;
 46};
 47
 48const char * const edac_repair_type[] = {
 49	[EDAC_REPAIR_PPR] = "ppr",
 50	[EDAC_REPAIR_CACHELINE_SPARING] = "cacheline-sparing",
 51	[EDAC_REPAIR_ROW_SPARING] = "row-sparing",
 52	[EDAC_REPAIR_BANK_SPARING] = "bank-sparing",
 53	[EDAC_REPAIR_RANK_SPARING] = "rank-sparing",
 54};
 55EXPORT_SYMBOL_GPL(edac_repair_type);
 56
 57#define TO_MR_DEV_ATTR(_dev_attr)      \
 58	container_of(_dev_attr, struct edac_mem_repair_dev_attr, dev_attr)
 59
 60#define MR_ATTR_SHOW(attrib, cb, type, format)			\
 61static ssize_t attrib##_show(struct device *ras_feat_dev,			\
 62			     struct device_attribute *attr, char *buf)		\
 63{										\
 64	u8 inst = TO_MR_DEV_ATTR(attr)->instance;			\
 65	struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev);		\
 66	const struct edac_mem_repair_ops *ops =					\
 67		ctx->mem_repair[inst].mem_repair_ops;				\
 68	type data;								\
 69	int ret;								\
 70										\
 71	ret = ops->cb(ras_feat_dev->parent, ctx->mem_repair[inst].private,	\
 72		      &data);							\
 73	if (ret)								\
 74		return ret;							\
 75										\
 76	return sysfs_emit(buf, format, data);					\
 77}
 78
 79MR_ATTR_SHOW(repair_type, get_repair_type, const char *, "%s\n")
 80MR_ATTR_SHOW(persist_mode, get_persist_mode, bool, "%u\n")
 81MR_ATTR_SHOW(repair_safe_when_in_use, get_repair_safe_when_in_use, bool, "%u\n")
 82MR_ATTR_SHOW(hpa, get_hpa, u64, "0x%llx\n")
 83MR_ATTR_SHOW(min_hpa, get_min_hpa, u64, "0x%llx\n")
 84MR_ATTR_SHOW(max_hpa, get_max_hpa, u64, "0x%llx\n")
 85MR_ATTR_SHOW(dpa, get_dpa, u64, "0x%llx\n")
 86MR_ATTR_SHOW(min_dpa, get_min_dpa, u64, "0x%llx\n")
 87MR_ATTR_SHOW(max_dpa, get_max_dpa, u64, "0x%llx\n")
 88MR_ATTR_SHOW(nibble_mask, get_nibble_mask, u32, "0x%x\n")
 89MR_ATTR_SHOW(bank_group, get_bank_group, u32, "%u\n")
 90MR_ATTR_SHOW(bank, get_bank, u32, "%u\n")
 91MR_ATTR_SHOW(rank, get_rank, u32, "%u\n")
 92MR_ATTR_SHOW(row, get_row, u32, "0x%x\n")
 93MR_ATTR_SHOW(column, get_column, u32, "%u\n")
 94MR_ATTR_SHOW(channel, get_channel, u32, "%u\n")
 95MR_ATTR_SHOW(sub_channel, get_sub_channel, u32, "%u\n")
 96
 97#define MR_ATTR_STORE(attrib, cb, type, conv_func)			\
 98static ssize_t attrib##_store(struct device *ras_feat_dev,			\
 99			      struct device_attribute *attr,			\
100			      const char *buf, size_t len)			\
101{										\
102	u8 inst = TO_MR_DEV_ATTR(attr)->instance;			\
103	struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev);		\
104	const struct edac_mem_repair_ops *ops =					\
105		ctx->mem_repair[inst].mem_repair_ops;				\
106	type data;								\
107	int ret;								\
108										\
109	ret = conv_func(buf, 0, &data);						\
110	if (ret < 0)								\
111		return ret;							\
112										\
113	ret = ops->cb(ras_feat_dev->parent, ctx->mem_repair[inst].private,	\
114		      data);							\
115	if (ret)								\
116		return ret;							\
117										\
118	return len;								\
119}
120
121MR_ATTR_STORE(persist_mode, set_persist_mode, unsigned long, kstrtoul)
122MR_ATTR_STORE(hpa, set_hpa, u64, kstrtou64)
123MR_ATTR_STORE(dpa, set_dpa, u64, kstrtou64)
124MR_ATTR_STORE(nibble_mask, set_nibble_mask, unsigned long, kstrtoul)
125MR_ATTR_STORE(bank_group, set_bank_group, unsigned long, kstrtoul)
126MR_ATTR_STORE(bank, set_bank, unsigned long, kstrtoul)
127MR_ATTR_STORE(rank, set_rank, unsigned long, kstrtoul)
128MR_ATTR_STORE(row, set_row, unsigned long, kstrtoul)
129MR_ATTR_STORE(column, set_column, unsigned long, kstrtoul)
130MR_ATTR_STORE(channel, set_channel, unsigned long, kstrtoul)
131MR_ATTR_STORE(sub_channel, set_sub_channel, unsigned long, kstrtoul)
132
133#define MR_DO_OP(attrib, cb)						\
134static ssize_t attrib##_store(struct device *ras_feat_dev,				\
135			      struct device_attribute *attr,				\
136			      const char *buf, size_t len)				\
137{											\
138	u8 inst = TO_MR_DEV_ATTR(attr)->instance;				\
139	struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev);			\
140	const struct edac_mem_repair_ops *ops = ctx->mem_repair[inst].mem_repair_ops;	\
141	unsigned long data;								\
142	int ret;									\
143											\
144	ret = kstrtoul(buf, 0, &data);							\
145	if (ret < 0)									\
146		return ret;								\
147											\
148	ret = ops->cb(ras_feat_dev->parent, ctx->mem_repair[inst].private, data);	\
149	if (ret)									\
150		return ret;								\
151											\
152	return len;									\
153}
154
155MR_DO_OP(repair, do_repair)
156
157static umode_t mem_repair_attr_visible(struct kobject *kobj, struct attribute *a, int attr_id)
158{
159	struct device *ras_feat_dev = kobj_to_dev(kobj);
160	struct device_attribute *dev_attr = container_of(a, struct device_attribute, attr);
161	struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev);
162	u8 inst = TO_MR_DEV_ATTR(dev_attr)->instance;
163	const struct edac_mem_repair_ops *ops = ctx->mem_repair[inst].mem_repair_ops;
164
165	switch (attr_id) {
166	case MR_TYPE:
167		if (ops->get_repair_type)
168			return a->mode;
169		break;
170	case MR_PERSIST_MODE:
171		if (ops->get_persist_mode) {
172			if (ops->set_persist_mode)
173				return a->mode;
174			else
175				return 0444;
176		}
177		break;
178	case MR_SAFE_IN_USE:
179		if (ops->get_repair_safe_when_in_use)
180			return a->mode;
181		break;
182	case MR_HPA:
183		if (ops->get_hpa) {
184			if (ops->set_hpa)
185				return a->mode;
186			else
187				return 0444;
188		}
189		break;
190	case MR_MIN_HPA:
191		if (ops->get_min_hpa)
192			return a->mode;
193		break;
194	case MR_MAX_HPA:
195		if (ops->get_max_hpa)
196			return a->mode;
197		break;
198	case MR_DPA:
199		if (ops->get_dpa) {
200			if (ops->set_dpa)
201				return a->mode;
202			else
203				return 0444;
204		}
205		break;
206	case MR_MIN_DPA:
207		if (ops->get_min_dpa)
208			return a->mode;
209		break;
210	case MR_MAX_DPA:
211		if (ops->get_max_dpa)
212			return a->mode;
213		break;
214	case MR_NIBBLE_MASK:
215		if (ops->get_nibble_mask) {
216			if (ops->set_nibble_mask)
217				return a->mode;
218			else
219				return 0444;
220		}
221		break;
222	case MR_BANK_GROUP:
223		if (ops->get_bank_group) {
224			if (ops->set_bank_group)
225				return a->mode;
226			else
227				return 0444;
228		}
229		break;
230	case MR_BANK:
231		if (ops->get_bank) {
232			if (ops->set_bank)
233				return a->mode;
234			else
235				return 0444;
236		}
237		break;
238	case MR_RANK:
239		if (ops->get_rank) {
240			if (ops->set_rank)
241				return a->mode;
242			else
243				return 0444;
244		}
245		break;
246	case MR_ROW:
247		if (ops->get_row) {
248			if (ops->set_row)
249				return a->mode;
250			else
251				return 0444;
252		}
253		break;
254	case MR_COLUMN:
255		if (ops->get_column) {
256			if (ops->set_column)
257				return a->mode;
258			else
259				return 0444;
260		}
261		break;
262	case MR_CHANNEL:
263		if (ops->get_channel) {
264			if (ops->set_channel)
265				return a->mode;
266			else
267				return 0444;
268		}
269		break;
270	case MR_SUB_CHANNEL:
271		if (ops->get_sub_channel) {
272			if (ops->set_sub_channel)
273				return a->mode;
274			else
275				return 0444;
276		}
277		break;
278	case MEM_DO_REPAIR:
279		if (ops->do_repair)
280			return a->mode;
281		break;
282	default:
283		break;
284	}
285
286	return 0;
287}
288
289static const struct device_attribute mem_repair_dev_attr[] = {
290	[MR_TYPE]	  = __ATTR_RO(repair_type),
291	[MR_PERSIST_MODE] = __ATTR_RW(persist_mode),
292	[MR_SAFE_IN_USE]  = __ATTR_RO(repair_safe_when_in_use),
293	[MR_HPA]	  = __ATTR_RW(hpa),
294	[MR_MIN_HPA]	  = __ATTR_RO(min_hpa),
295	[MR_MAX_HPA]	  = __ATTR_RO(max_hpa),
296	[MR_DPA]	  = __ATTR_RW(dpa),
297	[MR_MIN_DPA]	  = __ATTR_RO(min_dpa),
298	[MR_MAX_DPA]	  = __ATTR_RO(max_dpa),
299	[MR_NIBBLE_MASK]  = __ATTR_RW(nibble_mask),
300	[MR_BANK_GROUP]   = __ATTR_RW(bank_group),
301	[MR_BANK]	  = __ATTR_RW(bank),
302	[MR_RANK]	  = __ATTR_RW(rank),
303	[MR_ROW]	  = __ATTR_RW(row),
304	[MR_COLUMN]	  = __ATTR_RW(column),
305	[MR_CHANNEL]	  = __ATTR_RW(channel),
306	[MR_SUB_CHANNEL]  = __ATTR_RW(sub_channel),
307	[MEM_DO_REPAIR]	  = __ATTR_WO(repair)
308};
309
310static int mem_repair_create_desc(struct device *dev,
311				  const struct attribute_group **attr_groups,
312				  u8 instance)
313{
314	struct edac_mem_repair_context *ctx;
315	struct attribute_group *group;
316	int i;
317	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
318	if (!ctx)
319		return -ENOMEM;
320
321	for (i = 0; i < MR_MAX_ATTRS; i++) {
322		ctx->mem_repair_dev_attr[i].dev_attr = mem_repair_dev_attr[i];
323		ctx->mem_repair_dev_attr[i].instance = instance;
324		sysfs_attr_init(&ctx->mem_repair_dev_attr[i].dev_attr.attr);
325		ctx->mem_repair_attrs[i] =
326			&ctx->mem_repair_dev_attr[i].dev_attr.attr;
327	}
328
329	sprintf(ctx->name, "%s%d", "mem_repair", instance);
330	group = &ctx->group;
331	group->name = ctx->name;
332	group->attrs = ctx->mem_repair_attrs;
333	group->is_visible  = mem_repair_attr_visible;
334	attr_groups[0] = group;
335
336	return 0;
337}
338
339/**
340 * edac_mem_repair_get_desc - get EDAC memory repair descriptors
341 * @dev: client device with memory repair feature
342 * @attr_groups: pointer to attribute group container
343 * @instance: device's memory repair instance number.
344 *
345 * Return:
346 *  * %0	- Success.
347 *  * %-EINVAL	- Invalid parameters passed.
348 *  * %-ENOMEM	- Dynamic memory allocation failed.
349 */
350int edac_mem_repair_get_desc(struct device *dev,
351			     const struct attribute_group **attr_groups, u8 instance)
352{
353	if (!dev || !attr_groups)
354		return -EINVAL;
355
356	return mem_repair_create_desc(dev, attr_groups, instance);
357}
~/ Read-only file preview.