1446 lines · 37.3 KB · C
RawDownload
// 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.