59bbcfdb创建于 4月21日历史提交
/****************************************************************************
 * arch/arm/src/phy62xx/uart.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

/****************************************************************************
 *   @file     uart.c
 *   @brief    Contains all functions support for uart driver
 *   @version  0.0
 *   @date     19. Oct. 2017
 *   @author   qing.han
 *
 ****************************************************************************/

#include "rom_sym_def.h"
#include <string.h>
#include "bus_dev.h"
#include "mcu.h"
#include "gpio.h"
#include "clock.h"
#include "uart.h"
#include "pwrmgr.h"
#include "error.h"
#include "jump_function.h"

#include <nuttx/arch.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/serial/serial.h>
#include <nuttx/circbuf.h>
#include <nuttx/spinlock.h>

#define UART_TX_BUFFER_SIZE   64
#define UART_RX_BUFFER_SIZE   64
uint8_t fifo_data_store[2][UART_RX_FIFO_SIZE];
uint8_t fifo_len_store[2] =
{
  0, 0
};

typedef struct _uart_Context
{
  bool          enable;
  bool          rx_available; /* rx byte available */

  UART_INDEX_e  ID;
  AP_UART_TypeDef *reg;

  uint8_t irq;

  uint8_t       tx_state;
  uart_Tx_Buf_t tx_buf;
  uart_Cfg_t    cfg;
  uint8_t fifo_buf_store[UART_RX_BUFFER_SIZE];
  int buf_head;
  int buf_tail;
} uart_Ctx_t;

static uart_Ctx_t m_uartCtx[2] =
{
  {
    .ID = UART0,
    .reg = (AP_UART_TypeDef *) AP_UART0_BASE,
    .irq = PHY62XX_IRQ_UART0_IRQn,
    .enable = FALSE,
  },
  {
    .ID = UART1,
    .reg = (AP_UART_TypeDef *) AP_UART1_BASE,
    .irq = PHY62XX_IRQ_UART1_IRQn,
    .enable = FALSE,
  },
};

/****************************************************************************
 * Public Functions
 ****************************************************************************/

static int txmit_buf_use_tx_buf(UART_INDEX_e uart_index, uint8_t *buf,
    uint16_t len)
{
  uart_Tx_Buf_t *p_txbuf = &(m_uartCtx[uart_index].tx_buf);
  uint8_t *p_data;
  AP_UART_TypeDef *cur_uart = (AP_UART_TypeDef *) AP_UART0_BASE;

  if (len == 0 || buf == NULL)
      return PPlus_ERR_INVALID_PARAM;

  if (p_txbuf->tx_state == TX_STATE_UNINIT)
      return PPlus_ERR_NO_MEM;

  if (p_txbuf->tx_buf_size < len)
      return PPlus_ERR_NO_MEM;

  if (p_txbuf->tx_state != TX_STATE_IDLE)
    {
      if (p_txbuf->tx_data_size + len > p_txbuf->tx_buf_size)
          return PPlus_ERR_NO_MEM;

      _HAL_CS_ALLOC_();
      HAL_ENTER_CRITICAL_SECTION();
      memcpy(p_txbuf->tx_buf + p_txbuf->tx_data_size, buf, len);
      p_txbuf->tx_data_size += len;
      HAL_EXIT_CRITICAL_SECTION();
      return PPlus_SUCCESS;
    }

  memcpy(p_txbuf->tx_buf, buf, len);
  p_txbuf->tx_data_size = len;
  p_txbuf->tx_data_offset = 0;
  p_txbuf->tx_state = TX_STATE_TX;
  p_data = p_txbuf->tx_buf;

  /* len = p_txbuf->tx_data_size; */

  len = len > UART_TX_FIFO_SIZE ? UART_TX_FIFO_SIZE : len;

  if (uart_index == UART1)
      cur_uart = (AP_UART_TypeDef *) AP_UART1_BASE;

  cur_uart->IER &= ~(IER_ETBEI);

  while (len--)
    {
      cur_uart->THR = p_data[p_txbuf->tx_data_offset++];
    }

  if (uart_index == UART0)
      hal_pwrmgr_lock(MOD_UART0);
  else
      hal_pwrmgr_lock(MOD_UART1);

  cur_uart->IER |= IER_ETBEI;
  return PPlus_SUCCESS;
}

