/*
 * Generic UART Serial driver for SH-4 processors
 *
 * Copyright (C) 2000 OKTET Ltd., St.-Petersburg, Russian Fed.
 * Author: Alexandra Kossovsky <sasha@oktet.ru>
 *
 *  COPYRIGHT (c) 1989-2000.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 *
 */

#include <rtems.h>
#include <termios.h>
#include <rtems/libio.h>
#include <bsp.h>
#include "sh/sh4uart.h"

#ifndef SH4_UART_INTERRUPT_LEVEL
#define SH4_UART_INTERRUPT_LEVEL 4
#endif

/* Forward function declarations */
static rtems_isr
sh4uart1_interrupt_transmit(rtems_vector_number vec);
static rtems_isr
sh4uart1_interrupt_receive(rtems_vector_number vec);
static rtems_isr
sh4uart2_interrupt_transmit(rtems_vector_number vec);
static rtems_isr
sh4uart2_interrupt_receive(rtems_vector_number vec);

/*
 * sh4uart_init --
 * This function verifies the input parameters and perform initialization
 *     of the SH-4 on-chip UART descriptor structure.
 *
 * PARAMETERS:
 *     uart - pointer to the UART channel descriptor structure
 *     tty - pointer to termios structure
 *     chn - channel number (SH4_SCI/SH4_SCIF -- 1/2)
 *     int_driven - interrupt-driven (1) or polled (0) I/O mode
 *
 * RETURNS:
 *     RTEMS_SUCCESSFUL if all parameters are valid, or error code
 */
rtems_status_code
sh4uart_init(sh4uart *uart, void *tty, int chn, int int_driven)
{
  if (uart == NULL)
    return RTEMS_INVALID_ADDRESS;

  if ((chn != SH4_SCI) && (chn != SH4_SCIF))
    return RTEMS_INVALID_NUMBER;

  uart->chn = chn;
  uart->tty = tty;
  uart->int_driven = int_driven;

#if 0
  sh4uart_poll_write(uart, "init", 4);
#endif
  return RTEMS_SUCCESSFUL;
}

/*
 * sh4uart_get_Pph --
 *    Get current peripheral module clock.
 *
 * PARAMETERS: none;
 *    Cpu clock is get from CPU_CLOCK_RATE_HZ marco
 *    (defined in bspopts.h, included from bsp.h)
 *
 * RETURNS:
 *    peripheral module clock in Hz.
 */
static uint32_t
sh4uart_get_Pph(void)
{
  uint16_t   frqcr = *(volatile uint16_t*)SH7750_FRQCR;
  uint32_t   Pph = CPU_CLOCK_RATE_HZ;

  switch (frqcr & SH7750_FRQCR_IFC) {
    case SH7750_FRQCR_IFCDIV1: break;
    case SH7750_FRQCR_IFCDIV2: Pph *= 2; break;
    case SH7750_FRQCR_IFCDIV3: Pph *= 3; break;
    case SH7750_FRQCR_IFCDIV4: Pph *= 4; break;
    case SH7750_FRQCR_IFCDIV6: Pph *= 6; break;
    case SH7750_FRQCR_IFCDIV8: Pph *= 8; break;
    default: /* unreachable */
      break;
  }

  switch (frqcr & SH7750_FRQCR_PFC) {
    case SH7750_FRQCR_PFCDIV2: Pph /= 2; break;
    case SH7750_FRQCR_PFCDIV3: Pph /= 3; break;
    case SH7750_FRQCR_PFCDIV4: Pph /= 4; break;
    case SH7750_FRQCR_PFCDIV6: Pph /= 6; break;
    case SH7750_FRQCR_PFCDIV8: Pph /= 8; break;
    default: /* unreachable */
      break;
  }

  return Pph;
}

/*
 * sh4uart_set_baudrate --
 *     Program the UART timer to specified baudrate
 *
 * PARAMETERS:
 *     uart - pointer to UART descriptor structure
 *     baud - termios baud rate (B50, B9600, etc...)
 *
 * ALGORITHM:
 *     see SH7750 Hardware Manual.
 *
 * RETURNS:
 *     none
 */
