#ifndef TPU_C
#define TPU_C

//----------------------------------
// Includes and forward declarations
//----------------------------------
#include "tpu.h"
#include <cyg/io/encoder.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_arbiter.h>
#include <pkgconf/io_encoder.h>
#include <pkgconf/system.h>

#define TPU_MCR 0x2260
#define FQD_CODE  0x06
#define NITC_CODE 0x0a

// This code is re-usable "as is" on the cme-555 and other mpc555 based boards.
// It makes however sense to rewrite it a little bit and shift the channels 
// to use in fqd mode by one (i.e. 2-3 instead of 1-2). That yields another extra 
// channel before the fqd channels. This extra channel can then be used for
// implementing the probe function (i.e.) latching something else than the index
// pulse. The code is already provided.

//--------------------
// Function prototypes
//--------------------
static bool tpu3_encoder_init(struct cyg_devtab_entry * a_tab);
static Cyg_ErrNo tpu3_encoder_lookup(struct cyg_devtab_entry ** a_tab,
                                     struct cyg_devtab_entry * a_sub_tab,
                                     const char * a_name);
static bool tpu3_encoder_reset(encoder_channel * a_channel);
static cyg_uint16 tpu3_encoder_read_position(encoder_channel * a_channel);
static void tpu3_encoder_latch_index_pulse(encoder_channel * a_channel);
static void tpu3_encoder_latch_probe_pulse(encoder_channel * a_channel);
static void tpu3_encoder_stop_latch(encoder_channel * a_channel);

static cyg_uint32 tpu3_encoder_index_pulse_ISR(cyg_vector_t a_vector, 
                                               cyg_addrword_t a_data);
static void       tpu3_encoder_index_pulse_DSR(cyg_vector_t a_vector, 
                                               cyg_ucount32 a_count, 
					       cyg_addrword_t a_data);
static cyg_uint32 tpu3_encoder_probe_pulse_ISR(cyg_vector_t a_vector, 
                                               cyg_addrword_t a_data);
static void       tpu3_encoder_probe_pulse_DSR(cyg_vector_t a_vector, 
                                               cyg_ucount32 a_count, 
					       cyg_addrword_t a_data);

//-----------------------------------
//Register the driver with the Kernel
//-----------------------------------
static ENCODER_FUNS(tpu3_encoder_funs,
                    tpu3_encoder_reset,
                    tpu3_encoder_latch_index_pulse,
                    tpu3_encoder_latch_probe_pulse,
                    tpu3_encoder_stop_latch,
                    tpu3_encoder_read_position);

#ifdef CYGPKG_IO_ENCODER_TPU_EC555_ENCODER_A
static tpu3_encoder_t tpu3_encoder_a = {
  (tpu3_port_t *)CYGDAT_IO_TPU_EC555_A_BASE_ADDRESS,
  CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_A_CHANNEL,
  CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0 + CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_A_CHANNEL,
  CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0_PRIORITY,
  CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0 + CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_A_CHANNEL + 1,
  CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0_PRIORITY,
};

ENCODER_CHANNEL(tpu3_encoder_channela,
                tpu3_encoder_funs,
                &tpu3_encoder_a,
                CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_A_COUNT);

DEVTAB_ENTRY(tpu3_encoderio0,
             CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_A_NAME,
             0,
             &encoder_devio,
             tpu3_encoder_init,
             tpu3_encoder_lookup,
             &tpu3_encoder_channela);
#endif

#ifdef CYGPKG_IO_ENCODER_TPU_EC555_ENCODER_B
static tpu3_encoder_t tpu3_encoder_b = {
  (tpu3_port_t *)CYGDAT_IO_TPU_EC555_B_BASE_ADDRESS,
  CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_B_CHANNEL,
  CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0 + CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_B_CHANNEL,
  CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0_PRIORITY,
  CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0 + CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_B_CHANNEL + 1,
  CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0_PRIORITY,
};

ENCODER_CHANNEL(tpu3_encoder_channelb,
                tpu3_encoder_funs,
                &tpu3_encoder_b,
                CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_B_COUNT);
