File Index
Files and directories
Sign in
FILE

scrub.c

File preview with syntax highlighting.

linux-6.19.14/drivers/edac/scrub.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * The generic EDAC scrub driver controls the memory scrubbers in the
  4 * system. The common sysfs scrub interface abstracts the control of
  5 * various arbitrary scrubbing functionalities into a unified set of
  6 * functions.
  7 *
  8 * Copyright (c) 2024-2025 HiSilicon Limited.
  9 */
 10
 11#include <linux/edac.h>
 12
 13enum edac_scrub_attributes {
 14	SCRUB_ADDRESS,
 15	SCRUB_SIZE,
 16	SCRUB_ENABLE_BACKGROUND,
 17	SCRUB_MIN_CYCLE_DURATION,
 18	SCRUB_MAX_CYCLE_DURATION,
 19	SCRUB_CUR_CYCLE_DURATION,
 20	SCRUB_MAX_ATTRS
 21};
 22
 23struct edac_scrub_dev_attr {
 24	struct device_attribute dev_attr;
 25	u8 instance;
 26};
 27
 28struct edac_scrub_context {
 29	char name[EDAC_FEAT_NAME_LEN];
 30	struct edac_scrub_dev_attr scrub_dev_attr[SCRUB_MAX_ATTRS];
 31	struct attribute *scrub_attrs[SCRUB_MAX_ATTRS + 1];
 32	struct attribute_group group;
 33};
 34
 35#define TO_SCRUB_DEV_ATTR(_dev_attr)      \
 36		container_of(_dev_attr, struct edac_scrub_dev_attr, dev_attr)
 37
 38#define EDAC_SCRUB_ATTR_SHOW(attrib, cb, type, format)				\
 39static ssize_t attrib##_show(struct device *ras_feat_dev,			\
 40			     struct device_attribute *attr, char *buf)		\
 41{										\
 42	u8 inst = TO_SCRUB_DEV_ATTR(attr)->instance;				\
 43	struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev);		\
 44	const struct edac_scrub_ops *ops = ctx->scrub[inst].scrub_ops;		\
 45	type data;								\
 46	int ret;								\
 47										\
 48	ret = ops->cb(ras_feat_dev->parent, ctx->scrub[inst].private, &data);	\
 49	if (ret)								\
 50		return ret;							\
 51										\
 52	return sysfs_emit(buf, format, data);					\
 53}
 54
 55EDAC_SCRUB_ATTR_SHOW(addr, read_addr, u64, "0x%llx\n")
 56EDAC_SCRUB_ATTR_SHOW(size, read_size, u64, "0x%llx\n")
 57EDAC_SCRUB_ATTR_SHOW(enable_background, get_enabled_bg, bool, "%u\n")
 58EDAC_SCRUB_ATTR_SHOW(min_cycle_duration, get_min_cycle, u32, "%u\n")
 59EDAC_SCRUB_ATTR_SHOW(max_cycle_duration, get_max_cycle, u32, "%u\n")
 60EDAC_SCRUB_ATTR_SHOW(current_cycle_duration, get_cycle_duration, u32, "%u\n")
 61
 62#define EDAC_SCRUB_ATTR_STORE(attrib, cb, type, conv_func)			\
 63static ssize_t attrib##_store(struct device *ras_feat_dev,			\
 64			      struct device_attribute *attr,			\
 65			      const char *buf, size_t len)			\
 66{										\
 67	u8 inst = TO_SCRUB_DEV_ATTR(attr)->instance;				\
 68	struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev);		\
 69	const struct edac_scrub_ops *ops = ctx->scrub[inst].scrub_ops;		\
 70	type data;								\
 71	int ret;								\
 72										\
 73	ret = conv_func(buf, 0, &data);						\
 74	if (ret < 0)								\
 75		return ret;							\
 76										\
 77	ret = ops->cb(ras_feat_dev->parent, ctx->scrub[inst].private, data);	\
 78	if (ret)								\
 79		return ret;							\
 80										\
 81	return len;								\
 82}
 83
 84EDAC_SCRUB_ATTR_STORE(addr, write_addr, u64, kstrtou64)
 85EDAC_SCRUB_ATTR_STORE(size, write_size, u64, kstrtou64)
 86EDAC_SCRUB_ATTR_STORE(enable_background, set_enabled_bg, unsigned long, kstrtoul)
 87EDAC_SCRUB_ATTR_STORE(current_cycle_duration, set_cycle_duration, unsigned long, kstrtoul)
 88
 89static umode_t scrub_attr_visible(struct kobject *kobj, struct attribute *a, int attr_id)
 90{
 91	struct device *ras_feat_dev = kobj_to_dev(kobj);
 92	struct device_attribute *dev_attr = container_of(a, struct device_attribute, attr);
 93	u8 inst = TO_SCRUB_DEV_ATTR(dev_attr)->instance;
 94	struct edac_dev_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev);
 95	const struct edac_scrub_ops *ops = ctx->scrub[inst].scrub_ops;
 96
 97	switch (attr_id) {
 98	case SCRUB_ADDRESS:
 99		if (ops->read_addr) {
100			if (ops->write_addr)
101				return a->mode;
102			else
103				return 0444;
104		}
105		break;
106	case SCRUB_SIZE:
107		if (ops->read_size) {
108			if (ops->write_size)
109				return a->mode;
110			else
111				return 0444;
112		}
113		break;
114	case SCRUB_ENABLE_BACKGROUND:
115		if (ops->get_enabled_bg) {
116			if (ops->set_enabled_bg)
117				return a->mode;
118			else
119				return 0444;
120		}
121		break;
122	case SCRUB_MIN_CYCLE_DURATION:
123		if (ops->get_min_cycle)
124			return a->mode;
125		break;
126	case SCRUB_MAX_CYCLE_DURATION:
127		if (ops->get_max_cycle)
128			return a->mode;
129		break;
130	case SCRUB_CUR_CYCLE_DURATION:
131		if (ops->get_cycle_duration) {
132			if (ops->set_cycle_duration)
133				return a->mode;
134			else
135				return 0444;
136		}
137		break;
138	default:
139		break;
140	}
141
142	return 0;
143}
144
145#define EDAC_SCRUB_ATTR_RO(_name, _instance)       \
146	((struct edac_scrub_dev_attr) { .dev_attr = __ATTR_RO(_name), \
147					.instance = _instance })
148
149#define EDAC_SCRUB_ATTR_WO(_name, _instance)       \
150	((struct edac_scrub_dev_attr) { .dev_attr = __ATTR_WO(_name), \
151					.instance = _instance })
152
153#define EDAC_SCRUB_ATTR_RW(_name, _instance)       \
154	((struct edac_scrub_dev_attr) { .dev_attr = __ATTR_RW(_name), \
155					.instance = _instance })
156
157static int scrub_create_desc(struct device *scrub_dev,
158			     const struct attribute_group **attr_groups, u8 instance)
159{
160	struct edac_scrub_context *scrub_ctx;
161	struct attribute_group *group;
162	int i;
163	struct edac_scrub_dev_attr dev_attr[] = {
164		[SCRUB_ADDRESS] = EDAC_SCRUB_ATTR_RW(addr, instance),
165		[SCRUB_SIZE] = EDAC_SCRUB_ATTR_RW(size, instance),
166		[SCRUB_ENABLE_BACKGROUND] = EDAC_SCRUB_ATTR_RW(enable_background, instance),
167		[SCRUB_MIN_CYCLE_DURATION] = EDAC_SCRUB_ATTR_RO(min_cycle_duration, instance),
168		[SCRUB_MAX_CYCLE_DURATION] = EDAC_SCRUB_ATTR_RO(max_cycle_duration, instance),
169		[SCRUB_CUR_CYCLE_DURATION] = EDAC_SCRUB_ATTR_RW(current_cycle_duration, instance)
170	};
171
172	scrub_ctx = devm_kzalloc(scrub_dev, sizeof(*scrub_ctx), GFP_KERNEL);
173	if (!scrub_ctx)
174		return -ENOMEM;
175
176	group = &scrub_ctx->group;
177	for (i = 0; i < SCRUB_MAX_ATTRS; i++) {
178		memcpy(&scrub_ctx->scrub_dev_attr[i], &dev_attr[i], sizeof(dev_attr[i]));
179		sysfs_attr_init(&scrub_ctx->scrub_dev_attr[i].dev_attr.attr);
180		scrub_ctx->scrub_attrs[i] = &scrub_ctx->scrub_dev_attr[i].dev_attr.attr;
181	}
182	sprintf(scrub_ctx->name, "%s%d", "scrub", instance);
183	group->name = scrub_ctx->name;
184	group->attrs = scrub_ctx->scrub_attrs;
185	group->is_visible  = scrub_attr_visible;
186
187	attr_groups[0] = group;
188
189	return 0;
190}
191
192/**
193 * edac_scrub_get_desc - get EDAC scrub descriptors
194 * @scrub_dev: client device, with scrub support
195 * @attr_groups: pointer to attribute group container
196 * @instance: device's scrub instance number.
197 *
198 * Return:
199 *  * %0	- Success.
200 *  * %-EINVAL	- Invalid parameters passed.
201 *  * %-ENOMEM	- Dynamic memory allocation failed.
202 */
203int edac_scrub_get_desc(struct device *scrub_dev,
204			const struct attribute_group **attr_groups, u8 instance)
205{
206	if (!scrub_dev || !attr_groups)
207		return -EINVAL;
208
209	return scrub_create_desc(scrub_dev, attr_groups, instance);
210}
~/ Read-only file preview.