static void
sh4uart_set_baudrate(sh4uart *uart, speed_t baud)
{
  uint32_t   rate;
  int16_t   div;
  int n;
  uint32_t   Pph = sh4uart_get_Pph();

  switch (baud) {
    case B50:     rate = 50; break;
    case B75:     rate = 75; break;
    case B110:    rate = 110; break;
    case B134:    rate = 134; break;
    case B150:    rate = 150; break;
    case B200:    rate = 200; break;
    case B300:    rate = 300; break;
    case B600:    rate = 600; break;
    case B1200:   rate = 1200; break;
    case B2400:   rate = 2400; break;
    case B4800:   rate = 4800; break;
    case B9600:   rate = 9600; break;
    case B19200:  rate = 19200; break;
    case B38400:  rate = 38400; break;
    case B57600:  rate = 57600; break;
#ifdef B115200
    case B115200: rate = 115200; break;
#endif
#ifdef B230400
    case B230400: rate = 230400; break;
#endif
    default:      rate = 9600; break;
  }

  for (n = 0; n < 4; n++) {
    div = Pph / (32 * (1 << (2 * n)) * rate) - 1;
    if (div < 0x100)
      break;
  }

  /* Set default baudrate if specified baudrate is impossible */
  if (n >= 4)
    sh4uart_set_baudrate(uart, B9600);

  if ( uart->chn == 1 ) {
    volatile uint8_t *smr1 = (volatile uint8_t *)SH7750_SCSMR1;
    *smr1 &= ~SH7750_SCSMR_CKS;
    *smr1 |= n << SH7750_SCSMR_CKS_S;
  } else {
    volatile uint16_t *smr2 = (volatile uint16_t *)SH7750_SCSMR2;
    *smr2 &= ~SH7750_SCSMR_CKS;
    *smr2 |= n << SH7750_SCSMR_CKS_S;
  }

  SCBRR(uart->chn) = div;
  /* Wait at least 1 bit interwal */
  rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(1000 / rate));
}

/*
 * sh4uart_reset --
 *     This function perform the hardware initialization of SH-4
 *     on-chip UART controller using parameters
 *     filled by the sh4uart_init function.
 *
 * PARAMETERS:
 *     uart - pointer to UART channel descriptor structure
 *
 * RETURNS:
 *     RTEMS_SUCCESSFUL if channel is initialized successfully, error
 *     code in other case
 */