DEVTAB_ENTRY(tpu3_encoderio1,
             CYGDAT_IO_ENCODER_TPU_EC555_ENCODER_B_NAME,
             0,
             &encoder_devio,
             tpu3_encoder_init,
             tpu3_encoder_lookup,
             &tpu3_encoder_channelb);
#endif

//------------------
// Arbitration isr's
//------------------
#ifdef CYGPKG_IO_ENCODER_TPU_EC555_ENCODER_A
static cyg_uint32 hal_arbitration_isr_tpu3_a(cyg_vector_t a_vector, cyg_addrword_t a_data)
{
  // We have to check them all. Could be that four encoders are implemented on the same port
  // Not very likely though ....
  tpu3_port_t * tpu_ptr = (tpu3_port_t *)(CYGDAT_IO_TPU_EC555_A_BASE_ADDRESS);

  cyg_uint16 isr_src = tpu_ptr->cisr & tpu_ptr->cier;

  if(isr_src & 0x8000)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH15);
  if(isr_src & 0x4000)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH14);
  if(isr_src & 0x2000)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH13);
  if(isr_src & 0x1000)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH12);
  if(isr_src & 0x0800)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH11);
  if(isr_src & 0x0400)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH10);
  if(isr_src & 0x0200)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH9);
  if(isr_src & 0x0100)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH8);
  if(isr_src & 0x0080)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH7);
  if(isr_src & 0x0040)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH6);
  if(isr_src & 0x0020)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH5);
  if(isr_src & 0x0010)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH4);
  if(isr_src & 0x0008)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH3);
  if(isr_src & 0x0004)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH2);
  if(isr_src & 0x0002)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH1);
  if(isr_src & 0x0001)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUA_CH0);

  return 0;
}
#endif

#ifdef CYGPKG_IO_ENCODER_TPU_EC555_ENCODER_B
static cyg_uint32 hal_arbitration_isr_tpu3_b(cyg_vector_t a_vector, cyg_addrword_t a_data)
{
  // We have to check them all. Could be that four encoders are implemented on the same port
  // Not very likely though ....
  tpu3_port_t * tpu_ptr = (tpu3_port_t *)(CYGDAT_IO_TPU_EC555_B_BASE_ADDRESS);

  cyg_uint16 isr_src = tpu_ptr->cisr & tpu_ptr->cier;

  if(isr_src & 0x8000)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH15);
  if(isr_src & 0x4000)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH14);
  if(isr_src & 0x2000)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH13);
  if(isr_src & 0x1000)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH12);
  if(isr_src & 0x0800)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH11);
  if(isr_src & 0x0400)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH10);
  if(isr_src & 0x0200)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH9);
  if(isr_src & 0x0100)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH8);
  if(isr_src & 0x0080)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH7);
  if(isr_src & 0x0040)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH6);
  if(isr_src & 0x0020)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH5);
  if(isr_src & 0x0010)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH4);
  if(isr_src & 0x0008)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH3);
  if(isr_src & 0x0004)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH2);
  if(isr_src & 0x0002)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH1);
  if(isr_src & 0x0001)
    return hal_call_isr(CYGNUM_HAL_INTERRUPT_IMB3_TPUB_CH0);

  return 0;
}
#endif

#ifdef CYGPKG_IO_ENCODER_TPU_EC555_ENCODER_A
static hal_mpc5xx_arbitration_data arbiter_tpu_a;
#endif
#ifdef CYGPKG_IO_ENCODER_TPU_EC555_ENCODER_B
static hal_mpc5xx_arbitration_data arbiter_tpu_b;
#endif 

// tpu3_encoder_init
// Called at bootstrap time
static bool tpu3_encoder_init(struct cyg_devtab_entry * a_tab)
{
  encoder_channel * channel = (encoder_channel *)a_tab->priv;
  tpu3_encoder_t * encoder = (tpu3_encoder_t *)channel->m_dev_priv;
  tpu3_port_t * tpu_ptr = encoder->port;

#ifdef CYGPKG_IO_ENCODER_TPU_EC555_ENCODER_A
  arbiter_tpu_a.priority = CYGNUM_HAL_ISR_SOURCE_PRIORITY_TPU_A;
  arbiter_tpu_a.data     = 0;
  arbiter_tpu_a.arbiter  = hal_arbitration_isr_tpu3_a;
  
  // Make sure it does not get installed twice
  hal_mpc5xx_remove_arbitration_isr(&arbiter_tpu_a);
  hal_mpc5xx_install_arbitration_isr(&arbiter_tpu_a);
#endif

#ifdef CYGPKG_IO_ENCODER_TPU_EC555_ENCODER_B
  arbiter_tpu_b.priority = CYGNUM_HAL_ISR_SOURCE_PRIORITY_TPU_B;
  arbiter_tpu_b.data     = 0;
  arbiter_tpu_b.arbiter  = hal_arbitration_isr_tpu3_b;
  
  // Make sure it does not get installed twice
  hal_mpc5xx_remove_arbitration_isr(&arbiter_tpu_b);
  hal_mpc5xx_install_arbitration_isr(&arbiter_tpu_b);
#endif  

  tpu_ptr->tpumcr = TPU_MCR;
  channel->m_callbacks->init(channel, true);

  cyg_drv_interrupt_create(encoder->index_pulse_interrupt_number,
                           encoder->index_pulse_interrupt_priority,
                           (cyg_addrword_t)channel,
                           tpu3_encoder_index_pulse_ISR,
                           tpu3_encoder_index_pulse_DSR,
                           &encoder->index_pulse_interrupt_handle,
                           &encoder->index_pulse_interrupt);
  cyg_drv_interrupt_attach(encoder->index_pulse_interrupt_handle);
  // Leave this here. Who knows, maybe we'll redesign the hardware one day ...
#if 0
  cyg_drv_interrupt_create(encoder->probe_pulse_interrupt_number,
                           encoder->probe_pulse_interrupt_priority,
                           (cyg_addrword_t)channel,
                           tpu3_encoder_probe_pulse_ISR,
                           tpu3_encoder_probe_pulse_DSR,
                           &encoder->probe_pulse_interrupt_handle,
                           &encoder->probe_pulse_interrupt);
  cyg_drv_interrupt_attach(encoder->probe_pulse_interrupt_handle);
#endif
  return true;
}

