// SPDX-License-Identifier: GPL-2.0 /* * Xilinx ZynqMP OCM ECC Driver * * Copyright (C) 2022 Advanced Micro Devices, Inc. */ #include <linux/edac.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include "edac_module.h" #define ZYNQMP_OCM_EDAC_MSG_SIZE … #define ZYNQMP_OCM_EDAC_STRING … /* Error/Interrupt registers */ #define ERR_CTRL_OFST … #define OCM_ISR_OFST … #define OCM_IMR_OFST … #define OCM_IEN_OFST … #define OCM_IDS_OFST … /* ECC control register */ #define ECC_CTRL_OFST … /* Correctable error info registers */ #define CE_FFA_OFST … #define CE_FFD0_OFST … #define CE_FFD1_OFST … #define CE_FFD2_OFST … #define CE_FFD3_OFST … #define CE_FFE_OFST … /* Uncorrectable error info registers */ #define UE_FFA_OFST … #define UE_FFD0_OFST … #define UE_FFD1_OFST … #define UE_FFD2_OFST … #define UE_FFD3_OFST … #define UE_FFE_OFST … /* ECC control register bit field definitions */ #define ECC_CTRL_CLR_CE_ERR … #define ECC_CTRL_CLR_UE_ERR … /* Fault injection data and count registers */ #define OCM_FID0_OFST … #define OCM_FID1_OFST … #define OCM_FID2_OFST … #define OCM_FID3_OFST … #define OCM_FIC_OFST … #define UE_MAX_BITPOS_LOWER … #define UE_MIN_BITPOS_UPPER … #define UE_MAX_BITPOS_UPPER … /* Interrupt masks */ #define OCM_CEINTR_MASK … #define OCM_UEINTR_MASK … #define OCM_ECC_ENABLE_MASK … #define OCM_FICOUNT_MASK … #define OCM_NUM_UE_BITPOS … #define OCM_BASEVAL … #define EDAC_DEVICE … /** * struct ecc_error_info - ECC error log information * @addr: Fault generated at this address * @fault_lo: Generated fault data (lower 32-bit) * @fault_hi: Generated fault data (upper 32-bit) */ struct ecc_error_info { … }; /** * struct ecc_status - ECC status information to report * @ce_cnt: Correctable error count * @ue_cnt: Uncorrectable error count * @ceinfo: Correctable error log information * @ueinfo: Uncorrectable error log information */ struct ecc_status { … }; /** * struct edac_priv - OCM private instance data * @baseaddr: Base address of the OCM * @message: Buffer for framing the event specific info * @stat: ECC status information * @ce_cnt: Correctable Error count * @ue_cnt: Uncorrectable Error count * @debugfs_dir: Directory entry for debugfs * @ce_bitpos: Bit position for Correctable Error * @ue_bitpos: Array to store UnCorrectable Error bit positions * @fault_injection_cnt: Fault Injection Counter value */ struct edac_priv { … }; /** * get_error_info - Get the current ECC error info * @base: Pointer to the base address of the OCM * @p: Pointer to the OCM ECC status structure * @mask: Status register mask value * * Determines there is any ECC error or not * */ static void get_error_info(void __iomem *base, struct ecc_status *p, int mask) { … } /** * handle_error - Handle error types CE and UE * @dci: Pointer to the EDAC device instance * @p: Pointer to the OCM ECC status structure * * Handles correctable and uncorrectable errors. */ static void handle_error(struct edac_device_ctl_info *dci, struct ecc_status *p) { … } /** * intr_handler - ISR routine * @irq: irq number * @dev_id: device id pointer * * Return: IRQ_NONE, if CE/UE interrupt not set or IRQ_HANDLED otherwise */ static irqreturn_t intr_handler(int irq, void *dev_id) { … } /** * get_eccstate - Return the ECC status * @base: Pointer to the OCM base address * * Get the ECC enable/disable status * * Return: ECC status 0/1. */ static bool get_eccstate(void __iomem *base) { … } #ifdef CONFIG_EDAC_DEBUG /** * write_fault_count - write fault injection count * @priv: Pointer to the EDAC private struct * * Update the fault injection count register, once the counter reaches * zero, it injects errors */ static void write_fault_count(struct edac_priv *priv) { … } /* * To get the Correctable Error injected, the following steps are needed: * - Setup the optional Fault Injection Count: * echo <fault_count val> > /sys/kernel/debug/edac/ocm/inject_fault_count * - Write the Correctable Error bit position value: * echo <bit_pos val> > /sys/kernel/debug/edac/ocm/inject_ce_bitpos */ static ssize_t inject_ce_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { … } static const struct file_operations inject_ce_fops = …; /* * To get the Uncorrectable Error injected, the following steps are needed: * - Setup the optional Fault Injection Count: * echo <fault_count val> > /sys/kernel/debug/edac/ocm/inject_fault_count * - Write the Uncorrectable Error bit position values: * echo <bit_pos0 val>,<bit_pos1 val> > /sys/kernel/debug/edac/ocm/inject_ue_bitpos */ static ssize_t inject_ue_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { … } static const struct file_operations inject_ue_fops = …; static void setup_debugfs(struct edac_device_ctl_info *edac_dev) { … } #endif static int edac_probe(struct platform_device *pdev) { … } static void edac_remove(struct platform_device *pdev) { … } static const struct of_device_id zynqmp_ocm_edac_match[] = …; MODULE_DEVICE_TABLE(of, zynqmp_ocm_edac_match); static struct platform_driver zynqmp_ocm_edac_driver = …; module_platform_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;