1
0
Fork 0

* Stratix10 SDRAM support to altera_edac (Thor Thayer)

* the usual misc fixes all over the place
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAlsVQ/cACgkQEsHwGGHe
 VUrrVA/+NHpCe0dT97Q4NHZejusOZvujNdOeAZVMNVcg5fwFmwwXkStazY+J/lmK
 Zjh2P/JGVoL9CPCn/ya+F6KSDJPLaq0AQCpogEhDqEZrRcutWu/CtH2XEbjyknME
 LCGfaKRBbi1zxCEm8qCk3ghWO3rXtM14jLkuqPVxtCsBT9HXcBAXU/ivok23Yb4r
 d/NcoeV5veqFzdPMxkYRJFN9Me4uVN+MzoN9hhxTYll5n0eWhb7Aw8Aon8VR6OH5
 LSJFmxFOlE7OPHQ0AjBRiF0Q9MyUSFTWvn+i4rhSq1z8SCPZyPkG2YRNVDWS6Py9
 cLXSNMnv6ybPBst9LUMXjN06DBsnzeWNcsFFPw5uunD9JRFuaoxUgk633BLWMy2z
 Tx4fiwOligx65Fc4udQ1n2Ho5HKZaTHlafYDatYt3jecx71BQywnMi1zuwMnF+Id
 iHW0J413h1P0UZfeTZHGfv+THQNb+IoVDjWdY2nNil45OR1QCBva+CQVKCcB8xox
 RCW6yL1S7V2dzd4255F/xpMrbkvxeAaOBWWJqEQeUdsFGP//6Ub9Gy0IKsSDrHQ6
 3BtoOxGmBKCBCpysN7OtUUvC28Dd9PQCkH+Bn3SAjySWnEiTEdv6QeLdScJrsz75
 lcF4d17EKt2VtdX6z15QAGT9LMbRR177pizea03XDeFwb+eR0cA=
 =yBvi
 -----END PGP SIGNATURE-----

Merge tag 'edac_for_4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp

Pull EDAC updates from Borislav Petkov:

 - Stratix10 SDRAM support to altera_edac (Thor Thayer)

 - the usual misc fixes all over the place

[ Also, shared branch for socfpga_stratix10.dtsi file changes with the
  socfpga tree ]

* tag 'edac_for_4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp:
  EDAC, ghes: Make platform-based whitelisting x86-only
  EDAC, altera: Fix ARM64 build warning
  EDAC, skx: Fix skx_edac build error when ACPI_NFIT=m
  EDAC, ghes: Use BIT() macro
  EDAC, ghes: Add DDR4 and NVDIMM memory types
  EDAC, altera: Handle SDRAM Uncorrectable Errors on Stratix10
  Documentation: dt: edac: Move Altera SOCFPGA EDAC file
  EDAC, altera: Add support for Stratix10 SDRAM EDAC
  Documentation: dt: socfpga: Add Stratix10 ECC Manager binding
  EDAC, ghes: Remove unused argument to ghes_edac_report_mem_error()
  arm64: dts: stratix10: add sdram ecc
  EDAC, i7core: Fix spelling mistake: "redundacy" -> "redundancy"
  EDAC, ghes: Add a null pointer check in ghes_edac_unregister()
  ghes, EDAC: Fix ghes_edac registration
  arm64: dts: stratix10: Change pad skew values for EMAC0 PHY driver
  ARM: dts: consistently use 'atmel' as at24 manufacturer in cyclone5
  arm64: dts: stratix10: Add PL330 DMAC to Stratix10 dts
  arm64: dts: stratix10: enable i2c, add i2c periperals
  arm64: dts: stratix10: use clock bindings for the Stratix10 platform
hifive-unleashed-5.1
Linus Torvalds 2018-06-06 15:36:13 -07:00
commit 8450493076
11 changed files with 817 additions and 70 deletions

View File

@ -231,3 +231,38 @@ Example:
<48 IRQ_TYPE_LEVEL_HIGH>;
};
};
Stratix10 SoCFPGA ECC Manager
The Stratix10 SoC ECC Manager handles the IRQs for each peripheral
in a shared register similar to the Arria10. However, ECC requires
access to registers that can only be read from Secure Monitor with
SMC calls. Therefore the device tree is slightly different.
Required Properties:
- compatible : Should be "altr,socfpga-s10-ecc-manager"
- interrupts : Should be single bit error interrupt, then double bit error
interrupt.
- interrupt-controller : boolean indicator that ECC Manager is an interrupt controller
- #interrupt-cells : must be set to 2.
Subcomponents:
SDRAM ECC
Required Properties:
- compatible : Should be "altr,sdram-edac-s10"
- interrupts : Should be single bit error interrupt, then double bit error
interrupt, in this order.
Example:
eccmgr {
compatible = "altr,socfpga-s10-ecc-manager";
interrupts = <0 15 4>, <0 95 4>;
interrupt-controller;
#interrupt-cells = <2>;
sdramedac {
compatible = "altr,sdram-edac-s10";
interrupts = <16 4>, <48 4>;
};
};

View File

@ -161,7 +161,7 @@
};
at24@50 {
compatible = "at24,24c01";
compatible = "atmel,24c01";
pagesize = <8>;
reg = <0x50>;
};
@ -213,7 +213,7 @@
#size-cells = <0>;
reg = <6>;
eeprom@51 {
compatible = "at,24c01";
compatible = "atmel,24c01";
pagesize = <8>;
reg = <0x51>;
};
@ -224,7 +224,7 @@
#size-cells = <0>;
reg = <7>;
eeprom@51 {
compatible = "at,24c01";
compatible = "atmel,24c01";
pagesize = <8>;
reg = <0x51>;
};

