Commit d9de5ce8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'edac_updates_for_v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras

Pull EDAC updates from Borislav Petkov:

 - Add a driver for the RAS functionality on Xilinx's on chip memory
   controller

 - Add support for decoding errors from the first and second level
   memory on SKL-based hardware

 - Add support for the memory controllers in Intel Granite Rapids and
   Emerald Rapids machines

 - First round of amd64_edac driver simplification and removal of
   unneeded functionality

 - The usual cleanups and fixes

* tag 'edac_updates_for_v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras:
  EDAC/amd64: Shut up an -Werror,-Wsometimes-uninitialized clang false positive
  EDAC/amd64: Remove early_channel_count()
  EDAC/amd64: Remove PCI Function 0
  EDAC/amd64: Remove PCI Function 6
  EDAC/amd64: Remove scrub rate control for Family 17h and later
  EDAC/amd64: Don't set up EDAC PCI control on Family 17h+
  EDAC/i10nm: Add driver decoder for Sapphire Rapids server
  EDAC/i10nm: Add Intel Granite Rapids server support
  EDAC/i10nm: Make more configurations CPU model specific
  EDAC/i10nm: Add Intel Emerald Rapids server support
  EDAC/skx_common: Delete duplicated and unreachable code
  EDAC/skx_common: Enable EDAC support for the "near" memory
  EDAC/qcom: Add platform_device_id table for module autoloading
  EDAC/zynqmp: Add EDAC support for Xilinx ZynqMP OCM
  dt-bindings: edac: Add bindings for Xilinx ZynqMP OCM