static int txmit_buf_polling(UART_INDEX_e uart_index,
    uint8_t *buf, uint16_t len)
{
  /* volatile int timeout = 0; */

  AP_UART_TypeDef *cur_uart = (AP_UART_TypeDef *) AP_UART0_BASE;

  if (uart_index == UART1)
      cur_uart = (AP_UART_TypeDef *) AP_UART1_BASE;

  HAL_WAIT_CONDITION_TIMEOUT(!(cur_uart->USR & USR_BUSY), 100000);

  while (len--)
    {
      HAL_WAIT_CONDITION_TIMEOUT((cur_uart->LSR & LSR_THRE), 100000);
      cur_uart->THR = *buf++;

      /* timeout = 0; */
    }

  /* wait shift register empty */

  HAL_WAIT_CONDITION_TIMEOUT((cur_uart->LSR & LSR_TEMT), 100000);
  return PPlus_SUCCESS;
}

static void irq_rx_handler(UART_INDEX_e uart_index, uint8_t flg)
{
  int i;
  uint8_t data[UART_RX_FIFO_SIZE];
  uint8_t len;
  AP_UART_TypeDef *cur_uart = (AP_UART_TypeDef *)AP_UART0_BASE;

  if (uart_index == UART1)
    {
      cur_uart = (AP_UART_TypeDef *) AP_UART1_BASE;
    }

  if (m_uartCtx[uart_index].cfg.use_fifo)
    {
      len = cur_uart->RFL;
      fifo_len_store[uart_index] = len;
      for (i = 0; i < len; i++)
        {
          data[i] = (uint8_t)(cur_uart->RBR & 0xff);
          fifo_data_store[uart_index][i] = data[i];
        }
    }
  else
    {
      len = 1;
      cur_uart->LSR;  /* clear interrupt */
      data[0] = (uint8_t)(cur_uart->RBR & 0xff);
    }

  if (m_uartCtx[uart_index].cfg.evt_handler)
    {
      uart_Evt_t evt;
      evt.type = flg;
      evt.data = data;
      evt.len = len;
      m_uartCtx[uart_index].cfg.evt_handler(&evt);
    }
}

static void irq_tx_empty_handler(UART_INDEX_e uart_index)
{
  uart_Tx_Buf_t *p_txbuf = &(m_uartCtx[uart_index].tx_buf);
  uint8_t *p_data;
  uint16_t len;
  AP_UART_TypeDef *cur_uart = (AP_UART_TypeDef *)AP_UART0_BASE;

  if (m_uartCtx[uart_index].enable == FALSE)
      return;

  if (m_uartCtx[uart_index].cfg.use_fifo == FALSE)
      return;

  if (m_uartCtx[uart_index].cfg.use_tx_buf == FALSE)
      return;

  if (p_txbuf->tx_state != TX_STATE_TX)
      return;

  p_data = p_txbuf->tx_buf;
  len = p_txbuf->tx_data_size - p_txbuf->tx_data_offset;
  len = len > UART_TX_FIFO_SIZE ? UART_TX_FIFO_SIZE : len;

  if (len == 0)
    {
      p_txbuf->tx_state = TX_STATE_IDLE;
      p_txbuf->tx_data_offset = 0;
      p_txbuf->tx_data_size = 0;

      if (m_uartCtx[uart_index].cfg.evt_handler)
        {
          uart_Evt_t evt =
            {
              .type = UART_EVT_TYPE_TX_COMPLETED,
              .data = NULL,
              .len = 0,
            };

          m_uartCtx[uart_index].cfg.evt_handler(&evt);
        }

      if (UART0 == uart_index)
          hal_pwrmgr_unlock(MOD_UART0);
      else
          hal_pwrmgr_unlock(MOD_UART1);

      return;
    }

  if (uart_index == UART1)
      cur_uart = (AP_UART_TypeDef *) AP_UART1_BASE;

  while (len--)
    {
      cur_uart->THR = p_data[p_txbuf->tx_data_offset++];
    }
}

static int uart_hw_deinit(UART_INDEX_e uart_index)
{
  MODULE_e mod = MOD_UART0;
  IRQn_Type irq_type = PHY62XX_IRQ_UART0_IRQn;
  AP_UART_TypeDef *cur_uart = AP_UART0;

  if (uart_index == UART1)
    {
      mod = MOD_UART1;
      irq_type = PHY62XX_IRQ_UART1_IRQn;
      cur_uart = AP_UART1;
    }

  NVIC_DisableIRQ(irq_type);
  hal_gpio_fmux(m_uartCtx[uart_index].cfg.tx_pin, Bit_DISABLE);
  hal_gpio_fmux(m_uartCtx[uart_index].cfg.rx_pin, Bit_DISABLE);
  cur_uart->LCR = 0x80;
  cur_uart->DLM = 0;
  cur_uart->DLL = 0;
  cur_uart->LCR = 0;
  cur_uart->FCR = 0;
  cur_uart->IER = 0;

  /* hal_clk_gate_enable(mod); */

  hal_clk_reset(mod);
  hal_clk_gate_disable(mod);

  return PPlus_SUCCESS;
}

int hal_uart_rxint_en(UART_INDEX_e uart_index, bool en)
{
  AP_UART_TypeDef *cur_uart = AP_UART0;
  if (uart_index == UART1)
    {
      cur_uart = AP_UART1;
    }

  if (en)
    {
      cur_uart->IER |= IER_ERBFI;
    }
  else
    {
      cur_uart->IER &= ~IER_ERBFI;
    }

  return 0;
}

int hal_uart_txint_en(UART_INDEX_e uart_index, bool en)
{
  AP_UART_TypeDef *cur_uart = AP_UART0;
  if (uart_index == UART1)
    {
      cur_uart = AP_UART1;
    }

  if (en)
    {
      cur_uart->IER |= IER_ETBEI;
    }
  else
    {
      cur_uart->IER &= ~IER_ETBEI;
    }

  return 0;
}

extern uint32_t timer_sysclk_get_clk(void);

int uart_hw_init(UART_INDEX_e uart_index)
{
  uart_Cfg_t *pcfg;
  int pclk = timer_sysclk_get_clk();
  uint32_t dll;
  AP_UART_TypeDef *cur_uart = AP_UART0;
  MODULE_e mod = MOD_UART0;
  IRQn_Type irq_type = PHY62XX_IRQ_UART0_IRQn;
  gpio_fmux_e fmux_tx = FMUX_UART0_TX;
  gpio_fmux_e fmux_rx = FMUX_UART0_RX;
  uart_hw_deinit(uart_index);

  if (uart_index == UART1)
    {
      cur_uart = AP_UART1;
      mod = MOD_UART1;
      irq_type = PHY62XX_IRQ_UART1_IRQn;
      fmux_tx = FMUX_UART1_TX;
      fmux_rx = FMUX_UART1_RX;
    }

  if ((m_uartCtx[uart_index].cfg.tx_pin == GPIO_DUMMY) &&
      (m_uartCtx[uart_index].cfg.rx_pin == GPIO_DUMMY))
    {
      return PPlus_ERR_INVALID_PARAM;
    }

  pcfg = &(m_uartCtx[uart_index].cfg);
  hal_clk_gate_enable(mod);
  hal_clk_reset(mod);

  /*  if (m_uartCtx[uart_index].enable == FALSE){
   *      hal_gpio_fmux(P9, Bit_DISABLE);
   *      hal_gpio_fmux(P10, Bit_DISABLE);
   *  }
   */

  hal_gpio_pull_set(pcfg->tx_pin, GPIO_PULL_UP);
  hal_gpio_pull_set(pcfg->rx_pin, GPIO_PULL_UP);
  hal_gpio_fmux_set(pcfg->tx_pin, fmux_tx);
  hal_gpio_fmux_set(pcfg->rx_pin, fmux_rx);
  cur_uart->LCR = 0;
  dll = ((pclk >> 4) + (pcfg->baudrate >> 1)) / pcfg->baudrate;
  cur_uart->MCR = 0x0;
  cur_uart->LCR = 0x80;
  cur_uart->DLM = (dll & 0xff00) >> 8;
  cur_uart->DLL = (dll & 0xff);

  if (pcfg->parity)
      cur_uart->LCR = 0x1b; /* 8bit, 1 stop even parity */
  else
      cur_uart->LCR = 0x3;  /* 8bit, 1 stop no parity */

  if (pcfg->use_fifo)       /* set fifo, enable tx FIFO mode(empty trigger), rx FIFO mode(1/2 trigger) */
    {
      cur_uart->FCR = FCR_TX_FIFO_RESET | FCR_RX_FIFO_RESET | FCR_FIFO_ENABLE
          | UART_FIFO_RX_TRIGGER | UART_FIFO_TX_TRIGGER;
    }
  else
    {
      cur_uart->FCR = 0;
    }

  /* enable Received Data Available Interrupt */

  cur_uart->IER = IER_ERBFI;

  if (pcfg->use_fifo)
    {
      cur_uart->IER |= IER_PTIME;
    }

  if (pcfg->use_tx_buf)
      cur_uart->IER |= IER_ETBEI;

  NVIC_SetPriority(irq_type, IRQ_PRIO_HAL);
  NVIC_EnableIRQ(irq_type);
  return PPlus_SUCCESS;
}

/****************************************************************************
 *   @fn          hal_UART0_IRQHandler
 *
 *   @brief       This function process for uart interrupt
 *
 *   input parameters
 *
 *   @param       None.
 *
 *   output parameters
 *
 *   @param       None.
 *
 *   @return      None.
 ****************************************************************************/

void __ATTR_SECTION_SRAM__  hal_UART0_IRQHandler(void)
{
  uint8_t IRQ_ID = (AP_UART0->IIR & 0x0f);

  /* if (m_uartCtx[UART0].enable == FALSE)
   *    return;
   */

  switch (IRQ_ID)
    {
      case TIMEOUT_IRQ:
          irq_rx_handler(UART0, UART_EVT_TYPE_RX_DATA_TO);
          break;

      case RDA_IRQ:
          irq_rx_handler(UART0, UART_EVT_TYPE_RX_DATA);
          break;

      case THR_EMPTY:
          irq_tx_empty_handler(UART0);
          break;

      case RLS_IRQ:
          break;

      case BUSY_IRQ:
          (void)AP_UART0->USR;
          break;

      default:
          break;
    }
}

void __attribute__((used)) hal_UART1_IRQHandler(void)
{
  uint8_t IRQ_ID = (AP_UART1->IIR & 0x0f);

  /* if(m_uartCtx[UART1].enable == FALSE)
   *   return;
   */

  switch (IRQ_ID)
    {
      case TIMEOUT_IRQ:
          irq_rx_handler(UART1, UART_EVT_TYPE_RX_DATA_TO);
          break;

      case RDA_IRQ:
          irq_rx_handler(UART1, UART_EVT_TYPE_RX_DATA);
          break;

      case THR_EMPTY:
          irq_tx_empty_handler(UART1);
          break;

      case RLS_IRQ:
          break;

      case BUSY_IRQ:
          (void)AP_UART1->USR;
          break;

      default:
          break;
    }
}

static void uart_wakeup_process_0(void)
{
  uart_hw_init(UART0);
}

static void uart_wakeup_process_1(void)
{
  uart_hw_init(UART1);
}

int hal_uart_init(uart_Cfg_t cfg, UART_INDEX_e uart_index)
{
  if (m_uartCtx[uart_index].enable)
      return PPlus_ERR_BUSY;

  /* if(cfg.hw_fwctrl || cfg.parity)
   * return PPlus_ERR_NOT_SUPPORTED;
   */

  if (cfg.hw_fwctrl)
      return PPlus_ERR_NOT_SUPPORTED;

  /* memset(&(m_uartCtx[uart_index]), 0, sizeof(uart_Ctx_t)); */

  memcpy(&(m_uartCtx[uart_index].cfg), &cfg, sizeof(uart_Cfg_t));
  uart_hw_init(uart_index);
  m_uartCtx[uart_index].enable = TRUE;

  if (uart_index == UART0)
      hal_pwrmgr_register(MOD_UART0, NULL, uart_wakeup_process_0);
  else
      hal_pwrmgr_register(MOD_UART1, NULL, uart_wakeup_process_1);

  return PPlus_SUCCESS;
}

int hal_uart_deinit(UART_INDEX_e uart_index)
{
  uart_hw_deinit(uart_index);
  m_uartCtx[uart_index].enable = FALSE;

  if (uart_index == UART0)
      hal_pwrmgr_unregister(MOD_UART0);
  else
      hal_pwrmgr_unregister(MOD_UART1);

  return PPlus_SUCCESS;
}

