mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v4 0/3] EDAC/versal: Add XilSEM CRAM/NPI error monitoring support
@ 2026-10-21 12:57 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
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Rama devi Veggalam @ 2026-10-21 12:57 UTC (permalink / raw)
  To: bp, tony.luck, michal.simek, robh, krzk+dt, conor+dt
  Cc: linux-kernel, linux-edac, devicetree, james.morse, mchehab, rric,
	git, Rama devi Veggalam

Add support for XilSEM (Xilinx Soft Error Mitigation) to
the existing versal_edac driver. XilSEM runs as part of the Platform
Loader and Manager (PLM) on the PMC and monitors Configuration RAM
(CRAM) and the NoC Programming Interface (NPI) for correctable and
uncorrectable errors. It is registered as a second, independent EDAC
device alongside the existing DDR memory-controller (DDRMC) device,
using a new "xlnx,versal-xilsem" compatible string on the same
driver module.

Changes in this series:
- Extend the devicetree binding with the new compatible string.
- Add the firmware (EEMI) interface and error-event node ID/
masks XilSEM uses to report status.
- Add the driver support: probe/remove, event callback, sysfs
attributes, and their ABI documentation, bundled together in one
patch so the sysfs interface and its documentation land in the same
commit.

Rama devi Veggalam (3):
  dt-bindings: edac: xlnx,versal-ddrmc: Add xlnx,versal-xilsem
    compatible
  firmware: xilinx: Add XilSEM firmware interface
  edac: xilinx: Add XilSEM CRAM/NPI error monitoring support

 .../ABI/testing/sysfs-devices-versal-edac          |  286 +++++
 .../memory-controllers/xlnx,versal-ddrmc-edac.yaml |   64 +-
 MAINTAINERS                                        |    5 +
 drivers/edac/Kconfig                               |   17 +-
 drivers/edac/versal_edac.c                         | 1276 +++++++++++++++++++-
 drivers/edac/xsem_edac.h                           |  219 ++++
 drivers/firmware/xilinx/Makefile                   |    2 +-
 drivers/firmware/xilinx/zynqmp-sem.c               |  229 ++++
 drivers/soc/xilinx/xlnx_event_manager.c            |   16 +-
 include/linux/firmware/xlnx-zynqmp-sem.h           |  196 +++
 include/linux/firmware/xlnx-zynqmp.h               |   50 +
 11 files changed, 2328 insertions(+), 32 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-versal-edac
 create mode 100644 drivers/edac/xsem_edac.h
 create mode 100644 drivers/firmware/xilinx/zynqmp-sem.c
 create mode 100644 include/linux/firmware/xlnx-zynqmp-sem.h