rtems_status_code
sh4uart_reset(sh4uart *uart)
{
  register int chn;
  register int int_driven;
  rtems_status_code rc;
  uint16_t tmp;

  if (uart == NULL)
    return RTEMS_INVALID_ADDRESS;

  chn = uart->chn;
  int_driven = uart->int_driven;

  if ( chn == 1 ) {
    volatile uint8_t *scr1 = (volatile uint8_t *)SH7750_SCSCR1;
    volatile uint8_t *smr1 = (volatile uint8_t *)SH7750_SCSMR1;
    *scr1 = 0x0;       /* Is set properly at the end of this function */
    *smr1 = 0x0;       /* 8-bit, non-parity, 1 stop bit, pf/1 clock */
  } else {
    volatile uint16_t *scr2 = (volatile uint16_t *)SH7750_SCSCR2;
    volatile uint16_t *smr2 = (volatile uint16_t *)SH7750_SCSMR2;
    *scr2 = 0x0;       /* Is set properly at the end of this function */
    *smr2 = 0x0;       /* 8-bit, non-parity, 1 stop bit, pf/1 clock */
  }

  if (chn == SH4_SCIF)
    SCFCR2 = SH7750_SCFCR2_TFRST | SH7750_SCFCR2_RFRST |
             SH7750_SCFCR2_RTRG_1 | SH7750_SCFCR2_TTRG_4;

  if (chn == SH4_SCI)
    SCSPTR1 = int_driven ? 0x0 : SH7750_SCSPTR1_EIO;
  else
    SCSPTR2 = SH7750_SCSPTR2_RTSDT;

  if (int_driven) {
    uint16_t   ipr;

    if (chn == SH4_SCI) {
      ipr = IPRB;
      ipr &= ~SH7750_IPRB_SCI1;
      ipr |= SH4_UART_INTERRUPT_LEVEL << SH7750_IPRB_SCI1_S;
      IPRB = ipr;

      rc = rtems_interrupt_catch(sh4uart1_interrupt_transmit,
                                 SH7750_EVT_TO_NUM(SH7750_EVT_SCI_TXI),
                                 &uart->old_handler_transmit);
      if (rc != RTEMS_SUCCESSFUL)
        return rc;

      rc = rtems_interrupt_catch(sh4uart1_interrupt_receive,
                                 SH7750_EVT_TO_NUM(SH7750_EVT_SCI_RXI),
                                 &uart->old_handler_receive);
      if (rc != RTEMS_SUCCESSFUL)
        return rc;
    } else {
      ipr = IPRC;
      ipr &= ~SH7750_IPRC_SCIF;
      ipr |= SH4_UART_INTERRUPT_LEVEL << SH7750_IPRC_SCIF_S;
      IPRC = ipr;

      rc = rtems_interrupt_catch(sh4uart2_interrupt_transmit,
                                 SH7750_EVT_TO_NUM(SH7750_EVT_SCIF_TXI),
                                 &uart->old_handler_transmit);
      if (rc != RTEMS_SUCCESSFUL)
        return rc;
      rc = rtems_interrupt_catch(sh4uart2_interrupt_receive,
                                 SH7750_EVT_TO_NUM(SH7750_EVT_SCIF_RXI),
                                 &uart->old_handler_receive);
      if (rc != RTEMS_SUCCESSFUL)
        return rc;
    }
    uart->tx_buf = NULL;
    uart->tx_ptr = uart->tx_buf_len = 0;
  }

  sh4uart_set_baudrate(uart, B38400); /* debug defaults (unfortunately,
                                         it is differ to termios default */

  tmp = SH7750_SCSCR_TE | SH7750_SCSCR_RE |
          (chn == SH4_SCI ? 0x0 : SH7750_SCSCR2_REIE) |
          (int_driven ? (SH7750_SCSCR_RIE | SH7750_SCSCR_TIE) : 0x0);

  if ( chn == 1 ) {
    volatile uint8_t *scr = (volatile uint8_t *)SH7750_SCSCR1;
    *scr = tmp;
  } else {
    volatile uint16_t *scr = (volatile uint16_t *)SH7750_SCSCR2;
    *scr = tmp;
  }

  return RTEMS_SUCCESSFUL;
}

/*
 * sh4uart_disable --
 *     This function disable the operations on SH-4 UART controller
 *
 * PARAMETERS:
 *     uart - pointer to UART channel descriptor structure
 *     disable_port - disable receive and transmit on the port
 *
 * RETURNS:
 *     RTEMS_SUCCESSFUL if UART closed successfuly, or error code in
 *     other case
 */
rtems_status_code
sh4uart_disable(sh4uart *uart, int disable_port)
{
  rtems_status_code rc;

  if (disable_port) {
    if ( uart->chn == 1 ) {
      volatile uint8_t *scr = (volatile uint8_t *)SH7750_SCSCR1;
      *scr &= ~(SH7750_SCSCR_TE | SH7750_SCSCR_RE);
    } else {
      volatile uint16_t *scr = (volatile uint16_t *)SH7750_SCSCR2;
      *scr &= ~(SH7750_SCSCR_TE | SH7750_SCSCR_RE);
    }
  }

  if (uart->int_driven) {
    rc = rtems_interrupt_catch(uart->old_handler_transmit,
           uart->chn == SH4_SCI ?  SH7750_EVT_SCI_TXI : SH7750_EVT_SCIF_TXI,
           NULL);
    if (rc != RTEMS_SUCCESSFUL)
      return rc;
    rc = rtems_interrupt_catch(uart->old_handler_receive,
           uart->chn == SH4_SCI ?  SH7750_EVT_SCI_RXI : SH7750_EVT_SCIF_RXI,
           NULL);
    if (rc != RTEMS_SUCCESSFUL)
      return rc;
  }

  return RTEMS_SUCCESSFUL;
}

