linux/drivers/edac/versal_edac.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Xilinx Versal memory controller driver
 * Copyright (C) 2023 Advanced Micro Devices, Inc.
 */
#include <linux/bitfield.h>
#include <linux/edac.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/firmware/xlnx-zynqmp.h>
#include <linux/firmware/xlnx-event-manager.h>

#include "edac_module.h"

/* Granularity of reported error in bytes */
#define XDDR_EDAC_ERR_GRAIN

#define XDDR_EDAC_MSG_SIZE
#define EVENT

#define XDDR_PCSR_OFFSET
#define XDDR_ISR_OFFSET
#define XDDR_IRQ_EN_OFFSET
#define XDDR_IRQ1_EN_OFFSET
#define XDDR_IRQ_DIS_OFFSET
#define XDDR_IRQ_CE_MASK
#define XDDR_IRQ_UE_MASK

#define XDDR_REG_CONFIG0_OFFSET
#define XDDR_REG_CONFIG0_BUS_WIDTH_MASK
#define XDDR_REG_CONFIG0_NUM_CHANS_MASK
#define XDDR_REG_CONFIG0_NUM_RANKS_MASK
#define XDDR_REG_CONFIG0_SIZE_MASK

#define XDDR_REG_PINOUT_OFFSET
#define XDDR_REG_PINOUT_ECC_EN_MASK

#define ECCW0_FLIP_CTRL
#define ECCW0_FLIP0_OFFSET
#define ECCW0_FLIP0_BITS
#define ECCW0_FLIP1_OFFSET
#define ECCW1_FLIP_CTRL
#define ECCW1_FLIP0_OFFSET
#define ECCW1_FLIP1_OFFSET
#define ECCR0_CERR_STAT_OFFSET
#define ECCR0_CE_ADDR_LO_OFFSET
#define ECCR0_CE_ADDR_HI_OFFSET
#define ECCR0_CE_DATA_LO_OFFSET
#define ECCR0_CE_DATA_HI_OFFSET
#define ECCR0_CE_DATA_PAR_OFFSET

#define ECCR0_UERR_STAT_OFFSET
#define ECCR0_UE_ADDR_LO_OFFSET
#define ECCR0_UE_ADDR_HI_OFFSET
#define ECCR0_UE_DATA_LO_OFFSET
#define ECCR0_UE_DATA_HI_OFFSET
#define ECCR0_UE_DATA_PAR_OFFSET

#define ECCR1_CERR_STAT_OFFSET
#define ECCR1_CE_ADDR_LO_OFFSET
#define ECCR1_CE_ADDR_HI_OFFSET
#define ECCR1_CE_DATA_LO_OFFSET
#define ECCR1_CE_DATA_HI_OFFSET
#define ECCR1_CE_DATA_PAR_OFFSET

#define ECCR1_UERR_STAT_OFFSET
#define ECCR1_UE_ADDR_LO_OFFSET
#define ECCR1_UE_ADDR_HI_OFFSET
#define ECCR1_UE_DATA_LO_OFFSET
#define ECCR1_UE_DATA_HI_OFFSET
#define ECCR1_UE_DATA_PAR_OFFSET

#define XDDR_NOC_REG_ADEC4_OFFSET
#define RANK_1_MASK
#define LRANK_0_MASK
#define LRANK_1_MASK
#define MASK_24

#define XDDR_NOC_REG_ADEC5_OFFSET
#define XDDR_NOC_REG_ADEC6_OFFSET
#define XDDR_NOC_REG_ADEC7_OFFSET
#define XDDR_NOC_REG_ADEC8_OFFSET
#define XDDR_NOC_REG_ADEC9_OFFSET
#define XDDR_NOC_REG_ADEC10_OFFSET

#define XDDR_NOC_REG_ADEC11_OFFSET
#define MASK_0
#define GRP_0_MASK
#define GRP_1_MASK
#define CH_0_MASK

#define XDDR_NOC_REG_ADEC12_OFFSET
#define XDDR_NOC_REG_ADEC13_OFFSET

#define XDDR_NOC_REG_ADEC14_OFFSET
#define XDDR_NOC_ROW_MATCH_MASK
#define XDDR_NOC_COL_MATCH_MASK
#define XDDR_NOC_BANK_MATCH_MASK
#define XDDR_NOC_GRP_MATCH_MASK

#define XDDR_NOC_REG_ADEC15_OFFSET
#define XDDR_NOC_RANK_MATCH_MASK
#define XDDR_NOC_LRANK_MATCH_MASK
#define XDDR_NOC_CH_MATCH_MASK
#define XDDR_NOC_MOD_SEL_MASK
#define XDDR_NOC_MATCH_EN_MASK

#define ECCR_UE_CE_ADDR_HI_ROW_MASK

