Devicetree
 help / color / mirror / Atom feed
From: Rama devi Veggalam <rama.devi.veggalam@amd.com>
To: <bp@alien8.de>, <tony.luck@intel.com>, <michal.simek@amd.com>,
	<robh@kernel.org>, <krzk+dt@kernel.org>, <conor+dt@kernel.org>
Cc: <linux-kernel@vger.kernel.org>, <linux-edac@vger.kernel.org>,
	<devicetree@vger.kernel.org>, <james.morse@arm.com>,
	<mchehab@kernel.org>, <rric@kernel.org>, <git@amd.com>,
	Rama devi Veggalam <rama.devi.veggalam@amd.com>
Subject: [PATCH v4 3/3] edac: xilinx: Add XilSEM CRAM/NPI error monitoring support
Date: Wed, 21 Oct 2026 18:27:22 +0530	[thread overview]
Message-ID: <1792587442-2794880-4-git-send-email-rama.devi.veggalam@amd.com> (raw)
In-Reply-To: <1792587442-2794880-1-git-send-email-rama.devi.veggalam@amd.com>

Add support for the "xlnx,versal-xilsem" compatible string to
the existing versal_edac driver, registering a second, independent
EDAC device for XilSEM (Xilinx Soft Error Mitigation), which monitors
Configuration RAM (CRAM) and the NoC Programming Interface (NPI) for
correctable and uncorrectable errors.

Add drivers/edac/xsem_edac.h with the register offsets and structure
definitions required for XilSEM, kept separate from the existing DDR
memory-controller (DDRMC) definitions in versal_edac.c.

Extend versal_edac.c with xsem_edac_probe()/xsem_edac_remove() and
the 8 xsem_* sysfs attributes for XilSEM scan control/status
operations.

Update the EDAC_VERSAL Kconfig help text to describe both device
types now handled by this driver, and update MAINTAINERS with the
new files and an additional maintainer.

Signed-off-by: Rama devi Veggalam <rama.devi.veggalam@amd.com>
---
Changes in v4:
- Registered a second, independent EDAC device for XilSEM alongside
  the existing DDRMC device, under the same driver module.
- Added drivers/edac/xsem_edac.h with XilSEM register/structure
  definitions.
- Added xsem_edac_probe()/xsem_edac_remove() and 8 xsem_* sysfs
  attributes, with matching ABI docs in sysfs-devices-versal-edac
  (same commit).
- Updated Kconfig help text and MAINTAINERS for the new files/
  maintainer.

Changes in v3:
- Merged Versal XilSem edac with Versal edac

Changes in v2:
- Patch created on top of dependent patch series "enhance zynqmp_pm_get_family_info()"
- Fixed maximum length warning in patch description
- Added details for eprobe_defer conditions
- Updated copyright information
- Removed ARCH_ZYNQMP in dependent list of XilSEM Kconfig
- Added error code for invalid versal device type
- Removed redundant sysfs details in function headers
- Included MAINTAINERS to this patch
- Added more description in commit message
- Removed print for probe success
- Removed function comments for xsem_edac_remove()
---
 .../ABI/testing/sysfs-devices-versal-edac          |  286 +++++
 MAINTAINERS                                        |    5 +
 drivers/edac/Kconfig                               |   17 +-
 drivers/edac/versal_edac.c                         | 1276 +++++++++++++++++++-
 drivers/edac/xsem_edac.h                           |  219 ++++
 5 files changed, 1781 insertions(+), 22 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-versal-edac
 create mode 100644 drivers/edac/xsem_edac.h

