2023-09-21 01:32:47

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 00/18] EDAC/synopsys: Add generic DDRC info and address mapping

This patchset is a second one in the series created in the framework of
my Synopsys DW uMCTL2 DDRC-related work:

[1: In-progress v4] EDAC/mc/synopsys: Various fixes and cleanups
Link: https://lore.kernel.org/linux-edac/[email protected]
[2: In-progress v4] EDAC/synopsys: Add generic DDRC info and address mapping
Link: ---you are looking at it---
[3: In-progress v4] EDAC/synopsys: Add generic resources and Scrub support
Link: ---to be submitted---

Note the patchsets above must be merged in the same order as they are
placed in the list in order to prevent conflicts. Nothing prevents them
from being reviewed synchronously though. Any tests are very welcome.
Thanks in advance.

The second patchset mainly concerns converting the DW uMCTL2 DDRC driver
to being more generic, supporting wider range of the DW uMCTL2 DDRC IP-core
compilations and thus being utilized with more versions of the Synopsys
DDR controllers.

The series starts with the Error-injection functionality movement to
DebugFS. Indeed the Debug-parts should be in the dedicated DebugFS. SysFS
is not a place for it. Moreover a bit later here some more debug nodes
will be added.

Afterwards even though it isn't advertised but even at this stage the DW
uMCTL2 DDRC driver supports a bit more DDR protocols than it is actually
specified in the mem_ctrl_info.mtype_cap field. So first the EDAC MCI core
memory types enumeration is extended with LPDDR (mDDR) and LPDDR2, which
support can be enabled in the DW uMCTL2 DDR controller. Second the driver
is fixed to properly detected the new and older DDR protocol types during
the DW uMCTL DDRC probe procedure.

Then a bit painful patch goes. Alas we have to deviate the driver from the
EDAC standard private data allocation/initialization pattern. Since we are
going to add the DW uMCTL2 IP-core specific parameters detection procedure
and later on implement additional platform resources requests, there is no
other choice but to allocate the driver private data at the early stage of
the device probe procedure, even before it's possible to allocate the MCI
descriptor. The DW uMCTL2 DDRC platform resources and configuration info
will be then utilized to properly allocate and initialize the
mem_ctrl_info structure instance.

Fifth patch in the series is very important. It provides the DW uMCTL2
DDRC parameters detection procedure. The DDRC and ECC parameters detected
at this stage will be then utilized to make the driver working with much
wider set of the DW uMCTL2 revisions and configurations. In particular
from now the driver will retrieve the next DDRC info at the probe stage:
ECC type, SDRAM protocol (DDR type), Full and actual DQ-bus width, SDRAM
and HIF burst length, Core/SDRAM frequency ration, number of SDRAM ranks.
The DDRC parameters structure will be extended with some more fields later
in this and the next patchset. The provided private DDRC parameters
infrastructure can be utilized to implement the platform-specific
capabilities so the platform data and its quirks are replaced with it.

The detected at the probe stage DW uMCTL2 DDRC parameters can be now used
to implement the configuration specific functionality. In particular first
we introduce the conditional ADDRMAP* CSRs parsing since some of these
CSRs and their fields are left unused by the controller in some cases.
Secondly actual DIMM ECC errors grain, ECC corrected bit, syndrome and
full data+ecc pattern are determined based on the DDRC parameters.

Afterwards goes a series of the patches which introduce an interface to
generically determine the system address based on the SDRAM address and
vice-versa. Thus we'll be able to report actual PFN and offset in case of
the corrected and uncorrected errors. So first we get to convert the
currently available HIF/SDRAM mapping table utilized for the
errors-injection functionality into a more generic Sys<->SDRAM address
translation interface. Secondly we suggest to conform the SDRAM column
address mapping detection algorithm with what is defined in the DW uMCTL2
DDRC hw reference manual thus simplifying the ADDRMAP* CSRs parsing
procedure. After adding a handy DebugFS node to read the HIF/SDRAM mapping
and the system address regions support, we finally introduce the erroneous
page-frame/offset reporting to the MCI core. Since the full SDRAM address
mapping is now always available we suggest to use it for the attached
memory size calculation, which is a more correct approach rather than the
si_meminfo()-based one.

Changelog v2:
- Rebase onto the latest version of the patchset:
[PATCH v2 00/19] EDAC/mc/synopsys: Various fixes and cleanups
- Just resend.

Changelog v3:
- Just resend.

Changelog v4:
- Get syndrome from the ECCSTAT.ecc_corrected_bit_num field rather than
from ECCCSYN2. The later CSR in fact contains ECC.
- On correctable and uncorrectable errors retrieve ECC aside with the
erroneous data.
- Rebase onto the kernel v6.6-rcX.

Signed-off-by: Serge Semin <[email protected]>
Cc: Punnaiah Choudary Kalluri <[email protected]>
Cc: Dinh Nguyen <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]

Serge Semin (18):
EDAC/synopsys: Convert sysfs nodes to debugfs ones
EDAC/mc: Extend memtypes with LPDDR(mDDR) and LPDDR2
EDAC/synopsys: Extend memtypes supported by controller
EDAC/synopsys: Detach private data from mci instance
EDAC/synopsys: Add DDRC basic parameters infrastructure
EDAC/synopsys: Convert plat-data to plat-init function
EDAC/synopsys: Parse ADDRMAP[7-8] CSRs for (LP)DDR4 only
EDAC/synopsys: Parse ADDRMAP[0] CSR for multi-ranks case only
EDAC/synopsys: Set actual DIMM ECC errors grain
EDAC/synopsys: Get corrected bit position
EDAC/synopsys: Pass syndrome to EDAC error handler
EDAC/synopsys: Read full data+ecc pattern on errors
EDAC/synopsys: Introduce System/SDRAM address translation interface
EDAC/synopsys: Simplify HIF/SDRAM column mapping get procedure
EDAC/synopsys: Add HIF/SDRAM mapping debugfs node
EDAC/synopsys: Add erroneous page-frame/offset reporting
EDAC/synopsys: Add system address regions support
EDAC/synopsys: Add mapping-based memory size calculation

drivers/edac/edac_mc.c | 2 +
drivers/edac/synopsys_edac.c | 1828 ++++++++++++++++++++++++----------
include/linux/edac.h | 6 +
3 files changed, 1321 insertions(+), 515 deletions(-)

--
2.41.0


2023-09-21 01:36:58

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 04/18] EDAC/synopsys: Detach private data from mci instance

A comprehensive DW uMCTL2 DDRC parameters detection procedure and some
resources requests (clocks and resets) are about to be added to the
driver. Since these parameters will be utilized in the various parts of
the driver and in particular used for the Memory Controller data instance
pre-initialization, they need to be: first retrieved before the MCI is
allocated; second preserved in the driver private data. Therefore the best
approach would be to add the parameters structure right into the driver
private data and just allocate the data separately from the mem_ctl_info
instance. For that: add a new static method snps_data_create(), which
aside with the snps_edac_priv structure allocation will also perform the
private data basic initialization like CSRs region mapping, device data
getting, platform data pointer copying and spin-lock initialization;
convert the snps_mc_init() method to snps_mc_create(), which from now will
be used to allocate and initialize the mem_ctl_info structure instance.

Note in order to have an access to the snps_edac_priv structure instance
as before this change, the mem_ctl_info.pvt_info field will be initialized
with the pointer to that structure instance.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/edac/synopsys_edac.c | 137 +++++++++++++++++++++++------------
1 file changed, 90 insertions(+), 47 deletions(-)

diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index e08e9f3c81cb..e177a36646c0 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -242,6 +242,7 @@ struct snps_ecc_status {

/**
* struct snps_edac_priv - DDR memory controller private data.
+ * @pdev: Platform device.
* @baseaddr: Base address of the DDR controller.
* @reglock: Concurrent CSRs access lock.
* @message: Buffer for framing the event specific info.
@@ -255,6 +256,7 @@ struct snps_ecc_status {
* @rank_shift: Bit shifts for rank bit.
*/
struct snps_edac_priv {
+ struct platform_device *pdev;
void __iomem *baseaddr;
spinlock_t reglock;
char message[SNPS_EDAC_MSG_SIZE];
@@ -463,6 +465,34 @@ static irqreturn_t snps_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}

+/**
+ * snps_create_data - Create private data.
+ * @pdev: platform device.
+ *
+ * Return: Private data instance or negative errno.
+ */
+static struct snps_edac_priv *snps_create_data(struct platform_device *pdev)
+{
+ struct snps_edac_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+
+ priv->baseaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->baseaddr))
+ return ERR_CAST(priv->baseaddr);
+
+ priv->p_data = of_device_get_match_data(&pdev->dev);
+ if (!priv->p_data)
+ return ERR_PTR(-ENODEV);
+
+ priv->pdev = pdev;
+ spin_lock_init(&priv->reglock);
+
+ return priv;
+}
+
/**
* snps_get_dtype - Return the controller memory width.
* @base: DDR memory controller base address.
@@ -594,18 +624,36 @@ static void snps_init_csrows(struct mem_ctl_info *mci)
}

/**
- * snps_mc_init - Initialize one driver instance.
- * @mci: EDAC memory controller instance.
- * @pdev: platform device.
+ * snps_mc_create - Create and initialize MC instance.
+ * @priv: DDR memory controller private data.
+ *
+ * Allocate the EDAC memory controller descriptor and initialize it
+ * using the private data info.
*
- * Perform initialization of the EDAC memory controller instance and
- * related driver-private data associated with the memory controller the
- * instance is bound to.
+ * Return: MC data instance or negative errno.
*/
-static void snps_mc_init(struct mem_ctl_info *mci, struct platform_device *pdev)
+static struct mem_ctl_info *snps_mc_create(struct snps_edac_priv *priv)
{
- mci->pdev = &pdev->dev;
- platform_set_drvdata(pdev, mci);
+ struct edac_mc_layer layers[2];
+ struct mem_ctl_info *mci;
+
+ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+ layers[0].size = SNPS_EDAC_NR_CSROWS;
+ layers[0].is_virt_csrow = true;
+ layers[1].type = EDAC_MC_LAYER_CHANNEL;
+ layers[1].size = SNPS_EDAC_NR_CHANS;
+ layers[1].is_virt_csrow = false;
+
+ mci = edac_mc_alloc(EDAC_AUTO_MC_NUM, ARRAY_SIZE(layers), layers, 0);
+ if (!mci) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Failed memory allocation for mc instance\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mci->pvt_info = priv;
+ mci->pdev = &priv->pdev->dev;
+ platform_set_drvdata(priv->pdev, mci);

/* Initialize controller capabilities and configuration */
mci->mtype_cap = MEM_FLAG_LPDDR | MEM_FLAG_DDR2 | MEM_FLAG_LPDDR2 |
@@ -625,24 +673,43 @@ static void snps_mc_init(struct mem_ctl_info *mci, struct platform_device *pdev)
mci->ctl_page_to_phys = NULL;

snps_init_csrows(mci);
+
+ return mci;
}

+/**
+ * snps_mc_free - Free MC instance.
+ * @mci: EDAC memory controller instance.
+ *
+ * Just revert what was done in the framework of the snps_mc_create().
+ *
+ * Return: MC data instance or negative errno.
+ */
+static void snps_mc_free(struct mem_ctl_info *mci)
+{
+ struct snps_edac_priv *priv = mci->pvt_info;
+
+ platform_set_drvdata(priv->pdev, NULL);

+ edac_mc_free(mci);
+}

-static int snps_setup_irq(struct mem_ctl_info *mci, struct platform_device *pdev)
+
+
+static int snps_setup_irq(struct mem_ctl_info *mci)
{
struct snps_edac_priv *priv = mci->pvt_info;
int ret, irq;

- irq = platform_get_irq(pdev, 0);
+ irq = platform_get_irq(priv->pdev, 0);
if (irq < 0) {
edac_printk(KERN_ERR, EDAC_MC,
"No IRQ %d in DT\n", irq);
return irq;
}

- ret = devm_request_irq(&pdev->dev, irq, snps_irq_handler,
- 0, dev_name(&pdev->dev), mci);
+ ret = devm_request_irq(&priv->pdev->dev, irq, snps_irq_handler,
+ 0, dev_name(&priv->pdev->dev), mci);
if (ret < 0) {
edac_printk(KERN_ERR, EDAC_MC, "Failed to request IRQ\n");
return ret;
@@ -1066,49 +1133,24 @@ static inline void snps_create_debugfs_nodes(struct mem_ctl_info *mci) {}
*/
static int snps_mc_probe(struct platform_device *pdev)
{
- const struct snps_platform_data *p_data;
- struct edac_mc_layer layers[2];
struct snps_edac_priv *priv;
struct mem_ctl_info *mci;
- void __iomem *baseaddr;
int rc;

- baseaddr = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(baseaddr))
- return PTR_ERR(baseaddr);
-
- p_data = of_device_get_match_data(&pdev->dev);
- if (!p_data)
- return -ENODEV;
+ priv = snps_create_data(pdev);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);

if (!snps_get_ecc_state(baseaddr)) {
edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n");
return -ENXIO;
}

- layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
- layers[0].size = SNPS_EDAC_NR_CSROWS;
- layers[0].is_virt_csrow = true;
- layers[1].type = EDAC_MC_LAYER_CHANNEL;
- layers[1].size = SNPS_EDAC_NR_CHANS;
- layers[1].is_virt_csrow = false;
-
- mci = edac_mc_alloc(EDAC_AUTO_MC_NUM, ARRAY_SIZE(layers), layers,
- sizeof(struct snps_edac_priv));
- if (!mci) {
- edac_printk(KERN_ERR, EDAC_MC,
- "Failed memory allocation for mc instance\n");
- return -ENOMEM;
- }
-
- priv = mci->pvt_info;
- priv->baseaddr = baseaddr;
- priv->p_data = p_data;
- spin_lock_init(&priv->reglock);
+ mci = snps_mc_create(priv);
+ if (IS_ERR(mci))
+ return PTR_ERR(mci);

- snps_mc_init(mci, pdev);
-
- rc = snps_setup_irq(mci, pdev);
+ rc = snps_setup_irq(mci);
if (rc)
goto free_edac_mc;

@@ -1124,7 +1166,7 @@ static int snps_mc_probe(struct platform_device *pdev)
return 0;

free_edac_mc:
- edac_mc_free(mci);
+ snps_mc_free(mci);

return rc;
}
@@ -1143,7 +1185,8 @@ static int snps_mc_remove(struct platform_device *pdev)
snps_disable_irq(priv);

edac_mc_del_mc(&pdev->dev);
- edac_mc_free(mci);
+
+ snps_mc_free(mci);

return 0;
}
--
2.41.0

2023-09-21 01:41:25

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 02/18] EDAC/mc: Extend memtypes with LPDDR(mDDR) and LPDDR2

These are normal memory types [1] which can be met on the real hardware.
DW uMCTL2 DDRC IP-core can be configured to have them supported [2,3].
Extend the EDAC memory types enumeration with the corresponding IDs then.

[1] https://en.wikipedia.org/wiki/LPDDR
[2] DesignWare® Cores Enhanced Universal DDR Memory Controller (uMCTL2)
Databook, Version 3.91a, October 2020, p.501
[3] DesignWare® Cores Enhanced Universal DDR Memory Controller (uMCTL2)
Databook, Version 3.91a, October 2020, p.1717

Signed-off-by: Serge Semin <[email protected]>
---
drivers/edac/edac_mc.c | 2 ++
include/linux/edac.h | 6 ++++++
2 files changed, 8 insertions(+)

diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 634c41ea7804..e353e98e01e2 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -151,10 +151,12 @@ const char * const edac_mem_types[] = {
[MEM_RDR] = "Registered-SDR",
[MEM_DDR] = "Unbuffered-DDR",
[MEM_RDDR] = "Registered-DDR",
+ [MEM_LPDDR] = "Low-Power-(m)DDR-RAM",
[MEM_RMBS] = "RMBS",
[MEM_DDR2] = "Unbuffered-DDR2",
[MEM_FB_DDR2] = "FullyBuffered-DDR2",
[MEM_RDDR2] = "Registered-DDR2",
+ [MEM_LPDDR2] = "Low-Power-DDR2-RAM",
[MEM_XDR] = "XDR",
[MEM_DDR3] = "Unbuffered-DDR3",
[MEM_RDDR3] = "Registered-DDR3",
diff --git a/include/linux/edac.h b/include/linux/edac.h
index fa4bda2a70f6..89167a4459d5 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -157,6 +157,7 @@ static inline char *mc_event_error_type(const unsigned int err_type)
* This is a variant of the DDR memories.
* A registered memory has a buffer inside it, hiding
* part of the memory details to the memory controller.
+ * @MEM_LPDDR: Low-Power DDR memory (mDDR).
* @MEM_RMBS: Rambus DRAM, used on a few Pentium III/IV controllers.
* @MEM_DDR2: DDR2 RAM, as described at JEDEC JESD79-2F.
* Those memories are labeled as "PC2-" instead of "PC" to
@@ -167,6 +168,7 @@ static inline char *mc_event_error_type(const unsigned int err_type)
* a chip select signal.
* @MEM_RDDR2: Registered DDR2 RAM
* This is a variant of the DDR2 memories.
+ * @MEM_LPDDR2: Low-Power DDR2 memory.
* @MEM_XDR: Rambus XDR
* It is an evolution of the original RAMBUS memories,
* created to compete with DDR2. Weren't used on any
@@ -199,10 +201,12 @@ enum mem_type {
MEM_RDR,
MEM_DDR,
MEM_RDDR,
+ MEM_LPDDR,
MEM_RMBS,
MEM_DDR2,
MEM_FB_DDR2,
MEM_RDDR2,
+ MEM_LPDDR2,
MEM_XDR,
MEM_DDR3,
MEM_RDDR3,
@@ -230,10 +234,12 @@ enum mem_type {
#define MEM_FLAG_RDR BIT(MEM_RDR)
#define MEM_FLAG_DDR BIT(MEM_DDR)
#define MEM_FLAG_RDDR BIT(MEM_RDDR)
+#define MEM_FLAG_LPDDR BIT(MEM_LPDDR)
#define MEM_FLAG_RMBS BIT(MEM_RMBS)
#define MEM_FLAG_DDR2 BIT(MEM_DDR2)
#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
#define MEM_FLAG_RDDR2 BIT(MEM_RDDR2)
+#define MEM_FLAG_LPDDR2 BIT(MEM_LPDDR2)
#define MEM_FLAG_XDR BIT(MEM_XDR)
#define MEM_FLAG_DDR3 BIT(MEM_DDR3)
#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3)
--
2.41.0

2023-09-21 01:48:14

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 09/18] EDAC/synopsys: Set actual DIMM ECC errors grain

It was wrong to set the DIMM errors grain parameter to just 1 byte because
DW uMCTL2 DDRC calculates ECC for each SDRAM word and passes it as an
additional byte of data to the memory chips. SDRAM word is the actual
DQ-bus width determined by the DQ-width set during the IP-core synthesize
and the DQ-bus mode (part of the DQ-bus actually used to get data from the
memory chips) selected during the DDR controller initial setup procedure.
Thus set the MCI DIMMs grain based on these parameters determined during
the DW uMCTL2 DDRC config getting procedure.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/edac/synopsys_edac.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index e6288e135480..e10778cead63 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -26,9 +26,6 @@
/* Number of channels per memory controller */
#define SNPS_EDAC_NR_CHANS 1

-/* Granularity of reported error in bytes */
-#define SNPS_EDAC_ERR_GRAIN 1
-
#define SNPS_EDAC_MSG_SIZE 256

#define SNPS_EDAC_MOD_STRING "snps_edac"
@@ -736,9 +733,12 @@ static void snps_init_csrows(struct mem_ctl_info *mci)
struct snps_edac_priv *priv = mci->pvt_info;
struct csrow_info *csi;
struct dimm_info *dimm;
- u32 size, row;
+ u32 size, row, width;
int j;

+ /* Actual SDRAM-word width for which ECC is calculated */
+ width = 1U << (priv->info.dq_width - priv->info.dq_mode);
+
for (row = 0; row < mci->nr_csrows; row++) {
csi = mci->csrows[row];
size = snps_get_memsize();
@@ -748,7 +748,7 @@ static void snps_init_csrows(struct mem_ctl_info *mci)
dimm->edac_mode = EDAC_SECDED;
dimm->mtype = priv->info.sdram_mode;
dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels;
- dimm->grain = SNPS_EDAC_ERR_GRAIN;
+ dimm->grain = width;
dimm->dtype = priv->info.dev_cfg;
}
}
--
2.41.0

2023-09-21 02:45:40

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 13/18] EDAC/synopsys: Introduce System/SDRAM address translation interface

Currently the address translation is performed only in the framework of
the ECC poison procedures. The available infrastructure is utilized for
the device and driver debugging. Meanwhile it would be very useful to know
not only the SDRAM address of the runtime ECC errors, but the originating
system address accepted on the AXI/AHB ports of the DW uMCTL2 DDR
controller. In order to be able to do so the currently available
infrastructure needs to be properly updated.

First of all move it away from under the EDAC_DEBUG config, since it will
be always utilized by the driver. Secondly for the sake of the code
simplification split up the translation procedure into the next three
stages:
1. System<->Application address translation (just type cast for now).
2. Application<->HIF address translation (DQ-bus width based address
shift).
3. HIF<->SDRAM address translation (ADDRMAPx-based mapping).

The suggested implementation supports the 1->3 translation only in the
same way as it was before this modification (the backward address
translation will be added later). Semantically it's the same except the
next four aspects: ff-value is used as a marker of the unmapped HIF/SDRAM
bits instead of zero-value which in some cases is a valid mapping bit id;
DQ-width is used to perform the Application<->HIF address translation
instead of the fixed 64-bit DQ-bus width assumption; the HIF/SDRAM address
translation procedure searches through the whole dimensions width instead
of stopping at the first unmapped bit since in general some of the
row/column/bank/etc bits (especially the column bits, like b10) can be
left unmapped; the number of supported ranks is extended to four - maximum
possible value.

Note while at it the code is simplified a bit: encapsulate the mapping
table into a dedicated structure (snps_hif_sdram_map); use the FIELD_GET()
helper to get ADDRMAP CSR fields; define more descriptive max-value
macros.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/edac/synopsys_edac.c | 750 +++++++++++++++++++++--------------
1 file changed, 453 insertions(+), 297 deletions(-)

diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index 7376a0fc6394..204d7f1fc7e2 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -153,17 +153,28 @@
#define ECC_CEPOISON_MASK GENMASK(1, 0)
#define ECC_UEPOISON_MASK BIT(0)

-/* DDRC Device config shifts/masks */
-#define DDR_MAX_ROW_SHIFT 18
-#define DDR_MAX_COL_SHIFT 14
-#define DDR_MAX_BANK_SHIFT 3
-#define DDR_MAX_BANKGRP_SHIFT 2
-
-#define ROW_MAX_VAL_MASK 0xF
-#define COL_MAX_VAL_MASK 0xF
-#define BANK_MAX_VAL_MASK 0x1F
-#define BANKGRP_MAX_VAL_MASK 0x1F
-#define RANK_MAX_VAL_MASK 0x1F
+/* DDRC address mapping parameters */
+#define DDR_ADDRMAP_NREGS 12
+
+#define DDR_MAX_ROW_WIDTH 18
+#define DDR_MAX_COL_WIDTH 14
+#define DDR_MAX_BANK_WIDTH 3
+#define DDR_MAX_BANKGRP_WIDTH 2
+#define DDR_MAX_RANK_WIDTH 2
+
+#define DDR_ADDRMAP_B0_M15 GENMASK(3, 0)
+#define DDR_ADDRMAP_B8_M15 GENMASK(11, 8)
+#define DDR_ADDRMAP_B16_M15 GENMASK(19, 16)
+#define DDR_ADDRMAP_B24_M15 GENMASK(27, 24)
+
+#define DDR_ADDRMAP_B0_M31 GENMASK(4, 0)
+#define DDR_ADDRMAP_B8_M31 GENMASK(12, 8)
+#define DDR_ADDRMAP_B16_M31 GENMASK(20, 16)
+#define DDR_ADDRMAP_B24_M31 GENMASK(28, 24)
+
+#define DDR_ADDRMAP_UNUSED ((u8)-1)
+#define DDR_ADDRMAP_MAX_15 DDR_ADDRMAP_B0_M15
+#define DDR_ADDRMAP_MAX_31 DDR_ADDRMAP_B0_M31

#define ROW_B0_BASE 6
#define ROW_B1_BASE 7
@@ -205,6 +216,7 @@
#define BANKGRP_B1_BASE 3

#define RANK_B0_BASE 6
+#define RANK_B1_BASE 7

/* ZynqMP DDR QOS Interrupt register definitions */
#define ZYNQMP_DDR_QOS_UE_MASK BIT(2)
@@ -296,6 +308,41 @@ struct snps_ddrc_info {
unsigned int ranks;
};

