Commit 013a37f9 authored by Federico Vaga's avatar Federico Vaga

*: set timeout coalescing dynamically

The driver exports a sysfs attrubte to change the coalescing timeout. A new
library funciton can be used to set/get this value; and the test program allows
the configuration before an acquisition.

Keep in mind that, for FIFOs the configuration is global (users are warned on
dmesg) while for DMA it is per-channel. I though it was better to have a common
interface for both. Users are warnd in the documentation as well. In the long
run, perhaps also the FIFO can have its own register.
Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent 8f34868a
......@@ -47,6 +47,7 @@ enum ft_zattr_in_idx {
FT_ATTR_TDC_DELAY_REF,
FT_ATTR_TDC_WR_OFFSET,
FT_ATTR_TDC_TRANSFER_MODE,
FT_ATTR_TDC_COALESCING_TIME,
FT_ATTR_TDC__LAST,
};
......@@ -283,6 +284,15 @@ signed long fmc_sdb_find_nth_device (struct sdb_array *tree, uint64_t vid,
uint32_t *size );
void gn4124_dma_read(struct fmctdc_dev *ft, uint32_t src, void *dst, int len);
void ft_irq_coalescing_size_set(struct fmctdc_dev *ft,
unsigned int chan,
uint32_t size);
void ft_irq_coalescing_timeout_set(struct fmctdc_dev *ft,
unsigned int chan,
uint32_t timeout_ms);
uint32_t ft_irq_coalescing_timeout_get(struct fmctdc_dev *ft,
unsigned int chan);
/**
......
......@@ -29,19 +29,12 @@
#include "fmc-tdc.h"
#include "hw/tdc_regs.h"
/* Module parameters */
static int dma_buf_irq_timeout_ms_default = 10;
module_param_named(dma_buf_irq_timeout_ms, dma_buf_irq_timeout_ms_default,
int, 0444);
MODULE_PARM_DESC(dma_buf_irq_timeout_ms, "IRQ coalesing timeout (default: 10ms).");
static int dma_buf_ddr_burst_size_default = 16;
module_param_named(dma_buf_ddr_burst_size, dma_buf_ddr_burst_size_default,
int, 0444);
MODULE_PARM_DESC(dma_buf_ddr_burst_size,
"DDR size coalesing timeout (default: 16 timestamps).");
static int ft_verbose;
module_param_named(verbose, ft_verbose, int, 0444);
MODULE_PARM_DESC(verbose, "Print a lot of debugging messages.");
......@@ -139,19 +132,6 @@ static void ft_buffer_burst_disable(struct fmctdc_dev *ft,
}
static void ft_buffer_burst_timeout_set(struct fmctdc_dev *ft,
unsigned int chan,
uint32_t timeout)
{
const uint32_t base = ft->ft_dma_base + (0x40 * chan);
uint32_t tmp;
tmp = ft_ioread(ft, base + TDC_BUF_REG_CSR);
tmp &= ~TDC_BUF_CSR_IRQ_TIMEOUT_MASK;
tmp |= TDC_BUF_CSR_IRQ_TIMEOUT_W(timeout);
ft_iowrite(ft, tmp, base + TDC_BUF_REG_CSR);
}
/**
* It configure the double buffers for a given channel
......@@ -172,7 +152,7 @@ static void ft_buffer_init(struct fmctdc_dev *ft, int channel)
st->buf_size = TDC_CHANNEL_BUFFER_SIZE_BYTES / sizeof(struct ft_hw_timestamp);
st->active_buffer = 0;
ft_iowrite(ft, 0, base + TDC_BUF_REG_CSR);
ft_buffer_burst_disable(ft, channel);
/* Buffer 1 */
st->buf_addr[0] = TDC_CHANNEL_BUFFER_SIZE_BYTES * (2 * channel);
......@@ -188,8 +168,6 @@ static void ft_buffer_init(struct fmctdc_dev *ft, int channel)
val |= TDC_BUF_NEXT_SIZE_VALID;
ft_iowrite(ft, val, base + TDC_BUF_REG_NEXT_SIZE);
ft_buffer_burst_timeout_set(ft, channel,
dma_buf_irq_timeout_ms_default);
ft_buffer_burst_enable(ft, channel);
dev_info(&ft->fmc->dev,
......
......@@ -19,6 +19,7 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/kfifo.h>
#include <linux/moduleparam.h>
#include <linux/zio.h>
#include <linux/zio-trigger.h>
......@@ -26,6 +27,12 @@
#include "fmc-tdc.h"
/* Module parameters */
static int irq_timeout_ms_default = 10;
module_param_named(irq_timeout_ms, irq_timeout_ms_default, int, 0444);
MODULE_PARM_DESC(irq_timeout_ms, "IRQ coalesing timeout (default: 10ms).");
#define TDC_EIC_EIC_IMR_TDC_DMA_MASK (TDC_EIC_EIC_ISR_TDC_DMA1 | \
TDC_EIC_EIC_ISR_TDC_DMA2 | \
TDC_EIC_EIC_ISR_TDC_DMA3 | \
......@@ -451,16 +458,142 @@ static irqreturn_t ft_irq_handler_ts(int irq, void *dev_id)
return IRQ_HANDLED;
}
/**
* It sets the coalescing timeout for the DMA buffers
* @ft FmcTdc instance
* @chan channel buffer -1 for all channels, otherwise [0, 4]
* @timeout timeout in milliseconds
*/
static void ft_dma_irq_coalescing_timeout_set(struct fmctdc_dev *ft,
unsigned int chan,
uint32_t timeout)
{
uint32_t base, tmp;
int i;
for (i = (chan == -1 ? 0 : chan);
i < (chan == -1 ? ft->zdev->n_cset : chan + 1);
++i) {
pr_info("%s:%d %d %d\n", __func__, __LINE__, i, timeout);
base = ft->ft_dma_base + (0x40 * i);
tmp = ft_ioread(ft, base + TDC_BUF_REG_CSR);
tmp &= ~TDC_BUF_CSR_IRQ_TIMEOUT_MASK;
tmp |= TDC_BUF_CSR_IRQ_TIMEOUT_W(timeout);
ft_iowrite(ft, tmp, base + TDC_BUF_REG_CSR);
}
}
/**
* It gets the coalescing timeout for the DMA buffers
* @ft FmcTdc instance
* @chan channel buffer [0, 4]
*
* Return: timeout in milliseconds
*/
static uint32_t ft_dma_irq_coalescing_timeout_get(struct fmctdc_dev *ft,
unsigned int chan)
{
const uint32_t base = ft->ft_dma_base + (0x40 * chan);
uint32_t tmp;
tmp = ft_ioread(ft, base + TDC_BUF_REG_CSR);
return TDC_BUF_CSR_IRQ_TIMEOUT_R(tmp);
}
/**
* It sets the coalescing timeout according to the acquisition mode
* @ft FmcTdc instance
* @chan channe [0, 4] (used only for DMA acquisition mode)
* @timeout_ms timeout in milliseconds to trigger IRQ
*/
void ft_irq_coalescing_timeout_set(struct fmctdc_dev *ft,
unsigned int chan,
uint32_t timeout_ms)
{
switch (ft->mode) {
case FT_ACQ_TYPE_FIFO:
if (unlikely(chan != -1)) {
dev_warn(&ft->fmc->dev,
"%s: FIFO acquisition mode has a gobal coalesing timeout. Ignore channel %d, set global value\n",
__func__, chan);
}
ft_writel(ft, timeout_ms, TDC_REG_IRQ_TIMEOUT);
break;
case FT_ACQ_TYPE_DMA:
ft_dma_irq_coalescing_timeout_set(ft, chan, timeout_ms);
break;
}
}
/**
* It sets the coalescing size according to the acquisition mode
* @ft FmcTdc instance
* @chan channe [0, 4] (used only for DMA acquisition mode)
*
* Return: timeout in milliseconds
*/
uint32_t ft_irq_coalescing_timeout_get(struct fmctdc_dev *ft,
unsigned int chan)
{
uint32_t timeout = 0;
switch (ft->mode) {
case FT_ACQ_TYPE_FIFO:
if (unlikely(chan != -1)) {
dev_warn(&ft->fmc->dev,
"%s: FIFO acquisition mode has a gobal coalesing timeout. Ignore channel %d, get global value\n",
__func__, chan);
}
timeout = ft_readl(ft, TDC_REG_IRQ_THRESHOLD);
break;
case FT_ACQ_TYPE_DMA:
/* There is none */
timeout = ft_dma_irq_coalescing_timeout_get(ft, chan);
break;
default:
dev_err(&ft->fmc->dev, "%s: unknown acquisition mode %d\n",
__func__, ft->mode);
}
return timeout;
}
/**
* It sets the coalescing size according to the acquisition mode
* @ft FmcTdc instance
* @chan channe [0, 4] (used only for DMA acquisition mode)
* @size number of samples to trigger IRQ
*/
void ft_irq_coalescing_size_set(struct fmctdc_dev *ft,
unsigned int chan,
uint32_t size)
{
switch (ft->mode) {
case FT_ACQ_TYPE_FIFO:
if (unlikely(chan != -1)) {
dev_warn(&ft->fmc->dev,
"FIFO acquisition mode has a gobal coalesing size. Ignore channel %d, apply globally\n",
chan);
}
ft_writel(ft, size, TDC_REG_IRQ_THRESHOLD);
break;
case FT_ACQ_TYPE_DMA:
/* There is none */
break;
}
}
int ft_irq_init(struct fmctdc_dev *ft)
{
int ret;
ft_irq_coalescing_timeout_set(ft, -1, irq_timeout_ms_default);
ft_irq_coalescing_size_set(ft, -1, 40);
switch (ft->mode) {
case FT_ACQ_TYPE_FIFO:
ft_writel(ft, 40, TDC_REG_IRQ_THRESHOLD);
ft_writel(ft, 40, TDC_REG_IRQ_TIMEOUT);
INIT_WORK(&ft->ts_work, ft_fifo_work);
break;
case FT_ACQ_TYPE_DMA:
......
......@@ -48,6 +48,8 @@ static struct zio_attribute ft_zattr_input[] = {
ZIO_ATTR_EXT("user-offset", ZIO_RW_PERM, FT_ATTR_TDC_USER_OFFSET, 0),
ZIO_ATTR_EXT("diff-reference", ZIO_RW_PERM, FT_ATTR_TDC_DELAY_REF, 0),
ZIO_ATTR_EXT("transfer-mode", ZIO_RO_PERM, FT_ATTR_TDC_TRANSFER_MODE, 0),
ZIO_ATTR_EXT("irq_coalescing_time", ZIO_RW_PERM,
FT_ATTR_TDC_COALESCING_TIME, 0),
};
/* This identifies if our "struct device" is device, input, output */
......@@ -93,6 +95,9 @@ static int ft_zio_info_channel(struct device *dev, struct zio_attribute *zattr,
case FT_ATTR_TDC_TRANSFER_MODE:
*usr_val = ft->mode;
break;
case FT_ATTR_TDC_COALESCING_TIME:
*usr_val = ft_irq_coalescing_timeout_get(ft, cset->index);
break;
}
return 0;
......@@ -172,6 +177,9 @@ static int ft_zio_conf_channel(struct device *dev, struct zio_attribute *zattr,
return -EINVAL;
/* FIXME write on HW */
break;
case FT_ATTR_TDC_COALESCING_TIME:
ft_irq_coalescing_timeout_set(ft, cset->index, usr_val);
break;
default:
return -EINVAL;
}
......
......@@ -1019,3 +1019,66 @@ int fmctdc_buffer_mode(struct fmctdc_board *userb,
*mode = val;
return 0;
}
/**
* It sets the coalescing timeout on a given channel
* @param[in] userb TDC board instance token
* @param[in] channel target channel [0, 4]
* @param[in] timeout_ms ms timeout to trigger IRQ
* @return 0 on success, otherwise -1 and errno is set appropriately
*
* It does not work per-channel for the following acuqisition mechanism:
* - FIFO
*/
int fmctdc_coalescing_timeout_set(struct fmctdc_board *userb,
unsigned int channel,
unsigned int timeout_ms)
{
struct __fmctdc_board *b = (void *)(userb);
char path[64];
uint32_t val = timeout_ms;
if (channel >= FMCTDC_NUM_CHANNELS) {
errno = EINVAL;
return -1;
}
snprintf(path, sizeof(path), "ft-ch%d/irq_coalescing_time",
channel + 1);
return fmctdc_sysfs_set(b, path, &val);
}
/**
* It gets the coalescing timeout from a given channel
* @param[in] userb TDC board instance token
* @param[in] channel target channel [0, 4]
* @param[out] timeout_ms ms timeout to trigger IRQ
* @return 0 on success, otherwise -1 and errno is set appropriately
*
* It does not work per-channel for the following acuqisition mechanism:
* - FIFO: there is a global configuration for all channels
*/
int fmctdc_coalescing_timeout_get(struct fmctdc_board *userb,
unsigned int channel,
unsigned int *timeout_ms)
{
struct __fmctdc_board *b = (void *)(userb);
char path[64];
uint32_t val;
int err;
if (channel >= FMCTDC_NUM_CHANNELS) {
errno = EINVAL;
return -1;
}
snprintf(path, sizeof(path), "ft-ch%d/irq_coalescing_time",
channel + 1);
err = fmctdc_sysfs_get(b, path, &val);
if (err)
return -1;
*timeout_ms = val;
return 0;
}
......@@ -152,6 +152,13 @@ extern int fmctdc_get_offset_user(struct fmctdc_board *userb,
extern int fmctdc_buffer_mode(struct fmctdc_board *userb,
unsigned int channel,
enum ft_transfer_mode *mode);
extern int fmctdc_coalescing_timeout_set(struct fmctdc_board *userb,
unsigned int channel,
unsigned int timeout_ms);
extern int fmctdc_coalescing_timeout_get(struct fmctdc_board *userb,
unsigned int channel,
unsigned int *timeout_ms);
/**@}*/
......
......@@ -115,6 +115,8 @@ static void help(char *name)
fprintf(stderr, " -h: print this message\n\n");
fprintf(stderr, " -V: print version info\n\n");
fprintf(stderr, " -t <mode>: It does some test of the incoming timestampts\n\n");
fprintf(stderr, " -o <ms>: IRQ coalescing milleseconds timeout\n\n");
fprintf(stderr, " channels enumerations go from %d to %d \n\n",
FMCTDC_CH_1, FMCTDC_CH_LAST);
......@@ -196,6 +198,7 @@ int main(int argc, char **argv)
int ch_valid[FMCTDC_NUM_CHANNELS] = {0, 1, 2, 3, 4};
struct pollfd p[FMCTDC_NUM_CHANNELS];
enum tstamp_testing_modes mode = 0;
int timeout_ms = -1;
/* Set up the structure to specify the new action. */
new_action.sa_handler = termination_handler;
......@@ -219,7 +222,7 @@ int main(int argc, char **argv)
ref[i] = -1;
/* Parse Options */
while ((opt = getopt(argc, argv, "D:hwns:d:frm:l:Lc:VS:t:")) != -1) {
while ((opt = getopt(argc, argv, "D:hwns:d:frm:l:Lc:VS:t:o:")) != -1) {
switch (opt) {
case 'D':
ret = sscanf(optarg, "0x%04x", &dev_id);
......@@ -311,6 +314,15 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
break;
case 'o':
ret = sscanf(optarg, "%u", &timeout_ms);
if (ret != 1) {
fprintf(stderr, "%s: invalid IRQ coalescing timeout %s\n",
argv[0], optarg);
help(argv[0]);
exit(EXIT_FAILURE);
}
break;
}
}
......@@ -357,6 +369,13 @@ int main(int argc, char **argv)
ref[ch] = -1;
}
ret = fmctdc_coalescing_timeout_set(brd, ch, timeout_ms);
if (ret) {
fprintf(stderr,
"%s: chan %d: cannot set IRQ coalescing timeout: %s. Use default\n",
argv[0], ch, fmctdc_strerror(errno));
}
/* set buffer mode */
ret = fmctdc_set_buffer_mode(brd, ch, bufmode);
if (ret) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment