brintos

brintos / linux-shallow public Read only

0
0
Text · 11.9 KiB · c9dc78d Raw
466 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Xilinx ZynqMP OCM ECC Driver4 *5 * Copyright (C) 2022 Advanced Micro Devices, Inc.6 */7 8#include <linux/edac.h>9#include <linux/interrupt.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/of_platform.h>13#include <linux/platform_device.h>14 15#include "edac_module.h"16 17#define ZYNQMP_OCM_EDAC_MSG_SIZE	25618 19#define ZYNQMP_OCM_EDAC_STRING	"zynqmp_ocm"20 21/* Error/Interrupt registers */22#define ERR_CTRL_OFST		0x023#define OCM_ISR_OFST		0x0424#define OCM_IMR_OFST		0x0825#define OCM_IEN_OFST		0x0C26#define OCM_IDS_OFST		0x1027 28/* ECC control register */29#define ECC_CTRL_OFST		0x1430 31/* Correctable error info registers */32#define CE_FFA_OFST		0x1C33#define CE_FFD0_OFST		0x2034#define CE_FFD1_OFST		0x2435#define CE_FFD2_OFST		0x2836#define CE_FFD3_OFST		0x2C37#define CE_FFE_OFST		0x3038 39/* Uncorrectable error info registers */40#define UE_FFA_OFST		0x3441#define UE_FFD0_OFST		0x3842#define UE_FFD1_OFST		0x3C43#define UE_FFD2_OFST		0x4044#define UE_FFD3_OFST		0x4445#define UE_FFE_OFST		0x4846 47/* ECC control register bit field definitions */48#define ECC_CTRL_CLR_CE_ERR	0x4049#define ECC_CTRL_CLR_UE_ERR	0x8050 51/* Fault injection data and count registers */52#define OCM_FID0_OFST		0x4C53#define OCM_FID1_OFST		0x5054#define OCM_FID2_OFST		0x5455#define OCM_FID3_OFST		0x5856#define OCM_FIC_OFST		0x7457 58#define UE_MAX_BITPOS_LOWER	3159#define UE_MIN_BITPOS_UPPER	3260#define UE_MAX_BITPOS_UPPER	6361 62/* Interrupt masks */63#define OCM_CEINTR_MASK		BIT(6)64#define OCM_UEINTR_MASK		BIT(7)65#define OCM_ECC_ENABLE_MASK	BIT(0)66 67#define OCM_FICOUNT_MASK	GENMASK(23, 0)68#define OCM_NUM_UE_BITPOS	269#define OCM_BASEVAL		0xFFFC000070#define EDAC_DEVICE		"ZynqMP-OCM"71 72/**73 * struct ecc_error_info - ECC error log information74 * @addr:	Fault generated at this address75 * @fault_lo:	Generated fault data (lower 32-bit)76 * @fault_hi:	Generated fault data (upper 32-bit)77 */78struct ecc_error_info {79	u32 addr;80	u32 fault_lo;81	u32 fault_hi;82};83 84/**85 * struct ecc_status - ECC status information to report86 * @ce_cnt:	Correctable error count87 * @ue_cnt:	Uncorrectable error count88 * @ceinfo:	Correctable error log information89 * @ueinfo:	Uncorrectable error log information90 */91struct ecc_status {92	u32 ce_cnt;93	u32 ue_cnt;94	struct ecc_error_info ceinfo;95	struct ecc_error_info ueinfo;96};97 98/**99 * struct edac_priv - OCM private instance data100 * @baseaddr:	Base address of the OCM101 * @message:	Buffer for framing the event specific info102 * @stat:	ECC status information103 * @ce_cnt:	Correctable Error count104 * @ue_cnt:	Uncorrectable Error count105 * @debugfs_dir:	Directory entry for debugfs106 * @ce_bitpos:	Bit position for Correctable Error107 * @ue_bitpos:	Array to store UnCorrectable Error bit positions108 * @fault_injection_cnt: Fault Injection Counter value109 */110struct edac_priv {111	void __iomem *baseaddr;112	char message[ZYNQMP_OCM_EDAC_MSG_SIZE];113	struct ecc_status stat;114	u32 ce_cnt;115	u32 ue_cnt;116#ifdef CONFIG_EDAC_DEBUG117	struct dentry *debugfs_dir;118	u8 ce_bitpos;119	u8 ue_bitpos[OCM_NUM_UE_BITPOS];120	u32 fault_injection_cnt;121#endif122};123 124/**125 * get_error_info - Get the current ECC error info126 * @base:	Pointer to the base address of the OCM127 * @p:		Pointer to the OCM ECC status structure128 * @mask:	Status register mask value129 *130 * Determines there is any ECC error or not131 *132 */133static void get_error_info(void __iomem *base, struct ecc_status *p, int mask)134{135	if (mask & OCM_CEINTR_MASK) {136		p->ce_cnt++;137		p->ceinfo.fault_lo = readl(base + CE_FFD0_OFST);138		p->ceinfo.fault_hi = readl(base + CE_FFD1_OFST);139		p->ceinfo.addr = (OCM_BASEVAL | readl(base + CE_FFA_OFST));140		writel(ECC_CTRL_CLR_CE_ERR, base + OCM_ISR_OFST);141	} else if (mask & OCM_UEINTR_MASK) {142		p->ue_cnt++;143		p->ueinfo.fault_lo = readl(base + UE_FFD0_OFST);144		p->ueinfo.fault_hi = readl(base + UE_FFD1_OFST);145		p->ueinfo.addr = (OCM_BASEVAL | readl(base + UE_FFA_OFST));146		writel(ECC_CTRL_CLR_UE_ERR, base + OCM_ISR_OFST);147	}148}149 150/**151 * handle_error - Handle error types CE and UE152 * @dci:	Pointer to the EDAC device instance153 * @p:		Pointer to the OCM ECC status structure154 *155 * Handles correctable and uncorrectable errors.156 */157static void handle_error(struct edac_device_ctl_info *dci, struct ecc_status *p)158{159	struct edac_priv *priv = dci->pvt_info;160	struct ecc_error_info *pinf;161 162	if (p->ce_cnt) {163		pinf = &p->ceinfo;164		snprintf(priv->message, ZYNQMP_OCM_EDAC_MSG_SIZE,165			 "\nOCM ECC error type :%s\nAddr: [0x%x]\nFault Data[0x%08x%08x]",166			 "CE", pinf->addr, pinf->fault_hi, pinf->fault_lo);167		edac_device_handle_ce(dci, 0, 0, priv->message);168	}169 170	if (p->ue_cnt) {171		pinf = &p->ueinfo;172		snprintf(priv->message, ZYNQMP_OCM_EDAC_MSG_SIZE,173			 "\nOCM ECC error type :%s\nAddr: [0x%x]\nFault Data[0x%08x%08x]",174			 "UE", pinf->addr, pinf->fault_hi, pinf->fault_lo);175		edac_device_handle_ue(dci, 0, 0, priv->message);176	}177 178	memset(p, 0, sizeof(*p));179}180 181/**182 * intr_handler - ISR routine183 * @irq:        irq number184 * @dev_id:     device id pointer185 *186 * Return: IRQ_NONE, if CE/UE interrupt not set or IRQ_HANDLED otherwise187 */188static irqreturn_t intr_handler(int irq, void *dev_id)189{190	struct edac_device_ctl_info *dci = dev_id;191	struct edac_priv *priv = dci->pvt_info;192	int regval;193 194	regval = readl(priv->baseaddr + OCM_ISR_OFST);195	if (!(regval & (OCM_CEINTR_MASK | OCM_UEINTR_MASK))) {196		WARN_ONCE(1, "Unhandled IRQ%d, ISR: 0x%x", irq, regval);197		return IRQ_NONE;198	}199 200	get_error_info(priv->baseaddr, &priv->stat, regval);201 202	priv->ce_cnt += priv->stat.ce_cnt;203	priv->ue_cnt += priv->stat.ue_cnt;204	handle_error(dci, &priv->stat);205 206	return IRQ_HANDLED;207}208 209/**210 * get_eccstate - Return the ECC status211 * @base:	Pointer to the OCM base address212 *213 * Get the ECC enable/disable status214 *215 * Return: ECC status 0/1.216 */217static bool get_eccstate(void __iomem *base)218{219	return readl(base + ECC_CTRL_OFST) & OCM_ECC_ENABLE_MASK;220}221 222#ifdef CONFIG_EDAC_DEBUG223/**224 * write_fault_count - write fault injection count225 * @priv:	Pointer to the EDAC private struct226 *227 * Update the fault injection count register, once the counter reaches228 * zero, it injects errors229 */230static void write_fault_count(struct edac_priv *priv)231{232	u32 ficount = priv->fault_injection_cnt;233 234	if (ficount & ~OCM_FICOUNT_MASK) {235		ficount &= OCM_FICOUNT_MASK;236		edac_printk(KERN_INFO, EDAC_DEVICE,237			    "Fault injection count value truncated to %d\n", ficount);238	}239 240	writel(ficount, priv->baseaddr + OCM_FIC_OFST);241}242 243/*244 * To get the Correctable Error injected, the following steps are needed:245 * - Setup the optional Fault Injection Count:246 *	echo <fault_count val> > /sys/kernel/debug/edac/ocm/inject_fault_count247 * - Write the Correctable Error bit position value:248 *	echo <bit_pos val> > /sys/kernel/debug/edac/ocm/inject_ce_bitpos249 */250static ssize_t inject_ce_write(struct file *file, const char __user *data,251			       size_t count, loff_t *ppos)252{253	struct edac_device_ctl_info *edac_dev = file->private_data;254	struct edac_priv *priv = edac_dev->pvt_info;255	int ret;256 257	if (!data)258		return -EFAULT;259 260	ret = kstrtou8_from_user(data, count, 0, &priv->ce_bitpos);261	if (ret)262		return ret;263 264	if (priv->ce_bitpos > UE_MAX_BITPOS_UPPER)265		return -EINVAL;266 267	if (priv->ce_bitpos <= UE_MAX_BITPOS_LOWER) {268		writel(BIT(priv->ce_bitpos), priv->baseaddr + OCM_FID0_OFST);269		writel(0, priv->baseaddr + OCM_FID1_OFST);270	} else {271		writel(BIT(priv->ce_bitpos - UE_MIN_BITPOS_UPPER),272		       priv->baseaddr + OCM_FID1_OFST);273		writel(0, priv->baseaddr + OCM_FID0_OFST);274	}275 276	write_fault_count(priv);277 278	return count;279}280 281static const struct file_operations inject_ce_fops = {282	.open = simple_open,283	.write = inject_ce_write,284	.llseek = generic_file_llseek,285};286 287/*288 * To get the Uncorrectable Error injected, the following steps are needed:289 * - Setup the optional Fault Injection Count:290 *      echo <fault_count val> > /sys/kernel/debug/edac/ocm/inject_fault_count291 * - Write the Uncorrectable Error bit position values:292 *      echo <bit_pos0 val>,<bit_pos1 val> > /sys/kernel/debug/edac/ocm/inject_ue_bitpos293 */294static ssize_t inject_ue_write(struct file *file, const char __user *data,295			       size_t count, loff_t *ppos)296{297	struct edac_device_ctl_info *edac_dev = file->private_data;298	struct edac_priv *priv = edac_dev->pvt_info;299	char buf[6], *pbuf, *token[2];300	u64 ue_bitpos;301	int i, ret;302	u8 len;303 304	if (!data)305		return -EFAULT;306 307	len = min_t(size_t, count, sizeof(buf));308	if (copy_from_user(buf, data, len))309		return -EFAULT;310 311	buf[len] = '\0';312	pbuf = &buf[0];313	for (i = 0; i < OCM_NUM_UE_BITPOS; i++)314		token[i] = strsep(&pbuf, ",");315 316	ret = kstrtou8(token[0], 0, &priv->ue_bitpos[0]);317	if (ret)318		return ret;319 320	ret = kstrtou8(token[1], 0, &priv->ue_bitpos[1]);321	if (ret)322		return ret;323 324	if (priv->ue_bitpos[0] > UE_MAX_BITPOS_UPPER ||325	    priv->ue_bitpos[1] > UE_MAX_BITPOS_UPPER)326		return -EINVAL;327 328	if (priv->ue_bitpos[0] == priv->ue_bitpos[1]) {329		edac_printk(KERN_ERR, EDAC_DEVICE, "Bit positions should not be equal\n");330		return -EINVAL;331	}332 333	ue_bitpos = BIT(priv->ue_bitpos[0]) | BIT(priv->ue_bitpos[1]);334 335	writel((u32)ue_bitpos, priv->baseaddr + OCM_FID0_OFST);336	writel((u32)(ue_bitpos >> 32), priv->baseaddr + OCM_FID1_OFST);337 338	write_fault_count(priv);339 340	return count;341}342 343static const struct file_operations inject_ue_fops = {344	.open = simple_open,345	.write = inject_ue_write,346	.llseek = generic_file_llseek,347};348 349static void setup_debugfs(struct edac_device_ctl_info *edac_dev)350{351	struct edac_priv *priv = edac_dev->pvt_info;352 353	priv->debugfs_dir = edac_debugfs_create_dir("ocm");354	if (!priv->debugfs_dir)355		return;356 357	edac_debugfs_create_x32("inject_fault_count", 0644, priv->debugfs_dir,358				&priv->fault_injection_cnt);359	edac_debugfs_create_file("inject_ue_bitpos", 0644, priv->debugfs_dir,360				 edac_dev, &inject_ue_fops);361	edac_debugfs_create_file("inject_ce_bitpos", 0644, priv->debugfs_dir,362				 edac_dev, &inject_ce_fops);363}364#endif365 366static int edac_probe(struct platform_device *pdev)367{368	struct edac_device_ctl_info *dci;369	struct edac_priv *priv;370	void __iomem *baseaddr;371	struct resource *res;372	int irq, ret;373 374	baseaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);375	if (IS_ERR(baseaddr))376		return PTR_ERR(baseaddr);377 378	if (!get_eccstate(baseaddr)) {379		edac_printk(KERN_INFO, EDAC_DEVICE, "ECC not enabled\n");380		return -ENXIO;381	}382 383	dci = edac_device_alloc_ctl_info(sizeof(*priv), ZYNQMP_OCM_EDAC_STRING,384					 1, ZYNQMP_OCM_EDAC_STRING, 1, 0,385					 edac_device_alloc_index());386	if (!dci)387		return -ENOMEM;388 389	priv = dci->pvt_info;390	platform_set_drvdata(pdev, dci);391	dci->dev = &pdev->dev;392	priv->baseaddr = baseaddr;393	dci->mod_name = pdev->dev.driver->name;394	dci->ctl_name = ZYNQMP_OCM_EDAC_STRING;395	dci->dev_name = dev_name(&pdev->dev);396 397	irq = platform_get_irq(pdev, 0);398	if (irq < 0) {399		ret = irq;400		goto free_dev_ctl;401	}402 403	ret = devm_request_irq(&pdev->dev, irq, intr_handler, 0,404			       dev_name(&pdev->dev), dci);405	if (ret) {406		edac_printk(KERN_ERR, EDAC_DEVICE, "Failed to request Irq\n");407		goto free_dev_ctl;408	}409 410	/* Enable UE, CE interrupts */411	writel((OCM_CEINTR_MASK | OCM_UEINTR_MASK), priv->baseaddr + OCM_IEN_OFST);412 413#ifdef CONFIG_EDAC_DEBUG414	setup_debugfs(dci);415#endif416 417	ret = edac_device_add_device(dci);418	if (ret)419		goto free_dev_ctl;420 421	return 0;422 423free_dev_ctl:424	edac_device_free_ctl_info(dci);425 426	return ret;427}428 429static void edac_remove(struct platform_device *pdev)430{431	struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);432	struct edac_priv *priv = dci->pvt_info;433 434	/* Disable UE, CE interrupts */435	writel((OCM_CEINTR_MASK | OCM_UEINTR_MASK), priv->baseaddr + OCM_IDS_OFST);436 437#ifdef CONFIG_EDAC_DEBUG438	debugfs_remove_recursive(priv->debugfs_dir);439#endif440 441	edac_device_del_device(&pdev->dev);442	edac_device_free_ctl_info(dci);443}444 445static const struct of_device_id zynqmp_ocm_edac_match[] = {446	{ .compatible = "xlnx,zynqmp-ocmc-1.0"},447	{ /* end of table */ }448};449 450MODULE_DEVICE_TABLE(of, zynqmp_ocm_edac_match);451 452static struct platform_driver zynqmp_ocm_edac_driver = {453	.driver = {454		   .name = "zynqmp-ocm-edac",455		   .of_match_table = zynqmp_ocm_edac_match,456		   },457	.probe = edac_probe,458	.remove_new = edac_remove,459};460 461module_platform_driver(zynqmp_ocm_edac_driver);462 463MODULE_AUTHOR("Advanced Micro Devices, Inc");464MODULE_DESCRIPTION("Xilinx ZynqMP OCM ECC driver");465MODULE_LICENSE("GPL");466