-- 
1.9.5


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v4 1/3] dt-bindings: edac: xlnx,versal-ddrmc: Add xlnx,versal-xilsem compatible
  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 ` Rama devi Veggalam
  2026-10-21 12:57 ` [PATCH v4 2/3] firmware: xilinx: Add XilSEM firmware interface Rama devi Veggalam
  2026-10-21 12:57 ` [PATCH v4 3/3] edac: xilinx: Add XilSEM CRAM/NPI error monitoring support Rama devi Veggalam
  2 siblings, 0 replies; 4+ messages in thread
From: Rama devi Veggalam @ 2026-10-21 12:57 UTC (permalink / raw)
  To: bp, tony.luck, michal.simek, robh, krzk+dt, conor+dt
  Cc: linux-kernel, linux-edac, devicetree, james.morse, mchehab, rric,
	git, Rama devi Veggalam

Add the "xlnx,versal-xilsem" compatible string for the Versal
XilSEM (Xilinx Soft Error Mitigation) hardware block.

XilSEM runs as part of the Platform Loader and Manager (PLM) on the
PMC and detects (and optionally corrects) soft errors in Versal
Configuration Memory, notifying error events to user applications.

The new compatible string only needs a single register region and
does not use reg-names or interrupts, so the schema gates these
requirements per compatible string.

Signed-off-by: Rama devi Veggalam <rama.devi.veggalam@amd.com>
---
Changes in v4:
- Added a new "xlnx,versal-xilsem" compatible string to the existing
  xlnx,versal-ddrmc-edac.yaml binding, gated via allOf/if-then so
  DDRMC and XilSEM requirements (reg, reg-names, interrupts) are
  validated independently per compatible string.

Changes in v3:
- Merged XilSEM edac with Versal Edac

Changes in v2:
- Changed "xlnx,versal-xilsem-edac" to constant
- Removed "compatible: in required section
- Removed "|" in description
- Removed "items" in compatible
- Fixed indentation in examples
- Updated title and description
---
 .../memory-controllers/xlnx,versal-ddrmc-edac.yaml | 64 +++++++++++++++++++---
 1 file changed, 57 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml b/Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
index 12f8e9f..b5dc320 100644
--- a/Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
+++ b/Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
@@ -4,25 +4,35 @@
 $id: http://devicetree.org/schemas/memory-controllers/xlnx,versal-ddrmc-edac.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: Xilinx Versal DDRMC (Integrated DDR Memory Controller)
+title: Xilinx Versal DDRMC (Integrated DDR Memory Controller) and XilSEM EDAC
 
 maintainers:
   - Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
   - Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
+  - Rama Devi Veggalam <rama.devi.veggalam@amd.com>
 
 description:
   The integrated DDR Memory Controllers (DDRMCs) support both DDR4 and LPDDR4/
   4X memory interfaces. Versal DDR memory controller has an optional ECC support
   which correct single bit ECC errors and detect double bit ECC errors.
 
+  Xilinx Versal Soft Error Mitigation (XilSEM) is part of the Platform Loader
+  and Manager (PLM) which is loaded into and runs on the Platform Management
+  Controller (PMC). XilSEM is responsible for detecting and optionally
+  correcting soft errors in Configuration Memory of Versal. Whenever an error
+  is detected, be it correctable or uncorrectable, XilSEM notifies the errors
+  to user applications via the firmware's event notification interface,
+  rather than a devicetree-described interrupt.
+
 properties:
   compatible:
-    const: xlnx,versal-ddrmc
+    enum:
+      - xlnx,versal-ddrmc
+      - xlnx,versal-xilsem
 
   reg:
-    items:
-      - description: DDR Memory Controller registers
-      - description: NOC registers corresponding to DDR Memory Controller
+    minItems: 1
+    maxItems: 2
 
   reg-names:
     items:
@@ -35,8 +45,39 @@ properties:
 required:
   - compatible
   - reg
-  - reg-names
-  - interrupts
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: xlnx,versal-ddrmc
+    then:
+      properties:
+        reg:
+          items:
+            - description: DDR Memory Controller registers
+            - description: NOC registers corresponding to DDR Memory Controller
+        reg-names:
+          items:
+            - const: base
+            - const: noc
+      required:
+        - reg-names
+        - interrupts
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: xlnx,versal-xilsem
+    then:
+      properties:
+        reg:
+          items:
+            - description: XilSEM (Soft Error Mitigation) register region
+        reg-names: false
+        interrupts: false
 
 additionalProperties: false
 
@@ -55,3 +96,12 @@ examples:
         interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
       };
     };
+  - |
+    bus {
+      #address-cells = <2>;
+      #size-cells = <2>;
+      edac@f2014050 {
+          compatible = "xlnx,versal-xilsem";
+          reg = <0x0 0xf2014050 0x0 0xc4>;
+      };
+    };
-- 
1.9.5


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v4 2/3] firmware: xilinx: Add XilSEM firmware interface
  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 12:57 ` Rama devi Veggalam
  2026-10-21 12:57 ` [PATCH v4 3/3] edac: xilinx: Add XilSEM CRAM/NPI error monitoring support Rama devi Veggalam
  2 siblings, 0 replies; 4+ messages in thread
From: Rama devi Veggalam @ 2026-10-21 12:57 UTC (permalink / raw)
  To: bp, tony.luck, michal.simek, robh, krzk+dt, conor+dt
  Cc: linux-kernel, linux-edac, devicetree, james.morse, mchehab, rric,
	git, Rama devi Veggalam

Add the firmware EEMI interface used by the Versal XilSEM (Xilinx
Soft Error Mitigation) EDAC driver. XilSEM runs as part of the
Platform Loader and Manager (PLM) on the PMC and performs CRAM/NPI
scan and error-injection operations on request from Linux.

Add drivers/firmware/xilinx/zynqmp-sem.c with the
zynqmp_pm_xilsem_*() wrapper functions (scan control, error
injection, ECC read, config read, SSIT status read, CRC read, total
SSIT frame count).

Add XilSEM error event node ID and error masks, used to register for
event notification.

Update xlnx_event_manager.c to recognize the new SW_ERR node ID as a
valid error event for both the Versal and Versal NET family codes.

Signed-off-by: Rama devi Veggalam <rama.devi.veggalam@amd.com>
---
Changes in v4:
- Added XilSEM error-event node ID and error masks to the existing
  include/linux/firmware/xlnx-zynqmp.h, used to register for error
  event notification.
- Updated xlnx_event_manager.c to recognize the new SW_ERR node ID as
  a valid error event for both Versal and Versal NET.
- Added full SSIT (multi-die) support.
- Changed all firmware responses to use typed struct xsem_*_response *
  output parameters (7 response structs) instead of raw buffers.
- Added xsem_narrow_dma_addr() to validate and narrow coherent DMA
  addresses for the two calls that take a DMA buffer.
- Added NPI error injection as its own scan-control operation, CRAM
  error injection has its own dedicated call and is not reachable
  through the generic control-ops entry point.

Changes in v3:
- created separate file for Xilsem ATF EEMI calls.

Changes in v2:
- Patch created on top of dependent patch series
"enhance zynqmp_pm_get_family_info()"
- Removed non-relevant SOB names in error event header files
- Updated copyright information
- Merged Versal and Versal NET error event definitions to firmware patch

---
 drivers/firmware/xilinx/Makefile         |   2 +-
 drivers/firmware/xilinx/zynqmp-sem.c     | 229 +++++++++++++++++++++++++++++++
 drivers/soc/xilinx/xlnx_event_manager.c  |  16 ++-
 include/linux/firmware/xlnx-zynqmp-sem.h | 196 ++++++++++++++++++++++++++
 include/linux/firmware/xlnx-zynqmp.h     |  50 +++++++
 5 files changed, 490 insertions(+), 3 deletions(-)
 create mode 100644 drivers/firmware/xilinx/zynqmp-sem.c
 create mode 100644 include/linux/firmware/xlnx-zynqmp-sem.h

diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile
index 8db0e66..a71919a 100644
--- a/drivers/firmware/xilinx/Makefile
+++ b/drivers/firmware/xilinx/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 # Makefile for Xilinx firmwares
 
-obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o zynqmp-ufs.o zynqmp-crypto.o
+obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o zynqmp-ufs.o zynqmp-crypto.o zynqmp-sem.o
 obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o
diff --git a/drivers/firmware/xilinx/zynqmp-sem.c b/drivers/firmware/xilinx/zynqmp-sem.c
new file mode 100644
index 0000000..599033f
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp-sem.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Firmware layer for XilSEM APIs.
+ *
+ * Copyright (C), 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/export.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+#include <linux/firmware/xlnx-zynqmp-sem.h>
+#include <linux/string.h>
+#include <linux/wordpart.h>
+
+/**
+ * xsem_narrow_dma_addr - Validate and narrow a coherent DMA address for the
+ *			  XilSEM SMC calling convention
+ * @bufaddr:	Coherent DMA buffer address. Must be representable in 32 bits
+ *		(e.g. obtained under dma_set_mask_and_coherent(..,
+ *		DMA_BIT_MASK(32))) - the XilSEM SMC calls cross the calling
+ *		convention as a single u32, so any upper bits are rejected
+ *		rather than silently dropped.
+ * @out:	Output: the lower 32 bits of bufaddr. Valid only if this
+ *		function returns 0.
+ *
+ * Return: 0 on success, -EINVAL if bufaddr is not representable in 32 bits.
+ */
+static int xsem_narrow_dma_addr(dma_addr_t bufaddr, u32 *out)
+{
+	if (upper_32_bits(bufaddr))
+		return -EINVAL;
+
+	*out = lower_32_bits(bufaddr);
+
+	return 0;
+}
+
+/**
+ * zynqmp_pm_xilsem_cntrl_ops - PM call to perform XilSEM operations
+ * @cmd:	Command for XilSEM scan control operations
+ * @slrid:	SLR (Super Logic Region, i.e. die) id on which scan operation to be done
+ * @response:	Output response
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_xilsem_cntrl_ops(u32 cmd, u32 slrid, struct xsem_cntrl_ops_response *response)
+{
+	u32 ret_buf[PAYLOAD_ARG_CNT];
+	int ret;
+
+	switch (cmd) {
+	case PM_XSEM_CRAM_INIT_SCAN:
+	case PM_XSEM_CRAM_START_SCAN:
+	case PM_XSEM_CRAM_STOP_SCAN:
+	case PM_XSEM_NPI_START_SCAN:
+	case PM_XSEM_NPI_STOP_SCAN:
+	case PM_XSEM_NPI_ERR_INJECT:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = zynqmp_pm_invoke_fn(PM_XSEM_HEADER | cmd, ret_buf, 1, slrid);
+	if (ret)
+		return ret;
+
+	memcpy(response, &ret_buf[1], sizeof(*response));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_xilsem_cntrl_ops);
+
+/**
+ * zynqmp_pm_xilsem_cram_err_inj - PM call to perform CRAM error injection
+ * @slrid:	SLR id to inject error in CRAM
+ * @frame:	Frame number to be used for error injection
+ * @qword:	Word number to be used for error injection
+ * @bit:	Bit location to be used for error injection
+ * @row:	CFRAME row number to be used for error injection
+ * @response:	Output response
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_xilsem_cram_err_inj(u32 slrid, u32 frame, u32 qword, u32 bit, u32 row,
+				  struct xsem_cram_errinj_response *response)
+{
+	u32 ret_buf[PAYLOAD_ARG_CNT];
+	int ret;
+
+	ret = zynqmp_pm_invoke_fn(PM_XSEM_CRAM_ERRINJ, ret_buf, 5, slrid, frame, qword, bit, row);
+	if (ret)
+		return ret;
+
+	memcpy(response, &ret_buf[1], sizeof(*response));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_xilsem_cram_err_inj);
+
+/**
+ * zynqmp_pm_xilsem_cram_read_ecc - PM call to perform CFRAME ECC read
+ * @slrid:	SLR id on which Frame ECC read to be done
+ * @frame:	Frame number to be used for reading ECC
+ * @row:	CFRAME row number to be used for reading ECC
+ * @response:	Output response
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_xilsem_cram_read_ecc(u32 slrid, u32 frame, u32 row,
+				   struct xsem_cram_readecc_response *response)
+{
+	u32 ret_buf[PAYLOAD_ARG_CNT];
+	int ret;
+
+	ret = zynqmp_pm_invoke_fn(PM_XSEM_CRAM_RD_ECC, ret_buf, 3, slrid, frame, row);
+	if (ret)
+		return ret;
+
+	memcpy(response, &ret_buf[1], sizeof(*response));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_xilsem_cram_read_ecc);
+
+/**
+ * zynqmp_pm_xilsem_read_cfg - PM call to perform Xilsem configuration read
+ * @slrid:	SLR id for which configuration to be read
+ * @response:	Output response
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_xilsem_read_cfg(u32 slrid, struct xsem_read_cfg_response *response)
+{
+	u32 ret_buf[PAYLOAD_ARG_CNT];
+	int ret;
+
+	ret = zynqmp_pm_invoke_fn(PM_XSEM_RD_CONFIG, ret_buf, 1, slrid);
+	if (ret)
+		return ret;
+
+	memcpy(response, &ret_buf[1], sizeof(*response));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_xilsem_read_cfg);
+
+/**
+ * zynqmp_pm_xilsem_read_ssit_status - PM call to perform Xilsem SSIT status
+ * @slrid:	SLR id for which ECC read to be done
+ * @bufaddr:	Coherent DMA buffer address to get the status information.
+ *		See xsem_narrow_dma_addr().
+ * @response:	Output response
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_xilsem_read_ssit_status(u32 slrid, dma_addr_t bufaddr,
+				      struct xsem_read_ssit_status_response *response)
+{
+	u32 ret_buf[PAYLOAD_ARG_CNT];
+	u32 addr;
+	int ret;
+
+	ret = xsem_narrow_dma_addr(bufaddr, &addr);
+	if (ret)
+		return ret;
+
+	ret = zynqmp_pm_invoke_fn(PM_XSEM_SSIT_RD_STS, ret_buf, 2, slrid, addr);
+	if (ret)
+		return ret;
+
+	memcpy(response, &ret_buf[1], sizeof(*response));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_xilsem_read_ssit_status);
+
+/**
+ * zynqmp_pm_xilsem_cram_get_crc - PM call to perform CRAM Row CRC read
+ * @slrid:	SLR id for which CRC read to be done
+ * @rowindex:	CFRAME row number to be used for reading CRC
+ * @response:	Output response
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_xilsem_cram_get_crc(u32 slrid, u32 rowindex,
+				  struct xsem_cram_getcrc_response *response)
+{
+	u32 ret_buf[PAYLOAD_ARG_CNT];
+	int ret;
+
+	ret = zynqmp_pm_invoke_fn(PM_XSEM_SSIT_GET_CRC, ret_buf, 2, slrid, rowindex);
+	if (ret)
+		return ret;
+
+	memcpy(response, &ret_buf[1], sizeof(*response));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_xilsem_cram_get_crc);
+
+/**
+ * zynqmp_pm_xilsem_cram_ssit_tot_frames - PM call to perform total frames read
+ * @slrid:	SLR id for which total frames read to be done
+ * @row:	CFRAME row number to be used for reading ECC
+ * @bufaddr:	Coherent DMA buffer address to get total frames data.
+ *		See xsem_narrow_dma_addr().
+ * @response:	Output response
+ *
+ * Return: Returns 0 on success or error value on failure.
+ */
+int zynqmp_pm_xilsem_cram_ssit_tot_frames(u32 slrid, u32 row, dma_addr_t bufaddr,
+					  struct xsem_cram_ssit_totframes_response *response)
+{
+	u32 ret_buf[PAYLOAD_ARG_CNT];
+	u32 addr;
+	int ret;
+
+	ret = xsem_narrow_dma_addr(bufaddr, &addr);
+	if (ret)
+		return ret;
+
+	ret = zynqmp_pm_invoke_fn(PM_XSEM_SSIT_GET_FRAMES, ret_buf, 3, slrid, row, addr);
+	if (ret)
+		return ret;
+
+	memcpy(response, &ret_buf[1], sizeof(*response));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_xilsem_cram_ssit_tot_frames);
diff --git a/drivers/soc/xilinx/xlnx_event_manager.c b/drivers/soc/xilinx/xlnx_event_manager.c
index f733dc4..d832ee6 100644
--- a/drivers/soc/xilinx/xlnx_event_manager.c
+++ b/drivers/soc/xilinx/xlnx_event_manager.c
@@ -75,6 +75,16 @@ struct registered_event_data {
 	struct hlist_node hentry;
 };
 
+/*
+ * Error-event node ids are per-family firmware IDs, packed at
+ * 0x28100000 + n * 0x4000. The same value names different nodes on
+ * different families: 0x28110000 is VERSAL_EVENT_ERROR_SW_ERR and
+ * VERSAL_NET_EVENT_ERROR_PSM_ERR2. Compare a node_id only inside the
+ * arm for the family that owns it. For this predicate the overlap does
+ * not change the result, because 0x28110000 is an error-event node on
+ * every family that defines it. The family split is required for ids
+ * that exist in only one table.
+ */
 static bool xlnx_is_error_event(const u32 node_id)
 {
 	u32 pm_family_code;
@@ -85,7 +95,8 @@ static bool xlnx_is_error_event(const u32 node_id)
 		if (node_id == VERSAL_EVENT_ERROR_PMC_ERR1 ||
 		    node_id == VERSAL_EVENT_ERROR_PMC_ERR2 ||
 		    node_id == VERSAL_EVENT_ERROR_PSM_ERR1 ||
-		    node_id == VERSAL_EVENT_ERROR_PSM_ERR2)
+		    node_id == VERSAL_EVENT_ERROR_PSM_ERR2 ||
+		    node_id == VERSAL_EVENT_ERROR_SW_ERR)
 			return true;
 	} else if (pm_family_code == PM_VERSAL_NET_FAMILY_CODE) {
 		if (node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR1 ||
@@ -94,7 +105,8 @@ static bool xlnx_is_error_event(const u32 node_id)
 		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR1 ||
 		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR2 ||
 		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR3 ||
-		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR4)
+		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR4 ||
+		    node_id == VERSAL_NET_EVENT_ERROR_SW_ERR)
 			return true;
 	}
 
diff --git a/include/linux/firmware/xlnx-zynqmp-sem.h b/include/linux/firmware/xlnx-zynqmp-sem.h
new file mode 100644
index 0000000..f2b66cd
--- /dev/null
+++ b/include/linux/firmware/xlnx-zynqmp-sem.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Firmware layer for XilSEM APIs.
+ *
+ * Copyright (C), 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef __FIRMWARE_ZYNQMP_SEM_H__
+#define __FIRMWARE_ZYNQMP_SEM_H__
+
+#include <linux/types.h>
+
+/* XilSEM commands */
+#define PM_XSEM_HEADER			0x300
+#define PM_XSEM_CRAM_ERRINJ		0x304
+#define PM_XSEM_RD_CONFIG		0x309
+#define PM_XSEM_CRAM_RD_ECC		0x30B
+#define PM_XSEM_SSIT_GET_CRC		0x30C
+#define PM_XSEM_SSIT_RD_STS		0x30D
+#define PM_XSEM_SSIT_GET_FRAMES		0x30E
+
+/*
+ * Valid cmd values for zynqmp_pm_xilsem_cntrl_ops(). CRAM error injection
+ * has its own dedicated firmware call (zynqmp_pm_xilsem_cram_err_inj()) and
+ * must not be reachable through this generic entry point, so it is
+ * deliberately not a member of this enum.
+ */
+enum pm_xsem_cntrl_op {
+	PM_XSEM_CRAM_INIT_SCAN = 1,
+	PM_XSEM_CRAM_START_SCAN = 2,
+	PM_XSEM_CRAM_STOP_SCAN = 3,
+	PM_XSEM_NPI_START_SCAN = 5,
+	PM_XSEM_NPI_STOP_SCAN = 6,
+	PM_XSEM_NPI_ERR_INJECT = 7,
+};
+
+/**
+ * struct xsem_cntrl_ops_response - Response to zynqmp_pm_xilsem_cntrl_ops()
+ * @hdr:	Command header
+ * @status:	Error code or status
+ * @slrid:	SLR id
+ * @status1:	Reserved for future use
+ * @status2:	Reserved for future use
+ * @status3:	Reserved for future use
+ */
+struct xsem_cntrl_ops_response {
+	u32 hdr;
+	u32 status;
+	u32 slrid;
+	u32 status1;
+	u32 status2;
+	u32 status3;
+};
+
+/**
+ * struct xsem_cram_errinj_response - Response to zynqmp_pm_xilsem_cram_err_inj()
+ * @hdr:	Command header
+ * @status:	Error code or status
+ * @slrid:	SLR id
+ */
+struct xsem_cram_errinj_response {
+	u32 hdr;
+	u32 status;
+	u32 slrid;
+};
+
+/**
+ * struct xsem_cram_readecc_response - Response to zynqmp_pm_xilsem_cram_read_ecc()
+ * @hdr:	Frame ECC read command header
+ * @ecc0:	First ECC word
+ * @ecc1:	Second ECC word
+ * @status:	Status
+ */
+struct xsem_cram_readecc_response {
+	u32 hdr;
+	u32 ecc0;
+	u32 ecc1;
+	u32 status;
+};
+
+/**
+ * struct xsem_read_cfg_response - Response to zynqmp_pm_xilsem_read_cfg()
+ * @hdr:	Config read command header
+ * @cram_cfg:	CRAM scan configuration
+ * @npi_cfg:	NPI scan configuration
+ * @status:	Status
+ */
+struct xsem_read_cfg_response {
+	u32 hdr;
+	u32 cram_cfg;
+	u32 npi_cfg;
+	u32 status;
+};
+
+/**
+ * struct xsem_read_ssit_status_response - Response to
+ *					    zynqmp_pm_xilsem_read_ssit_status()
+ * @hdr:	Status read command header
+ * @slrid:	SLR id
+ */
+struct xsem_read_ssit_status_response {
+	u32 hdr;
+	u32 slrid;
+};
+
+/**
+ * struct xsem_cram_getcrc_response - Response to zynqmp_pm_xilsem_cram_get_crc()
+ * @hdr:	Get CRC command header
+ * @crc0:	First CRC word
+ * @crc1:	Second CRC word
+ * @crc2:	Third CRC word
+ * @crc3:	Fourth CRC word
+ * @status:	Status
+ */
+struct xsem_cram_getcrc_response {
+	u32 hdr;
+	u32 crc0;
+	u32 crc1;
+	u32 crc2;
+	u32 crc3;
+	u32 status;
+};
+
+/**
+ * struct xsem_cram_ssit_totframes_response - Response to
+ *					       zynqmp_pm_xilsem_cram_ssit_tot_frames()
+ * @hdr:	Total frames command header
+ * @slrid:	SLR id
+ * @row:	CFRAME row number
+ * @status:	Status
+ */
+struct xsem_cram_ssit_totframes_response {
+	u32 hdr;
+	u32 slrid;
+	u32 row;
+	u32 status;
+};
+
+#if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE)
+int zynqmp_pm_xilsem_cntrl_ops(u32 cmd, u32 slrid, struct xsem_cntrl_ops_response *response);
+int zynqmp_pm_xilsem_cram_err_inj(u32 slrid, u32 frame, u32 qword, u32 bit, u32 row,
+				  struct xsem_cram_errinj_response *response);
+int zynqmp_pm_xilsem_cram_read_ecc(u32 slrid, u32 frame, u32 row,
+				   struct xsem_cram_readecc_response *response);
+int zynqmp_pm_xilsem_read_cfg(u32 slrid, struct xsem_read_cfg_response *response);
+int zynqmp_pm_xilsem_read_ssit_status(u32 slrid, dma_addr_t bufaddr,
+				      struct xsem_read_ssit_status_response *response);
+int zynqmp_pm_xilsem_cram_get_crc(u32 slrid, u32 rowindex,
+				  struct xsem_cram_getcrc_response *response);
+int zynqmp_pm_xilsem_cram_ssit_tot_frames(u32 slrid, u32 row, dma_addr_t bufaddr,
+					  struct xsem_cram_ssit_totframes_response *response);
+#else
+static inline int zynqmp_pm_xilsem_cntrl_ops(u32 cmd, u32 slrid,
+					     struct xsem_cntrl_ops_response *response)
+{
+	return -ENODEV;
+}
+
+static inline int zynqmp_pm_xilsem_cram_err_inj(u32 slrid, u32 frame, u32 qword, u32 bit, u32 row,
+						struct xsem_cram_errinj_response *response)
+{
+	return -ENODEV;
+}
+
+static inline int zynqmp_pm_xilsem_cram_read_ecc(u32 slrid, u32 frame, u32 row,
+						 struct xsem_cram_readecc_response *response)
+{
+	return -ENODEV;
+}
+
+static inline int zynqmp_pm_xilsem_read_cfg(u32 slrid, struct xsem_read_cfg_response *response)
+{
+	return -ENODEV;
+}
+
+static inline int zynqmp_pm_xilsem_read_ssit_status(u32 slrid, dma_addr_t bufaddr,
+						    struct xsem_read_ssit_status_response *response)
+{
+	return -ENODEV;
+}
+
+static inline int zynqmp_pm_xilsem_cram_get_crc(u32 slrid, u32 rowindex,
+						struct xsem_cram_getcrc_response *response)
+{
+	return -ENODEV;
+}
+
+static inline int
+zynqmp_pm_xilsem_cram_ssit_tot_frames(u32 slrid, u32 row, dma_addr_t bufaddr,
+				      struct xsem_cram_ssit_totframes_response *response)
+{
+	return -ENODEV;
+}
+#endif
+
+#endif /* __FIRMWARE_ZYNQMP_SEM_H__ */
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index f4b3df6..80ca1dc 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -121,6 +121,12 @@
 #define VERSAL_EVENT_ERROR_PMC_ERR2	(0x28104000U)
 #define VERSAL_EVENT_ERROR_PSM_ERR1	(0x28108000U)
 #define VERSAL_EVENT_ERROR_PSM_ERR2	(0x2810C000U)
+/*
+ * SW Error Event Node Id (0x4000 stride from PSM_ERR2 above). This
+ * value coincidentally equals VERSAL_NET_EVENT_ERROR_PSM_ERR2 below -
+ * harmless, since each family's node IDs are an independent sequence.
+ */
+#define VERSAL_EVENT_ERROR_SW_ERR	(0x28110000U)
 
 #define VERSAL_NET_EVENT_ERROR_PMC_ERR1	(0x28100000U)
 #define VERSAL_NET_EVENT_ERROR_PMC_ERR2	(0x28104000U)
@@ -129,6 +135,8 @@
 #define VERSAL_NET_EVENT_ERROR_PSM_ERR2	(0x28110000U)
 #define VERSAL_NET_EVENT_ERROR_PSM_ERR3	(0x28114000U)
 #define VERSAL_NET_EVENT_ERROR_PSM_ERR4	(0x28118000U)
+/* SW Error Event Node Id */
+#define VERSAL_NET_EVENT_ERROR_SW_ERR	(0x2811C000U)
 
 /* ZynqMP SD tap delay tuning */
 #define SD_ITAPDLY	0xFF180314
@@ -146,6 +154,48 @@
 #define XPM_EVENT_ERROR_MASK_NOC_NCR		BIT(13)
 #define XPM_EVENT_ERROR_MASK_NOC_CR		BIT(12)
 
+/*
+ * XPM_VERSAL_EVENT_ERROR_MASK_XSEM_CRAM_CE_5: Error event mask for handling
+ * correctable error in Versal Configuration RAM which is reported by
+ * Soft Error Mitigation (XilSEM).
+ */
+#define XPM_VERSAL_EVENT_ERROR_MASK_XSEM_CRAM_CE_5		BIT(5)
+
+/*
+ * XPM_VERSAL_EVENT_ERROR_MASK_XSEM_CRAM_UE_6: Error event mask for handling
+ * uncorrectable error in Versal Configuration RAM which is reported by
+ * Soft Error Mitigation (XilSEM).
+ */
+#define XPM_VERSAL_EVENT_ERROR_MASK_XSEM_CRAM_UE_6		BIT(6)
+
+/*
+ * XPM_VERSAL_EVENT_ERROR_MASK_XSEM_NPI_UE_7: Error event mask for handling
+ * uncorrectable error in Versal NoC programming interface (NPI)
+ * register which is reported by Soft Error Mitigation (XilSEM).
+ */
+#define XPM_VERSAL_EVENT_ERROR_MASK_XSEM_NPI_UE_7		BIT(7)
+
+/*
+ * XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_CRAM_CE: Error event mask for handling
+ * correctable error in Versal Configuration RAM which is reported by
+ * Soft Error Mitigation (XilSEM).
+ */
+#define XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_CRAM_CE		BIT(7)
+
+/*
+ * XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_CRAM_UE: Error event mask for handling
+ * uncorrectable error in Versal Configuration RAM which is reported by
+ * Soft Error Mitigation (XilSEM).
+ */
+#define XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_CRAM_UE		BIT(8)
+
+/*
+ * XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_NPI_UE: Error event mask for handling
+ * uncorrectable error in Versal NoC programming interface (NPI)
+ * register which is reported by Soft Error Mitigation (XilSEM).
+ */
+#define XPM_VERSAL_NET_EVENT_ERROR_MASK_XSEM_NPI_UE		BIT(9)
+
 /* Node ID for all peripheral devices */
 #define PM_DEV_ALL_PERIPH	0x18224FFFU
 
-- 
1.9.5


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v4 3/3] edac: xilinx: Add XilSEM CRAM/NPI error monitoring support
  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 12:57 ` [PATCH v4 2/3] firmware: xilinx: Add XilSEM firmware interface Rama devi Veggalam
@ 2026-10-21 12:57 ` Rama devi Veggalam
  2 siblings, 0 replies; 4+ messages in thread
From: Rama devi Veggalam @ 2026-10-21 12:57 UTC (permalink / raw)
  To: bp, tony.luck, michal.simek, robh, krzk+dt, conor+dt
  Cc: linux-kernel, linux-edac, devicetree, james.morse, mchehab, rric,
	git, Rama devi Veggalam

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


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-21 12:57 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 12:57 ` [PATCH v4 2/3] firmware: xilinx: Add XilSEM firmware interface Rama devi Veggalam
2026-10-21 12:57 ` [PATCH v4 3/3] edac: xilinx: Add XilSEM CRAM/NPI error monitoring support Rama devi Veggalam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®