int hal_uart_set_tx_buf(UART_INDEX_e uart_index, uint8_t *buf, uint16_t size)
{
  uart_Tx_Buf_t *p_txbuf = &(m_uartCtx[uart_index].tx_buf);

  if (m_uartCtx[uart_index].enable == FALSE)
      return PPlus_ERR_INVALID_STATE;

  if (m_uartCtx[uart_index].cfg.use_tx_buf == FALSE)
      return PPlus_ERR_NOT_SUPPORTED;

  if (p_txbuf->tx_state != TX_STATE_UNINIT)
      return PPlus_ERR_INVALID_STATE;

  _HAL_CS_ALLOC_();
  HAL_ENTER_CRITICAL_SECTION();
  p_txbuf->tx_buf = buf;
  p_txbuf->tx_buf_size = size;
  p_txbuf->tx_data_offset = 0;
  p_txbuf->tx_data_size = 0;
  p_txbuf->tx_state = TX_STATE_IDLE;
  HAL_EXIT_CRITICAL_SECTION();
  return PPlus_SUCCESS;
}

int hal_uart_get_tx_ready(UART_INDEX_e uart_index)
{
  if (m_uartCtx[uart_index].cfg.use_tx_buf == FALSE)
      return PPlus_SUCCESS;

  if (m_uartCtx[uart_index].tx_buf.tx_state == TX_STATE_IDLE)
      return PPlus_SUCCESS;

  return PPlus_ERR_BUSY;
}

int hal_uart_send_buff(UART_INDEX_e uart_index, uint8_t *buff, uint16_t len)
{
  if (m_uartCtx[uart_index].cfg.use_tx_buf)
    {
      return txmit_buf_use_tx_buf(uart_index, buff, len);
    }

  return txmit_buf_polling(uart_index, buff, len);
}

int hal_uart_send_byte(UART_INDEX_e uart_index, unsigned char data)
{
  AP_UART_TypeDef *cur_uart = (AP_UART_TypeDef *) AP_UART0_BASE;

  if (uart_index == UART1)
      cur_uart = (AP_UART_TypeDef *) AP_UART1_BASE;

  HAL_WAIT_CONDITION_TIMEOUT((cur_uart->LSR & LSR_THRE), 10000);
  cur_uart->THR = data;
  HAL_WAIT_CONDITION_TIMEOUT((cur_uart->LSR & LSR_TEMT), 10000);
  return PPlus_SUCCESS;
}

static int pplus_uart_interrupt(int irq, void *context, void *arg);

static int pplus_uart_setup(struct uart_dev_s *dev)
{
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;
  uart_Cfg_t cfg =
    {
      .tx_pin = P9,
      .rx_pin = P10,
      .rts_pin = GPIO_DUMMY,
      .cts_pin = GPIO_DUMMY,
      .baudrate = 115200,
      .use_fifo = TRUE,
      .hw_fwctrl = FALSE,
      .use_tx_buf = FALSE,
      .parity     = FALSE,
      .evt_handler = NULL,
    };

  hal_uart_init(cfg, priv->ID);

  /* TODO: configure UART if not selected as console */

  return OK;
}

/****************************************************************************
 * Name: pplus_uart_shutdown
 *
 * Description:
 *   Disable the UART.  This method is called when the serial
 *   port is closed
 *
 ****************************************************************************/

static void pplus_uart_shutdown(struct uart_dev_s *dev)
{
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;

  /* Disable interrupts */

  /* Reset hardware and disable Rx and Tx */

  hal_uart_deinit(priv->ID);
}

/****************************************************************************
 * Name: pplus_uart_attach
 *
 * Description:
 *   Configure the UART to operation in interrupt driven mode.  This method
 *   is called when the serial port is opened.  Normally, this is just after
 *   the the setup() method is called, however, the serial console may
 *   operate in a non-interrupt driven mode during the boot phase.
 *
 *   RX and TX interrupts are not enabled when by the attach method (unless
 *   the hardware supports multiple levels of interrupt enabling).
 *   The RX and TX interrupts are not enabled until the txint() and rxint()
 *   methods are called.
 *
 ****************************************************************************/

static int pplus_uart_attach(struct uart_dev_s *dev)
{
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;
  int ret;

  /* Attach and enable the IRQ(s).  The interrupts are (probably) still
   * disabled in the C2 register.
   */

  ret = irq_attach(priv->irq, pplus_uart_interrupt, dev);
  if (ret == OK)
    {
      up_enable_irq(priv->irq);
    }

  return ret;
}

/****************************************************************************
 * Name: pplus_uart_detach
 *
 * Description:
 *   Detach UART interrupts.  This method is called when the serial port is
 *   closed normally just before the shutdown method is called.
 *   The exception is the serial console which is never shutdown.
 *
 ****************************************************************************/