/*
 * sh4uart_set_attributes --
 *     This function parse the termios attributes structure and perform
 *     the appropriate settings in hardware.
 *
 * PARAMETERS:
 *     uart - pointer to the UART descriptor structure
 *     t - pointer to termios parameters
 *
 * RETURNS:
 *     RTEMS_SUCCESSFUL
 */
rtems_status_code
sh4uart_set_attributes(sh4uart *uart, const struct termios *t)
{
  int level;
  speed_t baud;
  uint16_t   smr;

  smr = (uint16_t)(*(uint8_t*)SH7750_SCSMR(uart->chn));

  baud = cfgetospeed(t);

  /* Set flow control XXX*/
  if ((t->c_cflag & CRTSCTS) != 0) {
  }

  /* Set character size -- only 7 or 8 bit */
  switch (t->c_cflag & CSIZE) {
    case CS5:
    case CS6:
    case CS7: smr |= SH7750_SCSMR_CHR_7; break;
    case CS8: smr &= ~SH7750_SCSMR_CHR_7; break;
  }

    /* Set number of stop bits */
  if ((t->c_cflag & CSTOPB) != 0)
    smr |= SH7750_SCSMR_STOP_2;
  else
    smr &= ~SH7750_SCSMR_STOP_2;

  /* Set parity mode */
  if ((t->c_cflag & PARENB) != 0) {
    smr |= SH7750_SCSMR_PE;
    if ((t->c_cflag & PARODD) != 0)
       smr |= SH7750_SCSMR_PM_ODD;
    else
       smr &= ~SH7750_SCSMR_PM_ODD;
  } else
    smr &= ~SH7750_SCSMR_PE;

  rtems_interrupt_disable(level);
  /* wait untill all data is transmitted */
  /* XXX JOEL says this is broken -- interrupts are OFF so NO ticks  */
  rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(100));

  if ( uart->chn == 1 ) {
    volatile uint8_t *scrP = (volatile uint8_t *)SH7750_SCSCR1;
    volatile uint8_t *smrP = (volatile uint8_t *)SH7750_SCSMR1;

    *scrP &= ~(SH7750_SCSCR_TE | SH7750_SCSCR_RE); /* disable operations */
    sh4uart_set_baudrate(uart, baud);
    *smrP = (uint8_t)smr;
    *scrP |= SH7750_SCSCR_TE | SH7750_SCSCR_RE;    /* enable operations */
  } else {
    volatile uint16_t *scrP = (volatile uint16_t *)SH7750_SCSCR2;
    volatile uint16_t *smrP = (volatile uint16_t *)SH7750_SCSMR2;

    *scrP &= ~(SH7750_SCSCR_TE | SH7750_SCSCR_RE); /* disable operations */
    sh4uart_set_baudrate(uart, baud);
    *smrP = (uint8_t)smr;
    *scrP |= SH7750_SCSCR_TE | SH7750_SCSCR_RE;    /* enable operations */
  }

  rtems_interrupt_enable(level);

  return RTEMS_SUCCESSFUL;
}

/*
 * sh4uart_handle_error --
 *     Perfoms error (Overrun, Framing & Parity) handling
 *
 * PARAMETERS:
 *     uart - pointer to UART descriptor structure
 *
 * RETURNS:
 *     nothing
 */
static void
sh4uart_handle_error(sh4uart *uart)
{
  if (uart->chn == SH4_SCI) {
    volatile uint8_t *scr = (volatile uint8_t *)SH7750_SCSCR1;
    *scr &= ~(SH7750_SCSSR1_ORER | SH7750_SCSSR1_FER | SH7750_SCSSR1_PER);
  } else {
    volatile uint16_t *scr = (volatile uint16_t *)SH7750_SCSCR2;
    *scr &= ~(SH7750_SCSSR2_ER | SH7750_SCSSR2_BRK | SH7750_SCSSR2_FER);
    *scr &= ~(SH7750_SCLSR2_ORER);
  }
}

