tdc: process the data for ZIO

Added tdc_zio_raw_io() function. Added logic to the work thread.

It needs to be refactored, define some constants properly but more or less is fine.
Signed-off-by: Samuel Iglesias Gonsálvez's avatarSamuel Iglesias Gonsálvez <siglesias@igalia.com>
parent 608cec73
...@@ -96,4 +96,11 @@ ...@@ -96,4 +96,11 @@
#define TDC_MEZZANINE_I2C 0x60000 #define TDC_MEZZANINE_I2C 0x60000
#define TDC_MEZZANINE_1WIRE 0x80000 #define TDC_MEZZANINE_1WIRE 0x80000
/* Constants */
#define TDC_CHAN_NUMBER 5
#define TDC_EVENT_BUFFER_SIZE 1024
#define TDC_EVENT_CHANNEL_MASK 0x3
#define TDC_EVENT_DACAPO_FLAG BIT(0)
#endif /* __TDC_REGISTERS_H */ #endif /* __TDC_REGISTERS_H */
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/semaphore.h>
#include "spec.h" #include "spec.h"
#include "tdc.h" #include "tdc.h"
...@@ -49,7 +50,7 @@ static void tdc_fmc_fw_reset(struct spec_tdc *tdc) ...@@ -49,7 +50,7 @@ static void tdc_fmc_fw_reset(struct spec_tdc *tdc)
mdelay(600); mdelay(600);
} }
static int tdc_fmc_check_lost_events(u32 curr_wr_ptr, u32 prev_wr_ptr) static int tdc_fmc_check_lost_events(u32 curr_wr_ptr, u32 prev_wr_ptr, int *count)
{ {
u32 dacapo_prev, dacapo_curr, dacapo_diff; u32 dacapo_prev, dacapo_curr, dacapo_diff;
...@@ -62,14 +63,19 @@ static int tdc_fmc_check_lost_events(u32 curr_wr_ptr, u32 prev_wr_ptr) ...@@ -62,14 +63,19 @@ static int tdc_fmc_check_lost_events(u32 curr_wr_ptr, u32 prev_wr_ptr)
switch(dacapo_diff) { switch(dacapo_diff) {
case 1: case 1:
if ((curr_wr_ptr - prev_wr_ptr) > 0) if ((curr_wr_ptr - prev_wr_ptr) > 0) {
*count = TDC_EVENT_BUFFER_SIZE;
return 1; /* We lost data */ return 1; /* We lost data */
}
*count = curr_wr_ptr - prev_wr_ptr + TDC_EVENT_BUFFER_SIZE;
break; break;
case 0: case 0:
/* We didn't lose data */ /* We didn't lose data */
*count = curr_wr_ptr - prev_wr_ptr;
break; break;
default: default:
/* We lost data for sure. Notify to the user */ /* We lost data for sure. Notify to the user */
*count = TDC_EVENT_BUFFER_SIZE;
return 1; return 1;
} }
...@@ -80,11 +86,11 @@ static void tdc_fmc_irq_work(struct work_struct *work) ...@@ -80,11 +86,11 @@ static void tdc_fmc_irq_work(struct work_struct *work)
{ {
struct spec_tdc *tdc = container_of(work, struct spec_tdc, irq_work); struct spec_tdc *tdc = container_of(work, struct spec_tdc, irq_work);
u32 curr_wr_ptr, prev_wr_ptr; u32 curr_wr_ptr, prev_wr_ptr;
int ret; int ret, dacapo_flag, count, rd_ptr, chan;
struct tdc_event *events; struct tdc_event *events, *tmp_data;
/* TODO: change the size of the tdc buffer for a constant with a proper value */ /* TODO: change the size of the tdc buffer for a constant with a proper value */
events = kzalloc(1024*sizeof(struct tdc_event), GFP_KERNEL); events = kzalloc(TDC_EVENT_BUFFER_SIZE*sizeof(struct tdc_event), GFP_KERNEL);
if(!events) { if(!events) {
pr_err("error allocating memory for the events\n"); pr_err("error allocating memory for the events\n");
return; return;
...@@ -94,17 +100,14 @@ static void tdc_fmc_irq_work(struct work_struct *work) ...@@ -94,17 +100,14 @@ static void tdc_fmc_irq_work(struct work_struct *work)
mutex_lock(&fmc_dma_lock); mutex_lock(&fmc_dma_lock);
curr_wr_ptr = tdc_get_circular_buffer_wr_pointer(tdc); curr_wr_ptr = tdc_get_circular_buffer_wr_pointer(tdc);
if(curr_wr_ptr == tdc->wr_pointer) { if(curr_wr_ptr == tdc->wr_pointer)
mutex_unlock(&fmc_dma_lock); goto dma_out; /* No new events happened */
return; /* No new events happened */
}
prev_wr_ptr = tdc->wr_pointer; prev_wr_ptr = tdc->wr_pointer;
ret = tdc_dma_setup(tdc, 0, (unsigned long)events, 1024*sizeof(struct tdc_event)); ret = tdc_dma_setup(tdc, 0, (unsigned long)events, 1024*sizeof(struct tdc_event));
if (ret) { if (ret)
mutex_unlock(&fmc_dma_lock); goto dma_out;
kfree(events);
return;
}
/* Start DMA transfer and wait for it */ /* Start DMA transfer and wait for it */
tdc_dma_start(tdc); tdc_dma_start(tdc);
...@@ -113,19 +116,43 @@ static void tdc_fmc_irq_work(struct work_struct *work) ...@@ -113,19 +116,43 @@ static void tdc_fmc_irq_work(struct work_struct *work)
/* TODO: Check DMASTATR register to see if there was an error */ /* TODO: Check DMASTATR register to see if there was an error */
atomic_set(&fmc_dma_end, 0); atomic_set(&fmc_dma_end, 0);
tdc->wr_pointer = curr_wr_ptr; tdc->wr_pointer = curr_wr_ptr;
mutex_unlock(&fmc_dma_lock);
/* Process the data */ /* Process the data */
ret = tdc_fmc_check_lost_events(curr_wr_ptr, prev_wr_ptr); dacapo_flag = tdc_fmc_check_lost_events(curr_wr_ptr, prev_wr_ptr, &count);
if (ret) { if (dacapo_flag) {
pr_err("We have lost data\n"); pr_err("We have lost data\n");
/* TODO: Notify it in some way to the user. Flag in ctrl block? */ /* TODO: Notify it in some way to the user. Flag in ctrl block? */
} }
/* TODO: now we have the samples... what to do with them?? */ /* Start reading in the oldest event */
/* TODO: I put a kfree meanwhile */ if(count == TDC_EVENT_BUFFER_SIZE)
kfree(events); rd_ptr = curr_wr_ptr; /* The oldest is curr_wr_ptr */
else
rd_ptr = prev_wr_ptr; /* The oldest is prev_wr_ptr */
for ( ; count > 0; count--) {
tmp_data = &events[rd_ptr];
/* Check which channel to deliver the data */
chan = tmp_data->metadata & TDC_EVENT_CHANNEL_MASK; /* FIXME: mask to know the channel number */
/* Add the DaCapo flag to notify the user */
tdc->event[chan].dacapo_flag = dacapo_flag;
/* Copy the data and notify the readers (ZIO trigger) */
tdc->event[chan].data = *tmp_data;
/* XXX: Flag to avoid the ZIO trigger to read always the same element
* Until we change to a mutex or a data buffer bigger than one.
*/
tdc->event[chan].read = 0;
/* XXX: as it has only one element of data, maybe is better a mutex
* instead of semaphore!
*/
up(&tdc->event[chan].lock);
rd_ptr = (rd_ptr + 1) % TDC_EVENT_BUFFER_SIZE;
}
dma_out:
mutex_unlock(&fmc_dma_lock);
kfree(events);
} }
irqreturn_t tdc_fmc_irq_handler(int irq, void *dev_id) irqreturn_t tdc_fmc_irq_handler(int irq, void *dev_id)
...@@ -162,7 +189,7 @@ int tdc_fmc_probe(struct fmc_device *dev) ...@@ -162,7 +189,7 @@ int tdc_fmc_probe(struct fmc_device *dev)
{ {
struct spec_tdc *tdc; struct spec_tdc *tdc;
struct spec_dev *spec; struct spec_dev *spec;
int ret; int ret, i;
if(strcmp(dev->carrier_name, "SPEC") != 0) if(strcmp(dev->carrier_name, "SPEC") != 0)
return -ENODEV; return -ENODEV;
...@@ -179,6 +206,7 @@ int tdc_fmc_probe(struct fmc_device *dev) ...@@ -179,6 +206,7 @@ int tdc_fmc_probe(struct fmc_device *dev)
return -ENOMEM; return -ENOMEM;
} }
/* Initialize structures */
spec = dev->carrier_data; spec = dev->carrier_data;
tdc->spec = spec; tdc->spec = spec;
spec->sub_priv = tdc; spec->sub_priv = tdc;
...@@ -187,6 +215,9 @@ int tdc_fmc_probe(struct fmc_device *dev) ...@@ -187,6 +215,9 @@ int tdc_fmc_probe(struct fmc_device *dev)
tdc->regs = tdc->base; /* BAR 0 */ tdc->regs = tdc->base; /* BAR 0 */
tdc->gn412x_regs = spec->remap[2]; /* BAR 4 */ tdc->gn412x_regs = spec->remap[2]; /* BAR 4 */
tdc->wr_pointer = 0; tdc->wr_pointer = 0;
for(i = 0; i < TDC_CHAN_NUMBER; i++)
sema_init(&tdc->event[i].lock, 0);
/* Setup the Gennum 412x local clock frequency */ /* Setup the Gennum 412x local clock frequency */
tdc_fmc_gennum_setup_local_clock(tdc, 160); tdc_fmc_gennum_setup_local_clock(tdc, 160);
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#define _RW_ (S_IRUGO | S_IWUGO) /* I want 80-col lines so this lazy thing */ #define _RW_ (S_IRUGO | S_IWUGO) /* I want 80-col lines so this lazy thing */
static int tdc_zio_raw_io(struct zio_cset *cset);
/* The sample size. Mandatory, device-wide */ /* The sample size. Mandatory, device-wide */
DEFINE_ZATTR_STD(ZDEV, tdc_zattr_dev_std) = { DEFINE_ZATTR_STD(ZDEV, tdc_zattr_dev_std) = {
ZATTR_REG(zdev, ZATTR_NBITS, S_IRUGO, 0, 32), /* FIXME: 32 bits. Really? */ ZATTR_REG(zdev, ZATTR_NBITS, S_IRUGO, 0, 32), /* FIXME: 32 bits. Really? */
...@@ -45,7 +47,7 @@ static struct zio_attribute tdc_zattr_dev[] = { ...@@ -45,7 +47,7 @@ static struct zio_attribute tdc_zattr_dev[] = {
static struct zio_cset tdc_cset[] = { static struct zio_cset tdc_cset[] = {
{ {
SET_OBJECT_NAME("tdc-cset0"), SET_OBJECT_NAME("tdc-cset0"),
.raw_io = NULL, .raw_io = tdc_zio_raw_io,
.n_chan = 1, .n_chan = 1,
.ssize = 4, /* FIXME: 0? */ .ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME, .flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
...@@ -56,7 +58,7 @@ static struct zio_cset tdc_cset[] = { ...@@ -56,7 +58,7 @@ static struct zio_cset tdc_cset[] = {
}, },
{ {
SET_OBJECT_NAME("tdc-cset1"), SET_OBJECT_NAME("tdc-cset1"),
.raw_io = NULL, .raw_io = tdc_zio_raw_io,
.n_chan = 1, .n_chan = 1,
.ssize = 4, /* FIXME: 0? */ .ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME, .flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
...@@ -67,7 +69,7 @@ static struct zio_cset tdc_cset[] = { ...@@ -67,7 +69,7 @@ static struct zio_cset tdc_cset[] = {
}, },
{ {
SET_OBJECT_NAME("tdc-cset2"), SET_OBJECT_NAME("tdc-cset2"),
.raw_io = NULL, .raw_io = tdc_zio_raw_io,
.n_chan = 1, .n_chan = 1,
.ssize = 4, /* FIXME: 0? */ .ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME, .flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
...@@ -78,7 +80,7 @@ static struct zio_cset tdc_cset[] = { ...@@ -78,7 +80,7 @@ static struct zio_cset tdc_cset[] = {
}, },
{ {
SET_OBJECT_NAME("tdc-cset3"), SET_OBJECT_NAME("tdc-cset3"),
.raw_io = NULL, .raw_io = tdc_zio_raw_io,
.n_chan = 1, .n_chan = 1,
.ssize = 4, /* FIXME: 0? */ .ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME, .flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
...@@ -89,7 +91,7 @@ static struct zio_cset tdc_cset[] = { ...@@ -89,7 +91,7 @@ static struct zio_cset tdc_cset[] = {
}, },
{ {
SET_OBJECT_NAME("tdc-cset4"), SET_OBJECT_NAME("tdc-cset4"),
.raw_io = NULL, .raw_io = tdc_zio_raw_io,
.n_chan = 1, .n_chan = 1,
.ssize = 4, /* FIXME: 0? */ .ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME, .flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
...@@ -193,7 +195,7 @@ static const struct zio_sysfs_operations tdc_zio_s_op = { ...@@ -193,7 +195,7 @@ static const struct zio_sysfs_operations tdc_zio_s_op = {
static struct zio_device tdc_tmpl = { static struct zio_device tdc_tmpl = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.preferred_trigger = "user", /* FIXME: put other trigger */ .preferred_trigger = "user",
.s_op = &tdc_zio_s_op, .s_op = &tdc_zio_s_op,
.cset = tdc_cset, .cset = tdc_cset,
.n_cset = ARRAY_SIZE(tdc_cset), .n_cset = ARRAY_SIZE(tdc_cset),
...@@ -209,6 +211,40 @@ static const struct zio_device_id tdc_table[] = { ...@@ -209,6 +211,40 @@ static const struct zio_device_id tdc_table[] = {
{}, {},
}; };
static int tdc_zio_raw_io(struct zio_cset *cset)
{
struct spec_tdc *tdc;
struct zio_channel *zio_chan;
struct zio_control *ctrl;
struct zio_device *zdev = cset->zdev;
int chan;
zio_chan = cset->chan;
tdc = zdev->priv_d;
chan = cset->index - 1;
/* Wait for data */
down(&tdc->event[chan].lock);
/* Check if we have read this data before */
/* XXX: change it if we have more data or use a mutex */
if (tdc->event[chan].read)
return -EAGAIN;
tdc->event[chan].read = 1;
/* Process the data */
ctrl = zio_get_ctrl(zio_chan->active_block);
ctrl->ssize = 1; /* one event */
ctrl->nbits = 0; /* no sample data. Only metadata */
ctrl->tstamp.secs = tdc->event[chan].data.local_utc;
ctrl->tstamp.ticks = tdc->event[chan].data.coarse_time;
ctrl->tstamp.bins = tdc->event[chan].data.fine_time;
ctrl->flags = tdc->event[chan].dacapo_flag; /* XXX: Is it OK here? */
ctrl->reserved = tdc->event[chan].data.metadata;
return 0;
}
static int tdc_zio_probe(struct zio_device *zdev) static int tdc_zio_probe(struct zio_device *zdev)
{ {
/* TODO */ /* TODO */
......
...@@ -5,20 +5,8 @@ ...@@ -5,20 +5,8 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/semaphore.h>
struct spec_tdc { #include "hw/tdc_regs.h"
struct fmc_device *fmc;
struct spec_dev *spec;
struct zio_device *zdev, *hwzdev;
unsigned char __iomem *base; /* regs files are byte-oriented */
unsigned char __iomem *regs;
unsigned char __iomem *gn412x_regs;
atomic_t busy; /* whether the device is acquiring data */
u32 wr_pointer; /* XXX: Used to save the previous value of the wr_pointer
* XXX: Watch out the Da Capo Flag! It may confuse us!
*/
struct work_struct irq_work;
};
struct tdc_event { struct tdc_event {
u32 fine_time; /* In BIN (81 ps resolution) */ u32 fine_time; /* In BIN (81 ps resolution) */
...@@ -27,6 +15,13 @@ struct tdc_event { ...@@ -27,6 +15,13 @@ struct tdc_event {
u32 metadata; u32 metadata;
} __packed; } __packed;
struct tdc_event_buffer {
struct tdc_event data;
struct semaphore lock;
int dacapo_flag;
int read;
};
struct tdc_acam_cfg { struct tdc_acam_cfg {
u32 edge_config; /* ACAM reg. 0 */ u32 edge_config; /* ACAM reg. 0 */
u32 channel_adj; /* ACAM reg. 1 */ u32 channel_adj; /* ACAM reg. 1 */
...@@ -54,6 +49,19 @@ enum tdc_zattr_dev_idx { ...@@ -54,6 +49,19 @@ enum tdc_zattr_dev_idx {
TDC_ATTR_DEV__LAST, TDC_ATTR_DEV__LAST,
}; };
struct spec_tdc {
struct fmc_device *fmc;
struct spec_dev *spec;
struct zio_device *zdev, *hwzdev;
unsigned char __iomem *base; /* regs files are byte-oriented */
unsigned char __iomem *regs;
unsigned char __iomem *gn412x_regs;
atomic_t busy; /* whether the device is acquiring data */
u32 wr_pointer;
struct work_struct irq_work;
struct tdc_event_buffer event[TDC_CHAN_NUMBER];
};
/* ZIO helper functions */ /* ZIO helper functions */
extern int tdc_zio_register_device(struct spec_tdc *tdc); extern int tdc_zio_register_device(struct spec_tdc *tdc);
extern void tdc_zio_remove(struct spec_tdc *tdc); extern void tdc_zio_remove(struct spec_tdc *tdc);
......
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