#define XDDR_EDAC_NR_CSROWS
#define XDDR_EDAC_NR_CHANS

#define XDDR_BUS_WIDTH_64
#define XDDR_BUS_WIDTH_32
#define XDDR_BUS_WIDTH_16

#define XDDR_MAX_ROW_CNT
#define XDDR_MAX_COL_CNT
#define XDDR_MAX_RANK_CNT
#define XDDR_MAX_LRANK_CNT
#define XDDR_MAX_BANK_CNT
#define XDDR_MAX_GRP_CNT

/*
 * Config and system registers are usually locked. This is the
 * code which unlocks them in order to accept writes. See
 *
 * https://docs.xilinx.com/r/en-US/am012-versal-register-reference/PCSR_LOCK-XRAM_SLCR-Register
 */
#define PCSR_UNLOCK_VAL
#define PCSR_LOCK_VAL
#define XDDR_ERR_TYPE_CE
#define XDDR_ERR_TYPE_UE

#define XILINX_DRAM_SIZE_4G
#define XILINX_DRAM_SIZE_6G
#define XILINX_DRAM_SIZE_8G
#define XILINX_DRAM_SIZE_12G
#define XILINX_DRAM_SIZE_16G
#define XILINX_DRAM_SIZE_32G
#define NUM_UE_BITPOS

/**
 * struct ecc_error_info - ECC error log information.
 * @burstpos:		Burst position.
 * @lrank:		Logical Rank number.
 * @rank:		Rank number.
 * @group:		Group number.
 * @bank:		Bank number.
 * @col:		Column number.
 * @row:		Row number.
 * @rowhi:		Row number higher bits.
 * @i:			ECC error info.
 */
ecc_error_info __packed;

edac_info __packed;

/**
 * struct ecc_status - ECC status information to report.
 * @ceinfo:	Correctable error log information.
 * @ueinfo:	Uncorrectable error log information.
 * @channel:	Channel number.
 * @error_type:	Error type information.
 */
struct ecc_status {};

/**
 * struct edac_priv - DDR memory controller private instance data.
 * @ddrmc_baseaddr:	Base address of the DDR controller.
 * @ddrmc_noc_baseaddr:	Base address of the DDRMC NOC.
 * @message:		Buffer for framing the event specific info.
 * @mc_id:		Memory controller ID.
 * @ce_cnt:		Correctable error count.
 * @ue_cnt:		UnCorrectable error count.
 * @stat:		ECC status information.
 * @lrank_bit:		Bit shifts for lrank bit.
 * @rank_bit:		Bit shifts for rank bit.
 * @row_bit:		Bit shifts for row bit.
 * @col_bit:		Bit shifts for column bit.
 * @bank_bit:		Bit shifts for bank bit.
 * @grp_bit:		Bit shifts for group bit.
 * @ch_bit:		Bit shifts for channel bit.
 * @err_inject_addr:	Data poison address.
 * @debugfs:		Debugfs handle.
 */
struct edac_priv {};

static void get_ce_error_info(struct edac_priv *priv)
{}

static void get_ue_error_info(struct edac_priv *priv)
{}

static bool get_error_info(struct edac_priv *priv)
{}

/**
 * convert_to_physical - Convert to physical address.
 * @priv:	DDR memory controller private instance data.
 * @pinf:	ECC error info structure.
 *
 * Return: Physical address of the DDR memory.
 */
static unsigned long convert_to_physical(struct edac_priv *priv, union ecc_error_info pinf)
{}

/**
 * handle_error - Handle Correctable and Uncorrectable errors.
 * @mci:	EDAC memory controller instance.
 * @stat:	ECC status structure.
 *
 * Handles ECC correctable and uncorrectable errors.
 */
static void handle_error(struct mem_ctl_info *mci, struct ecc_status *stat)
{}

/**
 * err_callback - Handle Correctable and Uncorrectable errors.
 * @payload:	payload data.
 * @data:	mci controller data.
 *
 * Handles ECC correctable and uncorrectable errors.
 */
static void err_callback(const u32 *payload, void *data)
{}

/**
 * get_dwidth - Return the controller memory width.
 * @base:	DDR memory controller base address.
 *
 * Get the EDAC device type width appropriate for the controller
 * configuration.
 *
 * Return: a device type width enumeration.
 */
static enum dev_type get_dwidth(const void __iomem *base)
{}

/**
 * get_ecc_state - Return the controller ECC enable/disable status.
 * @base:	DDR memory controller base address.
 *
 * Get the ECC enable/disable status for the controller.
 *
 * Return: a ECC status boolean i.e true/false - enabled/disabled.
 */
static bool get_ecc_state(void __iomem *base)
{}