static void pplus_uart_detach(struct uart_dev_s *dev)
{
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;

  /* Disable interrupts */

  priv->reg->IER = 0;
  up_disable_irq(priv->irq);

  /* Detach from the interrupt(s) */

  irq_detach(priv->irq);
}

/****************************************************************************
 * Name: pplus_uart_interrupt
 *
 * Description:
 *   This is the UART interrupt handler.  It will be invoked when an
 *   interrupt is received on the 'irq'.  It should call uart_xmitchars or
 *   uart_recvchars to perform the appropriate data transfers.  The
 *   interrupt handling logic must be able to map the 'arg' to the
 *   appropriate uart_dev_s structure in order to call these functions.
 *
 ****************************************************************************/

static int pplus_uart_interrupt(int irq, void *context, void *arg)
{
  struct uart_dev_s *dev = (struct uart_dev_s *)arg;
  uart_Ctx_t *priv = (uart_Ctx_t *)(dev->priv);

  uint8_t IRQ_ID = (priv->reg->IIR & 0x0f);

  switch (IRQ_ID)
    {
      case TIMEOUT_IRQ:
      case RDA_IRQ:
          priv->rx_available = true;
          priv->reg->LSR;  /* clear interrupt */
          uart_recvchars(dev);
          break;

      case THR_EMPTY:
      case RLS_IRQ:
          break;

      case BUSY_IRQ:
         (void)priv->reg->USR;
         break;

      default:
         break;
  }

  return OK;
}

static int pplus_uart_ioctl(struct file *filep, int cmd, unsigned long arg)
{
#ifdef CONFIG_SERIAL_TERMIOS
  struct inode *inode = filep->f_inode;
  struct uart_dev_s *dev = inode->i_private;
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;
  struct uart_config_s *config = &priv->config;
#endif
  int ret = OK;

  switch (cmd)
    {
#ifdef CONFIG_SERIAL_TERMIOS
      case TCGETS:
        {
          struct termios *termiosp = (struct termios *)arg;

          if (!termiosp)
            {
              ret = -EINVAL;
              break;
            }

          termiosp->c_cflag = ((config->parity != 0) ? PARENB : 0)
                              | ((config->parity == 1) ? PARODD : 0)
                              | ((config->stopbits2) ? CSTOPB : 0) |
#ifdef CONFIG_SERIAL_OFLOWCONTROL
                              ((config->oflow) ? CCTS_OFLOW : 0) |
#endif
#ifdef CONFIG_SERIAL_IFLOWCONTROL
                              ((config->iflow) ? CRTS_IFLOW : 0) |
#endif
                              CS8;

          cfsetispeed(termiosp, config->baud);

          break;
        }

      case TCSETS:
        {
          struct termios *termiosp = (struct termios *)arg;

          if (!termiosp)
            {
              ret = -EINVAL;
              break;
            }

          /* Perform some sanity checks before accepting any changes */

          if ((termiosp->c_cflag & CSIZE) != CS8)
            {
              ret = -EINVAL;
              break;
            }

#ifndef HAVE_UART_STOPBITS
          if ((termiosp->c_cflag & CSTOPB) != 0)
            {
              ret = -EINVAL;
              break;
            }
#endif

          if (termiosp->c_cflag & PARODD)
            {
              ret = -EINVAL;
              break;
            }

          /* TODO: CCTS_OFLOW and CRTS_IFLOW */

          /* Parity */

          if (termiosp->c_cflag & PARENB)
            {
              config->parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
            }
          else
            {
              config->parity = 0;
            }

#ifdef HAVE_UART_STOPBITS
          /* Stop bits */

          config->stopbits2 = (termiosp->c_cflag & CSTOPB) != 0;
#endif

          /* Note that only cfgetispeed is used because we have knowledge
           * that only one speed is supported.
           */

          config->baud = cfgetispeed(termiosp);

          /* Effect the changes */

          pplus_uart_set_format(dev);

          break;
        }
#endif

      default:
        {
          ret = -ENOTTY;
          break;
        }
    }

  return ret;
}

/****************************************************************************
 * Name: pplus_uart_receive
 *
 * Description:
 *   Called (usually) from the interrupt level to receive one
 *   character from the UART.  Error bits associated with the
 *   receipt are provided in the return 'status'.
 *
 ****************************************************************************/