parents 0246725d 28980db9
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/memory-controllers/xlnx,zynqmp-ocmc-1.0.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Xilinx Zynqmp OCM(On-Chip Memory) Controller
maintainers:
- Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
- Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
description: |
The OCM supports 64-bit wide ECC functionality to detect multi-bit errors
and recover from a single-bit memory fault.On a write, if all bytes are
being written, the ECC is generated and written into the ECC RAM along with
the write-data that is written into the data RAM. If one or more bytes are
not written, then the read operation results in an correctable error or
uncorrectable error.
properties:
compatible:
const: xlnx,zynqmp-ocmc-1.0
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
memory-controller@ff960000 {
compatible = "xlnx,zynqmp-ocmc-1.0";
reg = <0xff960000 0x1000>;
interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
};
...@@ -22743,6 +22743,13 @@ F: Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml ...@@ -22743,6 +22743,13 @@ F: Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dpdma.yaml
F: drivers/dma/xilinx/xilinx_dpdma.c F: drivers/dma/xilinx/xilinx_dpdma.c
F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
XILINX ZYNQMP OCM EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,zynqmp-ocmc-1.0.yaml
F: drivers/edac/zynqmp_edac.c
XILINX ZYNQMP PSGTR PHY DRIVER XILINX ZYNQMP PSGTR PHY DRIVER
M: Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com> M: Anurag Kumar Vulisha <anurag.kumar.vulisha@xilinx.com>
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
......
...@@ -542,4 +542,12 @@ config EDAC_DMC520 ...@@ -542,4 +542,12 @@ config EDAC_DMC520
Support for error detection and correction on the Support for error detection and correction on the
SoCs with ARM DMC-520 DRAM controller. SoCs with ARM DMC-520 DRAM controller.
config EDAC_ZYNQMP
tristate "Xilinx ZynqMP OCM Controller"
depends on ARCH_ZYNQMP || COMPILE_TEST
help
This driver supports error detection and correction for the
Xilinx ZynqMP OCM (On Chip Memory) controller. It can also be
built as a module. In that case it will be called zynqmp_edac.
endif # EDAC endif # EDAC
...@@ -84,3 +84,4 @@ obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o ...@@ -84,3 +84,4 @@ obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o
obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o
obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o
obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o
obj-$(CONFIG_EDAC_ZYNQMP) += zynqmp_edac.o
...@@ -182,21 +182,6 @@ static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, ...@@ -182,21 +182,6 @@ static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct,
* other archs, we might not have access to the caches directly. * other archs, we might not have access to the caches directly.
*/ */
static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval)
{
/*
* Fam17h supports scrub values between 0x5 and 0x14. Also, the values
* are shifted down by 0x5, so scrubval 0x5 is written to the register
* as 0x0, scrubval 0x6 as 0x1, etc.
*/
if (scrubval >= 0x5 && scrubval <= 0x14) {
scrubval -= 0x5;
pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF);
pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1);
} else {
pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1);
}
}
/* /*
* Scan the scrub rate mapping table for a close or matching bandwidth value to * Scan the scrub rate mapping table for a close or matching bandwidth value to
* issue. If requested is too big, then use last maximum value found. * issue. If requested is too big, then use last maximum value found.
...@@ -229,9 +214,7 @@ static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) ...@@ -229,9 +214,7 @@ static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate)
scrubval = scrubrates[i].scrubval; scrubval = scrubrates[i].scrubval;
if (pvt->umc) { if (pvt->fam == 0x15 && pvt->model == 0x60) {
__f17h_set_scrubval(pvt, scrubval);
} else if (pvt->fam == 0x15 && pvt->model == 0x60) {
f15h_select_dct(pvt, 0); f15h_select_dct(pvt, 0);
pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F);
f15h_select_dct(pvt, 1); f15h_select_dct(pvt, 1);
...@@ -271,16 +254,7 @@ static int get_scrub_rate(struct mem_ctl_info *mci) ...@@ -271,16 +254,7 @@ static int get_scrub_rate(struct mem_ctl_info *mci)
int i, retval = -EINVAL; int i, retval = -EINVAL;
u32 scrubval = 0; u32 scrubval = 0;
if (pvt->umc) { if (pvt->fam == 0x15) {
amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval);
if (scrubval & BIT(0)) {
amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval);
scrubval &= 0xF;
scrubval += 0x5;
} else {
scrubval = 0;
}
} else if (pvt->fam == 0x15) {
/* Erratum #505 */ /* Erratum #505 */
if (pvt->model < 0x10) if (pvt->model < 0x10)
f15h_select_dct(pvt, 0); f15h_select_dct(pvt, 0);
...@@ -1454,9 +1428,6 @@ static void __dump_misc_regs_df(struct amd64_pvt *pvt) ...@@ -1454,9 +1428,6 @@ static void __dump_misc_regs_df(struct amd64_pvt *pvt)
debug_display_dimm_sizes_df(pvt, i); debug_display_dimm_sizes_df(pvt, i);
} }
edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n",
pvt->dhar, dhar_base(pvt));
} }
/* Display and decode various NB registers for debug purposes. */ /* Display and decode various NB registers for debug purposes. */
...@@ -1491,6 +1462,8 @@ static void __dump_misc_regs(struct amd64_pvt *pvt) ...@@ -1491,6 +1462,8 @@ static void __dump_misc_regs(struct amd64_pvt *pvt)
/* Only if NOT ganged does dclr1 have valid info */ /* Only if NOT ganged does dclr1 have valid info */
if (!dct_ganging_enabled(pvt)) if (!dct_ganging_enabled(pvt))
debug_dump_dramcfg_low(pvt, pvt->dclr1, 1); debug_dump_dramcfg_low(pvt, pvt->dclr1, 1);
edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
} }
/* Display and decode various NB registers for debug purposes. */ /* Display and decode various NB registers for debug purposes. */
...@@ -1501,8 +1474,6 @@ static void dump_misc_regs(struct amd64_pvt *pvt) ...@@ -1501,8 +1474,6 @@ static void dump_misc_regs(struct amd64_pvt *pvt)
else else
__dump_misc_regs(pvt); __dump_misc_regs(pvt);
edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no");
amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz); amd64_info("using x%u syndromes.\n", pvt->ecc_sym_sz);
} }
...@@ -1732,24 +1703,6 @@ static void determine_memory_type(struct amd64_pvt *pvt) ...@@ -1732,24 +1703,6 @@ static void determine_memory_type(struct amd64_pvt *pvt)
pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3; pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
} }
/* Get the number of DCT channels the memory controller is using. */
static int k8_early_channel_count(struct amd64_pvt *pvt)
{
int flag;
if (pvt->ext_model >= K8_REV_F)
/* RevF (NPT) and later */
flag = pvt->dclr0 & WIDTH_128;
else
/* RevE and earlier */
flag = pvt->dclr0 & REVE_WIDTH_128;
/* not used */
pvt->dclr1 = 0;
return (flag) ? 2 : 1;
}
/* On F10h and later ErrAddr is MC4_ADDR[47:1] */ /* On F10h and later ErrAddr is MC4_ADDR[47:1] */
static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m)
{ {
...@@ -2001,69 +1954,6 @@ static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, ...@@ -2001,69 +1954,6 @@ static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
} }
} }
/*
* Get the number of DCT channels in use.
*
* Return:
* number of Memory Channels in operation
* Pass back:
* contents of the DCL0_LOW register
*/
static int f1x_early_channel_count(struct amd64_pvt *pvt)
{
int i, j, channels = 0;
/* On F10h, if we are in 128 bit mode, then we are using 2 channels */
if (pvt->fam == 0x10 && (pvt->dclr0 & WIDTH_128))
return 2;
/*
* Need to check if in unganged mode: In such, there are 2 channels,
* but they are not in 128 bit mode and thus the above 'dclr0' status
* bit will be OFF.
*
* Need to check DCT0[0] and DCT1[0] to see if only one of them has
* their CSEnable bit on. If so, then SINGLE DIMM case.
*/
edac_dbg(0, "Data width is not 128 bits - need more decoding\n");
/*
* Check DRAM Bank Address Mapping values for each DIMM to see if there
* is more than just one DIMM present in unganged mode. Need to check
* both controllers since DIMMs can be placed in either one.
*/
for (i = 0; i < 2; i++) {
u32 dbam = (i ? pvt->dbam1 : pvt->dbam0);
for (j = 0; j < 4; j++) {
if (DBAM_DIMM(j, dbam) > 0) {
channels++;
break;
}
}
}
if (channels > 2)
channels = 2;
amd64_info("MCT channel count: %d\n", channels);
return channels;
}
static int f17_early_channel_count(struct amd64_pvt *pvt)
{
int i, channels = 0;
/* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */
for_each_umc(i)
channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT);
amd64_info("MCT channel count: %d\n", channels);
return channels;
}
static int ddr3_cs_size(unsigned i, bool dct_width) static int ddr3_cs_size(unsigned i, bool dct_width)
{ {
unsigned shift = 0; unsigned shift = 0;
...@@ -2858,7 +2748,6 @@ static struct amd64_family_type family_types[] = { ...@@ -2858,7 +2748,6 @@ static struct amd64_family_type family_types[] = {
.f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, .f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = k8_early_channel_count,
.map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow, .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
.dbam_to_cs = k8_dbam_to_chip_select, .dbam_to_cs = k8_dbam_to_chip_select,
} }
...@@ -2869,7 +2758,6 @@ static struct amd64_family_type family_types[] = { ...@@ -2869,7 +2758,6 @@ static struct amd64_family_type family_types[] = {
.f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM, .f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f1x_early_channel_count,
.map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
.dbam_to_cs = f10_dbam_to_chip_select, .dbam_to_cs = f10_dbam_to_chip_select,
} }
...@@ -2880,7 +2768,6 @@ static struct amd64_family_type family_types[] = { ...@@ -2880,7 +2768,6 @@ static struct amd64_family_type family_types[] = {
.f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2, .f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f1x_early_channel_count,
.map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
.dbam_to_cs = f15_dbam_to_chip_select, .dbam_to_cs = f15_dbam_to_chip_select,
} }
...@@ -2891,7 +2778,6 @@ static struct amd64_family_type family_types[] = { ...@@ -2891,7 +2778,6 @@ static struct amd64_family_type family_types[] = {
.f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2, .f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f1x_early_channel_count,
.map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
.dbam_to_cs = f16_dbam_to_chip_select, .dbam_to_cs = f16_dbam_to_chip_select,
} }
...@@ -2902,7 +2788,6 @@ static struct amd64_family_type family_types[] = { ...@@ -2902,7 +2788,6 @@ static struct amd64_family_type family_types[] = {
.f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2, .f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f1x_early_channel_count,
.map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
.dbam_to_cs = f15_m60h_dbam_to_chip_select, .dbam_to_cs = f15_m60h_dbam_to_chip_select,
} }
...@@ -2913,7 +2798,6 @@ static struct amd64_family_type family_types[] = { ...@@ -2913,7 +2798,6 @@ static struct amd64_family_type family_types[] = {
.f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2, .f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f1x_early_channel_count,
.map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
.dbam_to_cs = f16_dbam_to_chip_select, .dbam_to_cs = f16_dbam_to_chip_select,
} }
...@@ -2924,89 +2808,64 @@ static struct amd64_family_type family_types[] = { ...@@ -2924,89 +2808,64 @@ static struct amd64_family_type family_types[] = {
.f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, .f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f1x_early_channel_count,
.map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
.dbam_to_cs = f16_dbam_to_chip_select, .dbam_to_cs = f16_dbam_to_chip_select,
} }
}, },
[F17_CPUS] = { [F17_CPUS] = {
.ctl_name = "F17h", .ctl_name = "F17h",
.f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size, .dbam_to_cs = f17_addr_mask_to_cs_size,
} }
}, },
[F17_M10H_CPUS] = { [F17_M10H_CPUS] = {
.ctl_name = "F17h_M10h", .ctl_name = "F17h_M10h",
.f0_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size, .dbam_to_cs = f17_addr_mask_to_cs_size,
} }
}, },
[F17_M30H_CPUS] = { [F17_M30H_CPUS] = {
.ctl_name = "F17h_M30h", .ctl_name = "F17h_M30h",
.f0_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6,
.max_mcs = 8, .max_mcs = 8,
.ops = { .ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size, .dbam_to_cs = f17_addr_mask_to_cs_size,
} }
}, },
[F17_M60H_CPUS] = { [F17_M60H_CPUS] = {
.ctl_name = "F17h_M60h", .ctl_name = "F17h_M60h",
.f0_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_17H_M60H_DF_F6,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size, .dbam_to_cs = f17_addr_mask_to_cs_size,
} }
}, },
[F17_M70H_CPUS] = { [F17_M70H_CPUS] = {
.ctl_name = "F17h_M70h", .ctl_name = "F17h_M70h",
.f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size, .dbam_to_cs = f17_addr_mask_to_cs_size,
} }
}, },
[F19_CPUS] = { [F19_CPUS] = {
.ctl_name = "F19h", .ctl_name = "F19h",
.f0_id = PCI_DEVICE_ID_AMD_19H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_19H_DF_F6,
.max_mcs = 8, .max_mcs = 8,
.ops = { .ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size, .dbam_to_cs = f17_addr_mask_to_cs_size,
} }
}, },
[F19_M10H_CPUS] = { [F19_M10H_CPUS] = {
.ctl_name = "F19h_M10h", .ctl_name = "F19h_M10h",
.f0_id = PCI_DEVICE_ID_AMD_19H_M10H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_19H_M10H_DF_F6,
.max_mcs = 12, .max_mcs = 12,
.flags.zn_regs_v2 = 1, .flags.zn_regs_v2 = 1,
.ops = { .ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size, .dbam_to_cs = f17_addr_mask_to_cs_size,
} }
}, },
[F19_M50H_CPUS] = { [F19_M50H_CPUS] = {
.ctl_name = "F19h_M50h", .ctl_name = "F19h_M50h",
.f0_id = PCI_DEVICE_ID_AMD_19H_M50H_DF_F0,
.f6_id = PCI_DEVICE_ID_AMD_19H_M50H_DF_F6,
.max_mcs = 2, .max_mcs = 2,
.ops = { .ops = {
.early_channel_count = f17_early_channel_count,
.dbam_to_cs = f17_addr_mask_to_cs_size, .dbam_to_cs = f17_addr_mask_to_cs_size,
} }
}, },
...@@ -3316,36 +3175,12 @@ static void decode_umc_error(int node_id, struct mce *m) ...@@ -3316,36 +3175,12 @@ static void decode_umc_error(int node_id, struct mce *m)
/* /*
* Use pvt->F3 which contains the F3 CPU PCI device to get the related * Use pvt->F3 which contains the F3 CPU PCI device to get the related
* F1 (AddrMap) and F2 (Dct) devices. Return negative value on error. * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error.
* Reserve F0 and F6 on systems with a UMC.
*/ */
static int static int
reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
{ {
if (pvt->umc) { if (pvt->umc)
pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
if (!pvt->F0) {
edac_dbg(1, "F0 not found, device 0x%x\n", pci_id1);
return -ENODEV;
}
pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3);
if (!pvt->F6) {
pci_dev_put(pvt->F0);
pvt->F0 = NULL;
edac_dbg(1, "F6 not found: device 0x%x\n", pci_id2);
return -ENODEV;
}
if (!pci_ctl_dev)
pci_ctl_dev = &pvt->F0->dev;
edac_dbg(1, "F0: %s\n", pci_name(pvt->F0));
edac_dbg(1, "F3: %s\n", pci_name(pvt->F3));
edac_dbg(1, "F6: %s\n", pci_name(pvt->F6));
return 0; return 0;
}
/* Reserve the ADDRESS MAP Device */ /* Reserve the ADDRESS MAP Device */
pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3);
...@@ -3377,8 +3212,7 @@ reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) ...@@ -3377,8 +3212,7 @@ reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2)
static void free_mc_sibling_devs(struct amd64_pvt *pvt) static void free_mc_sibling_devs(struct amd64_pvt *pvt)
{ {
if (pvt->umc) { if (pvt->umc) {
pci_dev_put(pvt->F0); return;
pci_dev_put(pvt->F6);
} else { } else {
pci_dev_put(pvt->F1); pci_dev_put(pvt->F1);
pci_dev_put(pvt->F2); pci_dev_put(pvt->F2);
...@@ -3468,7 +3302,6 @@ static void read_mc_regs(struct amd64_pvt *pvt) ...@@ -3468,7 +3302,6 @@ static void read_mc_regs(struct amd64_pvt *pvt)
if (pvt->umc) { if (pvt->umc) {
__read_mc_regs_df(pvt); __read_mc_regs_df(pvt);
amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar);
goto skip; goto skip;
} }
...@@ -3691,7 +3524,7 @@ static int init_csrows(struct mem_ctl_info *mci) ...@@ -3691,7 +3524,7 @@ static int init_csrows(struct mem_ctl_info *mci)
: EDAC_SECDED; : EDAC_SECDED;
} }
for (j = 0; j < pvt->channel_count; j++) { for (j = 0; j < fam_type->max_mcs; j++) {
dimm = csrow->channels[j]->dimm; dimm = csrow->channels[j]->dimm;
dimm->mtype = pvt->dram_type; dimm->mtype = pvt->dram_type;
dimm->edac_mode = edac_mode; dimm->edac_mode = edac_mode;
...@@ -3967,6 +3800,9 @@ static void setup_mci_misc_attrs(struct mem_ctl_info *mci) ...@@ -3967,6 +3800,9 @@ static void setup_mci_misc_attrs(struct mem_ctl_info *mci)
mci->dev_name = pci_name(pvt->F3); mci->dev_name = pci_name(pvt->F3);
mci->ctl_page_to_phys = NULL; mci->ctl_page_to_phys = NULL;
if (pvt->fam >= 0x17)
return;
/* memory scrubber interface */ /* memory scrubber interface */
mci->set_sdram_scrub_rate = set_scrub_rate; mci->set_sdram_scrub_rate = set_scrub_rate;
mci->get_sdram_scrub_rate = get_scrub_rate; mci->get_sdram_scrub_rate = get_scrub_rate;
...@@ -4092,16 +3928,13 @@ static const struct attribute_group *amd64_edac_attr_groups[] = { ...@@ -4092,16 +3928,13 @@ static const struct attribute_group *amd64_edac_attr_groups[] = {
static int hw_info_get(struct amd64_pvt *pvt) static int hw_info_get(struct amd64_pvt *pvt)
{ {
u16 pci_id1, pci_id2; u16 pci_id1 = 0, pci_id2 = 0;
int ret; int ret;
if (pvt->fam >= 0x17) { if (pvt->fam >= 0x17) {
pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL); pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL);
if (!pvt->umc) if (!pvt->umc)
return -ENOMEM; return -ENOMEM;
pci_id1 = fam_type->f0_id;
pci_id2 = fam_type->f6_id;
} else { } else {
pci_id1 = fam_type->f1_id; pci_id1 = fam_type->f1_id;
pci_id2 = fam_type->f2_id; pci_id2 = fam_type->f2_id;
...@@ -4118,7 +3951,7 @@ static int hw_info_get(struct amd64_pvt *pvt) ...@@ -4118,7 +3951,7 @@ static int hw_info_get(struct amd64_pvt *pvt)
static void hw_info_put(struct amd64_pvt *pvt) static void hw_info_put(struct amd64_pvt *pvt)
{ {
if (pvt->F0 || pvt->F1) if (pvt->F1)
free_mc_sibling_devs(pvt); free_mc_sibling_devs(pvt);
kfree(pvt->umc); kfree(pvt->umc);
...@@ -4128,28 +3961,12 @@ static int init_one_instance(struct amd64_pvt *pvt) ...@@ -4128,28 +3961,12 @@ static int init_one_instance(struct amd64_pvt *pvt)
{ {
struct mem_ctl_info *mci = NULL; struct mem_ctl_info *mci = NULL;
struct edac_mc_layer layers[2]; struct edac_mc_layer layers[2];
int ret = -EINVAL; int ret = -ENOMEM;
/*
* We need to determine how many memory channels there are. Then use
* that information for calculating the size of the dynamic instance
* tables in the 'mci' structure.
*/
pvt->channel_count = pvt->ops->early_channel_count(pvt);
if (pvt->channel_count < 0)
return ret;
ret = -ENOMEM;
layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
layers[0].size = pvt->csels[0].b_cnt; layers[0].size = pvt->csels[0].b_cnt;
layers[0].is_virt_csrow = true; layers[0].is_virt_csrow = true;
layers[1].type = EDAC_MC_LAYER_CHANNEL; layers[1].type = EDAC_MC_LAYER_CHANNEL;
/*
* Always allocate two channels since we can have setups with DIMMs on
* only one channel. Also, this simplifies handling later for the price
* of a couple of KBs tops.
*/
layers[1].size = fam_type->max_mcs; layers[1].size = fam_type->max_mcs;
layers[1].is_virt_csrow = false; layers[1].is_virt_csrow = false;
...@@ -4370,12 +4187,12 @@ static int __init amd64_edac_init(void) ...@@ -4370,12 +4187,12 @@ static int __init amd64_edac_init(void)
} }
/* register stuff with EDAC MCE */ /* register stuff with EDAC MCE */
if (boot_cpu_data.x86 >= 0x17) if (boot_cpu_data.x86 >= 0x17) {
amd_register_ecc_decoder(decode_umc_error); amd_register_ecc_decoder(decode_umc_error);
else } else {
amd_register_ecc_decoder(decode_bus_error); amd_register_ecc_decoder(decode_bus_error);
setup_pci_device(); setup_pci_device();
}
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR); amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
......
...@@ -114,22 +114,6 @@ ...@@ -114,22 +114,6 @@
#define PCI_DEVICE_ID_AMD_16H_NB_F2 0x1532 #define PCI_DEVICE_ID_AMD_16H_NB_F2 0x1532
#define PCI_DEVICE_ID_AMD_16H_M30H_NB_F1 0x1581 #define PCI_DEVICE_ID_AMD_16H_M30H_NB_F1 0x1581
#define PCI_DEVICE_ID_AMD_16H_M30H_NB_F2 0x1582 #define PCI_DEVICE_ID_AMD_16H_M30H_NB_F2 0x1582
#define PCI_DEVICE_ID_AMD_17H_DF_F0 0x1460
#define PCI_DEVICE_ID_AMD_17H_DF_F6 0x1466
#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F0 0x15e8
#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F6 0x15ee
#define PCI_DEVICE_ID_AMD_17H_M30H_DF_F0 0x1490
#define PCI_DEVICE_ID_AMD_17H_M30H_DF_F6 0x1496
#define PCI_DEVICE_ID_AMD_17H_M60H_DF_F0 0x1448
#define PCI_DEVICE_ID_AMD_17H_M60H_DF_F6 0x144e
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F0 0x1440
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F6 0x1446
#define PCI_DEVICE_ID_AMD_19H_DF_F0 0x1650
#define PCI_DEVICE_ID_AMD_19H_DF_F6 0x1656
#define PCI_DEVICE_ID_AMD_19H_M10H_DF_F0 0x14ad
#define PCI_DEVICE_ID_AMD_19H_M10H_DF_F6 0x14b3
#define PCI_DEVICE_ID_AMD_19H_M50H_DF_F0 0x166a
#define PCI_DEVICE_ID_AMD_19H_M50H_DF_F6 0x1670
/* /*
* Function 1 - Address Map * Function 1 - Address Map
...@@ -215,8 +199,6 @@ ...@@ -215,8 +199,6 @@
#define DCT_SEL_HI 0x114 #define DCT_SEL_HI 0x114
#define F15H_M60H_SCRCTRL 0x1C8 #define F15H_M60H_SCRCTRL 0x1C8
#define F17H_SCR_BASE_ADDR 0x48
#define F17H_SCR_LIMIT_ADDR 0x4C
/* /*
* Function 3 - Misc Control * Function 3 - Misc Control
...@@ -356,7 +338,7 @@ struct amd64_pvt { ...@@ -356,7 +338,7 @@ struct amd64_pvt {
struct low_ops *ops; struct low_ops *ops;
/* pci_device handles which we utilize */ /* pci_device handles which we utilize */
struct pci_dev *F0, *F1, *F2, *F3, *F6; struct pci_dev *F1, *F2, *F3;
u16 mc_node_id; /* MC index of this MC node */ u16 mc_node_id; /* MC index of this MC node */
u8 fam; /* CPU family */ u8 fam; /* CPU family */
...@@ -364,7 +346,6 @@ struct amd64_pvt { ...@@ -364,7 +346,6 @@ struct amd64_pvt {
u8 stepping; /* ... stepping */ u8 stepping; /* ... stepping */
int ext_model; /* extended model value of this node */ int ext_model; /* extended model value of this node */
int channel_count;
/* Raw registers */ /* Raw registers */
u32 dclr0; /* DRAM Configuration Low DCT0 reg */ u32 dclr0; /* DRAM Configuration Low DCT0 reg */
...@@ -484,7 +465,6 @@ struct ecc_settings { ...@@ -484,7 +465,6 @@ struct ecc_settings {
* functions and per device encoding/decoding logic. * functions and per device encoding/decoding logic.
*/ */
struct low_ops { struct low_ops {
int (*early_channel_count) (struct amd64_pvt *pvt);
void (*map_sysaddr_to_csrow) (struct mem_ctl_info *mci, u64 sys_addr, void (*map_sysaddr_to_csrow) (struct mem_ctl_info *mci, u64 sys_addr,
struct err_info *); struct err_info *);
int (*dbam_to_cs) (struct amd64_pvt *pvt, u8 dct, int (*dbam_to_cs) (struct amd64_pvt *pvt, u8 dct,
...@@ -503,7 +483,7 @@ struct amd64_family_flags { ...@@ -503,7 +483,7 @@ struct amd64_family_flags {
struct amd64_family_type { struct amd64_family_type {
const char *ctl_name; const char *ctl_name;
u16 f0_id, f1_id, f2_id, f6_id; u16 f1_id, f2_id;
/* Maximum number of memory controllers per die/node. */ /* Maximum number of memory controllers per die/node. */
u8 max_mcs; u8 max_mcs;
struct amd64_family_flags flags; struct amd64_family_flags flags;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include "edac_module.h" #include "edac_module.h"
#include "skx_common.h" #include "skx_common.h"
#define I10NM_REVISION "v0.0.5" #define I10NM_REVISION "v0.0.6"
#define EDAC_MOD_STR "i10nm_edac" #define EDAC_MOD_STR "i10nm_edac"
/* Debug macros */ /* Debug macros */
...@@ -23,24 +23,33 @@ ...@@ -23,24 +23,33 @@
#define I10NM_GET_SCK_BAR(d, reg) \ #define I10NM_GET_SCK_BAR(d, reg) \
pci_read_config_dword((d)->uracu, 0xd0, &(reg)) pci_read_config_dword((d)->uracu, 0xd0, &(reg))
#define I10NM_GET_IMC_BAR(d, i, reg) \ #define I10NM_GET_IMC_BAR(d, i, reg) \
pci_read_config_dword((d)->uracu, 0xd8 + (i) * 4, &(reg)) pci_read_config_dword((d)->uracu, \
(res_cfg->type == GNR ? 0xd4 : 0xd8) + (i) * 4, &(reg))
#define I10NM_GET_SAD(d, offset, i, reg)\ #define I10NM_GET_SAD(d, offset, i, reg)\
pci_read_config_dword((d)->sad_all, (offset) + (i) * 8, &(reg)) pci_read_config_dword((d)->sad_all, (offset) + (i) * \
(res_cfg->type == GNR ? 12 : 8), &(reg))
#define I10NM_GET_HBM_IMC_BAR(d, reg) \ #define I10NM_GET_HBM_IMC_BAR(d, reg) \
pci_read_config_dword((d)->uracu, 0xd4, &(reg)) pci_read_config_dword((d)->uracu, 0xd4, &(reg))
#define I10NM_GET_CAPID3_CFG(d, reg) \ #define I10NM_GET_CAPID3_CFG(d, reg) \
pci_read_config_dword((d)->pcu_cr3, 0x90, &(reg)) pci_read_config_dword((d)->pcu_cr3, \
res_cfg->type == GNR ? 0x290 : 0x90, &(reg))
#define I10NM_GET_CAPID5_CFG(d, reg) \
pci_read_config_dword((d)->pcu_cr3, \
res_cfg->type == GNR ? 0x298 : 0x98, &(reg))
#define I10NM_GET_DIMMMTR(m, i, j) \ #define I10NM_GET_DIMMMTR(m, i, j) \
readl((m)->mbase + ((m)->hbm_mc ? 0x80c : 0x2080c) + \ readl((m)->mbase + ((m)->hbm_mc ? 0x80c : \
(res_cfg->type == GNR ? 0xc0c : 0x2080c)) + \
(i) * (m)->chan_mmio_sz + (j) * 4) (i) * (m)->chan_mmio_sz + (j) * 4)
#define I10NM_GET_MCDDRTCFG(m, i) \ #define I10NM_GET_MCDDRTCFG(m, i) \
readl((m)->mbase + ((m)->hbm_mc ? 0x970 : 0x20970) + \ readl((m)->mbase + ((m)->hbm_mc ? 0x970 : 0x20970) + \
(i) * (m)->chan_mmio_sz) (i) * (m)->chan_mmio_sz)
#define I10NM_GET_MCMTR(m, i) \ #define I10NM_GET_MCMTR(m, i) \
readl((m)->mbase + ((m)->hbm_mc ? 0xef8 : 0x20ef8) + \ readl((m)->mbase + ((m)->hbm_mc ? 0xef8 : \
(res_cfg->type == GNR ? 0xaf8 : 0x20ef8)) + \
(i) * (m)->chan_mmio_sz) (i) * (m)->chan_mmio_sz)
#define I10NM_GET_AMAP(m, i) \ #define I10NM_GET_AMAP(m, i) \
readl((m)->mbase + ((m)->hbm_mc ? 0x814 : 0x20814) + \ readl((m)->mbase + ((m)->hbm_mc ? 0x814 : \
(res_cfg->type == GNR ? 0xc14 : 0x20814)) + \
(i) * (m)->chan_mmio_sz) (i) * (m)->chan_mmio_sz)
#define I10NM_GET_REG32(m, i, offset) \ #define I10NM_GET_REG32(m, i, offset) \
readl((m)->mbase + (i) * (m)->chan_mmio_sz + (offset)) readl((m)->mbase + (i) * (m)->chan_mmio_sz + (offset))
...@@ -56,7 +65,10 @@ ...@@ -56,7 +65,10 @@
#define I10NM_GET_HBM_IMC_MMIO_OFFSET(reg) \ #define I10NM_GET_HBM_IMC_MMIO_OFFSET(reg) \
((GET_BITFIELD(reg, 0, 10) << 12) + 0x140000) ((GET_BITFIELD(reg, 0, 10) << 12) + 0x140000)
#define I10NM_GNR_IMC_MMIO_OFFSET 0x24c000
#define I10NM_GNR_IMC_MMIO_SIZE 0x4000
#define I10NM_HBM_IMC_MMIO_SIZE 0x9000 #define I10NM_HBM_IMC_MMIO_SIZE 0x9000
#define I10NM_DDR_IMC_CH_CNT(reg) GET_BITFIELD(reg, 21, 24)
#define I10NM_IS_HBM_PRESENT(reg) GET_BITFIELD(reg, 27, 30) #define I10NM_IS_HBM_PRESENT(reg) GET_BITFIELD(reg, 27, 30)
#define I10NM_IS_HBM_IMC(reg) GET_BITFIELD(reg, 29, 29) #define I10NM_IS_HBM_IMC(reg) GET_BITFIELD(reg, 29, 29)
...@@ -148,20 +160,37 @@ static void __enable_retry_rd_err_log(struct skx_imc *imc, int chan, bool enable ...@@ -148,20 +160,37 @@ static void __enable_retry_rd_err_log(struct skx_imc *imc, int chan, bool enable
static void enable_retry_rd_err_log(bool enable) static void enable_retry_rd_err_log(bool enable)
{ {
int i, j, imc_num, chan_num;
struct skx_imc *imc; struct skx_imc *imc;
struct skx_dev *d; struct skx_dev *d;
int i, j;
edac_dbg(2, "\n"); edac_dbg(2, "\n");
list_for_each_entry(d, i10nm_edac_list, list) list_for_each_entry(d, i10nm_edac_list, list) {
for (i = 0; i < I10NM_NUM_IMC; i++) { imc_num = res_cfg->ddr_imc_num;
chan_num = res_cfg->ddr_chan_num;
for (i = 0; i < imc_num; i++) {
imc = &d->imc[i]; imc = &d->imc[i];
if (!imc->mbase) if (!imc->mbase)
continue; continue;
for (j = 0; j < I10NM_NUM_CHANNELS; j++) { for (j = 0; j < chan_num; j++)
if (imc->hbm_mc) { __enable_retry_rd_err_log(imc, j, enable,
res_cfg->offsets_scrub,
res_cfg->offsets_demand,
res_cfg->offsets_demand2);
}
imc_num += res_cfg->hbm_imc_num;
chan_num = res_cfg->hbm_chan_num;
for (; i < imc_num; i++) {
imc = &d->imc[i];
if (!imc->mbase || !imc->hbm_mc)
continue;
for (j = 0; j < chan_num; j++) {
__enable_retry_rd_err_log(imc, j, enable, __enable_retry_rd_err_log(imc, j, enable,
res_cfg->offsets_scrub_hbm0, res_cfg->offsets_scrub_hbm0,
res_cfg->offsets_demand_hbm0, res_cfg->offsets_demand_hbm0,
...@@ -170,11 +199,6 @@ static void enable_retry_rd_err_log(bool enable) ...@@ -170,11 +199,6 @@ static void enable_retry_rd_err_log(bool enable)
res_cfg->offsets_scrub_hbm1, res_cfg->offsets_scrub_hbm1,
res_cfg->offsets_demand_hbm1, res_cfg->offsets_demand_hbm1,
NULL); NULL);
} else {
__enable_retry_rd_err_log(imc, j, enable,
res_cfg->offsets_scrub,
res_cfg->offsets_demand,
res_cfg->offsets_demand2);
} }
} }
} }
...@@ -311,6 +335,79 @@ static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus, ...@@ -311,6 +335,79 @@ static struct pci_dev *pci_get_dev_wrapper(int dom, unsigned int bus,
return pdev; return pdev;
} }
/**
* i10nm_get_imc_num() - Get the number of present DDR memory controllers.
*
* @cfg : The pointer to the structure of EDAC resource configurations.
*
* For Granite Rapids CPUs, the number of present DDR memory controllers read
* at runtime overwrites the value statically configured in @cfg->ddr_imc_num.
* For other CPUs, the number of present DDR memory controllers is statically
* configured in @cfg->ddr_imc_num.
*
* RETURNS : 0 on success, < 0 on failure.
*/
static int i10nm_get_imc_num(struct res_config *cfg)
{
int n, imc_num, chan_num = 0;
struct skx_dev *d;
u32 reg;
list_for_each_entry(d, i10nm_edac_list, list) {
d->pcu_cr3 = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->pcu_cr3_bdf.bus],
res_cfg->pcu_cr3_bdf.dev,
res_cfg->pcu_cr3_bdf.fun);
if (!d->pcu_cr3)
continue;
if (I10NM_GET_CAPID5_CFG(d, reg))
continue;
n = I10NM_DDR_IMC_CH_CNT(reg);
if (!chan_num) {
chan_num = n;
edac_dbg(2, "Get DDR CH number: %d\n", chan_num);
} else if (chan_num != n) {
i10nm_printk(KERN_NOTICE, "Get DDR CH numbers: %d, %d\n", chan_num, n);
}
}
switch (cfg->type) {
case GNR:
/*
* One channel per DDR memory controller for Granite Rapids CPUs.
*/
imc_num = chan_num;
if (!imc_num) {
i10nm_printk(KERN_ERR, "Invalid DDR MC number\n");
return -ENODEV;
}
if (imc_num > I10NM_NUM_DDR_IMC) {
i10nm_printk(KERN_ERR, "Need to make I10NM_NUM_DDR_IMC >= %d\n", imc_num);
return -EINVAL;
}
if (cfg->ddr_imc_num != imc_num) {
/*
* Store the number of present DDR memory controllers.
*/
cfg->ddr_imc_num = imc_num;
edac_dbg(2, "Set DDR MC number: %d", imc_num);
}
return 0;
default:
/*
* For other CPUs, the number of present DDR memory controllers
* is statically pre-configured in cfg->ddr_imc_num.
*/
return 0;
}
}
static bool i10nm_check_2lm(struct res_config *cfg) static bool i10nm_check_2lm(struct res_config *cfg)
{ {
struct skx_dev *d; struct skx_dev *d;
...@@ -318,9 +415,9 @@ static bool i10nm_check_2lm(struct res_config *cfg) ...@@ -318,9 +415,9 @@ static bool i10nm_check_2lm(struct res_config *cfg)
int i; int i;
list_for_each_entry(d, i10nm_edac_list, list) { list_for_each_entry(d, i10nm_edac_list, list) {
d->sad_all = pci_get_dev_wrapper(d->seg, d->bus[1], d->sad_all = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->sad_all_bdf.bus],
PCI_SLOT(cfg->sad_all_devfn), res_cfg->sad_all_bdf.dev,
PCI_FUNC(cfg->sad_all_devfn)); res_cfg->sad_all_bdf.fun);
if (!d->sad_all) if (!d->sad_all)
continue; continue;
...@@ -337,11 +434,13 @@ static bool i10nm_check_2lm(struct res_config *cfg) ...@@ -337,11 +434,13 @@ static bool i10nm_check_2lm(struct res_config *cfg)
} }
/* /*
* Check whether the error comes from DDRT by ICX/Tremont model specific error code. * Check whether the error comes from DDRT by ICX/Tremont/SPR model specific error code.
* Refer to SDM vol3B 16.11.3 Intel IMC MC error codes for IA32_MCi_STATUS. * Refer to SDM vol3B 17.11.3/17.13.2 Intel IMC MC error codes for IA32_MCi_STATUS.
*/ */
static bool i10nm_mscod_is_ddrt(u32 mscod) static bool i10nm_mscod_is_ddrt(u32 mscod)
{ {
switch (res_cfg->type) {
case I10NM:
switch (mscod) { switch (mscod) {
case 0x0106: case 0x0107: case 0x0106: case 0x0107:
case 0x0800: case 0x0804: case 0x0800: case 0x0804:
...@@ -353,11 +452,29 @@ static bool i10nm_mscod_is_ddrt(u32 mscod) ...@@ -353,11 +452,29 @@ static bool i10nm_mscod_is_ddrt(u32 mscod)
return true; return true;
} }
break;
case SPR:
switch (mscod) {
case 0x0800: case 0x0804:
case 0x0806 ... 0x0808:
case 0x080a ... 0x080e:
case 0x0810: case 0x0811:
case 0x0816: case 0x081e:
case 0x081f:
return true;
}
break;
default:
return false;
}
return false; return false;
} }
static bool i10nm_mc_decode_available(struct mce *mce) static bool i10nm_mc_decode_available(struct mce *mce)
{ {
#define ICX_IMCx_CHy 0x06666000
u8 bank; u8 bank;
if (!decoding_via_mca || mem_cfg_2lm) if (!decoding_via_mca || mem_cfg_2lm)
...@@ -371,8 +488,17 @@ static bool i10nm_mc_decode_available(struct mce *mce) ...@@ -371,8 +488,17 @@ static bool i10nm_mc_decode_available(struct mce *mce)
switch (res_cfg->type) { switch (res_cfg->type) {
case I10NM: case I10NM:
if (bank < 13 || bank > 26) /* Check whether the bank is one of {13,14,17,18,21,22,25,26} */
if (!(ICX_IMCx_CHy & (1 << bank)))
return false;
break;
case SPR:
if (bank < 13 || bank > 20)
return false; return false;
break;
default:
return false;
}
/* DDRT errors can't be decoded from MCA bank registers */ /* DDRT errors can't be decoded from MCA bank registers */
if (MCI_MISC_ECC_MODE(mce->misc) == MCI_MISC_ECC_DDRT) if (MCI_MISC_ECC_MODE(mce->misc) == MCI_MISC_ECC_DDRT)
...@@ -381,11 +507,7 @@ static bool i10nm_mc_decode_available(struct mce *mce) ...@@ -381,11 +507,7 @@ static bool i10nm_mc_decode_available(struct mce *mce)
if (i10nm_mscod_is_ddrt(MCI_STATUS_MSCOD(mce->status))) if (i10nm_mscod_is_ddrt(MCI_STATUS_MSCOD(mce->status)))
return false; return false;
/* Check whether one of {13,14,17,18,21,22,25,26} */ return true;
return ((bank - 13) & BIT(1)) == 0;
default:
return false;
}
} }
static bool i10nm_mc_decode(struct decoded_addr *res) static bool i10nm_mc_decode(struct decoded_addr *res)
...@@ -410,6 +532,26 @@ static bool i10nm_mc_decode(struct decoded_addr *res) ...@@ -410,6 +532,26 @@ static bool i10nm_mc_decode(struct decoded_addr *res)
bank = m->bank - 13; bank = m->bank - 13;
res->imc = bank / 4; res->imc = bank / 4;
res->channel = bank % 2; res->channel = bank % 2;
res->column = GET_BITFIELD(m->misc, 9, 18) << 2;
res->row = GET_BITFIELD(m->misc, 19, 39);
res->bank_group = GET_BITFIELD(m->misc, 40, 41);
res->bank_address = GET_BITFIELD(m->misc, 42, 43);
res->bank_group |= GET_BITFIELD(m->misc, 44, 44) << 2;
res->rank = GET_BITFIELD(m->misc, 56, 58);
res->dimm = res->rank >> 2;
res->rank = res->rank % 4;
break;
case SPR:
bank = m->bank - 13;
res->imc = bank / 2;
res->channel = bank % 2;
res->column = GET_BITFIELD(m->misc, 9, 18) << 2;
res->row = GET_BITFIELD(m->misc, 19, 36);
res->bank_group = GET_BITFIELD(m->misc, 37, 38);
res->bank_address = GET_BITFIELD(m->misc, 39, 40);
res->bank_group |= GET_BITFIELD(m->misc, 41, 41) << 2;
res->rank = GET_BITFIELD(m->misc, 57, 57);
res->dimm = GET_BITFIELD(m->misc, 58, 58);
break; break;
default: default:
return false; return false;
...@@ -421,18 +563,101 @@ static bool i10nm_mc_decode(struct decoded_addr *res) ...@@ -421,18 +563,101 @@ static bool i10nm_mc_decode(struct decoded_addr *res)
return false; return false;
} }
res->column = GET_BITFIELD(m->misc, 9, 18) << 2;
res->row = GET_BITFIELD(m->misc, 19, 39);
res->bank_group = GET_BITFIELD(m->misc, 40, 41);
res->bank_address = GET_BITFIELD(m->misc, 42, 43);
res->bank_group |= GET_BITFIELD(m->misc, 44, 44) << 2;
res->rank = GET_BITFIELD(m->misc, 56, 58);
res->dimm = res->rank >> 2;
res->rank = res->rank % 4;
return true; return true;
} }
/**
* get_gnr_mdev() - Get the PCI device of the @logical_idx-th DDR memory controller.
*
* @d : The pointer to the structure of CPU socket EDAC device.
* @logical_idx : The logical index of the present memory controller (0 ~ max present MC# - 1).
* @physical_idx : To store the corresponding physical index of @logical_idx.
*
* RETURNS : The PCI device of the @logical_idx-th DDR memory controller, NULL on failure.
*/
static struct pci_dev *get_gnr_mdev(struct skx_dev *d, int logical_idx, int *physical_idx)
{
#define GNR_MAX_IMC_PCI_CNT 28
struct pci_dev *mdev;
int i, logical = 0;
/*
* Detect present memory controllers from { PCI device: 8-5, function 7-1 }
*/
for (i = 0; i < GNR_MAX_IMC_PCI_CNT; i++) {
mdev = pci_get_dev_wrapper(d->seg,
d->bus[res_cfg->ddr_mdev_bdf.bus],
res_cfg->ddr_mdev_bdf.dev + i / 7,
res_cfg->ddr_mdev_bdf.fun + i % 7);
if (mdev) {
if (logical == logical_idx) {
*physical_idx = i;
return mdev;
}
pci_dev_put(mdev);
logical++;
}
}
return NULL;
}
/**
* get_ddr_munit() - Get the resource of the i-th DDR memory controller.
*
* @d : The pointer to the structure of CPU socket EDAC device.
* @i : The index of the CPU socket relative DDR memory controller.
* @offset : To store the MMIO offset of the i-th DDR memory controller.
* @size : To store the MMIO size of the i-th DDR memory controller.
*
* RETURNS : The PCI device of the i-th DDR memory controller, NULL on failure.
*/
static struct pci_dev *get_ddr_munit(struct skx_dev *d, int i, u32 *offset, unsigned long *size)
{
struct pci_dev *mdev;
int physical_idx;
u32 reg;
switch (res_cfg->type) {
case GNR:
if (I10NM_GET_IMC_BAR(d, 0, reg)) {
i10nm_printk(KERN_ERR, "Failed to get mc0 bar\n");
return NULL;
}
mdev = get_gnr_mdev(d, i, &physical_idx);
if (!mdev)
return NULL;
*offset = I10NM_GET_IMC_MMIO_OFFSET(reg) +
I10NM_GNR_IMC_MMIO_OFFSET +
physical_idx * I10NM_GNR_IMC_MMIO_SIZE;
*size = I10NM_GNR_IMC_MMIO_SIZE;
break;
default:
if (I10NM_GET_IMC_BAR(d, i, reg)) {
i10nm_printk(KERN_ERR, "Failed to get mc%d bar\n", i);
return NULL;
}
mdev = pci_get_dev_wrapper(d->seg,
d->bus[res_cfg->ddr_mdev_bdf.bus],
res_cfg->ddr_mdev_bdf.dev + i,
res_cfg->ddr_mdev_bdf.fun);
if (!mdev)
return NULL;
*offset = I10NM_GET_IMC_MMIO_OFFSET(reg);
*size = I10NM_GET_IMC_MMIO_SIZE(reg);
}
return mdev;
}
static int i10nm_get_ddr_munits(void) static int i10nm_get_ddr_munits(void)
{ {
struct pci_dev *mdev; struct pci_dev *mdev;
...@@ -444,11 +669,15 @@ static int i10nm_get_ddr_munits(void) ...@@ -444,11 +669,15 @@ static int i10nm_get_ddr_munits(void)
u64 base; u64 base;
list_for_each_entry(d, i10nm_edac_list, list) { list_for_each_entry(d, i10nm_edac_list, list) {
d->util_all = pci_get_dev_wrapper(d->seg, d->bus[1], 29, 1); d->util_all = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->util_all_bdf.bus],
res_cfg->util_all_bdf.dev,
res_cfg->util_all_bdf.fun);
if (!d->util_all) if (!d->util_all)
return -ENODEV; return -ENODEV;
d->uracu = pci_get_dev_wrapper(d->seg, d->bus[0], 0, 1); d->uracu = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->uracu_bdf.bus],
res_cfg->uracu_bdf.dev,
res_cfg->uracu_bdf.fun);
if (!d->uracu) if (!d->uracu)
return -ENODEV; return -ENODEV;
...@@ -461,9 +690,9 @@ static int i10nm_get_ddr_munits(void) ...@@ -461,9 +690,9 @@ static int i10nm_get_ddr_munits(void)
edac_dbg(2, "socket%d mmio base 0x%llx (reg 0x%x)\n", edac_dbg(2, "socket%d mmio base 0x%llx (reg 0x%x)\n",
j++, base, reg); j++, base, reg);
for (i = 0; i < I10NM_NUM_DDR_IMC; i++) { for (i = 0; i < res_cfg->ddr_imc_num; i++) {
mdev = pci_get_dev_wrapper(d->seg, d->bus[0], mdev = get_ddr_munit(d, i, &off, &size);
12 + i, 0);
if (i == 0 && !mdev) { if (i == 0 && !mdev) {
i10nm_printk(KERN_ERR, "No IMC found\n"); i10nm_printk(KERN_ERR, "No IMC found\n");
return -ENODEV; return -ENODEV;
...@@ -473,13 +702,6 @@ static int i10nm_get_ddr_munits(void) ...@@ -473,13 +702,6 @@ static int i10nm_get_ddr_munits(void)
d->imc[i].mdev = mdev; d->imc[i].mdev = mdev;
if (I10NM_GET_IMC_BAR(d, i, reg)) {
i10nm_printk(KERN_ERR, "Failed to get mc bar\n");
return -ENODEV;
}
off = I10NM_GET_IMC_MMIO_OFFSET(reg);
size = I10NM_GET_IMC_MMIO_SIZE(reg);
edac_dbg(2, "mc%d mmio base 0x%llx size 0x%lx (reg 0x%x)\n", edac_dbg(2, "mc%d mmio base 0x%llx size 0x%lx (reg 0x%x)\n",
i, base + off, size, reg); i, base + off, size, reg);
...@@ -519,7 +741,6 @@ static int i10nm_get_hbm_munits(void) ...@@ -519,7 +741,6 @@ static int i10nm_get_hbm_munits(void)
u64 base; u64 base;
list_for_each_entry(d, i10nm_edac_list, list) { list_for_each_entry(d, i10nm_edac_list, list) {
d->pcu_cr3 = pci_get_dev_wrapper(d->seg, d->bus[1], 30, 3);
if (!d->pcu_cr3) if (!d->pcu_cr3)
return -ENODEV; return -ENODEV;
...@@ -540,11 +761,13 @@ static int i10nm_get_hbm_munits(void) ...@@ -540,11 +761,13 @@ static int i10nm_get_hbm_munits(void)
} }
base += I10NM_GET_HBM_IMC_MMIO_OFFSET(reg); base += I10NM_GET_HBM_IMC_MMIO_OFFSET(reg);
lmc = I10NM_NUM_DDR_IMC; lmc = res_cfg->ddr_imc_num;
for (i = 0; i < res_cfg->hbm_imc_num; i++) {
mdev = pci_get_dev_wrapper(d->seg, d->bus[res_cfg->hbm_mdev_bdf.bus],
res_cfg->hbm_mdev_bdf.dev + i / 4,
res_cfg->hbm_mdev_bdf.fun + i % 4);
for (i = 0; i < I10NM_NUM_HBM_IMC; i++) {
mdev = pci_get_dev_wrapper(d->seg, d->bus[0],
12 + i / 4, 1 + i % 4);
if (i == 0 && !mdev) { if (i == 0 && !mdev) {
i10nm_printk(KERN_ERR, "No hbm mc found\n"); i10nm_printk(KERN_ERR, "No hbm mc found\n");
return -ENODEV; return -ENODEV;
...@@ -594,8 +817,16 @@ static struct res_config i10nm_cfg0 = { ...@@ -594,8 +817,16 @@ static struct res_config i10nm_cfg0 = {
.type = I10NM, .type = I10NM,
.decs_did = 0x3452, .decs_did = 0x3452,
.busno_cfg_offset = 0xcc, .busno_cfg_offset = 0xcc,
.ddr_imc_num = 4,
.ddr_chan_num = 2,
.ddr_dimm_num = 2,
.ddr_chan_mmio_sz = 0x4000, .ddr_chan_mmio_sz = 0x4000,
.sad_all_devfn = PCI_DEVFN(29, 0), .sad_all_bdf = {1, 29, 0},
.pcu_cr3_bdf = {1, 30, 3},
.util_all_bdf = {1, 29, 1},
.uracu_bdf = {0, 0, 1},
.ddr_mdev_bdf = {0, 12, 0},
.hbm_mdev_bdf = {0, 12, 1},
.sad_all_offset = 0x108, .sad_all_offset = 0x108,
.offsets_scrub = offsets_scrub_icx, .offsets_scrub = offsets_scrub_icx,
.offsets_demand = offsets_demand_icx, .offsets_demand = offsets_demand_icx,
...@@ -605,8 +836,16 @@ static struct res_config i10nm_cfg1 = { ...@@ -605,8 +836,16 @@ static struct res_config i10nm_cfg1 = {
.type = I10NM, .type = I10NM,
.decs_did = 0x3452, .decs_did = 0x3452,
.busno_cfg_offset = 0xd0, .busno_cfg_offset = 0xd0,
.ddr_imc_num = 4,
.ddr_chan_num = 2,
.ddr_dimm_num = 2,
.ddr_chan_mmio_sz = 0x4000, .ddr_chan_mmio_sz = 0x4000,
.sad_all_devfn = PCI_DEVFN(29, 0), .sad_all_bdf = {1, 29, 0},
.pcu_cr3_bdf = {1, 30, 3},
.util_all_bdf = {1, 29, 1},
.uracu_bdf = {0, 0, 1},
.ddr_mdev_bdf = {0, 12, 0},
.hbm_mdev_bdf = {0, 12, 1},
.sad_all_offset = 0x108, .sad_all_offset = 0x108,
.offsets_scrub = offsets_scrub_icx, .offsets_scrub = offsets_scrub_icx,
.offsets_demand = offsets_demand_icx, .offsets_demand = offsets_demand_icx,
...@@ -616,10 +855,21 @@ static struct res_config spr_cfg = { ...@@ -616,10 +855,21 @@ static struct res_config spr_cfg = {
.type = SPR, .type = SPR,
.decs_did = 0x3252, .decs_did = 0x3252,
.busno_cfg_offset = 0xd0, .busno_cfg_offset = 0xd0,
.ddr_imc_num = 4,
.ddr_chan_num = 2,
.ddr_dimm_num = 2,
.hbm_imc_num = 16,
.hbm_chan_num = 2,
.hbm_dimm_num = 1,
.ddr_chan_mmio_sz = 0x8000, .ddr_chan_mmio_sz = 0x8000,
.hbm_chan_mmio_sz = 0x4000, .hbm_chan_mmio_sz = 0x4000,
.support_ddr5 = true, .support_ddr5 = true,
.sad_all_devfn = PCI_DEVFN(10, 0), .sad_all_bdf = {1, 10, 0},
.pcu_cr3_bdf = {1, 30, 3},
.util_all_bdf = {1, 29, 1},
.uracu_bdf = {0, 0, 1},
.ddr_mdev_bdf = {0, 12, 0},
.hbm_mdev_bdf = {0, 12, 1},
.sad_all_offset = 0x300, .sad_all_offset = 0x300,
.offsets_scrub = offsets_scrub_spr, .offsets_scrub = offsets_scrub_spr,
.offsets_scrub_hbm0 = offsets_scrub_spr_hbm0, .offsets_scrub_hbm0 = offsets_scrub_spr_hbm0,
...@@ -630,6 +880,23 @@ static struct res_config spr_cfg = { ...@@ -630,6 +880,23 @@ static struct res_config spr_cfg = {
.offsets_demand_hbm1 = offsets_demand_spr_hbm1, .offsets_demand_hbm1 = offsets_demand_spr_hbm1,
}; };
static struct res_config gnr_cfg = {
.type = GNR,
.decs_did = 0x3252,
.busno_cfg_offset = 0xd0,
.ddr_imc_num = 12,
.ddr_chan_num = 1,
.ddr_dimm_num = 2,
.ddr_chan_mmio_sz = 0x4000,
.support_ddr5 = true,
.sad_all_bdf = {0, 13, 0},
.pcu_cr3_bdf = {0, 5, 0},
.util_all_bdf = {0, 13, 1},
.uracu_bdf = {0, 0, 1},
.ddr_mdev_bdf = {0, 5, 1},
.sad_all_offset = 0x300,
};
static const struct x86_cpu_id i10nm_cpuids[] = { static const struct x86_cpu_id i10nm_cpuids[] = {
X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_TREMONT_D, X86_STEPPINGS(0x0, 0x3), &i10nm_cfg0), X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_TREMONT_D, X86_STEPPINGS(0x0, 0x3), &i10nm_cfg0),
X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_TREMONT_D, X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1), X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ATOM_TREMONT_D, X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1),
...@@ -637,6 +904,8 @@ static const struct x86_cpu_id i10nm_cpuids[] = { ...@@ -637,6 +904,8 @@ static const struct x86_cpu_id i10nm_cpuids[] = {
X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_X, X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1), X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_X, X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1),
X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_D, X86_STEPPINGS(0x0, 0xf), &i10nm_cfg1), X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_D, X86_STEPPINGS(0x0, 0xf), &i10nm_cfg1),
X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(SAPPHIRERAPIDS_X, X86_STEPPINGS(0x0, 0xf), &spr_cfg), X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(SAPPHIRERAPIDS_X, X86_STEPPINGS(0x0, 0xf), &spr_cfg),
X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(EMERALDRAPIDS_X, X86_STEPPINGS(0x0, 0xf), &spr_cfg),
X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(GRANITERAPIDS_X, X86_STEPPINGS(0x0, 0xf), &gnr_cfg),
{} {}
}; };
MODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids); MODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids);
...@@ -656,7 +925,7 @@ static int i10nm_get_dimm_config(struct mem_ctl_info *mci, ...@@ -656,7 +925,7 @@ static int i10nm_get_dimm_config(struct mem_ctl_info *mci,
{ {
struct skx_pvt *pvt = mci->pvt_info; struct skx_pvt *pvt = mci->pvt_info;
struct skx_imc *imc = pvt->imc; struct skx_imc *imc = pvt->imc;
u32 mtr, amap, mcddrtcfg; u32 mtr, amap, mcddrtcfg = 0;
struct dimm_info *dimm; struct dimm_info *dimm;
int i, j, ndimms; int i, j, ndimms;
...@@ -666,7 +935,10 @@ static int i10nm_get_dimm_config(struct mem_ctl_info *mci, ...@@ -666,7 +935,10 @@ static int i10nm_get_dimm_config(struct mem_ctl_info *mci,
ndimms = 0; ndimms = 0;
amap = I10NM_GET_AMAP(imc, i); amap = I10NM_GET_AMAP(imc, i);
if (res_cfg->type != GNR)
mcddrtcfg = I10NM_GET_MCDDRTCFG(imc, i); mcddrtcfg = I10NM_GET_MCDDRTCFG(imc, i);
for (j = 0; j < imc->num_dimms; j++) { for (j = 0; j < imc->num_dimms; j++) {
dimm = edac_get_dimm(mci, i, j, 0); dimm = edac_get_dimm(mci, i, j, 0);
mtr = I10NM_GET_DIMMMTR(imc, i, j); mtr = I10NM_GET_DIMMMTR(imc, i, j);
...@@ -752,6 +1024,7 @@ static int __init i10nm_init(void) ...@@ -752,6 +1024,7 @@ static int __init i10nm_init(void)
struct skx_dev *d; struct skx_dev *d;
int rc, i, off[3] = {0xd0, 0xc8, 0xcc}; int rc, i, off[3] = {0xd0, 0xc8, 0xcc};
u64 tolm, tohm; u64 tolm, tohm;
int imc_num;
edac_dbg(2, "\n"); edac_dbg(2, "\n");
...@@ -784,6 +1057,10 @@ static int __init i10nm_init(void) ...@@ -784,6 +1057,10 @@ static int __init i10nm_init(void)
return -ENODEV; return -ENODEV;
} }
rc = i10nm_get_imc_num(cfg);
if (rc < 0)
goto fail;
mem_cfg_2lm = i10nm_check_2lm(cfg); mem_cfg_2lm = i10nm_check_2lm(cfg);
skx_set_mem_cfg(mem_cfg_2lm); skx_set_mem_cfg(mem_cfg_2lm);
...@@ -792,6 +1069,8 @@ static int __init i10nm_init(void) ...@@ -792,6 +1069,8 @@ static int __init i10nm_init(void)
if (i10nm_get_hbm_munits() && rc) if (i10nm_get_hbm_munits() && rc)
goto fail; goto fail;
imc_num = res_cfg->ddr_imc_num + res_cfg->hbm_imc_num;
list_for_each_entry(d, i10nm_edac_list, list) { list_for_each_entry(d, i10nm_edac_list, list) {
rc = skx_get_src_id(d, 0xf8, &src_id); rc = skx_get_src_id(d, 0xf8, &src_id);
if (rc < 0) if (rc < 0)
...@@ -802,7 +1081,7 @@ static int __init i10nm_init(void) ...@@ -802,7 +1081,7 @@ static int __init i10nm_init(void)
goto fail; goto fail;
edac_dbg(2, "src_id = %d node_id = %d\n", src_id, node_id); edac_dbg(2, "src_id = %d node_id = %d\n", src_id, node_id);
for (i = 0; i < I10NM_NUM_IMC; i++) { for (i = 0; i < imc_num; i++) {
if (!d->imc[i].mdev) if (!d->imc[i].mdev)
continue; continue;
...@@ -812,12 +1091,12 @@ static int __init i10nm_init(void) ...@@ -812,12 +1091,12 @@ static int __init i10nm_init(void)
d->imc[i].node_id = node_id; d->imc[i].node_id = node_id;
if (d->imc[i].hbm_mc) { if (d->imc[i].hbm_mc) {
d->imc[i].chan_mmio_sz = cfg->hbm_chan_mmio_sz; d->imc[i].chan_mmio_sz = cfg->hbm_chan_mmio_sz;
d->imc[i].num_channels = I10NM_NUM_HBM_CHANNELS; d->imc[i].num_channels = cfg->hbm_chan_num;
d->imc[i].num_dimms = I10NM_NUM_HBM_DIMMS; d->imc[i].num_dimms = cfg->hbm_dimm_num;
} else { } else {
d->imc[i].chan_mmio_sz = cfg->ddr_chan_mmio_sz; d->imc[i].chan_mmio_sz = cfg->ddr_chan_mmio_sz;
d->imc[i].num_channels = I10NM_NUM_DDR_CHANNELS; d->imc[i].num_channels = cfg->ddr_chan_num;
d->imc[i].num_dimms = I10NM_NUM_DDR_DIMMS; d->imc[i].num_dimms = cfg->ddr_dimm_num;
} }
rc = skx_register_mci(&d->imc[i], d->imc[i].mdev, rc = skx_register_mci(&d->imc[i], d->imc[i].mdev,
......
...@@ -396,12 +396,19 @@ static int qcom_llcc_edac_remove(struct platform_device *pdev) ...@@ -396,12 +396,19 @@ static int qcom_llcc_edac_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct platform_device_id qcom_llcc_edac_id_table[] = {
{ .name = "qcom_llcc_edac" },
{}
};
MODULE_DEVICE_TABLE(platform, qcom_llcc_edac_id_table);
static struct platform_driver qcom_llcc_edac_driver = { static struct platform_driver qcom_llcc_edac_driver = {
.probe = qcom_llcc_edac_probe, .probe = qcom_llcc_edac_probe,
.remove = qcom_llcc_edac_remove, .remove = qcom_llcc_edac_remove,
.driver = { .driver = {
.name = "qcom_llcc_edac", .name = "qcom_llcc_edac",
}, },
.id_table = qcom_llcc_edac_id_table,
}; };
module_platform_driver(qcom_llcc_edac_driver); module_platform_driver(qcom_llcc_edac_driver);
......
...@@ -560,22 +560,6 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, ...@@ -560,22 +560,6 @@ static void skx_mce_output_error(struct mem_ctl_info *mci,
tp_event = HW_EVENT_ERR_CORRECTED; tp_event = HW_EVENT_ERR_CORRECTED;
} }
/*
* According to Intel Architecture spec vol 3B,
* Table 15-10 "IA32_MCi_Status [15:0] Compound Error Code Encoding"
* memory errors should fit one of these masks:
* 000f 0000 1mmm cccc (binary)
* 000f 0010 1mmm cccc (binary) [RAM used as cache]
* where:
* f = Correction Report Filtering Bit. If 1, subsequent errors
* won't be shown
* mmm = error type
* cccc = channel
* If the mask doesn't match, report an error to the parsing logic
*/
if (!((errcode & 0xef80) == 0x80 || (errcode & 0xef80) == 0x280)) {
optype = "Can't parse: it is not a mem";
} else {
switch (optypenum) { switch (optypenum) {
case 0: case 0:
optype = "generic undef request error"; optype = "generic undef request error";
...@@ -597,7 +581,7 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, ...@@ -597,7 +581,7 @@ static void skx_mce_output_error(struct mem_ctl_info *mci,
optype = "reserved"; optype = "reserved";
break; break;
} }
}
if (res->decoded_by_adxl) { if (res->decoded_by_adxl) {
len = snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s", len = snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s",
overflow ? " OVERFLOW" : "", overflow ? " OVERFLOW" : "",
...@@ -632,12 +616,18 @@ static bool skx_error_in_1st_level_mem(const struct mce *m) ...@@ -632,12 +616,18 @@ static bool skx_error_in_1st_level_mem(const struct mce *m)
if (!skx_mem_cfg_2lm) if (!skx_mem_cfg_2lm)
return false; return false;
errcode = GET_BITFIELD(m->status, 0, 15); errcode = GET_BITFIELD(m->status, 0, 15) & MCACOD_MEM_ERR_MASK;
if ((errcode & 0xef80) != 0x280) return errcode == MCACOD_EXT_MEM_ERR;
return false; }
return true; static bool skx_error_in_mem(const struct mce *m)
{
u32 errcode;
errcode = GET_BITFIELD(m->status, 0, 15) & MCACOD_MEM_ERR_MASK;
return (errcode == MCACOD_MEM_CTL_ERR || errcode == MCACOD_EXT_MEM_ERR);
} }
int skx_mce_check_error(struct notifier_block *nb, unsigned long val, int skx_mce_check_error(struct notifier_block *nb, unsigned long val,
...@@ -651,8 +641,8 @@ int skx_mce_check_error(struct notifier_block *nb, unsigned long val, ...@@ -651,8 +641,8 @@ int skx_mce_check_error(struct notifier_block *nb, unsigned long val,
if (mce->kflags & MCE_HANDLED_CEC) if (mce->kflags & MCE_HANDLED_CEC)
return NOTIFY_DONE; return NOTIFY_DONE;
/* ignore unless this is memory related with an address */ /* Ignore unless this is memory related with an address */
if ((mce->status & 0xefff) >> 7 != 1 || !(mce->status & MCI_STATUS_ADDRV)) if (!skx_error_in_mem(mce) || !(mce->status & MCI_STATUS_ADDRV))
return NOTIFY_DONE; return NOTIFY_DONE;
memset(&res, 0, sizeof(res)); memset(&res, 0, sizeof(res));
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
#define SKX_NUM_CHANNELS 3 /* Channels per memory controller */ #define SKX_NUM_CHANNELS 3 /* Channels per memory controller */
#define SKX_NUM_DIMMS 2 /* Max DIMMS per channel */ #define SKX_NUM_DIMMS 2 /* Max DIMMS per channel */
#define I10NM_NUM_DDR_IMC 4 #define I10NM_NUM_DDR_IMC 12
#define I10NM_NUM_DDR_CHANNELS 2 #define I10NM_NUM_DDR_CHANNELS 2
#define I10NM_NUM_DDR_DIMMS 2 #define I10NM_NUM_DDR_DIMMS 2
...@@ -56,6 +56,30 @@ ...@@ -56,6 +56,30 @@
#define MCI_MISC_ECC_MODE(m) (((m) >> 59) & 15) #define MCI_MISC_ECC_MODE(m) (((m) >> 59) & 15)
#define MCI_MISC_ECC_DDRT 8 /* read from DDRT */ #define MCI_MISC_ECC_DDRT 8 /* read from DDRT */
/*
* According to Intel Architecture spec vol 3B,
* Table 15-10 "IA32_MCi_Status [15:0] Compound Error Code Encoding"
* memory errors should fit one of these masks:
* 000f 0000 1mmm cccc (binary)
* 000f 0010 1mmm cccc (binary) [RAM used as cache]
* where:
* f = Correction Report Filtering Bit. If 1, subsequent errors
* won't be shown
* mmm = error type
* cccc = channel
*/
#define MCACOD_MEM_ERR_MASK 0xef80
/*
* Errors from either the memory of the 1-level memory system or the
* 2nd level memory (the slow "far" memory) of the 2-level memory system.
*/
#define MCACOD_MEM_CTL_ERR 0x80
/*
* Errors from the 1st level memory (the fast "near" memory as cache)
* of the 2-level memory system.
*/
#define MCACOD_EXT_MEM_ERR 0x280
/* /*
* Each cpu socket contains some pci devices that provide global * Each cpu socket contains some pci devices that provide global
* information, and also some that are local to each of the two * information, and also some that are local to each of the two
...@@ -105,7 +129,8 @@ struct skx_pvt { ...@@ -105,7 +129,8 @@ struct skx_pvt {
enum type { enum type {
SKX, SKX,
I10NM, I10NM,
SPR SPR,
GNR
}; };
enum { enum {
...@@ -149,19 +174,47 @@ struct decoded_addr { ...@@ -149,19 +174,47 @@ struct decoded_addr {
bool decoded_by_adxl; bool decoded_by_adxl;
}; };
struct pci_bdf {
u32 bus : 8;
u32 dev : 5;
u32 fun : 3;
};
struct res_config { struct res_config {
enum type type; enum type type;
/* Configuration agent device ID */ /* Configuration agent device ID */
unsigned int decs_did; unsigned int decs_did;
/* Default bus number configuration register offset */ /* Default bus number configuration register offset */
int busno_cfg_offset; int busno_cfg_offset;
/* DDR memory controllers per socket */
int ddr_imc_num;
/* DDR channels per DDR memory controller */
int ddr_chan_num;
/* DDR DIMMs per DDR memory channel */
int ddr_dimm_num;
/* Per DDR channel memory-mapped I/O size */ /* Per DDR channel memory-mapped I/O size */
int ddr_chan_mmio_sz; int ddr_chan_mmio_sz;
/* HBM memory controllers per socket */
int hbm_imc_num;
/* HBM channels per HBM memory controller */
int hbm_chan_num;
/* HBM DIMMs per HBM memory channel */
int hbm_dimm_num;
/* Per HBM channel memory-mapped I/O size */ /* Per HBM channel memory-mapped I/O size */
int hbm_chan_mmio_sz; int hbm_chan_mmio_sz;
bool support_ddr5; bool support_ddr5;
/* SAD device number and function number */ /* SAD device BDF */
unsigned int sad_all_devfn; struct pci_bdf sad_all_bdf;
/* PCU device BDF */
struct pci_bdf pcu_cr3_bdf;
/* UTIL device BDF */
struct pci_bdf util_all_bdf;
/* URACU device BDF */
struct pci_bdf uracu_bdf;
/* DDR mdev device BDF */
struct pci_bdf ddr_mdev_bdf;
/* HBM mdev device BDF */
struct pci_bdf hbm_mdev_bdf;
int sad_all_offset; int sad_all_offset;
/* Offsets of retry_rd_err_log registers */ /* Offsets of retry_rd_err_log registers */
u32 *offsets_scrub; u32 *offsets_scrub;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Xilinx ZynqMP OCM ECC Driver
*
* Copyright (C) 2022 Advanced Micro Devices, Inc.
*/
#include <linux/edac.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "edac_module.h"
#define ZYNQMP_OCM_EDAC_MSG_SIZE 256
#define ZYNQMP_OCM_EDAC_STRING "zynqmp_ocm"
/* Error/Interrupt registers */
#define ERR_CTRL_OFST 0x0
#define OCM_ISR_OFST 0x04
#define OCM_IMR_OFST 0x08
#define OCM_IEN_OFST 0x0C
#define OCM_IDS_OFST 0x10
/* ECC control register */
#define ECC_CTRL_OFST 0x14
/* Correctable error info registers */
#define CE_FFA_OFST 0x1C
#define CE_FFD0_OFST 0x20
#define CE_FFD1_OFST 0x24
#define CE_FFD2_OFST 0x28
#define CE_FFD3_OFST 0x2C
#define CE_FFE_OFST 0x30
/* Uncorrectable error info registers */
#define UE_FFA_OFST 0x34
#define UE_FFD0_OFST 0x38
#define UE_FFD1_OFST 0x3C
#define UE_FFD2_OFST 0x40
#define UE_FFD3_OFST 0x44
#define UE_FFE_OFST 0x48
/* ECC control register bit field definitions */
#define ECC_CTRL_CLR_CE_ERR 0x40
#define ECC_CTRL_CLR_UE_ERR 0x80
/* Fault injection data and count registers */
#define OCM_FID0_OFST 0x4C
#define OCM_FID1_OFST 0x50
#define OCM_FID2_OFST 0x54
#define OCM_FID3_OFST 0x58
#define OCM_FIC_OFST 0x74
#define UE_MAX_BITPOS_LOWER 31
#define UE_MIN_BITPOS_UPPER 32
#define UE_MAX_BITPOS_UPPER 63
/* Interrupt masks */
#define OCM_CEINTR_MASK BIT(6)
#define OCM_UEINTR_MASK BIT(7)
#define OCM_ECC_ENABLE_MASK BIT(0)
#define OCM_FICOUNT_MASK GENMASK(23, 0)
#define OCM_NUM_UE_BITPOS 2
#define OCM_BASEVAL 0xFFFC0000
#define EDAC_DEVICE "ZynqMP-OCM"
/**
* struct ecc_error_info - ECC error log information
* @addr: Fault generated at this address
* @fault_lo: Generated fault data (lower 32-bit)
* @fault_hi: Generated fault data (upper 32-bit)
*/
struct ecc_error_info {
u32 addr;
u32 fault_lo;
u32 fault_hi;
};
/**
* struct ecc_status - ECC status information to report
* @ce_cnt: Correctable error count
* @ue_cnt: Uncorrectable error count
* @ceinfo: Correctable error log information
* @ueinfo: Uncorrectable error log information
*/
struct ecc_status {
u32 ce_cnt;
u32 ue_cnt;
struct ecc_error_info ceinfo;
struct ecc_error_info ueinfo;
};
/**
* struct edac_priv - OCM private instance data
* @baseaddr: Base address of the OCM
* @message: Buffer for framing the event specific info
* @stat: ECC status information
* @ce_cnt: Correctable Error count
* @ue_cnt: Uncorrectable Error count
* @debugfs_dir: Directory entry for debugfs
* @ce_bitpos: Bit position for Correctable Error
* @ue_bitpos: Array to store UnCorrectable Error bit positions
* @fault_injection_cnt: Fault Injection Counter value
*/
struct edac_priv {
void __iomem *baseaddr;
char message[ZYNQMP_OCM_EDAC_MSG_SIZE];
struct ecc_status stat;
u32 ce_cnt;
u32 ue_cnt;
#ifdef CONFIG_EDAC_DEBUG
struct dentry *debugfs_dir;
u8 ce_bitpos;
u8 ue_bitpos[OCM_NUM_UE_BITPOS];
u32 fault_injection_cnt;
#endif
};
/**
* get_error_info - Get the current ECC error info
* @base: Pointer to the base address of the OCM
* @p: Pointer to the OCM ECC status structure
* @mask: Status register mask value
*
* Determines there is any ECC error or not
*
*/
static void get_error_info(void __iomem *base, struct ecc_status *p, int mask)
{
if (mask & OCM_CEINTR_MASK) {
p->ce_cnt++;
p->ceinfo.fault_lo = readl(base + CE_FFD0_OFST);
p->ceinfo.fault_hi = readl(base + CE_FFD1_OFST);
p->ceinfo.addr = (OCM_BASEVAL | readl(base + CE_FFA_OFST));
writel(ECC_CTRL_CLR_CE_ERR, base + OCM_ISR_OFST);
} else if (mask & OCM_UEINTR_MASK) {
p->ue_cnt++;
p->ueinfo.fault_lo = readl(base + UE_FFD0_OFST);
p->ueinfo.fault_hi = readl(base + UE_FFD1_OFST);
p->ueinfo.addr = (OCM_BASEVAL | readl(base + UE_FFA_OFST));
writel(ECC_CTRL_CLR_UE_ERR, base + OCM_ISR_OFST);
}
}
/**
* handle_error - Handle error types CE and UE
* @dci: Pointer to the EDAC device instance
* @p: Pointer to the OCM ECC status structure
*
* Handles correctable and uncorrectable errors.
*/
static void handle_error(struct edac_device_ctl_info *dci, struct ecc_status *p)
{
struct edac_priv *priv = dci->pvt_info;
struct ecc_error_info *pinf;
if (p->ce_cnt) {
pinf = &p->ceinfo;
snprintf(priv->message, ZYNQMP_OCM_EDAC_MSG_SIZE,
"\nOCM ECC error type :%s\nAddr: [0x%x]\nFault Data[0x%08x%08x]",
"CE", pinf->addr, pinf->fault_hi, pinf->fault_lo);
edac_device_handle_ce(dci, 0, 0, priv->message);
}
if (p->ue_cnt) {
pinf = &p->ueinfo;
snprintf(priv->message, ZYNQMP_OCM_EDAC_MSG_SIZE,
"\nOCM ECC error type :%s\nAddr: [0x%x]\nFault Data[0x%08x%08x]",
"UE", pinf->addr, pinf->fault_hi, pinf->fault_lo);
edac_device_handle_ue(dci, 0, 0, priv->message);
}
memset(p, 0, sizeof(*p));
}
/**
* intr_handler - ISR routine
* @irq: irq number
* @dev_id: device id pointer
*
* Return: IRQ_NONE, if CE/UE interrupt not set or IRQ_HANDLED otherwise
*/
static irqreturn_t intr_handler(int irq, void *dev_id)
{
struct edac_device_ctl_info *dci = dev_id;
struct edac_priv *priv = dci->pvt_info;
int regval;
regval = readl(priv->baseaddr + OCM_ISR_OFST);
if (!(regval & (OCM_CEINTR_MASK | OCM_UEINTR_MASK))) {
WARN_ONCE(1, "Unhandled IRQ%d, ISR: 0x%x", irq, regval);
return IRQ_NONE;
}
get_error_info(priv->baseaddr, &priv->stat, regval);
priv->ce_cnt += priv->stat.ce_cnt;
priv->ue_cnt += priv->stat.ue_cnt;
handle_error(dci, &priv->stat);
return IRQ_HANDLED;
}
/**
* get_eccstate - Return the ECC status
* @base: Pointer to the OCM base address
*
* Get the ECC enable/disable status
*
* Return: ECC status 0/1.
*/
static bool get_eccstate(void __iomem *base)
{
return readl(base + ECC_CTRL_OFST) & OCM_ECC_ENABLE_MASK;
}
#ifdef CONFIG_EDAC_DEBUG
/**
* write_fault_count - write fault injection count
* @priv: Pointer to the EDAC private struct
*
* Update the fault injection count register, once the counter reaches
* zero, it injects errors
*/
static void write_fault_count(struct edac_priv *priv)
{
u32 ficount = priv->fault_injection_cnt;
if (ficount & ~OCM_FICOUNT_MASK) {
ficount &= OCM_FICOUNT_MASK;
edac_printk(KERN_INFO, EDAC_DEVICE,
"Fault injection count value truncated to %d\n", ficount);
}
writel(ficount, priv->baseaddr + OCM_FIC_OFST);
}
/*
* To get the Correctable Error injected, the following steps are needed:
* - Setup the optional Fault Injection Count:
* echo <fault_count val> > /sys/kernel/debug/edac/ocm/inject_fault_count
* - Write the Correctable Error bit position value:
* echo <bit_pos val> > /sys/kernel/debug/edac/ocm/inject_ce_bitpos
*/
static ssize_t inject_ce_write(struct file *file, const char __user *data,
size_t count, loff_t *ppos)
{
struct edac_device_ctl_info *edac_dev = file->private_data;
struct edac_priv *priv = edac_dev->pvt_info;
int ret;
if (!data)
return -EFAULT;
ret = kstrtou8_from_user(data, count, 0, &priv->ce_bitpos);
if (ret)
return ret;
if (priv->ce_bitpos > UE_MAX_BITPOS_UPPER)
return -EINVAL;
if (priv->ce_bitpos <= UE_MAX_BITPOS_LOWER) {
writel(BIT(priv->ce_bitpos), priv->baseaddr + OCM_FID0_OFST);
writel(0, priv->baseaddr + OCM_FID1_OFST);
} else {
writel(BIT(priv->ce_bitpos - UE_MIN_BITPOS_UPPER),
priv->baseaddr + OCM_FID1_OFST);
writel(0, priv->baseaddr + OCM_FID0_OFST);
}
write_fault_count(priv);
return count;
}
static const struct file_operations inject_ce_fops = {
.open = simple_open,
.write = inject_ce_write,
.llseek = generic_file_llseek,
};
/*
* To get the Uncorrectable Error injected, the following steps are needed:
* - Setup the optional Fault Injection Count:
* echo <fault_count val> > /sys/kernel/debug/edac/ocm/inject_fault_count
* - Write the Uncorrectable Error bit position values:
* echo <bit_pos0 val>,<bit_pos1 val> > /sys/kernel/debug/edac/ocm/inject_ue_bitpos
*/
static ssize_t inject_ue_write(struct file *file, const char __user *data,
size_t count, loff_t *ppos)
{
struct edac_device_ctl_info *edac_dev = file->private_data;
struct edac_priv *priv = edac_dev->pvt_info;
char buf[6], *pbuf, *token[2];
u64 ue_bitpos;
int i, ret;
u8 len;
if (!data)
return -EFAULT;
len = min_t(size_t, count, sizeof(buf));
if (copy_from_user(buf, data, len))
return -EFAULT;
buf[len] = '\0';
pbuf = &buf[0];
for (i = 0; i < OCM_NUM_UE_BITPOS; i++)
token[i] = strsep(&pbuf, ",");
ret = kstrtou8(token[0], 0, &priv->ue_bitpos[0]);
if (ret)
return ret;
ret = kstrtou8(token[1], 0, &priv->ue_bitpos[1]);
if (ret)
return ret;
if (priv->ue_bitpos[0] > UE_MAX_BITPOS_UPPER ||
priv->ue_bitpos[1] > UE_MAX_BITPOS_UPPER)
return -EINVAL;
if (priv->ue_bitpos[0] == priv->ue_bitpos[1]) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Bit positions should not be equal\n");
return -EINVAL;
}
ue_bitpos = BIT(priv->ue_bitpos[0]) | BIT(priv->ue_bitpos[1]);
writel((u32)ue_bitpos, priv->baseaddr + OCM_FID0_OFST);
writel((u32)(ue_bitpos >> 32), priv->baseaddr + OCM_FID1_OFST);
write_fault_count(priv);
return count;
}
static const struct file_operations inject_ue_fops = {
.open = simple_open,
.write = inject_ue_write,
.llseek = generic_file_llseek,
};
static void setup_debugfs(struct edac_device_ctl_info *edac_dev)
{
struct edac_priv *priv = edac_dev->pvt_info;
priv->debugfs_dir = edac_debugfs_create_dir("ocm");
if (!priv->debugfs_dir)
return;
edac_debugfs_create_x32("inject_fault_count", 0644, priv->debugfs_dir,
&priv->fault_injection_cnt);
edac_debugfs_create_file("inject_ue_bitpos", 0644, priv->debugfs_dir,
edac_dev, &inject_ue_fops);
edac_debugfs_create_file("inject_ce_bitpos", 0644, priv->debugfs_dir,
edac_dev, &inject_ce_fops);
}
#endif
static int edac_probe(struct platform_device *pdev)
{
struct edac_device_ctl_info *dci;
struct edac_priv *priv;
void __iomem *baseaddr;
struct resource *res;
int irq, ret;
baseaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(baseaddr))
return PTR_ERR(baseaddr);
if (!get_eccstate(baseaddr)) {
edac_printk(KERN_INFO, EDAC_DEVICE, "ECC not enabled\n");
return -ENXIO;
}
dci = edac_device_alloc_ctl_info(sizeof(*priv), ZYNQMP_OCM_EDAC_STRING,
1, ZYNQMP_OCM_EDAC_STRING, 1, 0, NULL, 0,
edac_device_alloc_index());
if (!dci)
return -ENOMEM;
priv = dci->pvt_info;
platform_set_drvdata(pdev, dci);
dci->dev = &pdev->dev;
priv->baseaddr = baseaddr;
dci->mod_name = pdev->dev.driver->name;
dci->ctl_name = ZYNQMP_OCM_EDAC_STRING;
dci->dev_name = dev_name(&pdev->dev);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto free_dev_ctl;
}
ret = devm_request_irq(&pdev->dev, irq, intr_handler, 0,
dev_name(&pdev->dev), dci);
if (ret) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Failed to request Irq\n");
goto free_dev_ctl;
}
/* Enable UE, CE interrupts */
writel((OCM_CEINTR_MASK | OCM_UEINTR_MASK), priv->baseaddr + OCM_IEN_OFST);
#ifdef CONFIG_EDAC_DEBUG
setup_debugfs(dci);
#endif
ret = edac_device_add_device(dci);
if (ret)
goto free_dev_ctl;
return 0;
free_dev_ctl:
edac_device_free_ctl_info(dci);
return ret;
}
static int edac_remove(struct platform_device *pdev)
{
struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
struct edac_priv *priv = dci->pvt_info;
/* Disable UE, CE interrupts */
writel((OCM_CEINTR_MASK | OCM_UEINTR_MASK), priv->baseaddr + OCM_IDS_OFST);
#ifdef CONFIG_EDAC_DEBUG
debugfs_remove_recursive(priv->debugfs_dir);
#endif
edac_device_del_device(&pdev->dev);
edac_device_free_ctl_info(dci);
return 0;
}
static const struct of_device_id zynqmp_ocm_edac_match[] = {
{ .compatible = "xlnx,zynqmp-ocmc-1.0"},
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, zynqmp_ocm_edac_match);
static struct platform_driver zynqmp_ocm_edac_driver = {
.driver = {
.name = "zynqmp-ocm-edac",
.of_match_table = zynqmp_ocm_edac_match,
},
.probe = edac_probe,
.remove = edac_remove,
};
module_platform_driver(zynqmp_ocm_edac_driver);
MODULE_AUTHOR("Advanced Micro Devices, Inc");
MODULE_DESCRIPTION("Xilinx ZynqMP OCM ECC driver");
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment