/****************************************************************************
 * 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 Prototypes
 ****************************************************************************/

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 */
