/*
* Copyright Altera Corporation (C) 2014-2016. All rights reserved.
* Copyright 2011-2012 Calxeda, Inc.
*
* 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/>.
*
* Adapted from the highbank_mc_edac driver.
*/
#include <asm/cacheflush.h>
#include <linux/ctype.h>
#include <linux/edac.h>
#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include "altera_edac.h"
#include "edac_core.h"
#include "edac_module.h"
#define EDAC_MOD_STR "altera_edac"
#define EDAC_VERSION "1"
#define EDAC_DEVICE "Altera"
static const struct altr_sdram_prv_data c5_data = {
.ecc_ctrl_offset = CV_CTLCFG_OFST,
.ecc_ctl_en_mask = CV_CTLCFG_ECC_AUTO_EN,
.ecc_stat_offset = CV_DRAMSTS_OFST,
.ecc_stat_ce_mask = CV_DRAMSTS_SBEERR,
.ecc_stat_ue_mask = CV_DRAMSTS_DBEERR,
.ecc_saddr_offset = CV_ERRADDR_OFST,
.ecc_daddr_offset = CV_ERRADDR_OFST,
.ecc_cecnt_offset = CV_SBECOUNT_OFST,
.ecc_uecnt_offset = CV_DBECOUNT_OFST,
.ecc_irq_en_offset = CV_DRAMINTR_OFST,
.ecc_irq_en_mask = CV_DRAMINTR_INTREN,
.ecc_irq_clr_offset = CV_DRAMINTR_OFST,
.ecc_irq_clr_mask = (CV_DRAMINTR_INTRCLR | CV_DRAMINTR_INTREN),
.ecc_cnt_rst_offset = CV_DRAMINTR_OFST,
.ecc_cnt_rst_mask = CV_DRAMINTR_INTRCLR,
.ce_ue_trgr_offset = CV_CTLCFG_OFST,
.ce_set_mask = CV_CTLCFG_GEN_SB_ERR,
.ue_set_mask = CV_CTLCFG_GEN_DB_ERR,
};
static const struct altr_sdram_prv_data a10_data = {
.ecc_ctrl_offset = A10_ECCCTRL1_OFST,
.ecc_ctl_en_mask = A10_ECCCTRL1_ECC_EN,
.ecc_stat_offset = A10_INTSTAT_OFST,
.ecc_stat_ce_mask = A10_INTSTAT_SBEERR,
.ecc_stat_ue_mask = A10_INTSTAT_DBEERR,
.ecc_saddr_offset = A10_SERRADDR_OFST,
.ecc_daddr_offset = A10_DERRADDR_OFST,
.ecc_irq_en_offset = A10_ERRINTEN_OFST,
.ecc_irq_en_mask = A10_ECC_IRQ_EN_MASK,
.ecc_irq_clr_offset = A10_INTSTAT_OFST,
.ecc_irq_clr_mask = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR),
.ecc_cnt_rst_offset = A10_ECCCTRL1_OFST,
.ecc_cnt_rst_mask = A10_ECC_CNT_RESET_MASK,
.ce_ue_trgr_offset = A10_DIAGINTTEST_OFST,
.ce_set_mask = A10_DIAGINT_TSERRA_MASK,
.ue_set_mask = A10_DIAGINT_TDERRA_MASK,
};
/*********************** EDAC Memory Controller Functions ****************/
/* The SDRAM controller uses the EDAC Memory Controller framework. */
static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id)
{
struct mem_ctl_info *mci = dev_id;
struct altr_sdram_mc_data *drvdata = mci->pvt_info;
const struct altr_sdram_prv_data *priv = drvdata->data;
u32 status, err_count = 1, err_addr;
regmap_read(drvdata->mc_vbase, priv->ecc_stat_offset, &status);
if (status & priv->ecc_stat_ue_mask) {
regmap_read(drvdata->mc_vbase, priv->ecc_daddr_offset,
&err_addr);
if (priv->ecc_uecnt_offset)
regmap_read(drvdata->mc_vbase, priv->ecc_uecnt_offset,
&err_count);
panic