diff --git a/Documentation/ABI/testing/sysfs-devices-versal-edac b/Documentation/ABI/testing/sysfs-devices-versal-edac
new file mode 100644
index 0000000..0bbe207
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-versal-edac
@@ -0,0 +1,286 @@
+What:		/sys/devices/system/edac/versal_xilsem/xsem_scan_control_ops
+Date:		October 2026
+Contact:	rama.devi.veggalam@amd.com
+Description:
+		It is a read/write file.
+		Writing to this file causes the software to initiate a
+		request to the firmware for doing requested scan operation in
+		Configuration RAM (CRAM) or NPI of Versal mono/SSIT devices.
+		User needs to provide scan operation id (init, start, stop) and
+		SLR id (Versal SSIT device) details. For Versal mono devices,
+		the SLR id should be 0.
+		Write format: <scan operation id> <SLR id>, space-separated,
+		each value in decimal or 0x-prefixed hex.
+		The scan operation id values are as given below:
+		1 - Initialize the scan
+		2 - Start CRAM scan
+		3 - Stop CRAM scan
+		5 - Start NPI scan
+		6 - Stop NPI scan
+		7 - Inject NPI error in first descriptor
+		Id 4 (CRAM error injection) is reserved for the separate
+		xsem_cram_injecterr attribute below; writing 4 here returns
+		-EINVAL.
+
+		When read, it shows the current scan status with error code.
+		The format is [header][error code][Slr ID][status word 1]
+		[status word 2][status word 3], where header is
+		(0x1030 | operation Id) and the three trailing status words
+		are additional firmware status data returned alongside the
+		error code.
+		The different error codes are as given below:
+		==========	=====
+		Error Code	Cause
+		==========	=====
+		0x0			Scan operation success
+		0x1			Failure in NPI scan
+		0x80		Calibration timeout
+		0x2000		Internal error
+		0x500000	CRAM initialization not yet done
+		0x600000	Start scan failed
+		0x700000	Stop scan failed
+		0xF00000	Active CRC/UE error
+		0x1000000	ECC/CRC error detected during calibration
+		==========	=====
+
+		Note: the result read back is the last command's result for
+		this attribute, held in state shared by every opener of this
+		file, not per-caller state. Concurrent writers must serialize
+		their own access if they need a guaranteed request/response
+		pairing.
+
+What:		/sys/devices/system/edac/versal_xilsem/xsem_cram_injecterr
+Date:		October 2026
+Contact:	rama.devi.veggalam@amd.com
+Description:
+		It is a read/write file.
+		Writing to this file causes the software to initiate a
+		request to the firmware for doing error injection in
+		Configuration RAM (CRAM) of Versal mono/SSIT devices.
+		User needs to provide the location details of CRAM
+		(frame, qword, bit number, row number) and
+		SLR id (Versal SSIT device) details to inject the error.
+		Write format: <frame> <qword> <bit number> <row number>
+		<SLR id>, space-separated, each value in decimal or
+		0x-prefixed hex.
+		For Versal mono devices, the SLR id should be 0.
+		When read, it shows the current error injection status. The
+		format is [header][error code][Slr ID].
+		Example: [0x10304][0x0][0x0]
+		The different error codes are as given below:
+		==========	=====
+		Error Code	Cause
+		==========	=====
+		0x0			Error injection success
+		0x2000		Internal NULL pointer error
+		0x500000	CRAM initialization not yet done
+		0x800000	Invalid row
+		0x900000	Invalid qword
+		0xA00000	Invalid bit
+		0xB00000	Invalid frame address
+		0xC00000	Unexpected bits flipped
+		0xD00000	Masked bit
+		0xE00000	Invalid block type
+		0xF00000	Active CRC/UE error in CRAM
+		==========	=====
+
+		Note: the result read back is the last command's result for
+		this attribute, held in state shared by every opener of this
+		file, not per-caller state. Concurrent writers must serialize
+		their own access if they need a guaranteed request/response
+		pairing.
+
+What:		/sys/devices/system/edac/versal_xilsem/xsem_cram_framecc_read
+Date:		October 2026
+Contact:	rama.devi.veggalam@amd.com
+Description:
+		It is a read/write file.
+		Writing to this file causes the software to initiate a
+		request to the firmware for reading frame ECC values in
+		Configuration RAM (CRAM) of Versal devices. User needs
+		to provide the location details of CRAM
+		(frame, row number, SLR id in SSIT device) to read the ECC values.
+		Write format: <frame> <row number> <SLR id>, space-separated,
+		each value in decimal or 0x-prefixed hex.
+		For Versal mono devices, SLR id should be 0.
+		When read, it shows the ECC values for the requested frame.
+		The format is <header> <ECC_0> <ECC_1> <status>
+		Example:
+		Read Frame ECC Cmd: [0x3030a]
+		Frame ECC Word_0: [0x52f245]
+		Frame ECC Word_1: [0x7c5a6b]
+		Cmd Status: [0x0]
+
+		Note: the result read back is the last command's result for
+		this attribute, held in state shared by every opener of this
+		file, not per-caller state. Concurrent writers must serialize
+		their own access if they need a guaranteed request/response
+		pairing. This file also returns multiple formatted values per
+		read(), rather than the single value per file recommended by
+		Documentation/filesystems/sysfs.rst.
+
+What:		/sys/devices/system/edac/versal_xilsem/xsem_read_config
+Date:		October 2026
+Contact:	rama.devi.veggalam@amd.com
+Description:
+		It is a read/write file.
+		Writing to this file causes the software to initiate a
+		request to the firmware for reading Xilsem configuration.
+		User needs to provide SLR id (Versal SSIT device) details.
+		For Versal mono devices, the SLR id should be 0.
+		Write format: <SLR id>, in decimal or 0x-prefixed hex.
+		When read, it shows the CRAM and NPI scan configuration.
+		The format is <header> <CRAM config> <NPI config> <status>
+		Example: Read Config Cmd: [0x30309]
+		CRAM Scan Config: [0x2e]
+		NPI Scan Config: [0x5016]
+		Cmd Status: [0x0]
+
+		Note: the result read back is the last command's result for
+		this attribute, held in state shared by every opener of this
+		file, not per-caller state. Concurrent writers must serialize
+		their own access if they need a guaranteed request/response
+		pairing. This file also returns multiple formatted values per
+		read(), rather than the single value per file recommended by
+		Documentation/filesystems/sysfs.rst.
+
+What:		/sys/devices/system/edac/versal_xilsem/xsem_read_status
+Date:		October 2026
+Contact:	rama.devi.veggalam@amd.com
+Description:
+		It is a read/write file.
+		Writing to this file causes the software to initiate a
+		request to read the Xilsem status. User needs to provide
+		the module id for status. The module id values are as given below:
+		1 - CRAM scan
+		2 - NPI scan
+		Write format: <module id>, in decimal or 0x-prefixed hex.
+		When read, it shows the status of the requested module. The
+		format is always [value 0][value 1][value 2].
+		For CRAM: value 0 is status, value 1 is CE count, value 2 is
+		always 0 (unused for this module).
+		Example: [0x10005][0x0][0x0]
+		For NPI: value 0 is status, value 1 is scan count, value 2 is
+		heartbeat count.
+		Example: [0xA01][0x10][0x1]
+
+		Note: the result read back is the last command's result for
+		this attribute, held in state shared by every opener of this
+		file, not per-caller state. Concurrent writers must serialize
+		their own access if they need a guaranteed request/response
+		pairing. This file also returns multiple formatted values per
+		read(), rather than the single value per file recommended by
+		Documentation/filesystems/sysfs.rst.
+
+What:		/sys/devices/system/edac/versal_xilsem/xsem_cram_ssit_getcrc
+Date:		October 2026
+Contact:	rama.devi.veggalam@amd.com
+Description:
+		It is a read/write file.
+		Writing to this file causes the software to initiate a
+		request to read CRC of a row in CRAM. User needs to provide
+		the row number and SLR id (Versal SSIT device) for which CRC
+		to be read. Write format: <row number> <SLR id>,
+		space-separated, each value in decimal or 0x-prefixed hex.
+		For Versal mono devices, SLR id should be 0.
+		When read, it shows the CRC of the requested row.
+		The format is: <Header> <CRC Word 0 to 3> <status>
+		Example: Read CRC Cmd:[0x1030c]
+		CRC_Word 0:[0x0]
+		CRC_Word 1:[0x1f72d881]
+		CRC_Word 2:[0x0]
+		CRC_Word 3:[0x0]
+		Cmd status: [0x0]
+
+		Note: the result read back is the last command's result for
+		this attribute, held in state shared by every opener of this
+		file, not per-caller state. Concurrent writers must serialize
+		their own access if they need a guaranteed request/response
+		pairing. This file also returns multiple formatted values per
+		read(), rather than the single value per file recommended by
+		Documentation/filesystems/sysfs.rst.
+
+What:		/sys/devices/system/edac/versal_xilsem/xsem_read_ssit_status
+Date:		October 2026
+Contact:	rama.devi.veggalam@amd.com
+Description:
+		It is a read/write file.
+		Writing to this file causes the software to initiate a
+		request to read CRAM and NPI scan status information in a given SLR.
+		User needs to provide SLR id in Versal SSIT device for which
+		XilSEM status to be read. Write format: <SLR id>, in decimal
+		or 0x-prefixed hex. When read, it shows
+		the XilSEM status of the requested SLR.
+		The format is: <Header> <SLR id> <NPI status info> <CRAM status info>
+		Example: Read SLR Status Cmd:[0x1030d]
+		SLR ID:[0x3]
+		NPI status:[0xa01]
+		NPI scan count:[0x413]
+		NPI Heartbeat count:[0x1150c]
+		NPI scan skip count 0 :[0x0]
+		NPI scan skip count 1 :[0x0]
+		NPI scan skip count 2 :[0x0]
+		NPI scan skip count 3 :[0x0]
+		NPI scan skip count 4 :[0x0]
+		NPI scan skip count 5 :[0x0]
+		NPI scan skip count 6 :[0x0]
+		NPI scan skip count 7 :[0x0]
+		NPI error info 0 :[0x0]
+		NPI error info 1 :[0x0]
+		CRAM status:[0x10005]
+		Error Location High 0: [0x0]
+		Error Location Low 0: [0x0]
+		Error Location High 1: [0x0]
+		Error Location Low 1: [0x0]
+		Error Location High 2: [0x0]
+		Error Location Low 2: [0x0]
+		Error Location High 3: [0x0]
+		Error Location Low 3: [0x0]
+		Error Location High 4: [0x0]
+		Error Location Low 4: [0x0]
+		Error Location High 5: [0x0]
+		Error Location Low 5: [0x0]
+		Error Location High 6: [0x0]
+		Error Location Low 6: [0x0]
+		CRAM scan CE count:[0x0]
+
+		Note: the result read back is the last command's result for
+		this attribute, held in state shared by every opener of this
+		file, not per-caller state. Concurrent writers must serialize
+		their own access if they need a guaranteed request/response
+		pairing. This file also returns many formatted values across
+		many lines per read(), rather than the single value per file
+		recommended by Documentation/filesystems/sysfs.rst.
+
+What:		/sys/devices/system/edac/versal_xilsem/xsem_total_cframes_ssit
+Date:		October 2026
+Contact:	rama.devi.veggalam@amd.com
+Description:
+		It is a read/write file.
+		Writing to this file causes the software to initiate a
+		request to read total cframes in a row in CRAM.
+		User needs to provide row number and SLR id in
+		Versal SSIT device for which total frames to be read.
+		Write format: <row number> <SLR id>, space-separated, each
+		value in decimal or 0x-prefixed hex.
+		When read, it shows total number of Cframes for the requested SLR.
+		The format is: <Header> <SLR id> <row> <total frames> <Cmd status>
+		Example: Read Total Frames Cmd : [0x4030e]
+		SLR ID : [0x3]
+		Row Index: [0x0]
+		Type[0] frame count: [38752]
+		Type[1] frame count: [262144]
+		Type[2] frame count: [20480]
+		Type[3] frame count: [16]
+		Type[4] frame count: [8]
+		Type[5] frame count: [0]
+		Type[6] frame count: [83]
+		Cmd Status: [0x0]
+
+		Note: the result read back is the last command's result for
+		this attribute, held in state shared by every opener of this
+		file, not per-caller state. Concurrent writers must serialize
+		their own access if they need a guaranteed request/response
+		pairing. This file also returns multiple formatted values per
+		read(), rather than the single value per file recommended by
+		Documentation/filesystems/sysfs.rst.
diff --git a/MAINTAINERS b/MAINTAINERS
index 7b5cab7..fc68e0e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29992,10 +29992,15 @@ F:	include/uapi/linux/xilinx-v4l2-controls.h
 XILINX VERSAL EDAC DRIVER
 M:	Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
 M:	Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
+M:	Rama Devi Veggalam <rama.devi.veggalam@amd.com>
 R:	Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-devices-versal-edac
 F:	Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
 F:	drivers/edac/versal_edac.c
+F:	drivers/edac/xsem_edac.h
+F:	drivers/firmware/xilinx/zynqmp-sem.c
+F:	include/linux/firmware/xlnx-zynqmp-sem.h
 
 XILINX VERSALNET EDAC DRIVER
 M:	Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 4c70729..66090db 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -559,15 +559,20 @@ config EDAC_NPCM
 	  device used to store data is used for ECC storage).
 
 config EDAC_VERSAL
-	tristate "Xilinx Versal DDR Memory Controller"
+	tristate "Xilinx Versal DDR Memory Controller and XilSEM"
 	depends on ARCH_ZYNQMP || COMPILE_TEST
 	help
 	  Support for error detection and correction on the Xilinx Versal DDR
-	  memory controller.
-
-	  Report both single bit errors (CE) and double bit errors (UE).
-	  Support injecting both correctable and uncorrectable errors
-	  for debugging purposes.
+	  memory controller, and for the XilSEM (Soft Error Mitigation)
+	  monitor of configuration memory in the programmable logic (PL)
+	  fabric and NPI interconnect. These are two independent devices
+	  with independent probe paths, both handled by this one driver
+	  module.
+
+	  Report both single bit errors (CE) and double bit errors (UE) for
+	  DDR, and correctable/uncorrectable CRAM and NPI errors for XilSEM.
+	  Support injecting both correctable and uncorrectable errors for
+	  debugging purposes.
 
 config EDAC_LOONGSON
 	tristate "Loongson Memory Controller"
diff --git a/drivers/edac/versal_edac.c b/drivers/edac/versal_edac.c
index 5a43b5d..7a50243 100644
--- a/drivers/edac/versal_edac.c
+++ b/drivers/edac/versal_edac.c
@@ -1,11 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Xilinx Versal memory controller driver
- * Copyright (C) 2023 Advanced Micro Devices, Inc.
+ * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc.
  */
 #include <linux/bitfield.h>
+#include <linux/dma-mapping.h>
 #include <linux/edac.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
@@ -13,9 +15,13 @@
 #include <linux/platform_device.h>
 #include <linux/sizes.h>
 #include <linux/firmware/xlnx-zynqmp.h>
+#include <linux/firmware/xlnx-zynqmp-sem.h>
 #include <linux/firmware/xlnx-event-manager.h>
 
 #include "edac_module.h"