/*
 * sh4uart_poll_read --
 *     This function tried to read character from SH-4 UART and perform
 *     error handling. When parity or framing error occured, return
 *     value dependent on termios input mode flags:
 *         - received character, if IGNPAR == 1
 *         - 0, if IGNPAR == 0 and PARMRK == 0
 *         - 0xff and 0x00 on next poll_read invocation, if IGNPAR == 0 and
 *           PARMRK == 1
 *
 * PARAMETERS:
 *     uart - pointer to UART descriptor structure
 *
 * RETURNS:
 *     code of received character or -1 if no characters received.
 */
int
sh4uart_poll_read(sh4uart *uart)
{
  int chn = uart->chn;
  int parity_error = 0;
  int break_occured = 0;
  int ch;

  if (uart->parerr_mark_flag == true) {
    uart->parerr_mark_flag = false;
    return 0;
  }

  if (chn == SH4_SCI) {
    if ((SCSSR1 & (SH7750_SCSSR1_PER | SH7750_SCSSR1_FER |
                 SH7750_SCSSR1_ORER)) != 0) {
      if (SCSSR1 & (SH7750_SCSSR1_PER | SH7750_SCSSR1_FER))
        parity_error = 1;
      sh4uart_handle_error(uart);
    }
    if ((SCSSR1 & SH7750_SCSSR1_RDRF) == 0)
      return -1;
  } else {
    if ((SCSSR2 & (SH7750_SCSSR2_ER | SH7750_SCSSR2_DR |
                 SH7750_SCSSR2_BRK)) != 0 ||
            (SCLSR2 & SH7750_SCLSR2_ORER) != 0) {
      if (SCSSR2 & (SH7750_SCSSR1_PER | SH7750_SCSSR1_FER))
        parity_error = 1;
      if (SCSSR2 & SH7750_SCSSR2_BRK)
        break_occured = 1;
      sh4uart_handle_error(uart);
    }
    if ((SCSSR2 & SH7750_SCSSR2_RDF) == 0)
      return -1;
  }

  if (parity_error && !(uart->c_iflag & IGNPAR)) {
    if (uart->c_iflag & PARMRK) {
      uart->parerr_mark_flag = true;
      return 0xff;
    } else
      return 0;
  }

  if (break_occured && !(uart->c_iflag & BRKINT)) {
    if (uart->c_iflag & IGNBRK)
      return 0;
    else
      return 0;   /* XXX -- SIGINT */
  }

  ch = SCRDR(chn);

  if (uart->chn == SH4_SCI) {
    volatile uint8_t *scr = (volatile uint8_t *)SH7750_SCSCR1;
    *scr &= ~SH7750_SCSSR1_RDRF;
  } else {
    volatile uint16_t *scr = (volatile uint16_t *)SH7750_SCSCR2;
    *scr &= ~SH7750_SCSSR2_RDF;
  }

  return ch;
}

/*
 * sh4uart_poll_write --
 *     This function transmit buffer byte-by-byte in polling mode.
 *
 * PARAMETERS:
 *     uart - pointer to the UART descriptor structure
 *     buf - pointer to transmit buffer
 *     len - transmit buffer length
 *
 * RETURNS:
 *     0
 */
int
sh4uart_poll_write(sh4uart *uart, const char *buf, int len)
{
  volatile uint8_t *ssr1 = (volatile uint8_t *)SH7750_SCSSR1;
  volatile uint16_t *ssr2 = (volatile uint16_t *)SH7750_SCSSR2;

  while (len) {
    if (uart->chn == SH4_SCI) {
      while ((SCSSR1 & SH7750_SCSSR1_TDRE) != 0) {
          SCTDR1 = *buf++;
          len--;
          *ssr1 &= ~SH7750_SCSSR1_TDRE;
      }
    } else {
      while ((SCSSR2 & SH7750_SCSSR2_TDFE) != 0) {
        int i;
        for (i = 0;
                i < 16 - TRANSMIT_TRIGGER_VALUE(SCFCR2 &
                    SH7750_SCFCR2_TTRG);
                i++) {
            SCTDR2 = *buf++;
            len--;
        }
        while ((SCSSR2 & SH7750_SCSSR2_TDFE) == 0 ||
                (SCSSR2 & SH7750_SCSSR2_TEND) == 0);
        *ssr2 &= ~(SH7750_SCSSR1_TDRE | SH7750_SCSSR2_TEND);
      }
    }
  }
  return 0;
}

