mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-01 10:42:11 +00:00
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: Santhosh Dyavanapally <SDYAVANA@xilinx.com> Tested by: Punnaiah Choudary Kalluri <punnaia@xilinx.com> Tested-by: Derek Kiernan <derek.kiernan@xilinx.com> Tested-by: Dragan Cvetic <dragan.cvetic@xilinx.com> Signed-off-by: Derek Kiernan <derek.kiernan@xilinx.com> Signed-off-by: Dragan Cvetic <dragan.cvetic@xilinx.com> Link: https://lore.kernel.org/r/1564216438-322406-7-git-send-email-dragan.cvetic@xilinx.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
cc538f609d
commit
6bd6a690c2
@ -206,6 +206,7 @@ struct xsdfec_clks {
|
|||||||
* @irq: IRQ number
|
* @irq: IRQ number
|
||||||
* @state_updated: indicates State updated by interrupt handler
|
* @state_updated: indicates State updated by interrupt handler
|
||||||
* @stats_updated: indicates Stats 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
|
* This structure contains necessary state for SDFEC driver to operate
|
||||||
*/
|
*/
|
||||||
@ -228,6 +229,7 @@ struct xsdfec_dev {
|
|||||||
int irq;
|
int irq;
|
||||||
bool state_updated;
|
bool state_updated;
|
||||||
bool stats_updated;
|
bool stats_updated;
|
||||||
|
bool intr_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void xsdfec_regwrite(struct xsdfec_dev *xsdfec, u32 addr,
|
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;
|
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)
|
static int xsdfec_get_config(struct xsdfec_dev *xsdfec, void __user *arg)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -840,6 +861,82 @@ static int xsdfec_dev_release(struct inode *iptr, struct file *fptr)
|
|||||||
return 0;
|
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,
|
static long xsdfec_dev_ioctl(struct file *fptr, unsigned int cmd,
|
||||||
unsigned long data)
|
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);
|
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 */
|
/* check if ioctl argument is present and valid */
|
||||||
if (_IOC_DIR(cmd) != _IOC_NONE) {
|
if (_IOC_DIR(cmd) != _IOC_NONE) {
|
||||||
arg = (void __user *)data;
|
arg = (void __user *)data;
|
||||||
@ -857,9 +964,27 @@ static long xsdfec_dev_ioctl(struct file *fptr, unsigned int cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (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:
|
case XSDFEC_GET_CONFIG:
|
||||||
rval = xsdfec_get_config(xsdfec, arg);
|
rval = xsdfec_get_config(xsdfec, arg);
|
||||||
break;
|
break;
|
||||||
|
case XSDFEC_SET_DEFAULT_CONFIG:
|
||||||
|
rval = xsdfec_set_default_config(xsdfec);
|
||||||
|
break;
|
||||||
case XSDFEC_SET_IRQ:
|
case XSDFEC_SET_IRQ:
|
||||||
rval = xsdfec_set_irq(xsdfec, arg);
|
rval = xsdfec_set_irq(xsdfec, arg);
|
||||||
break;
|
break;
|
||||||
|
@ -233,6 +233,21 @@ struct xsdfec_config {
|
|||||||
__s8 code_wr_protect;
|
__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
|
* struct xsdfec_ldpc_param_table_sizes - Used to store sizes of SD-FEC table
|
||||||
* entries for an individual LPDC code
|
* entries for an individual LPDC code
|
||||||
@ -251,6 +266,32 @@ struct xsdfec_ldpc_param_table_sizes {
|
|||||||
* XSDFEC IOCTL List
|
* XSDFEC IOCTL List
|
||||||
*/
|
*/
|
||||||
#define XSDFEC_MAGIC 'f'
|
#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
|
* DOC: XSDFEC_SET_IRQ
|
||||||
* @Parameters
|
* @Parameters
|
||||||
@ -370,4 +411,38 @@ struct xsdfec_ldpc_param_table_sizes {
|
|||||||
* ioctl that determines if SD-FEC is processing data
|
* ioctl that determines if SD-FEC is processing data
|
||||||
*/
|
*/
|
||||||
#define XSDFEC_IS_ACTIVE _IOR(XSDFEC_MAGIC, 10, bool)
|
#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__ */
|
#endif /* __XILINX_SDFEC_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user