drivers/edac/versal_edac.c

Source file repositories/reference/linux-study-clean/drivers/edac/versal_edac.c

File Facts

System
Linux kernel
Corpus path
drivers/edac/versal_edac.c
Extension
.c
Size
32786 bytes
Lines
1197
Domain
Driver Families
Bucket
drivers/edac
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct file_operations xddr_inject_ce_fops = {
	.open = simple_open,
	.write = inject_data_ce_store,
	.llseek = generic_file_llseek,
};

static void xddr_inject_data_ue_store(struct mem_ctl_info *mci, u32 val0, u32 val1)
{
	struct edac_priv *priv = mci->pvt_info;

	writel(val0, priv->ddrmc_baseaddr + ECCW0_FLIP0_OFFSET);
	writel(val0, priv->ddrmc_baseaddr + ECCW0_FLIP1_OFFSET);
	writel(val1, priv->ddrmc_baseaddr + ECCW1_FLIP1_OFFSET);
	writel(val1, priv->ddrmc_baseaddr + ECCW1_FLIP1_OFFSET);
}

/*
 * 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)
{
	struct device *dev = file->private_data;
	struct mem_ctl_info *mci = to_mci(dev);
	struct edac_priv *priv = mci->pvt_info;
	char buf[6], *pbuf, *token[2];
	u32 val0 = 0, val1 = 0;
	u8 len, ue0, ue1;
	int i, ret;

	len = min_t(size_t, count, sizeof(buf));
	if (copy_from_user(buf, data, len))
		return -EFAULT;

	buf[len] = '\0';
	pbuf = &buf[0];
	for (i = 0; i < NUM_UE_BITPOS; i++)
		token[i] = strsep(&pbuf, ",");

	if (!token[0] || !token[1])
		return -EFAULT;

	ret = kstrtou8(token[0], 0, &ue0);
	if (ret)
		return ret;

	ret = kstrtou8(token[1], 0, &ue1);
	if (ret)
		return ret;

	if (ue0 < ECCW0_FLIP0_BITS) {
		val0 = BIT(ue0);
	} else {
		ue0 = ue0 - ECCW0_FLIP0_BITS;
		val1 = BIT(ue0);
	}

	if (ue1 < ECCW0_FLIP0_BITS) {
		val0 |= BIT(ue1);
	} else {
		ue1 = ue1 - ECCW0_FLIP0_BITS;
		val1 |= BIT(ue1);
	}

	/* Unlock the PCSR registers */
	writel(PCSR_UNLOCK_VAL, priv->ddrmc_baseaddr + XDDR_PCSR_OFFSET);
	writel(PCSR_UNLOCK_VAL, priv->ddrmc_noc_baseaddr + XDDR_PCSR_OFFSET);

	poison_setup(priv);

	xddr_inject_data_ue_store(mci, val0, val1);

	/* Lock the PCSR registers */
	writel(PCSR_LOCK_VAL, priv->ddrmc_noc_baseaddr + XDDR_PCSR_OFFSET);

Annotation

Implementation Notes