/**********************************
 * Functions to handle interrupts *
 **********************************/
/* sh4uart1_interrupt_receive --
 *     UART interrupt handler routine -- SCI
 *     Receiving data
 *
 * PARAMETERS:
 *     vec - interrupt vector number
 *
 * RETURNS:
 *     none
 */
static rtems_isr
sh4uart1_interrupt_receive(rtems_vector_number vec)
{
  register int bp = 0;
  char buf[32];
  volatile uint8_t *ssr1 = (volatile uint8_t *)SH7750_SCSSR1;


  /* Find UART descriptor from vector number */
  sh4uart *uart = &sh4_uarts[0];

  while (1) {
    if ((bp < sizeof(buf) - 1) && ((SCSSR1 & SH7750_SCSSR1_RDRF) != 0)) {
      /* Receive character and handle frame/parity errors */
      if ((SCSSR1 & (SH7750_SCSSR1_PER | SH7750_SCSSR1_FER |
                      SH7750_SCSSR1_ORER)) != 0) {
        if (SCSSR1 & (SH7750_SCSSR1_PER | SH7750_SCSSR1_FER)) {
            if (!(uart->c_iflag & IGNPAR)) {
              if (uart->c_iflag & PARMRK) {
                buf[bp++] = 0xff;
                buf[bp++] = 0x00;
              } else
                buf[bp++] = 0x00;
            } else
              buf[bp++] = SCRDR1;
          }
          sh4uart_handle_error(uart);
      } else
          buf[bp++] = SCRDR1;
      *ssr1 &= ~SH7750_SCSSR1_RDRF;
    } else {
      if (bp != 0)
        rtems_termios_enqueue_raw_characters(uart->tty, buf, bp);
      break;
    }
  }
}

/* sh4uart2_interrupt_receive --
 *     UART interrupt handler routine -- SCIF
 *     Receiving data
 *
 * PARAMETERS:
 *     vec - interrupt vector number
 *
 * RETURNS:
 *     none
 */
static rtems_isr
sh4uart2_interrupt_receive(rtems_vector_number vec)
{
  register int bp = 0;
  char buf[32];
  volatile uint16_t *ssr2 = (volatile uint16_t *)SH7750_SCSSR2;


  /* Find UART descriptor from vector number */
  sh4uart *uart = &sh4_uarts[1];

  while (1) {
    if ((bp < sizeof(buf) - 1) && ((SCSSR2 & SH7750_SCSSR2_RDF) != 0)) {
      if ((SCSSR2 & (SH7750_SCSSR2_ER | SH7750_SCSSR2_DR |
                      SH7750_SCSSR2_BRK)) != 0 ||
              (SH7750_SCLSR2 & SH7750_SCLSR2_ORER) != 0) {
        if (SCSSR2 & SH7750_SCSSR2_ER) {
          if (!(uart->c_iflag & IGNPAR)) {
            if (uart->c_iflag & PARMRK) {
              buf[bp++] = 0xff;
              buf[bp++] = 0x00;
            } else
              buf[bp++] = 0x00;
          } else
              buf[bp++] = SCRDR1;
        }

        if (SCSSR2 & SH7750_SCSSR2_BRK) {
          if (uart->c_iflag & IGNBRK)
            buf[bp++] = 0x00;
          else
            buf[bp++] = 0x00;   /* XXX -- SIGINT */
        }

        sh4uart_handle_error(uart);
      } else
        buf[bp++] = SCRDR1;
      *ssr2 &= ~SH7750_SCSSR2_RDF;
    } else {
      if (bp != 0)
        rtems_termios_enqueue_raw_characters(uart->tty, buf, bp);
      break;
    }
  }
}