+#include "xsem_edac.h"
+
+#define EDAC_DEVICE	"Xilsem"
 
 /* Granularity of reported error in bytes */
 #define XDDR_EDAC_ERR_GRAIN			1
@@ -1038,14 +1044,17 @@ static void setup_address_map(struct edac_priv *priv)
 }
 #endif /* CONFIG_EDAC_DEBUG */
 
-static const struct of_device_id xlnx_edac_match[] = {
-	{ .compatible = "xlnx,versal-ddrmc", },
-	{
-		/* end of table */
-	}
-};
-
-MODULE_DEVICE_TABLE(of, xlnx_edac_match);
+/*
+ * emif_get_id - Return the DDR memory controller instance ID.
+ * @node:	Device node for the DDR memory controller.
+ *
+ * Scoped to "xlnx,versal-ddrmc" nodes only, so that once this driver
+ * also matches "xlnx,versal-xilsem" (see the combined
+ * xlnx_edac_match[] table below), XilSEM nodes never affect DDR memory
+ * controller instance numbering.
+ *
+ * Return: a unique ID for the given DDR memory controller.
+ */
 static u32 emif_get_id(struct device_node *node)
 {
 	u32 addr, my_addr, my_id = 0;
@@ -1055,7 +1064,7 @@ static u32 emif_get_id(struct device_node *node)
 	addrp = of_get_address(node, 0, NULL, NULL);
 	my_addr = (u32)of_translate_address(node, addrp);
 
-	for_each_matching_node(np, xlnx_edac_match) {
+	for_each_compatible_node(np, NULL, "xlnx,versal-ddrmc") {
 		if (np == node)
 			continue;
 
@@ -1073,7 +1082,7 @@ static u32 emif_get_id(struct device_node *node)
 	return my_id;
 }
 
-static int mc_probe(struct platform_device *pdev)
+static int ddrmc_probe(struct platform_device *pdev)
 {
 	void __iomem *ddrmc_baseaddr, *ddrmc_noc_baseaddr;
 	struct edac_mc_layer layers[2];
@@ -1162,7 +1171,7 @@ static int mc_probe(struct platform_device *pdev)
 	return rc;
 }
 
-static void mc_remove(struct platform_device *pdev)
+static void ddrmc_remove(struct platform_device *pdev)
 {
 	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
 	struct edac_priv *priv = mci->pvt_info;
@@ -1180,7 +1189,1242 @@ static void mc_remove(struct platform_device *pdev)
 	edac_mc_free(mci);
 }
 
-static struct platform_driver xilinx_ddr_edac_mc_driver = {
+/**
+ * xsem_scan_control_show - Shows scan control operation status
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ *
+ * Shows the scan control operations status
+ * Return: Number of bytes copied.
+ */
+static ssize_t xsem_scan_control_show(struct edac_device_ctl_info *dci, char *data)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = scnprintf(data, PAGE_SIZE, "[0x%x][0x%x][0x%x][0x%x][0x%x][0x%x]\n",
+			priv->scan_ctrl_status.hdr, priv->scan_ctrl_status.status,
+			priv->scan_ctrl_status.slrid, priv->scan_ctrl_status.status1,
+			priv->scan_ctrl_status.status2, priv->scan_ctrl_status.status3);
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+/**
+ * xsem_store_prep - Copy a sysfs store() write into a kernel buffer
+ * @data:	Pointer to user data
+ * @count:	Number of bytes written
+ *
+ * Return: Pointer to a NUL-terminated kernel copy of data, to be freed by
+ * the caller with kfree(), or NULL on allocation failure.
+ */
+static char *xsem_store_prep(const char *data, size_t count)
+{
+	char *kern_buff;
+
+	/*
+	 * Allocate one extra byte for the NUL terminator: strscpy() copies at
+	 * most (size-1) bytes from data and always writes the terminator at
+	 * kern_buff[size-1]. A "count"-sized buffer would silently drop the
+	 * last byte of a full, non-newline-terminated "count"-byte write.
+	 */
+	kern_buff = kzalloc(count + 1, GFP_KERNEL);
+	if (!kern_buff)
+		return NULL;
+
+	strscpy(kern_buff, data, count + 1);
+
+	return kern_buff;
+}
+
+/**
+ * xsem_parse_token - Parse the next space-separated token as an unsigned int
+ * @inbuf:	Pointer to the remaining unparsed buffer, advanced past the
+ *		consumed token
+ * @val:	Output value
+ *
+ * Return: 0 on success, -EINVAL if there is no token left or it is not a
+ * valid unsigned integer.
+ */
+static int xsem_parse_token(char **inbuf, u32 *val)
+{
+	char *tok;
+
+	tok = strsep(inbuf, " ");
+	if (!tok)
+		return -EINVAL;
+
+	if (kstrtouint(tok, 0, val))
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * xsem_scan_control_store - Set scan control operation
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ * @count:	read the size bytes from buffer
+ *
+ * User-space interface for doing Xilsem scan operations
+ * (initialization, start, stop)
+ * Return: count argument if request succeeds, else error code
+ */
+static ssize_t xsem_scan_control_store(struct edac_device_ctl_info *dci,
+				       const char *data, size_t count)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	char *kern_buff, *inbuf;
+	u32 cmd;
+	u32 slrid;
+	int ret;
+
+	kern_buff = xsem_store_prep(data, count);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	inbuf = kern_buff;
+
+	/* Read Scan command */
+	ret = xsem_parse_token(&inbuf, &cmd);
+	if (ret)
+		goto err;
+
+	/* Read SLR number */
+	ret = xsem_parse_token(&inbuf, &slrid);
+	if (ret)
+		goto err;
+
+	switch (cmd) {
+	case CRAM_INIT_SCAN:
+	case CRAM_START_SCAN:
+	case CRAM_STOP_SCAN:
+	case NPI_START_SCAN:
+	case NPI_STOP_SCAN:
+	case NPI_ERR_INJECT:
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (slrid > MAX_SLR_ID) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_lock(&priv->lock);
+	ret = zynqmp_pm_xilsem_cntrl_ops(cmd, slrid, &priv->scan_ctrl_status);
+	mutex_unlock(&priv->lock);
+err:
+	kfree(kern_buff);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * xsem_cram_injecterr_show - Shows CRAM error injection status
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ *
+ * Shows CRAM error injection status
+ * Return: Number of bytes copied.
+ */
+static ssize_t xsem_cram_injecterr_show(struct edac_device_ctl_info *dci, char *data)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = scnprintf(data, PAGE_SIZE, "[0x%x][0x%x][0x%x]\n", priv->cram_errinj_status.hdr,
+			priv->cram_errinj_status.status, priv->cram_errinj_status.slrid);
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+/**
+ * xsem_cram_injecterr_store - Start error injection
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ * @count:	read the size bytes from buffer
+ *
+ * User-space interface for doing CRAM error injection
+ * Return: count argument if request succeeds, else error code
+ */
+static ssize_t xsem_cram_injecterr_store(struct edac_device_ctl_info *dci,
+					 const char *data, size_t count)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	char *kern_buff, *inbuf;
+	u32 row, frame, qword, bitloc, slrid;
+	int ret;
+
+	kern_buff = xsem_store_prep(data, count);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	inbuf = kern_buff;
+
+	/* Read Frame number */
+	ret = xsem_parse_token(&inbuf, &frame);
+	if (ret)
+		goto err;
+
+	/* Read Qword number */
+	ret = xsem_parse_token(&inbuf, &qword);
+	if (ret)
+		goto err;
+
+	/* Read Bit location */
+	ret = xsem_parse_token(&inbuf, &bitloc);
+	if (ret)
+		goto err;
+
+	/* Read Row number */
+	ret = xsem_parse_token(&inbuf, &row);
+	if (ret)
+		goto err;
+
+	/* Read slr id */
+	ret = xsem_parse_token(&inbuf, &slrid);
+	if (ret)
+		goto err;
+
+	if (slrid > MAX_SLR_ID || row > MAX_CFRAME_ROW_ID) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_lock(&priv->lock);
+	ret = zynqmp_pm_xilsem_cram_err_inj(slrid, frame, qword, bitloc, row,
+					    &priv->cram_errinj_status);
+	mutex_unlock(&priv->lock);
+err:
+	kfree(kern_buff);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * xsem_cram_framecc_read_show - Shows CRAM Frame ECC
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ *
+ * Shows CRAM Frame ECC value
+ * Return: Number of bytes copied.
+ */
+static ssize_t xsem_cram_framecc_read_show(struct edac_device_ctl_info *dci,
+					   char *data)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	int offset = 0;
+
+	mutex_lock(&priv->lock);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset,
+			    "Read Frame ECC Cmd: [0x%x]\n", priv->cram_frame_ecc.hdr);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset,
+			    "Frame ECC Word_0: [0x%x]\n", priv->cram_frame_ecc.ecc0);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset,
+			    "Frame ECC Word_1: [0x%x]\n", priv->cram_frame_ecc.ecc1);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset,
+			    "Cmd Status: [0x%x]\n", priv->cram_frame_ecc.status);
+	mutex_unlock(&priv->lock);
+
+	return offset;
+}
+
+/**
+ * xsem_cram_framecc_read_store - Read CRAM Frame ECC
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ * @count:	read the size bytes from buffer
+ *
+ * User-space interface for reading CRAM frame ECC
+ * Return: count argument if request succeeds, else error code
+ */
+static ssize_t xsem_cram_framecc_read_store(struct edac_device_ctl_info *dci,
+					    const char *data, size_t count)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	char *kern_buff, *inbuf;
+	u32 frameaddr, row, slrid;
+	int ret;
+
+	kern_buff = xsem_store_prep(data, count);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	inbuf = kern_buff;
+
+	/* Read Frame address */
+	ret = xsem_parse_token(&inbuf, &frameaddr);
+	if (ret)
+		goto err;
+
+	/* Read Row number */
+	ret = xsem_parse_token(&inbuf, &row);
+	if (ret)
+		goto err;
+
+	/* Read slr id */
+	ret = xsem_parse_token(&inbuf, &slrid);
+	if (ret)
+		goto err;
+
+	if (slrid > MAX_SLR_ID || row > MAX_CFRAME_ROW_ID) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_lock(&priv->lock);
+	ret = zynqmp_pm_xilsem_cram_read_ecc(slrid, frameaddr, row, &priv->cram_frame_ecc);
+	mutex_unlock(&priv->lock);
+err:
+	kfree(kern_buff);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * xsem_cram_ssit_getcrc_show - Shows CRAM Row CRC
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ *
+ * Shows CRAM Row CRC value
+ * Return: Number of bytes copied.
+ */
+static ssize_t xsem_cram_ssit_getcrc_show(struct edac_device_ctl_info *dci, char *data)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	int offset = 0;
+
+	mutex_lock(&priv->lock);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "Read CRC Cmd:[0x%x]\n",
+			    priv->cram_get_crc_status.hdr);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "CRC_Word %u:[0x%x]\n", 0U,
+			    priv->cram_get_crc_status.crc0);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "CRC_Word %u:[0x%x]\n", 1U,
+			    priv->cram_get_crc_status.crc1);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "CRC_Word %u:[0x%x]\n", 2U,
+			    priv->cram_get_crc_status.crc2);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "CRC_Word %u:[0x%x]\n", 3U,
+			    priv->cram_get_crc_status.crc3);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "Cmd status: [0x%x]\n",
+			    priv->cram_get_crc_status.status);
+	mutex_unlock(&priv->lock);
+
+	return offset;
+}
+
+/**
+ * xsem_cram_ssit_getcrc_store - Read CRAM row CRC
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ * @count:	read the size bytes from buffer
+ *
+ * User-space interface for reading CRAM row CRC
+ * Return: count argument if request succeeds, else error code
+ */
+static ssize_t xsem_cram_ssit_getcrc_store(struct edac_device_ctl_info *dci, const char *data,
+					   size_t count)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	char *kern_buff, *inbuf;
+	u32 rowindex, slrid;
+	int ret;
+
+	kern_buff = xsem_store_prep(data, count);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	inbuf = kern_buff;
+
+	/* Read Row number */
+	ret = xsem_parse_token(&inbuf, &rowindex);
+	if (ret)
+		goto err;
+
+	/* Read SLR Id */
+	ret = xsem_parse_token(&inbuf, &slrid);
+	if (ret)
+		goto err;
+
+	if (slrid > MAX_SLR_ID || rowindex > MAX_CFRAME_ROW_ID) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_lock(&priv->lock);
+	ret = zynqmp_pm_xilsem_cram_get_crc(slrid, rowindex, &priv->cram_get_crc_status);
+	mutex_unlock(&priv->lock);
+err:
+	kfree(kern_buff);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * xsem_total_cframes_ssit_show - Shows total cframes
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ *
+ * Shows CRAM total cframes
+ * Return: Number of bytes copied.
+ */
+static ssize_t xsem_total_cframes_ssit_show(struct edac_device_ctl_info *dci, char *data)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	const u32 *raw;
+	u32 decoded[CFRAME_MAX_TYPE];
+	u32 id;
+	int offset = 0;
+
+	mutex_lock(&priv->lock);
+
+	/*
+	 * Decode into a local array - cram_total_frames[] holds the raw
+	 * firmware response and must stay untouched, otherwise a second
+	 * read of this file would try to re-decode already-decoded values
+	 * and produce garbage.
+	 */
+	raw = priv->cram_total_frames;
+
+	/*
+	 * raw[3] and raw[6] are intentionally not read below - per the
+	 * CFRAME_REG register interface (LAST_FRAME_BOT/LAST_FRAME_TOP),
+	 * these words are entirely reserved.
+	 */
+	decoded[0]  = (raw[0] & CFRAME_TYPE_0_4_MASK);
+	decoded[1]  = (raw[0] & CFRAME_TYPE_1_5_MASK_L) >> CFRAME_TYPE_1_5_SHIFT_R;
+	decoded[1] |= (raw[1] & CFRAME_TYPE_1_5_MASK_H) << CFRAME_TYPE_1_5_SHIFT_L;
+	decoded[2]  = (raw[1] & CFRAME_TYPE_2_6_MASK) >> CFRAME_TYPE_2_6_SHIFT_R;
+	decoded[3]  = (raw[1] & CFRAME_TYPE_3_MASK_L) >> CFRAME_TYPE_3_SHIFT_R;
+	decoded[3] |= (raw[2] & CFRAME_TYPE_3_MASK_H) << CFRAME_TYPE_3_SHIFT_L;
+	decoded[4]  = (raw[4] & CFRAME_TYPE_0_4_MASK);
+	decoded[5]  = (raw[4] & CFRAME_TYPE_1_5_MASK_L) >> CFRAME_TYPE_1_5_SHIFT_R;
+	decoded[5] |= (raw[5] & CFRAME_TYPE_1_5_MASK_H) << CFRAME_TYPE_1_5_SHIFT_L;
+	decoded[6]  = (raw[5] & CFRAME_TYPE_2_6_MASK) >> CFRAME_TYPE_2_6_SHIFT_R;
+
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "Read Total Frames Cmd : [0x%x]\n",
+					  priv->cram_get_frames_status.hdr);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "SLR ID : [0x%x]\n",
+					  priv->cram_get_frames_status.slrid);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "Row Index: [0x%x]\n",
+					  priv->cram_get_frames_status.row);
+	for (id = 0; id < CFRAME_MAX_TYPE; id++)
+		offset += scnprintf(data + offset, PAGE_SIZE - offset,
+				    "Type[%u] frame count: [%u]\n", id, decoded[id]);
+
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "Cmd Status: [0x%x]\n",
+					  priv->cram_get_frames_status.status);
+	mutex_unlock(&priv->lock);
+
+	return offset;
+}
+
+/**
+ * xsem_total_cframes_ssit_store - Read total cframes in CRAM
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ * @count:	read the size bytes from buffer
+ *
+ * User-space interface for reading CRAM total frames in ssit device
+ * Return: count argument if request succeeds, else error code
+ */
+static ssize_t xsem_total_cframes_ssit_store(struct edac_device_ctl_info *dci, const char *data,
+					     size_t count)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	char *kern_buff, *inbuf;
+	void *kbuf1;
+	dma_addr_t dma_addr = 0;
+	u32 row, slrid;
+	size_t dma_size;
+	int ret;
+
+	kern_buff = xsem_store_prep(data, count);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	inbuf = kern_buff;
+
+	/* Read Row number */
+	ret = xsem_parse_token(&inbuf, &row);
+	if (ret)
+		goto err;
+
+	/* Read slr id */
+	ret = xsem_parse_token(&inbuf, &slrid);
+	if (ret)
+		goto err;
+
+	if (slrid > MAX_SLR_ID || row > MAX_CFRAME_ROW_ID) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	dma_size = sizeof(priv->cram_total_frames);
+	kbuf1 = dma_alloc_coherent(dci->dev, dma_size, &dma_addr, GFP_KERNEL);
+	if (!kbuf1) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	mutex_lock(&priv->lock);
+
+	ret = zynqmp_pm_xilsem_cram_ssit_tot_frames(slrid, row, dma_addr,
+						    &priv->cram_get_frames_status);
+	if (ret) {
+		edac_printk(KERN_ERR, EDAC_DEVICE, "ERROR: XilSEM Status PM API failed\n");
+		mutex_unlock(&priv->lock);
+		dma_free_coherent(dci->dev, dma_size, kbuf1, dma_addr);
+		goto err;
+	}
+
+	memcpy(priv->cram_total_frames, kbuf1, dma_size);
+	mutex_unlock(&priv->lock);
+	dma_free_coherent(dci->dev, dma_size, kbuf1, dma_addr);
+
+err:
+	kfree(kern_buff);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * xsem_read_status_show - Shows CRAM & NPI scan status
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ *
+ * Shows CRAM & NPI scan status
+ * Return: Number of bytes copied.
+ */
+static ssize_t xsem_read_status_show(struct edac_device_ctl_info *dci, char *data)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = scnprintf(data, PAGE_SIZE, "[0x%x][0x%x][0x%x]\n",
+			priv->xilsem_status[0], priv->xilsem_status[1],
+			priv->xilsem_status[2]);
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+/**
+ * xsem_read_status_store - Read CRAM & NPI scan status
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ * @count:	read the size bytes from buffer
+ *
+ * User-space interface for reading Xilsem status
+ * Return: count argument if read succeeds, else error code
+ */
+static ssize_t xsem_read_status_store(struct edac_device_ctl_info *dci,
+				      const char *data, size_t count)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	unsigned long flags;
+	u32 module;
+
+	if (!data)
+		return -EFAULT;
+
+	if (kstrtouint(data, 0, &module))
+		return -EINVAL;
+
+	if (module != CRAM_MOD_ID && module != NPI_MOD_ID) {
+		edac_printk(KERN_ERR, EDAC_DEVICE, "Invalid module %d\n", module);
+		return -EINVAL;
+	}
+
+	mutex_lock(&priv->lock);
+	/*
+	 * CRAM_STS_INFO_OFFSET/CRAM_CE_COUNT_OFFSET (CRAM_MOD_ID) and
+	 * priv->baseaddr's base register (NPI_MOD_ID) are also read from
+	 * hardirq context by xsem_geterror_info() via xsem_err_callback().
+	 * priv->lock is a mutex and provides no protection against that IRQ
+	 * preempting this critical section on the same CPU, so take the
+	 * same irq-safe cnt_lock xsem_err_callback() holds around its MMIO
+	 * reads to keep this snapshot consistent.
+	 */
+	raw_spin_lock_irqsave(&priv->cnt_lock, flags);
+	if (module == CRAM_MOD_ID) {
+		priv->xilsem_status[0] = readl(priv->baseaddr + CRAM_STS_INFO_OFFSET);
+		priv->xilsem_status[1] = readl(priv->baseaddr + CRAM_CE_COUNT_OFFSET);
+		priv->xilsem_status[2] = 0;
+	} else {
+		priv->xilsem_status[0] = readl(priv->baseaddr);
+		priv->xilsem_status[1] = readl(priv->baseaddr + NPI_SCAN_COUNT);
+		priv->xilsem_status[2] = readl(priv->baseaddr + NPI_SCAN_HB_COUNT);
+	}
+	raw_spin_unlock_irqrestore(&priv->cnt_lock, flags);
+	mutex_unlock(&priv->lock);
+
+	return count;
+}
+
+/**
+ * xsem_read_ssit_status_show - Shows CRAM & NPI scan for SSIT device
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ *
+ * Shows CRAM & NPI scan status for given SLR
+ * Return: Number of bytes copied.
+ */
+static ssize_t xsem_read_ssit_status_show(struct edac_device_ctl_info *dci, char *data)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	int offset = 0;
+	int id;
+
+	if (!priv->slr_info)
+		return -EINVAL;
+
+	mutex_lock(&priv->lock);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "Read SLR Status Cmd:[0x%x]\n",
+			  priv->xilsem_ssit_status.hdr);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "SLR ID:[0x%x]\n",
+			    priv->xilsem_ssit_status.slrid);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "NPI status:[0x%x]\n",
+			    priv->slr_info->npi_status);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "NPI scan count:[0x%x]\n",
+			    priv->slr_info->scancnt);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "NPI Heartbeat count:[0x%x]\n",
+			    priv->slr_info->hbcnt);
+	for (id = 0; id < MAX_NPI_SLV_SKIP_CNT; id++)
+		offset += scnprintf(data + offset, PAGE_SIZE - offset,
+				    "NPI scan skip count %x :[0x%x]\n", id,
+				    priv->slr_info->slvskpcnt[id]);
+
+	for (id = 0; id < MAX_NPI_ERR_INFO_CNT; id++)
+		offset += scnprintf(data + offset, PAGE_SIZE - offset,
+				    "NPI error info %x :[0x%x]\n", id,
+				    priv->slr_info->err_info[id]);
+
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "CRAM status:[0x%x]\n",
+			    priv->slr_info->cram_status);
+
+	for (id = 0U; id < 7; id++) {
+		offset += scnprintf(data + offset, PAGE_SIZE - offset,
+				    "Error Location High %x: [0x%x]\n", id,
+				    priv->slr_info->err_addr[(id * 2) + 1]);
+		offset += scnprintf(data + offset, PAGE_SIZE - offset,
+				    "Error Location Low %x: [0x%x]\n", id,
+				    priv->slr_info->err_addr[id * 2]);
+	}
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "CRAM scan CE count:[0x%x]\n",
+			    priv->slr_info->errcnt);
+	mutex_unlock(&priv->lock);
+
+	return offset;
+}
+
+/**
+ * xsem_read_ssit_status_store - Read CRAM & NPI scan SSIT status
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ * @count:	read the size bytes from buffer
+ *
+ * User-space interface for reading Xilsem status for SSIT device
+ * Return: count argument if read succeeds, else error code
+ */
+static ssize_t xsem_read_ssit_status_store(struct edac_device_ctl_info *dci, const char *data,
+					   size_t count)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	char *kern_buff, *inbuf;
+	void *kbuf1;
+	size_t dma_size;
+	dma_addr_t dma_addr = 0;
+	u32 slrid;
+	int ret;
+
+	if (!priv->slr_info)
+		return -EINVAL;
+
+	kern_buff = xsem_store_prep(data, count);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	inbuf = kern_buff;
+
+	/* Read SLR ID */
+	ret = xsem_parse_token(&inbuf, &slrid);
+	if (ret)
+		goto err;
+
+	if (slrid > MAX_SLR_ID) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	dma_size  = sizeof(struct xsem_ssit_status);
+
+	kbuf1 = dma_alloc_coherent(dci->dev, dma_size, &dma_addr, GFP_KERNEL);
+	if (!kbuf1) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	mutex_lock(&priv->lock);
+
+	ret = zynqmp_pm_xilsem_read_ssit_status(slrid, dma_addr, &priv->xilsem_ssit_status);
+	if (ret) {
+		edac_printk(KERN_ERR, EDAC_DEVICE, "ERROR: XilSEM Status PM API failed\n");
+		mutex_unlock(&priv->lock);
+		dma_free_coherent(dci->dev, dma_size, kbuf1, dma_addr);
+		goto err;
+	}
+
+	memcpy(priv->slr_info, kbuf1, dma_size);
+	mutex_unlock(&priv->lock);
+
+	dma_free_coherent(dci->dev, dma_size, kbuf1, dma_addr);
+err:
+	kfree(kern_buff);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * xsem_read_config_show - Shows CRAM & NPI configuration
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ *
+ * Shows CRAM & NPI configuration
+ * Return: Number of bytes copied.
+ */
+static ssize_t xsem_read_config_show(struct edac_device_ctl_info *dci, char *data)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	int offset = 0;
+
+	mutex_lock(&priv->lock);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "Read Config Cmd: [0x%x]\n",
+			    priv->xilsem_cfg.hdr);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "CRAM Scan Config: [0x%x]\n",
+			    priv->xilsem_cfg.cram_cfg);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "NPI Scan Config: [0x%x]\n",
+			    priv->xilsem_cfg.npi_cfg);
+	offset += scnprintf(data + offset, PAGE_SIZE - offset, "Cmd Status: [0x%x]\n",
+			    priv->xilsem_cfg.status);
+	mutex_unlock(&priv->lock);
+
+	return offset;
+}
+
+/**
+ * xsem_read_config_store - Read CRAM & NPI configuration
+ * @dci:	Pointer to the edac device struct
+ * @data:	Pointer to user data
+ * @count:	read the size bytes from buffer
+ *
+ * User-space interface for reading Xilsem configuration
+ * Return: count argument if request succeeds, else error code
+ */
+static ssize_t xsem_read_config_store(struct edac_device_ctl_info *dci,
+				      const char *data, size_t count)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	char *kern_buff, *inbuf;
+	u32 slrid;
+	int ret;
+
+	kern_buff = xsem_store_prep(data, count);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	inbuf = kern_buff;
+
+	/* Read SLR id */
+	ret = xsem_parse_token(&inbuf, &slrid);
+	if (ret)
+		goto err;
+
+	if (slrid > MAX_SLR_ID) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_lock(&priv->lock);
+	ret = zynqmp_pm_xilsem_read_cfg(slrid, &priv->xilsem_cfg);
+	mutex_unlock(&priv->lock);
+
+err:
+	kfree(kern_buff);
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * xsem_handle_error - Handle XilSem error types CE and UE
+ * @dci:	Pointer to the edac device controller instance
+ * @p:		Pointer to the xilsem error status structure
+ *
+ * Handles the correctable and uncorrectable error.
+ */
+static void xsem_handle_error(struct edac_device_ctl_info *dci, struct xsem_error_status *p)
+{
+	struct xsem_ecc_error_info *pinf;
+	char message[VERSAL_XILSEM_EDAC_MSG_SIZE];
+
+	if (p->ce_cnt) {
+		pinf = &p->ceinfo;
+		snprintf(message, VERSAL_XILSEM_EDAC_MSG_SIZE,
+			 "\nXILSEM CRAM error type :%s\n"
+			 "\nFrame_Addr: [0x%X]\t Row_num: [0x%X]\t Bit_loc: [0x%X]\t Qword: [0x%X]\n",
+			 "CE", pinf->frame_addr, pinf->row_id,
+			 pinf->bit_loc, pinf->qword);
+		edac_device_handle_ce(dci, 0, 0, message);
+	}
+
+	/*
+	 * CRAM and NPI UEs are tracked in separate info structs (not one
+	 * shared field) and reported independently, so a callback that sees
+	 * both at once still reports both instead of one silently
+	 * overwriting the other.
+	 */
+	if (p->cram_ue) {
+		pinf = &p->cram_ueinfo;
+		snprintf(message, VERSAL_XILSEM_EDAC_MSG_SIZE,
+			 "\nXILSEM error type :%s\n"
+			 "status: [0x%X]\nError_Info0: [0x%X]\n"
+			 "Error_Info1: [0x%X]",
+			 "UE (CRAM)", pinf->status, pinf->data0, pinf->data1);
+		edac_device_handle_ue(dci, 0, 0, message);
+	}
+
+	if (p->npi_ue) {
+		pinf = &p->npi_ueinfo;
+		snprintf(message, VERSAL_XILSEM_EDAC_MSG_SIZE,
+			 "\nXILSEM error type :%s\n"
+			 "status: [0x%X]\nError_Info0: [0x%X]\n"
+			 "Error_Info1: [0x%X]",
+			 "UE (NPI)", pinf->status, pinf->data0, pinf->data1);
+		edac_device_handle_ue(dci, 0, 0, message);
+	}
+}
+
+/**
+ * xsem_geterror_info - Get the current ecc error info
+ * @dci:	Pointer to the edac device controller instance
+ * @p:		Pointer to the Xilsem error status structure
+ * @mask:	mask indicates the error type
+ *
+ * Determines there is any ecc error or not
+ */
+static void xsem_geterror_info(struct edac_device_ctl_info *dci, struct xsem_error_status *p,
+			       u32 mask)
+{
+	struct xsem_edac_priv *priv = dci->pvt_info;
+	u32 error_word_0, error_word_1, ce_count;
+	u8 index;
+
+	if (mask & priv->cram_ce_mask) {
+		p->ce_cnt++;
+
+		/* Read CRAM total correctable error count */
+		ce_count = readl(priv->baseaddr + CRAM_CE_COUNT_OFFSET);
+		/* Calculate index for error log */
+		index = (ce_count % XILSEM_MAX_CE_LOG_CNT);
+		/*
+		 * Check if addr index is not 0
+		 * if yes, then decrement index, else set index as last entry
+		 */
+		if (index != 0U) {
+			/* Decrement Index */
+			--index;
+		} else {
+			/* Set log index to 6 (Max-1) */
+			index = (XILSEM_MAX_CE_LOG_CNT - 1);
+		}
+		error_word_0 = readl(priv->baseaddr + CRAM_CE_ADDRL0_OFFSET + (index * 8U));
+		error_word_1 = readl(priv->baseaddr + CRAM_CE_ADDRH0_OFFSET + (index * 8U));
+
+		/* Frame is at 22:0 bits of SEM_CRAMERR_ADDRH0 reg */
+		p->ceinfo.frame_addr = FIELD_GET(CRAM_ERR_FRAME_MASK, error_word_1);
+
+		/* row is at 26:23 bits of SEM_CRAMERR_ADDRH0 reg */
+		p->ceinfo.row_id = FIELD_GET(CRAM_ERR_ROW_MASK, error_word_1);
+
+		/* bit is at 22:16 bits of SEM_CRAMERR_ADDRL0 reg */
+		p->ceinfo.bit_loc = FIELD_GET(CRAM_ERR_BIT_MASK, error_word_0);
+
+		/* Qword is at 27:23 bits of SEM_CRAMERR_ADDRL0 reg */
+		p->ceinfo.qword = FIELD_GET(CRAM_ERR_QWRD_MASK, error_word_0);
+
+		/* Read CRAM status */
+		p->ceinfo.status = readl(priv->baseaddr + CRAM_STS_INFO_OFFSET);
+	}
+
+	/*
+	 * CE and UE bits are OR'd together into a single registered event
+	 * mask, so a single callback invocation can have both a CE bit and a
+	 * UE bit set at once - including both the CRAM UE and NPI UE bits
+	 * together. These are separate conditions, so a simultaneous CRAM UE
+	 * + NPI UE event still counts and reads both; each source writes its
+	 * own cram_ueinfo/npi_ueinfo rather than a shared field, so
+	 * xsem_handle_error() can report both separately.
+	 */
+	if (mask & priv->cram_ue_mask) {
+		p->ue_cnt++;
+		p->cram_ue = true;
+		p->cram_ueinfo.data0 = 0;
+		p->cram_ueinfo.data1 = 0;
+		p->cram_ueinfo.status = readl(priv->baseaddr + CRAM_STS_INFO_OFFSET);
+	}
+	if (mask & priv->npi_ue_mask) {
+		p->ue_cnt++;
+		p->npi_ue = true;
+		p->npi_ueinfo.data0 = readl(priv->baseaddr + NPI_ERR0_INFO_OFFSET);
+		p->npi_ueinfo.data1 = readl(priv->baseaddr + NPI_ERR1_INFO_OFFSET);
+		p->npi_ueinfo.status = readl(priv->baseaddr);
+	}
+
+	if (!(mask & (priv->cram_ce_mask | priv->cram_ue_mask | priv->npi_ue_mask)))
+		edac_printk(KERN_ERR, EDAC_DEVICE, "Invalid Event received %x\n", mask);
+}
+
+/**
+ * xsem_err_callback - Handle Correctable and Uncorrectable errors.
+ * @payload:	payload data.
+ * @data:	controller data.
+ *
+ * Handles ECC correctable and uncorrectable errors.
+ */
+static void xsem_err_callback(const u32 *payload, void *data)
+{
+	struct edac_device_ctl_info *dci = (struct edac_device_ctl_info *)data;
+	struct xsem_error_status stat;
+	struct xsem_edac_priv *priv;
+	unsigned long flags;
+	u32 event;
+
+	priv = dci->pvt_info;
+	memset(&stat, 0, sizeof(stat));
+	/* Read payload to get the event type */
+	event = payload[2];
+	edac_printk(KERN_INFO, EDAC_DEVICE, "Event received %x\n", event);
+
+	/*
+	 * Concurrent events (e.g. from different SLRs) can invoke this
+	 * callback on more than one CPU at once, and xsem_geterror_info()
+	 * reads MMIO registers (CRAM_CE_COUNT_OFFSET, CRAM_STS_INFO_OFFSET,
+	 * NPI_ERR0/1_INFO_OFFSET) that xsem_read_status_store() also reads
+	 * in process context under priv->lock alone - a mutex provides no
+	 * protection against this hardirq-context path preempting it on the
+	 * same CPU. Hold cnt_lock across the read, the += RMW below, and
+	 * xsem_handle_error() - the latter calls edac_device_handle_ce/ue(),
+	 * which bump plain (non-atomic) u32 counters in EDAC core state
+	 * shared across every instance of this callback; serializing here is
+	 * the only way to keep those updates from racing when this callback
+	 * runs concurrently on more than one CPU.
+	 */
+	raw_spin_lock_irqsave(&priv->cnt_lock, flags);
+	xsem_geterror_info(dci, &stat, event);
+	priv->ce_cnt += stat.ce_cnt;
+	priv->ue_cnt += stat.ue_cnt;
+	xsem_handle_error(dci, &stat);
+	raw_spin_unlock_irqrestore(&priv->cnt_lock, flags);
+}
+
+static const struct edac_dev_sysfs_attribute xsem_edac_sysfs_attributes[] = {
+	{
+		.attr = {
+			.name = "xsem_scan_control_ops",
+			.mode = (0644)
+		},
+		.show = xsem_scan_control_show,
+		.store = xsem_scan_control_store},
+	{
+		.attr = {
+			.name = "xsem_cram_injecterr",
+			.mode = (0644)
+		},
+		.show = xsem_cram_injecterr_show,
+		.store = xsem_cram_injecterr_store},
+	{
+		.attr = {
+			.name = "xsem_cram_framecc_read",
+			.mode = (0644)
+		},
+		.show = xsem_cram_framecc_read_show,
+		.store = xsem_cram_framecc_read_store},
+	{
+		.attr = {
+			.name = "xsem_cram_ssit_getcrc",
+			.mode = (0644)
+		},
+		.show = xsem_cram_ssit_getcrc_show,
+		.store = xsem_cram_ssit_getcrc_store},
+	{
+		.attr = {
+			.name = "xsem_read_status",
+			.mode = (0644)
+		},
+		.show = xsem_read_status_show,
+		.store = xsem_read_status_store},
+	{
+		.attr = {
+			.name = "xsem_read_ssit_status",
+			.mode = (0644)
+		},
+		.show = xsem_read_ssit_status_show,
+		.store = xsem_read_ssit_status_store},
+	{
+		.attr = {
+			.name = "xsem_total_cframes_ssit",
+			.mode = (0644)
+		},
+		.show = xsem_total_cframes_ssit_show,
+		.store = xsem_total_cframes_ssit_store},
+	{
+		.attr = {
+			.name = "xsem_read_config",
+			.mode = (0644)
+		},
+		.show = xsem_read_config_show,
+		.store = xsem_read_config_store},
+	{
+		.attr = {.name = NULL}
+	}
+};
+
+/**
+ * xsem_edac_probe - Check controller and bind driver.
+ * @pdev:	platform device.
+ *
+ * Probe a specific controller instance for binding with the driver.
+ *
+ * Return: 0 if the controller instance was successfully bound to the
+ * driver; otherwise, < 0 on error.
+ */
+static int xsem_edac_probe(struct platform_device *pdev)
+{
+	struct xsem_edac_priv *priv;
+	void __iomem *plmrtca_baseaddr;
+	struct edac_device_ctl_info *dci;
+	u32 family_code;
+	int rc;
+
+	plmrtca_baseaddr = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(plmrtca_baseaddr))
+		return PTR_ERR(plmrtca_baseaddr);
+
+	dci = edac_device_alloc_ctl_info(sizeof(*priv), VERSAL_XILSEM_EDAC_STRNG,
+					 1, VERSAL_XILSEM_EDAC_STRNG, 1, 0,
+					 edac_device_alloc_index());
+	if (!dci) {
+		edac_printk(KERN_ERR, EDAC_DEVICE, "Unable to allocate EDAC device\n");
+		return -ENOMEM;
+	}
+
+	priv = dci->pvt_info;
+	platform_set_drvdata(pdev, dci);
+	dci->dev = &pdev->dev;
+	priv->baseaddr = plmrtca_baseaddr;
+	dci->mod_name = "xlnx_xilsem_edac";
+	dci->ctl_name = VERSAL_XILSEM_EDAC_STRNG;
+	dci->dev_name = dev_name(&pdev->dev);
+
+	mutex_init(&priv->lock);
+	raw_spin_lock_init(&priv->cnt_lock);
+
+	rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	if (rc < 0) {
+		edac_printk(KERN_ERR, EDAC_DEVICE, "no usable DMA configuration\n");
+		goto free_dev_ctl;
+	}
+
+	priv->slr_info = devm_kzalloc(&pdev->dev, sizeof(struct xsem_ssit_status), GFP_KERNEL);
+	if (!priv->slr_info) {
+		rc = -ENOMEM;
+		goto free_dev_ctl;
+	}
+
+	rc = zynqmp_pm_get_family_info(&family_code);
+	if (rc) {
+		/*
+		 * -ENODEV means the firmware driver hasn't registered yet on
+		 * a real ARCH_ZYNQMP build - defer and retry. On a
+		 * COMPILE_TEST build without CONFIG_ZYNQMP_FIRMWARE reachable
+		 * the stub always returns -ENODEV, so deferring would retry
+		 * forever for no reason; let it fail outright there instead.
+		 */
+		if (rc == -ENODEV && IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE))
+			rc = -EPROBE_DEFER;
+		goto free_dev_ctl;
+	}
+
+	if (family_code == PM_VERSAL_NET_FAMILY_CODE) {
+		priv->sw_event_node_id = VERSAL_NET_EVENT_ERROR_SW_ERR;
+		priv->cram_ce_mask = XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_CRAM_CE;
+		priv->cram_ue_mask = XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_CRAM_UE;
+		priv->npi_ue_mask = XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_NPI_UE;
+	} else if (family_code == PM_VERSAL_FAMILY_CODE) {
+		priv->sw_event_node_id = VERSAL_EVENT_ERROR_SW_ERR;
+		priv->cram_ce_mask = XPM_VERSAL_EVENT_ERROR_MASK_XSEM_CRAM_CE_5;
+		priv->cram_ue_mask = XPM_VERSAL_EVENT_ERROR_MASK_XSEM_CRAM_UE_6;
+		priv->npi_ue_mask = XPM_VERSAL_EVENT_ERROR_MASK_XSEM_NPI_UE_7;
+	} else {
+		/*
+		 * Bail out here instead of continuing to register an event
+		 * with a node id/mask of 0 (whatever devm_kzalloc() happened
+		 * to leave in priv) for a family we don't recognize - unwind
+		 * instead.
+		 */
+		edac_printk(KERN_ERR, EDAC_DEVICE, "Invalid Device family code %d\n",
+			    family_code);
+		rc = -ENODEV;
+		goto free_dev_ctl;
+	}
+	rc = xlnx_register_event(PM_NOTIFY_CB, priv->sw_event_node_id,
+				 priv->cram_ce_mask | priv->cram_ue_mask | priv->npi_ue_mask,
+				 false, xsem_err_callback, dci);
+	if (rc) {
+		if (rc == -EACCES)
+			rc = -EPROBE_DEFER;
+		goto free_dev_ctl;
+	}
+
+	/*
+	 * Only publish the sysfs attributes once priv->slr_info is allocated,
+	 * priv->{sw_event_node_id,cram_ce_mask,cram_ue_mask,npi_ue_mask} are
+	 * resolved, and the error-event callback is registered - otherwise a
+	 * write to xsem_scan_control_ops (or any other attribute) from
+	 * userspace during probe could start hardware scanning, or observe
+	 * priv->slr_info as NULL, before this state exists.
+	 */
+	dci->sysfs_attributes = xsem_edac_sysfs_attributes;
+	rc = edac_device_add_device(dci);
+	if (rc) {
+		/* edac_device_add_device() returns a bare 1 on failure, not a negative errno */
+		rc = -ENODEV;
+		goto free_event;
+	}
+
+	edac_printk(KERN_DEBUG, EDAC_DEVICE, "%s success\n", __func__);
+	return rc;
+
+free_event:
+	xlnx_unregister_event(PM_NOTIFY_CB, priv->sw_event_node_id,
+			      priv->cram_ce_mask | priv->cram_ue_mask | priv->npi_ue_mask,
+			      xsem_err_callback, dci);
+free_dev_ctl:
+	edac_device_free_ctl_info(dci);
+
+	return rc;
+}
+
+/**
+ * xsem_edac_remove - Unbind driver from controller.
+ * @pdev:	Platform device.
+ *
+ * Return: Unconditionally 0
+ */
+static void xsem_edac_remove(struct platform_device *pdev)
+{
+	struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
+	struct xsem_edac_priv *priv = dci->pvt_info;
+
+	xlnx_unregister_event(PM_NOTIFY_CB, priv->sw_event_node_id,
+			      priv->cram_ce_mask | priv->cram_ue_mask | priv->npi_ue_mask,
+			      xsem_err_callback, dci);
+	edac_device_del_device(&pdev->dev);
+	edac_device_free_ctl_info(dci);
+}
+
+/**
+ * struct xlnx_edac_config - Per-compatible-string dispatch entry.
+ * @probe:	Probe function for this compatible string's device class.
+ * @remove:	Remove function for this compatible string's device class.
+ */
+struct xlnx_edac_config {
+	int (*probe)(struct platform_device *pdev);
+	void (*remove)(struct platform_device *pdev);
+};
+
+static const struct xlnx_edac_config ddrmc_edac_config = {
+	.probe = ddrmc_probe,
+	.remove = ddrmc_remove,
+};
+
+static const struct xlnx_edac_config xsem_edac_config = {
+	.probe = xsem_edac_probe,
+	.remove = xsem_edac_remove,
+};
+
+static const struct of_device_id xlnx_edac_match[] = {
+	{
+		.compatible = "xlnx,versal-ddrmc",
+		.data = &ddrmc_edac_config,
+	},
+	{
+		.compatible = "xlnx,versal-xilsem",
+		.data = &xsem_edac_config,
+	},
+	{
+		/* end of table */
+	}
+};
+
+MODULE_DEVICE_TABLE(of, xlnx_edac_match);
+
+static int mc_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	const struct xlnx_edac_config *config;
+
+	match = of_match_node(xlnx_edac_match, pdev->dev.of_node);
+	if (!match)
+		return -ENODEV;
+
+	config = match->data;
+
+	return config->probe(pdev);
+}
+
+static void mc_remove(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	const struct xlnx_edac_config *config;
+
+	match = of_match_node(xlnx_edac_match, pdev->dev.of_node);
+	if (!match)
+		return;
+
+	config = match->data;
+	config->remove(pdev);
+}
+
+static struct platform_driver xilinx_edac_driver = {
 	.driver = {
 		.name = "xilinx-ddrmc-edac",
 		.of_match_table = xlnx_edac_match,
@@ -1189,8 +2433,8 @@ static void mc_remove(struct platform_device *pdev)
 	.remove = mc_remove,
 };
 
-module_platform_driver(xilinx_ddr_edac_mc_driver);
+module_platform_driver(xilinx_edac_driver);
 
-MODULE_AUTHOR("AMD Inc");
-MODULE_DESCRIPTION("Xilinx DDRMC ECC driver");
+MODULE_AUTHOR("Advanced Micro Devices, Inc.");
+MODULE_DESCRIPTION("Xilinx Versal DDRMC and XilSEM EDAC driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/edac/xsem_edac.h b/drivers/edac/xsem_edac.h
new file mode 100644
index 0000000..2b36e67
--- /dev/null
+++ b/drivers/edac/xsem_edac.h
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Xilinx Versal XilSEM (Soft Error Mitigation) EDAC definitions
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ *
+ * Constants and data structures for the "xlnx,versal-xilsem" compatible
+ * string in versal_edac.c (xsem_edac_probe(), xsem_edac_remove(), and the
+ * xsem_* sysfs attributes).
+ *
+ */
+#ifndef _XSEM_EDAC_V2_H
+#define _XSEM_EDAC_V2_H
+
+#include <linux/bitfield.h>
+#include <linux/firmware/xlnx-zynqmp-sem.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#define VERSAL_XILSEM_EDAC_MSG_SIZE	256
+#define VERSAL_XILSEM_EDAC_STRNG	"versal_xilsem"
+
+/* XilSem CE Error log count */
+#define XILSEM_MAX_CE_LOG_CNT	7
+
+/** Maximum CRAM error register count */
+#define MAX_CRAMERR_REG_CNT		14
+/** Maximum NPI slave skip count */
+#define MAX_NPI_SLV_SKIP_CNT	8
+/** Maximum NPI Error info count */
+#define MAX_NPI_ERR_INFO_CNT	2
+
+/* Maximum SLR count */
+#define MAX_SLR_ID	3
+
+/* Maximum CFRAME row number */
+#define MAX_CFRAME_ROW_ID	4
+
+/** Maximum number of cframe types  */
+#define CFRAME_MAX_TYPE	7
+
+/** Mask for getting Type_0, Type_4 frames */
+#define CFRAME_TYPE_0_4_MASK	GENMASK(19, 0)
+
+/** Low mask, High mask for getting Type_1, Type_5 frames */
+#define CFRAME_TYPE_1_5_MASK_L	GENMASK(31, 20)
+#define CFRAME_TYPE_1_5_MASK_H	GENMASK(7, 0)
+
+/** Shift for getting Type_1, Type_5 frames */
+#define CFRAME_TYPE_1_5_SHIFT_R		20
+#define CFRAME_TYPE_1_5_SHIFT_L		12
+
+/** Mask for getting Type_2, Type_6 frames */
+#define CFRAME_TYPE_2_6_MASK	GENMASK(27, 8)
+
+/** Shift for getting Type_2, Type_6 frames */
+#define CFRAME_TYPE_2_6_SHIFT_R		0x8
+
+/** Low mask, high mask for getting Type_3 frames */
+#define CFRAME_TYPE_3_MASK_L		GENMASK(31, 28)
+#define CFRAME_TYPE_3_MASK_H		GENMASK(15, 0)
+
+/** Shift for getting Type_3 */
+#define CFRAME_TYPE_3_SHIFT_R	28
+#define CFRAME_TYPE_3_SHIFT_L	0x4
+
+/* XilSem_CRAM scan error info registers */
+#define CRAM_STS_INFO_OFFSET	0x34
+#define CRAM_CE_ADDRL0_OFFSET	0x38
+#define CRAM_CE_ADDRH0_OFFSET	0x3C
+#define CRAM_CE_COUNT_OFFSET	0x70
+
+/* XilSem_NPI_Scan uncorrectable error info registers */
+#define NPI_SCAN_COUNT			0x24
+#define NPI_SCAN_HB_COUNT		0x28
+#define NPI_ERR0_INFO_OFFSET	0x2C
+#define NPI_ERR1_INFO_OFFSET	0x30
+
+/* XilSem bit masks for extracting error details */
+#define CRAM_ERR_ROW_MASK	GENMASK(26, 23)
+#define CRAM_ERR_BIT_MASK	GENMASK(22, 16)
+#define CRAM_ERR_QWRD_MASK	GENMASK(27, 23)
+#define CRAM_ERR_FRAME_MASK	GENMASK(22, 0)
+
+enum xsem_cmd_id {
+	CRAM_INIT_SCAN = 1, /* To initialize CRAM scan */
+	CRAM_START_SCAN = 2, /* To start CRAM scan */
+	CRAM_STOP_SCAN = 3, /* To stop CRAM scan */
+	CRAM_ERR_INJECT = 4, /* To inject CRAM error */
+	NPI_START_SCAN = 5, /* To start NPI scan */
+	NPI_STOP_SCAN = 6, /* To stop NPI scan */
+	NPI_ERR_INJECT = 7, /* To inject NPI error */
+};
+
+/* XilSem Module IDs */
+#define CRAM_MOD_ID			0x1
+#define NPI_MOD_ID			0x2
+
+/**
+ * struct xsem_ecc_error_info - ECC error log information
+ * @status:	CRAM/NPI scan error status
+ * @data0:	Checksum of the error descriptor
+ * @data1:	Index of the error descriptor
+ * @frame_addr:	Frame location at which error occurred
+ * @block_type:	Block type
+ * @row_id:	Row number
+ * @bit_loc:	Bit position in the Qword
+ * @qword:	Qword location in the frame
+ */
+struct xsem_ecc_error_info {
+	u32 status;
+	u32 data0;
+	u32 data1;
+	u32 frame_addr;
+	u8 block_type;
+	u8 row_id;
+	u8 bit_loc;
+	u8 qword;
+};
+
+/**
+ * struct xsem_error_status - ECC status information to report
+ * @ce_cnt:	Correctable error count
+ * @ue_cnt:	Uncorrectable error count
+ * @cram_ue:	True if a CRAM UE was seen in this callback
+ * @npi_ue:	True if an NPI UE was seen in this callback
+ * @ceinfo:	Correctable error log information
+ * @cram_ueinfo: CRAM uncorrectable error log information
+ * @npi_ueinfo:	NPI uncorrectable error log information
+ */
+struct xsem_error_status {
+	u32 ce_cnt;
+	u32 ue_cnt;
+	bool cram_ue;
+	bool npi_ue;
+	struct xsem_ecc_error_info ceinfo;
+	struct xsem_ecc_error_info cram_ueinfo;
+	struct xsem_ecc_error_info npi_ueinfo;
+};
+
+/**
+ * struct xsem_ssit_status - SSIT status information
+ * @npi_status: NPI Status
+ * @slvskpcnt: NPI Slave skip count
+ * @scancnt: NPI Scan count
+ * @hbcnt: NPI Heartbeat count
+ * @err_info: NPI Error Information
+ * @cram_status: Cram Status
+ * @err_addr: Address of corrected error location
+ * @errcnt: Corrected Error count.
+ */
+struct xsem_ssit_status {
+	u32 npi_status;
+	u32 slvskpcnt[MAX_NPI_SLV_SKIP_CNT];
+	u32 scancnt;
+	u32 hbcnt;
+	u32 err_info[MAX_NPI_ERR_INFO_CNT];
+	u32 cram_status;
+	u32 err_addr[MAX_CRAMERR_REG_CNT];
+	u32 errcnt;
+};
+
+/**
+ * struct xsem_edac_priv - Xilsem private instance data
+ * @baseaddr:	Base address of the XilSem PLM RTCA module
+ * @cram_get_frames_status:	Buffer for get total frames command
+ * @cram_get_crc_status:	Buffer for CRC read command
+ * @xilsem_ssit_status:	Buffer for SLR status command
+ * @cram_errinj_status:	Buffer for CRAM error injection
+ * @cram_total_frames:	Buffer for total cframes data
+ * @scan_ctrl_status:	Buffer for scan ctrl commands
+ * @cram_frame_ecc:	Buffer for CRAM frame ECC
+ * @xilsem_status:	Buffer for CRAM & NPI status
+ * @xilsem_cfg:	Buffer for CRAM & NPI configuration
+ * @sw_event_node_id:	Error event node Id
+ * @cram_ce_mask: Event bit mask for CRAM correctable error
+ * @cram_ue_mask: Event bit mask for CRAM uncorrectable error
+ * @npi_ue_mask: Event bit mask for NPI uncorrectable error
+ * @ce_cnt:	Correctable Error count
+ * @ue_cnt:	Uncorrectable Error count
+ * @slr_info:	Pointer to get SSIT status information
+ * @lock:	Serializes sysfs show/store access to the buffers above,
+ *		which are shared between all xsem_* sysfs callbacks.
+ * @cnt_lock:	irq-safe lock protecting ce_cnt/ue_cnt and the CRAM/NPI status
+ *		MMIO registers, which are read from both xsem_err_callback()
+ *		(hardirq context, via xsem_geterror_info()) and process
+ *		context (xsem_read_status_store()); priv->lock is a mutex
+ *		and cannot provide that protection. Also held across
+ *		xsem_handle_error() in xsem_err_callback(), since that calls
+ *		edac_device_handle_ce()/ue(), which update plain u32 counters
+ *		in EDAC core state with no locking of their own - without
+ *		this, two concurrent invocations of this callback (e.g. from
+ *		different SLRs, on different CPUs) could race on those
+ *		counters.
+ */
+struct xsem_edac_priv {
+	void __iomem *baseaddr;
+	struct xsem_cram_ssit_totframes_response cram_get_frames_status;
+	struct xsem_cram_getcrc_response cram_get_crc_status;
+	struct xsem_read_ssit_status_response xilsem_ssit_status;
+	struct xsem_cram_errinj_response cram_errinj_status;
+	u32 cram_total_frames[7];
+	struct xsem_cntrl_ops_response scan_ctrl_status;
+	struct xsem_cram_readecc_response cram_frame_ecc;
+	u32 xilsem_status[4];
+	struct xsem_read_cfg_response xilsem_cfg;
+	u32 sw_event_node_id;
+	u32 cram_ce_mask;
+	u32 cram_ue_mask;
+	u32 npi_ue_mask;
+	u32 ce_cnt;
+	u32 ue_cnt;
+	struct xsem_ssit_status *slr_info;
+	struct mutex lock;	/* protects the status fields above, read from sysfs show() */
+	raw_spinlock_t cnt_lock;	/* protects ce_cnt/ue_cnt and CRAM/NPI status regs */
+};
+
+#endif /* _XSEM_EDAC_V2_H */
-- 
1.9.5


      parent reply	other threads:[~2026-10-21 12:57 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21 12:57 [PATCH v4 0/3] EDAC/versal: Add XilSEM CRAM/NPI error monitoring support Rama devi Veggalam
2026-10-21 12:57 ` [PATCH v4 1/3] dt-bindings: edac: xlnx,versal-ddrmc: Add xlnx,versal-xilsem compatible Rama devi Veggalam
2026-10-21 13:08   ` sashiko-bot
2026-10-22 14:37   ` Rob Herring
2026-10-21 12:57 ` [PATCH v4 2/3] firmware: xilinx: Add XilSEM firmware interface Rama devi Veggalam
2026-10-21 12:57 ` Rama devi Veggalam [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1792587442-2794880-4-git-send-email-rama.devi.veggalam@amd.com \
    --to=rama.devi.veggalam@amd.com \
    --cc=bp@alien8.de \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=git@amd.com \
    --cc=james.morse@arm.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-edac@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=michal.simek@amd.com \
    --cc=robh@kernel.org \
    --cc=rric@kernel.org \
    --cc=tony.luck@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox