#ifndef QADC_C
#define QADC_C

//----------------------------------
// Includes and forward declarations
//----------------------------------
#include "qadc.h"
#include <cyg/io/adc.h>
#include <pkgconf/io_adc.h>
#include <pkgconf/system.h>

//----------------------
// Define some constants
//----------------------
// The following define a QSCLK of 2MHz, meaning 7 uSeconds conversion time
#define PSH                11
#define PSL                 7

#define SOFT_CONTINUOUS  0x11
#define IST                 0          // 2 Clockcycles
#define BYP                 0          // do not enable Bypass mode
#define EOQ                63          
#define PAUSE               0          // Never pause the queue

#define VRL                60
#define VRH                61
#define VRMID              62

//--------------------
// Function prototypes
//--------------------
static bool qadc64_adc_init(struct cyg_devtab_entry * a_tab);
static Cyg_ErrNo qadc64_adc_lookup(struct cyg_devtab_entry ** a_tab,
                                   struct cyg_devtab_entry * a_sub_tab,
                                   const char * a_name);
static double qadc64_adc_read(adc_channel * a_channel);
static bool qadc64_adc_reset(adc_channel * a_channel);

//------------------------------------
// Register the driver with the kernel
//------------------------------------
static ADC_FUNS(qadc64_adc_funs,
                qadc64_adc_read,
                qadc64_adc_reset);

static cyg_uint16 channel_selector[] = 
{ 0,    // Not used
  0,    // Channel  1 
  1,    // Channel  2
  2,    // Channel  3
  3,    // Channel  4
  48,   // Channel  5
  49,   // Channel  6
  50,   // Channel  7
  51,   // Channel  8
  52,   // Channel  9
  53,   // Channel 10
  54,   // Channel 11
  55,   // Channel 12
  56,   // Channel 13
  57,   // Channel 14
  58,   // Channel 15
  59    // Channel 16
};

#ifdef CYGPKG_IO_ADC_QADC_EC555_ADC_A
static qadc64_adc_t qadc64_adc_a = {
  (qadc64_port_t *)CYGDAT_IO_QADC_EC555_BASE_ADDRESS,
  0,
  CYGDAT_IO_ADC_QADC_EC555_ADC_A_CHANNEL
};

ADC_CHANNEL(qadc64_adc_channela,
            qadc64_adc_funs,
            &qadc64_adc_a,
            CYGDAT_IO_ADC_QADC_EC555_ADC_A_SPAN,
            CYGDAT_IO_ADC_QADC_EC555_ADC_A_SCALE,
            CYGDAT_IO_ADC_QADC_EC555_ADC_A_OFFSET);

DEVTAB_ENTRY(qadc64_io_dac0,
             CYGDAT_IO_ADC_QADC_EC555_ADC_A_NAME,
             0,
             &adc_devio,
             qadc64_adc_init,
             qadc64_adc_lookup,
             &qadc64_adc_channela);
#endif

#ifdef CYGPKG_IO_ADC_QADC_EC555_ADC_B
static qadc64_adc_t qadc64_adc_b = {
  (qadc64_port_t *)CYGDAT_IO_QADC_EC555_BASE_ADDRESS,
  0,
  CYGDAT_IO_ADC_QADC_EC555_ADC_B_CHANNEL
};

ADC_CHANNEL(qadc64_adc_channelb,
            qadc64_adc_funs,
            &qadc64_adc_b,
            CYGDAT_IO_ADC_QADC_EC555_ADC_B_SPAN,
            CYGDAT_IO_ADC_QADC_EC555_ADC_B_SCALE,
            CYGDAT_IO_ADC_QADC_EC555_ADC_B_OFFSET);

DEVTAB_ENTRY(qadc64_io_dac1,
             CYGDAT_IO_ADC_QADC_EC555_ADC_B_NAME,
             0,
             &adc_devio,
             qadc64_adc_init,
             qadc64_adc_lookup,
             &qadc64_adc_channelb);
#endif

#ifdef CYGPKG_IO_ADC_QADC_EC555_ADC_C
static qadc64_adc_t qadc64_adc_c = {
  (qadc64_port_t *)CYGDAT_IO_QADC_EC555_BASE_ADDRESS,
  0,
  CYGDAT_IO_ADC_QADC_EC555_ADC_C_CHANNEL
};