/**
 * get_memsize - Get the size of the attached memory device.
 * @priv:	DDR memory controller private instance data.
 *
 * Return: the memory size in bytes.
 */
static u64 get_memsize(struct edac_priv *priv)
{}

/**
 * init_csrows - Initialize the csrow data.
 * @mci:	EDAC memory controller instance.
 *
 * Initialize the chip select rows associated with the EDAC memory
 * controller instance.
 */
static void init_csrows(struct mem_ctl_info *mci)
{}

/**
 * mc_init - Initialize one driver instance.
 * @mci:	EDAC memory controller instance.
 * @pdev:	platform device.
 *
 * Perform initialization of the EDAC memory controller instance and
 * related driver-private data associated with the memory controller the
 * instance is bound to.
 */
static void mc_init(struct mem_ctl_info *mci, struct platform_device *pdev)
{}

static void enable_intr(struct edac_priv *priv)
{}

static void disable_intr(struct edac_priv *priv)
{}

#define to_mci(k)

#ifdef CONFIG_EDAC_DEBUG
/**
 * poison_setup - Update poison registers.
 * @priv:	DDR memory controller private instance data.
 *
 * Update poison registers as per DDR mapping upon write of the address
 * location the fault is injected.
 * Return: none.
 */
static void poison_setup(struct edac_priv *priv)
{}

static void xddr_inject_data_ce_store(struct mem_ctl_info *mci, u8 ce_bitpos)
{}

/*
 * To inject a correctable error, the following steps are needed:
 *
 * - Write the correctable error bit position value:
 *	echo <bit_pos val> > /sys/kernel/debug/edac/<controller instance>/inject_ce
 *
 * poison_setup() derives the row, column, bank, group and rank and
 * writes to the ADEC registers based on the address given by the user.
 *
 * The ADEC12 and ADEC13 are mask registers; write 0 to make sure default
 * configuration is there and no addresses are masked.
 *
 * The row, column, bank, group and rank registers are written to the
 * match ADEC bit to generate errors at the particular address. ADEC14
 * and ADEC15 have the match bits.
 *
 * xddr_inject_data_ce_store() updates the ECC FLIP registers with the
 * bits to be corrupted based on the bit position given by the user.
 *
 * Upon doing a read to the address the errors are injected.
 */
static ssize_t inject_data_ce_store(struct file *file, const char __user *data,
				    size_t count, loff_t *ppos)
{}

static const struct file_operations xddr_inject_ce_fops =;

static void xddr_inject_data_ue_store(struct mem_ctl_info *mci, u32 val0, u32 val1)
{}

/*
 * To inject an uncorrectable error, the following steps are needed:
 *	echo <bit_pos val> > /sys/kernel/debug/edac/<controller instance>/inject_ue
 *
 * poison_setup() derives the row, column, bank, group and rank and
 * writes to the ADEC registers based on the address given by the user.
 *
 * The ADEC12 and ADEC13 are mask registers; write 0 so that none of the
 * addresses are masked. The row, column, bank, group and rank registers
 * are written to the match ADEC bit to generate errors at the
 * particular address. ADEC14 and ADEC15 have the match bits.
 *
 * xddr_inject_data_ue_store() updates the ECC FLIP registers with the
 * bits to be corrupted based on the bit position given by the user. For
 * uncorrectable errors
 * 2 bit errors are injected.
 *
 * Upon doing a read to the address the errors are injected.
 */
static ssize_t inject_data_ue_store(struct file *file, const char __user *data,
				    size_t count, loff_t *ppos)
{}

static const struct file_operations xddr_inject_ue_fops =;

static void create_debugfs_attributes(struct mem_ctl_info *mci)
{}

static inline void process_bit(struct edac_priv *priv, unsigned int start, u32 regval)
{}

static void setup_row_address_map(struct edac_priv *priv)
{}

static void setup_column_address_map(struct edac_priv *priv)
{}

static void setup_bank_grp_ch_address_map(struct edac_priv *priv)
{}

static void setup_rank_lrank_address_map(struct edac_priv *priv)
{}

/**
 * setup_address_map - Set Address Map by querying ADDRMAP registers.
 * @priv:	DDR memory controller private instance data.
 *
 * Set Address Map by querying ADDRMAP registers.
 *
 * Return: none.
 */
static void setup_address_map(struct edac_priv *priv)
{}
#endif /* CONFIG_EDAC_DEBUG */

static const struct of_device_id xlnx_edac_match[] =;

MODULE_DEVICE_TABLE(of, xlnx_edac_match);
static u32 emif_get_id(struct device_node *node)
{}

static int mc_probe(struct platform_device *pdev)
{}

static void mc_remove(struct platform_device *pdev)
{}

static struct platform_driver xilinx_ddr_edac_mc_driver =;

module_platform_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();