/*!
  \file
  \brief SCI の初期化

  \author Satofumi KAMIMURA

  $Id: sci_control.c 1043 2009-06-19 14:51:09Z satofumi $
*/

#include "sci_control.h"
#include "cpu_aki_sh2.h"
#ifdef SCI_USE_INTERRUPT
#include "ring_buffer.h"
#include "imask_control.h"
#endif

#ifdef SCI_USE_INTERRUPT
static char SendBuffer[2][SCI_BUFFER_SIZE];
static char RecvBuffer[2][SCI_BUFFER_SIZE];
static ringBuffer_t RingSend[2];
static ringBuffer_t RingRecv[2];

static void init_buffer(sci_port port)
{
  ring_init(&RingSend[port], SendBuffer[port], SCI_BUFFER_SIZE_SHIFT);
  ring_init(&RingRecv[port], RecvBuffer[port], SCI_BUFFER_SIZE_SHIFT);
}
#endif


void sci_initialize(sci_port port, int level)
{
  (void)level;
  volatile int i;

  enum {
    /* 使うボーレート中の最大の値を代表値として用いる */
    SCI_BIT_WAIT = (int)(CPU_CLOCK / 115200),
  };

#ifdef SCI_USE_INTERRUPT
  // 割り込みバッファの初期化
  init_buffer(port);
#endif

  /* 指定ポートの初期化 */
  if (port == SCI_0) {
    SCI0.SCR.BYTE = 0x00;       /* TE=0, RE=0 */
    SCI0.SMR.BYTE = 0;          /* 8bit, 1stop bit, no parity, TE=0, RE=0 */
    SCI0.BRR = 7;               /* 115200 bps */
    PFC.PACRL2.WORD |= 0x0005;  /* use TXD0, RXD0 function */

  } else if (port == SCI_1) {
    /* SCI1 の初期化 */
    SCI1.SCR.BYTE = 0x00;       /* TE=0, RE=0 */
    SCI1.SMR.BYTE = 0;          /* 8bit, 1stop bit, no parity, TE=0, RE=0 */
    SCI1.BRR = 7;               /* 115200 bps */
    PFC.PACRL2.WORD |= 0x0140;  /* use TXD1, RXD1 function */
  }

  for (i = 0; i < SCI_BIT_WAIT; ++i)
    ;

  if (port == SCI_0) {
    SCI0.SCR.BYTE = 0x30;
    SCI0.SSR.BYTE &= ~0x40;

#ifdef SCI_USE_INTERRUPT
    INTC.IPRF.WORD &= ~0x00f0;
    INTC.IPRF.WORD |= (level << 4) & 0x00f0;
    SCI0.SCR.BYTE |= 0x40;
#endif
  } else if (port == SCI_1) {
    SCI1.SCR.BYTE = 0x30;
    SCI1.SSR.BYTE &= ~0x40;

#ifdef SCI_USE_INTERRUPT
    INTC.IPRF.WORD &= ~0x000f;
    INTC.IPRF.WORD |= level & 0x000f;
    SCI1.SCR.BYTE |= 0x40;
#endif
  }
}

#ifdef SCI_USE_INTERRUPT

static int get_sciLevel(sci_port port)
{
  return ((port == SCI_0) ?
          (INTC.IPRF.WORD & 0x00f0) >> 4 : INTC.IPRF.WORD & 0x000f);
}


int sci_write(sci_port port, const char *data, int size)
{
  int sci_level = get_sciLevel(port);
  int original_level = imask_maskLevel();
  int n;

  // 送信データの格納
  imask_setMaskLevel(sci_level);
  n = ring_write(&RingSend[port], data, size);
  if (n > 0) {

    // 送信割り込みの許可
    if (port == SCI_0) {
      SCI0.SCR.BYTE |= 0x80;
    } else if (port == SCI_1) {
      SCI1.SCR.BYTE |= 0x80;
    }
  }
  imask_setMaskLevel(original_level);

  return n;
}


int sci_read(sci_port port, char *buffer, int size)
{
  int sci_level = get_sciLevel(port);
  int original_level = imask_maskLevel();
  int read_size = 0;

  // !!! ring_read(,, size) にすべきか？
  imask_setMaskLevel(sci_level);
  read_size = ring_size(&RingRecv[port]);
  if (read_size > size) {
    read_size = size;
  }
  ring_read(&RingRecv[port], buffer, read_size);
  imask_setMaskLevel(original_level);

  return read_size;
}


