diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt b/Documentation/devicetree/bindings/edac/socfpga-eccmgr.txt similarity index 87% rename from Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt rename to Documentation/devicetree/bindings/edac/socfpga-eccmgr.txt index 4a1714f96bab..5626560a6cfd 100644 --- a/Documentation/devicetree/bindings/arm/altera/socfpga-eccmgr.txt +++ b/Documentation/devicetree/bindings/edac/socfpga-eccmgr.txt @@ -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>; + }; + }; diff --git a/arch/arm/boot/dts/socfpga_cyclone5_vining_fpga.dts b/arch/arm/boot/dts/socfpga_cyclone5_vining_fpga.dts index 2459d133f1be..f50b19447de6 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_vining_fpga.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_vining_fpga.dts @@ -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>; }; diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi index c89d0c307f8d..e6b059378dc0 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi @@ -17,6 +17,7 @@ /dts-v1/; #include #include +#include / { 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>; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts index eaf13fe29287..f9b1ef12db48 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts @@ -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>; + }; +}; diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 1efefe919555..02c6fd9caff7 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -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); diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index da2da53bca6d..57304b2e989f 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -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. diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index 11d6419788c2..d0d5c4dbe097 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -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 . - * - * Adapted from the highbank_mc_edac driver. */ #include @@ -26,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -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; +} + +/************** ***********/ + /* * 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"); diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h index cbc96290f743..81f0554e09de 100644 --- a/drivers/edac/altera_edac.h +++ b/drivers/edac/altera_edac.h @@ -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 . */ #ifndef _ALTERA_EDAC_H #define _ALTERA_EDAC_H +#include #include #include @@ -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 */ diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index 68b6ee18bea6..473aeec4b1da 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -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); diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 8c5540160a23..4d0ea3563d47 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -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"; diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index 8feb0c866ee0..1624e2be485c 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -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)