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