tdc: filled up the irq_handler.

It has now a workqueue to setup the DMA transfer, a wait list for waiting for a
DMA transfer to finish and a mutex to serialize the DMA transfers.

Still, there is no processing of the data. It must be added later.
Signed-off-by: Samuel Iglesias Gonsálvez's avatarSamuel Iglesias Gonsálvez <siglesias@igalia.com>
parent 411f5fd7
......@@ -13,6 +13,10 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/atomic.h>
#include "spec.h"
#include "tdc.h"
......@@ -20,6 +24,9 @@
static struct fmc_driver tdc_fmc_driver;
static struct workqueue_struct *tdc_workqueue;
DEFINE_MUTEX(fmc_dma_lock);
DECLARE_WAIT_QUEUE_HEAD(fmc_wait_dma);
static atomic_t fmc_dma_end;
static void tdc_fmc_gennum_setup_local_clock(struct spec_tdc *tdc, int freq)
{
......@@ -42,10 +49,83 @@ 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)
{
u32 dacapo_prev, dacapo_curr, dacapo_diff;
dacapo_prev = prev_wr_ptr >> 12;
dacapo_curr = curr_wr_ptr >> 12;
curr_wr_ptr &= 0x0fff; /* Pick last 12 bits */
prev_wr_ptr &= 0x0fff; /* Pick last 12 bits */
dacapo_diff = dacapo_curr - dacapo_prev;
switch(dacapo_diff) {
case 1:
if ((curr_wr_ptr - prev_wr_ptr) > 0)
return 1; /* We lost data */
break;
case 0:
/* We didn't lose data */
break;
default:
/* We lost data for sure. Notify to the user */
return 1;
}
return 0;
}
static void tdc_fmc_irq_work(struct work_struct *work)
{
struct spec_tdc *tdc = container_of(work, struct spec_tdc, irq_work);
/* TODO: fill with DMA transfer and so on */
u32 curr_wr_ptr, prev_wr_ptr;
int ret;
struct tdc_event *events;
/* TODO: change the size of the tdc buffer for a constant with a proper value */
events = kzalloc(1024*sizeof(struct tdc_event), GFP_KERNEL);
if(!events) {
pr_err("error allocating memory for the events\n");
return;
}
/* Setup DMA transfer. Only one process can do it */
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 */
}
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;
}
/* Start DMA transfer and wait for it */
tdc_dma_start(tdc);
wait_event(fmc_wait_dma, atomic_read(&fmc_dma_end));
/* DMA happened */
/* 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) {
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);
}
irqreturn_t tdc_fmc_irq_handler(int irq, void *dev_id)
......@@ -53,10 +133,26 @@ irqreturn_t tdc_fmc_irq_handler(int irq, void *dev_id)
struct fmc_device *fmc = dev_id;
struct spec_dev *spec = fmc->carrier_data;
struct spec_tdc *tdc = spec->sub_priv;
u32 irq_code;
/* TODO: fill with everything */
pr_err("tdc: IRQ is coming\n");
queue_work(tdc_workqueue, &tdc->irq_work);
/* Check the source of the interrupt */
irq_code = readl(fmc->base + TDC_IRQ_CODE_R);
/* Tstamp threshold or time threshold */
if((irq_code & TDC_IRQ_TDC_TSTAMP) ||
(irq_code & TDC_IRQ_TDC_TIME_THRESH))
queue_work(tdc_workqueue, &tdc->irq_work);
/* DMA interrupt */
if((irq_code & TDC_IRQ_GNUM_CORE_0) ||
(irq_code & TDC_IRQ_GNUM_CORE_1)) {
/* Wake up the threads waiting for the DMA transfer */
atomic_set(&fmc_dma_end, 1);
wake_up(&fmc_wait_dma);
}
/* Acknowledge the IRQ and exit */
fmc->op->irq_ack(fmc);
return IRQ_HANDLED;
......@@ -125,6 +221,7 @@ int tdc_fmc_remove(struct fmc_device *dev)
int tdc_fmc_init(void)
{
tdc_workqueue = create_singlethread_workqueue(KBUILD_MODNAME);
atomic_set(&fmc_dma_end, 0);
tdc_fmc_driver.probe = tdc_fmc_probe;
tdc_fmc_driver.remove = tdc_fmc_remove;
fmc_driver_register(&tdc_fmc_driver);
......
......@@ -13,13 +13,20 @@ struct spec_tdc {
unsigned char __iomem *base; /* regs files are byte-oriented */
unsigned char __iomem *regs;
unsigned char __iomem *gn412x_regs;
struct work_struct irq_work;
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 {
u32 fine_time; /* In BIN (81 ps resolution) */
u32 coarse_time; /* 8 ns resolution */
u32 local_utc; /* 1 second resolution */
u32 metadata;
} __packed;
struct tdc_acam_cfg {
u32 edge_config; /* ACAM reg. 0 */
u32 channel_adj; /* ACAM reg. 1 */
......
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