diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index d2e015c09d83..705f346f23de 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -225,6 +225,20 @@ config STM32_FMC2_EBI
 	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
 	  SOCs containing the FMC2 External Bus Interface.
 
+config MEM_RAS2
+	bool "Memory RAS2 driver"
+	depends on ACPI_RAS2
+	depends on SCRUB
+	help
+	  The driver bound to the platform device added by the ACPI RAS2
+	  driver. Driver registers the PCC channel for communicating with
+	  the ACPI compliant platform that contains RAS2 command support
+	  in the hardware.
+	  Registers with the scrub configure driver to provide sysfs interfaces
+	  for configuring the hw patrol scrubber in the system, which exposed
+	  via the ACPI RAS2 table and PCC. Provides the interface functions
+	  support configuring the HW patrol scrubbers in the system.
+
 source "drivers/memory/samsung/Kconfig"
 source "drivers/memory/tegra/Kconfig"
 source "drivers/memory/scrub/Kconfig"
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 4b37312cb342..52afd9d2259a 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -7,6 +7,8 @@ obj-$(CONFIG_DDR)		+= jedec_ddr_data.o
 ifeq ($(CONFIG_DDR),y)
 obj-$(CONFIG_OF)		+= of_memory.o
 endif
+obj-$(CONFIG_MEM_RAS2)		+= ras2_common.o ras2.o
+
 obj-$(CONFIG_ARM_PL172_MPMC)	+= pl172.o
 obj-$(CONFIG_ATMEL_EBI)		+= atmel-ebi.o
 obj-$(CONFIG_BRCMSTB_DPFE)	+= brcmstb_dpfe.o
