mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
* 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
This commit is contained in:
commit
8450493076
@ -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>;
|
||||
};
|
||||
};
|
@ -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>;
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user