+/**
+ * struct snps_hif_sdram_map - HIF/SDRAM mapping table.
+ * @row: HIF bit offsets used as row address bits.
+ * @col: HIF bit offsets used as column address bits.
+ * @bank: HIF bit offsets used as bank address bits.
+ * @bankgrp: HIF bit offsets used as bank group address bits.
+ * @rank: HIF bit offsets used as rank address bits.
+ *
+ * For example, row[0] = 6 means row bit #0 is encoded by the HIF
+ * address bit #6 and vice-versa.
+ */
+struct snps_hif_sdram_map {
+ u8 row[DDR_MAX_ROW_WIDTH];
+ u8 col[DDR_MAX_COL_WIDTH];
+ u8 bank[DDR_MAX_BANK_WIDTH];
+ u8 bankgrp[DDR_MAX_BANKGRP_WIDTH];
+ u8 rank[DDR_MAX_RANK_WIDTH];
+};
+
+/**
+ * struct snps_sdram_addr - SDRAM address.
+ * @row: Row number.
+ * @col: Column number.
+ * @bank: Bank number.
+ * @bankgrp: Bank group number.
+ * @rank: Rank number.
+ */
+struct snps_sdram_addr {
+ u16 row;
+ u16 col;
+ u8 bank;
+ u8 bankgrp;
+ u8 rank;
+};
+
/**
* struct snps_ecc_error_info - ECC error log information.
* @row: Row number.
@@ -335,20 +382,17 @@ struct snps_ecc_status {
/**
* struct snps_edac_priv - DDR memory controller private data.
* @info: DDR controller config info.
+ * @hif_sdram_map: HIF/SDRAM mapping table.
* @pdev: Platform device.
* @baseaddr: Base address of the DDR controller.
* @reglock: Concurrent CSRs access lock.
* @message: Buffer for framing the event specific info.
* @stat: ECC status information.
* @poison_addr: Data poison address.
- * @row_shift: Bit shifts for row bit.
- * @col_shift: Bit shifts for column bit.
- * @bank_shift: Bit shifts for bank bit.
- * @bankgrp_shift: Bit shifts for bank group bit.
- * @rank_shift: Bit shifts for rank bit.
*/
struct snps_edac_priv {
struct snps_ddrc_info info;
+ struct snps_hif_sdram_map hif_sdram_map;
struct platform_device *pdev;
void __iomem *baseaddr;
spinlock_t reglock;
@@ -356,14 +400,97 @@ struct snps_edac_priv {
struct snps_ecc_status stat;
#ifdef CONFIG_EDAC_DEBUG
ulong poison_addr;
- u32 row_shift[18];
- u32 col_shift[14];
- u32 bank_shift[3];
- u32 bankgrp_shift[2];
- u32 rank_shift[1];
#endif
};

+/**
+ * snps_map_app_to_hif - Map Application address to HIF address.
+ * @priv: DDR memory controller private instance data.
+ * @app: Application address (source).
+ * @hif: HIF address (destination).
+ *
+ * HIF address is used to perform the DQ bus width aligned burst transactions.
+ * So in order to perform the Application-to-HIF address translation we just
+ * need to discard the SDRAM-word bits of the Application address.
+ */
+static void snps_map_app_to_hif(struct snps_edac_priv *priv,
+ u64 app, u64 *hif)
+{
+ *hif = app >> priv->info.dq_width;
+}
+
+/**
+ * snps_map_hif_to_sdram - Map HIF address to SDRAM address.
+ * @priv: DDR memory controller private instance data.
+ * @hif: HIF address (source).
+ * @sdram: SDRAM address (destination).
+ *
+ * HIF-SDRAM address mapping is configured with the ADDRMAPx registers, Based
+ * on the CSRs value the HIF address bits are mapped to the corresponding bits
+ * in the SDRAM rank/bank/column/row. If an SDRAM address bit is unused (there
+ * is no any HIF address bit corresponding to it) it will be set to zero. Using
+ * this fact we can freely set the output SDRAM address with zeros and walk
+ * over the set HIF address bits only. Similarly the unmapped HIF address bits
+ * are just ignored.
+ */
+static void snps_map_hif_to_sdram(struct snps_edac_priv *priv,
+ u64 hif, struct snps_sdram_addr *sdram)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
+
+ sdram->row = 0;
+ for (i = 0; i < DDR_MAX_ROW_WIDTH; i++) {
+ if (map->row[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->row[i]))
+ sdram->row |= BIT(i);
+ }
+
+ sdram->col = 0;
+ for (i = 0; i < DDR_MAX_COL_WIDTH; i++) {
+ if (map->col[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->col[i]))
+ sdram->col |= BIT(i);
+ }
+
+ sdram->bank = 0;
+ for (i = 0; i < DDR_MAX_BANK_WIDTH; i++) {
+ if (map->bank[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->bank[i]))
+ sdram->bank |= BIT(i);
+ }
+
+ sdram->bankgrp = 0;
+ for (i = 0; i < DDR_MAX_BANKGRP_WIDTH; i++) {
+ if (map->bankgrp[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->bankgrp[i]))
+ sdram->bankgrp |= BIT(i);
+ }
+
+ sdram->rank = 0;
+ for (i = 0; i < DDR_MAX_RANK_WIDTH; i++) {
+ if (map->rank[i] != DDR_ADDRMAP_UNUSED && hif & BIT(map->rank[i]))
+ sdram->rank |= BIT(i);
+ }
+}
+
+/**
+ * snps_map_sys_to_sdram - Map System address to SDRAM address.
+ * @priv: DDR memory controller private instance data.
+ * @sys: System address (source).
+ * @sdram: SDRAM address (destination).
+ *
+ * Perform a full mapping of the system address (detected on the controller
+ * ports) to the SDRAM address tuple row/column/bank/etc.
+ */
+static void snps_map_sys_to_sdram(struct snps_edac_priv *priv,
+ dma_addr_t sys, struct snps_sdram_addr *sdram)
+{
+ u64 app, hif;
+
+ app = sys;
+
+ snps_map_app_to_hif(priv, app, &hif);
+
+ snps_map_hif_to_sdram(priv, hif, sdram);
+}
+
/**
* snps_get_bitpos - Get DQ-bus corrected bit position.
* @syndrome: Error syndrome.
@@ -755,6 +882,301 @@ static int snps_get_ddrc_info(struct snps_edac_priv *priv)
return init_plat ? init_plat(priv) : 0;
}

+/**
+ * snps_get_hif_row_map - Get HIF/SDRAM-row address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
+ *
+ * SDRAM-row address is defined by the fields in the ADDRMAP[5-7,9-11]
+ * registers. Those fields value indicate the HIF address bits used to encode
+ * the DDR row address.
+ */
+static void snps_get_hif_row_map(struct snps_edac_priv *priv, u32 *addrmap)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ u8 map_row_b2_10;
+ int i;
+
+ for (i = 0; i < DDR_MAX_ROW_WIDTH; i++)
+ map->row[i] = DDR_ADDRMAP_UNUSED;
+
+ map->row[0] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[5]) + ROW_B0_BASE;
+ map->row[1] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[5]) + ROW_B1_BASE;
+
+ map_row_b2_10 = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[5]);
+ if (map_row_b2_10 != DDR_ADDRMAP_MAX_15) {
+ for (i = 2; i < 11; i++)
+ map->row[i] = map_row_b2_10 + i + ROW_B0_BASE;
+ } else {
+ map->row[2] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[9]) + ROW_B2_BASE;
+ map->row[3] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[9]) + ROW_B3_BASE;
+ map->row[4] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[9]) + ROW_B4_BASE;
+ map->row[5] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[9]) + ROW_B5_BASE;
+ map->row[6] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[10]) + ROW_B6_BASE;
+ map->row[7] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[10]) + ROW_B7_BASE;
+ map->row[8] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[10]) + ROW_B8_BASE;
+ map->row[9] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[10]) + ROW_B9_BASE;
+ map->row[10] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[11]) + ROW_B10_BASE;
+ }
+
+ map->row[11] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[5]);
+ map->row[11] = map->row[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[11] + ROW_B11_BASE;
+
+ map->row[12] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[6]);
+ map->row[12] = map->row[12] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[12] + ROW_B12_BASE;
+
+ map->row[13] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[6]);
+ map->row[13] = map->row[13] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[13] + ROW_B13_BASE;
+
+ map->row[14] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[6]);
+ map->row[14] = map->row[14] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[14] + ROW_B14_BASE;
+
+ map->row[15] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[6]);
+ map->row[15] = map->row[15] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[15] + ROW_B15_BASE;
+
+ if (priv->info.sdram_mode == MEM_DDR4 || priv->info.sdram_mode == MEM_LPDDR4) {
+ map->row[16] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[7]);
+ map->row[16] = map->row[16] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[16] + ROW_B16_BASE;
+
+ map->row[17] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[7]);
+ map->row[17] = map->row[17] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->row[17] + ROW_B17_BASE;
+ }
+}
+
+/**
+ * snps_get_hif_col_map - Get HIF/SDRAM-column address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
+ *
+ * SDRAM-column address is defined by the fields in the ADDRMAP[2-4]
+ * registers. Those fields value indicate the HIF address bits used to encode
+ * the DDR row address.
+ */
+static void snps_get_hif_col_map(struct snps_edac_priv *priv, u32 *addrmap)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
+
+ for (i = 0; i < DDR_MAX_COL_WIDTH; i++)
+ map->col[i] = DDR_ADDRMAP_UNUSED;
+
+ map->col[0] = 0;
+ map->col[1] = 1;
+ map->col[2] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[2]) + COL_B2_BASE;
+ map->col[3] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[2]) + COL_B3_BASE;
+
+ map->col[4] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[2]);
+ map->col[4] = map->col[4] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[4] + COL_B4_BASE;
+
+ map->col[5] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[2]);
+ map->col[5] = map->col[5] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[5] + COL_B5_BASE;
+
+ map->col[6] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[3]);
+ map->col[6] = map->col[6] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[6] + COL_B6_BASE;
+
+ map->col[7] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[3]);
+ map->col[7] = map->col[7] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[7] + COL_B7_BASE;
+
+ map->col[8] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[3]);
+ map->col[8] = map->col[8] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[8] + COL_B8_BASE;
+
+ map->col[9] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[3]);
+ map->col[9] = map->col[9] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[9] + COL_B9_BASE;
+
+ if (priv->info.dq_mode) {
+ for (i = 9; i > priv->info.dq_mode; i--) {
+ map->col[i] = map->col[i - priv->info.dq_mode];
+ map->col[i - priv->info.dq_mode] = DDR_ADDRMAP_UNUSED;
+ }
+ }
+
+ /*
+ * Per JEDEC DDR2/3/4/mDDR specification, column address bit 10 is
+ * reserved for indicating auto-precharge, and hence no source
+ * address bit can be mapped to col[10].
+ * Per JEDEC specification, column address bit 12 is reserved
+ * for the Burst-chop status, so no source address bit mapping
+ * for col[12] either.
+ */
+ if (priv->info.dq_mode == SNPS_DQ_FULL) {
+ if (priv->info.sdram_mode == MEM_LPDDR3) {
+ map->col[10] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[4]);
+ map->col[10] = map->col[10] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[10] + COL_B10_BASE;
+
+ map->col[11] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[4]);
+ map->col[11] = map->col[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[11] + COL_B11_BASE;
+ } else {
+ map->col[11] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[4]);
+ map->col[11] = map->col[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[11] + COL_B10_BASE;
+
+ map->col[13] = FIELD_GET(DDR_ADDRMAP_B8_M15, addrmap[4]);
+ map->col[13] = map->col[13] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[13] + COL_B11_BASE;
+ }
+ } else if (priv->info.dq_mode == SNPS_DQ_HALF) {
+ if (priv->info.sdram_mode == MEM_LPDDR3) {
+ map->col[10] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[3]);
+ map->col[10] = map->col[10] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[10] + COL_B9_BASE;
+
+ map->col[11] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[4]);
+ map->col[11] = map->col[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[11] + COL_B10_BASE;
+ } else {
+ map->col[11] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[3]);
+ map->col[11] = map->col[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[11] + COL_B9_BASE;
+
+ map->col[13] = FIELD_GET(DDR_ADDRMAP_B0_M15, addrmap[4]);
+ map->col[13] = map->col[13] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[13] + COL_B10_BASE;
+ }
+ } else {
+ if (priv->info.sdram_mode == MEM_LPDDR3) {
+ map->col[10] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[3]);
+ map->col[10] = map->col[10] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[10] + COL_B8_BASE;
+
+ map->col[11] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[3]);
+ map->col[11] = map->col[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[11] + COL_B9_BASE;
+ } else {
+ map->col[11] = FIELD_GET(DDR_ADDRMAP_B16_M15, addrmap[3]);
+ map->col[11] = map->col[11] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[11] + COL_B8_BASE;
+
+ map->col[11] = FIELD_GET(DDR_ADDRMAP_B24_M15, addrmap[3]);
+ map->col[13] = map->col[13] == DDR_ADDRMAP_MAX_15 ?
+ DDR_ADDRMAP_UNUSED : map->col[13] + COL_B9_BASE;
+ }
+ }
+}
+
+/**
+ * snps_get_hif_bank_map - Get HIF/SDRAM-bank address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
+ *
+ * SDRAM-bank address is defined by the fields in the ADDRMAP[1]
+ * register. Those fields value indicate the HIF address bits used to encode
+ * the DDR bank address.
+ */
+static void snps_get_hif_bank_map(struct snps_edac_priv *priv, u32 *addrmap)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
+
+ for (i = 0; i < DDR_MAX_BANK_WIDTH; i++)
+ map->bank[i] = DDR_ADDRMAP_UNUSED;
+
+ map->bank[0] = FIELD_GET(DDR_ADDRMAP_B0_M31, addrmap[1]) + BANK_B0_BASE;
+ map->bank[1] = FIELD_GET(DDR_ADDRMAP_B8_M31, addrmap[1]) + BANK_B1_BASE;
+
+ map->bank[2] = FIELD_GET(DDR_ADDRMAP_B16_M31, addrmap[1]);
+ map->bank[2] = map->bank[2] == DDR_ADDRMAP_MAX_31 ?
+ DDR_ADDRMAP_UNUSED : map->bank[2] + BANK_B2_BASE;
+}
+
+/**
+ * snps_get_hif_bankgrp_map - Get HIF/SDRAM-bank group address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
+ *
+ * SDRAM-bank group address is defined by the fields in the ADDRMAP[8]
+ * register. Those fields value indicate the HIF address bits used to encode
+ * the DDR bank group address.
+ */
+static void snps_get_hif_bankgrp_map(struct snps_edac_priv *priv, u32 *addrmap)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
+
+ for (i = 0; i < DDR_MAX_BANKGRP_WIDTH; i++)
+ map->bankgrp[i] = DDR_ADDRMAP_UNUSED;
+
+ /* Bank group signals are available on the DDR4 memory only */
+ if (priv->info.sdram_mode != MEM_DDR4)
+ return;
+
+ map->bankgrp[0] = FIELD_GET(DDR_ADDRMAP_B0_M31, addrmap[8]) + BANKGRP_B0_BASE;
+
+ map->bankgrp[1] = FIELD_GET(DDR_ADDRMAP_B8_M31, addrmap[8]);
+ map->bankgrp[1] = map->bankgrp[1] == DDR_ADDRMAP_MAX_31 ?
+ DDR_ADDRMAP_UNUSED : map->bankgrp[1] + BANKGRP_B1_BASE;
+}
+
+/**
+ * snps_get_hif_rank_map - Get HIF/SDRAM-rank address map.
+ * @priv: DDR memory controller private instance data.
+ * @addrmap: Array with ADDRMAP registers value.
+ *
+ * SDRAM-rank address is defined by the fields in the ADDRMAP[0]
+ * register. Those fields value indicate the HIF address bits used to encode
+ * the DDR rank address.
+ */
+static void snps_get_hif_rank_map(struct snps_edac_priv *priv, u32 *addrmap)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
+
+ for (i = 0; i < DDR_MAX_RANK_WIDTH; i++)
+ map->rank[i] = DDR_ADDRMAP_UNUSED;
+
+ if (priv->info.ranks > 1) {
+ map->rank[0] = FIELD_GET(DDR_ADDRMAP_B0_M31, addrmap[0]);
+ map->rank[0] = map->rank[0] == DDR_ADDRMAP_MAX_31 ?
+ DDR_ADDRMAP_UNUSED : map->rank[0] + RANK_B0_BASE;
+ }
+
+ if (priv->info.ranks > 2) {
+ map->rank[1] = FIELD_GET(DDR_ADDRMAP_B8_M31, addrmap[0]);
+ map->rank[1] = map->rank[1] == DDR_ADDRMAP_MAX_31 ?
+ DDR_ADDRMAP_UNUSED : map->rank[1] + RANK_B1_BASE;
+ }
+}
+
+/**
+ * snps_get_addr_map - Get HIF/SDRAM/etc address map from CSRs.
+ * @priv: DDR memory controller private instance data.
+ *
+ * Parse the controller registers content creating the addresses mapping tables.
+ * They will be used for the erroneous and poison addresses encode/decode.
+ */
+static void snps_get_addr_map(struct snps_edac_priv *priv)
+{
+ u32 regval[DDR_ADDRMAP_NREGS];
+ int i;
+
+ for (i = 0; i < DDR_ADDRMAP_NREGS; i++)
+ regval[i] = readl(priv->baseaddr + DDR_ADDRMAP0_OFST + i * 4);
+
+ snps_get_hif_row_map(priv, regval);
+
+ snps_get_hif_col_map(priv, regval);
+
+ snps_get_hif_bank_map(priv, regval);
+
+ snps_get_hif_bankgrp_map(priv, regval);
+
+ snps_get_hif_rank_map(priv, regval);
+}
+
/**
* snps_init_csrows - Initialize the csrow data.
* @mci: EDAC memory controller instance.
@@ -940,285 +1362,21 @@ DEFINE_SHOW_ATTRIBUTE(snps_ddrc_info);
*/
static void snps_data_poison_setup(struct snps_edac_priv *priv)
{
- int col = 0, row = 0, bank = 0, bankgrp = 0, rank = 0, regval;
- int index;
- ulong hif_addr = 0;
-
- hif_addr = priv->poison_addr >> 3;
-
- for (index = 0; index < DDR_MAX_ROW_SHIFT; index++) {
- if (priv->row_shift[index])
- row |= (((hif_addr >> priv->row_shift[index]) &
- BIT(0)) << index);
- else
- break;
- }
-
- for (index = 0; index < DDR_MAX_COL_SHIFT; index++) {
- if (priv->col_shift[index] || index < 3)
- col |= (((hif_addr >> priv->col_shift[index]) &
- BIT(0)) << index);
- else
- break;
- }
-
- for (index = 0; index < DDR_MAX_BANK_SHIFT; index++) {
- if (priv->bank_shift[index])
- bank |= (((hif_addr >> priv->bank_shift[index]) &
- BIT(0)) << index);
- else
- break;
- }
-
- for (index = 0; index < DDR_MAX_BANKGRP_SHIFT; index++) {
- if (priv->bankgrp_shift[index])
- bankgrp |= (((hif_addr >> priv->bankgrp_shift[index])
- & BIT(0)) << index);
- else
- break;
- }
+ struct snps_sdram_addr sdram;
+ u32 regval;

- if (priv->rank_shift[0])
- rank = (hif_addr >> priv->rank_shift[0]) & BIT(0);
+ snps_map_sys_to_sdram(priv, priv->poison_addr, &sdram);

- regval = FIELD_PREP(ECC_POISON0_RANK_MASK, rank) |
- FIELD_PREP(ECC_POISON0_COL_MASK, col);
+ regval = FIELD_PREP(ECC_POISON0_RANK_MASK, sdram.rank) |
+ FIELD_PREP(ECC_POISON0_COL_MASK, sdram.col);
writel(regval, priv->baseaddr + ECC_POISON0_OFST);

- regval = FIELD_PREP(ECC_POISON1_BANKGRP_MASK, bankgrp) |
- FIELD_PREP(ECC_POISON1_BANK_MASK, bank) |
- FIELD_PREP(ECC_POISON1_ROW_MASK, row);
+ regval = FIELD_PREP(ECC_POISON1_BANKGRP_MASK, sdram.bankgrp) |
+ FIELD_PREP(ECC_POISON1_BANK_MASK, sdram.bank) |
+ FIELD_PREP(ECC_POISON1_ROW_MASK, sdram.row);
writel(regval, priv->baseaddr + ECC_POISON1_OFST);
}

-static void snps_setup_row_address_map(struct snps_edac_priv *priv, u32 *addrmap)
-{
- u32 addrmap_row_b2_10;
- int index;
-
- priv->row_shift[0] = (addrmap[5] & ROW_MAX_VAL_MASK) + ROW_B0_BASE;
- priv->row_shift[1] = ((addrmap[5] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B1_BASE;
-
- addrmap_row_b2_10 = (addrmap[5] >> 16) & ROW_MAX_VAL_MASK;
- if (addrmap_row_b2_10 != ROW_MAX_VAL_MASK) {
- for (index = 2; index < 11; index++)
- priv->row_shift[index] = addrmap_row_b2_10 +
- index + ROW_B0_BASE;
-
- } else {
- priv->row_shift[2] = (addrmap[9] &
- ROW_MAX_VAL_MASK) + ROW_B2_BASE;
- priv->row_shift[3] = ((addrmap[9] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B3_BASE;
- priv->row_shift[4] = ((addrmap[9] >> 16) &
- ROW_MAX_VAL_MASK) + ROW_B4_BASE;
- priv->row_shift[5] = ((addrmap[9] >> 24) &
- ROW_MAX_VAL_MASK) + ROW_B5_BASE;
- priv->row_shift[6] = (addrmap[10] &
- ROW_MAX_VAL_MASK) + ROW_B6_BASE;
- priv->row_shift[7] = ((addrmap[10] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B7_BASE;
- priv->row_shift[8] = ((addrmap[10] >> 16) &
- ROW_MAX_VAL_MASK) + ROW_B8_BASE;
- priv->row_shift[9] = ((addrmap[10] >> 24) &
- ROW_MAX_VAL_MASK) + ROW_B9_BASE;
- priv->row_shift[10] = (addrmap[11] &
- ROW_MAX_VAL_MASK) + ROW_B10_BASE;
- }
-
- priv->row_shift[11] = (((addrmap[5] >> 24) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[5] >> 24) &
- ROW_MAX_VAL_MASK) + ROW_B11_BASE);
- priv->row_shift[12] = ((addrmap[6] & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : ((addrmap[6] &
- ROW_MAX_VAL_MASK) + ROW_B12_BASE);
- priv->row_shift[13] = (((addrmap[6] >> 8) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B13_BASE);
- priv->row_shift[14] = (((addrmap[6] >> 16) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 16) &
- ROW_MAX_VAL_MASK) + ROW_B14_BASE);
- priv->row_shift[15] = (((addrmap[6] >> 24) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 24) &
- ROW_MAX_VAL_MASK) + ROW_B15_BASE);
-
- if (priv->info.sdram_mode == MEM_DDR4 || priv->info.sdram_mode == MEM_LPDDR4) {
- priv->row_shift[16] = ((addrmap[7] & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : ((addrmap[7] &
- ROW_MAX_VAL_MASK) + ROW_B16_BASE);
- priv->row_shift[17] = (((addrmap[7] >> 8) & ROW_MAX_VAL_MASK) ==
- ROW_MAX_VAL_MASK) ? 0 : (((addrmap[7] >> 8) &
- ROW_MAX_VAL_MASK) + ROW_B17_BASE);
- }
-}
-
-static void snps_setup_column_address_map(struct snps_edac_priv *priv, u32 *addrmap)
-{
- int index;
-
- priv->col_shift[0] = 0;
- priv->col_shift[1] = 1;
- priv->col_shift[2] = (addrmap[2] & COL_MAX_VAL_MASK) + COL_B2_BASE;
- priv->col_shift[3] = ((addrmap[2] >> 8) &
- COL_MAX_VAL_MASK) + COL_B3_BASE;
- priv->col_shift[4] = (((addrmap[2] >> 16) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[2] >> 16) &
- COL_MAX_VAL_MASK) + COL_B4_BASE);
- priv->col_shift[5] = (((addrmap[2] >> 24) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[2] >> 24) &
- COL_MAX_VAL_MASK) + COL_B5_BASE);
- priv->col_shift[6] = ((addrmap[3] & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : ((addrmap[3] &
- COL_MAX_VAL_MASK) + COL_B6_BASE);
- priv->col_shift[7] = (((addrmap[3] >> 8) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 8) &
- COL_MAX_VAL_MASK) + COL_B7_BASE);
- priv->col_shift[8] = (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 16) &
- COL_MAX_VAL_MASK) + COL_B8_BASE);
- priv->col_shift[9] = (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) ==
- COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) + COL_B9_BASE);
- if (priv->info.dq_mode == SNPS_DQ_FULL) {
- if (priv->info.sdram_mode == MEM_LPDDR3) {
- priv->col_shift[10] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- priv->col_shift[11] = (((addrmap[4] >> 8) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[4] >> 8) & COL_MAX_VAL_MASK) +
- COL_B11_BASE);
- } else {
- priv->col_shift[11] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- priv->col_shift[13] = (((addrmap[4] >> 8) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[4] >> 8) & COL_MAX_VAL_MASK) +
- COL_B11_BASE);
- }
- } else if (priv->info.dq_mode == SNPS_DQ_HALF) {
- if (priv->info.sdram_mode == MEM_LPDDR3) {
- priv->col_shift[10] = (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) +
- COL_B9_BASE);
- priv->col_shift[11] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- } else {
- priv->col_shift[11] = (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) +
- COL_B9_BASE);
- priv->col_shift[13] = ((addrmap[4] &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- ((addrmap[4] & COL_MAX_VAL_MASK) +
- COL_B10_BASE);
- }
- } else {
- if (priv->info.sdram_mode == MEM_LPDDR3) {
- priv->col_shift[10] = (((addrmap[3] >> 16) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) +
- COL_B8_BASE);
- priv->col_shift[11] = (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) +
- COL_B9_BASE);
- } else {
- priv->col_shift[11] = (((addrmap[3] >> 16) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) +
- COL_B8_BASE);
- priv->col_shift[13] = (((addrmap[3] >> 24) &
- COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 :
- (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) +
- COL_B9_BASE);
- }
- }
-
- if (priv->info.dq_mode) {
- for (index = 9; index > priv->info.dq_mode; index--) {
- priv->col_shift[index] =
- priv->col_shift[index - priv->info.dq_mode];
- priv->col_shift[index - priv->info.dq_mode] = 0;
- }
- }
-
-}
-
-static void snps_setup_bank_address_map(struct snps_edac_priv *priv, u32 *addrmap)
-{
- priv->bank_shift[0] = (addrmap[1] & BANK_MAX_VAL_MASK) + BANK_B0_BASE;
- priv->bank_shift[1] = ((addrmap[1] >> 8) &
- BANK_MAX_VAL_MASK) + BANK_B1_BASE;
- priv->bank_shift[2] = (((addrmap[1] >> 16) &
- BANK_MAX_VAL_MASK) == BANK_MAX_VAL_MASK) ? 0 :
- (((addrmap[1] >> 16) & BANK_MAX_VAL_MASK) +
- BANK_B2_BASE);
-
-}
-
-static void snps_setup_bg_address_map(struct snps_edac_priv *priv, u32 *addrmap)
-{
- /* Bank group signals are available on the DDR4 memory only */
- if (priv->info.sdram_mode != MEM_DDR4)
- return;
-
- priv->bankgrp_shift[0] = (addrmap[8] &
- BANKGRP_MAX_VAL_MASK) + BANKGRP_B0_BASE;
- priv->bankgrp_shift[1] = (((addrmap[8] >> 8) & BANKGRP_MAX_VAL_MASK) ==
- BANKGRP_MAX_VAL_MASK) ? 0 : (((addrmap[8] >> 8)
- & BANKGRP_MAX_VAL_MASK) + BANKGRP_B1_BASE);
-
-}
-
-static void snps_setup_rank_address_map(struct snps_edac_priv *priv, u32 *addrmap)
-{
- /* Ranks mapping is unavailable for the single-ranked memory */
- if (priv->info.ranks > 1) {
- priv->rank_shift[0] = ((addrmap[0] & RANK_MAX_VAL_MASK) ==
- RANK_MAX_VAL_MASK) ? 0 : ((addrmap[0] &
- RANK_MAX_VAL_MASK) + RANK_B0_BASE);
- }
-}
-
-/**
- * snps_setup_address_map - Set Address Map by querying ADDRMAP registers.
- * @priv: DDR memory controller private instance data.
- *
- * Set Address Map by querying ADDRMAP registers.
- *
- * Return: none.
- */
-static void snps_setup_address_map(struct snps_edac_priv *priv)
-{
- u32 addrmap[12];
- int index;
-
- for (index = 0; index < 12; index++) {
- u32 addrmap_offset;
-
- addrmap_offset = DDR_ADDRMAP0_OFST + (index * 4);
- addrmap[index] = readl(priv->baseaddr + addrmap_offset);
- }
-
- snps_setup_row_address_map(priv, addrmap);
-
- snps_setup_column_address_map(priv, addrmap);
-
- snps_setup_bank_address_map(priv, addrmap);
-
- snps_setup_bg_address_map(priv, addrmap);
-
- snps_setup_rank_address_map(priv, addrmap);
-}
-
static ssize_t snps_inject_data_error_read(struct file *filep, char __user *ubuf,
size_t size, loff_t *offp)
{
@@ -1311,10 +1469,6 @@ SNPS_DEBUGFS_FOPS(snps_inject_data_poison, snps_inject_data_poison_read,
*/
static void snps_create_debugfs_nodes(struct mem_ctl_info *mci)
{
- struct snps_edac_priv *priv = mci->pvt_info;
-
- snps_setup_address_map(priv);
-
edac_debugfs_create_file("ddrc_info", 0400, mci->debugfs, mci,
&snps_ddrc_info_fops);

@@ -1354,6 +1508,8 @@ static int snps_mc_probe(struct platform_device *pdev)
if (rc)
return rc;

+ snps_get_addr_map(priv);
+
mci = snps_mc_create(priv);
if (IS_ERR(mci))
return PTR_ERR(mci);
--
2.41.0

2023-09-21 02:58:56

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 11/18] EDAC/synopsys: Pass syndrome to EDAC error handler

EDAC MC error handler permits specifying a corrected error syndrome which
then will be printed as a part of the generic error message. Since it's
available in the error info structure pass it to the
edac_mc_handle_error() function.

Signed-off-by: Serge Semin <[email protected]>

---

Changelog v4:
- Get syndrome from the ECCSTAT.ecc_corrected_bit_num field rather than
from ECCCSYN2. The later CSR in fact contains ECC.
---
drivers/edac/synopsys_edac.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index e08cb30b7a7d..fbf1f8af9788 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -474,7 +474,7 @@ static void snps_handle_error(struct mem_ctl_info *mci, struct snps_ecc_status *
pinf->bitpos, pinf->data);

edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
- p->ce_cnt, 0, 0, 0, 0, 0, -1,
+ p->ce_cnt, 0, 0, pinf->syndrome, 0, 0, -1,
priv->message, "");
}

--
2.41.0

2023-09-21 02:59:17

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 15/18] EDAC/synopsys: Add HIF/SDRAM mapping debugfs node

