From mboxrd@z Thu Jan 1 00:00:00 1970 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 BE02CC3DA62 for ; Wed, 17 Jul 2024 17:14:28 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 3F1456B0088; Wed, 17 Jul 2024 13:14:28 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 3A14A6B0092; Wed, 17 Jul 2024 13:14:28 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 21B006B0093; Wed, 17 Jul 2024 13:14:28 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0017.hostedemail.com [216.40.44.17]) by kanga.kvack.org (Postfix) with ESMTP id F39866B0088 for ; Wed, 17 Jul 2024 13:14:27 -0400 (EDT) Received: from smtpin30.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay08.hostedemail.com (Postfix) with ESMTP id 799611418A7 for ; Wed, 17 Jul 2024 17:14:27 +0000 (UTC) X-FDA: 82349893374.30.ED7D1B1 Received: from mail-yb1-f175.google.com (mail-yb1-f175.google.com [209.85.219.175]) by imf01.hostedemail.com (Postfix) with ESMTP id 7A2FD4002D for ; Wed, 17 Jul 2024 17:14:25 +0000 (UTC) Authentication-Results: imf01.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=GNBHxbR4; spf=pass (imf01.hostedemail.com: domain of nifan.cxl@gmail.com designates 209.85.219.175 as permitted sender) smtp.mailfrom=nifan.cxl@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1721236412; 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: in-reply-to:in-reply-to:references:references:dkim-signature; bh=kLkXS4JNkxJPRpT02NgFAng+HVvSx9yoDRGZiEauZGI=; b=cw26BOYnhd6ILYmoeX8P3kZzMvEFBHbdbhJnCUQca51CD63Chw4/qRZ4h2wa76wScKURs4 qX8wXEyqR+lsji0kP7W3EuAwIk0/LMFomHkzoHEtUCDtFl1JrqEbx+oWABKy70PzzYgV2l OedqvQ7DLB/kYAODPC9j8PlK6owpcdw= ARC-Authentication-Results: i=1; imf01.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=GNBHxbR4; spf=pass (imf01.hostedemail.com: domain of nifan.cxl@gmail.com designates 209.85.219.175 as permitted sender) smtp.mailfrom=nifan.cxl@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1721236412; a=rsa-sha256; cv=none; b=a13YSFxIS/wBntRzOAhpR6eBO71bSVuvykbklHQA7DI1fvH4OIhf3PcRUijXJDVFoXdmok EwccOCHoM2iRNN8UC9GR7uTKZjdENcYb6oJLPt1B+QgDtwy64sH6GsxfkJSuGnSKdcGJXo oLTTQOVtvbzl2AKTAjh5ERDQKzfaXzQ= Received: by mail-yb1-f175.google.com with SMTP id 3f1490d57ef6-e05ebca3a32so17421276.1 for ; Wed, 17 Jul 2024 10:14:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721236464; x=1721841264; darn=kvack.org; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:date:from:message-id:from:to:cc:subject:date:message-id:reply-to; bh=kLkXS4JNkxJPRpT02NgFAng+HVvSx9yoDRGZiEauZGI=; b=GNBHxbR4vOzSI5rmVoGeMkg+IzXhM0KgBW03yfgkx5OSOzr7VW8uDw2AGGa4j+DfBH 6sqI8DLrj1GVQFxzbj87dufYm2gJo3Srx6V58XgmlsJ2F4fkcQM+WZc2Dpl5fY1nr9ts lGiiKJ6ykdw/+E/8usg7xc7KnsKUOSyTi5y8YO4umeMclEkiw82hTR8SijR6KDt5czrv p8mhZTk0Xwx4Ud6YQdxzAjyL0ARFbTzUeN8vQyG5qAhIhPIOnQesEc/fK9ksuUqdMsjj i/lbZQsyor49uO7xTuw2u47smbSECbKS1jqLGkX7n7YDmZ7TZ8qMHMRnD7m4EpbbUctb bgTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721236464; x=1721841264; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:date:from:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=kLkXS4JNkxJPRpT02NgFAng+HVvSx9yoDRGZiEauZGI=; b=X1Pn5SeytED8GB10Cj/21QBNNe5mXGxUWQUqOPi9OTMbaM59AvFC4sN9Eb4Fo2ElQ2 2Nv+naq2v8jGD59NCZmxx4YHtGtJD/iQOI3UA2Fu5c9HPLVfOg+cs1sUsCv9u0qQPA6X C/kVcChakEbPZtRBHBQE05nrNdgSEgVuNjflOsTlxKf3Aa+8Q2oss40gie0wJCoJFN8Y kpVDCQQlII/CwRVPDtg+1KIDc1ZRLrrOOnq5//zzQ5GJouI3BbcCJevrkrsb8evWsbfS Z/3y/UhKl/MdRslNVzppJ8ZCPh/zGd6CJO+z+0jk7sTR8PeIofW1aN2wdHFGEQyz1+5n L54A== X-Forwarded-Encrypted: i=1; AJvYcCWpZDoBUCjpJTYgnfr0BCY6AUgYMZ1PWDtX3h1FH7BpMadLAqyK2upsXcJImgSvTcRRflXLSWhYt+gfEEU6QOhqZSU= X-Gm-Message-State: AOJu0YznxBf/U4FaC2J7nDKOyP2srVM674f+LeGsypbjJhgXSO22H2h9 re9cp/k6/HOd7ir0iwHfglrHVsMbUC7Mr26+llwX14WGKoNzZFyP X-Google-Smtp-Source: AGHT+IGv0Fp8VtqtaEbxPcnhkeI+TU2IkWi0U9x446wbrGVqb0eUoIkz99EP6B5DwE3+FcxH0Eyo1Q== X-Received: by 2002:a05:6902:1614:b0:e05:e1d5:8852 with SMTP id 3f1490d57ef6-e05ed6de401mr2844209276.13.1721236464289; Wed, 17 Jul 2024 10:14:24 -0700 (PDT) Received: from gpd. ([50.205.20.42]) by smtp.gmail.com with ESMTPSA id 3f1490d57ef6-e05fea266desm6231276.22.2024.07.17.10.14.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 17 Jul 2024 10:14:23 -0700 (PDT) Message-ID: <6697fbef.250a0220.24877d.00a7@mx.google.com> X-Google-Original-Message-ID: From: nifan.cxl@gmail.com X-Google-Original-From: fan@gpd. Date: Wed, 17 Jul 2024 10:13:24 -0700 To: shiju.jose@huawei.com Cc: linux-edac@vger.kernel.org, linux-cxl@vger.kernel.org, linux-acpi@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, bp@alien8.de, tony.luck@intel.com, rafael@kernel.org, lenb@kernel.org, mchehab@kernel.org, dan.j.williams@intel.com, dave@stgolabs.net, jonathan.cameron@huawei.com, dave.jiang@intel.com, alison.schofield@intel.com, vishal.l.verma@intel.com, ira.weiny@intel.com, david@redhat.com, Vilas.Sridharan@amd.com, leo.duran@amd.com, Yazen.Ghannam@amd.com, rientjes@google.com, jiaqiyan@google.com, Jon.Grimm@amd.com, dave.hansen@linux.intel.com, naoya.horiguchi@nec.com, james.morse@arm.com, jthoughton@google.com, somasundaram.a@hpe.com, erdemaktas@google.com, pgonda@google.com, duenwen@google.com, mike.malvestuto@intel.com, gthelen@google.com, wschwartz@amperecomputing.com, dferguson@amperecomputing.com, wbs@os.amperecomputing.com, nifan.cxl@gmail.com, tanxiaofei@huawei.com, prime.zeng@hisilicon.com, roberto.sassu@huawei.com, kangkang.shen@futurewei.com, wanghuiqiang@huawei.com, linuxarm@huawei.com Subject: Re: [RFC PATCH v9 03/11] EDAC: Add EDAC ECS control driver References: <20240716150336.2042-1-shiju.jose@huawei.com> <20240716150336.2042-4-shiju.jose@huawei.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20240716150336.2042-4-shiju.jose@huawei.com> X-Rspamd-Server: rspam06 X-Rspamd-Queue-Id: 7A2FD4002D X-Stat-Signature: jatn749cqpj494nzfd8ecpyg4e1omyjf X-Rspam-User: X-HE-Tag: 1721236465-687243 X-HE-Meta: U2FsdGVkX19oEp1sOMU9fgURgmAkqvGspthAcBy4Ef+pqHDbr7ijzwmyL1v+FOi7Wtpfb3cvb9npMoCVzcaqOUGSjeIaIJ6RufVXPx42+ZhwpwvsnAVdK9yoFnA8uTRAhtp7/lDac7JUZGyxegryU2E5ruNxwJNmK8yahkVTHfWDgvd1puQF/m08h8oAe7K/BnVNzBD1PRn4gGorAQFft+HyuknbtPRSEZdNMM+w0UXtq7SGhtjp+rAkfns2u9UCQSo/ffavAu6q371elI/m/vFFnq6AzYiE5zbkOMUMSLf+5DLBbEVCCRWg+xd0MGGvY4j/tamUAv7ggB+vb9wxycHVERb2C/Mig9l2pkR+fK33mI9GyDVh7RGdO3w0OKfQi3ZPzJX89VkRTjLlHe9vcYVwhI/6j5jIjSWby7UnwoR7CkqkNVAPSIAr9y7mDfoovJjTcvqfKBSN3gqV60I7p6HLzPYCsG4hSU+7gvKIeZycsWEQcyQUXeQPrVE1D7FRc7hCGejNmomvwsqaLegZkt3YWCplORaoyHxuCzBbh9g+TBzVSu7DNu45PnnbmSY3VCBgsd4Q9B1dcrc227NMYYg6+xcrzbN82udv7k0Iz++fK4urrqov4t6tKILTmQAZGIqcii73xNDhczfQ70yEiEUZiI8+bXicZi1AGwkQfdAFC9BvGFaX7avifLORBTiNXp3+UozCpViPAzsul648jusR1GlPGuSbVYwva0uQiX7JL5FC92ui20CYiUqIiWaQ+Jn7/vBja26Z6Lcq84lnLqsT0vJ/8rlgRuGYyXoPSL9dzh4Inlx6Tnhuy94oeah/X9HXeS8N/F7/4iNb+11Ppw+hi17iKqhZU1LNfbs9fs1nhiWuSHRq4vXgMmrIwsutFNgSRZZJvygmwbYNQHTwV/hGLgvB8e3Jc6/79xg2qXc2bMPHG/9AiMHPx7Ozj2gc60GoLWqQIs0bsBsqohF eEN8INx9 JwI5VCiPhqUKqbcW6/qrrH7EUDQtl3kYR0YvQamPSe3d7XRz3Y9ugNd0qcFMw9VespMsbuP4TBWDRKV8DhYSVzzMQRxHPK2GYEG0xRdXzLxIST0lp9y/6ME5Lp1AM28xZi8uXmcIj4bOpmiR7m9sM07T91DDkh3x8ioeCaL2lxdXQmdcf0V3UsaELx4YFTQOj3RdPUlaI5lKJ+HL3kPvHSIUD0/+xKzVwcgIrctasCj6CxpNB7KfrwSwRxiO0Nmcs0nllqUJGSl+QG1vNCiZQWrb0w0R/2Gz1IPKnD9eCbqwhKE1yHsqb7mi2lVjyS4GyVOe3ipeB9X8U/INbK7dQWAFy6LUITd/vqBlzjW+LoNt/OQP/aYrpbQhzx91jegm8n1yrEjwyH5YI+0E4W+9gYG/DiBBB38Of1kvmHJG3MQM1HA+EfyrDcRNJvkWzaYViWs7edF8Z9vvvURc= 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: On Tue, Jul 16, 2024 at 04:03:27PM +0100, shiju.jose@huawei.com wrote: > From: Shiju Jose > > Add EDAC ECS (Error Check Scrub) control driver supports configuring > the memory device's ECS feature. > > The Error Check Scrub (ECS) is a feature defined in JEDEC DDR5 SDRAM > Specification (JESD79-5) and allows the DRAM to internally read, correct > single-bit errors, and write back corrected data bits to the DRAM array > while providing transparency to error counts. > > The DDR5 device contains number of memory media FRUs per device. The > DDR5 ECS feature and thus the ECS control driver supports configuring > the ECS parameters per FRU. > > The memory devices supports ECS feature register with the EDAC ECS driver > and thus with the generic EDAC RAS feature driver, which adds the sysfs > ECS control interface. The ECS control attributes are exposed to the > userspace in /sys/bus/edac/devices//ecs_fruX/. > > Generic EDAC ECS driver and the common sysfs ECS interface promotes > unambiguous control from the userspace irrespective of the underlying > devices, support ECS feature. > > The support for ECS feature is added separately because the DDR5 ECS > feature's control attributes are dissimilar from those of the scrub > feature. > > Note: Documentation can be added if necessary. > > Co-developed-by: Jonathan Cameron > Signed-off-by: Jonathan Cameron > Signed-off-by: Shiju Jose > --- > drivers/edac/Makefile | 2 +- > drivers/edac/edac_ecs.c | 396 +++++++++++++++++++++++++++++++ > drivers/edac/edac_ras_feature.c | 5 + > include/linux/edac_ras_feature.h | 36 +++ > 4 files changed, 438 insertions(+), 1 deletion(-) > create mode 100755 drivers/edac/edac_ecs.c > > diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile > index de56cbd039eb..c1412c7d3efb 100644 > --- a/drivers/edac/Makefile > +++ b/drivers/edac/Makefile > @@ -10,7 +10,7 @@ obj-$(CONFIG_EDAC) := edac_core.o > > edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o > edac_core-y += edac_module.o edac_device_sysfs.o wq.o > -edac_core-y += edac_ras_feature.o edac_scrub.o > +edac_core-y += edac_ras_feature.o edac_scrub.o edac_ecs.o > > edac_core-$(CONFIG_EDAC_DEBUG) += debugfs.o > > diff --git a/drivers/edac/edac_ecs.c b/drivers/edac/edac_ecs.c > new file mode 100755 > index 000000000000..37dabd053c36 > --- /dev/null > +++ b/drivers/edac/edac_ecs.c > @@ -0,0 +1,396 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * ECS driver supporting controlling on die error check scrub > + * (e.g. DDR5 ECS). The common sysfs ECS interface promotes > + * unambiguous access from the userspace. > + * > + * Copyright (c) 2024 HiSilicon Limited. > + */ > + > +#define pr_fmt(fmt) "EDAC ECS: " fmt > + > +#include > + > +#define EDAC_ECS_FRU_NAME "ecs_fru" > + > +enum edac_ecs_attributes { > + ecs_log_entry_type, > + ecs_log_entry_type_per_dram, > + ecs_log_entry_type_per_memory_media, > + ecs_mode, > + ecs_mode_counts_rows, > + ecs_mode_counts_codewords, > + ecs_reset, > + ecs_name, > + ecs_threshold, > + ecs_max_attrs > +}; As mentioned in other review, use uppercase. Fan > + > +struct edac_ecs_dev_attr { > + struct device_attribute dev_attr; > + int fru_id; > +}; > + > +struct edac_ecs_fru_context { > + char name[EDAC_RAS_NAME_LEN]; > + struct edac_ecs_dev_attr ecs_dev_attr[ecs_max_attrs]; > + struct attribute *ecs_attrs[ecs_max_attrs + 1]; > + struct attribute_group group; > +}; > + > +struct edac_ecs_context { > + u16 num_media_frus; > + struct edac_ecs_fru_context *fru_ctxs; > +}; > + > +#define to_ecs_dev_attr(_dev_attr) \ > + container_of(_dev_attr, struct edac_ecs_dev_attr, dev_attr) > + > +static ssize_t log_entry_type_show(struct device *ras_feat_dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + u64 val; > + int ret; > + > + ret = ops->get_log_entry_type(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, &val); > + if (ret) > + return ret; > + > + return sysfs_emit(buf, "0x%llx\n", val); > +} > + > +static ssize_t log_entry_type_store(struct device *ras_feat_dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + long val; > + int ret; > + > + ret = kstrtol(buf, 10, &val); > + if (ret < 0) > + return ret; > + > + ret = ops->set_log_entry_type(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, val); > + if (ret) > + return ret; > + > + return len; > +} > + > +static ssize_t log_entry_type_per_dram_show(struct device *ras_feat_dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + u64 val; > + int ret; > + > + ret = ops->get_log_entry_type_per_dram(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, &val); > + if (ret) > + return ret; > + > + return sysfs_emit(buf, "0x%llx\n", val); > +} > + > +static ssize_t log_entry_type_per_memory_media_show(struct device *ras_feat_dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + u64 val; > + int ret; > + > + ret = ops->get_log_entry_type_per_memory_media(ras_feat_dev->parent, > + ctx->ecs.private, > + ecs_dev_attr->fru_id, &val); > + if (ret) > + return ret; > + > + return sysfs_emit(buf, "0x%llx\n", val); > +} > + > +static ssize_t mode_show(struct device *ras_feat_dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + u64 val; > + int ret; > + > + ret = ops->get_mode(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, &val); > + if (ret) > + return ret; > + > + return sysfs_emit(buf, "0x%llx\n", val); > +} > + > +static ssize_t mode_store(struct device *ras_feat_dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + long val; > + int ret; > + > + ret = kstrtol(buf, 10, &val); > + if (ret < 0) > + return ret; > + > + ret = ops->set_mode(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, val); > + if (ret) > + return ret; > + > + return len; > +} > + > +static ssize_t mode_counts_rows_show(struct device *ras_feat_dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + u64 val; > + int ret; > + > + ret = ops->get_mode_counts_rows(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, &val); > + if (ret) > + return ret; > + > + return sysfs_emit(buf, "0x%llx\n", val); > +} > + > +static ssize_t mode_counts_codewords_show(struct device *ras_feat_dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + u64 val; > + int ret; > + > + ret = ops->get_mode_counts_codewords(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, &val); > + if (ret) > + return ret; > + > + return sysfs_emit(buf, "0x%llx\n", val); > +} > + > +static ssize_t reset_store(struct device *ras_feat_dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + long val; > + int ret; > + > + ret = kstrtol(buf, 10, &val); > + if (ret < 0) > + return ret; > + > + ret = ops->reset(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, val); > + if (ret) > + return ret; > + > + return len; > +} > + > +static ssize_t name_show(struct device *ras_feat_dev, > + struct device_attribute *attr, char *buf) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + int ret; > + > + ret = ops->get_name(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, buf); > + if (ret) > + return ret; > + > + return strlen(buf); > +} > + > +static ssize_t threshold_show(struct device *ras_feat_dev, > + struct device_attribute *attr, char *buf) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + int ret; > + u64 val; > + > + ret = ops->get_threshold(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, &val); > + if (ret) > + return ret; > + > + return sysfs_emit(buf, "0x%llx\n", val); > +} > + > +static ssize_t threshold_store(struct device *ras_feat_dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct edac_ecs_dev_attr *ecs_dev_attr = to_ecs_dev_attr(attr); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + long val; > + int ret; > + > + ret = kstrtol(buf, 10, &val); > + if (ret < 0) > + return ret; > + > + ret = ops->set_threshold(ras_feat_dev->parent, ctx->ecs.private, > + ecs_dev_attr->fru_id, val); > + if (ret) > + return ret; > + > + return len; > +} > + > +static umode_t ecs_attr_visible(struct kobject *kobj, > + struct attribute *a, int attr_id) > +{ > + struct device *ras_feat_dev = kobj_to_dev(kobj); > + struct edac_ras_feat_ctx *ctx = dev_get_drvdata(ras_feat_dev); > + const struct edac_ecs_ops *ops = ctx->ecs.ops; > + > + switch (attr_id) { > + case ecs_log_entry_type: > + if (ops->get_log_entry_type && ops->set_log_entry_type) > + return a->mode; > + if (ops->get_log_entry_type) > + return 0444; > + return 0; > + case ecs_log_entry_type_per_dram: > + return ops->get_log_entry_type_per_dram ? a->mode : 0; > + case ecs_log_entry_type_per_memory_media: > + return ops->get_log_entry_type_per_memory_media ? a->mode : 0; > + case ecs_mode: > + if (ops->get_mode && ops->set_mode) > + return a->mode; > + if (ops->get_mode) > + return 0444; > + return 0; > + case ecs_mode_counts_rows: > + return ops->get_mode_counts_rows ? a->mode : 0; > + case ecs_mode_counts_codewords: > + return ops->get_mode_counts_codewords ? a->mode : 0; > + case ecs_reset: > + return ops->reset ? a->mode : 0; > + case ecs_name: > + return ops->get_name ? a->mode : 0; > + case ecs_threshold: > + if (ops->get_threshold && ops->set_threshold) > + return a->mode; > + if (ops->get_threshold) > + return 0444; > + return 0; > + default: > + return 0; > + } > +} > + > +#define EDAC_ECS_ATTR_RO(_name, _fru_id) \ > + ((struct edac_ecs_dev_attr) { .dev_attr = __ATTR_RO(_name), \ > + .fru_id = _fru_id }) > + > +#define EDAC_ECS_ATTR_WO(_name, _fru_id) \ > + ((struct edac_ecs_dev_attr) { .dev_attr = __ATTR_WO(_name), \ > + .fru_id = _fru_id }) > + > +#define EDAC_ECS_ATTR_RW(_name, _fru_id) \ > + ((struct edac_ecs_dev_attr) { .dev_attr = __ATTR_RW(_name), \ > + .fru_id = _fru_id }) > + > +static int ecs_create_desc(struct device *ecs_dev, > + const struct attribute_group **attr_groups, > + u16 num_media_frus) > +{ > + struct edac_ecs_context *ecs_ctx; > + u32 fru; > + > + ecs_ctx = devm_kzalloc(ecs_dev, sizeof(*ecs_ctx), GFP_KERNEL); > + if (!ecs_ctx) > + return -ENOMEM; > + > + ecs_ctx->num_media_frus = num_media_frus; > + ecs_ctx->fru_ctxs = devm_kcalloc(ecs_dev, num_media_frus, > + sizeof(*ecs_ctx->fru_ctxs), > + GFP_KERNEL); > + if (!ecs_ctx->fru_ctxs) > + return -ENOMEM; > + > + for (fru = 0; fru < num_media_frus; fru++) { > + struct edac_ecs_fru_context *fru_ctx = &ecs_ctx->fru_ctxs[fru]; > + struct attribute_group *group = &fru_ctx->group; > + int i; > + > + fru_ctx->ecs_dev_attr[0] = EDAC_ECS_ATTR_RW(log_entry_type, fru); > + fru_ctx->ecs_dev_attr[1] = EDAC_ECS_ATTR_RO(log_entry_type_per_dram, fru); > + fru_ctx->ecs_dev_attr[2] = EDAC_ECS_ATTR_RO(log_entry_type_per_memory_media, fru); > + fru_ctx->ecs_dev_attr[3] = EDAC_ECS_ATTR_RW(mode, fru); > + fru_ctx->ecs_dev_attr[4] = EDAC_ECS_ATTR_RO(mode_counts_rows, fru); > + fru_ctx->ecs_dev_attr[5] = EDAC_ECS_ATTR_RO(mode_counts_codewords, fru); > + fru_ctx->ecs_dev_attr[6] = EDAC_ECS_ATTR_WO(reset, fru); > + fru_ctx->ecs_dev_attr[7] = EDAC_ECS_ATTR_RO(name, fru); > + fru_ctx->ecs_dev_attr[8] = EDAC_ECS_ATTR_RW(threshold, fru); > + for (i = 0; i < ecs_max_attrs; i++) > + fru_ctx->ecs_attrs[i] = &fru_ctx->ecs_dev_attr[i].dev_attr.attr; > + > + sprintf(fru_ctx->name, "%s%d", EDAC_ECS_FRU_NAME, fru); > + group->name = fru_ctx->name; > + group->attrs = fru_ctx->ecs_attrs; > + group->is_visible = ecs_attr_visible; > + > + attr_groups[fru] = group; > + } > + > + return 0; > +} > + > +/** > + * edac_ecs_get_desc - get edac ecs descriptors > + * @ecs_dev: client ecs device > + * @attr_groups: pointer to attrribute group container > + * @num_media_frus: number of media FRUs in the device > + * > + * Returns 0 on success, error otherwise. > + */ > +int edac_ecs_get_desc(struct device *ecs_dev, > + const struct attribute_group **attr_groups, > + u16 num_media_frus) > +{ > + if (!ecs_dev || !attr_groups || !num_media_frus) > + return -EINVAL; > + > + return ecs_create_desc(ecs_dev, attr_groups, num_media_frus); > +} > diff --git a/drivers/edac/edac_ras_feature.c b/drivers/edac/edac_ras_feature.c > index 48927f868372..a02ffbcc1c1e 100755 > --- a/drivers/edac/edac_ras_feature.c > +++ b/drivers/edac/edac_ras_feature.c > @@ -47,10 +47,15 @@ static int edac_ras_feat_ecs_init(struct device *parent, > const struct attribute_group **attr_groups) > { > int num = efeat->ecs_info.num_media_frus; > + int ret; > > edata->ops = efeat->ecs_ops; > edata->private = efeat->ecs_ctx; > > + ret = edac_ecs_get_desc(parent, attr_groups, num); > + if (ret) > + return ret; > + > return num; > } > > diff --git a/include/linux/edac_ras_feature.h b/include/linux/edac_ras_feature.h > index 462f9ecbf9d4..153f8a3557f1 100755 > --- a/include/linux/edac_ras_feature.h > +++ b/include/linux/edac_ras_feature.h > @@ -47,10 +47,46 @@ struct edac_scrub_ops { > > const struct attribute_group *edac_scrub_get_desc(void); > > +/** > + * struct ecs_ops - ECS device operations (all elements optional) > + * @get_log_entry_type: read the log entry type value. > + * @set_log_entry_type: set the log entry type value. > + * @get_log_entry_type_per_dram: read the log entry type per dram value. > + * @get_log_entry_type_memory_media: read the log entry type per memory media value. > + * @get_mode: read the mode value. > + * @set_mode: set the mode value. > + * @get_mode_counts_rows: read the mode counts rows value. > + * @get_mode_counts_codewords: read the mode counts codewords value. > + * @reset: reset the ECS counter. > + * @get_threshold: read the threshold value. > + * @set_threshold: set the threshold value. > + * @get_name: get the ECS's name. > + */ > +struct edac_ecs_ops { > + int (*get_log_entry_type)(struct device *dev, void *drv_data, int fru_id, u64 *val); > + int (*set_log_entry_type)(struct device *dev, void *drv_data, int fru_id, u64 val); > + int (*get_log_entry_type_per_dram)(struct device *dev, void *drv_data, > + int fru_id, u64 *val); > + int (*get_log_entry_type_per_memory_media)(struct device *dev, void *drv_data, > + int fru_id, u64 *val); > + int (*get_mode)(struct device *dev, void *drv_data, int fru_id, u64 *val); > + int (*set_mode)(struct device *dev, void *drv_data, int fru_id, u64 val); > + int (*get_mode_counts_rows)(struct device *dev, void *drv_data, int fru_id, u64 *val); > + int (*get_mode_counts_codewords)(struct device *dev, void *drv_data, int fru_id, u64 *val); > + int (*reset)(struct device *dev, void *drv_data, int fru_id, u64 val); > + int (*get_threshold)(struct device *dev, void *drv_data, int fru_id, u64 *threshold); > + int (*set_threshold)(struct device *dev, void *drv_data, int fru_id, u64 threshold); > + int (*get_name)(struct device *dev, void *drv_data, int fru_id, char *buf); > +}; > + > struct edac_ecs_ex_info { > u16 num_media_frus; > }; > > +int edac_ecs_get_desc(struct device *ecs_dev, > + const struct attribute_group **attr_groups, > + u16 num_media_frus); > + > /* > * EDAC RAS feature information structure > */ > -- > 2.34.1 >