19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27cc5a5d3SThor Thayer /*
37cc5a5d3SThor Thayer * Copyright Altera Corporation (C) 2016. All rights reserved.
47cc5a5d3SThor Thayer */
52364d423SThor Thayer #include <linux/delay.h>
67cc5a5d3SThor Thayer #include <linux/io.h>
7*37e2d7d2SRob Herring #include <linux/of.h>
87cc5a5d3SThor Thayer #include <linux/of_address.h>
97cc5a5d3SThor Thayer
102364d423SThor Thayer #include "core.h"
112364d423SThor Thayer
127cc5a5d3SThor Thayer #define ALTR_OCRAM_CLEAR_ECC 0x00000018
137cc5a5d3SThor Thayer #define ALTR_OCRAM_ECC_EN 0x00000019
147cc5a5d3SThor Thayer
socfpga_init_ocram_ecc(void)157cc5a5d3SThor Thayer void socfpga_init_ocram_ecc(void)
167cc5a5d3SThor Thayer {
177cc5a5d3SThor Thayer struct device_node *np;
187cc5a5d3SThor Thayer void __iomem *mapped_ocr_edac_addr;
197cc5a5d3SThor Thayer
207cc5a5d3SThor Thayer /* Find the OCRAM EDAC device tree node */
217cc5a5d3SThor Thayer np = of_find_compatible_node(NULL, NULL, "altr,socfpga-ocram-ecc");
227cc5a5d3SThor Thayer if (!np) {
237cc5a5d3SThor Thayer pr_err("Unable to find socfpga-ocram-ecc\n");
247cc5a5d3SThor Thayer return;
257cc5a5d3SThor Thayer }
267cc5a5d3SThor Thayer
277cc5a5d3SThor Thayer mapped_ocr_edac_addr = of_iomap(np, 0);
287cc5a5d3SThor Thayer of_node_put(np);
297cc5a5d3SThor Thayer if (!mapped_ocr_edac_addr) {
307cc5a5d3SThor Thayer pr_err("Unable to map OCRAM ecc regs.\n");
317cc5a5d3SThor Thayer return;
327cc5a5d3SThor Thayer }
337cc5a5d3SThor Thayer
347cc5a5d3SThor Thayer /* Clear any pending OCRAM ECC interrupts, then enable ECC */
357cc5a5d3SThor Thayer writel(ALTR_OCRAM_CLEAR_ECC, mapped_ocr_edac_addr);
367cc5a5d3SThor Thayer writel(ALTR_OCRAM_ECC_EN, mapped_ocr_edac_addr);
377cc5a5d3SThor Thayer
387cc5a5d3SThor Thayer iounmap(mapped_ocr_edac_addr);
397cc5a5d3SThor Thayer }
402364d423SThor Thayer
412364d423SThor Thayer /* Arria10 OCRAM Section */
422364d423SThor Thayer #define ALTR_A10_ECC_CTRL_OFST 0x08
432364d423SThor Thayer #define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0))
442364d423SThor Thayer #define ALTR_A10_ECC_INITA BIT(16)
452364d423SThor Thayer
462364d423SThor Thayer #define ALTR_A10_ECC_INITSTAT_OFST 0x0C
472364d423SThor Thayer #define ALTR_A10_ECC_INITCOMPLETEA BIT(0)
482364d423SThor Thayer #define ALTR_A10_ECC_INITCOMPLETEB BIT(8)
492364d423SThor Thayer
502364d423SThor Thayer #define ALTR_A10_ECC_ERRINTEN_OFST 0x10
512364d423SThor Thayer #define ALTR_A10_ECC_SERRINTEN BIT(0)
522364d423SThor Thayer
532364d423SThor Thayer #define ALTR_A10_ECC_INTSTAT_OFST 0x20
542364d423SThor Thayer #define ALTR_A10_ECC_SERRPENA BIT(0)
552364d423SThor Thayer #define ALTR_A10_ECC_DERRPENA BIT(8)
562364d423SThor Thayer #define ALTR_A10_ECC_ERRPENA_MASK (ALTR_A10_ECC_SERRPENA | \
572364d423SThor Thayer ALTR_A10_ECC_DERRPENA)
582364d423SThor Thayer /* ECC Manager Defines */
592364d423SThor Thayer #define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94
602364d423SThor Thayer #define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98
612364d423SThor Thayer #define A10_SYSMGR_ECC_INTMASK_OCRAM BIT(1)
622364d423SThor Thayer
632364d423SThor Thayer #define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000
642364d423SThor Thayer
ecc_set_bits(u32 bit_mask,void __iomem * ioaddr)652364d423SThor Thayer static inline void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr)
662364d423SThor Thayer {
672364d423SThor Thayer u32 value = readl(ioaddr);
682364d423SThor Thayer
692364d423SThor Thayer value |= bit_mask;
702364d423SThor Thayer writel(value, ioaddr);
712364d423SThor Thayer }
722364d423SThor Thayer
ecc_clear_bits(u32 bit_mask,void __iomem * ioaddr)732364d423SThor Thayer static inline void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr)
742364d423SThor Thayer {
752364d423SThor Thayer u32 value = readl(ioaddr);
762364d423SThor Thayer
772364d423SThor Thayer value &= ~bit_mask;
782364d423SThor Thayer writel(value, ioaddr);
792364d423SThor Thayer }
802364d423SThor Thayer
ecc_test_bits(u32 bit_mask,void __iomem * ioaddr)812364d423SThor Thayer static inline int ecc_test_bits(u32 bit_mask, void __iomem *ioaddr)
822364d423SThor Thayer {
832364d423SThor Thayer u32 value = readl(ioaddr);
842364d423SThor Thayer
852364d423SThor Thayer return (value & bit_mask) ? 1 : 0;
862364d423SThor Thayer }
872364d423SThor Thayer
882364d423SThor Thayer /*
892364d423SThor Thayer * This function uses the memory initialization block in the Arria10 ECC
902364d423SThor Thayer * controller to initialize/clear the entire memory data and ECC data.
912364d423SThor Thayer */
altr_init_memory_port(void __iomem * ioaddr)922364d423SThor Thayer static int altr_init_memory_port(void __iomem *ioaddr)
932364d423SThor Thayer {
942364d423SThor Thayer int limit = ALTR_A10_ECC_INIT_WATCHDOG_10US;
952364d423SThor Thayer
962364d423SThor Thayer ecc_set_bits(ALTR_A10_ECC_INITA, (ioaddr + ALTR_A10_ECC_CTRL_OFST));
972364d423SThor Thayer while (limit--) {
982364d423SThor Thayer if (ecc_test_bits(ALTR_A10_ECC_INITCOMPLETEA,
992364d423SThor Thayer (ioaddr + ALTR_A10_ECC_INITSTAT_OFST)))
1002364d423SThor Thayer break;
1012364d423SThor Thayer udelay(1);
1022364d423SThor Thayer }
1032364d423SThor Thayer if (limit < 0)
1042364d423SThor Thayer return -EBUSY;
1052364d423SThor Thayer
1062364d423SThor Thayer /* Clear any pending ECC interrupts */
1072364d423SThor Thayer writel(ALTR_A10_ECC_ERRPENA_MASK,
1082364d423SThor Thayer (ioaddr + ALTR_A10_ECC_INTSTAT_OFST));
1092364d423SThor Thayer
1102364d423SThor Thayer return 0;
1112364d423SThor Thayer }
1122364d423SThor Thayer
socfpga_init_arria10_ocram_ecc(void)1132364d423SThor Thayer void socfpga_init_arria10_ocram_ecc(void)
1142364d423SThor Thayer {
1152364d423SThor Thayer struct device_node *np;
1162364d423SThor Thayer int ret = 0;
1172364d423SThor Thayer void __iomem *ecc_block_base;
1182364d423SThor Thayer
1192364d423SThor Thayer if (!sys_manager_base_addr) {
1202364d423SThor Thayer pr_err("SOCFPGA: sys-mgr is not initialized\n");
1212364d423SThor Thayer return;
1222364d423SThor Thayer }
1232364d423SThor Thayer
1242364d423SThor Thayer /* Find the OCRAM EDAC device tree node */
1252364d423SThor Thayer np = of_find_compatible_node(NULL, NULL, "altr,socfpga-a10-ocram-ecc");
1262364d423SThor Thayer if (!np) {
1272364d423SThor Thayer pr_err("Unable to find socfpga-a10-ocram-ecc\n");
1282364d423SThor Thayer return;
1292364d423SThor Thayer }
1302364d423SThor Thayer
1312364d423SThor Thayer /* Map the ECC Block */
1322364d423SThor Thayer ecc_block_base = of_iomap(np, 0);
1332364d423SThor Thayer of_node_put(np);
1342364d423SThor Thayer if (!ecc_block_base) {
1352364d423SThor Thayer pr_err("Unable to map OCRAM ECC block\n");
1362364d423SThor Thayer return;
1372364d423SThor Thayer }
1382364d423SThor Thayer
1392364d423SThor Thayer /* Disable ECC */
1402364d423SThor Thayer writel(ALTR_A10_OCRAM_ECC_EN_CTL,
1412364d423SThor Thayer sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_SET_OFST);
1422364d423SThor Thayer ecc_clear_bits(ALTR_A10_ECC_SERRINTEN,
1432364d423SThor Thayer (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
1442364d423SThor Thayer ecc_clear_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
1452364d423SThor Thayer (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
1462364d423SThor Thayer
1472364d423SThor Thayer /* Ensure all writes complete */
1482364d423SThor Thayer wmb();
1492364d423SThor Thayer
1502364d423SThor Thayer /* Use HW initialization block to initialize memory for ECC */
1512364d423SThor Thayer ret = altr_init_memory_port(ecc_block_base);
1522364d423SThor Thayer if (ret) {
1532364d423SThor Thayer pr_err("ECC: cannot init OCRAM PORTA memory\n");
1542364d423SThor Thayer goto exit;
1552364d423SThor Thayer }
1562364d423SThor Thayer
1572364d423SThor Thayer /* Enable ECC */
1582364d423SThor Thayer ecc_set_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
1592364d423SThor Thayer (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
1602364d423SThor Thayer ecc_set_bits(ALTR_A10_ECC_SERRINTEN,
1612364d423SThor Thayer (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
1622364d423SThor Thayer writel(ALTR_A10_OCRAM_ECC_EN_CTL,
1632364d423SThor Thayer sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_CLR_OFST);
1642364d423SThor Thayer
1652364d423SThor Thayer /* Ensure all writes complete */
1662364d423SThor Thayer wmb();
1672364d423SThor Thayer exit:
1682364d423SThor Thayer iounmap(ecc_block_base);
1692364d423SThor Thayer }
170