2016-02-10 13:26:24 -06:00
/*
* Copyright Altera Corporation ( C ) 2016. All rights reserved .
*
* 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 < http : //www.gnu.org/licenses/>.
*/
2016-04-06 20:09:42 -05:00
# include <linux/delay.h>
2016-02-10 13:26:24 -06:00
# include <linux/io.h>
# include <linux/genalloc.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
2016-04-06 20:09:42 -05:00
# include "core.h"
2016-02-10 13:26:24 -06:00
# define ALTR_OCRAM_CLEAR_ECC 0x00000018
# define ALTR_OCRAM_ECC_EN 0x00000019
void socfpga_init_ocram_ecc ( void )
{
struct device_node * np ;
void __iomem * mapped_ocr_edac_addr ;
/* Find the OCRAM EDAC device tree node */
np = of_find_compatible_node ( NULL , NULL , " altr,socfpga-ocram-ecc " ) ;
if ( ! np ) {
pr_err ( " Unable to find socfpga-ocram-ecc \n " ) ;
return ;
}
mapped_ocr_edac_addr = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
if ( ! mapped_ocr_edac_addr ) {
pr_err ( " Unable to map OCRAM ecc regs. \n " ) ;
return ;
}
/* Clear any pending OCRAM ECC interrupts, then enable ECC */
writel ( ALTR_OCRAM_CLEAR_ECC , mapped_ocr_edac_addr ) ;
writel ( ALTR_OCRAM_ECC_EN , mapped_ocr_edac_addr ) ;
iounmap ( mapped_ocr_edac_addr ) ;
}
2016-04-06 20:09:42 -05:00
/* 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 ) ;
}