static int pplus_uart_receive(struct uart_dev_s *dev, unsigned int *status)
{
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;

  /* uint32_t data;
   * static uint8_t fifo_buf_store[UART_RX_BUFFER_SIZE];
   * static int buf_head;
   * static int buf_tail;
   */

  /* Put fifo data into loopback buffer */

  for (int i = 0; i < fifo_len_store[priv->ID] ; i++)
    {
      priv->fifo_buf_store[priv->buf_head] = fifo_data_store[priv->ID][i];
      priv->buf_head = priv->buf_head + 1;
      if (priv->buf_head == UART_RX_BUFFER_SIZE)
        {
          priv->buf_head = 0;
        }
    }

  fifo_len_store[priv->ID] = 0;
  if (priv->buf_tail == UART_RX_BUFFER_SIZE)
    {
      priv->buf_tail = 0;
    }

  priv->rx_available = false;

  /* Return receiver control information */

  if (status)
    {
      *status = 0x00;
    }

  /* Then return the fifo data byte by byte. */

  return (char)priv->fifo_buf_store[priv->buf_tail++];
}

/****************************************************************************
 * Name: pplus_uart_rxint
 *
 * Description:
 *   Call to enable or disable RX interrupts
 *
 ****************************************************************************/

static void pplus_uart_rxint(struct uart_dev_s *dev, bool enable)
{
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;

  hal_uart_rxint_en(priv->ID, enable);
}

/****************************************************************************
 * Name: pplus_uart_rxavailable
 *
 * Description:
 *   Return true if the receive register is not empty
 *
 ****************************************************************************/

static bool pplus_uart_rxavailable(struct uart_dev_s *dev)
{
  static int len_fifo;
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;

  /* Detect the length of received data from fifo */

  if (len_fifo == 0)
    {
      hal_UART0_IRQHandler();
      len_fifo = fifo_len_store[priv->ID];
    }

  if (len_fifo <= 0)
    {
      len_fifo = 0;
      return len_fifo;
    }
  else
    {
      return len_fifo--;
    }
}

/****************************************************************************
 * Name: pplus_uart_send
 *
 * Description:
 *   This method will send one byte on the UART.
 *
 ****************************************************************************/

static void pplus_uart_send(struct uart_dev_s *dev, int ch)
{
  uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv;

  hal_uart_send_byte(priv->ID, (uint8_t)ch);
}

/****************************************************************************
 * Name: pplus_uart_txint
 *
 * Description:
 *   Call to enable or disable TX interrupts
 *
 ****************************************************************************/

static void pplus_uart_txint(struct uart_dev_s *dev, bool enable)
{
  /* uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv; */

  if (enable)
    {
      irqstate_t flags = enter_critical_section();
      uart_xmitchars(dev);
      leave_critical_section(flags);
    }
}

/****************************************************************************
 * Name: pplus_uart_txready
 *
 * Description:
 *   Return true if the tranmsit data register is empty
 *
 ****************************************************************************/

static bool pplus_uart_txready(struct uart_dev_s *dev)
{
  /* uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv; */

  /* Return true if the transmit FIFO is "not full." */

  return true;
}

/****************************************************************************
 * Name: pplus_uart_txempty
 *
 * Description:
 *   Return true if the transmit data register is empty
 *
 ****************************************************************************/

static bool pplus_uart_txempty(struct uart_dev_s *dev)
{
  /* uart_Ctx_t *priv = (uart_Ctx_t *)dev->priv; */

  /* Return true if the transmit FIFO is "empty." */

  return true;
}
#define CONFIG_UART0_RXBUFSIZE 256
#define CONFIG_UART0_TXBUFSIZE 256

static char g_uart0rxbuffer[CONFIG_UART0_RXBUFSIZE];
static char g_uart0txbuffer[CONFIG_UART0_TXBUFSIZE];

static const struct uart_ops_s g_pplus_uart_ops =
{
  .setup          = pplus_uart_setup,
  .shutdown       = pplus_uart_shutdown,
  .attach         = pplus_uart_attach,
  .detach         = pplus_uart_detach,
  .ioctl          = pplus_uart_ioctl,
  .receive        = pplus_uart_receive,
  .rxint          = pplus_uart_rxint,
  .rxavailable    = pplus_uart_rxavailable,
#ifdef CONFIG_SERIAL_IFLOWCONTROL
  .rxflowcontrol  = NULL,
#endif
  .send           = pplus_uart_send,
  .txint          = pplus_uart_txint,
  .txready        = pplus_uart_txready,
  .txempty        = pplus_uart_txempty,
};