View File

@ -17,6 +17,7 @@
/dts-v1/;
#include <dt-bindings/reset/altr,rst-mgr-s10.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/clock/stratix10-clock.h>
/ {
compatible = "altr,socfpga-stratix10";
@ -92,9 +93,32 @@
interrupt-parent = <&intc>;
ranges = <0 0 0 0xffffffff>;
clkmgr@ffd1000 {
compatible = "altr,clk-mgr";
clkmgr: clock-controller@ffd10000 {
compatible = "intel,stratix10-clkmgr";
reg = <0xffd10000 0x1000>;
#clock-cells = <1>;
};
clocks {
cb_intosc_hs_div2_clk: cb-intosc-hs-div2-clk {
#clock-cells = <0>;
compatible = "fixed-clock";
};
cb_intosc_ls_clk: cb-intosc-ls-clk {
#clock-cells = <0>;
compatible = "fixed-clock";
};
f2s_free_clk: f2s-free-clk {
#clock-cells = <0>;
compatible = "fixed-clock";
};
osc1: osc1 {
#clock-cells = <0>;
compatible = "fixed-clock";
};
};
gmac0: ethernet@ff800000 {
@ -105,6 +129,8 @@
mac-address = [00 00 00 00 00 00];
resets = <&rst EMAC0_RESET>;
reset-names = "stmmaceth";
clocks = <&clkmgr STRATIX10_EMAC0_CLK>;
clock-names = "stmmaceth";
status = "disabled";
};
@ -116,6 +142,8 @@
mac-address = [00 00 00 00 00 00];
resets = <&rst EMAC1_RESET>;
reset-names = "stmmaceth";
clocks = <&clkmgr STRATIX10_EMAC1_CLK>;
clock-names = "stmmaceth";
status = "disabled";
};
@ -127,6 +155,8 @@
mac-address = [00 00 00 00 00 00];
resets = <&rst EMAC2_RESET>;
reset-names = "stmmaceth";
clocks = <&clkmgr STRATIX10_EMAC2_CLK>;
clock-names = "stmmaceth";
status = "disabled";
};
@ -177,6 +207,7 @@
reg = <0xffc02800 0x100>;
interrupts = <0 103 4>;
resets = <&rst I2C0_RESET>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
status = "disabled";
};
@ -187,6 +218,7 @@
reg = <0xffc02900 0x100>;
interrupts = <0 104 4>;
resets = <&rst I2C1_RESET>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
status = "disabled";
};
@ -197,6 +229,7 @@
reg = <0xffc02a00 0x100>;
interrupts = <0 105 4>;
resets = <&rst I2C2_RESET>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
status = "disabled";
};
@ -207,6 +240,7 @@
reg = <0xffc02b00 0x100>;
interrupts = <0 106 4>;
resets = <&rst I2C3_RESET>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
status = "disabled";
};
@ -217,6 +251,7 @@
reg = <0xffc02c00 0x100>;
interrupts = <0 107 4>;
resets = <&rst I2C4_RESET>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
status = "disabled";
};
@ -229,6 +264,9 @@
fifo-depth = <0x400>;
resets = <&rst SDMMC_RESET>;
reset-names = "reset";
clocks = <&clkmgr STRATIX10_L4_MP_CLK>,
<&clkmgr STRATIX10_SDMMC_CLK>;
clock-names = "biu", "ciu";
status = "disabled";
};
@ -237,6 +275,25 @@
reg = <0xffe00000 0x100000>;
};
pdma: pdma@ffda0000 {
compatible = "arm,pl330", "arm,primecell";
reg = <0xffda0000 0x1000>;
interrupts = <0 81 4>,
<0 82 4>,
<0 83 4>,
<0 84 4>,
<0 85 4>,
<0 86 4>,
<0 87 4>,
<0 88 4>,
<0 89 4>;
#dma-cells = <1>;
#dma-channels = <8>;
#dma-requests = <32>;
clocks = <&clkmgr STRATIX10_L4_MAIN_CLK>;
clock-names = "apb_pclk";
};
rst: rstmgr@ffd11000 {
#reset-cells = <1>;
compatible = "altr,rst-mgr";
@ -288,24 +345,32 @@
compatible = "snps,dw-apb-timer";
interrupts = <0 113 4>;
reg = <0xffc03000 0x100>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
clock-names = "timer";
};
timer1: timer1@ffc03100 {
compatible = "snps,dw-apb-timer";
interrupts = <0 114 4>;
reg = <0xffc03100 0x100>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
clock-names = "timer";
};
timer2: timer2@ffd00000 {
compatible = "snps,dw-apb-timer";
interrupts = <0 115 4>;
reg = <0xffd00000 0x100>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
clock-names = "timer";
};
timer3: timer3@ffd00100 {
compatible = "snps,dw-apb-timer";
interrupts = <0 116 4>;
reg = <0xffd00100 0x100>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
clock-names = "timer";
};
uart0: serial0@ffc02000 {
@ -315,6 +380,7 @@
reg-shift = <2>;
reg-io-width = <4>;
resets = <&rst UART0_RESET>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
status = "disabled";
};
@ -325,6 +391,7 @@
reg-shift = <2>;
reg-io-width = <4>;
resets = <&rst UART1_RESET>;
clocks = <&clkmgr STRATIX10_L4_SP_CLK>;
status = "disabled";
};
@ -387,5 +454,17 @@
resets = <&rst WATCHDOG3_RESET>;
status = "disabled";
};
eccmgr {
compatible = "altr,socfpga-s10-ecc-manager";
interrupts = <0 15 4>, <0 95 4>;
interrupt-controller;
#interrupt-cells = <2>;
sdramedac {
compatible = "altr,sdram-edac-s10";
interrupts = <16 4>, <48 4>;
};
};
};
};