int sci_readable(sci_port port)
{
  int sci_level = get_sciLevel(port);
  int original_level = imask_maskLevel();
  int n;

  imask_setMaskLevel(sci_level);
  n = ring_size(&RingRecv[port]);
  imask_setMaskLevel(original_level);

  return n;
}


void sci_stop(sci_port port)
{
  // 割り込みの禁止
  int sci_level = get_sciLevel(port);
  int original_level = imask_maskLevel();

  imask_setMaskLevel(sci_level);
  if (port == SCI_0) {
    INTC.IPRF.WORD &= ~0x00f0;
    SCI0.SCR.BYTE &= ~0x40;

  } else if (port == SCI_1) {
    INTC.IPRF.WORD &= ~0x000f;
    SCI1.SCR.BYTE &= ~0x40;
  }
  imask_setMaskLevel(original_level);
}


void sci_flush(sci_port port)
{
  // 割り込み許可が出ていたら、ポーリングで待機する
  while (1) {
    if (port == SCI_0) {
      if ((SCI0.SCR.BYTE & 0x80) == 0) {
        break;
      }
    } else if (port == SCI_1) {
      if ((SCI1.SCR.BYTE & 0x80) == 0) {
        break;
      }
    }
  }
}


__attribute__((interrupt_handler))
void txi0(void)
{
  char ch;

  if (ring_getchar(&RingSend[0], &ch) <= 0) {
    SCI0.SCR.BYTE &= ~0x80;     // 送信終了。以後の送信割り込みを禁止
  } else {
    SCI0.TDR = ch;
    SCI0.SSR.BYTE &= ~0x80;
  }
}


__attribute__((interrupt_handler))
void txi1(void)
{
  char ch;

  if (ring_getchar(&RingSend[1], &ch) <= 0) {
    SCI1.SCR.BYTE &= ~0x80;     // 送信終了。以後の送信割り込みを禁止
  } else {
    SCI1.TDR = ch;
    SCI1.SSR.BYTE &= ~0x80;
  }
}


__attribute__((interrupt_handler))
void tei0(void)
{
}


__attribute__((interrupt_handler))
void tei1(void)
{
}


__attribute__((interrupt_handler))
void rxi0(void)
{
  char ch = SCI0.RDR;
  ring_putchar(&RingRecv[0], ch);
  SCI0.SSR.BYTE &= ~0x40;
}


__attribute__((interrupt_handler))
void rxi1(void)
{
  char ch = SCI1.RDR;
  ring_putchar(&RingRecv[1], ch);
  SCI1.SSR.BYTE &= ~0x40;
}


__attribute__((interrupt_handler))
void eri0(void)
{
}


__attribute__((interrupt_handler))
void eri1(void)
{
}
#else


int sci_write(sci_port port, const char* data, int size)
{
  int i;
  for (i = 0; i < size; ++i) {
    if (port == SCI_0) {
      while (! (SCI0.SSR.BYTE & 0x80))
        ;
      SCI0.TDR = data[i];
      SCI0.SSR.BYTE &= ~0x80;

    } else if (port == SCI_1) {
      while (! (SCI1.SSR.BYTE & 0x80))
        ;
      SCI1.TDR = data[i];
      SCI1.SSR.BYTE &= ~0x80;
    }
  }
  return i;
}


int sci_read(sci_port port, char *buffer, int size)
{
  int i;
  for (i = 0; i < size; ++i) {
    if (port == SCI_0) {
      while (1) {
        if (SCI0.SSR.BYTE & 0x38) {
          // 受信エラー
          SCI0.SSR.BYTE &= ~0x38;
          break;

        } else if (SCI0.SSR.BYTE & 0x40) {
          buffer[i] = SCI0.RDR;
          SCI0.SSR.BYTE &= ~0x40;
          break;
        }
      }
    } else if (port == SCI_1) {
      while (1) {
        if (SCI1.SSR.BYTE & 0x38) {
          // 受信エラー
          SCI1.SSR.BYTE &= ~0x38;
          break;

        } else if (SCI1.SSR.BYTE & 0x40) {
          buffer[i] = SCI1.RDR;
          SCI1.SSR.BYTE &= ~0x40;
          break;
        }
      }
    }
  }
  return i;
}


int sci_readable(sci_port port)
{
  if (port == SCI_0) {
    return (SCI0.SSR.BYTE & 0x40) ? 1 : 0;

  } else if (port == SCI_1) {
    return (SCI1.SSR.BYTE & 0x40) ? 1 : 0;
  }
  return 0;
}


void sci_stop(sci_port port)
{
  (void)port;
  // !!!
}


void sci_flush(sci_port port)
{
  (void)port;
  // !!!
}
#endif