static uart_dev_t g_uart0port =
{
  .recv =
    {
      .size   = CONFIG_UART0_RXBUFSIZE,
      .buffer = g_uart0rxbuffer,
    },

  .xmit =
    {
      .size   = CONFIG_UART0_TXBUFSIZE,
      .buffer = g_uart0txbuffer,
    },

  .ops      = &g_pplus_uart_ops,
  .priv     = &m_uartCtx[0],
};

void arm_earlyserialinit(void)
{
}

/****************************************************************************
 * Name: stm32serial_getregit
 *
 * Description:
 *   Register serial console and serial ports.  This assumes
 *   that arm_earlyserialinit was called previously.
 *
 ****************************************************************************/

#  define CONSOLE_DEV         g_uart0port /* UART0 is console */
#  define TTYS0_DEV           g_uart0port /* UART0 is ttyS0 */

void arm_serialinit(void)
{
  /* #ifdef HAVE_UART_CONSOLE */

  /* Register the serial console */

  uart_register("/dev/console", &CONSOLE_DEV);

  /* #endif */

  uart_register("/dev/ttyS0", &TTYS0_DEV);
}

/****************************************************************************
 * Name: up_putc
 *
 * Description:
 *   Provide priority, low-level access to support OS debug writes
 *
 ****************************************************************************/

void up_putc(int ch)
{
  hal_uart_send_byte(UART0, (char)ch);
}

struct h4uart_param_s
{
  struct circbuf_s *pcirc_h2c;
  sem_t            *psem_h2c ;
  struct circbuf_s *pcirc_c2h;
  sem_t            *psem_c2h ;
};

void h4uart_rx_irq(void *arg)
{
  AP_UART_TypeDef *preg = AP_UART1;
  uint8_t buf[UART_RX_FIFO_SIZE];
  int i;
  int len = preg->RFL;
  struct h4uart_param_s *param = (struct h4uart_param_s *)arg;
  if (len)
    {
      for (i = 0; i < len; i++)
        {
          buf[i] = (uint8_t)(preg->RBR & 0xff);
        }

      circbuf_write(param->pcirc_h2c, buf, len);
      nxsem_post(param->psem_h2c);
    }
}

void h4uart_tx_irq(void *arg)
{
  AP_UART_TypeDef *preg = AP_UART1;
  uint8_t buf[UART_TX_FIFO_SIZE];
  int i;
  int len;
  struct h4uart_param_s *param = (struct h4uart_param_s *)arg;

  len = circbuf_read(param->pcirc_c2h, buf, UART_TX_FIFO_SIZE);
  if (circbuf_used(param->pcirc_c2h) == 0)
      hal_uart_txint_en(UART1, false);

  if (len)
    {
      for (i = 0; i < len; i++)
        {
          while (preg->TFL >= UART_TX_FIFO_SIZE)
            {
            }

          preg->THR = buf[i];
        }
    }
}

static int h4uart_interrupt(int irq, void *context, void *arg)
{
  AP_UART_TypeDef *preg = AP_UART1;

  uint8_t IRQ_ID = preg->IIR & 0x0f;

  switch (IRQ_ID)
    {
      case TIMEOUT_IRQ:
      case RDA_IRQ:
          h4uart_rx_irq(arg);
          break;
      case THR_EMPTY:
          h4uart_tx_irq(arg);
          break;
      case RLS_IRQ:
      case BUSY_IRQ:
          preg->USR;
          break;
      default:
          break;
    }

  return 0;
}

int h4uart_init(void *param)
{
  uart_Cfg_t pcfg1 =
    {
      .tx_pin = P32,
      .rx_pin = P31,
      .rts_pin = GPIO_DUMMY,
      .cts_pin = GPIO_DUMMY,
      .baudrate = 115200,
      .use_fifo = TRUE,
      .hw_fwctrl = FALSE,
      .use_tx_buf = FALSE,
      .parity     = FALSE,
      .evt_handler = NULL,
    };

  irq_attach(PHY62XX_IRQ_UART1_IRQn, h4uart_interrupt, param);
  hal_uart_init(pcfg1, UART1);
  hal_uart_txint_en(UART1, true);
  hal_uart_rxint_en(UART1, true);
  return 0;
}