From patchwork Fri Apr 19 16:47:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shiju Jose X-Patchwork-Id: 13636610 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id B865BC04FF6 for ; Fri, 19 Apr 2024 16:48:10 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 434CE6B00A0; Fri, 19 Apr 2024 12:47:56 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 3971D6B00A1; Fri, 19 Apr 2024 12:47:56 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 1E9856B00A2; Fri, 19 Apr 2024 12:47:56 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0015.hostedemail.com [216.40.44.15]) by kanga.kvack.org (Postfix) with ESMTP id DC7006B00A0 for ; Fri, 19 Apr 2024 12:47:55 -0400 (EDT) Received: from smtpin03.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay10.hostedemail.com (Postfix) with ESMTP id 93ED8C14E7 for ; Fri, 19 Apr 2024 16:47:55 +0000 (UTC) X-FDA: 82026863310.03.6EF514D Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) by imf24.hostedemail.com (Postfix) with ESMTP id 9C0ED18001A for ; Fri, 19 Apr 2024 16:47:53 +0000 (UTC) Authentication-Results: imf24.hostedemail.com; dkim=none; spf=pass (imf24.hostedemail.com: domain of shiju.jose@huawei.com designates 185.176.79.56 as permitted sender) smtp.mailfrom=shiju.jose@huawei.com; dmarc=pass (policy=quarantine) header.from=huawei.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1713545273; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xh2j0U4Juap1J9oHxK/1B4jMI1NVIMdiwHgJ0YtVMzM=; b=filKrJ4BGGPGLr0QtloOedaC6JoXphRoXhFwbFwSFrIKP+8fJFiwNU8pfTnV464ALWiLCC yvrC5abWECPhkdoBvOw1ZaS8U5oObsEbjDl4iT7UG9ZsIPDFiueMd9XZE7o0fY3cr5cMOM Gx+SIFvZVk56q1pdVWBX9kCnZJOQZ8I= ARC-Authentication-Results: i=1; imf24.hostedemail.com; dkim=none; spf=pass (imf24.hostedemail.com: domain of shiju.jose@huawei.com designates 185.176.79.56 as permitted sender) smtp.mailfrom=shiju.jose@huawei.com; dmarc=pass (policy=quarantine) header.from=huawei.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1713545273; a=rsa-sha256; cv=none; b=6nvifXGldRhEsD9SjTTgBSE3P2NSSYacH0d6Ibi0B9vN3FyluqGE6b5S6mRX8mLc7uS6M+ 4Wh/vP8LiHof/3f/uIaFMO809dg3lMNv9pzyKRr3+w0CbBx04IEHFbeYYqoUCI44eseFfF /W6JA85kh5D8WBXMc18cn/d3pFm+WCc= Received: from mail.maildlp.com (unknown [172.18.186.231]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4VLgYf2Cfyz6K8x1; Sat, 20 Apr 2024 00:47:50 +0800 (CST) Received: from lhrpeml500006.china.huawei.com (unknown [7.191.161.198]) by mail.maildlp.com (Postfix) with ESMTPS id AC42F140B55; Sat, 20 Apr 2024 00:47:51 +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_256_GCM_SHA384) id 15.1.2507.35; Fri, 19 Apr 2024 17:47:50 +0100 From: To: , , , , , , , , , CC: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , Subject: [RFC PATCH v8 10/10] ras: scrub: ACPI RAS2: Add memory ACPI RAS2 driver Date: Sat, 20 Apr 2024 00:47:19 +0800 Message-ID: <20240419164720.1765-11-shiju.jose@huawei.com> X-Mailer: git-send-email 2.35.1.windows.2 In-Reply-To: <20240419164720.1765-1-shiju.jose@huawei.com> References: <20240419164720.1765-1-shiju.jose@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.122.247.234] X-ClientProxiedBy: lhrpeml500006.china.huawei.com (7.191.161.198) To lhrpeml500006.china.huawei.com (7.191.161.198) X-Rspamd-Queue-Id: 9C0ED18001A X-Stat-Signature: zpbf7oxsrpmu3oo86bt7r8ftkooo5tr3 X-Rspam-User: X-Rspamd-Server: rspam07 X-HE-Tag: 1713545273-76920 X-HE-Meta: U2FsdGVkX18clCd3aXq0Y12mOhht828XgefVf9nxfazQ90pgax4V5xB4LfT6GJajHeakVorGYjjZY8FXjq97WHMBpXZoWLX8Rpe7wtp+zP1SdFmGbtK/bP7+2QEkSh9HVn0ZKDI+x/th8xBiE4YhBuDzyKhJDM7dCP9gqf2gLBeRbRk60Qfb7nYwGeqfL/hH+LPAju+lpXesuRjAb1hGQHZwnyhwSfEEGiX3NJAHalAEgO8b72EplU1QXwouHdOvuoOn2M4OPhLzWyYd2y0nMpBtj7Oyb7Y6iCFOrSvlTc5VmUuygWu5e+d1xmyWk1jhszBo9lRoYArSAFFEdV/J06wePO1cDyFIq9r47xYbFMQJCqf+AI1z1J2kCBUA9weDvpqwa23mEOhYTz509/cDcsGq23GszRJKMtzG0yRyQNCBjkbhW0cG195LsJuMF94w2YLvri3I5PHx9DLVBW6t/YmyF459k1O7UguUOM5zkTkX06T5c5T8fWFS9SsMwyUAY7cAl+z87p89QOZ9DYd/cFU1BglzWT3dRs4wlznPCzATLRXzGaifl4roLwBWrIIYoYF23ejYHBrxO5tOn475FT5WZfH0zjKB4a31iAdK8gBp3epMnVsVTzI1cYproo2i4ccAmU/ZHedB3OUE8vHvulkf12RQRjf1PhjY9vjdpqEPJuWdNmdwQ239HCZNBbEaouG4izXeH4M3zeaHlmoySK1/n0edxt3Xk3UKrBnOOwMtmuMF51mkbrJWHPPW20tk6PgE6uFuF+M8w9G9H12BaCLEXxG/2Q7FcS1cv1DgkedXDnSW+dNzCpGz365gLbz2s81PZE8t/7i++BGXg7OV70905PLy+0XWDw0to7fKcfSNnjGDo3USXpMVHS6CxBC6t672wwFf2vmblJCAAqi7Qcz3nJ+9Q4Zoxh7GqtvLUq8UmV6YyGO10awhrEt2jpktJ5Y2DiaU1c4yW5DEInr VUyaWf5J mVqTiLVLN5+if3rc05hVTr++JwjiS+Oi2Ail+OwP1wbdPfGI23zXVQ263y0aptZ3p9oOQTx3pF4zFLSa4JmdO9wZm/dYzw4Yhb9HoN4RRy0/ZSG3tgafTO0HlCzlqaGk1l0VQxZNaoF+NDo/Bdp01t+dfk2GkvmrEsZhtAUYItzaL5gPjV+iRwmhiUeX7iw4amGYJP8k6X+wvCUZavtqabFL5kA== X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: From: Shiju Jose Memory ACPI RAS2 driver binds to the platform device add by the ACPI RAS2 table parser. Driver uses a PCC subspace for communicating with the ACPI compliant platform to provide control of memory scrub parameters via the scrub subsystem. Co-developed-by: Jonathan Cameron Signed-off-by: Jonathan Cameron Signed-off-by: Shiju Jose --- Documentation/scrub/scrub-configure.rst | 33 +++ drivers/ras/Kconfig | 10 + drivers/ras/Makefile | 1 + drivers/ras/acpi_ras2.c | 358 ++++++++++++++++++++++++ 4 files changed, 402 insertions(+) create mode 100644 drivers/ras/acpi_ras2.c diff --git a/Documentation/scrub/scrub-configure.rst b/Documentation/scrub/scrub-configure.rst index 2275366b60d3..7a1bf87bc6d7 100644 --- a/Documentation/scrub/scrub-configure.rst +++ b/Documentation/scrub/scrub-configure.rst @@ -50,3 +50,36 @@ The usage takes the form shown in this example:: # echo 0 > /sys/class/ras/ras0/scrub/enable_background # cat /sys/class/ras/ras0/scrub/enable_background # 0 + +2. RAS2 +2.1 On demand scrubbing for a specific memory region. + # echo 0x120000 > /sys/class/ras/ras1/scrub/addr_range_base + # echo 0x150000 > /sys/class/ras/ras1/scrub/addr_range_size + # cat /sys/class/ras/ras1/scrub/rate_available + # 0x1-0x18 + # echo 20 > /sys/class/ras/ras1/scrub/rate + # echo 1 > /sys/class/ras/ras1/scrub/enable_on_demand + # cat /sys/class/ras/ras1/scrub/enable_on_demand + # 1 + # cat /sys/class/ras/ras1/scrub/rate + # 0x14 + # cat /sys/class/ras/ras1/scrub/addr_range_base + # 0x120000 + # cat /sys/class/ras/ras1/scrub/addr_range_size + # 0x150000 + # echo 0 > /sys/class/ras/ras1/scrub/enable_on_demand + # cat /sys/class/ras/ras1/scrub/enable_on_demand + # 0 + +2.2 Background scrubbing the entire memory + # cat /sys/class/ras/ras1/scrub/rate_available + # 0x1-0x18 + # echo 3 > /sys/class/ras/ras1/scrub/rate + # echo 1 > /sys/class/ras/ras1/scrub/enable_background + # cat /sys/class/ras/ras1/scrub/enable_background + # 1 + # cat /sys/class/ras/ras1/scrub/rate + # 0x3 + # echo 0 > /sys/class/ras/ras1/scrub/enable_background + # cat /sys/class/ras/ras1/scrub/enable_background + # 0 diff --git a/drivers/ras/Kconfig b/drivers/ras/Kconfig index 181701479564..57c346dfc01f 100644 --- a/drivers/ras/Kconfig +++ b/drivers/ras/Kconfig @@ -53,4 +53,14 @@ config SCRUB configuring the parameters of underlying scrubbers in the system for the DRAM memories. +config MEM_ACPI_RAS2 + tristate "Memory ACPI RAS2 driver" + depends on ACPI_RAS2 + depends on SCRUB + help + The driver binds to the platform device added by the ACPI RAS2 + table parser. Use a PCC channel subspace for communicating with + the ACPI compliant platform to provide control of memory scrub + parameters via the scrub subsystem. + endif diff --git a/drivers/ras/Makefile b/drivers/ras/Makefile index 89bcf0d84355..48339fee1cb3 100644 --- a/drivers/ras/Makefile +++ b/drivers/ras/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_RAS) += ras.o obj-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_RAS_CEC) += cec.o obj-$(CONFIG_SCRUB) += memory_scrub.o +obj-$(CONFIG_MEM_ACPI_RAS2) += acpi_ras2.o obj-$(CONFIG_RAS_FMPM) += amd/fmpm.o obj-y += amd/atl/ diff --git a/drivers/ras/acpi_ras2.c b/drivers/ras/acpi_ras2.c new file mode 100644 index 000000000000..b3e9b61367bb --- /dev/null +++ b/drivers/ras/acpi_ras2.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ACPI RAS2 memory driver + * + * Copyright (c) 2024 HiSilicon Limited. + * + */ + +#define pr_fmt(fmt) "MEMORY ACPI RAS2: " fmt + +#include +#include +#include + +#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_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) +#define RAS2_PATROL_SCRUB_FLAG_SCRUBBER_RUNNING BIT(0) + +struct acpi_ras2_ps_shared_mem { + struct acpi_ras2_shared_memory common; + struct acpi_ras2_patrol_scrub_parameter params; +}; + +static int ras2_is_patrol_scrub_support(struct ras2_scrub_ctx *ras2_ctx) +{ + struct acpi_ras2_shared_memory __iomem *common = (void *) + ras2_ctx->pcc_subspace->pcc_comm_addr; + + guard(mutex)(&ras2_ctx->lock); + common->set_capabilities[0] = 0; + + return common->features[0] & RAS2_SUPPORT_HW_PARTOL_SCRUB; +} + +static int ras2_update_patrol_scrub_params_cache(struct ras2_scrub_ctx *ras2_ctx) +{ + struct acpi_ras2_ps_shared_mem __iomem *ps_sm = (void *) + ras2_ctx->pcc_subspace->pcc_comm_addr; + int ret; + + ps_sm->common.set_capabilities[0] = RAS2_SUPPORT_HW_PARTOL_SCRUB; + ps_sm->params.patrol_scrub_command = RAS2_GET_PATROL_PARAMETERS; + + ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC); + if (ret) { + dev_err(ras2_ctx->dev, "failed to read parameters\n"); + return ret; + } + + ras2_ctx->rate_min = FIELD_GET(RAS2_PATROL_SCRUB_MIN_RATE_OUT_MASK, + ps_sm->params.scrub_params_out); + ras2_ctx->rate_max = FIELD_GET(RAS2_PATROL_SCRUB_MAX_RATE_OUT_MASK, + ps_sm->params.scrub_params_out); + ras2_ctx->base = ps_sm->params.actual_address_range[0]; + ras2_ctx->size = ps_sm->params.actual_address_range[1]; + ras2_ctx->rate = FIELD_GET(RAS2_PATROL_SCRUB_RATE_OUT_MASK, + ps_sm->params.scrub_params_out); + return 0; +} + +/* Context - lock must be held */ +static int ras2_get_patrol_scrub_running(struct ras2_scrub_ctx *ras2_ctx, + bool *running) +{ + struct acpi_ras2_ps_shared_mem __iomem *ps_sm = (void *) + ras2_ctx->pcc_subspace->pcc_comm_addr; + int ret; + + if (ras2_ctx->bg) + *running = true; + + ps_sm->common.set_capabilities[0] = RAS2_SUPPORT_HW_PARTOL_SCRUB; + ps_sm->params.patrol_scrub_command = RAS2_GET_PATROL_PARAMETERS; + + ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC); + if (ret) { + dev_err(ras2_ctx->dev, "failed to read parameters\n"); + return ret; + } + + *running = ps_sm->params.flags & RAS2_PATROL_SCRUB_FLAG_SCRUBBER_RUNNING; + + return 0; +} + +static int ras2_hw_scrub_write_rate(struct device *dev, u64 rate) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + bool running; + int ret; + + guard(mutex)(&ras2_ctx->lock); + ret = ras2_get_patrol_scrub_running(ras2_ctx, &running); + if (ret) + return ret; + + if (running) + return -EBUSY; + + if (rate < ras2_ctx->rate_min || rate > ras2_ctx->rate_max) + return -EINVAL; + + ras2_ctx->rate = rate; + + return 0; +} + +static int ras2_hw_scrub_read_rate(struct device *dev, u64 *rate) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + + *rate = ras2_ctx->rate; + + return 0; +} + +static int ras2_hw_scrub_read_rate_avail(struct device *dev, u64 *min, u64 *max) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + + *min = ras2_ctx->rate_min; + *max = ras2_ctx->rate_max; + + return 0; +} + +static int ras2_hw_scrub_read_range(struct device *dev, u64 *base, u64 *size) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + + *base = ras2_ctx->base; + *size = ras2_ctx->size; + + return 0; +} + +static int ras2_hw_scrub_write_range(struct device *dev, u64 base, u64 size) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + bool running; + int ret; + + guard(mutex)(&ras2_ctx->lock); + ret = ras2_get_patrol_scrub_running(ras2_ctx, &running); + if (ret) + return ret; + + if (running) + return -EBUSY; + + ras2_ctx->base = base; + ras2_ctx->size = size; + + return 0; +} + +static int ras2_hw_scrub_set_enabled_bg(struct device *dev, bool enable) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + struct acpi_ras2_ps_shared_mem __iomem *ps_sm = (void *) + ras2_ctx->pcc_subspace->pcc_comm_addr; + int ret; + + guard(mutex)(&ras2_ctx->lock); + ps_sm->common.set_capabilities[0] = RAS2_SUPPORT_HW_PARTOL_SCRUB; + if (enable) { + ps_sm->params.requested_address_range[0] = 0; + ps_sm->params.requested_address_range[1] = 0; + ps_sm->params.scrub_params_in &= ~RAS2_PATROL_SCRUB_RATE_IN_MASK; + ps_sm->params.scrub_params_in |= FIELD_PREP(RAS2_PATROL_SCRUB_RATE_IN_MASK, + ras2_ctx->rate); + ps_sm->params.patrol_scrub_command = RAS2_START_PATROL_SCRUBBER; + } else { + ps_sm->params.patrol_scrub_command = RAS2_STOP_PATROL_SCRUBBER; + } + ps_sm->params.scrub_params_in &= ~RAS2_PATROL_SCRUB_EN_BACKGROUND; + ps_sm->params.scrub_params_in |= FIELD_PREP(RAS2_PATROL_SCRUB_EN_BACKGROUND, + enable); + + ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC); + if (ret) { + dev_err(ras2_ctx->dev, "%s: failed to enable(%d) background scrubbing\n", + __func__, enable); + return ret; + } + ras2_ctx->bg = true; + + /* Update the cache to account for rounding of supplied parameters and similar */ + return ras2_update_patrol_scrub_params_cache(ras2_ctx); +} + +static int ras2_hw_scrub_get_enabled_bg(struct device *dev, bool *enabled) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + + *enabled = ras2_ctx->bg; + + return 0; +} + +static int ras2_hw_scrub_set_enabled_od(struct device *dev, bool enable) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + struct acpi_ras2_ps_shared_mem __iomem *ps_sm = (void *) + ras2_ctx->pcc_subspace->pcc_comm_addr; + bool enabled; + int ret; + + guard(mutex)(&ras2_ctx->lock); + ps_sm->common.set_capabilities[0] = RAS2_SUPPORT_HW_PARTOL_SCRUB; + if (enable) { + if (!ras2_ctx->size) { + dev_warn(ras2_ctx->dev, + "%s: Invalid requested address range, requested_address_range[0]=0x%llx " + "requested_address_range[1]=0x%llx\n", __func__, + ps_sm->params.requested_address_range[0], + ps_sm->params.requested_address_range[1]); + return -ERANGE; + } + ret = ras2_get_patrol_scrub_running(ras2_ctx, &enabled); + if (ret) + return ret; + + if (enabled) + return 0; + + ps_sm->params.scrub_params_in &= ~RAS2_PATROL_SCRUB_RATE_IN_MASK; + ps_sm->params.scrub_params_in |= FIELD_PREP(RAS2_PATROL_SCRUB_RATE_IN_MASK, + ras2_ctx->rate); + ps_sm->params.requested_address_range[0] = ras2_ctx->base; + ps_sm->params.requested_address_range[1] = ras2_ctx->size; + ps_sm->params.patrol_scrub_command = RAS2_START_PATROL_SCRUBBER; + } else { + ps_sm->params.patrol_scrub_command = RAS2_STOP_PATROL_SCRUBBER; + } + + ret = ras2_send_pcc_cmd(ras2_ctx, RAS2_PCC_CMD_EXEC); + if (ret) { + dev_err(ras2_ctx->dev, "failed to enable(%d) the demand scrubbing\n", enable); + return ret; + } + ras2_ctx->bg = false; + + return ras2_update_patrol_scrub_params_cache(ras2_ctx); +} + +static int ras2_hw_scrub_get_enabled_od(struct device *dev, bool *enabled) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + + guard(mutex)(&ras2_ctx->lock); + if (ras2_ctx->bg) { + *enabled = false; + return 0; + } + + return ras2_get_patrol_scrub_running(ras2_ctx, enabled); +} + +static int ras2_hw_scrub_get_name(struct device *dev, char *name) +{ + struct ras2_scrub_ctx *ras2_ctx = dev_get_drvdata(dev); + + return sysfs_emit(name, "ras2_scrub%d\n", ras2_ctx->id); +} + +static const struct scrub_ops ras2_scrub_ops = { + .read_range = ras2_hw_scrub_read_range, + .write_range = ras2_hw_scrub_write_range, + .get_enabled_bg = ras2_hw_scrub_get_enabled_bg, + .set_enabled_bg = ras2_hw_scrub_set_enabled_bg, + .get_enabled_od = ras2_hw_scrub_get_enabled_od, + .set_enabled_od = ras2_hw_scrub_set_enabled_od, + .get_name = ras2_hw_scrub_get_name, + .rate_avail_range = ras2_hw_scrub_read_rate_avail, + .rate_read = ras2_hw_scrub_read_rate, + .rate_write = ras2_hw_scrub_write_rate, +}; + +static DEFINE_IDA(ras2_ida); + +static void ida_release(void *ctx) +{ + struct ras2_scrub_ctx *ras2_ctx = ctx; + + ida_free(&ras2_ida, ras2_ctx->id); +} + +static int ras2_probe(struct platform_device *pdev) +{ + struct ras2_scrub_ctx *ras2_ctx; + struct device *hw_scrub_dev; + int ret, id; + + /* RAS2 PCC Channel and Scrub specific context */ + ras2_ctx = devm_kzalloc(&pdev->dev, sizeof(*ras2_ctx), GFP_KERNEL); + if (!ras2_ctx) + return -ENOMEM; + + ras2_ctx->dev = &pdev->dev; + mutex_init(&ras2_ctx->lock); + + ret = devm_ras2_register_pcc_channel(&pdev->dev, ras2_ctx, + *((int *)dev_get_platdata(&pdev->dev))); + if (ret < 0) { + dev_dbg(ras2_ctx->dev, + "failed to register pcc channel ret=%d\n", ret); + return ret; + } + if (!ras2_is_patrol_scrub_support(ras2_ctx)) + return -EOPNOTSUPP; + + ret = ras2_update_patrol_scrub_params_cache(ras2_ctx); + if (ret) + return ret; + + id = ida_alloc(&ras2_ida, GFP_KERNEL); + if (id < 0) + return id; + + ras2_ctx->id = id; + + ret = devm_add_action_or_reset(&pdev->dev, ida_release, ras2_ctx); + if (ret < 0) + return ret; + + hw_scrub_dev = devm_scrub_device_register(&pdev->dev, ras2_ctx, &ras2_scrub_ops); + if (IS_ERR(hw_scrub_dev)) + return PTR_ERR(hw_scrub_dev); + + ras2_ctx->scrub_dev = hw_scrub_dev; + + return 0; +} + +static const struct platform_device_id ras2_id_table[] = { + { .name = "acpi_ras2", }, + { } +}; +MODULE_DEVICE_TABLE(platform, ras2_id_table); + +static struct platform_driver ras2_driver = { + .probe = ras2_probe, + .driver = { + .name = "acpi_ras2", + }, + .id_table = ras2_id_table, +}; +module_driver(ras2_driver, platform_driver_register, platform_driver_unregister); + +MODULE_IMPORT_NS(ACPI_RAS2); +MODULE_DESCRIPTION("ACPI RAS2 memory driver"); +MODULE_LICENSE("GPL");