Message ID | 1459450087-24792-7-git-send-email-tthayer@opensource.altera.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi, On 03/31/2016 01:48 PM, tthayer@opensource.altera.com wrote: > From: Thor Thayer <tthayer@opensource.altera.com> > > Enable ECC for Arria10 On-Chip RAM on machine startup. The ECC has to > be enabled and memory initialized before data is stored in memory > otherwise the ECC will fail on reads. > > Signed-off-by: Thor Thayer <tthayer@opensource.altera.com> > --- > v2: Add Arria10 ECC block initialization locally. > --- > arch/arm/mach-socfpga/ocram.c | 128 +++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 128 insertions(+) > > diff --git a/arch/arm/mach-socfpga/ocram.c b/arch/arm/mach-socfpga/ocram.c > index 60ec643..d4a524c 100644 > --- a/arch/arm/mach-socfpga/ocram.c > +++ b/arch/arm/mach-socfpga/ocram.c > @@ -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,128 @@ 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 void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr) > +{ > + u32 value = readl(ioaddr); > + > + value |= bit_mask; > + writel(value, ioaddr); > +} > + > +static void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr) > +{ > + u32 value = readl(ioaddr); > + > + value &= ~bit_mask; > + writel(value, ioaddr); > +} > + > +static 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)); > + > + /* 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"); > + return; I realize that I'm not calling iounmap(ecc_block_base) and I'll fix that in the next revision with a goto. > + } > + > + /* 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); > + The iounmap() will go here. I'd still like to hear review comments for the rest. Thanks for reviewing. > + /* Ensure all writes complete */ > + wmb(); > +} >
On Tue, Apr 05, 2016 at 12:25:33AM -0500, Thor Thayer wrote: > I realize that I'm not calling iounmap(ecc_block_base) and I'll fix that in > the next revision with a goto. I'm assuming nothing else changes. Because I've applied 1-4 already. Yes, no? If no, then please send only an updated version of this patch as a reply to this thread here. Thanks.
On Tue, 5 Apr 2016, Borislav Petkov wrote: > On Tue, Apr 05, 2016 at 12:25:33AM -0500, Thor Thayer wrote: > > I realize that I'm not calling iounmap(ecc_block_base) and I'll fix that in > > the next revision with a goto. > > I'm assuming nothing else changes. Because I've applied 1-4 already. > > Yes, no? > > If no, then please send only an updated version of this patch as a reply > to this thread here. > My only suggestion was to change the 3 helper functions(ecc_set_bits, ecc_clear_bits, and ecc_test_bits) should be static inline. So with that change: Acked-by: Dinh Nguyen <dinguyen@opensource.altera.com> BR, Dinh
On Tue, Apr 05, 2016 at 01:37:49PM -0500, Dinh Nguyen wrote: > My only suggestion was to change the 3 helper functions(ecc_set_bits, > ecc_clear_bits, and ecc_test_bits) should be static inline. That doesn't take care of the iounmap error path Thor is talking about, AFAICT. So, I've pushed out what I have applied so far: http://git.kernel.org/cgit/linux/kernel/git/bp/bp.git/log/?h=for-next Thor, please send me what is outstanding ontop. Thanks.
Hi Boris, On 04/05/2016 12:31 AM, Borislav Petkov wrote: > On Tue, Apr 05, 2016 at 12:25:33AM -0500, Thor Thayer wrote: >> I realize that I'm not calling iounmap(ecc_block_base) and I'll fix that in >> the next revision with a goto. > > I'm assuming nothing else changes. Because I've applied 1-4 already. > > Yes, no? > > If no, then please send only an updated version of this patch as a reply > to this thread here. > > Thanks. > Yes, nothing else changes. The rest was OK. Thanks!
On Wed, Apr 06, 2016 at 07:36:49PM -0500, Thor Thayer wrote:
> Yes, nothing else changes. The rest was OK. Thanks!
Cool. The two remaining applied and pushed out.
Thanks.
diff --git a/arch/arm/mach-socfpga/ocram.c b/arch/arm/mach-socfpga/ocram.c index 60ec643..d4a524c 100644 --- a/arch/arm/mach-socfpga/ocram.c +++ b/arch/arm/mach-socfpga/ocram.c @@ -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,128 @@ 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 void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr) +{ + u32 value = readl(ioaddr); + + value |= bit_mask; + writel(value, ioaddr); +} + +static void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr) +{ + u32 value = readl(ioaddr); + + value &= ~bit_mask; + writel(value, ioaddr); +} + +static 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)); + + /* 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"); + return; + } + + /* 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(); +}