// SPDX-License-Identifier: GPL-2.0/* * Xilinx SDFEC * * Copyright (C) 2019 Xilinx, Inc. * * Description: * This driver is developed for SDFEC16 (Soft Decision FEC 16nm) * IP. It exposes a char device which supports file operations * like open(), close() and ioctl(). */ #include <linux/miscdevice.h>#include <linux/io.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/of.h>#include <linux/platform_device.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/clk.h>#include <linux/compat.h>#include <linux/highmem.h> #include <uapi/misc/xilinx_sdfec.h> #define DEV_NAME_LEN 12 static DEFINE_IDA(dev_nrs); /* Xilinx SDFEC Register Map *//* CODE_WRI_PROTECT Register */#define XSDFEC_CODE_WR_PROTECT_ADDR (0x4) /* ACTIVE Register */#define XSDFEC_ACTIVE_ADDR (0x8)#define XSDFEC_IS_ACTIVITY_SET (0x1) /* AXIS_WIDTH Register */#define XSDFEC_AXIS_WIDTH_ADDR (0xC)#define XSDFEC_AXIS_DOUT_WORDS_LSB (5)#define XSDFEC_AXIS_DOUT_WIDTH_LSB (3)#define XSDFEC_AXIS_DIN_WORDS_LSB (2)#define XSDFEC_AXIS_DIN_WIDTH_LSB (0) /* AXIS_ENABLE Register */#define XSDFEC_AXIS_ENABLE_ADDR (0x10)#define XSDFEC_AXIS_OUT_ENABLE_MASK (0x38)#define XSDFEC_AXIS_IN_ENABLE_MASK (0x7)#define XSDFEC_AXIS_ENABLE_MASK \ (XSDFEC_AXIS_OUT_ENABLE_MASK | XSDFEC_AXIS_IN_ENABLE_MASK) /* FEC_CODE Register */#define XSDFEC_FEC_CODE_ADDR (0x14) /* ORDER Register Map */#define XSDFEC_ORDER_ADDR (0x18) /* Interrupt Status Register */#define XSDFEC_ISR_ADDR (0x1C)/* Interrupt Status Register Bit Mask */#define XSDFEC_ISR_MASK (0x3F) /* Write Only - Interrupt Enable Register */#define XSDFEC_IER_ADDR (0x20)/* Write Only - Interrupt Disable Register */#define XSDFEC_IDR_ADDR (0x24)/* Read Only - Interrupt Mask Register */#define XSDFEC_IMR_ADDR (0x28) /* ECC Interrupt Status Register */#define XSDFEC_ECC_ISR_ADDR (0x2C)/* Single Bit Errors */#define XSDFEC_ECC_ISR_SBE_MASK (0x7FF)/* PL Initialize Single Bit Errors */#define XSDFEC_PL_INIT_ECC_ISR_SBE_MASK (0x3C00000)/* Multi Bit Errors */#define XSDFEC_ECC_ISR_MBE_MASK (0x3FF800)/* PL Initialize Multi Bit Errors */#define XSDFEC_PL_INIT_ECC_ISR_MBE_MASK (0x3C000000)/* Multi Bit Error to Event Shift */#define XSDFEC_ECC_ISR_MBE_TO_EVENT_SHIFT (11)/* PL Initialize Multi Bit Error to Event Shift */#define XSDFEC_PL_INIT_ECC_ISR_MBE_TO_EVENT_SHIFT (4)/* ECC Interrupt Status Bit Mask */#define XSDFEC_ECC_ISR_MASK (XSDFEC_ECC_ISR_SBE_MASK | XSDFEC_ECC_ISR_MBE_MASK)/* ECC Interrupt Status PL Initialize Bit Mask */#define XSDFEC_PL_INIT_ECC_ISR_MASK \ (XSDFEC_PL_INIT_ECC_ISR_SBE_MASK | XSDFEC_PL_INIT_ECC_ISR_MBE_MASK)/* ECC Interrupt Status All Bit Mask */#define XSDFEC_ALL_ECC_ISR_MASK \ (XSDFEC_ECC_ISR_MASK | XSDFEC_PL_INIT_ECC_ISR_MASK)/* ECC Interrupt Status Single Bit Errors Mask */#define XSDFEC_ALL_ECC_ISR_SBE_MASK \ (XSDFEC_ECC_ISR_SBE_MASK | XSDFEC_PL_INIT_ECC_ISR_SBE_MASK)/* ECC Interrupt Status Multi Bit Errors Mask */#define XSDFEC_ALL_ECC_ISR_MBE_MASK \ (XSDFEC_ECC_ISR_MBE_MASK | XSDFEC_PL_INIT_ECC_ISR_MBE_MASK) /* Write Only - ECC Interrupt Enable Register */#define XSDFEC_ECC_IER_ADDR (0x30)/* Write Only - ECC Interrupt Disable Register */#define XSDFEC_ECC_IDR_ADDR (0x34)/* Read Only - ECC Interrupt Mask Register */#define XSDFEC_ECC_IMR_ADDR (0x38) /* BYPASS Register */#define XSDFEC_BYPASS_ADDR (0x3C) /* Turbo Code Register */#define XSDFEC_TURBO_ADDR (0x100)#define XSDFEC_TURBO_SCALE_MASK (0xFFF)#define XSDFEC_TURBO_SCALE_BIT_POS (8)#define XSDFEC_TURBO_SCALE_MAX (15) /* REG0 Register */#define XSDFEC_LDPC_CODE_REG0_ADDR_BASE (0x2000)#define XSDFEC_LDPC_CODE_REG0_ADDR_HIGH (0x27F0)#define XSDFEC_REG0_N_MIN (4)#define XSDFEC_REG0_N_MAX (32768)#define XSDFEC_REG0_N_MUL_P (256)#define XSDFEC_REG0_N_LSB (0)#define XSDFEC_REG0_K_MIN (2)#define XSDFEC_REG0_K_MAX (32766)#define XSDFEC_REG0_K_MUL_P (256)#define XSDFEC_REG0_K_LSB (16) /* REG1 Register */#define XSDFEC_LDPC_CODE_REG1_ADDR_BASE (0x2004)#define XSDFEC_LDPC_CODE_REG1_ADDR_HIGH (0x27f4)#define XSDFEC_REG1_PSIZE_MIN (2)#define XSDFEC_REG1_PSIZE_MAX (512)#define XSDFEC_REG1_NO_PACKING_MASK (0x400)#define XSDFEC_REG1_NO_PACKING_LSB (10)#define XSDFEC_REG1_NM_MASK (0xFF800)#define XSDFEC_REG1_NM_LSB (11)#define XSDFEC_REG1_BYPASS_MASK (0x100000) /* REG2 Register */#define XSDFEC_LDPC_CODE_REG2_ADDR_BASE (0x2008)#define XSDFEC_LDPC_CODE_REG2_ADDR_HIGH (0x27f8)#define XSDFEC_REG2_NLAYERS_MIN (1)#define XSDFEC_REG2_NLAYERS_MAX (256)#define XSDFEC_REG2_NNMQC_MASK (0xFFE00)#define XSDFEC_REG2_NMQC_LSB (9)#define XSDFEC_REG2_NORM_TYPE_MASK (0x100000)#define XSDFEC_REG2_NORM_TYPE_LSB (20)#define XSDFEC_REG2_SPECIAL_QC_MASK (0x200000)#define XSDFEC_REG2_SPEICAL_QC_LSB (21)#define XSDFEC_REG2_NO_FINAL_PARITY_MASK (0x400000)#define XSDFEC_REG2_NO_FINAL_PARITY_LSB (22)#define XSDFEC_REG2_MAX_SCHEDULE_MASK (0x1800000)#define XSDFEC_REG2_MAX_SCHEDULE_LSB (23) /* REG3 Register */#define XSDFEC_LDPC_CODE_REG3_ADDR_BASE (0x200C)#define XSDFEC_LDPC_CODE_REG3_ADDR_HIGH (0x27FC)#define XSDFEC_REG3_LA_OFF_LSB (8)#define XSDFEC_REG3_QC_OFF_LSB (16) #define XSDFEC_LDPC_REG_JUMP (0x10)#define XSDFEC_REG_WIDTH_JUMP (4) /* The maximum number of pinned pages */#define MAX_NUM_PAGES ((XSDFEC_QC_TABLE_DEPTH / PAGE_SIZE) + 1) /** * struct xsdfec_clks - For managing SD-FEC clocks * @core_clk: Main processing clock for core * @axi_clk: AXI4-Lite memory-mapped clock * @din_words_clk: DIN Words AXI4-Stream Slave clock * @din_clk: DIN AXI4-Stream Slave clock * @dout_clk: DOUT Words AXI4-Stream Slave clock * @dout_words_clk: DOUT AXI4-Stream Slave clock * @ctrl_clk: Control AXI4-Stream Slave clock * @status_clk: Status AXI4-Stream Slave clock */struct xsdfec_clks { struct clk *core_clk; struct clk *axi_clk; struct clk *din_words_clk; struct clk *din_clk; struct clk *dout_clk; struct clk *dout_words_clk; struct clk *ctrl_clk; struct clk *status_clk;}; /** * struct xsdfec_dev - Driver data for SDFEC * @miscdev: Misc device handle * @clks: Clocks managed by the SDFEC driver * @waitq: Driver wait queue * @config: Configuration of the SDFEC device * @dev_name: Device name * @flags: spinlock flags * @regs: device physical base address * @dev: pointer to device struct * @state: State of the SDFEC device * @error_data_lock: Error counter and states spinlock * @dev_id: Device ID * @isr_err_count: Count of ISR errors * @cecc_count: Count of Correctable ECC errors (SBE) * @uecc_count: Count of Uncorrectable ECC errors (MBE) * @irq: IRQ number * @state_updated: indicates State updated by interrupt handler * @stats_updated: indicates Stats updated by interrupt handler * @intr_enabled: indicates IRQ enabled * * This structure contains necessary state for SDFEC driver to operate */struct xsdfec_dev { struct miscdevice miscdev; struct xsdfec_clks clks; wait_queue_head_t waitq; struct xsdfec_config config; char dev_name[DEV_NAME_LEN]; unsigned long flags; void __iomem *regs; struct device *dev; enum xsdfec_state state; /* Spinlock to protect state_updated and stats_updated */ spinlock_t error_data_lock; int dev_id; u32 isr_err_count; u32 cecc_count; u32 uecc_count; int irq; bool state_updated; bool stats_updated; bool intr_enabled;}; static inline void xsdfec_regwrite(struct xsdfec_dev *xsdfec, u32 addr, u32 value){ dev_dbg(xsdfec->dev, "Writing 0x%x to offset 0x%x", value, addr); iowrite32(value, xsdfec->regs + addr);} static inline u32 xsdfec_regread(struct xsdfec_dev *xsdfec, u32 addr){ u32 rval; rval = ioread32(xsdfec->regs + addr); dev_dbg(xsdfec->dev, "Read value = 0x%x from offset 0x%x", rval, addr); return rval;} static void update_bool_config_from_reg(struct xsdfec_dev *xsdfec, u32 reg_offset, u32 bit_num, char *config_value){ u32 reg_val; u32 bit_mask = 1 << bit_num; reg_val = xsdfec_regread(xsdfec, reg_offset); *config_value = (reg_val & bit_mask) > 0;} static void update_config_from_hw(struct xsdfec_dev *xsdfec){ u32 reg_value; bool sdfec_started; /* Update the Order */ reg_value = xsdfec_regread(xsdfec, XSDFEC_ORDER_ADDR); xsdfec->config.order = reg_value; update_bool_config_from_reg(xsdfec, XSDFEC_BYPASS_ADDR, 0, /* Bit Number, maybe change to mask */ &xsdfec->config.bypass); update_bool_config_from_reg(xsdfec, XSDFEC_CODE_WR_PROTECT_ADDR, 0, /* Bit Number */ &xsdfec->config.code_wr_protect); reg_value = xsdfec_regread(xsdfec, XSDFEC_IMR_ADDR); xsdfec->config.irq.enable_isr = (reg_value & XSDFEC_ISR_MASK) > 0; reg_value = xsdfec_regread(xsdfec, XSDFEC_ECC_IMR_ADDR); xsdfec->config.irq.enable_ecc_isr = (reg_value & XSDFEC_ECC_ISR_MASK) > 0; reg_value = xsdfec_regread(xsdfec, XSDFEC_AXIS_ENABLE_ADDR); sdfec_started = (reg_value & XSDFEC_AXIS_IN_ENABLE_MASK) > 0; if (sdfec_started) xsdfec->state = XSDFEC_STARTED; else xsdfec->state = XSDFEC_STOPPED;} static int xsdfec_get_status(struct xsdfec_dev *xsdfec, void __user *arg){ struct xsdfec_status status; int err; memset(&status, 0, sizeof(status)); spin_lock_irqsave(&xsdfec->error_data_lock, xsdfec->flags); status.state = xsdfec->state; xsdfec->state_updated = false; spin_unlock_irqrestore(&xsdfec->error_data_lock, xsdfec->flags); status.activity = (xsdfec_regread(xsdfec, XSDFEC_ACTIVE_ADDR) & XSDFEC_IS_ACTIVITY_SET); err = copy_to_user(arg, &status, sizeof(status)); if (err) err = -EFAULT; return err;} static int xsdfec_get_config(struct xsdfec_dev *xsdfec, void __user *arg){ int err; err = copy_to_user(arg, &xsdfec->config, sizeof(xsdfec->config)); if (err) err = -EFAULT; return err;} static int xsdfec_isr_enable(struct xsdfec_dev *xsdfec, bool enable){ u32 mask_read; if (enable) { /* Enable */ xsdfec_regwrite(xsdfec, XSDFEC_IER_ADDR, XSDFEC_ISR_MASK); mask_read = xsdfec_regread(xsdfec, XSDFEC_IMR_ADDR); if (mask_read & XSDFEC_ISR_MASK) { dev_dbg(xsdfec->dev, "SDFEC enabling irq with IER failed"); return -EIO; } } else { /* Disable */ xsdfec_regwrite(xsdfec, XSDFEC_IDR_ADDR, XSDFEC_ISR_MASK); mask_read = xsdfec_regread(xsdfec, XSDFEC_IMR_ADDR); if ((mask_read & XSDFEC_ISR_MASK) != XSDFEC_ISR_MASK) { dev_dbg(xsdfec->dev, "SDFEC disabling irq with IDR failed"); return -EIO; } } return 0;} static int xsdfec_ecc_isr_enable(struct xsdfec_dev *xsdfec, bool enable){ u32 mask_read; if (enable) { /* Enable */ xsdfec_regwrite(xsdfec, XSDFEC_ECC_IER_ADDR, XSDFEC_ALL_ECC_ISR_MASK); mask_read = xsdfec_regread(xsdfec, XSDFEC_ECC_IMR_ADDR); if (mask_read & XSDFEC_ALL_ECC_ISR_MASK) { dev_dbg(xsdfec->dev, "SDFEC enabling ECC irq with ECC IER failed"); return -EIO; } } else { /* Disable */ xsdfec_regwrite(xsdfec, XSDFEC_ECC_IDR_ADDR, XSDFEC_ALL_ECC_ISR_MASK); mask_read = xsdfec_regread(xsdfec, XSDFEC_ECC_IMR_ADDR); if (!(((mask_read & XSDFEC_ALL_ECC_ISR_MASK) == XSDFEC_ECC_ISR_MASK) || ((mask_read & XSDFEC_ALL_ECC_ISR_MASK) == XSDFEC_PL_INIT_ECC_ISR_MASK))) { dev_dbg(xsdfec->dev, "SDFEC disable ECC irq with ECC IDR failed"); return -EIO; } } return 0;} static int xsdfec_set_irq(struct xsdfec_dev *xsdfec, void __user *arg){ struct xsdfec_irq irq; int err; int isr_err; int ecc_err; err = copy_from_user(&irq, arg, sizeof(irq)); if (err) return -EFAULT; /* Setup tlast related IRQ */ isr_err = xsdfec_isr_enable(xsdfec, irq.enable_isr); if (!isr_err) xsdfec->config.irq.enable_isr = irq.enable_isr; /* Setup ECC related IRQ */ ecc_err = xsdfec_ecc_isr_enable(xsdfec, irq.enable_ecc_isr); if (!ecc_err) xsdfec->config.irq.enable_ecc_isr = irq.enable_ecc_isr; if (isr_err < 0 || ecc_err < 0) err = -EIO; return err;} static int xsdfec_set_turbo(struct xsdfec_dev *xsdfec, void __user *arg){ struct xsdfec_turbo turbo; int err; u32 turbo_write; err = copy_from_user(&turbo, arg, sizeof(turbo)); if (err) return -EFAULT; if (turbo.alg >= XSDFEC_TURBO_ALG_MAX) return -EINVAL; if (turbo.scale > XSDFEC_TURBO_SCALE_MAX) return -EINVAL; /* Check to see what device tree says about the FEC codes */ if (xsdfec->config.code == XSDFEC_LDPC_CODE) return -EIO; turbo_write = ((turbo.scale & XSDFEC_TURBO_SCALE_MASK) << XSDFEC_TURBO_SCALE_BIT_POS) | turbo.alg; xsdfec_regwrite(xsdfec, XSDFEC_TURBO_ADDR, turbo_write); return err;} static int xsdfec_get_turbo(struct xsdfec_dev *xsdfec, void __user *arg){ u32 reg_value; struct xsdfec_turbo turbo_params; int err; if (xsdfec->config.code == XSDFEC_LDPC_CODE) return -EIO; memset(&turbo_params, 0, sizeof(turbo_params)); reg_value = xsdfec_regread(xsdfec, XSDFEC_TURBO_ADDR); turbo_params.scale = (reg_value & XSDFEC_TURBO_SCALE_MASK) >> XSDFEC_TURBO_SCALE_BIT_POS; turbo_params.alg = reg_value & 0x1; err = copy_to_user(arg, &turbo_params, sizeof(turbo_params)); if (err) err = -EFAULT; return err;} static int xsdfec_ldpc_reg_addr(struct xsdfec_dev *xsdfec, u32 base, u32 high, u32 offset, u32 *addr){ if (high < base || offset > (high - base) / XSDFEC_LDPC_REG_JUMP) { dev_dbg(xsdfec->dev, "LDPC register offset %u outside space 0x%x-0x%x", offset, base, high); return -EINVAL; } *addr = base + offset * XSDFEC_LDPC_REG_JUMP; return 0;} static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, u32 offset){ u32 wdata; u32 addr; if (n < XSDFEC_REG0_N_MIN || n > XSDFEC_REG0_N_MAX || psize == 0 || (n > XSDFEC_REG0_N_MUL_P * psize) || n <= k || ((n % psize) != 0)) { dev_dbg(xsdfec->dev, "N value is not in range"); return -EINVAL; } n <<= XSDFEC_REG0_N_LSB; if (k < XSDFEC_REG0_K_MIN || k > XSDFEC_REG0_K_MAX || (k > XSDFEC_REG0_K_MUL_P * psize) || ((k % psize) != 0)) { dev_dbg(xsdfec->dev, "K value is not in range"); return -EINVAL; } k = k << XSDFEC_REG0_K_LSB; wdata = k | n; if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG0_ADDR_BASE, XSDFEC_LDPC_CODE_REG0_ADDR_HIGH, offset, &addr)) return -EINVAL; xsdfec_regwrite(xsdfec, addr, wdata); return 0;}Lines 1–500 of 1446. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Xilinx SDFEC * * Copyright (C) 2019 Xilinx, Inc. * * Description: * This driver is developed for SDFEC16 (Soft Decision FEC 16nm) * IP. It exposes a char device which supports file operations * like open(), close() and ioctl(). */ #include <linux/miscdevice.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/poll.h> #include <linux/slab.h> #include <linux/clk.h> #include <linux/compat.h> #include <linux/highmem.h> #include <uapi/misc/xilinx_sdfec.h> #define DEV_NAME_LEN 12 static DEFINE_IDA(dev_nrs); /* Xilinx SDFEC Register Map */ /* CODE_WRI_PROTECT Register */ #define XSDFEC_CODE_WR_PROTECT_ADDR (0x4) /* ACTIVE Register */ #define XSDFEC_ACTIVE_ADDR (0x8) #define XSDFEC_IS_ACTIVITY_SET (0x1) /* AXIS_WIDTH Register */ #define XSDFEC_AXIS_WIDTH_ADDR (0xC) #define XSDFEC_AXIS_DOUT_WORDS_LSB (5) #define XSDFEC_AXIS_DOUT_WIDTH_LSB (3) #define XSDFEC_AXIS_DIN_WORDS_LSB (2) #define XSDFEC_AXIS_DIN_WIDTH_LSB (0) /* AXIS_ENABLE Register */ #define XSDFEC_AXIS_ENABLE_ADDR (0x10) #define XSDFEC_AXIS_OUT_ENABLE_MASK (0x38) #define XSDFEC_AXIS_IN_ENABLE_MASK (0x7) #define XSDFEC_AXIS_ENABLE_MASK \ (XSDFEC_AXIS_OUT_ENABLE_MASK | XSDFEC_AXIS_IN_ENABLE_MASK) /* FEC_CODE Register */ #define XSDFEC_FEC_CODE_ADDR (0x14) /* ORDER Register Map */ #define XSDFEC_ORDER_ADDR (0x18) /* Interrupt Status Register */ #define XSDFEC_ISR_ADDR (0x1C) /* Interrupt Status Register Bit Mask */ #define XSDFEC_ISR_MASK (0x3F) /* Write Only - Interrupt Enable Register */ #define XSDFEC_IER_ADDR (0x20) /* Write Only - Interrupt Disable Register */ #define XSDFEC_IDR_ADDR (0x24) /* Read Only - Interrupt Mask Register */ #define XSDFEC_IMR_ADDR (0x28) /* ECC Interrupt Status Register */ #define XSDFEC_ECC_ISR_ADDR (0x2C) /* Single Bit Errors */ #define XSDFEC_ECC_ISR_SBE_MASK (0x7FF) /* PL Initialize Single Bit Errors */ #define XSDFEC_PL_INIT_ECC_ISR_SBE_MASK (0x3C00000) /* Multi Bit Errors */ #define XSDFEC_ECC_ISR_MBE_MASK (0x3FF800) /* PL Initialize Multi Bit Errors */ #define XSDFEC_PL_INIT_ECC_ISR_MBE_MASK (0x3C000000) /* Multi Bit Error to Event Shift */ #define XSDFEC_ECC_ISR_MBE_TO_EVENT_SHIFT (11) /* PL Initialize Multi Bit Error to Event Shift */ #define XSDFEC_PL_INIT_ECC_ISR_MBE_TO_EVENT_SHIFT (4) /* ECC Interrupt Status Bit Mask */ #define XSDFEC_ECC_ISR_MASK (XSDFEC_ECC_ISR_SBE_MASK | XSDFEC_ECC_ISR_MBE_MASK) /* ECC Interrupt Status PL Initialize Bit Mask */ #define XSDFEC_PL_INIT_ECC_ISR_MASK \ (XSDFEC_PL_INIT_ECC_ISR_SBE_MASK | XSDFEC_PL_INIT_ECC_ISR_MBE_MASK) /* ECC Interrupt Status All Bit Mask */ #define XSDFEC_ALL_ECC_ISR_MASK \ (XSDFEC_ECC_ISR_MASK | XSDFEC_PL_INIT_ECC_ISR_MASK) /* ECC Interrupt Status Single Bit Errors Mask */ #define XSDFEC_ALL_ECC_ISR_SBE_MASK \ (XSDFEC_ECC_ISR_SBE_MASK | XSDFEC_PL_INIT_ECC_ISR_SBE_MASK) /* ECC Interrupt Status Multi Bit Errors Mask */ #define XSDFEC_ALL_ECC_ISR_MBE_MASK \ (XSDFEC_ECC_ISR_MBE_MASK | XSDFEC_PL_INIT_ECC_ISR_MBE_MASK) /* Write Only - ECC Interrupt Enable Register */ #define XSDFEC_ECC_IER_ADDR (0x30) /* Write Only - ECC Interrupt Disable Register */ #define XSDFEC_ECC_IDR_ADDR (0x34) /* Read Only - ECC Interrupt Mask Register */ #define XSDFEC_ECC_IMR_ADDR (0x38) /* BYPASS Register */ #define XSDFEC_BYPASS_ADDR (0x3C) /* Turbo Code Register */ #define XSDFEC_TURBO_ADDR (0x100) #define XSDFEC_TURBO_SCALE_MASK (0xFFF) #define XSDFEC_TURBO_SCALE_BIT_POS (8) #define XSDFEC_TURBO_SCALE_MAX (15) /* REG0 Register */ #define XSDFEC_LDPC_CODE_REG0_ADDR_BASE (0x2000) #define XSDFEC_LDPC_CODE_REG0_ADDR_HIGH (0x27F0) #define XSDFEC_REG0_N_MIN (4) #define XSDFEC_REG0_N_MAX (32768) #define XSDFEC_REG0_N_MUL_P (256) #define XSDFEC_REG0_N_LSB (0) #define XSDFEC_REG0_K_MIN (2) #define XSDFEC_REG0_K_MAX (32766) #define XSDFEC_REG0_K_MUL_P (256) #define XSDFEC_REG0_K_LSB (16) /* REG1 Register */ #define XSDFEC_LDPC_CODE_REG1_ADDR_BASE (0x2004) #define XSDFEC_LDPC_CODE_REG1_ADDR_HIGH (0x27f4) #define XSDFEC_REG1_PSIZE_MIN (2) #define XSDFEC_REG1_PSIZE_MAX (512) #define XSDFEC_REG1_NO_PACKING_MASK (0x400) #define XSDFEC_REG1_NO_PACKING_LSB (10) #define XSDFEC_REG1_NM_MASK (0xFF800) #define XSDFEC_REG1_NM_LSB (11) #define XSDFEC_REG1_BYPASS_MASK (0x100000) /* REG2 Register */ #define XSDFEC_LDPC_CODE_REG2_ADDR_BASE (0x2008) #define XSDFEC_LDPC_CODE_REG2_ADDR_HIGH (0x27f8) #define XSDFEC_REG2_NLAYERS_MIN (1) #define XSDFEC_REG2_NLAYERS_MAX (256) #define XSDFEC_REG2_NNMQC_MASK (0xFFE00) #define XSDFEC_REG2_NMQC_LSB (9) #define XSDFEC_REG2_NORM_TYPE_MASK (0x100000) #define XSDFEC_REG2_NORM_TYPE_LSB (20) #define XSDFEC_REG2_SPECIAL_QC_MASK (0x200000) #define XSDFEC_REG2_SPEICAL_QC_LSB (21) #define XSDFEC_REG2_NO_FINAL_PARITY_MASK (0x400000) #define XSDFEC_REG2_NO_FINAL_PARITY_LSB (22) #define XSDFEC_REG2_MAX_SCHEDULE_MASK (0x1800000) #define XSDFEC_REG2_MAX_SCHEDULE_LSB (23) /* REG3 Register */ #define XSDFEC_LDPC_CODE_REG3_ADDR_BASE (0x200C) #define XSDFEC_LDPC_CODE_REG3_ADDR_HIGH (0x27FC) #define XSDFEC_REG3_LA_OFF_LSB (8) #define XSDFEC_REG3_QC_OFF_LSB (16) #define XSDFEC_LDPC_REG_JUMP (0x10) #define XSDFEC_REG_WIDTH_JUMP (4) /* The maximum number of pinned pages */ #define MAX_NUM_PAGES ((XSDFEC_QC_TABLE_DEPTH / PAGE_SIZE) + 1) /** * struct xsdfec_clks - For managing SD-FEC clocks * @core_clk: Main processing clock for core * @axi_clk: AXI4-Lite memory-mapped clock * @din_words_clk: DIN Words AXI4-Stream Slave clock * @din_clk: DIN AXI4-Stream Slave clock * @dout_clk: DOUT Words AXI4-Stream Slave clock * @dout_words_clk: DOUT AXI4-Stream Slave clock * @ctrl_clk: Control AXI4-Stream Slave clock * @status_clk: Status AXI4-Stream Slave clock */ struct xsdfec_clks { struct clk *core_clk; struct clk *axi_clk; struct clk *din_words_clk; struct clk *din_clk; struct clk *dout_clk; struct clk *dout_words_clk; struct clk *ctrl_clk; struct clk *status_clk; }; /** * struct xsdfec_dev - Driver data for SDFEC * @miscdev: Misc device handle * @clks: Clocks managed by the SDFEC driver * @waitq: Driver wait queue * @config: Configuration of the SDFEC device * @dev_name: Device name * @flags: spinlock flags * @regs: device physical base address * @dev: pointer to device struct * @state: State of the SDFEC device * @error_data_lock: Error counter and states spinlock * @dev_id: Device ID * @isr_err_count: Count of ISR errors * @cecc_count: Count of Correctable ECC errors (SBE) * @uecc_count: Count of Uncorrectable ECC errors (MBE) * @irq: IRQ number * @state_updated: indicates State updated by interrupt handler * @stats_updated: indicates Stats updated by interrupt handler * @intr_enabled: indicates IRQ enabled * * This structure contains necessary state for SDFEC driver to operate */ struct xsdfec_dev { struct miscdevice miscdev; struct xsdfec_clks clks; wait_queue_head_t waitq; struct xsdfec_config config; char dev_name[DEV_NAME_LEN]; unsigned long flags; void __iomem *regs; struct device *dev; enum xsdfec_state state; /* Spinlock to protect state_updated and stats_updated */ spinlock_t error_data_lock; int dev_id; u32 isr_err_count; u32 cecc_count; u32 uecc_count; int irq; bool state_updated; bool stats_updated; bool intr_enabled; }; static inline void xsdfec_regwrite(struct xsdfec_dev *xsdfec, u32 addr, u32 value) { dev_dbg(xsdfec->dev, "Writing 0x%x to offset 0x%x", value, addr); iowrite32(value, xsdfec->regs + addr); } static inline u32 xsdfec_regread(struct xsdfec_dev *xsdfec, u32 addr) { u32 rval; rval = ioread32(xsdfec->regs + addr); dev_dbg(xsdfec->dev, "Read value = 0x%x from offset 0x%x", rval, addr); return rval; } static void update_bool_config_from_reg(struct xsdfec_dev *xsdfec, u32 reg_offset, u32 bit_num, char *config_value) { u32 reg_val; u32 bit_mask = 1 << bit_num; reg_val = xsdfec_regread(xsdfec, reg_offset); *config_value = (reg_val & bit_mask) > 0; } static void update_config_from_hw(struct xsdfec_dev *xsdfec) { u32 reg_value; bool sdfec_started; /* Update the Order */ reg_value = xsdfec_regread(xsdfec, XSDFEC_ORDER_ADDR); xsdfec->config.order = reg_value; update_bool_config_from_reg(xsdfec, XSDFEC_BYPASS_ADDR, 0, /* Bit Number, maybe change to mask */ &xsdfec->config.bypass); update_bool_config_from_reg(xsdfec, XSDFEC_CODE_WR_PROTECT_ADDR, 0, /* Bit Number */ &xsdfec->config.code_wr_protect); reg_value = xsdfec_regread(xsdfec, XSDFEC_IMR_ADDR); xsdfec->config.irq.enable_isr = (reg_value & XSDFEC_ISR_MASK) > 0; reg_value = xsdfec_regread(xsdfec, XSDFEC_ECC_IMR_ADDR); xsdfec->config.irq.enable_ecc_isr = (reg_value & XSDFEC_ECC_ISR_MASK) > 0; reg_value = xsdfec_regread(xsdfec, XSDFEC_AXIS_ENABLE_ADDR); sdfec_started = (reg_value & XSDFEC_AXIS_IN_ENABLE_MASK) > 0; if (sdfec_started) xsdfec->state = XSDFEC_STARTED; else xsdfec->state = XSDFEC_STOPPED; } static int xsdfec_get_status(struct xsdfec_dev *xsdfec, void __user *arg) { struct xsdfec_status status; int err; memset(&status, 0, sizeof(status)); spin_lock_irqsave(&xsdfec->error_data_lock, xsdfec->flags); status.state = xsdfec->state; xsdfec->state_updated = false; spin_unlock_irqrestore(&xsdfec->error_data_lock, xsdfec->flags); status.activity = (xsdfec_regread(xsdfec, XSDFEC_ACTIVE_ADDR) & XSDFEC_IS_ACTIVITY_SET); err = copy_to_user(arg, &status, sizeof(status)); if (err) err = -EFAULT; return err; } static int xsdfec_get_config(struct xsdfec_dev *xsdfec, void __user *arg) { int err; err = copy_to_user(arg, &xsdfec->config, sizeof(xsdfec->config)); if (err) err = -EFAULT; return err; } static int xsdfec_isr_enable(struct xsdfec_dev *xsdfec, bool enable) { u32 mask_read; if (enable) { /* Enable */ xsdfec_regwrite(xsdfec, XSDFEC_IER_ADDR, XSDFEC_ISR_MASK); mask_read = xsdfec_regread(xsdfec, XSDFEC_IMR_ADDR); if (mask_read & XSDFEC_ISR_MASK) { dev_dbg(xsdfec->dev, "SDFEC enabling irq with IER failed"); return -EIO; } } else { /* Disable */ xsdfec_regwrite(xsdfec, XSDFEC_IDR_ADDR, XSDFEC_ISR_MASK); mask_read = xsdfec_regread(xsdfec, XSDFEC_IMR_ADDR); if ((mask_read & XSDFEC_ISR_MASK) != XSDFEC_ISR_MASK) { dev_dbg(xsdfec->dev, "SDFEC disabling irq with IDR failed"); return -EIO; } } return 0; } static int xsdfec_ecc_isr_enable(struct xsdfec_dev *xsdfec, bool enable) { u32 mask_read; if (enable) { /* Enable */ xsdfec_regwrite(xsdfec, XSDFEC_ECC_IER_ADDR, XSDFEC_ALL_ECC_ISR_MASK); mask_read = xsdfec_regread(xsdfec, XSDFEC_ECC_IMR_ADDR); if (mask_read & XSDFEC_ALL_ECC_ISR_MASK) { dev_dbg(xsdfec->dev, "SDFEC enabling ECC irq with ECC IER failed"); return -EIO; } } else { /* Disable */ xsdfec_regwrite(xsdfec, XSDFEC_ECC_IDR_ADDR, XSDFEC_ALL_ECC_ISR_MASK); mask_read = xsdfec_regread(xsdfec, XSDFEC_ECC_IMR_ADDR); if (!(((mask_read & XSDFEC_ALL_ECC_ISR_MASK) == XSDFEC_ECC_ISR_MASK) || ((mask_read & XSDFEC_ALL_ECC_ISR_MASK) == XSDFEC_PL_INIT_ECC_ISR_MASK))) { dev_dbg(xsdfec->dev, "SDFEC disable ECC irq with ECC IDR failed"); return -EIO; } } return 0; } static int xsdfec_set_irq(struct xsdfec_dev *xsdfec, void __user *arg) { struct xsdfec_irq irq; int err; int isr_err; int ecc_err; err = copy_from_user(&irq, arg, sizeof(irq)); if (err) return -EFAULT; /* Setup tlast related IRQ */ isr_err = xsdfec_isr_enable(xsdfec, irq.enable_isr); if (!isr_err) xsdfec->config.irq.enable_isr = irq.enable_isr; /* Setup ECC related IRQ */ ecc_err = xsdfec_ecc_isr_enable(xsdfec, irq.enable_ecc_isr); if (!ecc_err) xsdfec->config.irq.enable_ecc_isr = irq.enable_ecc_isr; if (isr_err < 0 || ecc_err < 0) err = -EIO; return err; } static int xsdfec_set_turbo(struct xsdfec_dev *xsdfec, void __user *arg) { struct xsdfec_turbo turbo; int err; u32 turbo_write; err = copy_from_user(&turbo, arg, sizeof(turbo)); if (err) return -EFAULT; if (turbo.alg >= XSDFEC_TURBO_ALG_MAX) return -EINVAL; if (turbo.scale > XSDFEC_TURBO_SCALE_MAX) return -EINVAL; /* Check to see what device tree says about the FEC codes */ if (xsdfec->config.code == XSDFEC_LDPC_CODE) return -EIO; turbo_write = ((turbo.scale & XSDFEC_TURBO_SCALE_MASK) << XSDFEC_TURBO_SCALE_BIT_POS) | turbo.alg; xsdfec_regwrite(xsdfec, XSDFEC_TURBO_ADDR, turbo_write); return err; } static int xsdfec_get_turbo(struct xsdfec_dev *xsdfec, void __user *arg) { u32 reg_value; struct xsdfec_turbo turbo_params; int err; if (xsdfec->config.code == XSDFEC_LDPC_CODE) return -EIO; memset(&turbo_params, 0, sizeof(turbo_params)); reg_value = xsdfec_regread(xsdfec, XSDFEC_TURBO_ADDR); turbo_params.scale = (reg_value & XSDFEC_TURBO_SCALE_MASK) >> XSDFEC_TURBO_SCALE_BIT_POS; turbo_params.alg = reg_value & 0x1; err = copy_to_user(arg, &turbo_params, sizeof(turbo_params)); if (err) err = -EFAULT; return err; } static int xsdfec_ldpc_reg_addr(struct xsdfec_dev *xsdfec, u32 base, u32 high, u32 offset, u32 *addr) { if (high < base || offset > (high - base) / XSDFEC_LDPC_REG_JUMP) { dev_dbg(xsdfec->dev, "LDPC register offset %u outside space 0x%x-0x%x", offset, base, high); return -EINVAL; } *addr = base + offset * XSDFEC_LDPC_REG_JUMP; return 0; } static int xsdfec_reg0_write(struct xsdfec_dev *xsdfec, u32 n, u32 k, u32 psize, u32 offset) { u32 wdata; u32 addr; if (n < XSDFEC_REG0_N_MIN || n > XSDFEC_REG0_N_MAX || psize == 0 || (n > XSDFEC_REG0_N_MUL_P * psize) || n <= k || ((n % psize) != 0)) { dev_dbg(xsdfec->dev, "N value is not in range"); return -EINVAL; } n <<= XSDFEC_REG0_N_LSB; if (k < XSDFEC_REG0_K_MIN || k > XSDFEC_REG0_K_MAX || (k > XSDFEC_REG0_K_MUL_P * psize) || ((k % psize) != 0)) { dev_dbg(xsdfec->dev, "K value is not in range"); return -EINVAL; } k = k << XSDFEC_REG0_K_LSB; wdata = k | n; if (xsdfec_ldpc_reg_addr(xsdfec, XSDFEC_LDPC_CODE_REG0_ADDR_BASE, XSDFEC_LDPC_CODE_REG0_ADDR_HIGH, offset, &addr)) return -EINVAL; xsdfec_regwrite(xsdfec, addr, wdata); return 0; }