// tpu3_encoder_lookup
// Called when the device is "looked up"
// Only enable the FQD functions on the third and fourth channel
// NITC functions will only be enabled when requested
static Cyg_ErrNo tpu3_encoder_lookup(struct cyg_devtab_entry ** a_tab,
                                     struct cyg_devtab_entry * a_sub_tab,
                                     const char * a_name)
{
  encoder_channel * channel = (encoder_channel *)(*a_tab)->priv;
  tpu3_encoder_t * encoder = (tpu3_encoder_t *)channel->m_dev_priv;
  tpu3_port_t * tpu_ptr = encoder->port;
  cyg_uint16 test_init;

  switch(encoder->channel_number)
  {
    case 0:
      {
      // Disable the channels we are going to use (channels 1-2)
      cyg_uint16 mask = 0x0003;
      mask = mask << 2;
      tpu_ptr->cpr1 &= ~mask;
      mask = mask << 2;
      tpu_ptr->cpr1 &= ~mask;
      // And now program the desired code into them
      tpu_ptr->cfsr3 |= (FQD_CODE << 8 | FQD_CODE << 4);
      }
      break;

    case 4:
      {
      // Disable the channels we are going to use (channels 5-6)
      cyg_uint16 mask = 0x0300;
      mask = mask << 2;
      tpu_ptr->cpr1 &= ~mask;
      mask = mask << 2;
      tpu_ptr->cpr1 &= ~mask;
      // And now program the desired code into them
      tpu_ptr->cfsr2 |= (FQD_CODE << 8 | FQD_CODE << 4);
      }
      break;

    case 8:
      {
      // Disable the channels we are going to use (channels 9-10)
      cyg_uint16 mask = 0x0003;
      mask = mask << 2;
      tpu_ptr->cpr0 &= ~mask;
      mask = mask << 2;
      tpu_ptr->cpr0 &= ~mask;
      // And now program the desired code into them
      tpu_ptr->cfsr1 |= (FQD_CODE << 8 | FQD_CODE << 4);
      }
      break;

    case 12:
      {
      // Disable the channels we are going to use (channels 13-14)
      cyg_uint16 mask = 0x0300;
      mask = mask << 2;
      tpu_ptr->cpr0 &= ~mask;
      mask = mask << 2;
      tpu_ptr->cpr0 &= ~mask;
      // And now program the desired code into them
      tpu_ptr->cfsr0 |= (FQD_CODE << 8 | FQD_CODE << 4);
      }
      break;

    default:
      return -EINVAL;
  }

  // set corresponding pins for fqd 
  tpu_ptr->channel[encoder->channel_number + 1].parameter4 = (0x10 * (encoder->channel_number + 1)) + 0x16;
  tpu_ptr->channel[encoder->channel_number + 2].parameter4 = (0x10 * (encoder->channel_number + 1)) + 0x06;
    
  // set pinstates for fqd
  tpu_ptr->channel[encoder->channel_number + 1].parameter3 = 0x8000;
  tpu_ptr->channel[encoder->channel_number + 2].parameter3 = 0x8000;

  // set edgetime address 
  tpu_ptr->channel[encoder->channel_number + 1].parameter5 = (0x10 * (encoder->channel_number + 1)) + 0x01;
  tpu_ptr->channel[encoder->channel_number + 2].parameter5 = (0x10 * (encoder->channel_number + 1)) + 0x01;
  tpu_ptr->channel[encoder->channel_number + 1].parameter1 = 100;

  switch(encoder->channel_number)
  {
    case 0:
      {
      cyg_uint16 mask = 0xffc3;
      tpu_ptr->hsqr1 &= mask;
      tpu_ptr->hsrr1 &= mask;
      tpu_ptr->cpr1  &= mask;

      mask = 0x0010;
      tpu_ptr->hsqr1 |= mask;
      mask = 0x003c;
      tpu_ptr->hsrr1 |= mask;
      tpu_ptr->cpr1  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr1 & 0x003c)));
      }
      break;

    case 4:
      {
      cyg_uint16 mask = 0xc3ff;
      tpu_ptr->hsqr1 &= mask;
      tpu_ptr->hsrr1 &= mask;
      tpu_ptr->cpr1  &= mask;

      mask = 0x1000;
      tpu_ptr->hsqr1 |= mask;
      mask = 0x3c00;
      tpu_ptr->hsrr1 |= mask;
      tpu_ptr->cpr1  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr1 & 0x3c00)));
      }
      break;

    case 8:
      {
      cyg_uint16 mask = 0xffc3;
      tpu_ptr->hsqr0 &= mask;
      tpu_ptr->hsrr0 &= mask;
      tpu_ptr->cpr0  &= mask;

      mask = 0x0010;
      tpu_ptr->hsqr1 |= mask;
      mask = 0x003c;
      tpu_ptr->hsrr0 |= mask;
      tpu_ptr->cpr0  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr0 & 0x003c)));
      }
      break;

    case 12:
      {
      cyg_uint16 mask = 0xc3ff;
      tpu_ptr->hsqr0 &= mask;
      tpu_ptr->hsrr0 &= mask;
      tpu_ptr->cpr0  &= mask;

      mask = 0x1000;
      tpu_ptr->hsqr1 |= mask;
      mask = 0x3c00;
      tpu_ptr->hsrr0 |= mask;
      tpu_ptr->cpr0  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr0 & 0x3c00)));
      }
      break;

    default:
      return -EINVAL;
  }

  // Unmask the interrupts and return
  cyg_drv_interrupt_acknowledge(encoder->index_pulse_interrupt_number);
  cyg_drv_interrupt_unmask(encoder->index_pulse_interrupt_number);

  // Made a misstake in the hardware layout for the ec555 interface board.
  // Too bad, no probe ... (Thats the reason we use channels 1-2 instead of 2-3
  // for the FQD function)
  
  //  cyg_drv_interrupt_acknowledge(encoder->probe_pulse_interrupt_number);
  //  cyg_drv_interrupt_unmask(encoder->probe_pulse_interrupt_number);

  return ENOERR;
}

