Commit 6bd6a690 authored by Dragan Cvetic's avatar Dragan Cvetic Committed by Greg Kroah-Hartman
Browse files

misc: xilinx_sdfec: Add stats & status ioctls



SD-FEC statistic data are:
- count of data interface errors (isr_err_count)
- count of Correctable ECC errors (cecc_count)
- count of Uncorrectable ECC errors (uecc_count)

Add support:
1. clear stats ioctl callback which clears collected
statistic data,
2. get stats ioctl callback which reads a collected
statistic data,
3. set default configuration ioctl callback,
4. start ioctl callback enables SD-FEC HW,
5. stop ioctl callback disables SD-FEC HW.

In a failed state driver enables the following ioctls:
- get status
- get statistics
- clear stats
- set default SD-FEC device configuration

Tested-by: default avatarSanthosh Dyavanapally <SDYAVANA@xilinx.com>
Tested by: Punnaiah Choudary Kalluri <punnaia@xilinx.com>
Tested-by: default avatarDerek Kiernan <derek.kiernan@xilinx.com>
Tested-by: default avatarDragan Cvetic <dragan.cvetic@xilinx.com>
Signed-off-by: default avatarDerek Kiernan <derek.kiernan@xilinx.com>
Signed-off-by: default avatarDragan Cvetic <dragan.cvetic@xilinx.com>
Link: https://lore.kernel.org/r/1564216438-322406-7-git-send-email-dragan.cvetic@xilinx.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent cc538f60
Loading
Loading
Loading
Loading
+125 −0
Original line number Diff line number Diff line
@@ -206,6 +206,7 @@ struct xsdfec_clks {
 * @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
 */
@@ -228,6 +229,7 @@ struct xsdfec_dev {
	int irq;
	bool state_updated;
	bool stats_updated;
	bool intr_enabled;
};

static inline void xsdfec_regwrite(struct xsdfec_dev *xsdfec, u32 addr,
@@ -289,6 +291,25 @@ static void update_config_from_hw(struct xsdfec_dev *xsdfec)
		xsdfec->state = XSDFEC_STOPPED;
}

static int xsdfec_get_status(struct xsdfec_dev *xsdfec, void __user *arg)
{
	struct xsdfec_status status;
	int err;

	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;
@@ -840,6 +861,82 @@ static int xsdfec_dev_release(struct inode *iptr, struct file *fptr)
	return 0;
}

static int xsdfec_start(struct xsdfec_dev *xsdfec)
{
	u32 regread;

	regread = xsdfec_regread(xsdfec, XSDFEC_FEC_CODE_ADDR);
	regread &= 0x1;
	if (regread != xsdfec->config.code) {
		dev_dbg(xsdfec->dev,
			"%s SDFEC HW code does not match driver code, reg %d, code %d",
			__func__, regread, xsdfec->config.code);
		return -EINVAL;
	}

	/* Set AXIS enable */
	xsdfec_regwrite(xsdfec, XSDFEC_AXIS_ENABLE_ADDR,
			XSDFEC_AXIS_ENABLE_MASK);
	/* Done */
	xsdfec->state = XSDFEC_STARTED;
	return 0;
}

static int xsdfec_stop(struct xsdfec_dev *xsdfec)
{
	u32 regread;

	if (xsdfec->state != XSDFEC_STARTED)
		dev_dbg(xsdfec->dev, "Device not started correctly");
	/* Disable AXIS_ENABLE Input interfaces only */
	regread = xsdfec_regread(xsdfec, XSDFEC_AXIS_ENABLE_ADDR);
	regread &= (~XSDFEC_AXIS_IN_ENABLE_MASK);
	xsdfec_regwrite(xsdfec, XSDFEC_AXIS_ENABLE_ADDR, regread);
	/* Stop */
	xsdfec->state = XSDFEC_STOPPED;
	return 0;
}

static int xsdfec_clear_stats(struct xsdfec_dev *xsdfec)
{
	spin_lock_irqsave(&xsdfec->error_data_lock, xsdfec->flags);
	xsdfec->isr_err_count = 0;
	xsdfec->uecc_count = 0;
	xsdfec->cecc_count = 0;
	spin_unlock_irqrestore(&xsdfec->error_data_lock, xsdfec->flags);

	return 0;
}

static int xsdfec_get_stats(struct xsdfec_dev *xsdfec, void __user *arg)
{
	int err;
	struct xsdfec_stats user_stats;

	spin_lock_irqsave(&xsdfec->error_data_lock, xsdfec->flags);
	user_stats.isr_err_count = xsdfec->isr_err_count;
	user_stats.cecc_count = xsdfec->cecc_count;
	user_stats.uecc_count = xsdfec->uecc_count;
	xsdfec->stats_updated = false;
	spin_unlock_irqrestore(&xsdfec->error_data_lock, xsdfec->flags);

	err = copy_to_user(arg, &user_stats, sizeof(user_stats));
	if (err)
		err = -EFAULT;

	return err;
}

static int xsdfec_set_default_config(struct xsdfec_dev *xsdfec)
{
	/* Ensure registers are aligned with core configuration */
	xsdfec_regwrite(xsdfec, XSDFEC_FEC_CODE_ADDR, xsdfec->config.code);
	xsdfec_cfg_axi_streams(xsdfec);
	update_config_from_hw(xsdfec);

	return 0;
}

static long xsdfec_dev_ioctl(struct file *fptr, unsigned int cmd,
			     unsigned long data)
{
@@ -849,6 +946,16 @@ static long xsdfec_dev_ioctl(struct file *fptr, unsigned int cmd,

	xsdfec = container_of(fptr->private_data, struct xsdfec_dev, miscdev);

	/* In failed state allow only reset and get status IOCTLs */
	if (xsdfec->state == XSDFEC_NEEDS_RESET &&
	    (cmd != XSDFEC_SET_DEFAULT_CONFIG && cmd != XSDFEC_GET_STATUS &&
	     cmd != XSDFEC_GET_STATS && cmd != XSDFEC_CLEAR_STATS)) {
		return -EPERM;
	}

	if (_IOC_TYPE(cmd) != XSDFEC_MAGIC)
		return -ENOTTY;

	/* check if ioctl argument is present and valid */
	if (_IOC_DIR(cmd) != _IOC_NONE) {
		arg = (void __user *)data;
@@ -857,9 +964,27 @@ static long xsdfec_dev_ioctl(struct file *fptr, unsigned int cmd,
	}

	switch (cmd) {
	case XSDFEC_START_DEV:
		rval = xsdfec_start(xsdfec);
		break;
	case XSDFEC_STOP_DEV:
		rval = xsdfec_stop(xsdfec);
		break;
	case XSDFEC_CLEAR_STATS:
		rval = xsdfec_clear_stats(xsdfec);
		break;
	case XSDFEC_GET_STATS:
		rval = xsdfec_get_stats(xsdfec, arg);
		break;
	case XSDFEC_GET_STATUS:
		rval = xsdfec_get_status(xsdfec, arg);
		break;
	case XSDFEC_GET_CONFIG:
		rval = xsdfec_get_config(xsdfec, arg);
		break;
	case XSDFEC_SET_DEFAULT_CONFIG:
		rval = xsdfec_set_default_config(xsdfec);
		break;
	case XSDFEC_SET_IRQ:
		rval = xsdfec_set_irq(xsdfec, arg);
		break;
+75 −0
Original line number Diff line number Diff line
@@ -233,6 +233,21 @@ struct xsdfec_config {
	__s8 code_wr_protect;
};

/**
 * struct xsdfec_stats - Stats retrived by ioctl XSDFEC_GET_STATS. Used
 *			 to buffer atomic_t variables from struct
 *			 xsdfec_dev. Counts are accumulated until
 *			 the user clears them.
 * @isr_err_count: Count of ISR errors
 * @cecc_count: Count of Correctable ECC errors (SBE)
 * @uecc_count: Count of Uncorrectable ECC errors (MBE)
 */
struct xsdfec_stats {
	__u32 isr_err_count;
	__u32 cecc_count;
	__u32 uecc_count;
};

/**
 * struct xsdfec_ldpc_param_table_sizes - Used to store sizes of SD-FEC table
 *					  entries for an individual LPDC code
@@ -251,6 +266,32 @@ struct xsdfec_ldpc_param_table_sizes {
 * XSDFEC IOCTL List
 */
#define XSDFEC_MAGIC 'f'
/**
 * DOC: XSDFEC_START_DEV
 *
 * @Description
 *
 * ioctl to start SD-FEC core
 *
 * This fails if the XSDFEC_SET_ORDER ioctl has not been previously called
 */
#define XSDFEC_START_DEV _IO(XSDFEC_MAGIC, 0)
/**
 * DOC: XSDFEC_STOP_DEV
 *
 * @Description
 *
 * ioctl to stop the SD-FEC core
 */
#define XSDFEC_STOP_DEV _IO(XSDFEC_MAGIC, 1)
/**
 * DOC: XSDFEC_GET_STATUS
 *
 * @Description
 *
 * ioctl that returns status of SD-FEC core
 */
#define XSDFEC_GET_STATUS _IOR(XSDFEC_MAGIC, 2, struct xsdfec_status)
/**
 * DOC: XSDFEC_SET_IRQ
 * @Parameters
@@ -370,4 +411,38 @@ struct xsdfec_ldpc_param_table_sizes {
 * ioctl that determines if SD-FEC is processing data
 */
#define XSDFEC_IS_ACTIVE _IOR(XSDFEC_MAGIC, 10, bool)
/**
 * DOC: XSDFEC_CLEAR_STATS
 *
 * @Description
 *
 * ioctl that clears error stats collected during interrupts
 */
#define XSDFEC_CLEAR_STATS _IO(XSDFEC_MAGIC, 11)
/**
 * DOC: XSDFEC_GET_STATS
 * @Parameters
 *
 * @struct xsdfec_stats *
 *	Pointer to the &struct xsdfec_stats that will contain the updated stats
 *	values
 *
 * @Description
 *
 * ioctl that returns SD-FEC core stats
 *
 * This can only be used when the driver is in the XSDFEC_STOPPED state
 */
#define XSDFEC_GET_STATS _IOR(XSDFEC_MAGIC, 12, struct xsdfec_stats)
/**
 * DOC: XSDFEC_SET_DEFAULT_CONFIG
 *
 * @Description
 *
 * ioctl that returns SD-FEC core to default config, use after a reset
 *
 * This can only be used when the driver is in the XSDFEC_STOPPED state
 */
#define XSDFEC_SET_DEFAULT_CONFIG _IO(XSDFEC_MAGIC, 13)

#endif /* __XILINX_SDFEC_H__ */