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 @@
#define TDC_MEZZANINE_I2C 0x60000
#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 */
......@@ -17,6 +17,7 @@
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/atomic.h>
#include <linux/semaphore.h>
#include "spec.h"
#include "tdc.h"
......@@ -49,7 +50,7 @@ static void tdc_fmc_fw_reset(struct spec_tdc *tdc)
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;
......@@ -62,14 +63,19 @@ static int tdc_fmc_check_lost_events(u32 curr_wr_ptr, u32 prev_wr_ptr)
switch(dacapo_diff) {
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 */
}
*count = curr_wr_ptr - prev_wr_ptr + TDC_EVENT_BUFFER_SIZE;
break;
case 0:
/* We didn't lose data */
*count = curr_wr_ptr - prev_wr_ptr;
break;
default:
/* We lost data for sure. Notify to the user */
*count = TDC_EVENT_BUFFER_SIZE;
return 1;
}
......@@ -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);
u32 curr_wr_ptr, prev_wr_ptr;
int ret;
struct tdc_event *events;
int ret, dacapo_flag, count, rd_ptr, chan;
struct tdc_event *events, *tmp_data;
/* 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) {
pr_err("error allocating memory for the events\n");
return;
......@@ -94,17 +100,14 @@ static void tdc_fmc_irq_work(struct work_struct *work)
mutex_lock(&fmc_dma_lock);
curr_wr_ptr = tdc_get_circular_buffer_wr_pointer(tdc);
if(curr_wr_ptr == tdc->wr_pointer) {
mutex_unlock(&fmc_dma_lock);
return; /* No new events happened */
}
if(curr_wr_ptr == tdc->wr_pointer)
goto dma_out; /* No new events happened */
prev_wr_ptr = tdc->wr_pointer;
ret = tdc_dma_setup(tdc, 0, (unsigned long)events, 1024*sizeof(struct tdc_event));
if (ret) {
mutex_unlock(&fmc_dma_lock);
kfree(events);
return;
}
if (ret)
goto dma_out;
/* Start DMA transfer and wait for it */
tdc_dma_start(tdc);
......@@ -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 */
atomic_set(&fmc_dma_end, 0);
tdc->wr_pointer = curr_wr_ptr;
mutex_unlock(&fmc_dma_lock);
/* Process the data */
ret = tdc_fmc_check_lost_events(curr_wr_ptr, prev_wr_ptr);
if (ret) {
dacapo_flag = tdc_fmc_check_lost_events(curr_wr_ptr, prev_wr_ptr, &count);
if (dacapo_flag) {
pr_err("We have lost data\n");
/* TODO: Notify it in some way to the user. Flag in ctrl block? */
}
/* TODO: now we have the samples... what to do with them?? */
/* TODO: I put a kfree meanwhile */
kfree(events);
/* Start reading in the oldest event */
if(count == TDC_EVENT_BUFFER_SIZE)
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)
......@@ -162,7 +189,7 @@ int tdc_fmc_probe(struct fmc_device *dev)
{
struct spec_tdc *tdc;
struct spec_dev *spec;
int ret;
int ret, i;
if(strcmp(dev->carrier_name, "SPEC") != 0)
return -ENODEV;
......@@ -179,6 +206,7 @@ int tdc_fmc_probe(struct fmc_device *dev)
return -ENOMEM;
}
/* Initialize structures */
spec = dev->carrier_data;
tdc->spec = spec;
spec->sub_priv = tdc;
......@@ -187,6 +215,9 @@ int tdc_fmc_probe(struct fmc_device *dev)
tdc->regs = tdc->base; /* BAR 0 */
tdc->gn412x_regs = spec->remap[2]; /* BAR 4 */
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 */
tdc_fmc_gennum_setup_local_clock(tdc, 160);
......
......@@ -24,6 +24,8 @@
#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 */
DEFINE_ZATTR_STD(ZDEV, tdc_zattr_dev_std) = {
ZATTR_REG(zdev, ZATTR_NBITS, S_IRUGO, 0, 32), /* FIXME: 32 bits. Really? */
......@@ -45,7 +47,7 @@ static struct zio_attribute tdc_zattr_dev[] = {
static struct zio_cset tdc_cset[] = {
{
SET_OBJECT_NAME("tdc-cset0"),
.raw_io = NULL,
.raw_io = tdc_zio_raw_io,
.n_chan = 1,
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
......@@ -56,7 +58,7 @@ static struct zio_cset tdc_cset[] = {
},
{
SET_OBJECT_NAME("tdc-cset1"),
.raw_io = NULL,
.raw_io = tdc_zio_raw_io,
.n_chan = 1,
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
......@@ -67,7 +69,7 @@ static struct zio_cset tdc_cset[] = {
},
{
SET_OBJECT_NAME("tdc-cset2"),
.raw_io = NULL,
.raw_io = tdc_zio_raw_io,
.n_chan = 1,
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
......@@ -78,7 +80,7 @@ static struct zio_cset tdc_cset[] = {
},
{
SET_OBJECT_NAME("tdc-cset3"),
.raw_io = NULL,
.raw_io = tdc_zio_raw_io,
.n_chan = 1,
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
......@@ -89,7 +91,7 @@ static struct zio_cset tdc_cset[] = {
},
{
SET_OBJECT_NAME("tdc-cset4"),
.raw_io = NULL,
.raw_io = tdc_zio_raw_io,
.n_chan = 1,
.ssize = 4, /* FIXME: 0? */
.flags = ZIO_DIR_INPUT | ZCSET_TYPE_TIME,
......@@ -193,7 +195,7 @@ static const struct zio_sysfs_operations tdc_zio_s_op = {
static struct zio_device tdc_tmpl = {
.owner = THIS_MODULE,
.preferred_trigger = "user", /* FIXME: put other trigger */
.preferred_trigger = "user",
.s_op = &tdc_zio_s_op,
.cset = tdc_cset,
.n_cset = ARRAY_SIZE(tdc_cset),
......@@ -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)
{
/* TODO */
......
......@@ -5,20 +5,8 @@
#include <linux/types.h>
#include <linux/workqueue.h>
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; /* 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;
};
#include <linux/semaphore.h>
#include "hw/tdc_regs.h"
struct tdc_event {
u32 fine_time; /* In BIN (81 ps resolution) */
......@@ -27,6 +15,13 @@ struct tdc_event {
u32 metadata;
} __packed;
struct tdc_event_buffer {
struct tdc_event data;
struct semaphore lock;
int dacapo_flag;
int read;
};
struct tdc_acam_cfg {
u32 edge_config; /* ACAM reg. 0 */
u32 channel_adj; /* ACAM reg. 1 */
......@@ -54,6 +49,19 @@ enum tdc_zattr_dev_idx {
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 */
extern int tdc_zio_register_device(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