/* sh4uart1_interrupt_transmit --
 *     UART interrupt handler routine -- SCI
 *     It continues transmit data when old part of data is transmitted
 *
 * PARAMETERS:
 *     vec - interrupt vector number
 *
 * RETURNS:
 *     none
 */
static rtems_isr
sh4uart1_interrupt_transmit(rtems_vector_number vec)
{
  volatile uint8_t *scr1 = (volatile uint8_t *)SH7750_SCSCR1;
  volatile uint8_t *ssr1 = (volatile uint8_t *)SH7750_SCSSR1;

  /* Find UART descriptor from vector number */
  sh4uart *uart = &sh4_uarts[0];

  if (uart->tx_buf != NULL && uart->tx_ptr < uart->tx_buf_len) {
    while ((SCSSR1 & SH7750_SCSSR1_TDRE) != 0 &&
            uart->tx_ptr < uart->tx_buf_len) {
        SCTDR1 = uart->tx_buf[uart->tx_ptr++];
        *ssr1 &= ~SH7750_SCSSR1_TDRE;
    }
  } else {
    register int dequeue = uart->tx_buf_len;

    uart->tx_buf = NULL;
    uart->tx_ptr = uart->tx_buf_len = 0;

    /* Disable interrupts while we do not have any data to transmit */
    *scr1 &= ~SH7750_SCSCR_TIE;

    rtems_termios_dequeue_characters(uart->tty, dequeue);
  }
}

/* sh4uart2_interrupt_transmit --
 *     UART interrupt handler routine -- SCI
 *     It continues transmit data when old part of data is transmitted
 *
 * PARAMETERS:
 *     vec - interrupt vector number
 *
 * RETURNS:
 *     none
 */
static rtems_isr
sh4uart2_interrupt_transmit(rtems_vector_number vec)
{
  volatile uint8_t *ssr1 = (volatile uint8_t *)SH7750_SCSSR1;
  volatile uint16_t *scr2 = (volatile uint16_t *)SH7750_SCSCR2;

  /* Find UART descriptor from vector number */
  sh4uart *uart = &sh4_uarts[1];

  if (uart->tx_buf != NULL && uart->tx_ptr < uart->tx_buf_len) {
    while ((SCSSR2 & SH7750_SCSSR2_TDFE) != 0) {
        int i;
        for (i = 0;
             i < 16 - TRANSMIT_TRIGGER_VALUE(SCFCR2 & SH7750_SCFCR2_TTRG);
             i++)
          SCTDR2 = uart->tx_buf[uart->tx_ptr++];
        while ((SCSSR1 & SH7750_SCSSR1_TDRE) == 0 ||
                (SCSSR1 & SH7750_SCSSR1_TEND) == 0);
        *ssr1 &= ~(SH7750_SCSSR1_TDRE | SH7750_SCSSR2_TEND);
    }
  } else {
    register int dequeue = uart->tx_buf_len;

    uart->tx_buf = NULL;
    uart->tx_ptr = uart->tx_buf_len = 0;

    /* Disable interrupts while we do not have any data to transmit */
    *scr2 &= ~SH7750_SCSCR_TIE;

    rtems_termios_dequeue_characters(uart->tty, dequeue);
  }
}

/* sh4uart_interrupt_write --
 *     This function initiate transmitting of the buffer in interrupt mode.
 *
 * PARAMETERS:
 *     uart - pointer to the UART descriptor structure
 *     buf - pointer to transmit buffer
 *     len - transmit buffer length
 *
 * RETURNS:
 *     0
 */
rtems_status_code
sh4uart_interrupt_write(sh4uart *uart, const char *buf, int len)
{
  if (len > 0) {
    volatile uint8_t  *scr1 = (volatile uint8_t *)SH7750_SCSCR1;
    volatile uint16_t *scr2 = (volatile uint16_t *)SH7750_SCSCR2;

    while ((SCSSR1 & SH7750_SCSSR1_TEND) == 0);

    uart->tx_buf = buf;
    uart->tx_buf_len = len;
    uart->tx_ptr = 0;

    if (uart->chn == SH4_SCI)
      *scr1 |= SH7750_SCSCR_TIE;
    else
      *scr2 |= SH7750_SCSCR_TIE;
  }

  return RTEMS_SUCCESSFUL;
}

