mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:09:30 +02:00
Merge tag 'edac_updates_for_v7.3_rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras
Pull forgotten EDAC updates from Borislav Petkov:
"Somewhat belated (and forgotten :-\) EDAC updates lineup for v7.3:
- Mark the mpc85xx and ThunderX EDAC drivers as orphaned due to lack
of access to hardware
- Remove the unused fake error injection interface from the EDAC
debugfs code due to potential races between logging a fake and a
real hw error
- edac_mc_sysfs: Use sysfs_emit_at() for proper bounds checking
- Remove Mark Gross from maintainer entries and move him to CREDITS
- Load the AMD address translation library only on systems which can
actually make use of it (have ECC memory) instead of on every AMD
Zen system out there
- In edac_altera, detect the SoC variant using the ECC manager's
compatible string instead of the build architecture to select the
correct interrupt layout, and remove leftover architecture-specific
ifdeffery from the double-bit error handling path
- Add a new reviewer for the Xilinx EDAC drivers
- Unify address translation logic in Intel client EDAC drivers igen6
and ie31200 along with detecting memory controller counts at boot
time instead of relying on hardcoded, platform specific numbers.
Also, fix a bunch of issues in them; work by Qiuxu Zhuo
- Add support for a new Intel processor platform Starfire which is a
derivative of Panther Lake SoCs
- The usual cleanups and fixlets all over"
* tag 'edac_updates_for_v7.3_rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras: (24 commits)
EDAC/thunderx: Orphan it
EDAC/device_sysfs: Cleanup around edac_device_ctl_poll_msec_store()
EDAC/device_sysfs: Use kstrtouint() for poll_msec to prevent truncation
EDAC/igen6: Add Intel Starfire SoCs support
EDAC/igen6: Refactor address translation logic
EDAC/igen6: Remove redundant resource configuration tables
EDAC/igen6: Detect present memory controllers at runtime
EDAC/igen6: Simplify compute die ID comments
EDAC/igen6: Remove unnecessary XOR on the zero-valued interleave bit
EDAC/igen6: Fix Raptor Lake-P logged error address
EDAC/igen6: Fix channel address decode for non-hash mode
EDAC/igen6: Fix channel selection hash
EDAC/igen6: Fix interleave boundary condition
EDAC/ie31200: Decouple DIMM width decoding from enum order
RAS/AMD/ATL: Remove conditional return with no effect
EDAC: Remove redundant dev_err()
MAINTAINERS: Add Radhey Shyam Pandey as Xilinx EDAC reviewer
EDAC/altera: Remove remaining CONFIG_64BIT ifdefs in the DB-error path
EDAC/altera: Use ECC manager compatible to select A10/S10 IRQ layout
RAS/AMD/ATL, EDAC/amd64: Only load ATL when needed
...
This commit is contained in:
@@ -1512,6 +1512,10 @@ N: Andy Gross
|
||||
E: agross@kernel.org
|
||||
D: Qualcomm SoC subsystem and drivers
|
||||
|
||||
N: Mark Gross
|
||||
E: markgross@kernel.org
|
||||
D: x86/mellanox platform maintenance and various x86 specific drivers
|
||||
|
||||
N: Grant Grundler
|
||||
E: grantgrundler@gmail.com
|
||||
W: http://obmouse.sourceforge.net/
|
||||
|
||||
+7
-9
@@ -9387,9 +9387,8 @@ S: Maintained
|
||||
F: drivers/edac/octeon_edac*
|
||||
|
||||
EDAC-CAVIUM THUNDERX
|
||||
M: Robert Richter <rric@kernel.org>
|
||||
L: linux-edac@vger.kernel.org
|
||||
S: Odd Fixes
|
||||
S: Orphan
|
||||
F: drivers/edac/thunderx_edac*
|
||||
|
||||
EDAC-CORE
|
||||
@@ -9416,9 +9415,8 @@ S: Supported
|
||||
F: drivers/edac/dmc520_edac.c
|
||||
|
||||
EDAC-E752X
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
L: linux-edac@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Orphan
|
||||
F: drivers/edac/e752x_edac.c
|
||||
|
||||
EDAC-E7XXX
|
||||
@@ -9493,9 +9491,8 @@ S: Maintained
|
||||
F: drivers/edac/igen6_edac.c
|
||||
|
||||
EDAC-MPC85XX
|
||||
M: Johannes Thumshirn <morbidrsa@gmail.com>
|
||||
L: linux-edac@vger.kernel.org
|
||||
S: Maintained
|
||||
S: Orphan
|
||||
F: drivers/edac/mpc85xx_edac.[ch]
|
||||
|
||||
EDAC-NPCM
|
||||
@@ -13283,7 +13280,6 @@ F: drivers/crypto/intel/keembay/ocs-aes.h
|
||||
|
||||
INTEL KEEM BAY OCS ECC CRYPTO DRIVER
|
||||
M: Prabhjot Khurana <prabhjot.khurana@intel.com>
|
||||
M: Mark Gross <mgross@linux.intel.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/crypto/intel,keembay-ocs-ecc.yaml
|
||||
F: drivers/crypto/intel/keembay/Kconfig
|
||||
@@ -26954,8 +26950,7 @@ S: Maintained
|
||||
F: drivers/net/ethernet/tehuti/tn40*
|
||||
|
||||
TELECOM CLOCK DRIVER FOR MCPL0010
|
||||
M: Mark Gross <markgross@kernel.org>
|
||||
S: Supported
|
||||
S: Orphan
|
||||
F: drivers/char/tlclk.c
|
||||
|
||||
TEMPO SEMICONDUCTOR DRIVERS
|
||||
@@ -29937,12 +29932,14 @@ F: include/uapi/linux/xilinx-v4l2-controls.h
|
||||
XILINX VERSAL EDAC DRIVER
|
||||
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
|
||||
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
|
||||
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
|
||||
F: drivers/edac/versal_edac.c
|
||||
|
||||
XILINX VERSALNET EDAC DRIVER
|
||||
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
|
||||
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.yaml
|
||||
F: drivers/edac/versalnet_edac.c
|
||||
@@ -29988,6 +29985,7 @@ 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>
|
||||
R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/memory-controllers/xlnx,zynqmp-ocmc-1.0.yaml
|
||||
F: drivers/edac/zynqmp_edac.c
|
||||
|
||||
@@ -302,12 +302,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
|
||||
IRQF_SHARED,
|
||||
pdev->name,
|
||||
pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request UE IRQ %d (%d)\n",
|
||||
al_mc->irq_ue, ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (al_mc->irq_ce > 0) {
|
||||
@@ -317,12 +313,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
|
||||
IRQF_SHARED,
|
||||
pdev->name,
|
||||
pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request CE IRQ %d (%d)\n",
|
||||
al_mc->irq_ce, ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+62
-65
@@ -1507,6 +1507,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
|
||||
int edac_idx, rc;
|
||||
struct device_node *np;
|
||||
const struct edac_device_prv_data *prv = &a10_sdmmceccb_data;
|
||||
bool is_s10 = device->edac->is_s10;
|
||||
|
||||
rc = altr_check_ecc_deps(device);
|
||||
if (rc)
|
||||
@@ -1548,15 +1549,14 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
|
||||
|
||||
/*
|
||||
* Update the PortB IRQs - A10 has 4, S10 has 2, Index accordingly
|
||||
*
|
||||
* FIXME: Instead of ifdefs with different architectures the driver
|
||||
* should properly use compatibles.
|
||||
*/
|
||||
#ifdef CONFIG_64BIT
|
||||
altdev->sb_irq = irq_of_parse_and_map(np, 1);
|
||||
#else
|
||||
altdev->sb_irq = irq_of_parse_and_map(np, 2);
|
||||
#endif
|
||||
|
||||
/* Using compatibles to determine the IRQ Index */
|
||||
if (is_s10)
|
||||
altdev->sb_irq = irq_of_parse_and_map(np, 1);
|
||||
else
|
||||
altdev->sb_irq = irq_of_parse_and_map(np, 2);
|
||||
|
||||
if (!altdev->sb_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n");
|
||||
rc = -ENODEV;
|
||||
@@ -1570,29 +1570,28 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
|
||||
goto err_release_group_1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* Use IRQ to determine SError origin instead of assigning IRQ */
|
||||
rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE,
|
||||
"Error PortB DBIRQ alloc\n");
|
||||
goto err_release_group_1;
|
||||
if (is_s10) {
|
||||
/* Use IRQ to determine SError origin instead of assigning IRQ */
|
||||
rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
|
||||
goto err_release_group_1;
|
||||
}
|
||||
} else {
|
||||
altdev->db_irq = irq_of_parse_and_map(np, 3);
|
||||
if (!altdev->db_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
|
||||
rc = -ENODEV;
|
||||
goto err_release_group_1;
|
||||
}
|
||||
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
|
||||
prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
|
||||
ecc_name, altdev);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
|
||||
goto err_release_group_1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
altdev->db_irq = irq_of_parse_and_map(np, 3);
|
||||
if (!altdev->db_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
|
||||
rc = -ENODEV;
|
||||
goto err_release_group_1;
|
||||
}
|
||||
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
|
||||
prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
|
||||
ecc_name, altdev);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
|
||||
goto err_release_group_1;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = edac_device_add_device(dci);
|
||||
if (rc) {
|
||||
@@ -1974,29 +1973,29 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
|
||||
goto err_release_group1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* Use IRQ to determine SError origin instead of assigning IRQ */
|
||||
rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE,
|
||||
"Unable to parse DB IRQ index\n");
|
||||
goto err_release_group1;
|
||||
if (edac->is_s10) {
|
||||
/* Use IRQ to determine SError origin instead of assigning IRQ */
|
||||
rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE,
|
||||
"Unable to parse DB IRQ index\n");
|
||||
goto err_release_group1;
|
||||
}
|
||||
} else {
|
||||
altdev->db_irq = irq_of_parse_and_map(np, 1);
|
||||
if (!altdev->db_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
|
||||
rc = -ENODEV;
|
||||
goto err_release_group1;
|
||||
}
|
||||
rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
|
||||
IRQF_TRIGGER_HIGH,
|
||||
ecc_name, altdev);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
|
||||
goto err_release_group1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
altdev->db_irq = irq_of_parse_and_map(np, 1);
|
||||
if (!altdev->db_irq) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
|
||||
rc = -ENODEV;
|
||||
goto err_release_group1;
|
||||
}
|
||||
rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
|
||||
IRQF_TRIGGER_HIGH,
|
||||
ecc_name, altdev);
|
||||
if (rc) {
|
||||
edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
|
||||
goto err_release_group1;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = edac_device_add_device(dci);
|
||||
if (rc) {
|
||||
@@ -2059,7 +2058,6 @@ static const struct irq_domain_ops a10_eccmgr_ic_ops = {
|
||||
/************** Stratix 10 EDAC Double Bit Error Handler ************/
|
||||
#define to_a10edac(p, m) container_of(p, struct altr_arria10_edac, m)
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* panic routine issues reboot on non-zero panic_timeout */
|
||||
extern int panic_timeout;
|
||||
|
||||
@@ -2106,7 +2104,6 @@ static int s10_edac_dberr_handler(struct notifier_block *this,
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************** Arria 10 EDAC Probe Function *********************/
|
||||
static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
@@ -2122,6 +2119,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, edac);
|
||||
INIT_LIST_HEAD(&edac->a10_ecc_devices);
|
||||
|
||||
edac->is_s10 = !!device_get_match_data(&pdev->dev);
|
||||
|
||||
edac->ecc_mgr_map =
|
||||
altr_sysmgr_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"altr,sysmgr-syscon");
|
||||
@@ -2153,8 +2152,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
irq_set_chained_handler_and_data(edac->sb_irq,
|
||||
altr_edac_a10_irq_handler,
|
||||
edac);
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
if (edac->is_s10)
|
||||
{
|
||||
int dberror, err_addr;
|
||||
|
||||
@@ -2177,15 +2175,14 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
regmap_write(edac->ecc_mgr_map,
|
||||
S10_SYSMGR_UE_ADDR_OFST, 0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
edac->db_irq = platform_get_irq(pdev, 1);
|
||||
if (edac->db_irq < 0)
|
||||
return edac->db_irq;
|
||||
} else {
|
||||
edac->db_irq = platform_get_irq(pdev, 1);
|
||||
if (edac->db_irq < 0)
|
||||
return edac->db_irq;
|
||||
|
||||
irq_set_chained_handler_and_data(edac->db_irq,
|
||||
altr_edac_a10_irq_handler, edac);
|
||||
#endif
|
||||
irq_set_chained_handler_and_data(edac->db_irq,
|
||||
altr_edac_a10_irq_handler, edac);
|
||||
}
|
||||
|
||||
for_each_child_of_node(pdev->dev.of_node, child) {
|
||||
if (!of_device_is_available(child))
|
||||
@@ -2207,7 +2204,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id altr_edac_a10_of_match[] = {
|
||||
{ .compatible = "altr,socfpga-a10-ecc-manager" },
|
||||
{ .compatible = "altr,socfpga-s10-ecc-manager" },
|
||||
{ .compatible = "altr,socfpga-s10-ecc-manager", .data = (void *)1 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match);
|
||||
|
||||
@@ -394,6 +394,7 @@ struct altr_arria10_edac {
|
||||
struct irq_chip irq_chip;
|
||||
struct list_head a10_ecc_devices;
|
||||
struct notifier_block panic_notifier;
|
||||
bool is_s10;
|
||||
};
|
||||
|
||||
#endif /* #ifndef _ALTERA_EDAC_H */
|
||||
|
||||
@@ -4173,6 +4173,8 @@ static int __init amd64_edac_init(void)
|
||||
goto err_pci;
|
||||
}
|
||||
|
||||
request_module_nowait("amd_atl");
|
||||
|
||||
/* register stuff with EDAC MCE */
|
||||
if (boot_cpu_data.x86 >= 0x17) {
|
||||
amd_register_ecc_decoder(decode_umc_error);
|
||||
|
||||
@@ -214,10 +214,8 @@ static int config_irq(void *ctx, struct platform_device *pdev)
|
||||
|
||||
rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH,
|
||||
DRV_NAME, ctx);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "unable to request irq %d\n", irq);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* enable interrupts */
|
||||
regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
|
||||
|
||||
+1
-64
@@ -1,50 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/string_choices.h>
|
||||
|
||||
#include "edac_module.h"
|
||||
|
||||
static struct dentry *edac_debugfs;
|
||||
|
||||
static ssize_t edac_fake_inject_write(struct file *file,
|
||||
const char __user *data,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct device *dev = file->private_data;
|
||||
struct mem_ctl_info *mci = to_mci(dev);
|
||||
static enum hw_event_mc_err_type type;
|
||||
u16 errcount = mci->fake_inject_count;
|
||||
|
||||
if (!errcount)
|
||||
errcount = 1;
|
||||
|
||||
type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
|
||||
: HW_EVENT_ERR_CORRECTED;
|
||||
|
||||
printk(KERN_DEBUG
|
||||
"Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
|
||||
errcount,
|
||||
(type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
|
||||
str_plural(errcount),
|
||||
mci->fake_inject_layer[0],
|
||||
mci->fake_inject_layer[1],
|
||||
mci->fake_inject_layer[2]
|
||||
);
|
||||
edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
|
||||
mci->fake_inject_layer[0],
|
||||
mci->fake_inject_layer[1],
|
||||
mci->fake_inject_layer[2],
|
||||
"FAKE ERROR", "for EDAC testing only");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations debug_fake_inject_fops = {
|
||||
.open = simple_open,
|
||||
.write = edac_fake_inject_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
void __init edac_debugfs_init(void)
|
||||
{
|
||||
edac_debugfs = debugfs_create_dir("edac", NULL);
|
||||
@@ -57,29 +16,7 @@ void edac_debugfs_exit(void)
|
||||
|
||||
void edac_create_debugfs_nodes(struct mem_ctl_info *mci)
|
||||
{
|
||||
struct dentry *parent;
|
||||
char name[80];
|
||||
int i;
|
||||
|
||||
parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
|
||||
|
||||
for (i = 0; i < mci->n_layers; i++) {
|
||||
sprintf(name, "fake_inject_%s",
|
||||
edac_layer_name[mci->layers[i].type]);
|
||||
debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
|
||||
&mci->fake_inject_layer[i]);
|
||||
}
|
||||
|
||||
debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
|
||||
&mci->fake_inject_ue);
|
||||
|
||||
debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
|
||||
&mci->fake_inject_count);
|
||||
|
||||
debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev,
|
||||
&debug_fake_inject_fops);
|
||||
|
||||
mci->debugfs = parent;
|
||||
mci->debugfs = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
|
||||
}
|
||||
|
||||
/* Create a toplevel dir under EDAC's debugfs hierarchy */
|
||||
|
||||
@@ -342,14 +342,10 @@ static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
|
||||
}
|
||||
|
||||
/*
|
||||
* edac_device_reset_delay_period
|
||||
*
|
||||
* need to stop any outstanding workq queued up at this time
|
||||
* because we will be resetting the sleep time.
|
||||
* Then restart the workq on the new delay
|
||||
* Stop any outstanding workq queued up at this time because sleep time will
|
||||
* be reset. Then restart the workq on the new delay.
|
||||
*/
|
||||
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
|
||||
unsigned long msec)
|
||||
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec)
|
||||
{
|
||||
edac_dev->poll_msec = msec;
|
||||
edac_dev->delay = msecs_to_jiffies(msec);
|
||||
|
||||
@@ -84,18 +84,23 @@ static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info
|
||||
return sprintf(data, "%u\n", ctl_info->poll_msec);
|
||||
}
|
||||
|
||||
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info
|
||||
*ctl_info, const char *data,
|
||||
size_t count)
|
||||
static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info *ctl_info,
|
||||
const char *data, size_t count)
|
||||
{
|
||||
unsigned long value;
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
/* get the value and enforce that it is non-zero, must be at least
|
||||
* one millisecond for the delay period, between scans
|
||||
* Then cancel last outstanding delay for the work request
|
||||
* and set a new one.
|
||||
/*
|
||||
* Get the value, make sure it is non-zero, must be at least one millisecond
|
||||
* for the delay period between scans.
|
||||
*/
|
||||
value = simple_strtoul(data, NULL, 0);
|
||||
ret = kstrtouint(data, 0, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (value < 1)
|
||||
return -EINVAL;
|
||||
|
||||
edac_device_reset_delay_period(ctl_info, value);
|
||||
|
||||
return count;
|
||||
|
||||
@@ -129,7 +129,7 @@ static ssize_t dimmdev_location_show(struct device *dev,
|
||||
ssize_t count;
|
||||
|
||||
count = edac_dimm_info_location(dimm, data, PAGE_SIZE);
|
||||
count += scnprintf(data + count, PAGE_SIZE - count, "\n");
|
||||
count += sysfs_emit_at(data, count, "\n");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@ bool edac_queue_work(struct delayed_work *work, unsigned long delay);
|
||||
bool edac_stop_work(struct delayed_work *work);
|
||||
bool edac_mod_work(struct delayed_work *work, unsigned long delay);
|
||||
|
||||
extern void edac_device_reset_delay_period(struct edac_device_ctl_info
|
||||
*edac_dev, unsigned long msec);
|
||||
extern void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec);
|
||||
extern void edac_mc_reset_delay_period(unsigned long value);
|
||||
|
||||
/*
|
||||
|
||||
@@ -235,10 +235,8 @@ static int highbank_mc_probe(struct platform_device *pdev)
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
|
||||
0, dev_name(&pdev->dev), mci);
|
||||
if (res < 0) {
|
||||
dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
|
||||
if (res < 0)
|
||||
goto err2;
|
||||
}
|
||||
|
||||
devres_close_group(&pdev->dev, NULL);
|
||||
return 0;
|
||||
|
||||
@@ -416,7 +416,23 @@ static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int dimm,
|
||||
{
|
||||
dd->size = field_get(cfg->reg_mad_dimm_size_mask[dimm], addr_decode) * cfg->reg_mad_dimm_size_granularity;
|
||||
dd->ranks = field_get(cfg->reg_mad_dimm_rank_mask[dimm], addr_decode) + 1;
|
||||
dd->dtype = field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode) + DEV_X8;
|
||||
|
||||
switch (field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode)) {
|
||||
case 0:
|
||||
dd->dtype = DEV_X8;
|
||||
break;
|
||||
case 1:
|
||||
dd->dtype = DEV_X16;
|
||||
break;
|
||||
case 2:
|
||||
dd->dtype = DEV_X32;
|
||||
break;
|
||||
case 3:
|
||||
dd->dtype = DEV_X64;
|
||||
break;
|
||||
default:
|
||||
dd->dtype = DEV_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static void ie31200_get_dimm_config(struct mem_ctl_info *mci, void __iomem *window,
|
||||
|
||||
+268
-205
@@ -42,7 +42,8 @@
|
||||
|
||||
#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo))
|
||||
|
||||
#define NUM_IMC 2 /* Max memory controllers */
|
||||
/* Probing upper bound, not a hardware capability limit. */
|
||||
#define MAX_IMC_TO_PROBE 8
|
||||
#define NUM_CHANNELS 2 /* Max channels */
|
||||
#define NUM_DIMMS 2 /* Max DIMMs per channel */
|
||||
|
||||
@@ -122,6 +123,43 @@
|
||||
#define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6)
|
||||
#define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26)
|
||||
|
||||
/*
|
||||
* A slice represents a portion of memory space participating in an
|
||||
* interleave relationship within the memory hierarchy.
|
||||
*
|
||||
* It can represent in different levels such as:
|
||||
*
|
||||
* - a pair of memory controllers
|
||||
* - a memory controller
|
||||
* - a memory channel
|
||||
* - a memory sub-channel / DIMM
|
||||
*
|
||||
* +--------+
|
||||
* | |
|
||||
* | Zone 1 |
|
||||
* | |
|
||||
* +--------+ +--------+
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | Zone 0 | | Zone 0 |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* +--------+ +--------+
|
||||
*
|
||||
* Slice L Slice S
|
||||
*
|
||||
* Memory space is divided into:
|
||||
*
|
||||
* - Zone 0 : Interleaved region
|
||||
* - Zone 1 : Non-interleaved region (upper part of the large slice).
|
||||
*/
|
||||
struct slice {
|
||||
/* Slice address. */
|
||||
u64 addr;
|
||||
/* Slice that @addr belongs to. */
|
||||
int id;
|
||||
};
|
||||
|
||||
struct igen6_imc {
|
||||
int mc;
|
||||
struct mem_ctl_info *mci;
|
||||
@@ -175,8 +213,6 @@ static struct res_config {
|
||||
/* Set imc->dimm_{l_size,s_size,l_map}[chan]. */
|
||||
void (*set_dimm_params)(struct igen6_imc *imc, int chan);
|
||||
bool (*ibecc_available)(struct pci_dev *pdev);
|
||||
/* Extract error address logged in IBECC */
|
||||
u64 (*err_addr)(u64 ecclog);
|
||||
/* Convert error address logged in IBECC to system physical address */
|
||||
u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc);
|
||||
/* Convert error address logged in IBECC to integrated memory controller address */
|
||||
@@ -184,11 +220,11 @@ static struct res_config {
|
||||
} *res_cfg;
|
||||
|
||||
static struct igen6_pvt {
|
||||
struct igen6_imc imc[NUM_IMC];
|
||||
void __iomem *memss_pma_cr;
|
||||
u64 ms_hash;
|
||||
u64 ms_s_size;
|
||||
int ms_l_map;
|
||||
struct igen6_imc imc[];
|
||||
} *igen6_pvt;
|
||||
|
||||
/* The top of low usable DRAM */
|
||||
@@ -225,7 +261,8 @@ static char ecclog_buf[ECCLOG_POOL_SIZE];
|
||||
static struct irq_work ecclog_irq_work;
|
||||
static struct work_struct ecclog_work;
|
||||
|
||||
/* Compute die IDs for Elkhart Lake with IBECC */
|
||||
/* SoC compute die IDs with IBECC capability. */
|
||||
/* Elkhart Lake */
|
||||
#define DID_EHL_SKU5 0x4514
|
||||
#define DID_EHL_SKU6 0x4528
|
||||
#define DID_EHL_SKU7 0x452a
|
||||
@@ -238,22 +275,22 @@ static struct work_struct ecclog_work;
|
||||
#define DID_EHL_SKU14 0x4534
|
||||
#define DID_EHL_SKU15 0x4536
|
||||
|
||||
/* Compute die IDs for ICL-NNPI with IBECC */
|
||||
/* ICL-NNPI */
|
||||
#define DID_ICL_SKU8 0x4581
|
||||
#define DID_ICL_SKU10 0x4585
|
||||
#define DID_ICL_SKU11 0x4589
|
||||
#define DID_ICL_SKU12 0x458d
|
||||
|
||||
/* Compute die IDs for Tiger Lake with IBECC */
|
||||
/* Tiger Lake */
|
||||
#define DID_TGL_SKU 0x9a14
|
||||
|
||||
/* Compute die IDs for Alder Lake with IBECC */
|
||||
/* Alder Lake */
|
||||
#define DID_ADL_SKU1 0x4601
|
||||
#define DID_ADL_SKU2 0x4602
|
||||
#define DID_ADL_SKU3 0x4621
|
||||
#define DID_ADL_SKU4 0x4641
|
||||
|
||||
/* Compute die IDs for Alder Lake-N with IBECC */
|
||||
/* Alder Lake-N */
|
||||
#define DID_ADL_N_SKU1 0x4614
|
||||
#define DID_ADL_N_SKU2 0x4617
|
||||
#define DID_ADL_N_SKU3 0x461b
|
||||
@@ -267,38 +304,38 @@ static struct work_struct ecclog_work;
|
||||
#define DID_ADL_N_SKU11 0x467c
|
||||
#define DID_ADL_N_SKU12 0x4632
|
||||
|
||||
/* Compute die IDs for Arizona Beach with IBECC */
|
||||
/* Arizona Beach */
|
||||
#define DID_AZB_SKU1 0x4676
|
||||
|
||||
/* Compute did IDs for Amston Lake with IBECC */
|
||||
/* Amston Lake */
|
||||
#define DID_ASL_SKU1 0x464a
|
||||
#define DID_ASL_SKU2 0x4646
|
||||
#define DID_ASL_SKU3 0x4652
|
||||
|
||||
/* Compute die IDs for Raptor Lake-P with IBECC */
|
||||
/* Raptor Lake-P */
|
||||
#define DID_RPL_P_SKU1 0xa706
|
||||
#define DID_RPL_P_SKU2 0xa707
|
||||
#define DID_RPL_P_SKU3 0xa708
|
||||
#define DID_RPL_P_SKU4 0xa716
|
||||
#define DID_RPL_P_SKU5 0xa718
|
||||
|
||||
/* Compute die IDs for Meteor Lake-PS with IBECC */
|
||||
/* Meteor Lake-PS */
|
||||
#define DID_MTL_PS_SKU1 0x7d21
|
||||
#define DID_MTL_PS_SKU2 0x7d22
|
||||
#define DID_MTL_PS_SKU3 0x7d23
|
||||
#define DID_MTL_PS_SKU4 0x7d24
|
||||
|
||||
/* Compute die IDs for Meteor Lake-P with IBECC */
|
||||
/* Meteor Lake-P */
|
||||
#define DID_MTL_P_SKU1 0x7d01
|
||||
#define DID_MTL_P_SKU2 0x7d02
|
||||
#define DID_MTL_P_SKU3 0x7d14
|
||||
|
||||
/* Compute die IDs for Arrow Lake-UH with IBECC */
|
||||
/* Arrow Lake-UH */
|
||||
#define DID_ARL_UH_SKU1 0x7d06
|
||||
#define DID_ARL_UH_SKU2 0x7d20
|
||||
#define DID_ARL_UH_SKU3 0x7d30
|
||||
|
||||
/* Compute die IDs for Panther Lake-H with IBECC */
|
||||
/* Panther Lake-H */
|
||||
#define DID_PTL_H_SKU1 0xb000
|
||||
#define DID_PTL_H_SKU2 0xb001
|
||||
#define DID_PTL_H_SKU3 0xb002
|
||||
@@ -314,15 +351,114 @@ static struct work_struct ecclog_work;
|
||||
#define DID_PTL_H_SKU13 0xb02a
|
||||
#define DID_PTL_H_SKU14 0xb00a
|
||||
|
||||
/* Compute die IDs for Wildcat Lake with IBECC */
|
||||
/* Starfire */
|
||||
#define DID_STF_SKU1 0xb02b
|
||||
|
||||
/* Wildcat Lake */
|
||||
#define DID_WCL_SKU1 0xfd00
|
||||
|
||||
/* Compute die IDs for Nova Lake-H/HX with IBECC */
|
||||
/* Nova Lake-H/HX */
|
||||
#define DID_NVL_H_SKU1 0xd701
|
||||
#define DID_NVL_H_SKU2 0xd702
|
||||
#define DID_NVL_H_SKU3 0xd704
|
||||
#define DID_NVL_H_SKU4 0xd705
|
||||
|
||||
/* Remove the interleave bit and shift upper part down to fill gap. */
|
||||
static u64 squeeze_addr(u64 addr, int intlv_bit)
|
||||
{
|
||||
u64 slice_addr;
|
||||
|
||||
slice_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
|
||||
slice_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
|
||||
|
||||
return slice_addr;
|
||||
}
|
||||
|
||||
/* Shift the upper bits up and insert a zero at the @intlv_bit bit position. */
|
||||
static u64 inflate_addr(u64 addr, int intlv_bit)
|
||||
{
|
||||
u64 inflated_addr;
|
||||
|
||||
/* Insert a zero at @intlv_bit position. */
|
||||
inflated_addr = GET_BITFIELD(addr, intlv_bit, 63) << (intlv_bit + 1);
|
||||
inflated_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
|
||||
|
||||
return inflated_addr;
|
||||
}
|
||||
|
||||
static u64 compute_hash(u64 addr, u64 hash_mask, u64 hash_base, int intlv_bit)
|
||||
{
|
||||
u64 hash_addr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* In hash mode, @intlv_bit is the lowest selected bit of @addr
|
||||
* to be XORed. While @mask may or may not include this @intlv_bit,
|
||||
* we enforce that @mask includes @intlv_bit to ensure @intlv_bit is
|
||||
* XORed exactly once.
|
||||
*/
|
||||
hash_mask |= BIT_ULL(intlv_bit);
|
||||
hash_addr = addr & hash_mask;
|
||||
|
||||
for (i = 6; i < 20; i++)
|
||||
hash_base ^= (hash_addr >> i) & 1;
|
||||
|
||||
return hash_base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a higher-level address (system / IMC / channel) into a lower-level
|
||||
* slice address and identifier.
|
||||
*/
|
||||
static void translate_to_lower_level(u64 addr, u64 hash_mask, u64 hash_base,
|
||||
int intlv_bit, u64 s_size, int l_map,
|
||||
struct slice *slice)
|
||||
{
|
||||
/* In non-interleave zone. */
|
||||
if (addr >= 2 * s_size) {
|
||||
slice->addr = addr - s_size;
|
||||
slice->id = l_map;
|
||||
return;
|
||||
}
|
||||
|
||||
/* In interleave zone. */
|
||||
slice->addr = squeeze_addr(addr, intlv_bit);
|
||||
|
||||
/* Non-hash mode. */
|
||||
if (!hash_mask) {
|
||||
slice->id = GET_BITFIELD(addr, intlv_bit, intlv_bit);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Hash mode. */
|
||||
slice->id = compute_hash(addr, hash_mask, hash_base, intlv_bit);
|
||||
}
|
||||
|
||||
/* Reconstruct address for upper memory hierarchy level. */
|
||||
static u64 translate_to_upper_level(u64 addr, u64 hash_mask, u64 hash_base,
|
||||
int intlv_bit, u64 s_size)
|
||||
{
|
||||
u64 inflated_addr, hash_val;
|
||||
|
||||
/* In non-interleave zone. */
|
||||
if (addr >= s_size)
|
||||
return addr + s_size;
|
||||
|
||||
/*
|
||||
* In interleave zone.
|
||||
*
|
||||
* Insert a zero at @intlv_bit position.
|
||||
*/
|
||||
inflated_addr = inflate_addr(addr, intlv_bit);
|
||||
|
||||
/*
|
||||
* Reconstruct the removed interleave bit and use it to replace
|
||||
* the zero at @intlv_bit position.
|
||||
*/
|
||||
hash_val = compute_hash(inflated_addr, hash_mask, hash_base, intlv_bit);
|
||||
return inflated_addr | (hash_val << intlv_bit);
|
||||
}
|
||||
|
||||
static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
|
||||
{
|
||||
union {
|
||||
@@ -354,6 +490,46 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether the memory controller is absent. */
|
||||
static bool imc_absent(void __iomem *window)
|
||||
{
|
||||
return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
|
||||
}
|
||||
|
||||
/* Return MMIO base address of the memory controller if it's present, otherwise return NULL. */
|
||||
static void __iomem *map_imc_window(u64 mchbar, int pmc)
|
||||
{
|
||||
void __iomem *window;
|
||||
|
||||
window = ioremap(mchbar + pmc * MCHBAR_SIZE, MCHBAR_SIZE);
|
||||
if (!window)
|
||||
return NULL;
|
||||
|
||||
if (imc_absent(window)) {
|
||||
iounmap(window);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
/* Return the number of present memory controllers. */
|
||||
static int get_imc_num(u64 mchbar)
|
||||
{
|
||||
void __iomem *window;
|
||||
int lmc, pmc;
|
||||
|
||||
for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
|
||||
window = map_imc_window(mchbar, pmc);
|
||||
if (window) {
|
||||
iounmap(window);
|
||||
lmc++;
|
||||
}
|
||||
}
|
||||
|
||||
return lmc;
|
||||
}
|
||||
|
||||
static bool ehl_ibecc_available(struct pci_dev *pdev)
|
||||
{
|
||||
u32 v;
|
||||
@@ -450,21 +626,9 @@ static u64 mem_addr_to_sys_addr(u64 maddr)
|
||||
return maddr;
|
||||
}
|
||||
|
||||
static u64 mem_slice_hash(u64 addr, u64 mask, u64 hash_init, int intlv_bit)
|
||||
{
|
||||
u64 hash_addr = addr & mask, hash = hash_init;
|
||||
u64 intlv = (addr >> intlv_bit) & 1;
|
||||
int i;
|
||||
|
||||
for (i = 6; i < 20; i++)
|
||||
hash ^= (hash_addr >> i) & 1;
|
||||
|
||||
return hash ^ intlv;
|
||||
}
|
||||
|
||||
static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
|
||||
{
|
||||
u64 maddr, hash, mask, ms_s_size;
|
||||
u64 mask, ms_s_size;
|
||||
int intlv_bit;
|
||||
u32 ms_hash;
|
||||
|
||||
@@ -477,12 +641,7 @@ static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
|
||||
mask = MEM_SLICE_HASH_MASK(ms_hash);
|
||||
intlv_bit = MEM_SLICE_HASH_LSB_MASK_BIT(ms_hash) + 6;
|
||||
|
||||
maddr = GET_BITFIELD(eaddr, intlv_bit, 63) << (intlv_bit + 1) |
|
||||
GET_BITFIELD(eaddr, 0, intlv_bit - 1);
|
||||
|
||||
hash = mem_slice_hash(maddr, mask, mc, intlv_bit);
|
||||
|
||||
return maddr | (hash << intlv_bit);
|
||||
return translate_to_upper_level(eaddr, mask, mc, intlv_bit, ms_s_size);
|
||||
}
|
||||
|
||||
static u64 tgl_err_addr_to_sys_addr(u64 eaddr, int mc)
|
||||
@@ -504,8 +663,9 @@ static u64 adl_err_addr_to_sys_addr(u64 eaddr, int mc)
|
||||
|
||||
static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
|
||||
{
|
||||
u64 imc_addr, ms_s_size = igen6_pvt->ms_s_size;
|
||||
u64 ms_s_size = igen6_pvt->ms_s_size;
|
||||
struct igen6_imc *imc = &igen6_pvt->imc[mc];
|
||||
struct slice slice;
|
||||
int intlv_bit;
|
||||
u32 mc_hash;
|
||||
|
||||
@@ -516,15 +676,8 @@ static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
|
||||
|
||||
intlv_bit = MAC_MC_HASH_LSB(mc_hash) + 6;
|
||||
|
||||
imc_addr = GET_BITFIELD(eaddr, intlv_bit + 1, 63) << intlv_bit |
|
||||
GET_BITFIELD(eaddr, 0, intlv_bit - 1);
|
||||
|
||||
return imc_addr;
|
||||
}
|
||||
|
||||
static u64 rpl_p_err_addr(u64 ecclog)
|
||||
{
|
||||
return field_get(res_cfg->reg_eccerrlog_addr_mask, ecclog);
|
||||
translate_to_lower_level(eaddr, 0, 0, intlv_bit, ms_s_size, 0, &slice);
|
||||
return slice.addr;
|
||||
}
|
||||
|
||||
static enum mem_type ptl_h_get_mem_type(struct igen6_imc *imc)
|
||||
@@ -686,6 +839,7 @@ static struct res_config tgl_cfg = {
|
||||
.err_addr_to_imc_addr = tgl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
/* Shared by Alder Lake, Alder Lake-N, Arizona Beach, Amston Lake, and Raptor Lake-P */
|
||||
static struct res_config adl_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -701,37 +855,6 @@ static struct res_config adl_cfg = {
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config adl_n_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 1,
|
||||
.reg_mchbar_mask = GENMASK_ULL(41, 17),
|
||||
.reg_tom_mask = GENMASK_ULL(41, 20),
|
||||
.reg_touud_mask = GENMASK_ULL(41, 20),
|
||||
.reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
|
||||
.imc_base = 0xd800,
|
||||
.ibecc_base = 0xd400,
|
||||
.ibecc_error_log_offset = 0x68,
|
||||
.ibecc_available = tgl_ibecc_available,
|
||||
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config rpl_p_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
.reg_mchbar_mask = GENMASK_ULL(41, 17),
|
||||
.reg_tom_mask = GENMASK_ULL(41, 20),
|
||||
.reg_touud_mask = GENMASK_ULL(41, 20),
|
||||
.reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
|
||||
.imc_base = 0xd800,
|
||||
.ibecc_base = 0xd400,
|
||||
.ibecc_error_log_offset = 0x68,
|
||||
.ibecc_available = tgl_ibecc_available,
|
||||
.err_addr = rpl_p_err_addr,
|
||||
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config mtl_ps_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -749,6 +872,7 @@ static struct res_config mtl_ps_cfg = {
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
/* Shared by Meteor Lake-P, Arrow Lake-UH, and Wildcat Lake */
|
||||
static struct res_config mtl_p_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -764,6 +888,7 @@ static struct res_config mtl_p_cfg = {
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
/* Shared by Panther Lake-H and Starfire */
|
||||
static struct res_config ptl_h_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -794,21 +919,6 @@ static struct res_config ptl_h_cfg = {
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config wcl_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 1,
|
||||
.reg_mchbar_mask = GENMASK_ULL(41, 17),
|
||||
.reg_tom_mask = GENMASK_ULL(41, 20),
|
||||
.reg_touud_mask = GENMASK_ULL(41, 20),
|
||||
.reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5),
|
||||
.imc_base = 0xd800,
|
||||
.ibecc_base = 0xd400,
|
||||
.ibecc_error_log_offset = 0x170,
|
||||
.ibecc_available = mtl_p_ibecc_available,
|
||||
.err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
|
||||
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
|
||||
};
|
||||
|
||||
static struct res_config nvl_h_cfg = {
|
||||
.machine_check = true,
|
||||
.num_imc = 2,
|
||||
@@ -861,27 +971,27 @@ static struct pci_device_id igen6_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU1), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU2), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU3), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
|
||||
@@ -892,6 +1002,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
@@ -906,7 +1017,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU14), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&wcl_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_STF_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU1), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU2), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
|
||||
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU3), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
|
||||
@@ -1007,55 +1118,13 @@ static void set_dimm_params(struct igen6_imc *imc, int chan)
|
||||
imc->dimm_s_size[chan] = MAD_DIMM_CH_DIMM_S_SIZE(val);
|
||||
}
|
||||
|
||||
static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit)
|
||||
{
|
||||
u64 hash_addr = addr & mask, hash = 0;
|
||||
u64 intlv = (addr >> intlv_bit) & 1;
|
||||
int i;
|
||||
|
||||
for (i = 6; i < 20; i++)
|
||||
hash ^= (hash_addr >> i) & 1;
|
||||
|
||||
return (int)hash ^ intlv;
|
||||
}
|
||||
|
||||
static u64 decode_channel_addr(u64 addr, int intlv_bit)
|
||||
{
|
||||
u64 channel_addr;
|
||||
|
||||
/* Remove the interleave bit and shift upper part down to fill gap */
|
||||
channel_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
|
||||
channel_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
|
||||
|
||||
return channel_addr;
|
||||
}
|
||||
|
||||
static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map,
|
||||
int *idx, u64 *sub_addr)
|
||||
{
|
||||
int intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
|
||||
|
||||
if (addr > 2 * s_size) {
|
||||
*sub_addr = addr - s_size;
|
||||
*idx = l_map;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CHANNEL_HASH_MODE(hash)) {
|
||||
*sub_addr = decode_channel_addr(addr, intlv_bit);
|
||||
*idx = decode_chan_idx(addr, CHANNEL_HASH_MASK(hash), intlv_bit);
|
||||
} else {
|
||||
*sub_addr = decode_channel_addr(addr, 6);
|
||||
*idx = GET_BITFIELD(addr, 6, 6);
|
||||
}
|
||||
}
|
||||
|
||||
static int igen6_decode(struct decoded_addr *res)
|
||||
{
|
||||
struct igen6_imc *imc = &igen6_pvt->imc[res->mc];
|
||||
u64 addr = res->imc_addr, sub_addr, s_size;
|
||||
int idx, l_map;
|
||||
u32 hash;
|
||||
u64 addr = res->imc_addr, s_size;
|
||||
int intlv_bit, l_map;
|
||||
u32 hash, hash_mask;
|
||||
struct slice slice;
|
||||
|
||||
if (addr >= igen6_tom) {
|
||||
edac_dbg(0, "Address 0x%llx out of range\n", addr);
|
||||
@@ -1066,17 +1135,25 @@ static int igen6_decode(struct decoded_addr *res)
|
||||
hash = readl(imc->window + CHANNEL_HASH_OFFSET);
|
||||
s_size = imc->ch_s_size;
|
||||
l_map = imc->ch_l_map;
|
||||
decode_addr(addr, hash, s_size, l_map, &idx, &sub_addr);
|
||||
res->channel_idx = idx;
|
||||
res->channel_addr = sub_addr;
|
||||
hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
|
||||
intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
|
||||
|
||||
translate_to_lower_level(addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
|
||||
|
||||
res->channel_idx = slice.id;
|
||||
res->channel_addr = slice.addr;
|
||||
|
||||
/* Decode sub-channel/DIMM */
|
||||
hash = readl(imc->window + CHANNEL_EHASH_OFFSET);
|
||||
s_size = imc->dimm_s_size[idx];
|
||||
l_map = imc->dimm_l_map[idx];
|
||||
decode_addr(res->channel_addr, hash, s_size, l_map, &idx, &sub_addr);
|
||||
res->sub_channel_idx = idx;
|
||||
res->sub_channel_addr = sub_addr;
|
||||
s_size = imc->dimm_s_size[res->channel_idx];
|
||||
l_map = imc->dimm_l_map[res->channel_idx];
|
||||
hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
|
||||
intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
|
||||
|
||||
translate_to_lower_level(res->channel_addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
|
||||
|
||||
res->sub_channel_idx = slice.id;
|
||||
res->sub_channel_addr = slice.addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1230,11 +1307,7 @@ static void ecclog_work_cb(struct work_struct *work)
|
||||
|
||||
llist_for_each_entry_safe(node, tmp, head, llnode) {
|
||||
memset(&res, 0, sizeof(res));
|
||||
if (res_cfg->err_addr)
|
||||
eaddr = res_cfg->err_addr(node->ecclog);
|
||||
else
|
||||
eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
|
||||
|
||||
eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
|
||||
res.mc = node->mc;
|
||||
res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr, res.mc);
|
||||
res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr, res.mc);
|
||||
@@ -1476,18 +1549,27 @@ static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev)
|
||||
{
|
||||
void __iomem *memss_pma_cr;
|
||||
struct igen6_pvt *pvt;
|
||||
int imc_num, rc;
|
||||
u64 mchbar;
|
||||
int rc;
|
||||
|
||||
pvt = kzalloc_obj(*igen6_pvt);
|
||||
if (!pvt)
|
||||
return NULL;
|
||||
|
||||
rc = get_mchbar(pdev, &mchbar);
|
||||
if (rc) {
|
||||
kfree(pvt);
|
||||
if (rc)
|
||||
return NULL;
|
||||
|
||||
imc_num = get_imc_num(mchbar);
|
||||
if (!imc_num) {
|
||||
igen6_printk(KERN_ERR, "No mc found.\n");
|
||||
return NULL;
|
||||
}
|
||||
edac_dbg(2, "%d mcs found.\n", imc_num);
|
||||
|
||||
/* Use the runtime detected IMC count. */
|
||||
if (res_cfg->num_imc != imc_num)
|
||||
res_cfg->num_imc = imc_num;
|
||||
|
||||
pvt = kzalloc_flex(*pvt, imc, imc_num);
|
||||
if (!pvt)
|
||||
return NULL;
|
||||
|
||||
memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2);
|
||||
if (!memss_pma_cr) {
|
||||
@@ -1572,12 +1654,6 @@ static void igen6_check(struct mem_ctl_info *mci)
|
||||
irq_work_queue(&ecclog_irq_work);
|
||||
}
|
||||
|
||||
/* Check whether the memory controller is absent. */
|
||||
static bool igen6_imc_absent(void __iomem *window)
|
||||
{
|
||||
return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
|
||||
}
|
||||
|
||||
static void imc_release(struct device *dev)
|
||||
{
|
||||
/* Nothing to do, the 'imc' owns the 'dev' and will also release it. */
|
||||
@@ -1689,26 +1765,15 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
|
||||
{
|
||||
void __iomem *window;
|
||||
int lmc, pmc, rc;
|
||||
u64 base;
|
||||
|
||||
for (lmc = 0, pmc = 0; pmc < NUM_IMC; pmc++) {
|
||||
base = mchbar + pmc * MCHBAR_SIZE;
|
||||
window = ioremap(base, MCHBAR_SIZE);
|
||||
if (!window) {
|
||||
igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx for mc%d\n", base, pmc);
|
||||
rc = -ENOMEM;
|
||||
goto out_unregister_mcis;
|
||||
}
|
||||
|
||||
if (igen6_imc_absent(window)) {
|
||||
iounmap(window);
|
||||
edac_dbg(2, "Skip absent mc%d\n", pmc);
|
||||
for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
|
||||
window = map_imc_window(mchbar, pmc);
|
||||
if (!window)
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = igen6_register_mci(lmc, window, pdev);
|
||||
if (rc)
|
||||
goto out_iounmap;
|
||||
goto err_unregister;
|
||||
|
||||
/* Done, if all present MCs are detected and registered. */
|
||||
if (++lmc >= res_cfg->num_imc)
|
||||
@@ -1728,10 +1793,8 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
|
||||
|
||||
return 0;
|
||||
|
||||
out_iounmap:
|
||||
err_unregister:
|
||||
iounmap(window);
|
||||
|
||||
out_unregister_mcis:
|
||||
igen6_unregister_mcis();
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -729,10 +729,8 @@ static int thunderx_lmc_probe(struct pci_dev *pdev,
|
||||
thunderx_lmc_err_isr,
|
||||
thunderx_lmc_threaded_isr, 0,
|
||||
"[EDAC] ThunderX LMC", mci);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0));
|
||||
|
||||
|
||||
@@ -1924,11 +1924,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
|
||||
rc = devm_request_irq(&pdev->dev, irq,
|
||||
xgene_edac_isr, IRQF_SHARED,
|
||||
dev_name(&pdev->dev), edac);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not request IRQ %d\n", irq);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,6 @@ static const struct x86_cpu_id amd_atl_cpuids[] = {
|
||||
X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL),
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
|
||||
|
||||
static int __init amd_atl_init(void)
|
||||
{
|
||||
|
||||
@@ -771,9 +771,5 @@ int get_address_map(struct addr_ctx *ctx)
|
||||
|
||||
dump_address_map(&ctx->map);
|
||||
|
||||
ret = validate_address_map(ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return validate_address_map(ctx);
|
||||
}
|
||||
|
||||
@@ -598,9 +598,6 @@ struct mem_ctl_info {
|
||||
int op_state;
|
||||
|
||||
struct dentry *debugfs;
|
||||
u8 fake_inject_layer[EDAC_MAX_LAYERS];
|
||||
bool fake_inject_ue;
|
||||
u16 fake_inject_count;
|
||||
|
||||
/*
|
||||
* Memory Controller hierarchy
|
||||
|
||||
Reference in New Issue
Block a user