Re: [PATCH 1/3] EDAC/loongson: Encode node and MC info into mc_idx

Huacai Chen <[email protected]> Mon, 3 Aug 2026 16:25:06 +0800
Newsgroups dev.linux.lists.loongarch,org.kernel.vger.linux-edac,org.kernel.vger.linux-kernel
Message-ID <CAAhV-H4gQ=DZC+TmvMtrtV4FM7wrRibHFu-dAyVkxo0j2Fkbrg@mail.gmail.com>
Hi, Qunqin,

On Thu, Jul 30, 2026 at 2:45=E2=80=AFPM Qunqin Zhao <[email protected]=
> wrote:
>
> From: Wang Jinwei <[email protected]>
>
> On Loongson multi-node systems, memory controllers are distributed
> across multiple nodes. Encode node and MC information into mc_idx
> so that sysfs entries uniquely identify each controller.
>
> The driver gets mc-idx from ACPI _DSD property 'mc-idx' if firmware
> provides it. If not, it falls back to edac_device_alloc_index() for
> legacy firmware compatibility.
>
> The number of memory controllers per node (mc-per-node) is obtained
> from firmware via ACPI _DSD property, with a default of 4 for
> backward compatibility. The node and MC IDs are decoded from mc_idx:
>      node =3D mc_idx / mc_per_node
>      mc =3D mc_idx % mc_per_node
>
> For example, with mc-per-node=3D4:
>      mc-idx 0-3   -> node0 mc0-mc3
>      mc-idx 4-7   -> node1 mc0-mc3
>      ...
>      mc-idx 28-31 -> node7 mc0-mc3
>
> The dmesg output looks like this=EF=BC=9A
> Before: (no _DSD mc-idx)
> EDAC MC3: 510 CE error on MC#3Channel#0_DIMM#0 (channel:0 slot:0
> page:0x0 offset:0x0 grain:8 syndrome:0x0)
>
> After: (_DSD mc-idx)
> EDAC MC3: 510 CE error on MC#3Channel#0_DIMM#0 (channel:0 slot:0
> page:0x0 offset:0x0 grain:8 syndrome:0x0 - node:0 mc:3)
>
> Cc: Yulong Wang <[email protected]>
> Cc: Dongyan Qian <[email protected]>
> Cc: Chao Li <[email protected]>
> Signed-off-by: Wang Jinwei <[email protected]>
> Signed-off-by: Qunqin Zhao <[email protected]>
> ---
>  drivers/edac/loongson_edac.c | 38 +++++++++++++++++++++++++++++++-----
>  1 file changed, 33 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/edac/loongson_edac.c b/drivers/edac/loongson_edac.c
> index 38745800ed..f2452e98f5 100644
> --- a/drivers/edac/loongson_edac.c
> +++ b/drivers/edac/loongson_edac.c
> @@ -9,6 +9,7 @@
>  #include <linux/io-64-nonatomic-lo-hi.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/property.h>
>  #include "edac_module.h"
>
>  #define ECC_CS_COUNT_REG       0x18
> @@ -23,6 +24,8 @@ struct loongson_edac_pvt {
>          * register state.
>          */
>         int last_ce_count;
> +       int mc_per_node;
I think mcs_per_node is better than mc_per_node.

> +       bool mc_idx_valid;
>  };
>
>  static int read_ecc(struct mem_ctl_info *mci)
> @@ -44,7 +47,8 @@ static int read_ecc(struct mem_ctl_info *mci)
>  static void edac_check(struct mem_ctl_info *mci)
>  {
>         struct loongson_edac_pvt *pvt =3D mci->pvt_info;
> -       int new, add;
> +       char other_detail[64];
Use char other_detail[64] =3D {0}, then we can drop the 'else' branch below=
.

> +       int new, add, node, mc;
>
>         new =3D read_ecc(mci);
>         add =3D new - pvt->last_ce_count;
> @@ -52,8 +56,17 @@ static void edac_check(struct mem_ctl_info *mci)
>         if (add <=3D 0)
>                 return;
>
> +       if (pvt->mc_idx_valid) {
> +               node =3D mci->mc_idx / pvt->mc_per_node;
> +               mc =3D mci->mc_idx % pvt->mc_per_node;
> +               snprintf(other_detail, sizeof(other_detail),
> +                               "node:%d mc:%d", node, mc);
The snprintf() can be put in oneline.

> +       } else {
> +               other_detail[0] =3D '\0';
> +       }
> +
>         edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add,
> -                            0, 0, 0, 0, 0, -1, "error", "");
> +                            0, 0, 0, 0, 0, -1, "error", other_detail);
>  }
>
>  static void dimm_config_init(struct mem_ctl_info *mci)
> @@ -72,20 +85,26 @@ static void dimm_config_init(struct mem_ctl_info *mci=
)
>         dimm->grain =3D 8;
>  }
>
> -static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase)
> +static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase,
> +               bool mc_idx_valid, int mc_per_node)
The function definition can be put in one line.

>  {
>         struct loongson_edac_pvt *pvt =3D mci->pvt_info;
>
>         pvt->ecc_base =3D vbase;
>         pvt->last_ce_count =3D read_ecc(mci);
> +       pvt->mc_idx_valid =3D mc_idx_valid;
> +       pvt->mc_per_node =3D mc_per_node;
>  }
>
>  static int edac_probe(struct platform_device *pdev)
>  {
>         struct edac_mc_layer layers[2];
>         struct mem_ctl_info *mci;
> +       struct device *dev =3D &pdev->dev;
>         void __iomem *vbase;
> +       u32 mc_per_node;
>         int ret;
> +       bool mc_idx_valid;
Use bool mc_idx_valid =3D true, then we can drop the 'else' branch below.


Huacai

>
>         vbase =3D devm_platform_ioremap_resource(pdev, 0);
>         if (IS_ERR(vbase))
> @@ -102,7 +121,6 @@ static int edac_probe(struct platform_device *pdev)
>         if (mci =3D=3D NULL)
>                 return -ENOMEM;
>
> -       mci->mc_idx =3D edac_device_alloc_index();
>         mci->mtype_cap =3D MEM_FLAG_RDDR4;
>         mci->edac_ctl_cap =3D EDAC_FLAG_NONE;
>         mci->edac_cap =3D EDAC_FLAG_NONE;
> @@ -114,7 +132,17 @@ static int edac_probe(struct platform_device *pdev)
>         mci->error_desc.grain =3D 8;
>         mci->edac_check =3D edac_check;
>
> -       pvt_init(mci, vbase);
> +       if (device_property_read_u32(dev, "mc-idx", &mci->mc_idx)) {
> +               mci->mc_idx =3D edac_device_alloc_index();
> +               mc_idx_valid =3D false;
> +       } else {
> +               mc_idx_valid =3D true;
> +       }
> +
> +       if (device_property_read_u32(dev, "mc-per-node", &mc_per_node))
> +               mc_per_node =3D 4;
> +
> +       pvt_init(mci, vbase, mc_idx_valid, mc_per_node);
>         dimm_config_init(mci);
>
>         ret =3D edac_mc_add_mc(mci);
>
> base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
> --
> 2.47.2
>
>