// reset
static bool tpu3_encoder_reset(encoder_channel * a_channel)
{ // Do nothing for now, maybe in the future
  return true;
}

// tpu3_encoder_latch_index_pulse
static void tpu3_encoder_latch_index_pulse(encoder_channel * a_channel)
{
  tpu3_encoder_t * encoder = (tpu3_encoder_t *)a_channel->m_dev_priv;
  tpu3_port_t * tpu_ptr = encoder->port;
  cyg_uint16 test_init;

  switch(encoder->channel_number)
  {
    case 0:
      { // Disable the channel
      cyg_uint16 mask = 0x0003;
      tpu_ptr->cpr1 &= ~mask;
      // And now program the desired code
      tpu_ptr->cfsr3 |= NITC_CODE;
      }
      break;

    case 4:
      { // Disable the channel
      cyg_uint16 mask = 0x0300;
      tpu_ptr->cpr1 &= ~mask;
      // And now program the desired code
      tpu_ptr->cfsr2 |= NITC_CODE;
      }
      break;

    case 8:
      { // Disable the channel
      cyg_uint16 mask = 0x0003;
      tpu_ptr->cpr0 &= ~mask;
      // And now program the desired code
      tpu_ptr->cfsr1 |= NITC_CODE;
      }
      break;

    case 12:
      { // Disable the channel
      cyg_uint16 mask = 0x0300;
      tpu_ptr->cpr0 &= ~mask;
      // And now program the desired code into them
      tpu_ptr->cfsr0 |= NITC_CODE;
      }
      break;

    default:
      return;
  }

  // program the Nitc configuration parameters
  tpu_ptr->channel[encoder->channel_number].parameter0 = 0x000F;  // Detect either edge
  tpu_ptr->channel[encoder->channel_number].parameter1 = ((0x10 * (encoder->channel_number + 1)) + 2)/* << 1*/;
  tpu_ptr->channel[encoder->channel_number].parameter2 = 1;

  switch(encoder->channel_number)
  {
    case 0:
      {
      cyg_uint16 mask = 0xfffc;
      tpu_ptr->hsqr1 &= mask;
      tpu_ptr->hsrr1 &= mask;
      tpu_ptr->cpr1  &= mask;

      mask = 0x0002;
      tpu_ptr->hsrr1 |= mask;
      mask = 0x0003;
      tpu_ptr->cpr1  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr1 & 0x0003)));
      }
      break;

    case 4:
      {
      cyg_uint16 mask = 0xfcff;
      tpu_ptr->hsqr1 &= mask;
      tpu_ptr->hsrr1 &= mask;
      tpu_ptr->cpr1  &= mask;

      mask = 0x0200;
      tpu_ptr->hsrr1 |= mask;
      mask = 0x0300;
      tpu_ptr->cpr1  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr1 & 0x0300)));
      }
      break;

    case 8:
      {
      cyg_uint16 mask = 0xfffc;
      tpu_ptr->hsqr0 &= mask;
      tpu_ptr->hsrr0 &= mask;
      tpu_ptr->cpr0  &= mask;

      mask = 0x0002;
      tpu_ptr->hsrr0 |= mask;
      mask = 0x0003;
      tpu_ptr->cpr0  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr0 & 0x0003)));
      }
      break;

    case 12:
      {
      cyg_uint16 mask = 0xfcff;
      tpu_ptr->hsqr0 &= mask;
      tpu_ptr->hsrr0 &= mask;
      tpu_ptr->cpr0  &= mask;

      mask = 0x0200;
      tpu_ptr->hsrr0 |= mask;
      mask = 0x0300;
      tpu_ptr->cpr0  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr0 & 0x0300)));
      }
      break;

    default:
      return;
  }

  cyg_drv_interrupt_acknowledge(encoder->index_pulse_interrupt_number);
  cyg_drv_interrupt_unmask(encoder->index_pulse_interrupt_number);
}

