Merge tag 'edac_for_4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
Pull EDAC updates from Borislav Petkov: "It was pretty busy in EDAC land this time: - Altera Arria10 L2 cache and On-Chip RAM ECC handling (Thor Thayer) - Remove ad-hoc buffering of MCE records in sb_edac and i7core_edac (Tony Luck) - Do not register sb_edac with pci_register_driver() (Tony Luck) - Add support for Skylake to ie31200_edac (Jason Baron) - Do not register amd64_edac with pci_register_driver() (Borislav Petkov) ... plus the usual round of cleanups and fixes all over the place" * tag 'edac_for_4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp: (25 commits) EDAC, amd64_edac: Drop pci_register_driver() use EDAC, ie31200_edac: Add Skylake support EDAC, sb_edac: Use cpu family/model in driver detection EDAC, i7core: Remove double buffering of error records EDAC, amd64_edac: Issue driver banner only on success ARM: socfpga: Initialize Arria10 OCRAM ECC on startup EDAC: Increment correct counter in edac_inc_ue_error() EDAC, sb_edac: Remove double buffering of error records EDAC: Fix used after kfree() error in edac_unregister_sysfs() EDAC, altera: Avoid unused function warnings EDAC, altera: Remove useless casts ARM: socfpga: Enable Arria10 OCRAM ECC on startup EDAC, altera: Add Arria10 OCRAM ECC support Documentation: dt: socfpga: Add Altera Arria10 OCRAM binding EDAC, altera: Make OCRAM ECC dependency check generic EDAC, altera: Add register offset for ECC Enable EDAC, altera: Extract error inject operations to a struct fops ARM: socfpga: Enable Arria10 L2 cache ECC on startup EDAC, altera: Add Arria10 L2 Cache ECC handling Documentation, dt, socfpga: Add Altera Arria10 L2 cache binding ...
This commit is contained in:
@@ -38,6 +38,8 @@ extern void socfpga_init_clocks(void);
|
||||
extern void socfpga_sysmgr_init(void);
|
||||
void socfpga_init_l2_ecc(void);
|
||||
void socfpga_init_ocram_ecc(void);
|
||||
void socfpga_init_arria10_l2_ecc(void);
|
||||
void socfpga_init_arria10_ocram_ecc(void);
|
||||
|
||||
extern void __iomem *sys_manager_base_addr;
|
||||
extern void __iomem *rst_manager_base_addr;
|
||||
|
@@ -17,6 +17,20 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
/* A10 System Manager L2 ECC Control register */
|
||||
#define A10_MPU_CTRL_L2_ECC_OFST 0x0
|
||||
#define A10_MPU_CTRL_L2_ECC_EN BIT(0)
|
||||
|
||||
/* A10 System Manager Global IRQ Mask register */
|
||||
#define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98
|
||||
#define A10_SYSMGR_ECC_INTMASK_CLR_L2 BIT(0)
|
||||
|
||||
/* A10 System Manager L2 ECC IRQ Clear register */
|
||||
#define A10_SYSMGR_MPU_CLEAR_L2_ECC_OFST 0xA8
|
||||
#define A10_SYSMGR_MPU_CLEAR_L2_ECC (BIT(31) | BIT(15))
|
||||
|
||||
void socfpga_init_l2_ecc(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
@@ -39,3 +53,38 @@ void socfpga_init_l2_ecc(void)
|
||||
writel(0x01, mapped_l2_edac_addr);
|
||||
iounmap(mapped_l2_edac_addr);
|
||||
}
|
||||
|
||||
void socfpga_init_arria10_l2_ecc(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
void __iomem *mapped_l2_edac_addr;
|
||||
|
||||
/* Find the L2 EDAC device tree node */
|
||||
np = of_find_compatible_node(NULL, NULL, "altr,socfpga-a10-l2-ecc");
|
||||
if (!np) {
|
||||
pr_err("Unable to find socfpga-a10-l2-ecc in dtb\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mapped_l2_edac_addr = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
if (!mapped_l2_edac_addr) {
|
||||
pr_err("Unable to find L2 ECC mapping in dtb\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sys_manager_base_addr) {
|
||||
pr_err("System Mananger not mapped for L2 ECC\n");
|
||||
goto exit;
|
||||
}
|
||||
/* Clear any pending IRQs */
|
||||
writel(A10_SYSMGR_MPU_CLEAR_L2_ECC, (sys_manager_base_addr +
|
||||
A10_SYSMGR_MPU_CLEAR_L2_ECC_OFST));
|
||||
/* Enable ECC */
|
||||
writel(A10_SYSMGR_ECC_INTMASK_CLR_L2, sys_manager_base_addr +
|
||||
A10_SYSMGR_ECC_INTMASK_CLR_OFST);
|
||||
writel(A10_MPU_CTRL_L2_ECC_EN, mapped_l2_edac_addr +
|
||||
A10_MPU_CTRL_L2_ECC_OFST);
|
||||
exit:
|
||||
iounmap(mapped_l2_edac_addr);
|
||||
}
|
||||
|
@@ -13,12 +13,15 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#define ALTR_OCRAM_CLEAR_ECC 0x00000018
|
||||
#define ALTR_OCRAM_ECC_EN 0x00000019
|
||||
|
||||
@@ -47,3 +50,133 @@ void socfpga_init_ocram_ecc(void)
|
||||
|
||||
iounmap(mapped_ocr_edac_addr);
|
||||
}
|
||||
|
||||
/* Arria10 OCRAM Section */
|
||||
#define ALTR_A10_ECC_CTRL_OFST 0x08
|
||||
#define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0))
|
||||
#define ALTR_A10_ECC_INITA BIT(16)
|
||||
|
||||
#define ALTR_A10_ECC_INITSTAT_OFST 0x0C
|
||||
#define ALTR_A10_ECC_INITCOMPLETEA BIT(0)
|
||||
#define ALTR_A10_ECC_INITCOMPLETEB BIT(8)
|
||||
|
||||
#define ALTR_A10_ECC_ERRINTEN_OFST 0x10
|
||||
#define ALTR_A10_ECC_SERRINTEN BIT(0)
|
||||
|
||||
#define ALTR_A10_ECC_INTSTAT_OFST 0x20
|
||||
#define ALTR_A10_ECC_SERRPENA BIT(0)
|
||||
#define ALTR_A10_ECC_DERRPENA BIT(8)
|
||||
#define ALTR_A10_ECC_ERRPENA_MASK (ALTR_A10_ECC_SERRPENA | \
|
||||
ALTR_A10_ECC_DERRPENA)
|
||||
/* ECC Manager Defines */
|
||||
#define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94
|
||||
#define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98
|
||||
#define A10_SYSMGR_ECC_INTMASK_OCRAM BIT(1)
|
||||
|
||||
#define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000
|
||||
|
||||
static inline void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr)
|
||||
{
|
||||
u32 value = readl(ioaddr);
|
||||
|
||||
value |= bit_mask;
|
||||
writel(value, ioaddr);
|
||||
}
|
||||
|
||||
static inline void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr)
|
||||
{
|
||||
u32 value = readl(ioaddr);
|
||||
|
||||
value &= ~bit_mask;
|
||||
writel(value, ioaddr);
|
||||
}
|
||||
|
||||
static inline int ecc_test_bits(u32 bit_mask, void __iomem *ioaddr)
|
||||
{
|
||||
u32 value = readl(ioaddr);
|
||||
|
||||
return (value & bit_mask) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function uses the memory initialization block in the Arria10 ECC
|
||||
* controller to initialize/clear the entire memory data and ECC data.
|
||||
*/
|
||||
static int altr_init_memory_port(void __iomem *ioaddr)
|
||||
{
|
||||
int limit = ALTR_A10_ECC_INIT_WATCHDOG_10US;
|
||||
|
||||
ecc_set_bits(ALTR_A10_ECC_INITA, (ioaddr + ALTR_A10_ECC_CTRL_OFST));
|
||||
while (limit--) {
|
||||
if (ecc_test_bits(ALTR_A10_ECC_INITCOMPLETEA,
|
||||
(ioaddr + ALTR_A10_ECC_INITSTAT_OFST)))
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
if (limit < 0)
|
||||
return -EBUSY;
|
||||
|
||||
/* Clear any pending ECC interrupts */
|
||||
writel(ALTR_A10_ECC_ERRPENA_MASK,
|
||||
(ioaddr + ALTR_A10_ECC_INTSTAT_OFST));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void socfpga_init_arria10_ocram_ecc(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret = 0;
|
||||
void __iomem *ecc_block_base;
|
||||
|
||||
if (!sys_manager_base_addr) {
|
||||
pr_err("SOCFPGA: sys-mgr is not initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the OCRAM EDAC device tree node */
|
||||
np = of_find_compatible_node(NULL, NULL, "altr,socfpga-a10-ocram-ecc");
|
||||
if (!np) {
|
||||
pr_err("Unable to find socfpga-a10-ocram-ecc\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Map the ECC Block */
|
||||
ecc_block_base = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
if (!ecc_block_base) {
|
||||
pr_err("Unable to map OCRAM ECC block\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disable ECC */
|
||||
writel(ALTR_A10_OCRAM_ECC_EN_CTL,
|
||||
sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_SET_OFST);
|
||||
ecc_clear_bits(ALTR_A10_ECC_SERRINTEN,
|
||||
(ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
|
||||
ecc_clear_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
|
||||
(ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
|
||||
|
||||
/* Ensure all writes complete */
|
||||
wmb();
|
||||
|
||||
/* Use HW initialization block to initialize memory for ECC */
|
||||
ret = altr_init_memory_port(ecc_block_base);
|
||||
if (ret) {
|
||||
pr_err("ECC: cannot init OCRAM PORTA memory\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Enable ECC */
|
||||
ecc_set_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
|
||||
(ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
|
||||
ecc_set_bits(ALTR_A10_ECC_SERRINTEN,
|
||||
(ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
|
||||
writel(ALTR_A10_OCRAM_ECC_EN_CTL,
|
||||
sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_CLR_OFST);
|
||||
|
||||
/* Ensure all writes complete */
|
||||
wmb();
|
||||
exit:
|
||||
iounmap(ecc_block_base);
|
||||
}
|
||||
|
@@ -66,6 +66,16 @@ static void __init socfpga_init_irq(void)
|
||||
socfpga_init_ocram_ecc();
|
||||
}
|
||||
|
||||
static void __init socfpga_arria10_init_irq(void)
|
||||
{
|
||||
irqchip_init();
|
||||
socfpga_sysmgr_init();
|
||||
if (IS_ENABLED(CONFIG_EDAC_ALTERA_L2C))
|
||||
socfpga_init_arria10_l2_ecc();
|
||||
if (IS_ENABLED(CONFIG_EDAC_ALTERA_OCRAM))
|
||||
socfpga_init_arria10_ocram_ecc();
|
||||
}
|
||||
|
||||
static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd)
|
||||
{
|
||||
u32 temp;
|
||||
@@ -113,7 +123,7 @@ static const char *altera_a10_dt_match[] = {
|
||||
DT_MACHINE_START(SOCFPGA_A10, "Altera SOCFPGA Arria10")
|
||||
.l2c_aux_val = 0,
|
||||
.l2c_aux_mask = ~0,
|
||||
.init_irq = socfpga_init_irq,
|
||||
.init_irq = socfpga_arria10_init_irq,
|
||||
.restart = socfpga_arria10_restart,
|
||||
.dt_compat = altera_a10_dt_match,
|
||||
MACHINE_END
|
||||
|
Reference in New Issue
Block a user