/* sh4uart_stop_remote_tx --
 *     This function stop data flow from remote device.
 *
 * PARAMETERS:
 *     uart - pointer to the UART descriptor structure
 *
 * RETURNS:
 *     RTEMS_SUCCESSFUL
 */
rtems_status_code
sh4uart_stop_remote_tx(sh4uart *uart)
{
  if ( uart->chn == 1 ) {
    volatile uint8_t *scr = (volatile uint8_t *)SH7750_SCSCR1;
    *scr &= ~(SH7750_SCSCR_RIE | SH7750_SCSCR_RE);
  } else {
    volatile uint16_t *scr = (volatile uint16_t *)SH7750_SCSCR2;
    *scr &= ~(SH7750_SCSCR_RIE | SH7750_SCSCR_RE);
  }

  return RTEMS_SUCCESSFUL;
}

/* sh4uart_start_remote_tx --
 *     This function resume data flow from remote device.
 *
 * PARAMETERS:
 *     uart - pointer to the UART descriptor structure
 *
 * RETURNS:
 *     RTEMS_SUCCESSFUL
 */
rtems_status_code
sh4uart_start_remote_tx(sh4uart *uart)
{
  if ( uart->chn == 1 ) {
    volatile uint8_t *scr = (volatile uint8_t *)SH7750_SCSCR1;
    *scr |= SH7750_SCSCR_RIE | SH7750_SCSCR_RE;
  } else {
    volatile uint16_t *scr = (volatile uint16_t *)SH7750_SCSCR2;
    *scr |= SH7750_SCSCR_RIE | SH7750_SCSCR_RE;
  }

  return RTEMS_SUCCESSFUL;
}

#ifdef SH4_WITH_IPL
/*********************************
 * Functions for SH-IPL gdb stub *
 *********************************/

/*
 * ipl_finish --
 *     Says gdb that program finished to get out from it.
 */
extern void ipl_finish(void);
__asm__ (
"   .global _ipl_finish\n"
"_ipl_finish:\n"
"   mov.l   __ipl_finish_value, r0\n"
"   trapa   #0x3f\n"
"   nop\n"
"   rts\n"
"   nop\n"
"   .align 4\n"
"__ipl_finish_value:\n"
"   .long   255"
);

extern int ipl_serial_input(int poll_count);
__asm__ (
"    .global _ipl_serial_input\n"
"_ipl_serial_input:\n"
"    mov  #1,r0\n"
"    trapa #0x3f\n"
"    nop\n"
"    rts\n"
"    nop\n");

extern void ipl_serial_output(const char *buf, int len);
__asm__ (
"    .global _ipl_serial_output\n"
"_ipl_serial_output:\n"
"    mov  #0,r0\n"
"    trapa #0x3f\n"
"    nop\n"
"    rts\n"
"    nop\n");

/* ipl_console_poll_read --
 *     poll read operation for simulator console through ipl mechanism.
 *
 * PARAMETERS:
 *     minor - minor device number
 *
 * RETURNS:
 *     character code red from UART, or -1 if there is no characters
 *     available
 */
int
ipl_console_poll_read(int minor)
{
    unsigned char buf;
    buf = ipl_serial_input(0x100000);
    return buf;
}

/* ipl_console_poll_write --
 *     wrapper for polling mode write function
 *
 * PARAMETERS:
 *     minor - minor device number
 *     buf - output buffer
 *     len - output buffer length
 *
 * RETURNS:
 *     result code (0)
 */
int
ipl_console_poll_write(int minor, const char *buf, int len)
{
  int c;
  while (len > 0) {
    c = (len < 64 ? len : 64);
    ipl_serial_output(buf, c);
    len -= c;
    buf += c;
  }
  return 0;
}
#endif
