Re: [PATCH v3 4/4] edac: xilinx: Add EDAC support for Versal XilSem

From: Pandey, Radhey Shyam

Date: Thu Jun 25 2026 - 12:13:54 EST


Xilinx Versal Soft Error Mitigation (XilSEM) is responsible for reporting
and optionally correcting soft errors in Configuration Memory of Versal.
The Configuration Memory includes Configuration RAM and
Network on Chip (NoC) peripheral interconnect (NPI) Registers.

The Configuration RAM (CRAM) memory is used for storing configuration
data for the programmable logic (PL) fabric. The NPI registers are used
for configuring the memory controllers, miscellaneous integrated hardware,
NoC interface units in the Veral device.

Add support to handle correctable and uncorrectable error events
from XilSEM.

Add sysfs interface for XilSEM scan operations
initialize, start, stop scan, error inject, read ECC, scan status and
configuration values.

Signed-off-by: Rama devi Veggalam <rama.devi.veggalam@xxxxxxx>
---
<snip>

static int mc_probe(struct platform_device *pdev)
{
- void __iomem *ddrmc_baseaddr, *ddrmc_noc_baseaddr;
+ void __iomem *ddrmc_baseaddr, *ddrmc_noc_baseaddr, *sem_baseaddr;
struct edac_mc_layer layers[2];
struct mem_ctl_info *mci;
u8 num_chans, num_csrows;
struct edac_priv *priv;
u32 edac_mc_id, regval;
+ u32 family_code;
int rc;
ddrmc_baseaddr = devm_platform_ioremap_resource_byname(pdev, "base");
@@ -1094,6 +2365,10 @@ static int mc_probe(struct platform_device *pdev)
if (!get_ecc_state(ddrmc_baseaddr))
return -ENXIO;
+ sem_baseaddr = devm_platform_ioremap_resource_byname(pdev, "semrtca");
+ if (IS_ERR(sem_baseaddr))
+ return PTR_ERR(sem_baseaddr);
+

As mentioned by Krzysztof in DT review this change breaks existing
system. Please ioremap this semrtca resource only for xilSEM support.
We can make use of compatible based match data.

/* Allocate ID number for the EMIF controller */
edac_mc_id = emif_get_id(pdev->dev.of_node);
@@ -1124,9 +2399,29 @@ static int mc_probe(struct platform_device *pdev)
priv = mci->pvt_info;
priv->ddrmc_baseaddr = ddrmc_baseaddr;
priv->ddrmc_noc_baseaddr = ddrmc_noc_baseaddr;
+ priv->sem_baseaddr = sem_baseaddr;
priv->ce_cnt = 0;
priv->ue_cnt = 0;
priv->mc_id = edac_mc_id;
+ priv->xsem_rtca = NULL;
+
+ /* Allocate and initialize XilSem RTCA structure */
+ priv->xsem_rtca = devm_kzalloc(&pdev->dev,
+ sizeof(struct xsem_rtca_priv),
+ GFP_KERNEL);
+ if (!priv->xsem_rtca) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed to allocate xsem_rtca\n");
+ rc = -ENOMEM;
+ goto free_edac_mc;
+ }
+
+ priv->xsem_rtca->slr_info = devm_kzalloc(&pdev->dev, sizeof(struct xsem_ssit_status),
+ GFP_KERNEL);
+ if (!priv->xsem_rtca->slr_info) {
+ rc = -ENOMEM;
+ goto free_edac_mc;
+ }
mc_init(mci, pdev);
@@ -1147,6 +2442,41 @@ static int mc_probe(struct platform_device *pdev)
goto del_mc;
}
+ /* Create XilSem sysfs attributes only if XilSem is available */
+ rc = xsem_edac_create_sysfs_attributes(mci);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed to create sysfs entries\n");
+ goto remove_sysfs;
+ }
+
+ /*
+ * Firmware driver returns -ENODEV if it is not probed. In this case
+ * defer XilSEM error event registration.
+ */
+ rc = zynqmp_pm_get_family_info(&family_code);
+ if (rc) {
+ if (rc == -ENODEV)
+ rc = -EPROBE_DEFER;
+
+ goto del_mc;
+ }
+ if (family_code == PM_VERSAL_FAMILY_CODE) {
+ priv->xsem_rtca->sw_event_node_id = VERSAL_EVENT_ERROR_SW_ERR;
+ priv->xsem_rtca->cram_ce_mask = XPM_VERSAL_EVENT_ERROR_MASK_XSEM_CRAM_CE_5;
+ priv->xsem_rtca->cram_ue_mask = XPM_VERSAL_EVENT_ERROR_MASK_XSEM_CRAM_UE_6;
+ priv->xsem_rtca->npi_ue_mask = XPM_VERSAL_EVENT_ERROR_MASK_XSEM_NPI_UE_7;
+ } else {
+ edac_printk(KERN_ERR, EDAC_MC, "Invalid Device family code %d\n", family_code);
+ }
+
+ rc = xlnx_register_event(PM_NOTIFY_CB, priv->xsem_rtca->sw_event_node_id,
+ priv->xsem_rtca->cram_ce_mask | priv->xsem_rtca->cram_ue_mask |
+ priv->xsem_rtca->npi_ue_mask,
+ false, xsem_err_callback, mci);
+ if (rc)
+ goto del_mc;
+
#ifdef CONFIG_EDAC_DEBUG
create_debugfs_attributes(mci);
setup_address_map(priv);
@@ -1154,6 +2484,8 @@ static int mc_probe(struct platform_device *pdev)
enable_intr(priv);
return rc;
+remove_sysfs:
+ xsem_edac_remove_sysfs_attributes(mci);
del_mc:
edac_mc_del_mc(&pdev->dev);
free_edac_mc:
@@ -1173,9 +2505,21 @@ static void mc_remove(struct platform_device *pdev)
debugfs_remove_recursive(priv->debugfs);
#endif
+ /* Unregister XilSem events if they were registered */
+ if (priv->xsem_rtca) {
+ xlnx_unregister_event(PM_NOTIFY_CB, priv->xsem_rtca->sw_event_node_id,
+ priv->xsem_rtca->cram_ce_mask |
+ priv->xsem_rtca->cram_ue_mask |
+ priv->xsem_rtca->npi_ue_mask,
+ xsem_err_callback, mci);
+ }
xlnx_unregister_event(PM_NOTIFY_CB, VERSAL_EVENT_ERROR_PMC_ERR1,
XPM_EVENT_ERROR_MASK_DDRMC_CR |
XPM_EVENT_ERROR_MASK_DDRMC_NCR, err_callback, mci);
+ /* Remove XilSem sysfs attributes if they were created */
+ if (priv->xsem_rtca)
+ xsem_edac_remove_sysfs_attributes(mci);
+
edac_mc_del_mc(&pdev->dev);
edac_mc_free(mci);
}