diff --git a/drivers/memory/ras2.c b/drivers/memory/ras2.c
new file mode 100644
index 000000000000..12fd1f4580d4
--- /dev/null
+++ b/drivers/memory/ras2.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ras2.c - ACPI RAS2 memory driver
+ *
+ * Copyright (c) 2023 HiSilicon Limited.
+ *
+ *  - Registers the PCC channel for communicating with the
+ *    ACPI compliant platform that contains RAS2 command
+ *    support in the hardware.
+ *  - Provides functions to configure HW patrol scrubs
+ *    in the system.
+ *  - Registers with the scrub configure driver for the
+ *    hw patrol scrub in the system, which exposed via
+ *    the ACPI RAS2 table and PCC.
+ */
+
+#define pr_fmt(fmt)	"MEMORY RAS2: " fmt
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/cleanup.h>
+
+#include <acpi/ras2_acpi.h>
+#include <memory/ras2.h>
+
+/* RAS2 specific definitions. */
+#define RAS2_SCRUB	"ras2_scrub"
+#define RAS2_ID_FORMAT RAS2_SCRUB "%d"
+#define RAS2_SUPPORT_HW_PARTOL_SCRUB	BIT(0)
+#define RAS2_TYPE_PATROL_SCRUB	0x0000
+
+#define RAS2_GET_PATROL_PARAMETERS	0x01
+#define	RAS2_START_PATROL_SCRUBBER	0x02
+#define	RAS2_STOP_PATROL_SCRUBBER	0x03
+
+#define RAS2_PATROL_SCRUB_RATE_VALID	BIT(0)
+#define RAS2_PATROL_SCRUB_RATE_IN_MASK	GENMASK(15, 8)
+#define RAS2_PATROL_SCRUB_EN_BACKGROUND	BIT(0)
+#define RAS2_PATROL_SCRUB_RATE_OUT_MASK	GENMASK(7, 0)
+#define RAS2_PATROL_SCRUB_MIN_RATE_OUT_MASK	GENMASK(15, 8)
+#define RAS2_PATROL_SCRUB_MAX_RATE_OUT_MASK	GENMASK(23, 16)
+
+static void ras2_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+	if (ret) {
+		dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n",
+			*(u16 *)msg, ret);
+	} else {
+		dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n",
+			*(u16 *)msg, ret);
+	}
+}
+
+/*
+ * The below functions are exposed to OSPM, to query, configure and
+ * initiate memory patrol scrub.
+ */
+static int ras2_is_patrol_scrub_support(struct ras2_context *ras2_ctx)
+{
+	int ret;
+	struct acpi_ras2_shared_memory  __iomem *generic_comm_base;
+
+	if (!ras2_ctx || !ras2_ctx->pcc_comm_addr)
+		return -EFAULT;
+
+	generic_comm_base = ras2_ctx->pcc_comm_addr;
+	guard(spinlock_irqsave)(&ras2_ctx->spinlock);
+	generic_comm_base->set_capabilities[0] = 0;
+
+	/* send command for reading RAS2 capabilities */
+	ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"%s: ras2_send_pcc_cmd failed\n", __func__);
+		return ret;
+	}
+
+	return generic_comm_base->features[0] & RAS2_SUPPORT_HW_PARTOL_SCRUB;
+}
+
+static int ras2_get_patrol_scrub_params(struct ras2_context *ras2_ctx,
+					struct ras2_scrub_params *params)
+{
+	int ret = 0;
+	u8  min_supp_scrub_rate, max_supp_scrub_rate;
+	struct acpi_ras2_shared_memory  __iomem *generic_comm_base;
+	struct acpi_ras2_patrol_scrub_parameter __iomem *patrol_scrub_params;
+
+	if (!ras2_ctx || !ras2_ctx->pcc_comm_addr)
+		return -EFAULT;
+
+	generic_comm_base = ras2_ctx->pcc_comm_addr;
+	patrol_scrub_params = ras2_ctx->pcc_comm_addr + sizeof(*generic_comm_base);
+
+	guard(spinlock_irqsave)(&ras2_ctx->spinlock);
+	generic_comm_base->set_capabilities[0] = RAS2_SUPPORT_HW_PARTOL_SCRUB;
+	/* send command for reading RAS2 capabilities */
+	ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"%s: ras2_send_pcc_cmd failed\n", __func__);
+		return ret;
+	}
+
+	if (!(generic_comm_base->features[0] & RAS2_SUPPORT_HW_PARTOL_SCRUB) ||
+	    !(generic_comm_base->num_parameter_blocks)) {
+		dev_err(ras2_ctx->dev,
+			"%s: Platform does not support HW Patrol Scrubber\n", __func__);
+		return -EOPNOTSUPP;
+	}
+
+	if (!patrol_scrub_params->requested_address_range[1]) {
+		dev_err(ras2_ctx->dev,
+			"%s: Invalid requested address range, \
+			requested_address_range[0]=0x%llx \
+			requested_address_range[1]=0x%llx\n",
+			__func__,
+			patrol_scrub_params->requested_address_range[0],
+			patrol_scrub_params->requested_address_range[1]);
+		return -EOPNOTSUPP;
+	}
+
+	generic_comm_base->set_capabilities[0] = RAS2_SUPPORT_HW_PARTOL_SCRUB;
+	patrol_scrub_params->header.type = RAS2_TYPE_PATROL_SCRUB;
+	patrol_scrub_params->patrol_scrub_command = RAS2_GET_PATROL_PARAMETERS;
+
+	/* send command for reading the HW patrol scrub parameters */
+	ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"%s: failed to read HW patrol scrub parameters\n",
+			__func__);
+		return ret;
+	}
+
+	/* copy output scrub parameters */
+	params->addr_base = patrol_scrub_params->actual_address_range[0];
+	params->addr_size = patrol_scrub_params->actual_address_range[1];
+	params->flags = patrol_scrub_params->flags;
+	params->rate = FIELD_GET(RAS2_PATROL_SCRUB_RATE_OUT_MASK,
+				 patrol_scrub_params->scrub_params_out);
+	min_supp_scrub_rate = FIELD_GET(RAS2_PATROL_SCRUB_MIN_RATE_OUT_MASK,
+					patrol_scrub_params->scrub_params_out);
+	max_supp_scrub_rate = FIELD_GET(RAS2_PATROL_SCRUB_MAX_RATE_OUT_MASK,
+					patrol_scrub_params->scrub_params_out);
+	snprintf(params->rate_avail, RAS2_MAX_RATE_RANGE_LENGTH,
+		 "%d-%d", min_supp_scrub_rate, max_supp_scrub_rate);
+
+	return 0;
+}
+
+static int ras2_enable_patrol_scrub(struct ras2_context *ras2_ctx, bool enable)
+{
+	int ret = 0;
+	struct ras2_scrub_params params;
+	struct acpi_ras2_shared_memory  __iomem *generic_comm_base;
+	u8 scrub_rate_to_set, min_supp_scrub_rate, max_supp_scrub_rate;
+	struct acpi_ras2_patrol_scrub_parameter __iomem *patrol_scrub_params;
+
+	if (!ras2_ctx || !ras2_ctx->pcc_comm_addr)
+		return -EFAULT;
+
+	generic_comm_base = ras2_ctx->pcc_comm_addr;
+	patrol_scrub_params = ras2_ctx->pcc_comm_addr + sizeof(*generic_comm_base);
+
+	if (enable) {
+		ret = ras2_get_patrol_scrub_params(ras2_ctx, &params);
+		if (ret)
+			return ret;
+	}
+
+	guard(spinlock_irqsave)(&ras2_ctx->spinlock);
+	generic_comm_base->set_capabilities[0] = RAS2_SUPPORT_HW_PARTOL_SCRUB;
+	patrol_scrub_params->header.type = RAS2_TYPE_PATROL_SCRUB;
+
+	if (enable) {
+		patrol_scrub_params->patrol_scrub_command = RAS2_START_PATROL_SCRUBBER;
+		patrol_scrub_params->requested_address_range[0] = params.addr_base;
+		patrol_scrub_params->requested_address_range[1] = params.addr_size;
+
+		scrub_rate_to_set = FIELD_GET(RAS2_PATROL_SCRUB_RATE_IN_MASK,
+					      patrol_scrub_params->scrub_params_in);
+		min_supp_scrub_rate = FIELD_GET(RAS2_PATROL_SCRUB_MIN_RATE_OUT_MASK,
+						patrol_scrub_params->scrub_params_out);
+		max_supp_scrub_rate = FIELD_GET(RAS2_PATROL_SCRUB_MAX_RATE_OUT_MASK,
+						patrol_scrub_params->scrub_params_out);
+		if (scrub_rate_to_set < min_supp_scrub_rate ||
+		    scrub_rate_to_set > max_supp_scrub_rate) {
+			dev_warn(ras2_ctx->dev,
+				 "patrol scrub rate to set is out of the supported range\n");
+			dev_warn(ras2_ctx->dev,
+				 "min_supp_scrub_rate=%d max_supp_scrub_rate=%d\n",
+				 min_supp_scrub_rate, max_supp_scrub_rate);
+			return -EINVAL;
+		}
+	} else {
+		patrol_scrub_params->patrol_scrub_command = RAS2_STOP_PATROL_SCRUBBER;
+	}
+
+	/* send command for enable/disable HW patrol scrub */
+	ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC);
+	if (ret) {
+		pr_err("%s: failed to enable/disable the HW patrol scrub\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ras2_enable_background_scrub(struct ras2_context *ras2_ctx, bool enable)
+{
+	int ret;
+	struct acpi_ras2_shared_memory  __iomem *generic_comm_base;
+	struct acpi_ras2_patrol_scrub_parameter __iomem *patrol_scrub_params;
+
+	if (!ras2_ctx || !ras2_ctx->pcc_comm_addr)
+		return -EFAULT;
+
+	generic_comm_base = ras2_ctx->pcc_comm_addr;
+	patrol_scrub_params = ras2_ctx->pcc_comm_addr + sizeof(*generic_comm_base);
+
+	guard(spinlock_irqsave)(&ras2_ctx->spinlock);
+	generic_comm_base->set_capabilities[0] = RAS2_SUPPORT_HW_PARTOL_SCRUB;
+	patrol_scrub_params->header.type = RAS2_TYPE_PATROL_SCRUB;
+	patrol_scrub_params->patrol_scrub_command = RAS2_START_PATROL_SCRUBBER;
+
+	patrol_scrub_params->scrub_params_in &= ~RAS2_PATROL_SCRUB_EN_BACKGROUND;
+	patrol_scrub_params->scrub_params_in |= FIELD_PREP(RAS2_PATROL_SCRUB_EN_BACKGROUND,
+							   enable);
+
+	/* send command for enable/disable HW patrol scrub */
+	ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"%s: failed to enable/disable background patrol scrubbing\n",
+			__func__);
+		return ret;
+	}
+
+	return 0;
+}
+static int ras2_set_patrol_scrub_params(struct ras2_context *ras2_ctx,
+					struct ras2_scrub_params *params, u8 param_type)
+{
+	struct acpi_ras2_shared_memory  __iomem *generic_comm_base;
+	struct acpi_ras2_patrol_scrub_parameter __iomem *patrol_scrub_params;
+
+	if (!ras2_ctx || !ras2_ctx->pcc_comm_addr)
+		return -EFAULT;
+
+	generic_comm_base = ras2_ctx->pcc_comm_addr;
+	patrol_scrub_params = ras2_ctx->pcc_comm_addr + sizeof(*generic_comm_base);
+
+	guard(spinlock_irqsave)(&ras2_ctx->spinlock);
+	patrol_scrub_params->header.type = RAS2_TYPE_PATROL_SCRUB;
+	if (param_type == RAS2_MEM_SCRUB_PARAM_ADDR_BASE && params->addr_base) {
+		patrol_scrub_params->requested_address_range[0] = params->addr_base;
+	} else if (param_type == RAS2_MEM_SCRUB_PARAM_ADDR_SIZE && params->addr_size) {
+		patrol_scrub_params->requested_address_range[1] = params->addr_size;
+	} else if (param_type == RAS2_MEM_SCRUB_PARAM_RATE) {
+		patrol_scrub_params->scrub_params_in &= ~RAS2_PATROL_SCRUB_RATE_IN_MASK;
+		patrol_scrub_params->scrub_params_in |= FIELD_PREP(RAS2_PATROL_SCRUB_RATE_IN_MASK,
+								   params->rate);
+	} else {
+		dev_err(ras2_ctx->dev, "Invalid patrol scrub parameter to set\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct ras2_hw_scrub_ops ras2_hw_ops = {
+	.enable_scrub = ras2_enable_patrol_scrub,
+	.enable_background_scrub = ras2_enable_background_scrub,
+	.get_scrub_params = ras2_get_patrol_scrub_params,
+	.set_scrub_params = ras2_set_patrol_scrub_params,
+};
+
+static const struct scrub_ops ras2_scrub_ops = {
+	.is_visible = ras2_hw_scrub_is_visible,
+	.read = ras2_hw_scrub_read,
+	.write = ras2_hw_scrub_write,
+	.read_string = ras2_hw_scrub_read_strings,
+};
+
+static DEFINE_IDA(ras2_ida);
+
+static void devm_ras2_release(void *ctx)
+{
+	struct ras2_context *ras2_ctx = ctx;
+
+	ida_free(&ras2_ida, ras2_ctx->id);
+	ras2_unregister_pcc_channel(ras2_ctx);
+}
+
+static int ras2_probe(struct platform_device *pdev)
+{
+	int ret, id;
+	struct mbox_client *cl;
+	struct device *hw_scrub_dev;
+	struct ras2_context *ras2_ctx;
+	char scrub_name[RAS2_MAX_NAME_LENGTH];
+
+	ras2_ctx = devm_kzalloc(&pdev->dev, sizeof(*ras2_ctx), GFP_KERNEL);
+	if (!ras2_ctx)
+		return -ENOMEM;
+
+	ras2_ctx->dev = &pdev->dev;
+	ras2_ctx->ops = &ras2_hw_ops;
+	spin_lock_init(&ras2_ctx->spinlock);
+	platform_set_drvdata(pdev, ras2_ctx);
+
+	cl = &ras2_ctx->mbox_client;
+	/* Request mailbox channel */
+	cl->dev = &pdev->dev;
+	cl->tx_done = ras2_tx_done;
+	cl->knows_txdone = true;
+	ras2_ctx->pcc_subspace_idx = *((int *)pdev->dev.platform_data);
+	dev_dbg(&pdev->dev, "pcc-subspace-id=%d\n", ras2_ctx->pcc_subspace_idx);
+	ret = ras2_register_pcc_channel(ras2_ctx);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_add_action_or_reset(&pdev->dev, devm_ras2_release, ras2_ctx);
+	if (ret < 0)
+		return ret;
+
+	if (ras2_is_patrol_scrub_support(ras2_ctx)) {
+		id = ida_alloc(&ras2_ida, GFP_KERNEL);
+		if (id < 0)
+			return id;
+		ras2_ctx->id = id;
+		snprintf(scrub_name, sizeof(scrub_name), "%s%d", RAS2_SCRUB, id);
+		dev_set_name(&pdev->dev, RAS2_ID_FORMAT, id);
+		hw_scrub_dev = devm_scrub_device_register(&pdev->dev, scrub_name,
+							  ras2_ctx, &ras2_scrub_ops,
+							  0, NULL);
+		if (PTR_ERR_OR_ZERO(hw_scrub_dev))
+			return PTR_ERR_OR_ZERO(hw_scrub_dev);
+	}
+	ras2_ctx->scrub_dev = hw_scrub_dev;
+
+	return 0;
+}
+
+static const struct platform_device_id ras2_id_table[] = {
+	{ .name = "ras2", },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, ras2_id_table);
+
+static struct platform_driver ras2_driver = {
+	.probe = ras2_probe,
+	.driver = {
+		.name = "ras2",
+		.suppress_bind_attrs = true,
+	},
+	.id_table = ras2_id_table,
+};
+module_driver(ras2_driver, platform_driver_register, platform_driver_unregister);
+
+MODULE_DESCRIPTION("ras2 memory driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/memory/ras2_common.c b/drivers/memory/ras2_common.c
new file mode 100644
index 000000000000..97e1852e9fd7
--- /dev/null
+++ b/drivers/memory/ras2_common.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Common functions for memory RAS2 driver
+ *
+ * Copyright (c) 2024 HiSilicon Limited.
+ *
+ * This driver implements call back functions for the scrub
+ * configure driver to configure the parameters of the hw patrol
+ * scrubbers in the system, which exposed via the ACPI AS2
+ * table and PCC.
+ */
+
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <acpi/ras2_acpi.h>
+#include <memory/ras2.h>
+
+static int enable_write(struct ras2_context *ras2_ctx, long val)
+{
+	int ret;
+	bool enable = val;
+
+	ret = ras2_ctx->ops->enable_scrub(ras2_ctx, enable);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"enable patrol scrub fail, enable=%d ret=%d\n",
+			enable, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int enable_background_scrub_write(struct ras2_context *ras2_ctx, long val)
+{
+	int ret;
+	bool enable = val;
+
+	ret = ras2_ctx->ops->enable_background_scrub(ras2_ctx, enable);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"enable background patrol scrub fail, enable=%d ret=%d\n",
+			enable, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int addr_base_read(struct ras2_context *ras2_ctx, u64 *val)
+{
+	int ret;
+	struct ras2_scrub_params params;
+
+	ret = ras2_ctx->ops->get_scrub_params(ras2_ctx, &params);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"get patrol scrub params fail ret=%d\n", ret);
+		return ret;
+	}
+	*val = params.addr_base;
+
+	return 0;
+}
+
+static int addr_base_write(struct ras2_context *ras2_ctx, u64 val)
+{
+	int ret;
+	struct ras2_scrub_params params;
+
+	params.addr_base = val;
+	ret = ras2_ctx->ops->set_scrub_params(ras2_ctx, &params,
+					      RAS2_MEM_SCRUB_PARAM_ADDR_BASE);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"set patrol scrub params for addr_base fail ret=%d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int addr_size_read(struct ras2_context *ras2_ctx, u64 *val)
+{
+	int ret;
+	struct ras2_scrub_params params;
+
+	ret = ras2_ctx->ops->get_scrub_params(ras2_ctx, &params);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"get patrol scrub params fail ret=%d\n", ret);
+		return ret;
+	}
+	*val = params.addr_size;
+
+	return 0;
+}
+
+static int addr_size_write(struct ras2_context *ras2_ctx, u64 val)
+{
+	int ret;
+	struct ras2_scrub_params params;
+
+	params.addr_size = val;
+	ret = ras2_ctx->ops->set_scrub_params(ras2_ctx, &params,
+					      RAS2_MEM_SCRUB_PARAM_ADDR_SIZE);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"set patrol scrub params for addr_size fail ret=%d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rate_read(struct ras2_context *ras2_ctx, u64 *val)
+{
+	int ret;
+	struct ras2_scrub_params params;
+
+	ret = ras2_ctx->ops->get_scrub_params(ras2_ctx, &params);
+	if (ret) {
+		dev_err(ras2_ctx->dev, "get patrol scrub params fail ret=%d\n",
+			ret);
+		return ret;
+	}
+	*val = params.rate;
+
+	return 0;
+}
+
+static int rate_write(struct ras2_context *ras2_ctx, long val)
+{
+	int ret;
+	struct ras2_scrub_params params;
+
+	params.rate = val;
+	ret = ras2_ctx->ops->set_scrub_params(ras2_ctx, &params,
+					      RAS2_MEM_SCRUB_PARAM_RATE);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"set patrol scrub params for rate fail ret=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rate_available_read(struct ras2_context *ras2_ctx, char *buf)
+{
+	int ret;
+	struct ras2_scrub_params params;
+
+	ret = ras2_ctx->ops->get_scrub_params(ras2_ctx, &params);
+	if (ret) {
+		dev_err(ras2_ctx->dev,
+			"get patrol scrub params fail ret=%d\n", ret);
+		return ret;
+	}
+
+	sprintf(buf, "%s\n", params.rate_avail);
+
+	return 0;
+}
+
+/**
+ * ras2_hw_scrub_is_visible() - Callback to return attribute visibility
+ * @drv_data: Pointer to driver-private data structure passed
+ *	      as argument to devm_scrub_device_register().
+ * @attr_id: Scrub attribute
+ * @mode: attribute's mode
+ * @region_id: ID of the memory region
+ *
+ * Returns: 0 on success, an error otherwise
+ */
+umode_t ras2_hw_scrub_is_visible(struct device *dev, u32 attr_id,
+				 umode_t mode, int region_id)
+{
+	switch (attr_id) {
+	case scrub_rate_available:
+	case scrub_enable:
+	case scrub_enable_background_scrub:
+	case scrub_addr_base:
+	case scrub_addr_size:
+	case scrub_rate:
+		return mode;
+	default:
+		return 0;
+	}
+}
+
+/**
+ * ras2_hw_scrub_read() - Read callback for data attributes
+ * @device: Pointer to scrub device
+ * @attr_id: Scrub attribute
+ * @region_id: ID of the memory region
+ * @val: Pointer to the returned data
+ *
+ * Returns: 0 on success, an error otherwise
+ */
+int ras2_hw_scrub_read(struct device *device, u32 attr_id,
+		       int region_id, u64 *val)
+{
+	struct ras2_context *ras2_ctx;
+
+	ras2_ctx = dev_get_drvdata(device);
+
+	switch (attr_id) {
+	case scrub_addr_base:
+		return addr_base_read(ras2_ctx, val);
+	case scrub_addr_size:
+		return addr_size_read(ras2_ctx, val);
+	case scrub_rate:
+		return rate_read(ras2_ctx, val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+/**
+ * ras2_hw_scrub_write() - Write callback for data attributes
+ * @device: Pointer to scrub device
+ * @attr_id: Scrub attribute
+ * @region_id: ID of the memory region
+ * @val: Value to write
+ *
+ * Returns: 0 on success, an error otherwise
+ */
+int ras2_hw_scrub_write(struct device *device, u32 attr_id,
+			int region_id, u64 val)
+{
+	struct ras2_context *ras2_ctx;
+
+	ras2_ctx = dev_get_drvdata(device);
+
+	switch (attr_id) {
+	case scrub_addr_base:
+		return addr_base_write(ras2_ctx, val);
+	case scrub_addr_size:
+		return addr_size_write(ras2_ctx, val);
+	case scrub_enable:
+		return enable_write(ras2_ctx, val);
+	case scrub_enable_background_scrub:
+		return enable_background_scrub_write(ras2_ctx, val);
+	case scrub_rate:
+		return rate_write(ras2_ctx, val);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+/**
+ * ras2_hw_scrub_read_strings() - Read callback for string attributes
+ * @device: Pointer to scrub device
+ * @attr_id: Scrub attribute
+ * @region_id: ID of the memory region
+ * @buf: Pointer to the buffer for copying returned string
+ *
+ * Returns: 0 on success, an error otherwise
+ */
+int ras2_hw_scrub_read_strings(struct device *dev, u32 attr_id,
+			       int region_id, char *buf)
+{
+	struct ras2_context *ras2_ctx;
+
+	ras2_ctx = dev_get_drvdata(dev);
+
+	switch (attr_id) {
+	case scrub_rate_available:
+		return rate_available_read(ras2_ctx, buf);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
diff --git a/include/memory/ras2.h b/include/memory/ras2.h
new file mode 100755
index 000000000000..3db1dce5dd34
--- /dev/null
+++ b/include/memory/ras2.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/*
+ * Memory RAS2 driver header file
+ *
+ * Copyright (c) 2024 HiSilicon Limited
+ */
+
+#ifndef _RAS2_H
+#define _RAS2_H
+
+#include <memory/memory-scrub.h>
+
+#define RAS2_MAX_NAME_LENGTH      64
+#define RAS2_MAX_RATE_RANGE_LENGTH      64
+
+/*
+ * Data structures RAS2
+ */
+
+/**
+ * struct ras2_scrub_params- RAS2 scrub parameter data structure.
+ * @addr_base:	[IN] Base address of the address range to be patrol scrubbed.
+ *		[OUT] Base address of the actual address range.
+ * @addr_size:	[IN] Size of the address range to be patrol scrubbed.
+ *		[OUT] Size of the actual address range.
+ * @flags:	[OUT] The platform returns this value in response to
+ *		GET_PATROL_PARAMETERS.
+ *		For RAS2:
+ *		Bit [0]: Will be set if memory scrubber is already
+ *		running for address range specified in “Actual Address Range”.
+ * @rate:	[IN] Requested patrol scrub rate.
+ *		[OUT] Current patrol scrub rate.
+ * @rate_avail:[OUT] Supported patrol rates.
+ */
+struct ras2_scrub_params {
+	u64 addr_base;
+	u64 addr_size;
+	u16 flags;
+	u32 rate;
+	char rate_avail[RAS2_MAX_RATE_RANGE_LENGTH];
+};
+
+enum {
+	RAS2_MEM_SCRUB_PARAM_ADDR_BASE = 0,
+	RAS2_MEM_SCRUB_PARAM_ADDR_SIZE,
+	RAS2_MEM_SCRUB_PARAM_RATE,
+};
+
+/**
+ * struct ras2_hw_scrub_ops - ras2 hw scrub device operations
+ * @enable_scrub: Function to enable/disable RAS2 scrubber.
+ *		Parameters are:
+ *		@ras2_ctx: Pointer to RAS2 context structure.
+ *		@enable: enable/disable RAS2 patrol scrubber.
+ *		The function returns 0 on success or a negative error number.
+ * @enable_background_scrub: Function to enable/disable background scrubbing.
+ *		Parameters are:
+ *		@ras2_ctx: Pointer to RAS2 context structure.
+ *		@enable: enable/disable background patrol scrubbing.
+ *		The function returns 0 on success or a negative error number.
+ * @get_scrub_params:	Read scrubber parameters. Mandatory
+ *		Parameters are:
+ *		@ras2_ctx: Pointer to RAS2 context structure.
+ *		@params: Pointer to scrub params data structure.
+ *		The function returns 0 on success or a negative error number.
+ * @set_scrub_params: Set scrubber parameters. Mandatory.
+ *		Parameters are:
+ *		@ras2_ctx: Pointer to RAS2 context structure.
+ *		@params: Pointer to scrub params data structure.
+ *		@param_type: Scrub parameter type to set.
+ *		The function returns 0 on success or a negative error number.
+ */
+struct ras2_hw_scrub_ops {
+	int (*enable_scrub)(struct ras2_context *ras2_ctx, bool enable);
+	int (*enable_background_scrub)(struct ras2_context *ras2_ctx, bool enable);
+	int (*get_scrub_params)(struct ras2_context *ras2_ctx,
+				struct ras2_scrub_params *params);
+	int (*set_scrub_params)(struct ras2_context *ras2_ctx,
+				struct ras2_scrub_params *params, u8 param_type);
+};
+
+umode_t ras2_hw_scrub_is_visible(struct device *dev, u32 attr_id,
+				 umode_t mode, int region_id);
+int ras2_hw_scrub_read(struct device *dev, u32 attr_id, int region_id, u64 *val);
+int ras2_hw_scrub_write(struct device *dev, u32 attr_id, int region_id, u64 val);
+int ras2_hw_scrub_read_strings(struct device *dev, u32 attr_id,
+			       int region_id, char *buf);
+#endif /* _RAS2_H */
