Received: by 2002:a05:7412:e794:b0:fa:551:50a7 with SMTP id o20csp1638920rdd; Thu, 11 Jan 2024 05:20:51 -0800 (PST) X-Google-Smtp-Source: AGHT+IFWT6SpjYGDSXW31JYbqeAWbb6yAv5pQ4K0W6rJjvvZZbgt+2U/3dDffp/WMtqN4k55Afzd X-Received: by 2002:aa7:d7c1:0:b0:558:87a2:c6e4 with SMTP id e1-20020aa7d7c1000000b0055887a2c6e4mr617706eds.56.1704979251379; Thu, 11 Jan 2024 05:20:51 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1704979251; cv=none; d=google.com; s=arc-20160816; b=y/gHc8zK6FCxwmIcYOGTuveKvAruUEoUPoqjtwHMIG0VjLZW8x0sE6oW6QrXFPnkUY LIMlENt6W8Grw/QsBe1iltS/rXSdBNG35IWaPy9nqiV0Vu1H0jMEgRnNdQI7aQUyZ5hj yD3eyUxZ3BLxuQ5JcC4boAyAeVEtynbjpxPAw9edaeFZg4Lv03zITYWTA69zxBX+Jn7L dEPU9Mop0cJMFKa1oTRKgKWffaukb/i/eW7DaQd9JHHdvCHRR69gOp1JFRMITGPA9ntS 47pUhVzS9xGgSXHlv+PspmwgeURM50PZ9JW9EqN7ONy4ISA1N0lnvVKdm4TIhpgUeGXM GARA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from; bh=t1BlODPUg6CuoQzmLLcsjii7pGpx0xejdCYXdW/YPx4=; fh=qDqQHfEi9f3w948NNS0VTn/A6XGvBs3E6RqG676YUrA=; b=q57/pUMnpskug023zotOtOn37k4E/A/rrrO4eGitYPEVJCZjqMwBQd/1N7G/+mP914 0ujnRbmV5dShdnXSoJHy94sw0HZMeBYUMCSJrdapy2sy8orLQgMFMIj9zeIBJIX3q0pn GW1wMG4qpgQlDSe5OevBfaBGEwc81hzL6Y4uN4gcYPH1BG4+324BKvnniZrPcLYc53y0 gWxEoxy8i+jKhcpndrcfGgph0dZa8gr9Lw88FBoPrhU3+WyHsWTTWRgCqyLZ3M2ka2o+ Gl087R06QUNtpJedT299ApF3xoubO/pddPKTZUQBTFCUKHXLxliYwFA5Wpbdw/CpS5EH SzzQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel+bounces-23639-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-23639-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=huawei.com Return-Path: Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [147.75.80.249]) by mx.google.com with ESMTPS id x15-20020a50d60f000000b0055400bfd003si528104edi.556.2024.01.11.05.20.51 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jan 2024 05:20:51 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-23639-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) client-ip=147.75.80.249; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel+bounces-23639-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-23639-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=huawei.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id E2DD81F23F4E for ; Thu, 11 Jan 2024 13:20:50 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 2B4083F8C7; Thu, 11 Jan 2024 13:18:13 +0000 (UTC) Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B53C13C49B; Thu, 11 Jan 2024 13:18:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Received: from mail.maildlp.com (unknown [172.18.186.31]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4T9lXc5j7Xz6D8hH; Thu, 11 Jan 2024 21:15:44 +0800 (CST) Received: from lhrpeml500006.china.huawei.com (unknown [7.191.161.198]) by mail.maildlp.com (Postfix) with ESMTPS id 6A04A140B33; Thu, 11 Jan 2024 21:18:07 +0800 (CST) Received: from SecurePC30232.china.huawei.com (10.122.247.234) by lhrpeml500006.china.huawei.com (7.191.161.198) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Thu, 11 Jan 2024 13:18:06 +0000 From: To: , , , , , , , , , CC: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , Subject: [RFC PATCH v5 06/12] memory: scrub: Add scrub subsystem driver supports configuring memory scrubs in the system Date: Thu, 11 Jan 2024 21:17:35 +0800 Message-ID: <20240111131741.1356-7-shiju.jose@huawei.com> X-Mailer: git-send-email 2.35.1.windows.2 In-Reply-To: <20240111131741.1356-1-shiju.jose@huawei.com> References: <20240111131741.1356-1-shiju.jose@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ClientProxiedBy: lhrpeml500004.china.huawei.com (7.191.163.9) To lhrpeml500006.china.huawei.com (7.191.161.198) From: Shiju Jose Add scrub driver supports configuring the memory scrubs in the system. The scrub driver provides the interface for registering the scrub devices and supports configuring memory scrubs in the system. Driver exposes the sysfs scrub control attributes to the user in /sys/class/scrub/scrubX/regionN/ Signed-off-by: Shiju Jose --- .../ABI/testing/sysfs-class-scrub-configure | 91 +++++ drivers/memory/Kconfig | 1 + drivers/memory/Makefile | 1 + drivers/memory/scrub/Kconfig | 11 + drivers/memory/scrub/Makefile | 6 + drivers/memory/scrub/memory-scrub.c | 367 ++++++++++++++++++ include/memory/memory-scrub.h | 78 ++++ 7 files changed, 555 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-scrub-configure create mode 100644 drivers/memory/scrub/Kconfig create mode 100644 drivers/memory/scrub/Makefile create mode 100755 drivers/memory/scrub/memory-scrub.c create mode 100755 include/memory/memory-scrub.h diff --git a/Documentation/ABI/testing/sysfs-class-scrub-configure b/Documentation/ABI/testing/sysfs-class-scrub-configure new file mode 100644 index 000000000000..d2d422b667cf --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-scrub-configure @@ -0,0 +1,91 @@ +What: /sys/class/scrub/ +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + The scrub/ class subdirectory belongs to the + scrubber subsystem. + +What: /sys/class/scrub/scrubX/ +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + The /sys/class/scrub/scrub{0,1,2,3,...} directories + correspond to each scrub device. + +What: /sys/class/scrub/scrubX/name +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + (RO) name of the memory scrub device + +What: /sys/class/scrub/scrubX/regionN/ +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + The /sys/class/scrub/scrubX/region{0,1,2,3,...} + directories correspond to each scrub region under a scrub device. + Scrub region is a physical address range for which scrub may be + separately controlled. Regions may overlap in which case the + scrubbing rate of the overlapped memory will be at least that + expected due to each overlapping region. + +What: /sys/class/scrub/scrubX/regionN/addr_base +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + (RW) The base of the address range of the memory region + to be scrubbed. + On reading, returns the base of the memory region for + the actual address range(The platform calculates + the nearest patrol scrub boundary address from where + it can start scrub). + +What: /sys/class/scrub/scrubX/regionN/addr_size +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + (RW) The size of the address range to be scrubbed. + On reading, returns the size of the memory region for + the actual address range. + +What: /sys/class/scrub/scrubX/regionN/enable +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + (WO) Enable/Disable scrub the memory region. + 1 - enable the memory scrub. + 0 - disable the memory scrub. + +What: /sys/class/scrub/scrubX/regionN/enable_background_scrub +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + (WO) Enable/Disable background scrubbing if supported. + 1 - enable background scrubbing. + 0 - disable background scrubbing. + +What: /sys/class/scrub/scrubX/regionN/rate_available +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + (RO) Supported range for the scrub rate) + by the scrubber for a memory region. + The unit of the scrub rate vary depends on the scrub. + +What: /sys/class/scrub/scrubX/regionN/rate +Date: January 2024 +KernelVersion: 6.8 +Contact: linux-kernel@vger.kernel.org +Description: + (RW) The scrub rate in the memory region specified + and it must be with in the supported range by the scrub. + The unit of the scrub rate vary depends on the scrub. diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 8efdd1f97139..d2e015c09d83 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -227,5 +227,6 @@ config STM32_FMC2_EBI source "drivers/memory/samsung/Kconfig" source "drivers/memory/tegra/Kconfig" +source "drivers/memory/scrub/Kconfig" endif diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index d2e6ca9abbe0..4b37312cb342 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o obj-$(CONFIG_SAMSUNG_MC) += samsung/ obj-$(CONFIG_TEGRA_MC) += tegra/ +obj-$(CONFIG_SCRUB) += scrub/ obj-$(CONFIG_TI_EMIF_SRAM) += ti-emif-sram.o obj-$(CONFIG_FPGA_DFL_EMIF) += dfl-emif.o diff --git a/drivers/memory/scrub/Kconfig b/drivers/memory/scrub/Kconfig new file mode 100644 index 000000000000..fa7d68f53a69 --- /dev/null +++ b/drivers/memory/scrub/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Memory scrub driver configurations +# + +config SCRUB + bool "Memory scrub driver" + help + This option selects the memory scrub subsystem, supports + configuring the parameters of underlying scrubbers in the + system for the DRAM memories. diff --git a/drivers/memory/scrub/Makefile b/drivers/memory/scrub/Makefile new file mode 100644 index 000000000000..1b677132ca13 --- /dev/null +++ b/drivers/memory/scrub/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for memory scrub drivers +# + +obj-$(CONFIG_SCRUB) += memory-scrub.o diff --git a/drivers/memory/scrub/memory-scrub.c b/drivers/memory/scrub/memory-scrub.c new file mode 100755 index 000000000000..02488bbf6392 --- /dev/null +++ b/drivers/memory/scrub/memory-scrub.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Memory scrub driver supports configuring + * the memory scrubs. + * + * Copyright (c) 2023 HiSilicon Limited. + */ + +#define pr_fmt(fmt) "MEM SCRUB: " fmt + +#include +#include +#include +#include +#include +#include +#include + +/* memory scrubber config definitions */ +#define SCRUB_ID_PREFIX "scrub" +#define SCRUB_ID_FORMAT SCRUB_ID_PREFIX "%d" +#define SCRUB_DEV_MAX_NAME_LENGTH 128 +#define SCRUB_MAX_SYSFS_ATTR_NAME_LENGTH 64 + +static DEFINE_IDA(scrub_ida); + +struct scrub_device { + char name[SCRUB_DEV_MAX_NAME_LENGTH]; + int id; + struct device dev; + char region_name[SCRUB_MAX_SYSFS_ATTR_NAME_LENGTH]; + int region_id; + struct attribute_group group; + const struct attribute_group *groups[2]; + const struct scrub_ops *ops; +}; + +#define to_scrub_device(d) container_of(d, struct scrub_device, dev) + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_scrub_device(dev)->name); +} +static DEVICE_ATTR_RO(name); + +static struct attribute *scrub_dev_attrs[] = { + &dev_attr_name.attr, + NULL +}; + +static umode_t scrub_dev_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + if (attr != &dev_attr_name.attr) + return 0; + + return attr->mode; +} + +static const struct attribute_group scrub_dev_attr_group = { + .attrs = scrub_dev_attrs, + .is_visible = scrub_dev_attr_is_visible, +}; + +static const struct attribute_group *scrub_dev_attr_groups[] = { + &scrub_dev_attr_group, + NULL +}; + +static void scrub_dev_release(struct device *dev) +{ + struct scrub_device *scrub_dev = to_scrub_device(dev); + + ida_free(&scrub_ida, scrub_dev->id); + kfree(scrub_dev); +} + +static struct class scrub_class = { + .name = "scrub", + .dev_groups = scrub_dev_attr_groups, + .dev_release = scrub_dev_release, +}; + +static umode_t scrub_attr_visible(struct kobject *kobj, + struct attribute *a, int attr_id) +{ + struct device *dev = kobj_to_dev(kobj); + struct scrub_device *scrub_dev = to_scrub_device(dev); + int region_id = scrub_dev->region_id; + + if (!scrub_dev->ops) + return 0; + + return scrub_dev->ops->is_visible(dev, attr_id, region_id); +} + +static ssize_t scrub_attr_show(struct device *dev, int attr_id, + char *buf) +{ + struct scrub_device *scrub_dev = to_scrub_device(dev); + int region_id = scrub_dev->region_id; + int ret; + u64 val; + + ret = scrub_dev->ops->read(dev, attr_id, region_id, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%lld\n", val); +} + +static ssize_t scrub_attr_show_hex(struct device *dev, int attr_id, + char *buf) +{ + struct scrub_device *scrub_dev = to_scrub_device(dev); + int region_id = scrub_dev->region_id; + int ret; + u64 val; + + ret = scrub_dev->ops->read(dev, attr_id, region_id, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "0x%llx\n", val); +} + +static ssize_t scrub_attr_show_string(struct device *dev, int attr_id, + char *buf) +{ + struct scrub_device *scrub_dev = to_scrub_device(dev); + int region_id = scrub_dev->region_id; + int ret; + + ret = scrub_dev->ops->read_string(dev, attr_id, region_id, buf); + if (ret < 0) + return ret; + + return strlen(buf); +} + +static ssize_t scrub_attr_store(struct device *dev, int attr_id, + const char *buf, size_t count) +{ + struct scrub_device *scrub_dev = to_scrub_device(dev); + int region_id = scrub_dev->region_id; + long val; + int ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + ret = scrub_dev->ops->write(dev, attr_id, region_id, val); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t scrub_attr_store_hex(struct device *dev, int attr_id, + const char *buf, size_t count) +{ + struct scrub_device *scrub_dev = to_scrub_device(dev); + int region_id = scrub_dev->region_id; + int ret; + u64 val; + + ret = kstrtou64(buf, 16, &val); + if (ret < 0) + return ret; + + ret = scrub_dev->ops->write(dev, attr_id, region_id, val); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t show_scrub_attr(struct device *dev, char *buf, int attr_id) +{ + switch (attr_id) { + case scrub_addr_base: + case scrub_addr_size: + return scrub_attr_show_hex(dev, attr_id, buf); + case scrub_enable: + case scrub_rate: + return scrub_attr_show(dev, attr_id, buf); + case scrub_rate_available: + return scrub_attr_show_string(dev, attr_id, buf); + } + + return -ENOTSUPP; +} + +static ssize_t store_scrub_attr(struct device *dev, const char *buf, + size_t count, int attr_id) +{ + switch (attr_id) { + case scrub_addr_base: + case scrub_addr_size: + return scrub_attr_store_hex(dev, attr_id, buf, count); + case scrub_enable: + case scrub_enable_background_scrub: + case scrub_rate: + return scrub_attr_store(dev, attr_id, buf, count); + } + + return -ENOTSUPP; +} + +#define SCRUB_ATTR_RW(attrb) \ +static ssize_t attrb##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + return show_scrub_attr(dev, buf, (scrub_##attrb)); \ +} \ +static ssize_t attrb##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_scrub_attr(dev, buf, count, (scrub_##attrb));\ +} \ +static DEVICE_ATTR_RW(attrb) + +#define SCRUB_ATTR_RO(attrb) \ +static ssize_t attrb##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + return show_scrub_attr(dev, buf, (scrub_##attrb)); \ +} \ +static DEVICE_ATTR_RO(attrb) + +#define SCRUB_ATTR_WO(attrb) \ +static ssize_t attrb##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_scrub_attr(dev, buf, count, (scrub_##attrb));\ +} \ +static DEVICE_ATTR_WO(attrb) + +SCRUB_ATTR_RW(addr_base); +SCRUB_ATTR_RW(addr_size); +SCRUB_ATTR_RW(enable); +SCRUB_ATTR_RW(enable_background_scrub); +SCRUB_ATTR_RW(rate); +SCRUB_ATTR_RO(rate_available); + +static struct attribute *scrub_attrs[] = { + &dev_attr_addr_base.attr, + &dev_attr_addr_size.attr, + &dev_attr_enable.attr, + &dev_attr_enable_background_scrub.attr, + &dev_attr_rate.attr, + &dev_attr_rate_available.attr, + NULL, +}; + +static struct device * +scrub_device_register(struct device *dev, const char *name, void *drvdata, + const struct scrub_ops *ops, + int region_id, + struct attribute_group *attr_group) +{ + struct scrub_device *scrub_dev; + struct device *hdev; + int err; + + scrub_dev = kzalloc(sizeof(*scrub_dev), GFP_KERNEL); + if (!scrub_dev) + return ERR_PTR(-ENOMEM); + hdev = &scrub_dev->dev; + + scrub_dev->id = ida_alloc(&scrub_ida, GFP_KERNEL); + if (scrub_dev->id < 0) { + kfree(scrub_dev); + return ERR_PTR(-ENOMEM); + } + + snprintf((char *)scrub_dev->region_name, SCRUB_MAX_SYSFS_ATTR_NAME_LENGTH, + "region%d", region_id); + if (attr_group) { + attr_group->name = (char *)scrub_dev->region_name; + scrub_dev->groups[0] = attr_group; + scrub_dev->region_id = region_id; + } else { + scrub_dev->group.name = (char *)scrub_dev->region_name; + scrub_dev->group.attrs = scrub_attrs; + scrub_dev->group.is_visible = scrub_attr_visible; + scrub_dev->groups[0] = &scrub_dev->group; + scrub_dev->ops = ops; + scrub_dev->region_id = region_id; + } + + hdev->groups = scrub_dev->groups; + hdev->class = &scrub_class; + hdev->parent = dev; + dev_set_drvdata(hdev, drvdata); + dev_set_name(hdev, SCRUB_ID_FORMAT, scrub_dev->id); + snprintf(scrub_dev->name, SCRUB_DEV_MAX_NAME_LENGTH, "%s", name); + err = device_register(hdev); + if (err) { + put_device(hdev); + return ERR_PTR(err); + } + + return hdev; +} + +static void devm_scrub_release(void *dev) +{ + struct device *hdev = dev; + + device_unregister(hdev); +} + +/** + * devm_scrub_device_register - register hw scrubber device + * @dev: the parent device + * @name: hw scrubber name attribute + * @drvdata: driver data to attach to created device + * @ops: pointer to scrub_ops structure (optional) + * @region_id: region ID + * @attr_group: input attribute group (optional) + * + * Returns the pointer to the new device. The new device is automatically + * unregistered with the parent device. + */ +struct device * +devm_scrub_device_register(struct device *dev, const char *name, + void *drvdata, + const struct scrub_ops *ops, + int region_id, + struct attribute_group *attr_group) +{ + struct device *hdev; + int ret; + + if (!dev || !name) + return ERR_PTR(-EINVAL); + + hdev = scrub_device_register(dev, name, drvdata, ops, + region_id, attr_group); + if (IS_ERR(hdev)) + return hdev; + + ret = devm_add_action_or_reset(dev, devm_scrub_release, hdev); + if (ret) + return ERR_PTR(ret); + + return hdev; +} +EXPORT_SYMBOL_GPL(devm_scrub_device_register); + +static int __init memory_scrub_control_init(void) +{ + int err; + + err = class_register(&scrub_class); + if (err) { + pr_err("couldn't register memory scrub control sysfs class\n"); + return err; + } + + return 0; +} +subsys_initcall(memory_scrub_control_init); diff --git a/include/memory/memory-scrub.h b/include/memory/memory-scrub.h new file mode 100755 index 000000000000..3d7054e98b9a --- /dev/null +++ b/include/memory/memory-scrub.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Memory scrub controller driver support to configure + * the controls of the memory scrub and enable. + * + * Copyright (c) 2023 HiSilicon Limited. + */ + +#ifndef __MEMORY_SCRUB_H +#define __MEMORY_SCRUB_H + +#include + +enum scrub_types { + scrub_common, + scrub_max, +}; + +enum scrub_attributes { + scrub_addr_base, + scrub_addr_size, + scrub_enable, + scrub_enable_background_scrub, + scrub_rate, + scrub_rate_available, + max_attrs, +}; + +/** + * struct scrub_ops - scrub device operations + * @is_visible: Callback to return attribute visibility. Mandatory. + * Parameters are: + * @dev: pointer to hardware scrub device + * @attr: scrub attribute + * @region_id: memory region id + * The function returns the file permissions. + * If the return value is 0, no attribute will be created. + * @read: Read callback for data attributes. Mandatory if readable + * data attributes are present. + * Parameters are: + * @dev: pointer to hardware scrub device + * @attr: scrub attribute + * @region_id: + * memory region id + * @val: pointer to returned value + * The function returns 0 on success or a negative error number. + * @read_string: Read callback for string attributes. Mandatory if string + * attributes are present. + * Parameters are: + * @dev: pointer to hardware scrub device + * @attr: scrub attribute + * @region_id: + * memory region id + * @buf: pointer to buffer to copy string + * The function returns 0 on success or a negative error number. + * @write: Write callback for data attributes. Mandatory if writeable + * data attributes are present. + * Parameters are: + * @dev: pointer to hardware scrub device + * @attr: scrub attribute + * @region_id: + * memory region id + * @val: value to write + * The function returns 0 on success or a negative error number. + */ +struct scrub_ops { + umode_t (*is_visible)(struct device *dev, u32 attr, int region_id); + int (*read)(struct device *dev, u32 attr, int region_id, u64 *val); + int (*read_string)(struct device *dev, u32 attr, int region_id, char *buf); + int (*write)(struct device *dev, u32 attr, int region_id, u64 val); +}; + +struct device * +devm_scrub_device_register(struct device *dev, const char *name, + void *drvdata, const struct scrub_ops *ops, + int region_id, + struct attribute_group *attr_group); +#endif /* __MEMORY_SCRUB_H */ -- 2.34.1