// tpu3_encoder_latch_probe_pulse
static void tpu3_encoder_latch_probe_pulse(encoder_channel * a_channel)
{
  // See higher, we made a misstake in the hardware design of the interface board ...
  // Return immediately. The same thing can be used on the cme555 without any problems
#if 0
  tpu3_encoder_t * encoder = (tpu3_encoder_t *)a_channel->m_dev_priv;
  tpu3_port_t * tpu_ptr = encoder->port;
  cyg_uint16 test_init;

  switch(encoder->channel_number)
  {
    case 0:
      { // Disable the channel
      cyg_uint16 mask = 0x000c;
      tpu_ptr->cpr1 &= ~mask;
      // And now program the desired code
      tpu_ptr->cfsr3 |= (NITC_CODE << 4);
      }
      break;

    case 4:
      { // Disable the channel
      cyg_uint16 mask = 0x0c00;
      tpu_ptr->cpr1 &= ~mask;
      // And now program the desired code
      tpu_ptr->cfsr2 |= (NITC_CODE << 4);
      }
      break;

    case 8:
      { // Disable the channel
      cyg_uint16 mask = 0x000c;
      tpu_ptr->cpr0 &= ~mask;
      // And now program the desired code
      tpu_ptr->cfsr1 |= (NITC_CODE << 4);
      }
      break;

    case 12:
      { // Disable the channel
      cyg_uint16 mask = 0x0c00;
      tpu_ptr->cpr0 &= ~mask;
      // And now program the desired code into them
      tpu_ptr->cfsr0 |= (NITC_CODE << 4);
      }
      break;

    default:
      return;
  }

  // program the Nitc configuration parameters
  tpu_ptr->channel[encoder->channel_number + 1].parameter0 = 0x000F;  // Detect either edge
  tpu_ptr->channel[encoder->channel_number + 1].parameter1 = ((0x10 * (encoder->channel_number + 2)) + 2)/* << 1*/;
  tpu_ptr->channel[encoder->channel_number + 1].parameter2 = 1;

  switch(encoder->channel_number)
  {
    case 0:
      {
      cyg_uint16 mask = 0xfff3;
      tpu_ptr->hsqr1 &= mask;
      tpu_ptr->hsrr1 &= mask;
      tpu_ptr->cpr1  &= mask;

      mask = 0x0008;
      tpu_ptr->hsrr1 |= mask;
      mask = 0x000c;
      tpu_ptr->cpr1  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr1 & 0x000c)));
      }
      break;

    case 4:
      {
      cyg_uint16 mask = 0xf3ff;
      tpu_ptr->hsqr1 &= mask;
      tpu_ptr->hsrr1 &= mask;
      tpu_ptr->cpr1  &= mask;

      mask = 0x0800;
      tpu_ptr->hsrr1 |= mask;
      mask = 0x0c00;
      tpu_ptr->cpr1  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr1 & 0x0c00)));
      }
      break;

    case 8:
      {
      cyg_uint16 mask = 0xfff3;
      tpu_ptr->hsqr0 &= mask;
      tpu_ptr->hsrr0 &= mask;
      tpu_ptr->cpr0  &= mask;

      mask = 0x0008;
      tpu_ptr->hsrr0 |= mask;
      mask = 0x000c;
      tpu_ptr->cpr0  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr0 & 0x000c)));
      }
      break;

    case 12:
      {
      cyg_uint16 mask = 0xf3ff;
      tpu_ptr->hsqr0 &= mask;
      tpu_ptr->hsrr0 &= mask;
      tpu_ptr->cpr0  &= mask;

      mask = 0x0800;
      tpu_ptr->hsrr0 |= mask;
      mask = 0x0c00;
      tpu_ptr->cpr0  |= mask;

      // Wait for things to settle down
      while((test_init = (tpu_ptr->hsrr0 & 0x0c00)));
      }
      break;

    default:
      return;
  }

  cyg_drv_interrupt_acknowledge(encoder->probe_pulse_interrupt_number);
  cyg_drv_interrupt_unmask(encoder->probe_pulse_interrupt_number);
