// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Intel client SoC with integrated memory controller using IBECC
*
* Copyright (C) 2020 Intel Corporation
*
* The In-Band ECC (IBECC) IP provides ECC protection to all or specific
* regions of the physical memory space. It's used for memory controllers
* that don't support the out-of-band ECC which often needs an additional
* storage device to each channel for storing ECC data.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/irq_work.h>
#include <linux/llist.h>
#include <linux/genalloc.h>
#include <linux/edac.h>
#include <linux/bits.h>
#include <linux/io.h>
#include <asm/mach_traps.h>
#include <asm/nmi.h>
#include <asm/mce.h>
#include "edac_mc.h"
#include "edac_module.h"
#define IGEN6_REVISION "v2.5.1"
#define EDAC_MOD_STR "igen6_edac"
#define IGEN6_NMI_NAME "igen6_ibecc"
/* Debug macros */
#define igen6_printk(level, fmt, arg...) \
edac_printk(level, "igen6", fmt, ##arg)
#define igen6_mc_printk(mci, level, fmt, arg...) \
edac_mc_chipset_printk(mci, level, "igen6", fmt, ##arg)
#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo))
#define NUM_IMC 2 /* Max memory controllers */
#define NUM_CHANNELS 2 /* Max channels */
#define NUM_DIMMS 2 /* Max DIMMs per channel */
#define _4GB BIT_ULL(32)
/* Size of physical memory */
#define TOM_OFFSET 0xa0
/* Top of low usable DRAM */
#define TOLUD_OFFSET 0xbc
/* Capability register C */
#define CAPID_C_OFFSET 0xec
#define CAPID_C_IBECC BIT(15)
/* Capability register E */
#define CAPID_E_OFFSET 0xf0
#define CAPID_E_IBECC BIT(12)
/* Error Status */
#define ERRSTS_OFFSET 0xc8
#define ERRSTS_CE BIT_ULL(6)
#define ERRSTS_UE BIT_ULL(7)
/* Error Command */
#define ERRCMD_OFFSET 0xca
#define ERRCMD_CE BIT_ULL(6)
#define ERRCMD_UE BIT_ULL(7)
/* IBECC MMIO base address */
#define IBECC_BASE (res_cfg->ibecc_base)
#define IBECC_ACTIVATE_OFFSET IBECC_BASE
#define IBECC_ACTIVATE_EN BIT(0)
/* IBECC error log */
#define ECC_ERROR_LOG_OFFSET (IBECC_BASE + res_cfg->ibecc_error_log_offset)
#define ECC_ERROR_LOG_CE BIT_ULL(62)
#define ECC_ERROR_LOG_UE BIT_ULL(63)
#define ECC_ERROR_LOG_ADDR_SHIFT 5
#define ECC_ERROR_LOG_ADDR(v) GET_BITFIELD(v, 5, 38)
#define ECC_ERROR_LOG_SYND(v) GET_BITFIELD(v, 46, 61)
/* Host MMIO base address */
#define MCHBAR_OFFSET 0x48
#define MCHBAR_EN BIT_ULL(0)
#define MCHBAR_BASE(v) (GET_BITFIELD(v, 16, 38) << 16)
#define MCHBAR_SIZE 0x10000
/* Parameters for the channel decode stage */
#define IMC_BASE (res_cfg->imc_base)
#define MAD_INTER_CHANNEL_OFFSET IMC_BASE
#define MAD_INTER_CHANNEL_DDR_TYPE(v) GET_BITFIELD(v, 0, 2)
#define MAD_INTER_CHANNEL_ECHM(v) GET_BITFIELD(v, 3, 3)
#define MAD_INTER_CHANNEL_CH_L_MAP(v) GET_BITFIELD(v, 4, 4)
#define MAD_INTER_CHANNEL_CH_S_SIZE(v) ((u64)GET_BITFIELD(v, 12, 19) << 29)
/* Parameters for DRAM decode stage */
#define MAD_INTRA_CH0_OFFSET (IMC_BASE + 4)
#define MAD_INTRA_CH_DIMM_L_MAP(v) GET_BITFIELD(v, 0, 0)
/* DIMM characteristics */
#define MAD_DIMM_CH0_OFFSET (IMC_BASE + 0xc)
#define MAD_DIMM_CH_DIMM_L_SIZE(v) ((u64)GET_BITFIELD(v, 0, 6) << 29)
#define MAD_DIMM_CH_DLW(v) GET_BITFIELD(v, 7, 8)
#define MAD_DIMM_CH_DIMM_S_SIZE(v) ((u64)GET_BITFIELD(v, 16, 22) << 29)
#define MAD_DIMM_CH_DSW(v) GET_BITFIELD(v, 24, 25)
/* Hash for memory controller selection */
#define MAD_MC_HASH_OFFSET (IMC_BASE + 0x1b8)
#define MAC_MC_HASH_LSB(v) GET_BITFIELD(v, 1, 3)
/* Hash for channel selection */
#define CHANNEL_HASH_OFFSET (IMC_BASE + 0x24)
/* Hash for enhanced channel selection */
#define CHANNEL_EHASH_OFFSET (IMC_BASE + 0x28)
#define CHANNEL_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6)
#define CHANNEL_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26)
#define CHANNEL_HASH_MODE(v) GET_BITFIELD(v, 28, 28)
/* Parameters for memory slice decode stage */
#define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6)
#define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26)
static struct res_config {
bool machine_check;
int num_imc;
u32 imc_base;
u32 cmf_base;
u32 cmf_size;
u32 ms_hash_offset;
u32 ibecc_base;
u32 ibecc_error_log_offset;
bool (*ibecc_available)(struct pci_dev *pdev);
/* Convert error address logged in IBECC to system physical address */
u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc);
/* Convert error address logged in IBECC to integrated memory controller address */
u64 (*err_addr_to_imc_addr)(u64 eaddr, int mc);
} *res_cfg;
struct igen6_imc {
int mc;
struct mem_ctl_info *mci;
struct pci_dev *pdev;
struct device dev;
void __iomem *window;
u64 size;
u64 ch_s_size;
int ch_l_map;
u64 dimm_s_size[NUM_CHANNELS];
u64 dimm_l_size[NUM_CHANNELS];
int dimm_l_map[NUM_CHANNELS];
};
static struct igen6_pvt {
struct igen6_imc imc[NUM_IMC];
u64 ms_hash;
u64 ms_s_size;
int ms_l_map;