Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1/*
  2 * Copyright Altera Corporation (C) 2016. All rights reserved.
  3 *
  4 * This program is free software; you can redistribute it and/or modify it
  5 * under the terms and conditions of the GNU General Public License,
  6 * version 2, as published by the Free Software Foundation.
  7 *
  8 * This program is distributed in the hope it will be useful, but WITHOUT
  9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 11 * more details.
 12 *
 13 * You should have received a copy of the GNU General Public License along with
 14 * this program.  If not, see <http://www.gnu.org/licenses/>.
 15 */
 16#include <linux/delay.h>
 17#include <linux/io.h>
 18#include <linux/genalloc.h>
 19#include <linux/module.h>
 20#include <linux/of_address.h>
 21#include <linux/of_platform.h>
 22
 23#include "core.h"
 24
 25#define ALTR_OCRAM_CLEAR_ECC          0x00000018
 26#define ALTR_OCRAM_ECC_EN             0x00000019
 27
 28void socfpga_init_ocram_ecc(void)
 29{
 30	struct device_node *np;
 31	void __iomem *mapped_ocr_edac_addr;
 32
 33	/* Find the OCRAM EDAC device tree node */
 34	np = of_find_compatible_node(NULL, NULL, "altr,socfpga-ocram-ecc");
 35	if (!np) {
 36		pr_err("Unable to find socfpga-ocram-ecc\n");
 37		return;
 38	}
 39
 40	mapped_ocr_edac_addr = of_iomap(np, 0);
 41	of_node_put(np);
 42	if (!mapped_ocr_edac_addr) {
 43		pr_err("Unable to map OCRAM ecc regs.\n");
 44		return;
 45	}
 46
 47	/* Clear any pending OCRAM ECC interrupts, then enable ECC */
 48	writel(ALTR_OCRAM_CLEAR_ECC, mapped_ocr_edac_addr);
 49	writel(ALTR_OCRAM_ECC_EN, mapped_ocr_edac_addr);
 50
 51	iounmap(mapped_ocr_edac_addr);
 52}
 53
 54/* Arria10 OCRAM Section */
 55#define ALTR_A10_ECC_CTRL_OFST          0x08
 56#define ALTR_A10_OCRAM_ECC_EN_CTL       (BIT(1) | BIT(0))
 57#define ALTR_A10_ECC_INITA              BIT(16)
 58
 59#define ALTR_A10_ECC_INITSTAT_OFST      0x0C
 60#define ALTR_A10_ECC_INITCOMPLETEA      BIT(0)
 61#define ALTR_A10_ECC_INITCOMPLETEB      BIT(8)
 62
 63#define ALTR_A10_ECC_ERRINTEN_OFST      0x10
 64#define ALTR_A10_ECC_SERRINTEN          BIT(0)
 65
 66#define ALTR_A10_ECC_INTSTAT_OFST       0x20
 67#define ALTR_A10_ECC_SERRPENA           BIT(0)
 68#define ALTR_A10_ECC_DERRPENA           BIT(8)
 69#define ALTR_A10_ECC_ERRPENA_MASK       (ALTR_A10_ECC_SERRPENA | \
 70					 ALTR_A10_ECC_DERRPENA)
 71/* ECC Manager Defines */
 72#define A10_SYSMGR_ECC_INTMASK_SET_OFST   0x94
 73#define A10_SYSMGR_ECC_INTMASK_CLR_OFST   0x98
 74#define A10_SYSMGR_ECC_INTMASK_OCRAM      BIT(1)
 75
 76#define ALTR_A10_ECC_INIT_WATCHDOG_10US   10000
 77
 78static inline void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr)
 79{
 80	u32 value = readl(ioaddr);
 81
 82	value |= bit_mask;
 83	writel(value, ioaddr);
 84}
 85
 86static inline void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr)
 87{
 88	u32 value = readl(ioaddr);
 89
 90	value &= ~bit_mask;
 91	writel(value, ioaddr);
 92}
 93
 94static inline int ecc_test_bits(u32 bit_mask, void __iomem *ioaddr)
 95{
 96	u32 value = readl(ioaddr);
 97
 98	return (value & bit_mask) ? 1 : 0;
 99}
100
101/*
102 * This function uses the memory initialization block in the Arria10 ECC
103 * controller to initialize/clear the entire memory data and ECC data.
104 */
105static int altr_init_memory_port(void __iomem *ioaddr)
106{
107	int limit = ALTR_A10_ECC_INIT_WATCHDOG_10US;
108
109	ecc_set_bits(ALTR_A10_ECC_INITA, (ioaddr + ALTR_A10_ECC_CTRL_OFST));
110	while (limit--) {
111		if (ecc_test_bits(ALTR_A10_ECC_INITCOMPLETEA,
112				  (ioaddr + ALTR_A10_ECC_INITSTAT_OFST)))
113			break;
114		udelay(1);
115	}
116	if (limit < 0)
117		return -EBUSY;
118
119	/* Clear any pending ECC interrupts */
120	writel(ALTR_A10_ECC_ERRPENA_MASK,
121	       (ioaddr + ALTR_A10_ECC_INTSTAT_OFST));
122
123	return 0;
124}
125
126void socfpga_init_arria10_ocram_ecc(void)
127{
128	struct device_node *np;
129	int ret = 0;
130	void __iomem *ecc_block_base;
131
132	if (!sys_manager_base_addr) {
133		pr_err("SOCFPGA: sys-mgr is not initialized\n");
134		return;
135	}
136
137	/* Find the OCRAM EDAC device tree node */
138	np = of_find_compatible_node(NULL, NULL, "altr,socfpga-a10-ocram-ecc");
139	if (!np) {
140		pr_err("Unable to find socfpga-a10-ocram-ecc\n");
141		return;
142	}
143
144	/* Map the ECC Block */
145	ecc_block_base = of_iomap(np, 0);
146	of_node_put(np);
147	if (!ecc_block_base) {
148		pr_err("Unable to map OCRAM ECC block\n");
149		return;
150	}
151
152	/* Disable ECC */
153	writel(ALTR_A10_OCRAM_ECC_EN_CTL,
154	       sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_SET_OFST);
155	ecc_clear_bits(ALTR_A10_ECC_SERRINTEN,
156		       (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
157	ecc_clear_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
158		       (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
159
160	/* Ensure all writes complete */
161	wmb();
162
163	/* Use HW initialization block to initialize memory for ECC */
164	ret = altr_init_memory_port(ecc_block_base);
165	if (ret) {
166		pr_err("ECC: cannot init OCRAM PORTA memory\n");
167		goto exit;
168	}
169
170	/* Enable ECC */
171	ecc_set_bits(ALTR_A10_OCRAM_ECC_EN_CTL,
172		     (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
173	ecc_set_bits(ALTR_A10_ECC_SERRINTEN,
174		     (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST));
175	writel(ALTR_A10_OCRAM_ECC_EN_CTL,
176	       sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_CLR_OFST);
177
178	/* Ensure all writes complete */
179	wmb();
180exit:
181	iounmap(ecc_block_base);
182}