View File

@ -50,6 +50,21 @@
/* We expect the bootloader to fill in the reg */
reg = <0 0 0 0>;
};
ref_033v: 033-v-ref {
compatible = "regulator-fixed";
regulator-name = "0.33V";
regulator-min-microvolt = <330000>;
regulator-max-microvolt = <330000>;
};
soc {
clocks {
osc1 {
clock-frequency = <25000000>;
};
};
};
};
&gpio1 {
@ -79,7 +94,7 @@
rxd2-skew-ps = <420>; /* 0ps */
rxd3-skew-ps = <420>; /* 0ps */
txen-skew-ps = <0>; /* -420ps */
txc-skew-ps = <1860>; /* 960ps */
txc-skew-ps = <900>; /* 0ps */
rxdv-skew-ps = <420>; /* 0ps */
rxc-skew-ps = <1680>; /* 780ps */
};
@ -105,3 +120,30 @@
&watchdog0 {
status = "okay";
};
&i2c1 {
status = "okay";
clock-frequency = <100000>;
adc@14 {
compatible = "lltc,ltc2497";
reg = <0x14>;
vref-supply = <&ref_033v>;
};
temp@4c {
compatible = "maxim,max1619";
reg = <0x4c>;
};
eeprom@51 {
compatible = "atmel,24c32";
reg = <0x51>;
pagesize = <32>;
};
rtc@68 {
compatible = "dallas,ds1339";
reg = <0x68>;
};
};

View File