Since the available address mapping is about to be utilized for the
erroneous SDRAM address decode, before adding such functionality it will
be useful to have a way to get an info regarding the most complicated part
of the address translation - HIF/SDRAM mapping table just in case
something gets wrong in the implemented translation procedures. So add the
DebugFS node which on read returns the HIF/SDRAM mapping table in the
hexdump-like manner: first line contains the HIF address bit position
units, first column contains the HIF address bit position tens, the line
and column intersections have the SDRAM dimension (row/column/bank/etc)
and a bit position used to encode the corresponding HIF address bit.

Note DW uMCTL2 DDRC IP-core doesn't have a parameter to set the HIF
address width. So the maximum value (60 bits) of the UMCTL2_A_ADDRW
synthesize parameter [1] is utilized as the maximum HIF address width.
That parameter defines the controller ports address bus width and in case
if the DQ bus width equals to eight bytes defines the HIF address width
too. So its upper constraints is fully applicable in this case.

[1] DesignWare® Cores Enhanced Universal DDR Memory Controller (uMCTL2)
Databook, Version 3.91a, October 2020, p.515

Signed-off-by: Serge Semin <[email protected]>
---
drivers/edac/synopsys_edac.c | 82 ++++++++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)

diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index a359018c261c..6b8949c66eef 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -156,6 +156,7 @@
/* DDRC address mapping parameters */
#define DDR_ADDRMAP_NREGS 12