#endif
}

// tpu3_encoder_stop_latch
static void tpu3_encoder_stop_latch(encoder_channel * a_channel)
{
  tpu3_encoder_t * encoder = (tpu3_encoder_t *)a_channel->m_dev_priv;
  tpu3_port_t * tpu_ptr = encoder->port;

  switch(encoder->channel_number)
  {
    case 0:
      { // Disable the channel
      cyg_uint16 mask = 0x0003;
      tpu_ptr->cpr1 &= ~mask;
      }
      break;

    case 4:
      { // Disable the channel
      cyg_uint16 mask = 0x0300;
      tpu_ptr->cpr1 &= ~mask;
      }
      break;

    case 8:
      { // Disable the channel
      cyg_uint16 mask = 0x0003;
      tpu_ptr->cpr0 &= ~mask;
      }
      break;

    case 12:
      { // Disable the channel
      cyg_uint16 mask = 0x0300;
      tpu_ptr->cpr0 &= ~mask;
      }
      break;

    default:
      return;
  }
}

// read_position
static cyg_uint16 tpu3_encoder_read_position(encoder_channel * a_channel)
{
  tpu3_encoder_t * encoder = (tpu3_encoder_t *)a_channel->m_dev_priv;
  tpu3_port_t * tpu_ptr = encoder->port;
  cyg_uint16 enc_value;

//  enc_value = tpu_ptr->channel[encoder->channel_number + 2].parameter1;
  enc_value = tpu_ptr->channel[encoder->channel_number + 1].parameter1;
  return enc_value;
}

//------------------------------------------------------
// And last but not least the encoder interrupt handling
//------------------------------------------------------
// The ISR
static cyg_uint32 tpu3_encoder_index_pulse_ISR(cyg_vector_t a_vector, cyg_addrword_t a_data)
{
  cyg_drv_interrupt_acknowledge(a_vector);

  // No need in waisting time in masking, the NITC function must be re-initialized to generate a new interrupt
  return CYG_ISR_CALL_DSR;
}

// the DSR
static void tpu3_encoder_index_pulse_DSR(cyg_vector_t a_vector, cyg_ucount32 a_count, cyg_addrword_t a_data)
{
  encoder_channel * channel = (encoder_channel *)a_data;
  tpu3_encoder_t * encoder = (tpu3_encoder_t *)channel->m_dev_priv;
  tpu3_port_t * tpu_ptr = encoder->port;
  
  // Get the latched position and present it to higher layers
  cyg_uint16 enc_value = tpu_ptr->channel[encoder->channel_number].parameter4;
  channel->m_callbacks->latch(channel, enc_value);
}

// The probe ISR
static cyg_uint32 tpu3_encoder_probe_pulse_ISR(cyg_vector_t a_vector, cyg_addrword_t a_data)
{
  cyg_drv_interrupt_acknowledge(a_vector);

  // No need in waisting time in masking, the NITC function must be re-initialized to generate a new interrupt
  return CYG_ISR_CALL_DSR;
}

// The probe DSR
static void tpu3_encoder_probe_pulse_DSR(cyg_vector_t a_vector, cyg_ucount32 a_count, cyg_addrword_t a_data)
{
  encoder_channel * channel = (encoder_channel *)a_data;
  tpu3_encoder_t * encoder = (tpu3_encoder_t *)channel->m_dev_priv;
  tpu3_port_t * tpu_ptr = encoder->port;
 
  // Get the latched position and present it to higher layers 
  cyg_uint16 enc_value = tpu_ptr->channel[encoder->channel_number + 1].parameter4;
  channel->m_callbacks->latch(channel, enc_value);
}

#endif // TPU_C
