summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/tiva/tiva_adclow.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/arch/arm/src/tiva/tiva_adclow.c')
-rw-r--r--nuttx/arch/arm/src/tiva/tiva_adclow.c1030
1 files changed, 1030 insertions, 0 deletions
diff --git a/nuttx/arch/arm/src/tiva/tiva_adclow.c b/nuttx/arch/arm/src/tiva/tiva_adclow.c
new file mode 100644
index 000000000..ede07693b
--- /dev/null
+++ b/nuttx/arch/arm/src/tiva/tiva_adclow.c
@@ -0,0 +1,1030 @@
+/****************************************************************************
+ * arch/arm/src/tiva/tiva_adclow.c
+ *
+ * Copyright (C) 2015 TRD2 Inc. All rights reserved.
+ * Author: Calvin Maguranis <calvin.maguranis@trd2inc.com>
+ *
+ * References:
+ *
+ * TM4C123GH6PM Series Data Sheet
+ * TI Tivaware driverlib ADC sample code.
+ *
+ * The Tivaware sample code has a BSD compatible license that requires this
+ * copyright notice:
+ *
+ * Copyright (c) 2005-2014 Texas Instruments Incorporated. All rights reserved.
+ * Software License Agreement
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Texas Instruments Incorporated nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This is part of revision 2.1.0.12573 of the Tiva Peripheral Driver Library.
+ *****************************************************************************/
+
+/* Keep in mind that for every step there should be another entry in the
+ * CONFIG_ADC_FIFOSIZE value.
+ * e.g. if there are 12 steps in use; CONFIG_ADC_FIFOSIZE = 12+1 = 13
+ * if there are 3 steps in use; CONFIG_ADC_FIFOSIZE = 3+1 = 4
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/analog/adc.h>
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+
+#include <arch/board/board.h>
+
+#include "up_arch.h"
+#include "up_internal.h"
+#include "tiva_gpio.h"
+#include "tiva_adc.h"
+#include "chip/tiva_adc.h"
+#include "chip/tiva_pinmap.h"
+#include "chip/tiva_syscontrol.h"
+
+#if defined (CONFIG_TIVA_ADC) && defined (CONFIG_ADC)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_TIVA_ADC_CLOCK
+# define CONFIG_TIVA_ADC_CLOCK TIVA_ADC_CLOCK_MIN
+#endif
+
+#ifdef CONFIG_TIVA_ADC_VREF
+# ifndef CONFIG_ARCH_CHIP_TM4C129
+# error Voltage reference selection only supported in TM4C129 parts
+# endif
+#endif
+
+#ifdef CONFIG_TIVA_ADC_ALT_CLK
+# warning CONFIG_TIVA_ADC_ALT_CLK unsupported.
+#endif
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+# error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
+#endif
+#ifndef CONFIG_SCHED_HPWORK
+# error High priority worker threads is required (CONFIG_SCHED_HPWORK)
+#endif
+
+/* PWM trigger support definitions ******************************************/
+
+/* Decodes the PWM generator and module from trigger and converts
+ * to the TSSEL_PS register
+ */
+
+#define ADC_TRIG_PWM_CFG(t) \
+ (1<<(ADC_TSSEL_PS_SHIFT(ADC_TRIG_gen(t))))
+
+/* ADC support definitions **************************************************/
+
+#define SSE_PROC_TRIG(n) (1 << (n))
+#define SEM_PROCESS_PRIVATE 0
+#define SEM_PROCESS_SHARED 1
+
+/* DEBUG ********************************************************************/
+
+#ifdef CONFIG_DEBUG_ANALOG
+#endif
+
+/****************************************************************************
+ * Public Functions
+ * **************************************************************************/
+
+/* Upper level ADC driver ***************************************************/
+
+static void tiva_adc_reset(struct adc_dev_s *dev);
+static int tiva_adc_setup(struct adc_dev_s *dev);
+static void tiva_adc_shutdown(struct adc_dev_s *dev);
+static void tiva_adc_rxint(struct adc_dev_s *dev, bool enable);
+static int tiva_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* ADC lower half device operations */
+
+static const struct adc_ops_s g_adcops =
+{
+ .ao_reset = tiva_adc_reset,
+ .ao_setup = tiva_adc_setup,
+ .ao_shutdown = tiva_adc_shutdown,
+ .ao_rxint = tiva_adc_rxint,
+ .ao_ioctl = tiva_adc_ioctl,
+};
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct tiva_adc_s
+{
+ struct adc_dev_s *dev;
+ bool cfg; /* Configuration state */
+ bool ena; /* Operation state */
+ uint8_t devno; /* ADC device number */
+};
+
+struct tiva_adc_sse_s
+{
+ sem_t exclsem; /* Mutual exclusion semaphore */
+ struct work_s work; /* Supports the interrupt handling "bottom half" */
+ bool cfg; /* Configuration state */
+ bool ena; /* Sample sequencer operation state */
+ uint8_t adc; /* Parent peripheral */
+ uint8_t num; /* SSE number */
+};
+
+/****************************************************************************
+ * Private Function Definitions
+ ****************************************************************************/
+
+static void tiva_adc_interrupt(struct tiva_adc_sse_s *sse);
+#ifdef CONFIG_DEBUG_ANALOG
+static void tiva_adc_runtimeobj_ptrs(void);
+static void tiva_adc_runtimeobj_vals(void);
+static void tiva_adc_dump_dev(void);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Run-time ADC objects */
+
+#ifdef CONFIG_TIVA_ADC0
+static struct adc_dev_s dev0;
+static struct tiva_adc_s adc0;
+
+static struct tiva_adc_sse_s sse00;
+static struct tiva_adc_sse_s sse01;
+static struct tiva_adc_sse_s sse02;
+static struct tiva_adc_sse_s sse03;
+#endif
+
+#ifdef CONFIG_TIVA_ADC1
+static struct adc_dev_s dev1;
+static struct tiva_adc_s adc1;
+
+static struct tiva_adc_sse_s sse10;
+static struct tiva_adc_sse_s sse11;
+static struct tiva_adc_sse_s sse12;
+static struct tiva_adc_sse_s sse13;
+#endif
+
+/* Offer run-time ADC objects in array form to help reduce the reliance on
+ * hard-coded, non-generic function calls.
+ */
+
+static struct adc_dev_s *g_devs[] =
+{
+#ifdef CONFIG_TIVA_ADC0
+ &dev0,
+#else
+ NULL,
+#endif
+#ifdef CONFIG_TIVA_ADC1
+ &dev1
+#else
+ NULL
+#endif
+};
+
+static struct tiva_adc_s *g_adcs[] =
+{
+#ifdef CONFIG_TIVA_ADC0
+ &adc0,
+#else
+ NULL,
+#endif
+#ifdef CONFIG_TIVA_ADC1
+ &adc1
+#else
+ NULL
+#endif
+};
+
+static struct tiva_adc_sse_s *g_sses[] =
+{
+#ifdef CONFIG_TIVA_ADC0
+ &sse00, &sse01, &sse02, &sse03,
+#else
+ NULL, NULL, NULL, NULL,
+#endif
+#ifdef CONFIG_TIVA_ADC1
+ &sse10, &sse11, &sse12, &sse13
+#else
+ NULL, NULL, NULL, NULL
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/* Interrupt handlers are inevitably hard-coded and specific */
+
+#ifdef CONFIG_TIVA_ADC0
+static int tiva_adc0_sse0_interrupt(int irq, void *context)
+{
+ tiva_adc_interrupt(&sse00);
+ return OK;
+}
+
+static int tiva_adc0_sse1_interrupt(int irq, void *context)
+{
+ tiva_adc_interrupt(&sse01);
+ return OK;
+}
+
+static int tiva_adc0_sse2_interrupt(int irq, void *context)
+{
+ tiva_adc_interrupt(&sse02);
+ return OK;
+}
+
+static int tiva_adc0_sse3_interrupt(int irq, void *context)
+{
+ tiva_adc_interrupt(&sse03);
+ return OK;
+}
+#endif
+
+#ifdef CONFIG_TIVA_ADC1
+static int tiva_adc1_sse0_interrupt(int irq, void *context)
+{
+ tiva_adc_interrupt(&sse10);
+ return OK;
+}
+
+static int tiva_adc1_sse1_interrupt(int irq, void *context)
+{
+ tiva_adc_interrupt(&sse11);
+ return OK;
+}
+
+static int tiva_adc1_sse2_interrupt(int irq, void *context)
+{
+ tiva_adc_interrupt(&sse12);
+ return OK;
+}
+
+static int tiva_adc1_sse3_interrupt(int irq, void *context)
+{
+ tiva_adc_interrupt(&sse13);
+ return OK;
+}
+#endif
+
+static void tiva_adc_irqinitialize(struct tiva_adc_cfg_s *cfg)
+{
+ avdbg("initialize irqs for ADC%d...\n", cfg->adc);
+
+#ifdef CONFIG_TIVA_ADC0
+ if (cfg->adc == 0)
+ {
+ if (cfg->sse[0] && (cfg->ssecfg[0].trigger > 0))
+ {
+ tiva_adc_irq_attach(0, 0, tiva_adc0_sse0_interrupt);
+ }
+
+ if (cfg->sse[1] && (cfg->ssecfg[1].trigger > 0))
+ {
+ tiva_adc_irq_attach(0, 1, tiva_adc0_sse1_interrupt);
+ }
+
+ if (cfg->sse[2] && (cfg->ssecfg[2].trigger > 0))
+ {
+ tiva_adc_irq_attach(0, 2, tiva_adc0_sse2_interrupt);
+ }
+
+ if (cfg->sse[3] && (cfg->ssecfg[3].trigger > 0))
+ {
+ tiva_adc_irq_attach(0, 3, tiva_adc0_sse3_interrupt);
+ }
+ }
+#endif
+#ifdef CONFIG_TIVA_ADC1
+ if (cfg->adc == 1)
+ {
+ if (cfg->sse[0] && (cfg->ssecfg[0].trigger > 0))
+ {
+ tiva_adc_irq_attach(1, 0, tiva_adc1_sse0_interrupt);
+ }
+
+ if (cfg->sse[1] && (cfg->ssecfg[1].trigger > 0))
+ {
+ tiva_adc_irq_attach(1, 1, tiva_adc1_sse1_interrupt);
+ }
+
+ if (cfg->sse[2] && (cfg->ssecfg[2].trigger > 0))
+ {
+ tiva_adc_irq_attach(1, 2, tiva_adc1_sse2_interrupt);
+ }
+
+ if (cfg->sse[3] && (cfg->ssecfg[3].trigger > 0))
+ {
+ tiva_adc_irq_attach(1, 3, tiva_adc1_sse3_interrupt);
+ }
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: tiva_adc_reset
+ *
+ * Description:
+ * Reset the ADC device. Called early to initialize the hardware. This is
+ * called before tiva_adc_setup() and on error conditions.
+ *
+ * Resetting disables interrupts and the enabled SSEs for the ADC.
+ *
+ ****************************************************************************/
+
+static void tiva_adc_reset(struct adc_dev_s *dev)
+{
+ avdbg("Resetting...\n");
+
+ struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
+ struct tiva_adc_sse_s *sse;
+ uint8_t s;
+
+ tiva_adc_rxint(dev, false);
+
+ for (s = 0; s < 4; ++s)
+ {
+ sse = g_sses[SSE_IDX(priv->devno, s)];
+
+ if (sse->ena == true)
+ {
+ tiva_adc_sse_enable(priv->devno, s, false);
+ sse->ena = false;
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: tiva_adc_setup
+ *
+ * Description:
+ * Configure the ADC. This method is called the first time that the ADC
+ * device is opened. This will occur when the port is first opened.
+ * Interrupts are all disabled upon return.
+ *
+ ****************************************************************************/
+
+static int tiva_adc_setup(struct adc_dev_s *dev)
+{
+ avdbg("Setup\n");
+
+ struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
+ struct tiva_adc_sse_s *sse;
+ uint8_t s = 0;
+
+ priv->ena = true;
+
+ for (s = 0; s < 4; ++s)
+ {
+ sse = g_sses[SSE_IDX(priv->devno, s)];
+ if (sse->cfg == true)
+ {
+ tiva_adc_sse_enable(priv->devno, s, true);
+ sse->ena = true;
+ }
+ }
+
+ tiva_adc_rxint(dev, false);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: tiva_adc_shutdown
+ *
+ * Description:
+ * Disable the ADC. This method is called when the ADC device is closed.
+ * This method reverses the operation the setup method.
+ *
+ ****************************************************************************/
+
+static void tiva_adc_shutdown(struct adc_dev_s *dev)
+{
+ struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
+ avdbg("Shutdown\n");
+
+ DEBUGASSERT(priv->ena);
+
+ /* Resetting the ADC peripheral disables interrupts and all SSEs */
+
+ tiva_adc_reset(dev);
+
+ /* Currently all of the setup operations are undone in reset() */
+
+#if 0
+ struct tiva_adc_sse_s *sse;
+ uint8_t s = 0;
+
+ for (s=0; s<4; ++s)
+ {
+ }
+#endif
+
+ priv->ena = false;
+}
+
+/****************************************************************************
+ * Name: tiva_adc_rxint
+ *
+ * Description:
+ * Call to enable or disable RX interrupts
+ *
+ * Input Parameters:
+ * enable - the enable state of interrupts for this device
+ *
+ ****************************************************************************/
+
+static void tiva_adc_rxint(struct adc_dev_s *dev, bool enable)
+{
+ avdbg("RXINT=%d\n", enable);
+
+ struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
+ struct tiva_adc_sse_s *sse;
+ uint32_t trigger;
+ uint8_t s = 0;
+
+ DEBUGASSERT(priv->ena);
+
+ for (s=0; s<4; ++s)
+ {
+ trigger = tiva_adc_get_trigger(priv->devno, s);
+ sse = g_sses[SSE_IDX(priv->devno, s)];
+ if ((sse->ena == true)
+ && (trigger > 0))
+ {
+ tiva_adc_sse_int_enable(priv->devno, s, enable);
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: tiva_adc_ioctl
+ *
+ * Description:
+ * All ioctl calls will be routed through this method.
+ *
+ * Input Parameters:
+ * cmd - ADC ioctl command
+ * arg - argument for the ioctl command
+ *
+ * Returned Value:
+ * Non negative value on success; negative value on failure.
+ *
+ ****************************************************************************/
+
+static int tiva_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg)
+{
+ int ret = OK;
+
+ avdbg("cmd=%d arg=%ld\n", cmd, arg);
+
+ switch (cmd)
+ {
+ /* Software trigger */
+
+ case ANIOC_TRIGGER:
+ {
+ struct tiva_adc_s *priv = (struct tiva_adc_s *)dev->ad_priv;
+ uint8_t i = 0;
+ uint8_t fifo_count = 0;
+ uint8_t sse = (uint8_t) arg;
+ int32_t buf[8];
+
+ /* Get exclusive access to the driver data structure */
+
+ tiva_adc_lock(priv, sse);
+
+ /* Start conversion and wait for end of conversion */
+
+ tiva_adc_proc_trig(priv->devno, (uint8_t)SSE_PROC_TRIG(sse));
+ while (!tiva_adc_sse_int_status(priv->devno, sse))
+ {
+ usleep(100);
+ }
+
+ tiva_adc_sse_clear_int(priv->devno, sse);
+
+ /* Pass sampled data to upper ADC driver */
+
+ fifo_count = tiva_adc_sse_data(priv->devno, sse, buf);
+
+ for (i=0; i<fifo_count; ++i)
+ {
+ (void)adc_receive(dev,
+ tiva_adc_get_ain(priv->devno, sse, i),
+ buf[i]);
+ }
+
+ /* Release our lock on the ADC structure */
+
+ tiva_adc_unlock(priv, sse);
+ }
+ break;
+
+ /* PWM triggering */
+
+#warning Missing Logic
+
+ /* TODO: Needs to be tested */
+
+#ifdef CONFIG_EXPERIMENTAL
+ case TIVA_ADC_PWM_TRIG_IOCTL:
+ {
+ uint8_t sse = (uint8_t)(arg & 0x2);
+ uint8_t regval = tiva_adc_get_trigger(adc, sse);
+
+ /* Verify input SSE trigger is a PWM trigger */
+
+ if ((regval & TIVA_ADC_TRIG_PWM0) ||
+ (regval & TIVA_ADC_TRIG_PWM1) ||
+ (regval & TIVA_ADC_TRIG_PWM2) ||
+ (regval & TIVA_ADC_TRIG_PWM3))
+ {
+ tiva_adc_sse_pwm_trig(adc, sse, (uint32_t)(arg&0xFFFFFFFC));
+ }
+ }
+ break;
+#endif
+
+#warning Missing Logic
+
+ /* Unsupported or invalid command */
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: tiva_adc_read
+ *
+ * Description:
+ * This function executes on the worker thread. It is scheduled by
+ * tiva_adc_interrupt whenever any enabled event occurs. All interrupts
+ * are disabled when this function runs. tiva_adc_read will
+ * re-enable interrupts when it completes processing all pending events.
+ *
+ * Input Parameters
+ * arg - The ADC SSE data structure cast to (void *)
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void tiva_adc_read(void *arg)
+{
+ struct tiva_adc_sse_s *sse = (struct tiva_adc_sse_s *)arg;
+ struct adc_dev_s *dev = 0;
+ int irq = tiva_adc_getirq(sse->adc, sse->num);
+ uint8_t i = 0;
+ uint8_t fifo_count = 0;
+ int32_t buf[8];
+
+ /* Get exclusive access to the driver data structure */
+
+ tiva_adc_lock(g_adcs[sse->adc], sse->num);
+
+ /* Get sampled data */
+
+ fifo_count = tiva_adc_sse_data(sse->adc, sse->num, buf);
+
+ /* Determine which adc_dev_s we need */
+
+ dev = g_devs[sse->adc];
+ if (dev == NULL)
+ {
+ /* This is a serious error: indicates invalid pointer indirection
+ * and should cause a full system stop.
+ */
+ alldbg("PANIC!!! Invalid ADC device number given %d\n", sse->adc);
+ PANIC();
+ return;
+ }
+
+ for (i=0; i<fifo_count; ++i)
+ {
+ (void)adc_receive(dev,
+ tiva_adc_get_ain(sse->adc, sse->num, i),
+ buf[i]);
+ avdbg("AIN%d=0x%04x\n",
+ tiva_adc_get_ain(sse->adc, sse->num, i), buf[i]);
+ }
+
+ /* Exit, re-enabling ADC interrupts */
+
+ up_enable_irq(irq);
+
+ /* Release our lock on the ADC structure */
+
+ tiva_adc_unlock(g_adcs[sse->adc], sse->num);
+}
+
+/****************************************************************************
+ * Name: tiva_adc_interrupt
+ *
+ * Description:
+ * This function is called by every interrupt handler and handles the
+ * actual worker dispatching.
+ *
+ ****************************************************************************/
+
+static void tiva_adc_interrupt(struct tiva_adc_sse_s *sse)
+{
+ int ret;
+ int irq = tiva_adc_getirq(sse->adc, sse->num);
+
+ DEBUGASSERT(sse->ena == true);
+
+ /* disable further interrupts. Interrupts will be re-enabled
+ * after the worker thread executes.
+ */
+
+ up_disable_irq(irq);
+
+ /* Clear interrupt status */
+
+ tiva_adc_sse_clear_int(sse->adc, sse->num);
+
+ /* Transfer processing to the worker thread. Since interrupts are
+ * disabled while the work is pending, no special action should be
+ * required to protected the work queue.
+ */
+
+ DEBUGASSERT(sse->work.worker == NULL);
+ ret = work_queue(HPWORK, &sse->work, tiva_adc_read, sse, 0);
+ if (ret != 0)
+ {
+ adbg("ERROR: Failed to queue work: %d ADC.SSE: %d.%d\n",
+ ret, sse->adc, sse->num);
+ }
+}
+
+/****************************************************************************
+ * Name: tiva_adc_struct_init
+ *
+ * Description:
+ * Initialize public and private adc structures their member SSE's.
+ *
+ ****************************************************************************/
+
+static struct tiva_adc_s *tiva_adc_struct_init(struct tiva_adc_cfg_s *cfg)
+{
+ struct tiva_adc_s *adc = g_adcs[cfg->adc];
+ struct tiva_adc_sse_s *sse = 0;
+ uint8_t s = 0;
+
+ /* Do not re-initialize the run-time structures, there is a chance another
+ * process is also using this ADC.
+ */
+
+ if (adc->cfg == true)
+ {
+ goto tiva_adc_struct_init_ok;
+ }
+ else
+ {
+ if (adc != NULL)
+ {
+ adc->ena = false;
+ adc->devno = cfg->adc;
+
+ for (s=0; s<4; s++)
+ {
+
+ /* Only configure selected SSEs */
+
+ if (cfg->sse[s])
+ {
+ sse = g_sses[SSE_IDX(cfg->adc, s)];
+
+ if (sse != NULL)
+ {
+ sse->adc = cfg->adc;
+ sse->num = s;
+ sem_init(&sse->exclsem, SEM_PROCESS_PRIVATE, 1);
+ sse->ena = false;
+ sse->cfg = true;
+ }
+ else
+ {
+ goto tiva_adc_struct_init_error;
+ }
+ }
+ }
+
+ /* Initialize the public ADC device data structure */
+
+ adc->dev = g_devs[cfg->adc];
+ if (adc->dev != NULL)
+ {
+ adc->dev->ad_ops = &g_adcops;
+ adc->dev->ad_priv = adc;
+ }
+ else
+ {
+ goto tiva_adc_struct_init_error;
+ }
+ goto tiva_adc_struct_init_ok;
+ }
+ else
+ {
+ goto tiva_adc_struct_init_error;
+ }
+ }
+
+tiva_adc_struct_init_error:
+ avdbg("Invalid ADC device number: expected=%d actual=%d\n",
+ 0, cfg->adc);
+ avdbg("ADC%d (CONFIG_TIVA_ADC%d) must be enabled in Kconfig first!",
+ cfg->adc, cfg->adc);
+ return NULL;
+
+tiva_adc_struct_init_ok:
+ adc->cfg = true;
+ return adc;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tiva_adc_initialize
+ *
+ * Description:
+ * Configuration and bind the ADC to the ADC lower half instance and
+ * register the ADC driver at 'devpath'.
+ *
+ * Input Parameters:
+ * devpath - The full path to the ADC device. This should be of the
+ * form /dev/adc0
+ * cfg - ADC configuration structure, configures the whole ADC.
+ * clock - clock speed for all ADC's. This is only set once for the first
+ * call to tiva_adc_initialize, otherwise the values are ignored.
+ * sample_rate - maximum sample rate of any ADC. This is only set once
+ * for the first call to tiva_adc_initialize, otherwise the values are
+ * ignored.
+ *
+ ****************************************************************************/
+
+int tiva_adc_initialize(const char *devpath, struct tiva_adc_cfg_s *cfg,
+ uint32_t clock, uint8_t sample_rate)
+{
+ struct tiva_adc_s *adc;
+ int ret = 0;
+
+ avdbg("initializing...\n");
+
+ /* Initialize the private ADC device data structure */
+
+ adc = tiva_adc_struct_init(cfg);
+ if (adc == NULL)
+ {
+ adbg("Invalid ADC device number: expected=%d actual=%d\n",
+ 0, cfg->adc);
+ return -ENODEV;
+ }
+
+ /* Turn on peripheral */
+
+ if (tiva_adc_enable(adc->devno, true) < 0)
+ {
+ adbg("ERROR: failure to power ADC peripheral (devno=%d)\n",
+ cfg->adc);
+ return ret;
+ }
+
+ /* Perform actual initialization */
+
+ tiva_adc_one_time_init(clock, sample_rate);
+ tiva_adc_configure(cfg);
+ tiva_adc_irqinitialize(cfg);
+
+ /* Now we are initialized */
+
+ adc->ena = true;
+
+#ifdef CONFIG_DEBUG_ANALOG
+ tiva_adc_runtimeobj_vals();
+#endif
+
+ /* Ensure our lower half is valid */
+
+ if (adc->dev == NULL)
+ {
+ adbg("ERROR: Failed to get interface %s\n", devpath);
+ return -ENODEV;
+ }
+
+ avdbg("adc_dev_s=0x%08x\n", adc->dev);
+
+ /* Register the ADC driver */
+
+ avdbg("Register the ADC driver at %s\n", devpath);
+
+ ret = adc_register(devpath, adc->dev);
+ if (ret < 0)
+ {
+ adbg("ERROR: Failed to register %s to character driver: %d\n",
+ devpath, ret);
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: tiva_adc_lock
+ *
+ * Description:
+ * Get exclusive access to the ADC interface
+ *
+ ****************************************************************************/
+
+void tiva_adc_lock(FAR struct tiva_adc_s *priv, int sse)
+{
+ avdbg("Locking...\n");
+
+ struct tiva_adc_sse_s *s = g_sses[SSE_IDX(priv->devno, sse)];
+ int ret;
+#ifdef CONFIG_DEBUG_ANALOG
+ uint16_t loop_count=0;
+#endif
+
+ do
+ {
+ ret = sem_wait(&s->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);
+
+#ifdef CONFIG_DEBUG_ANALOG
+ if (loop_count % 1000)
+ {
+ avdbg("loop=%d\n");
+ }
+ ++loop_count;
+#endif
+ }
+ while (ret < 0);
+}
+
+/****************************************************************************
+ * Name: tiva_adc_unlock
+ *
+ * Description:
+ * Relinquish the lock on the ADC interface
+ *
+ ****************************************************************************/
+
+void tiva_adc_unlock(FAR struct tiva_adc_s *priv, int sse)
+{
+ avdbg("Unlocking\n");
+ struct tiva_adc_sse_s *s = g_sses[SSE_IDX(priv->devno, sse)];
+ sem_post(&s->exclsem);
+}
+
+/* DEBUG ********************************************************************/
+
+#ifdef CONFIG_DEBUG_ANALOG
+
+/****************************************************************************
+ * Name: tiva_adc_runtimeobj_ptrs
+ *
+ * Description:
+ * Dumps the address of all run-time objects for verification.
+ *
+ ****************************************************************************/
+
+static void tiva_adc_runtimeobj_ptrs(void)
+{
+# ifdef CONFIG_TIVA_ADC0
+ avdbg("ADC0 [struct] [global value] [array value]\n");
+ avdbg(" adc_dev_s dev0=0x%08x g_devs[0]=0x%08x\n", &dev0, g_devs[0]);
+ avdbg(" tiva_adc_s adc0=0x%08x g_adcs[0]=0x%08x\n", &adc0, g_adcs[0]);
+ avdbg(" tiva_adc_sse_s sse0=0x%08x g_sses[0,0]=0x%08x\n", &sse00, g_sses[SSE_IDX(0,0)]);
+ avdbg(" tiva_adc_sse_s sse1=0x%08x g_sses[0,1]=0x%08x\n", &sse01, g_sses[SSE_IDX(0,1)]);
+ avdbg(" tiva_adc_sse_s sse2=0x%08x g_sses[0,2]=0x%08x\n", &sse02, g_sses[SSE_IDX(0,2)]);
+ avdbg(" tiva_adc_sse_s sse3=0x%08x g_sses[0,3]=0x%08x\n", &sse03, g_sses[SSE_IDX(0,3)]);
+# endif
+# ifdef CONFIG_TIVA_ADC1
+ avdbg("ADC1 [struct] [global value] [array value]\n");
+ avdbg(" adc_dev_s dev1=0x%08x g_devs[1]=0x%08x\n", &dev1, g_devs[1]);
+ avdbg(" tiva_adc_s adc1=0x%08x g_adcs[1]=0x%08x\n", &adc1, g_adcs[1]);
+ avdbg(" tiva_adc_sse_s sse0=0x%08x g_sses[1,0]=0x%08x\n", &sse10, g_sses[SSE_IDX(1,0)]);
+ avdbg(" tiva_adc_sse_s sse1=0x%08x g_sses[1,1]=0x%08x\n", &sse11, g_sses[SSE_IDX(1,1)]);
+ avdbg(" tiva_adc_sse_s sse2=0x%08x g_sses[1,2]=0x%08x\n", &sse12, g_sses[SSE_IDX(1,2)]);
+ avdbg(" tiva_adc_sse_s sse3=0x%08x g_sses[1,3]=0x%08x\n", &sse13, g_sses[SSE_IDX(1,3)]);
+# endif
+}
+
+static void tiva_adc_runtimeobj_vals(void)
+{
+ struct tiva_adc_sse_s *sse;
+ uint8_t s;
+# ifdef CONFIG_TIVA_ADC0
+ avdbg("ADC0 [0x%08x] cfg=%d ena=%d devno=%d\n",
+ &adc0, adc0.cfg, adc0.ena, adc0.devno);
+ for (s=0; s<4; ++s)
+ {
+ sse = g_sses[SSE_IDX(0, s)];
+ avdbg("SSE%d [0x%08x] adc=%d cfg=%d ena=%d num=%d\n",
+ s, sse, sse->adc, sse->cfg, sse->ena, sse->num);
+ }
+# endif
+# ifdef CONFIG_TIVA_ADC1
+ avdbg("ADC1 [0x%08x] cfg=%d ena=%d devno=%d\n",
+ &adc1, adc1.cfg, adc1.ena, adc1.devno);
+ for (s=0; s<4; ++s)
+ {
+ sse = g_sses[SSE_IDX(1, s)];
+ avdbg("SSE%d [0x%08x] adc=%d cfg=%d ena=%d num=%d\n",
+ s, sse, sse->adc, sse->cfg, sse->ena, sse->num);
+ }
+# endif
+}
+
+/****************************************************************************
+ * Name: tiva_adc_unlock
+ *
+ * Description:
+ * umps the device level objects for verification.
+ *
+ ****************************************************************************/
+
+static void tiva_adc_dump_dev(void)
+{
+# ifdef CONFIG_TIVA_ADC0
+ avdbg("adc_ops_s g_adcops=0x%08x adc0.dev->ad_ops=0x%08x\n",
+ &g_adcops, adc0.dev->ad_ops);
+ avdbg("tiva_adc_s adc0=0x%08x adc0.dev->ad_priv=0x%08x\n",
+ &adc0, adc0.dev->ad_priv);
+# endif
+# ifdef CONFIG_TIVA_ADC1
+ avdbg("adc_ops_s g_adcops=0x%08x adc1.dev->ad_ops=0x%08x\n",
+ &g_adcops, adc1.dev->ad_ops);
+ avdbg("tiva_adc_s adc1=0x%08x adc1.dev->ad_priv=0x%08x\n",
+ &adc1, adc1.dev->ad_priv);
+# endif
+}
+#endif
+
+#endif /* CONFIG_TIVA_ADC && CONFIG_ADC */