+#define DDR_MAX_HIF_WIDTH 60
#define DDR_MAX_ROW_WIDTH 18
#define DDR_MAX_COL_WIDTH 14
#define DDR_MAX_BANK_WIDTH 3
@@ -1324,6 +1325,84 @@ static int snps_ddrc_info_show(struct seq_file *s, void *data)

DEFINE_SHOW_ATTRIBUTE(snps_ddrc_info);

+static u8 snps_find_sdram_dim(struct snps_edac_priv *priv, u8 hif, char *dim)
+{
+ struct snps_hif_sdram_map *map = &priv->hif_sdram_map;
+ int i;
+
+ for (i = 0; i < DDR_MAX_ROW_WIDTH; i++) {
+ if (map->row[i] == hif) {
+ *dim = 'r';
+ return i;
+ }
+ }
+
+ for (i = 0; i < DDR_MAX_COL_WIDTH; i++) {
+ if (map->col[i] == hif) {
+ *dim = 'c';
+ return i;
+ }
+ }
+
+ for (i = 0; i < DDR_MAX_BANK_WIDTH; i++) {
+ if (map->bank[i] == hif) {
+ *dim = 'b';
+ return i;
+ }
+ }
+
+ for (i = 0; i < DDR_MAX_BANKGRP_WIDTH; i++) {
+ if (map->bankgrp[i] == hif) {
+ *dim = 'g';
+ return i;
+ }
+ }
+
+ for (i = 0; i < DDR_MAX_RANK_WIDTH; i++) {
+ if (map->rank[i] == hif) {
+ *dim = 'a';
+ return i;
+ }
+ }
+
+ return DDR_ADDRMAP_UNUSED;
+}
+
+static int snps_hif_sdram_map_show(struct seq_file *s, void *data)
+{
+ struct mem_ctl_info *mci = s->private;
+ struct snps_edac_priv *priv = mci->pvt_info;
+ char dim, buf[SNPS_DBGFS_BUF_LEN];
+ const int line_len = 10;
+ u8 bit;
+ int i;
+
+ seq_printf(s, "%3s", "");
+ for (i = 0; i < line_len; i++)
+ seq_printf(s, " %02d ", i);
+
+ for (i = 0; i < DDR_MAX_HIF_WIDTH; i++) {
+ if (i % line_len == 0)
+ seq_printf(s, "\n%02d ", i);
+
+ bit = snps_find_sdram_dim(priv, i, &dim);
+
+ if (bit != DDR_ADDRMAP_UNUSED)
+ scnprintf(buf, SNPS_DBGFS_BUF_LEN, "%c%hhu", dim, bit);
+ else
+ scnprintf(buf, SNPS_DBGFS_BUF_LEN, "--");
+
+ seq_printf(s, "%3s ", buf);
+ }
+ seq_putc(s, '\n');
+
+ seq_puts(s, "r - row, c - column, b - bank, g - bank group, a - rank\n");
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(snps_hif_sdram_map);
+
/**
* snps_data_poison_setup - Update poison registers.
* @priv: DDR memory controller private instance data.
@@ -1443,6 +1522,9 @@ static void snps_create_debugfs_nodes(struct mem_ctl_info *mci)
edac_debugfs_create_file("ddrc_info", 0400, mci->debugfs, mci,
&snps_ddrc_info_fops);

+ edac_debugfs_create_file("hif_sdram_map", 0400, mci->debugfs, mci,
+ &snps_hif_sdram_map_fops);
+
edac_debugfs_create_file("inject_data_error", 0600, mci->debugfs, mci,
&snps_inject_data_error);

--
2.41.0

2023-09-21 03:46:22

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 08/18] EDAC/synopsys: Parse ADDRMAP[0] CSR for multi-ranks case only

The ADDRMAP[0] CSR contains the SDRAM Rank bits mapping (and memory
channel mapping but it's irrelevant in this case). Obviously they are
applicable for the multi-ranked memory only. If either the attached memory
isn't multi-ranked or the controller simply doesn't support the multi-rank
memory, parsing the ADDRMAP[0] CSR will be not just pointless, but in the
later case erroneous since the CSR fields will contain zeros which will be
perceived by the mapping detection procedure as a valid value. So the
mapping will get to be invalid. Thus make sure the ADDRMAP[0] register is
parsed only if a multi-ranked memory setup has been detected.

Signed-off-by: Serge Semin <[email protected]>
---
drivers/edac/synopsys_edac.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index 5a06038aedcb..e6288e135480 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -1146,9 +1146,12 @@ static void snps_setup_bg_address_map(struct snps_edac_priv *priv, u32 *addrmap)

static void snps_setup_rank_address_map(struct snps_edac_priv *priv, u32 *addrmap)
{
- priv->rank_shift[0] = ((addrmap[0] & RANK_MAX_VAL_MASK) ==
- RANK_MAX_VAL_MASK) ? 0 : ((addrmap[0] &
- RANK_MAX_VAL_MASK) + RANK_B0_BASE);
+ /* Ranks mapping is unavailable for the single-ranked memory */
+ if (priv->info.ranks > 1) {
+ priv->rank_shift[0] = ((addrmap[0] & RANK_MAX_VAL_MASK) ==
+ RANK_MAX_VAL_MASK) ? 0 : ((addrmap[0] &
+ RANK_MAX_VAL_MASK) + RANK_B0_BASE);
+ }
}

/**
--
2.41.0

2023-09-23 20:23:12

by Serge Semin

[permalink] [raw]
Subject: [PATCH v4 03/18] EDAC/synopsys: Extend memtypes supported by controller

In accordance with [1] the DW uMCTL2 DDR controllers can support the next
DDR protocols: LPDDR, (LP)DDR(2|3|4). Even if the controller is configured
to support several of these memory chip types only one of these modes
could be enabled at runtime [2].

Taking all of that into account update the snps_get_mtype() procedure so
the DW uMCTL2 DDRC driver would be able to detect all the claimed to be
supported memory types in accordance with the table defined in [2]. Note
alas it's not possible do determine which MEMC DDR configs were enabled at
the IP-core synthesize. Therefore there is no other choice but to
initialize the EDAC MC mem-types capability field with all the types
claimed to be supported by the IP-core.

[1] DesignWare® Cores Enhanced Universal DDR Memory Controller (uMCTL2)
Databook, Version 3.91a, October 2020, p.501
[2] DesignWare® Cores Enhanced Universal DDR Memory Controller (uMCTL2)
Databook, Version 3.91a, October 2020, p.501

Signed-off-by: Serge Semin <[email protected]>
---
drivers/edac/synopsys_edac.c | 41 ++++++++++++++++++++++++------------
1 file changed, 27 insertions(+), 14 deletions(-)

diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index 10716f365c6f..e08e9f3c81cb 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -102,11 +102,14 @@
#define DDR_MSTR_BUSWIDTH_16 2
#define DDR_MSTR_BUSWIDTH_32 1
#define DDR_MSTR_BUSWIDTH_64 0
+#define DDR_MSTR_MEM_MASK GENMASK(5, 0)
#define DDR_MSTR_MEM_LPDDR4 BIT(5)
#define DDR_MSTR_MEM_DDR4 BIT(4)
#define DDR_MSTR_MEM_LPDDR3 BIT(3)
-#define DDR_MSTR_MEM_DDR2 BIT(2)
+#define DDR_MSTR_MEM_LPDDR2 BIT(2)
+#define DDR_MSTR_MEM_LPDDR BIT(1)
#define DDR_MSTR_MEM_DDR3 BIT(0)
+#define DDR_MSTR_MEM_DDR2 0

/* ECC CFG0 register definitions */
#define ECC_CFG0_MODE_MASK GENMASK(2, 0)
@@ -535,21 +538,29 @@ static u32 snps_get_memsize(void)
*/
static enum mem_type snps_get_mtype(const void __iomem *base)
{
- enum mem_type mt;
- u32 memtype;
+ u32 regval;

- memtype = readl(base + DDR_MSTR_OFST);
+ regval = readl(base + DDR_MSTR_OFST);
+ regval = FIELD_GET(DDR_MSTR_MEM_MASK, regval);

- if ((memtype & DDR_MSTR_MEM_DDR3) || (memtype & DDR_MSTR_MEM_LPDDR3))
- mt = MEM_DDR3;
- else if (memtype & DDR_MSTR_MEM_DDR2)
- mt = MEM_RDDR2;
- else if ((memtype & DDR_MSTR_MEM_LPDDR4) || (memtype & DDR_MSTR_MEM_DDR4))
- mt = MEM_DDR4;
- else
- mt = MEM_EMPTY;
+ switch (regval) {
+ case DDR_MSTR_MEM_DDR2:
+ return MEM_DDR2;
+ case DDR_MSTR_MEM_DDR3:
+ return MEM_DDR3;
+ case DDR_MSTR_MEM_LPDDR:
+ return MEM_LPDDR;
+ case DDR_MSTR_MEM_LPDDR2:
+ return MEM_LPDDR2;
+ case DDR_MSTR_MEM_LPDDR3:
+ return MEM_LPDDR3;
+ case DDR_MSTR_MEM_DDR4:
+ return MEM_DDR4;
+ case DDR_MSTR_MEM_LPDDR4:
+ return MEM_LPDDR4;
+ }

- return mt;
+ return MEM_RESERVED;
}

/**
@@ -597,7 +608,9 @@ static void snps_mc_init(struct mem_ctl_info *mci, struct platform_device *pdev)
platform_set_drvdata(pdev, mci);

/* Initialize controller capabilities and configuration */
- mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2;
+ mci->mtype_cap = MEM_FLAG_LPDDR | MEM_FLAG_DDR2 | MEM_FLAG_LPDDR2 |
+ MEM_FLAG_DDR3 | MEM_FLAG_LPDDR3 |
+ MEM_FLAG_DDR4 | MEM_FLAG_LPDDR4;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->scrub_cap = SCRUB_FLAG_HW_SRC;
mci->scrub_mode = SCRUB_NONE;
--
2.41.0

2023-09-27 15:16:37

by Yujie Liu

[permalink] [raw]
Subject: Re: [PATCH v4 13/18] EDAC/synopsys: Introduce System/SDRAM address translation interface

Hi Serge,

kernel test robot noticed the following build warnings:

[auto build test WARNING on ras/edac-for-next]
[also build test WARNING on linus/master v6.6-rc2 next-20230921]
[cannot apply to xilinx-xlnx/master bp/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Serge-Semin/EDAC-synopsys-Convert-sysfs-nodes-to-debugfs-ones/20230921-035302
base: https://git.kernel.org/pub/scm/linux/kernel/git/ras/ras.git edac-for-next
patch link: https://lore.kernel.org/r/20230920192806.29960-14-fancer.lancer%40gmail.com
patch subject: [PATCH v4 13/18] EDAC/synopsys: Introduce System/SDRAM address translation interface
config: arm64-randconfig-003-20230924 (https://download.01.org/0day-ci/archive/20230924/[email protected]/config)
compiler: aarch64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230924/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/

All warnings (new ones prefixed by >>):

>> drivers/edac/synopsys_edac.c:482:13: warning: 'snps_map_sys_to_sdram' defined but not used [-Wunused-function]
482 | static void snps_map_sys_to_sdram(struct snps_edac_priv *priv,
| ^~~~~~~~~~~~~~~~~~~~~


vim +/snps_map_sys_to_sdram +482 drivers/edac/synopsys_edac.c

07abea90e8d373 Serge Semin 2023-09-20 472
07abea90e8d373 Serge Semin 2023-09-20 473 /**
07abea90e8d373 Serge Semin 2023-09-20 474 * snps_map_sys_to_sdram - Map System address to SDRAM address.
07abea90e8d373 Serge Semin 2023-09-20 475 * @priv: DDR memory controller private instance data.
07abea90e8d373 Serge Semin 2023-09-20 476 * @sys: System address (source).
07abea90e8d373 Serge Semin 2023-09-20 477 * @sdram: SDRAM address (destination).
07abea90e8d373 Serge Semin 2023-09-20 478 *
07abea90e8d373 Serge Semin 2023-09-20 479 * Perform a full mapping of the system address (detected on the controller
07abea90e8d373 Serge Semin 2023-09-20 480 * ports) to the SDRAM address tuple row/column/bank/etc.
07abea90e8d373 Serge Semin 2023-09-20 481 */
07abea90e8d373 Serge Semin 2023-09-20 @482 static void snps_map_sys_to_sdram(struct snps_edac_priv *priv,
07abea90e8d373 Serge Semin 2023-09-20 483 dma_addr_t sys, struct snps_sdram_addr *sdram)
07abea90e8d373 Serge Semin 2023-09-20 484 {
07abea90e8d373 Serge Semin 2023-09-20 485 u64 app, hif;
07abea90e8d373 Serge Semin 2023-09-20 486
07abea90e8d373 Serge Semin 2023-09-20 487 app = sys;
07abea90e8d373 Serge Semin 2023-09-20 488
07abea90e8d373 Serge Semin 2023-09-20 489 snps_map_app_to_hif(priv, app, &hif);
07abea90e8d373 Serge Semin 2023-09-20 490
07abea90e8d373 Serge Semin 2023-09-20 491 snps_map_hif_to_sdram(priv, hif, sdram);
07abea90e8d373 Serge Semin 2023-09-20 492 }
07abea90e8d373 Serge Semin 2023-09-20 493

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki