EDAC/amd64: Fix size calculation for Non-Power-of-Two DIMMs

commit a3f3040657417aeadb9622c629d4a0c2693a0f93 upstream.

Each Chip-Select (CS) of a Unified Memory Controller (UMC) on AMD Zen-based
SOCs has an Address Mask and a Secondary Address Mask register associated with
it. The amd64_edac module logs DIMM sizes on a per-UMC per-CS granularity
during init using these two registers.

Currently, the module primarily considers only the Address Mask register for
computing DIMM sizes. The Secondary Address Mask register is only considered
for odd CS. Additionally, if it has been considered, the Address Mask register
is ignored altogether for that CS. For power-of-two DIMMs i.e. DIMMs whose
total capacity is a power of two (32GB, 64GB, etc), this is not an issue
since only the Address Mask register is used.

For non-power-of-two DIMMs i.e., DIMMs whose total capacity is not a power of
two (48GB, 96GB, etc), however, the Secondary Address Mask register is used
in conjunction with the Address Mask register. However, since the module only
considers either of the two registers for a CS, the size computed by the
module is incorrect. The Secondary Address Mask register is not considered for
even CS, and the Address Mask register is not considered for odd CS.

Introduce a new helper function so that both Address Mask and Secondary
Address Mask registers are considered, when valid, for computing DIMM sizes.
Furthermore, also rename some variables for greater clarity.

Fixes: 81f5090db8 ("EDAC/amd64: Support asymmetric dual-rank DIMMs")
Closes: https://lore.kernel.org/dbec22b6-00f2-498b-b70d-ab6f8a5ec87e@natrix.lt
Reported-by: Žilvinas Žaltiena <zilvinas@natrix.lt>
Signed-off-by: Avadhut Naik <avadhut.naik@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Yazen Ghannam <yazen.ghannam@amd.com>
Tested-by: Žilvinas Žaltiena <zilvinas@natrix.lt>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/20250529205013.403450-1-avadhut.naik@amd.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Avadhut Naik
2025-05-29 20:50:04 +00:00
committed by Greg Kroah-Hartman
parent c82c704125
commit e5e6a5aa39

View File

@@ -1475,7 +1475,9 @@ static int umc_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
if (csrow_enabled(2 * dimm + 1, ctrl, pvt)) if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
cs_mode |= CS_ODD_PRIMARY; cs_mode |= CS_ODD_PRIMARY;
/* Asymmetric dual-rank DIMM support. */ if (csrow_sec_enabled(2 * dimm, ctrl, pvt))
cs_mode |= CS_EVEN_SECONDARY;
if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt)) if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
cs_mode |= CS_ODD_SECONDARY; cs_mode |= CS_ODD_SECONDARY;
@@ -1496,12 +1498,13 @@ static int umc_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
return cs_mode; return cs_mode;
} }
static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode, static int calculate_cs_size(u32 mask, unsigned int cs_mode)
int csrow_nr, int dimm)
{ {
u32 msb, weight, num_zero_bits; int msb, weight, num_zero_bits;
u32 addr_mask_deinterleaved; u32 deinterleaved_mask;
int size = 0;
if (!mask)
return 0;
/* /*
* The number of zero bits in the mask is equal to the number of bits * The number of zero bits in the mask is equal to the number of bits
@@ -1514,19 +1517,30 @@ static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode,
* without swapping with the most significant bit. This can be handled * without swapping with the most significant bit. This can be handled
* by keeping the MSB where it is and ignoring the single zero bit. * by keeping the MSB where it is and ignoring the single zero bit.
*/ */
msb = fls(addr_mask_orig) - 1; msb = fls(mask) - 1;
weight = hweight_long(addr_mask_orig); weight = hweight_long(mask);
num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE); num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE);
/* Take the number of zero bits off from the top of the mask. */ /* Take the number of zero bits off from the top of the mask. */
addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1); deinterleaved_mask = GENMASK(msb - num_zero_bits, 1);
edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", deinterleaved_mask);
return (deinterleaved_mask >> 2) + 1;
}
static int __addr_mask_to_cs_size(u32 addr_mask, u32 addr_mask_sec,
unsigned int cs_mode, int csrow_nr, int dimm)
{
int size;
edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm); edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig); edac_dbg(1, " Primary AddrMask: 0x%x\n", addr_mask);
edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
/* Register [31:1] = Address [39:9]. Size is in kBs here. */ /* Register [31:1] = Address [39:9]. Size is in kBs here. */
size = (addr_mask_deinterleaved >> 2) + 1; size = calculate_cs_size(addr_mask, cs_mode);
edac_dbg(1, " Secondary AddrMask: 0x%x\n", addr_mask_sec);
size += calculate_cs_size(addr_mask_sec, cs_mode);
/* Return size in MBs. */ /* Return size in MBs. */
return size >> 10; return size >> 10;
@@ -1535,8 +1549,8 @@ static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode,
static int umc_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, static int umc_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
unsigned int cs_mode, int csrow_nr) unsigned int cs_mode, int csrow_nr)
{ {
u32 addr_mask = 0, addr_mask_sec = 0;
int cs_mask_nr = csrow_nr; int cs_mask_nr = csrow_nr;
u32 addr_mask_orig;
int dimm, size = 0; int dimm, size = 0;
/* No Chip Selects are enabled. */ /* No Chip Selects are enabled. */
@@ -1574,13 +1588,13 @@ static int umc_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
if (!pvt->flags.zn_regs_v2) if (!pvt->flags.zn_regs_v2)
cs_mask_nr >>= 1; cs_mask_nr >>= 1;
/* Asymmetric dual-rank DIMM support. */ if (cs_mode & (CS_EVEN_PRIMARY | CS_ODD_PRIMARY))
if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY)) addr_mask = pvt->csels[umc].csmasks[cs_mask_nr];
addr_mask_orig = pvt->csels[umc].csmasks_sec[cs_mask_nr];
else
addr_mask_orig = pvt->csels[umc].csmasks[cs_mask_nr];
return __addr_mask_to_cs_size(addr_mask_orig, cs_mode, csrow_nr, dimm); if (cs_mode & (CS_EVEN_SECONDARY | CS_ODD_SECONDARY))
addr_mask_sec = pvt->csels[umc].csmasks_sec[cs_mask_nr];
return __addr_mask_to_cs_size(addr_mask, addr_mask_sec, cs_mode, csrow_nr, dimm);
} }
static void umc_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) static void umc_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
@@ -3773,9 +3787,10 @@ static void gpu_get_err_info(struct mce *m, struct err_info *err)
static int gpu_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, static int gpu_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
unsigned int cs_mode, int csrow_nr) unsigned int cs_mode, int csrow_nr)
{ {
u32 addr_mask_orig = pvt->csels[umc].csmasks[csrow_nr]; u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr];
u32 addr_mask_sec = pvt->csels[umc].csmasks_sec[csrow_nr];
return __addr_mask_to_cs_size(addr_mask_orig, cs_mode, csrow_nr, csrow_nr >> 1); return __addr_mask_to_cs_size(addr_mask, addr_mask_sec, cs_mode, csrow_nr, csrow_nr >> 1);
} }
static void gpu_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) static void gpu_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)