Commit 6970670a authored by Michel Arruat's avatar Michel Arruat Committed by Federico Vaga

adc_driver CSET_BUSY flag raised in the IRQ handler

The IRQ handler defers to a workqueue the execution of the DMA.
The workqueue can be delayed (depending of workqueue scheduling policy)
and it may happen that meanwhile a trigger start/stop sequence occurs
to serve a trigger configuration change. Data blocks exist but are
inconsistent with the current acquisition.
To avoid this concurrency issue CSET_BUSY flag is raised
in the IRQ Handler.
parent 7651e986
...@@ -248,17 +248,37 @@ static void fa_irq_work(struct work_struct *work) ...@@ -248,17 +248,37 @@ static void fa_irq_work(struct work_struct *work)
struct zio_cset *cset = fa->zdev->cset; struct zio_cset *cset = fa->zdev->cset;
int res; int res;
zfat_irq_acq_end(cset);
res = zfad_dma_start(cset); res = zfad_dma_start(cset);
if (res) if (res)
zfad_dma_error(cset); zfad_dma_error(cset);
else {
/*
* No error.
* If there is an IRQ DMA src to notify the ends of the DMA,
* leave the workqueue.
* dma_done will be proceed on DMA_END reception.
* Otherwhise call dma_done in sequence
*/
if (fa->irq_src & FA_IRQ_SRC_DMA)
/*
* waiting for END_OF_DMA IRQ
* with the CSET_BUSY flag Raised
* The flag will be lowered by the irq_handler
* handling END_DMA
*/
goto end;
zfad_dma_done(cset);
}
/* /*
* No error. * Lower CSET_BUSY
* If there is not an IRQ DMA src to notify the ends of the DMA,
* process data immediately.
* Otherwhise this job is delayed till an IRQ DMA occurs
*/ */
else if (!(fa->irq_src & FA_IRQ_SRC_DMA)) spin_lock(&cset->lock);
zfad_dma_done(cset); cset->flags &= ~ZIO_CSET_BUSY;
spin_unlock(&cset->lock);
end:
/* ack the irq */ /* ack the irq */
fa->fmc->op->irq_ack(fa->fmc); fa->fmc->op->irq_ack(fa->fmc);
} }
...@@ -307,6 +327,8 @@ irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id) ...@@ -307,6 +327,8 @@ irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id)
struct fa_dev *fa = fmc_get_drvdata(fmc); struct fa_dev *fa = fmc_get_drvdata(fmc);
struct zio_cset *cset = fa->zdev->cset; struct zio_cset *cset = fa->zdev->cset;
uint32_t status; uint32_t status;
unsigned long flags;
struct zfad_block *zfad_block;
/* irq to handle */ /* irq to handle */
fa_get_irq_status(fa, irq_core_base, &status); fa_get_irq_status(fa, irq_core_base, &status);
...@@ -317,19 +339,36 @@ irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id) ...@@ -317,19 +339,36 @@ irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id)
fmc->slot_id); fmc->slot_id);
if (status & FA_IRQ_ADC_ACQ_END) { if (status & FA_IRQ_ADC_ACQ_END) {
zfat_irq_acq_end(cset); /*
/* Job delegated to the workqueue: */ * Acquiring samples is a critical section
/* Start DMA and acknowledge of the irq on the carrier */ * protected against any concurrent abbort trigger.
schedule_work(&fa->irq_work); * This is done by raising the flag CSET_BUSY at ACQ_END
/* register the core which just fired the IRQ */ * and lowered it at the end of DMA_END.
/* check proper sequence of IRQ for multi IRQ (ACQ + DMA)*/ */
fa->last_irq_core_src = irq_core_base; spin_lock_irqsave(&cset->lock, flags);
zfad_block = cset->interleave->priv_d;
/* Check first if any concurrent trigger stop */
/* has deleted zio blocks. In such a case */
/* the flag is not raised and nothing is done */
if (zfad_block != NULL)
cset->flags |= ZIO_CSET_BUSY;
spin_unlock_irqrestore(&cset->lock, flags);
if (cset->flags & ZIO_CSET_BUSY) {
/* Job deferred to the workqueue: */
/* Start DMA and ack irq on the carrier */
schedule_work(&fa->irq_work);
/* register the core firing the IRQ in order to */
/* check right IRQ seq.: ACQ_END followed by DMA_END */
fa->last_irq_core_src = irq_core_base;
} else /* current Acquiistion has been stopped */
fmc->op->irq_ack(fmc);
} else { /* unexpected interrupt we have to ack anyway */ } else { /* unexpected interrupt we have to ack anyway */
dev_err(&fa->fmc->dev, dev_err(&fa->fmc->dev,
"%s unexpected interrupt 0x%x\n", "%s unexpected interrupt 0x%x\n",
__func__, status); __func__, status);
fmc->op->irq_ack(fmc); fmc->op->irq_ack(fmc);
} }
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
...@@ -90,6 +90,14 @@ irqreturn_t fa_spec_irq_handler(int irq_core_base, void *ptr) ...@@ -90,6 +90,14 @@ irqreturn_t fa_spec_irq_handler(int irq_core_base, void *ptr)
/* check proper sequence of IRQ in case of multi IRQ (ACQ + DMA)*/ /* check proper sequence of IRQ in case of multi IRQ (ACQ + DMA)*/
fa->last_irq_core_src = irq_core_base; fa->last_irq_core_src = irq_core_base;
/*
* DMA transaction is finished
* we can safely lower CSET_BUSY
*/
spin_lock(&cset->lock);
cset->flags &= ~ZIO_CSET_BUSY;
spin_unlock(&cset->lock);
/* ack the irq */ /* ack the irq */
fa->fmc->op->irq_ack(fa->fmc); fa->fmc->op->irq_ack(fa->fmc);
......
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