/*
* APM X-Gene SoC EDAC (error detection and correction)
*
* Copyright (c) 2015, Applied Micro Circuits Corporation
* Author: Feng Kan <fkan@apm.com>
* Loc Ho <lho@apm.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that 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/>.
*/
#include <linux/ctype.h>
#include <linux/edac.h>
#include <linux/interrupt.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include "edac_core.h"
#define EDAC_MOD_STR "xgene_edac"
/* Global error configuration status registers (CSR) */
#define PCPHPERRINTSTS 0x0000
#define PCPHPERRINTMSK 0x0004
#define MCU_CTL_ERR_MASK BIT(12)
#define IOB_PA_ERR_MASK BIT(11)
#define IOB_BA_ERR_MASK BIT(10)
#define IOB_XGIC_ERR_MASK BIT(9)
#define IOB_RB_ERR_MASK BIT(8)
#define L3C_UNCORR_ERR_MASK BIT(5)
#define MCU_UNCORR_ERR_MASK BIT(4)
#define PMD3_MERR_MASK BIT(3)
#define PMD2_MERR_MASK BIT(2)
#define PMD1_MERR_MASK BIT(1)
#define PMD0_MERR_MASK BIT(0)
#define PCPLPERRINTSTS 0x0008
#define PCPLPERRINTMSK 0x000C
#define CSW_SWITCH_TRACE_ERR_MASK BIT(2)
#define L3C_CORR_ERR_MASK BIT(1)
#define MCU_CORR_ERR_MASK BIT(0)
#define MEMERRINTSTS 0x0010
#define MEMERRINTMSK 0x0014
struct xgene_edac {
struct device *dev;
struct regmap *csw_map;
struct regmap *mcba_map;
struct regmap *mcbb_map;
struct regmap *efuse_map;
void __iomem *pcp_csr;
spinlock_t lock;
struct dentry *dfs;
struct list_head mcus;
struct list_head pmds;
struct mutex mc_lock;
int mc_active_mask;
int mc_registered_mask;
};
static void xgene_edac_pcp_rd(struct xgene_edac *edac, u32 reg, u32 *val)
{
*val = readl(edac->pcp_csr + reg);
}
static void xgene_edac_pcp_clrbits(struct xgene_edac *edac, u32 reg,
u32 bits_mask)
{
u32 val;
spin_lock(&edac->lock);
val = readl(edac->pcp_csr + reg);
val &= ~bits_mask;
writel(val, edac->pcp_csr + reg);
spin_unlock(&edac->lock);
}
static void xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
u32 bits_mask)
{
u32 val;
spin_lock(&edac->lock);
val = readl(edac->pcp_csr + reg);
val |= bits_mask;
writel(val, edac->pcp_csr + reg);
spin_unlock(&edac->lock);
}
/* Memory controller error CSR */
#define MCU_MAX_RANK 8
#define MCU_RANK_STRIDE 0x40
#define MCUGECR 0x0110
#define MCU_GECR_DEMANDUCINTREN_MASK BIT(0)
#define MCU_GECR_BACKUCINTREN_MASK BIT(1)
#define MCU_GECR_CINTREN_MASK BIT(2)
#define MUC_GECR_MCUADDRERREN_MASK BIT(9)
#define MCUGESR 0x0114
#define MCU_GESR_ADDRNOMATCH_ERR_MASK BIT(7)
#define MCU_GESR_ADDRMULTIMATCH_ERR_MASK BIT(6)
#define MCU_GESR_PHYP_ERR_MASK BIT(3)
#define MCUESRR0 0x0314
#define MCU_ESRR_MULTUCERR_MASK BIT(3)
#define MCU_ESRR_BACKUCERR_MASK BIT(2)
#define MCU_ESRR_DEMANDUCERR_MASK BIT(1)
#define MCU_ESRR_CERR_MASK BIT(0)
#define MCUESRRA0 0x0318
#define MCUEBLRR0 0x031c
#define MCU_EBLRR_ERRBANK_RD(src) (((src) & 0x00000007) >> 0)
#define MCUERCRR0 0x0320
#define MCU_ERCRR_ERRROW_RD(src) (((src) & 0xFFFF0000) >> 16)
#define MCU_ERCRR_ERRCOL_RD(src) ((src) & 0x00000FFF)
#define MCUSBECNT0 0x0324
#define MCU_SBECNT_COUNT(src) ((src) & 0xFFFF)
#define CSW_CSWCR 0x0000
#define CSW_CSWCR_DUALMCB_MASK BIT(0)
#define MCBADDRMR 0x0000
#define MCBADDRMR_MCU_INTLV_MODE_MASK BIT(3)
#define MCBADDRMR_DUALMCU_MODE_MASK BIT(2)
#define MCBADDRMR_MCB_INTLV_MODE_MASK BIT(1)
#define MCBADDRMR_ADDRESS_MODE_MASK BIT(0)
struct xgene_edac_mc_ctx {
struct list_head next;
char *name;
struct mem_ctl_info *mci;
struct xgene_edac *edac;
void __iomem *mcu_csr;
u32 mcu_id;
};
static ssize_t xgene_edac_mc