ADC_CHANNEL(qadc64_adc_channelc,
            qadc64_adc_funs,
            &qadc64_adc_c,
            CYGDAT_IO_ADC_QADC_EC555_ADC_C_SPAN,
            CYGDAT_IO_ADC_QADC_EC555_ADC_C_SCALE,
            CYGDAT_IO_ADC_QADC_EC555_ADC_C_OFFSET);

DEVTAB_ENTRY(qadc64_io_dac2,
             CYGDAT_IO_ADC_QADC_EC555_ADC_C_NAME,
             0,
             &adc_devio,
             qadc64_adc_init,
             qadc64_adc_lookup,
             &qadc64_adc_channelc);
#endif

#ifdef CYGPKG_IO_ADC_QADC_EC555_ADC_D
static qadc64_adc_t qadc64_adc_d = {
  (qadc64_port_t *)CYGDAT_IO_QADC_EC555_BASE_ADDRESS,
  0,
  CYGDAT_IO_ADC_QADC_EC555_ADC_D_CHANNEL
};

ADC_CHANNEL(qadc64_adc_channeld,
            qadc64_adc_funs,
            &qadc64_adc_d,
            CYGDAT_IO_ADC_QADC_EC555_ADC_D_SPAN,
            CYGDAT_IO_ADC_QADC_EC555_ADC_D_SCALE,
            CYGDAT_IO_ADC_QADC_EC555_ADC_D_OFFSET);

DEVTAB_ENTRY(qadc64_io_dac3,
             CYGDAT_IO_ADC_QADC_EC555_ADC_D_NAME,
             0,
             &adc_devio,
             qadc64_adc_init,
             qadc64_adc_lookup,
             &qadc64_adc_channeld);
#endif

//-------------------------
// Functions implementation
//-------------------------
// qadc64_adc_init
static bool qadc64_adc_init(struct cyg_devtab_entry * a_tab)
{
  adc_channel * channel = (adc_channel *)a_tab->priv;
  qadc64_adc_t * adc = (qadc64_adc_t *)channel->m_dev_priv;
  qadc64_port_t * qadc64_port = adc->port;

  int idx = 0;
  for(idx = 0; idx < 64; idx++)
    qadc64_port->ccw[idx] = EOQ;  // This will be convenient later on

  qadc64_port->qacr0 = 0;         // Internally mux'ed, do not look at TRG
  qadc64_port->qacr0 |= PSL;
  qadc64_port->qacr0 |= (PSH << 4);

  qadc64_port->qacr1 = 0;         // Do not use any interrupts on queue 1
  qadc64_port->qacr1 |= (SOFT_CONTINUOUS << 8);

  qadc64_port->qacr2 = 0;         // Do not use any interrupts on queue 2
  qadc64_port->qacr2 |= 65;       // Don't use queue 2 at all

  return true;
}

// qadc64_adc_lookup
static Cyg_ErrNo qadc64_adc_lookup(struct cyg_devtab_entry ** a_tab,
                                   struct cyg_devtab_entry * a_sub_tab,
                                   const char * a_name)
{
  adc_channel * channel = (adc_channel *)(*a_tab)->priv;
  qadc64_adc_t * adc = (qadc64_adc_t *)channel->m_dev_priv;
  qadc64_port_t * qadc64_port = adc->port;

  adc->queuepos = 0;
  while((qadc64_port->ccw[adc->queuepos] & 0x3f) != EOQ)
    adc->queuepos++;

  qadc64_port->ccw[adc->queuepos] = channel_selector[adc->channel];

  return ENOERR;
}

// qadc64_ac_read
// Return a value between -1.0 and 1.0
static double qadc64_adc_read(adc_channel * a_channel)
{
  qadc64_adc_t * adc = (qadc64_adc_t *)a_channel->m_dev_priv;
  qadc64_port_t * qadc64_port = adc->port;
 
  return (qadc64_port->ljsrr[adc->queuepos] / 32704.0);   // The quadc knows it has 10bits resolution
}

// qadc64_adc_reset
static bool qadc64_adc_reset(adc_channel * a_channel)
{
  return true;
}

#endif // QADC_C
