/*
    IXP2400_UART.c, Copyright  (c) by Silvio Dragone & David Reist,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public Licence.
 
    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/

#include "Drivers/IXP2400_UART.h"
#include "Messages.h"
#include "IODevice.h"
#include "IOHal.h"
#include "Lock.h"
#include "Configuration.h"
#include "cpu.h"
#include "Exception.h"

//[SD]
//#include "Support.h"

IODeviceDesc sa1100_uart_a = {
                               NULL, NULL,                                        /* pointers for doubly linked list */
                               "ttya",                                            /* device name */
                               (Address)UART_RBR_REGISTER,                         /* device base address */
                               NULL,                                              /* buffer must be allocated by driver */
                               UART_I,                                            /* interrupt */
                               (InterruptHandler)devIXP2400_UART_interruptHandler, /* ttya interrupt handler */
                               0,                                                 /* thread id filled in after start */
                               devIXP2400_UART_read,                               /* device read function */
                               devIXP2400_UART_write,                              /* device write function */
                               devIXP2400_UART_close,                              /* device close function */
                               devIXP2400_UART_init,                               /* device init function */
                               NULL,                                              /* private msg handler */
                               FALSE,                                             /* initialisation state, normally FALSE */
                               NULL                                               /* data extension */
                             };

/* not currently used - may be broken! */
void devIXP2400_UART_interruptHandler(IODevice this)
{
  devFIFO_Ptr fifo_ptr;
  unsigned long* rx_data = (unsigned long*) UART_RBR_REGISTER;
  unsigned long int* status = (unsigned long int*) UART_LSR_REGISTER;
  unsigned long int* control = (unsigned long int*) UART_LCR_REGISTER;
  unsigned long int tmp;
  unsigned long c;

  printf("devIXP2400_UART_interruptHandler:\n");
  fifo_ptr = (devFIFO_Ptr)(this->extension);

  do
  {
    tmp=*status;
    //-CJ
    printf("\ttesting if ready %p %#8lx control %p %#lx data %p %#lx\n", status, tmp, control, *control, rx_data, *rx_data);
    tmp &= RX_FIFO_READY;
  }
  while(!tmp);
  printf("\tdevice if ready\n");

  while (tmp)
  {
    if ((fifo_ptr->in + 1) % IXP2400_UART_BUFSIZE == fifo_ptr->out)
    {
      WARNING("UART overflow\n");
      return;
    }
    c = *rx_data;
    this->buffer[fifo_ptr->in] = (char)c;

    fifo_ptr->in = (fifo_ptr->in + 1) % IXP2400_UART_BUFSIZE;
    tmp=*status & RX_FIFO_READY;
  }
  printf("read c: %li=%c\n",c ,(int)c);
  //printf("fifo_ptr = %p, fifo_ptr->in = %p == fifo_ptr->out = %p\n",
  // fifo_ptr, fifo_ptr->in, fifo_ptr->out);
}


Error devIXP2400_UART_init(IODevice this)
{
  devFIFO_Ptr fifo_ptr;
  volatile unsigned int *lcr = (unsigned int*) UART_LCR_REGISTER;
  volatile unsigned int *ier = (unsigned int*) UART_IER_REGISTER;
  volatile unsigned int *fcr = (unsigned int*) UART_FCR_REGISTER;
  volatile unsigned int *dlrl = (unsigned int*) UART_DLRL_REGISTER;
  volatile unsigned int *dlrh = (unsigned int*) UART_DLRH_REGISTER;

  *fcr = 0;
  *lcr = LCR_WLS_8BIT; //8 Data Bits
  *ier = IER_UUE_YES | IER_RLSE_YES | IER_RAVIE_YES; // enable uart unit & activate data avail interrupt

  /* buffers init - be careful to check for existing buffers */
  if (!this->isInitialised)
  {
    hmAlloc(&(this->extension), 2); /* IN & OUT */
    hmAlloc((Address*)&(this->buffer), IXP2400_UART_BUFSIZE);
    fifo_ptr = (devFIFO_Ptr)this->extension;
    this->isInitialised = TRUE;
  }
  else
  {
    fifo_ptr = (devFIFO_Ptr)this->extension;
  }
  fifo_ptr->in = 0;
  fifo_ptr->out = 0;
  //   printf("%i %i %i\n", *ier, *lcr, *fcr);
  return IO_INITOK;
}


Error devIXP2400_UART_read(IODevice this, ThreadId threadId, char* buffer,
                           long int* size)
{
  long int i = 0;
  devFIFO_Ptr fifo_ptr;
  volatile unsigned int *rbr = (unsigned int*) UART_RBR_REGISTER;
  volatile unsigned int *iir = (unsigned int*) UART_IIR_REGISTER;

  fifo_ptr = (devFIFO_Ptr)this->extension;
  while (TRUE)
  {
    if (i >= *size)
    {
      break;
    }
    if (*iir & IIR_RDA)
    {
      buffer[i++] = *rbr;
      fifo_ptr->out = (fifo_ptr->out + 1) % IXP2400_UART_BUFSIZE;
    }

  }
  *size = i;
  return IO_READOK;
}


Error devIXP2400_UART_write(IODevice this, ThreadId threadId, char* buffer, long int* size)
{
  long int i;
  volatile unsigned int *thr = (unsigned int*) UART_THR_REGISTER;
  volatile unsigned int *lsr = (unsigned int*) UART_LSR_REGISTER;

  for (i = 0; i < *size; i++)
  {

    /* Wait for UART to become ready */
    while (!(*lsr & LSR_TDRQ))
    {
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
    }

    *thr = buffer[i]; /* send it */

    ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  }
  return IO_WRITEOK;
}


Error devIXP2400_UART_close(IODevice this)
{
  /* this message is forwarded from ioThread */
  /* release buffers if any */

  if (this->isInitialised)
  {

    hmFree((Address)this->buffer);     /* Deallocate buffer space */
    hmFree((Address)this->extension);  /* Deallocate descriptor space */
    this->isInitialised = FALSE;       /* Set Initialized flag */
  }

  return IO_CLOSEOK;
}