@ -481,7 +481,7 @@ static void ghes_do_proc(struct ghes *ghes,
if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
ghes_edac_report_mem_error(ghes, sev, mem_err);
ghes_edac_report_mem_error(sev, mem_err);
arch_apei_report_mem_error(sev, mem_err);
ghes_handle_memory_failure(gdata, sev);
@ -1087,10 +1087,6 @@ static int ghes_probe(struct platform_device *ghes_dev)
goto err;
}
rc = ghes_edac_register(ghes, &ghes_dev->dev);
if (rc < 0)
goto err;
switch (generic->notify.type) {
case ACPI_HEST_NOTIFY_POLLED:
timer_setup(&ghes->timer, ghes_poll_func, TIMER_DEFERRABLE);
@ -1102,14 +1098,14 @@ static int ghes_probe(struct platform_device *ghes_dev)
if (rc) {
pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n",
generic->header.source_id);
goto err_edac_unreg;
goto err;
}
rc = request_irq(ghes->irq, ghes_irq_func, IRQF_SHARED,
"GHES IRQ", ghes);
if (rc) {
pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n",
generic->header.source_id);
goto err_edac_unreg;
goto err;
}
break;
@ -1132,14 +1128,16 @@ static int ghes_probe(struct platform_device *ghes_dev)
default:
BUG();
}
platform_set_drvdata(ghes_dev, ghes);
ghes_edac_register(ghes, &ghes_dev->dev);
/* Handle any pending errors right away */
ghes_proc(ghes);
return 0;
err_edac_unreg:
ghes_edac_unregister(ghes);
err:
if (ghes) {
ghes_fini(ghes);

View File

@ -232,6 +232,7 @@ config EDAC_SBRIDGE
config EDAC_SKX
tristate "Intel Skylake server Integrated MC"
depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG
depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_SKX can't be y
select DMI
help
Support for error detection and correction the Intel
@ -374,7 +375,7 @@ config EDAC_THUNDERX
config EDAC_ALTERA
bool "Altera SOCFPGA ECC"
depends on EDAC=y && ARCH_SOCFPGA
depends on EDAC=y && (ARCH_SOCFPGA || ARCH_STRATIX10)
help
Support for error detection and correction on the
Altera SOCs. This must be selected for SDRAM ECC.

View File

@ -1,20 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017-2018, Intel Corporation. All rights reserved
* Copyright Altera Corporation (C) 2014-2016. All rights reserved.
* Copyright 2011-2012 Calxeda, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* Adapted from the highbank_mc_edac driver.
*/
#include <asm/cacheflush.h>
@ -26,6 +14,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/notifier.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
@ -80,6 +69,25 @@ static const struct altr_sdram_prv_data a10_data = {
.ue_set_mask = A10_DIAGINT_TDERRA_MASK,
};
static const struct altr_sdram_prv_data s10_data = {
.ecc_ctrl_offset = S10_ECCCTRL1_OFST,
.ecc_ctl_en_mask = A10_ECCCTRL1_ECC_EN,
.ecc_stat_offset = S10_INTSTAT_OFST,
.ecc_stat_ce_mask = A10_INTSTAT_SBEERR,
.ecc_stat_ue_mask = A10_INTSTAT_DBEERR,
.ecc_saddr_offset = S10_SERRADDR_OFST,
.ecc_daddr_offset = S10_DERRADDR_OFST,
.ecc_irq_en_offset = S10_ERRINTEN_OFST,
.ecc_irq_en_mask = A10_ECC_IRQ_EN_MASK,
.ecc_irq_clr_offset = S10_INTSTAT_OFST,
.ecc_irq_clr_mask = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR),
.ecc_cnt_rst_offset = S10_ECCCTRL1_OFST,
.ecc_cnt_rst_mask = A10_ECC_CNT_RESET_MASK,
.ce_ue_trgr_offset = S10_DIAGINTTEST_OFST,
.ce_set_mask = A10_DIAGINT_TSERRA_MASK,
.ue_set_mask = A10_DIAGINT_TDERRA_MASK,
};
/*********************** EDAC Memory Controller Functions ****************/
/* The SDRAM controller uses the EDAC Memory Controller framework. */
@ -231,6 +239,7 @@ static unsigned long get_total_mem(void)
static const struct of_device_id altr_sdram_ctrl_of_match[] = {
{ .compatible = "altr,sdram-edac", .data = &c5_data},
{ .compatible = "altr,sdram-edac-a10", .data = &a10_data},
{ .compatible = "altr,sdram-edac-s10", .data = &s10_data},
{},
};
MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match);
@ -477,6 +486,292 @@ static int altr_sdram_remove(struct platform_device *pdev)
return 0;
}
/**************** Stratix 10 EDAC Memory Controller Functions ************/
/**
* s10_protected_reg_write
* Write to a protected SMC register.
* @context: Not used.
* @reg: Address of register
* @value: Value to write
* Return: INTEL_SIP_SMC_STATUS_OK (0) on success
* INTEL_SIP_SMC_REG_ERROR on error
* INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
*/
static int s10_protected_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct arm_smccc_res result;
arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, reg, val, 0, 0,
0, 0, 0, &result);
return (int)result.a0;
}
/**
* s10_protected_reg_read
* Read the status of a protected SMC register
* @context: Not used.
* @reg: Address of register
* @value: Value read.
* Return: INTEL_SIP_SMC_STATUS_OK (0) on success
* INTEL_SIP_SMC_REG_ERROR on error
* INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
*/
static int s10_protected_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
struct arm_smccc_res result;
arm_smccc_smc(INTEL_SIP_SMC_REG_READ, reg, 0, 0, 0,
0, 0, 0, &result);
*val = (unsigned int)result.a1;
return (int)result.a0;
}
static bool s10_sdram_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case S10_ECCCTRL1_OFST:
case S10_ERRINTEN_OFST:
case S10_INTMODE_OFST:
case S10_INTSTAT_OFST:
case S10_DIAGINTTEST_OFST:
case S10_SYSMGR_ECC_INTMASK_VAL_OFST:
case S10_SYSMGR_ECC_INTMASK_SET_OFST:
case S10_SYSMGR_ECC_INTMASK_CLR_OFST:
return true;
}
return false;
}
static bool s10_sdram_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case S10_ECCCTRL1_OFST:
case S10_ERRINTEN_OFST:
case S10_INTMODE_OFST:
case S10_INTSTAT_OFST:
case S10_DERRADDR_OFST:
case S10_SERRADDR_OFST:
case S10_DIAGINTTEST_OFST:
case S10_SYSMGR_ECC_INTMASK_VAL_OFST:
case S10_SYSMGR_ECC_INTMASK_SET_OFST:
case S10_SYSMGR_ECC_INTMASK_CLR_OFST:
case S10_SYSMGR_ECC_INTSTAT_SERR_OFST:
case S10_SYSMGR_ECC_INTSTAT_DERR_OFST:
return true;
}
return false;
}
static bool s10_sdram_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case S10_ECCCTRL1_OFST:
case S10_ERRINTEN_OFST:
case S10_INTMODE_OFST:
case S10_INTSTAT_OFST:
case S10_DERRADDR_OFST:
case S10_SERRADDR_OFST:
case S10_DIAGINTTEST_OFST:
case S10_SYSMGR_ECC_INTMASK_VAL_OFST:
case S10_SYSMGR_ECC_INTMASK_SET_OFST:
case S10_SYSMGR_ECC_INTMASK_CLR_OFST:
case S10_SYSMGR_ECC_INTSTAT_SERR_OFST:
case S10_SYSMGR_ECC_INTSTAT_DERR_OFST:
return true;
}
return false;
}
static const struct regmap_config s10_sdram_regmap_cfg = {
.name = "s10_ddr",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0xffffffff,
.writeable_reg = s10_sdram_writeable_reg,
.readable_reg = s10_sdram_readable_reg,
.volatile_reg = s10_sdram_volatile_reg,
.reg_read = s10_protected_reg_read,
.reg_write = s10_protected_reg_write,
.use_single_rw = true,
};
static int altr_s10_sdram_probe(struct platform_device *pdev)
{
const struct of_device_id *id;
struct edac_mc_layer layers[2];
struct mem_ctl_info *mci;
struct altr_sdram_mc_data *drvdata;
const struct altr_sdram_prv_data *priv;
struct regmap *regmap;
struct dimm_info *dimm;
u32 read_reg;
int irq, ret = 0;
unsigned long mem_size;
id = of_match_device(altr_sdram_ctrl_of_match, &pdev->dev);
if (!id)
return -ENODEV;
/* Grab specific offsets and masks for Stratix10 */
priv = of_match_node(altr_sdram_ctrl_of_match,
pdev->dev.of_node)->data;
regmap = devm_regmap_init(&pdev->dev, NULL, (void *)priv,
&s10_sdram_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/* Validate the SDRAM controller has ECC enabled */
if (regmap_read(regmap, priv->ecc_ctrl_offset, &read_reg) ||
((read_reg & priv->ecc_ctl_en_mask) != priv->ecc_ctl_en_mask)) {
edac_printk(KERN_ERR, EDAC_MC,
"No ECC/ECC disabled [0x%08X]\n", read_reg);
return -ENODEV;
}
/* Grab memory size from device tree. */
mem_size = get_total_mem();
if (!mem_size) {
edac_printk(KERN_ERR, EDAC_MC, "Unable to calculate memory size\n");
return -ENODEV;
}
/* Ensure the SDRAM Interrupt is disabled */
if (regmap_update_bits(regmap, priv->ecc_irq_en_offset,
priv->ecc_irq_en_mask, 0)) {
edac_printk(KERN_ERR, EDAC_MC,
"Error disabling SDRAM ECC IRQ\n");
return -ENODEV;
}
/* Toggle to clear the SDRAM Error count */
if (regmap_update_bits(regmap, priv->ecc_cnt_rst_offset,
priv->ecc_cnt_rst_mask,
priv->ecc_cnt_rst_mask)) {
edac_printk(KERN_ERR, EDAC_MC,
"Error clearing SDRAM ECC count\n");
return -ENODEV;
}
if (regmap_update_bits(regmap, priv->ecc_cnt_rst_offset,
priv->ecc_cnt_rst_mask, 0)) {
edac_printk(KERN_ERR, EDAC_MC,
"Error clearing SDRAM ECC count\n");
return -ENODEV;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
edac_printk(KERN_ERR, EDAC_MC,
"No irq %d in DT\n", irq);
return -ENODEV;
}
layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
layers[0].size = 1;
layers[0].is_virt_csrow = true;
layers[1].type = EDAC_MC_LAYER_CHANNEL;
layers[1].size = 1;
layers[1].is_virt_csrow = false;
mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
sizeof(struct altr_sdram_mc_data));
if (!mci)
return -ENOMEM;
mci->pdev = &pdev->dev;
drvdata = mci->pvt_info;
drvdata->mc_vbase = regmap;
drvdata->data = priv;
platform_set_drvdata(pdev, mci);
if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
edac_printk(KERN_ERR, EDAC_MC,
"Unable to get managed device resource\n");
ret = -ENOMEM;
goto free;
}
mci->mtype_cap = MEM_FLAG_DDR3;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_SECDED;
mci->mod_name = EDAC_MOD_STR;
mci->ctl_name = dev_name(&pdev->dev);
mci->scrub_mode = SCRUB_SW_SRC;
mci->dev_name = dev_name(&pdev->dev);
dimm = *mci->dimms;
dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1;
dimm->grain = 8;
dimm->dtype = DEV_X8;
dimm->mtype = MEM_DDR3;
dimm->edac_mode = EDAC_SECDED;
ret = edac_mc_add_mc(mci);
if (ret < 0)
goto err;
ret = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler,
IRQF_SHARED, dev_name(&pdev->dev), mci);
if (ret < 0) {
edac_mc_printk(mci, KERN_ERR,
"Unable to request irq %d\n", irq);
ret = -ENODEV;
goto err2;
}
if (regmap_write(regmap, S10_SYSMGR_ECC_INTMASK_CLR_OFST,
S10_DDR0_IRQ_MASK)) {
edac_printk(KERN_ERR, EDAC_MC,
"Error clearing SDRAM ECC count\n");
return -ENODEV;
}
if (regmap_update_bits(drvdata->mc_vbase, priv->ecc_irq_en_offset,
priv->ecc_irq_en_mask, priv->ecc_irq_en_mask)) {
edac_mc_printk(mci, KERN_ERR,
"Error enabling SDRAM ECC IRQ\n");
ret = -ENODEV;
goto err2;
}
altr_sdr_mc_create_debugfs_nodes(mci);
devres_close_group(&pdev->dev, NULL);
return 0;
err2:
edac_mc_del_mc(&pdev->dev);
err:
devres_release_group(&pdev->dev, NULL);
free:
edac_mc_free(mci);
edac_printk(KERN_ERR, EDAC_MC,
"EDAC Probe Failed; Error %d\n", ret);
return ret;
}
static int altr_s10_sdram_remove(struct platform_device *pdev)
{
struct mem_ctl_info *mci = platform_get_drvdata(pdev);
edac_mc_del_mc(&pdev->dev);
edac_mc_free(mci);
platform_set_drvdata(pdev, NULL);
return 0;
}
/************** </Stratix10 EDAC Memory Controller Functions> ***********/
/*
* If you want to suspend, need to disable EDAC by removing it
* from the device tree or defconfig.
@ -508,6 +803,20 @@ static struct platform_driver altr_sdram_edac_driver = {
module_platform_driver(altr_sdram_edac_driver);
static struct platform_driver altr_s10_sdram_edac_driver = {
.probe = altr_s10_sdram_probe,
.remove = altr_s10_sdram_remove,
.driver = {
.name = "altr_s10_sdram_edac",
#ifdef CONFIG_PM
.pm = &altr_sdram_pm_ops,
#endif
.of_match_table = altr_sdram_ctrl_of_match,
},
};
module_platform_driver(altr_s10_sdram_edac_driver);
/************************* EDAC Parent Probe *************************/
static const struct of_device_id altr_edac_device_of_match[];
@ -1106,7 +1415,7 @@ static void *ocram_alloc_mem(size_t size, void **other)
static void ocram_free_mem(void *p, size_t size, void *other)
{
gen_pool_free((struct gen_pool *)other, (u32)p, size);
gen_pool_free((struct gen_pool *)other, (unsigned long)p, size);
}
static const struct edac_device_prv_data ocramecc_data = {
@ -1925,6 +2234,171 @@ static struct platform_driver altr_edac_a10_driver = {
};
module_platform_driver(altr_edac_a10_driver);
/************** Stratix 10 EDAC Device Controller Functions> ************/
#define to_s10edac(p, m) container_of(p, struct altr_stratix10_edac, m)
/*
* The double bit error is handled through SError which is fatal. This is
* called as a panic notifier to printout ECC error info as part of the panic.
*/
static int s10_edac_dberr_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct altr_stratix10_edac *edac = to_s10edac(this, panic_notifier);
int err_addr, dberror;
s10_protected_reg_read(edac, S10_SYSMGR_ECC_INTSTAT_DERR_OFST,
&dberror);
/* Remember the UE Errors for a reboot */
s10_protected_reg_write(edac, S10_SYSMGR_UE_VAL_OFST, dberror);
if (dberror & S10_DDR0_IRQ_MASK) {
s10_protected_reg_read(edac, S10_DERRADDR_OFST, &err_addr);
/* Remember the UE Error address */
s10_protected_reg_write(edac, S10_SYSMGR_UE_ADDR_OFST,
err_addr);
edac_printk(KERN_ERR, EDAC_MC,
"EDAC: [Uncorrectable errors @ 0x%08X]\n\n",
err_addr);
}
return NOTIFY_DONE;
}
static void altr_edac_s10_irq_handler(struct irq_desc *desc)
{
struct altr_stratix10_edac *edac = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
int irq = irq_desc_get_irq(desc);
int bit, sm_offset, irq_status;
sm_offset = S10_SYSMGR_ECC_INTSTAT_SERR_OFST;
chained_irq_enter(chip, desc);
s10_protected_reg_read(NULL, sm_offset, &irq_status);
for_each_set_bit(bit, (unsigned long *)&irq_status, 32) {
irq = irq_linear_revmap(edac->domain, bit);
if (irq)
generic_handle_irq(irq);
}
chained_irq_exit(chip, desc);
}
static void s10_eccmgr_irq_mask(struct irq_data *d)
{
struct altr_stratix10_edac *edac = irq_data_get_irq_chip_data(d);
s10_protected_reg_write(edac, S10_SYSMGR_ECC_INTMASK_SET_OFST,
BIT(d->hwirq));
}
static void s10_eccmgr_irq_unmask(struct irq_data *d)
{
struct altr_stratix10_edac *edac = irq_data_get_irq_chip_data(d);
s10_protected_reg_write(edac, S10_SYSMGR_ECC_INTMASK_CLR_OFST,
BIT(d->hwirq));
}
static int s10_eccmgr_irqdomain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
struct altr_stratix10_edac *edac = d->host_data;
irq_set_chip_and_handler(irq, &edac->irq_chip, handle_simple_irq);
irq_set_chip_data(irq, edac);
irq_set_noprobe(irq);
return 0;
}
static const struct irq_domain_ops s10_eccmgr_ic_ops = {
.map = s10_eccmgr_irqdomain_map,
.xlate = irq_domain_xlate_twocell,
};
static int altr_edac_s10_probe(struct platform_device *pdev)
{
struct altr_stratix10_edac *edac;
struct device_node *child;
int dberror, err_addr;
edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL);
if (!edac)
return -ENOMEM;
edac->dev = &pdev->dev;
platform_set_drvdata(pdev, edac);
INIT_LIST_HEAD(&edac->s10_ecc_devices);
edac->irq_chip.name = pdev->dev.of_node->name;
edac->irq_chip.irq_mask = s10_eccmgr_irq_mask;
edac->irq_chip.irq_unmask = s10_eccmgr_irq_unmask;
edac->domain = irq_domain_add_linear(pdev->dev.of_node, 64,
&s10_eccmgr_ic_ops, edac);
if (!edac->domain) {
dev_err(&pdev->dev, "Error adding IRQ domain\n");
return -ENOMEM;
}
edac->sb_irq = platform_get_irq(pdev, 0);
if (edac->sb_irq < 0) {
dev_err(&pdev->dev, "No SBERR IRQ resource\n");
return edac->sb_irq;
}
irq_set_chained_handler_and_data(edac->sb_irq,
altr_edac_s10_irq_handler,
edac);
edac->panic_notifier.notifier_call = s10_edac_dberr_handler;
atomic_notifier_chain_register(&panic_notifier_list,
&edac->panic_notifier);
/* Printout a message if uncorrectable error previously. */
s10_protected_reg_read(edac, S10_SYSMGR_UE_VAL_OFST, &dberror);
if (dberror) {
s10_protected_reg_read(edac, S10_SYSMGR_UE_ADDR_OFST,
&err_addr);
edac_printk(KERN_ERR, EDAC_DEVICE,
"Previous Boot UE detected[0x%X] @ 0x%X\n",
dberror, err_addr);
/* Reset the sticky registers */
s10_protected_reg_write(edac, S10_SYSMGR_UE_VAL_OFST, 0);
s10_protected_reg_write(edac, S10_SYSMGR_UE_ADDR_OFST, 0);
}
for_each_child_of_node(pdev->dev.of_node, child) {
if (!of_device_is_available(child))
continue;
if (of_device_is_compatible(child, "altr,sdram-edac-s10"))
of_platform_populate(pdev->dev.of_node,
altr_sdram_ctrl_of_match,
NULL, &pdev->dev);
}
return 0;
}
static const struct of_device_id altr_edac_s10_of_match[] = {
{ .compatible = "altr,socfpga-s10-ecc-manager" },
{},
};
MODULE_DEVICE_TABLE(of, altr_edac_s10_of_match);
static struct platform_driver altr_edac_s10_driver = {
.probe = altr_edac_s10_probe,
.driver = {
.name = "socfpga_s10_ecc_manager",
.of_match_table = altr_edac_s10_of_match,
},
};
module_platform_driver(altr_edac_s10_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Thor Thayer");
MODULE_DESCRIPTION("EDAC Driver for Altera Memories");

View File

@ -1,23 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
*
* Copyright (C) 2017-2018, Intel Corporation
* Copyright (C) 2015 Altera Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ALTERA_EDAC_H
#define _ALTERA_EDAC_H
#include <linux/arm-smccc.h>
#include <linux/edac.h>
#include <linux/types.h>
@ -94,6 +84,7 @@
/* SDRAM Controller Address Width Register */
#define CV_DRAMADDRW 0xFFC2502C
#define A10_DRAMADDRW 0xFFCFA0A8
#define S10_DRAMADDRW 0xF80110E0
/* SDRAM Controller Address Widths Field Register */
#define DRAMADDRW_COLBIT_MASK 0x001F
@ -115,6 +106,7 @@
/* SDRAM Controller Interface Data Width Register */
#define CV_DRAMIFWIDTH 0xFFC25030
#define A10_DRAMIFWIDTH 0xFFCFB008
#define S10_DRAMIFWIDTH 0xF8011008
/* SDRAM Controller Interface Data Width Defines */
#define CV_DRAMIFWIDTH_16B_ECC 24
@ -164,6 +156,34 @@
#define A10_INTMASK_CLR_OFST 0x10
#define A10_DDR0_IRQ_MASK BIT(17)
/************* Stratix10 Defines **************/
/* SDRAM Controller EccCtrl Register */
#define S10_ECCCTRL1_OFST 0xF8011100
/* SDRAM Controller DRAM IRQ Register */
#define S10_ERRINTEN_OFST 0xF8011110
/* SDRAM Interrupt Mode Register */
#define S10_INTMODE_OFST 0xF801111C
/* SDRAM Controller Error Status Register */
#define S10_INTSTAT_OFST 0xF8011120
/* SDRAM Controller ECC Error Address Register */
#define S10_DERRADDR_OFST 0xF801112C
#define S10_SERRADDR_OFST 0xF8011130
/* SDRAM Controller ECC Diagnostic Register */
#define S10_DIAGINTTEST_OFST 0xF8011124
/* SDRAM Single Bit Error Count Compare Set Register */
#define S10_SERRCNTREG_OFST 0xF801113C
/* Sticky registers for Uncorrected Errors */
#define S10_SYSMGR_UE_VAL_OFST 0xFFD12220
#define S10_SYSMGR_UE_ADDR_OFST 0xFFD12224
struct altr_sdram_prv_data {
int ecc_ctrl_offset;
int ecc_ctl_en_mask;
@ -296,6 +316,18 @@ struct altr_sdram_mc_data {
/* A10 ECC Controller memory initialization timeout */
#define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000
/************* Stratix10 Defines **************/
/* Stratix10 ECC Manager Defines */
#define S10_SYSMGR_ECC_INTMASK_VAL_OFST 0xFFD12090
#define S10_SYSMGR_ECC_INTMASK_SET_OFST 0xFFD12094
#define S10_SYSMGR_ECC_INTMASK_CLR_OFST 0xFFD12098
#define S10_SYSMGR_ECC_INTSTAT_SERR_OFST 0xFFD1209C
#define S10_SYSMGR_ECC_INTSTAT_DERR_OFST 0xFFD120A0
#define S10_DDR0_IRQ_MASK BIT(16)
struct altr_edac_device_dev;
struct edac_device_prv_data {
@ -340,4 +372,78 @@ struct altr_arria10_edac {
struct list_head a10_ecc_devices;
};
/*
* Functions specified by ARM SMC Calling convention:
*
* FAST call executes atomic operations, returns when the requested operation
* has completed.
* STD call starts a operation which can be preempted by a non-secure
* interrupt. The call can return before the requested operation has
* completed.
*
* a0..a7 is used as register names in the descriptions below, on arm32
* that translates to r0..r7 and on arm64 to w0..w7.
*/
#define INTEL_SIP_SMC_STD_CALL_VAL(func_num) \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_64, \
ARM_SMCCC_OWNER_SIP, (func_num))
#define INTEL_SIP_SMC_FAST_CALL_VAL(func_num) \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
ARM_SMCCC_OWNER_SIP, (func_num))
#define INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
#define INTEL_SIP_SMC_STATUS_OK 0x0
#define INTEL_SIP_SMC_REG_ERROR 0x5
/*
* Request INTEL_SIP_SMC_REG_READ
*
* Read a protected register using SMCCC
*
* Call register usage:
* a0: INTEL_SIP_SMC_REG_READ.
* a1: register address.
* a2-7: not used.
*
* Return status:
* a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_REG_ERROR, or
* INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION
* a1: Value in the register
* a2-3: not used.
*/
#define INTEL_SIP_SMC_FUNCID_REG_READ 7
#define INTEL_SIP_SMC_REG_READ \
INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_READ)
/*
* Request INTEL_SIP_SMC_REG_WRITE
*
* Write a protected register using SMCCC
*
* Call register usage:
* a0: INTEL_SIP_SMC_REG_WRITE.
* a1: register address
* a2: value to program into register.
* a3-7: not used.
*
* Return status:
* a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_REG_ERROR, or
* INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION
* a1-3: not used.
*/
#define INTEL_SIP_SMC_FUNCID_REG_WRITE 8
#define INTEL_SIP_SMC_REG_WRITE \
INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_WRITE)
struct altr_stratix10_edac {
struct device *dev;
int sb_irq;
struct irq_domain *domain;
struct irq_chip irq_chip;
struct list_head s10_ecc_devices;
struct notifier_block panic_notifier;
};
#endif /* #ifndef _ALTERA_EDAC_H */

