summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/sama5/sam_adc.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/arch/arm/src/sama5/sam_adc.c')
-rw-r--r--nuttx/arch/arm/src/sama5/sam_adc.c147
1 files changed, 132 insertions, 15 deletions
diff --git a/nuttx/arch/arm/src/sama5/sam_adc.c b/nuttx/arch/arm/src/sama5/sam_adc.c
index cc6e94561..9af6cdc2c 100644
--- a/nuttx/arch/arm/src/sama5/sam_adc.c
+++ b/nuttx/arch/arm/src/sama5/sam_adc.c
@@ -50,6 +50,7 @@
#include <arch/board/board.h>
#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
#include <nuttx/analog/adc.h>
#include "up_internal.h"
@@ -147,11 +148,13 @@ struct sam_adc_s
{
#ifdef SAMA5_ADC_HAVE_CHANNELS
struct adc_dev_s dev; /* The external via of the ADC device */
-
+ uint32_t pending; /* Pending EOC events */
#ifdef CONFIG_SAMA5_ADC_DMA
DMA_HANDLE dma; /* Handle for DMA channel */
#endif
+ struct work_s work; /* Supports the interrupt handling "bottom half" */
#endif
+ sem_t exclsem; /* Supports exclusive access to the ADC interface */
/* Debug stuff */
@@ -184,7 +187,8 @@ static int sam_adc_dmasetup(struct sam_adc_s *priv, FAR uint8_t *buffer,
/* ADC interrupt handling */
#ifdef SAMA5_ADC_HAVE_CHANNELS
-static void sam_adc_endconversion(struct sam_adc_s *priv, uint32_t pending);
+static void sam_tsd_bottomhalf(void *arg);
+static int sam_tsd_schedule(struct sam_tsd_s *priv, uint32_t pending);
#endif
static int sam_adc_interrupt(int irq, void *context);
@@ -321,7 +325,7 @@ static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result)
* invalidating the data cache.
*
* Input Parameters:
- * dev - An instance of the SDIO device interface
+ * priv - An instance of the ADC device interface
* buffer - The memory to DMA from
* buflen - The size of the DMA transfer in bytes
*
@@ -331,10 +335,9 @@ static void sam_adc_dmacallback(DMA_HANDLE handle, void *arg, int result)
****************************************************************************/
#ifdef CONFIG_SAMA5_ADC_DMA
-static int sam_adc_dmasetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
+static int sam_adc_dmasetup(FAR struct sam_adc_s *priv, FAR uint8_t *buffer,
size_t buflen)
{
- struct sam_dev_s *priv = (struct sam_dev_s *)dev;
uint32_t paddr;
uint32_t maddr;
@@ -370,18 +373,40 @@ static int sam_adc_dmasetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
* ADC interrupt handling
****************************************************************************/
/****************************************************************************
- * Name: sam_adc_endconversion
+ * Name: sam_tsd_bottomhalf
*
* Description:
- * End of conversion interrupt handler
+ * This function executes on the worker thread. It is scheduled by
+ * sam_tsd_interrupt whenever any enabled end-of-conversion event occurs.
+ * All EOC interrupts are disabled when this function runs. sam_tsd_bottomhalf
+ * will re-enable EOC interrupts when it completes processing all pending
+ * EOC events.
+ *
+ * Input Parameters
+ * arg - The ADC private data structure cast to (void *)
+ *
+ * Returned Value:
+ * None
*
****************************************************************************/
-static void sam_adc_endconversion(struct sam_adc_s *priv, uint32_t pending)
+static void sam_tsd_bottomhalf(void *arg)
{
+ struct sam_tsd_s *priv = (struct sam_tsd_s *)arg;
uint32_t regval;
+ uint32_t pending;
int chan;
+ ASSERT(priv != NULL);
+
+ /* Get the set of unmasked, pending ADC interrupts */
+
+ pending = priv->pending;
+
+ /* Get exclusive access to the driver data structure */
+
+ sam_adc_lock(priv->adc);
+
/* Check for the end of conversion event on each channel */
for (chan = 0; chan < SAM_ADC_NCHANNELS && pending != 0; chan++)
@@ -396,8 +421,54 @@ static void sam_adc_endconversion(struct sam_adc_s *priv, uint32_t pending)
pending &= ~bit;
}
}
+
+
+ /* Exit, re-enabling ADC interrupts */
+
+ignored:
+ /* Re-enable ADC interrupts. */
+
+ sam_adc_putreg32(priv->adc, SAM_ADC_IER, SAMA5_CHAN_ENABLE);
+
+ /* Release our lock on the ADC structure */
+
+ sem_adc_unlock(priv->adc);
}
+/****************************************************************************
+ * Name: sam_tsd_schedule
+ ****************************************************************************/
+
+static int sam_tsd_schedule(struct sam_tsd_s *priv, uint32_t pending)
+{
+ int ret;
+
+ /* Disable further touchscreen interrupts. Touchscreen interrupts will be
+ * re-enabled after the worker thread executes.
+ */
+
+ sam_adc_putreg32(priv->adc, SAM_ADC_IDR, ADC_INT_EOCALL);
+
+ /* Save the set of pending interrupts for the bottom half (in case any
+ * were cleared by reading the ISR.
+ */
+
+ priv->pending = pending.
+
+ /* Transfer processing to the worker thread. Since touchscreen ADC interrupts are
+ * disabled while the work is pending, no special action should be required
+ * to protected the work queue.
+ */
+
+ DEBUGASSERT(priv->work.worker == NULL);
+ ret = work_queue(HPWORK, &priv->work, sam_tsd_bottomhalf, priv, 0);
+ if (ret != 0)
+ {
+ illdbg("Failed to queue work: %d\n", ret);
+ }
+
+ return OK;
+}
#endif /* SAMA5_ADC_HAVE_CHANNELS */
/****************************************************************************
@@ -439,9 +510,16 @@ static int sam_adc_interrupt(int irq, void *context)
if ((pending & ADC_INT_EOCALL) != 0)
{
- /* Let the touchscreen handle its interrupts */
+ /* Schedule sampling to occur by the interrupt bottom half on the
+ * worker thread.
+ */
+
+ ret = sam_tsd_schedule(priv, pending);
+ if (ret < 0)
+ {
+ idbg("ERROR: sam_tsd_schedule failed: %d\n", ret);
+ }
- sam_adc_endconversion(pending);
pending &= ~ADC_INT_EOCALL;
}
#endif
@@ -649,6 +727,10 @@ struct adc_dev_s *sam_adc_initialize(void)
sam_configpio(PIO_ADC_TRG);
#endif
+ /* Initialize the ADC device data structure */
+
+ sem_init(&priv->exclsem, 0, 1);
+
#ifdef CONFIG_SAMA5_ADC_DMA
/* Allocate a DMA channel */
@@ -670,6 +752,44 @@ struct adc_dev_s *sam_adc_initialize(void)
}
/****************************************************************************
+ * Name: sam_adc_lock
+ *
+ * Description:
+ * Get exclusive access to the ADC interface
+ *
+ ****************************************************************************/
+
+void sam_adc_lock(FAR struct sam_adc_s *priv)
+{
+ int ret;
+
+ do
+ {
+ ret = sem_wait(&priv->exclsem);
+
+ /* This should only fail if the wait was canceled by an signal
+ * (and the worker thread will receive a lot of signals).
+ */
+
+ DEBUGASSERT(ret == OK || errno == EINTR);
+ }
+ while (ret < 0);
+}
+
+/****************************************************************************
+ * Name: sam_adc_unlock
+ *
+ * Description:
+ * Relinquish the lock on the ADC interface
+ *
+ ****************************************************************************/
+
+void sam_adc_unlock(FAR struct sam_adc_s *priv)
+{
+ sem_post(&priv->exclsem)
+}
+
+/****************************************************************************
* Name: sam_adc_getreg
*
* Description:
@@ -678,9 +798,8 @@ struct adc_dev_s *sam_adc_initialize(void)
****************************************************************************/
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
-static uint32_t sam_adc_getreg(ADC_HANDLE handle, uintptr_t address)
+static uint32_t sam_adc_getreg(struct sam_adc_s *priv, uintptr_t address)
{
- struct sam_adc_s *priv = (struct sam_adc_s *)handle;
uint32_t regval = getreg32(address);
if (sam_adc_checkreg(priv, false, regval, address))
@@ -701,10 +820,8 @@ static uint32_t sam_adc_getreg(ADC_HANDLE handle, uintptr_t address)
****************************************************************************/
#ifdef CONFIG_SAMA5_ADC_REGDEBUG
-void sam_adc_putreg(ADC_HANDLE handle, uintptr_t address, uint32_t regval)
+void sam_adc_putreg(struct sam_adc_s *priv, uintptr_t address, uint32_t regval)
{
- struct sam_adc_s *priv = (struct sam_adc_s *)handle;
-
if (sam_adc_checkreg(priv, true, regval, address))
{
lldbg("%08x<-%08x\n", address, regval);