View File

@ -91,6 +91,7 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers,
dimm_fill->count, 0, 0);
u16 rdr_mask = BIT(7) | BIT(13);
if (entry->size == 0xffff) {
pr_info("Can't get DIMM%i size\n",
@ -99,22 +100,21 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
} else if (entry->size == 0x7fff) {
dimm->nr_pages = MiB_TO_PAGES(entry->extended_size);
} else {
if (entry->size & 1 << 15)
dimm->nr_pages = MiB_TO_PAGES((entry->size &
0x7fff) << 10);
if (entry->size & BIT(15))
dimm->nr_pages = MiB_TO_PAGES((entry->size & 0x7fff) << 10);
else
dimm->nr_pages = MiB_TO_PAGES(entry->size);
}
switch (entry->memory_type) {
case 0x12:
if (entry->type_detail & 1 << 13)
if (entry->type_detail & BIT(13))
dimm->mtype = MEM_RDDR;
else
dimm->mtype = MEM_DDR;
break;
case 0x13:
if (entry->type_detail & 1 << 13)
if (entry->type_detail & BIT(13))
dimm->mtype = MEM_RDDR2;
else
dimm->mtype = MEM_DDR2;
@ -123,20 +123,29 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
dimm->mtype = MEM_FB_DDR2;
break;
case 0x18:
if (entry->type_detail & 1 << 13)
if (entry->type_detail & BIT(12))
dimm->mtype = MEM_NVDIMM;
else if (entry->type_detail & BIT(13))
dimm->mtype = MEM_RDDR3;
else
dimm->mtype = MEM_DDR3;
break;
case 0x1a:
if (entry->type_detail & BIT(12))
dimm->mtype = MEM_NVDIMM;
else if (entry->type_detail & BIT(13))
dimm->mtype = MEM_RDDR4;
else
dimm->mtype = MEM_DDR4;
break;
default:
if (entry->type_detail & 1 << 6)
if (entry->type_detail & BIT(6))
dimm->mtype = MEM_RMBS;
else if ((entry->type_detail & ((1 << 7) | (1 << 13)))
== ((1 << 7) | (1 << 13)))
else if ((entry->type_detail & rdr_mask) == rdr_mask)
dimm->mtype = MEM_RDR;
else if (entry->type_detail & 1 << 7)
else if (entry->type_detail & BIT(7))
dimm->mtype = MEM_SDR;
else if (entry->type_detail & 1 << 9)
else if (entry->type_detail & BIT(9))
dimm->mtype = MEM_EDO;
else
dimm->mtype = MEM_UNKNOWN;
@ -172,8 +181,7 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
}
}
void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
struct cper_sec_mem_err *mem_err)
void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err)
{
enum hw_event_mc_err_type type;
struct edac_raw_error_desc *e;
@ -183,10 +191,8 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
char *p;
u8 grain_bits;
if (!pvt) {
pr_err("Internal error: Can't find EDAC structure\n");
if (!pvt)
return;
}
/*
* We can do the locking below because GHES defers error processing
@ -434,12 +440,16 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
struct mem_ctl_info *mci;
struct edac_mc_layer layers[1];
struct ghes_edac_dimm_fill dimm_fill;
int idx;
int idx = -1;
/* Check if safe to enable on this system */
idx = acpi_match_platform_list(plat_list);
if (!force_load && idx < 0)
return 0;
if (IS_ENABLED(CONFIG_X86)) {
/* Check if safe to enable on this system */
idx = acpi_match_platform_list(plat_list);
if (!force_load && idx < 0)
return -ENODEV;
} else {
idx = 0;
}
/*
* We have only one logical memory controller to which all DIMMs belong.
@ -519,6 +529,9 @@ void ghes_edac_unregister(struct ghes *ghes)
{
struct mem_ctl_info *mci;
if (!ghes_pvt)
return;
mci = ghes_pvt->mci;
edac_mc_del_mc(mci->pdev);
edac_mc_free(mci);

View File

@ -1743,7 +1743,7 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
err = "write parity error";
break;
case 19:
err = "redundacy loss";
err = "redundancy loss";
break;
case 20:
err = "reserved";

View File

@ -55,22 +55,21 @@ enum {
/* From drivers/edac/ghes_edac.c */
#ifdef CONFIG_EDAC_GHES
void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
struct cper_sec_mem_err *mem_err);
void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err);
int ghes_edac_register(struct ghes *ghes, struct device *dev);
void ghes_edac_unregister(struct ghes *ghes);
#else
static inline void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
static inline void ghes_edac_report_mem_error(int sev,
struct cper_sec_mem_err *mem_err)
{
}
static inline int ghes_edac_register(struct ghes *ghes, struct device *dev)
{
return 0;
return -ENODEV;
}
static inline void ghes_edac_unregister(struct ghes *ghes)