text: /*
   * File      : serial.c
   * This file is part of RT-Thread RTOS
   * COPYRIGHT (C) 2006, RT-Thread Development Team
   *
   * The license and distribution terms for this file may be
   * found in the file LICENSE in this distribution or at
   * http://openlab.rt-thread.com/license/LICENSE
   *
   * Change Logs:
   * Date           Author       Notes
   * 2006-08-23     Bernard      first version
   * 2009-05-14     Bernard      add RT-THread device interface
   *
   * 2011-12-17	  nl1031	   MicroBlaze
   */
  
  #include <rthw.h>
  #include <rtthread.h>
  #include "serial.h"
  
  typedef volatile rt_uint32_t REG32;
  struct rt_mb_uart_lite_hw
  {
  	REG32	 Rx_FIFO; 	// Receiver Holding Register
  	REG32	 Tx_FIFO; 	// Transmitter Holding Register
  	REG32	 STAT_REG; 	// Channel Status Register
  	REG32	 CTRL_REG; 	// Control Register
  };
  
  struct rt_mb_uart_lite
  {
  	struct rt_device parent;
  
  	struct rt_mb_uart_lite_hw* hw_base;
  	rt_uint16_t peripheral_id;
  	rt_uint32_t baudrate;
  
  	/* reception field */
  	rt_uint16_t save_index, read_index;
  	rt_uint8_t  rx_buffer[RT_UART_RX_BUFFER_SIZE];
  };
  #ifdef RT_USING_UART1
  struct rt_mb_uart_lite serial1;
  #endif
  
  static void rt_hw_serial_isr(void)
  {
  	unsigned int status;
  	rt_base_t level;
  	struct rt_device* device;
  	struct rt_mb_uart_lite* serial = RT_NULL;
  
  #ifdef RT_USING_UART1
  	/* serial 1 */
  	serial = &serial1;
  #endif
  	RT_ASSERT(serial != RT_NULL);
  
  	/* get generic device object */
  	device = (rt_device_t)serial;
  
  	/* disable interrupt */
  	level = rt_hw_interrupt_disable();
  
  	/* get uart status register */
  	status = serial->hw_base->STAT_REG;
  	while (status & XUL_SR_RX_FIFO_VALID_DATA)
  	{
  		/* get received character */
  		serial->rx_buffer[serial->save_index] = serial->hw_base->Rx_FIFO;
  
  		/* move to next position */
  		serial->save_index ++;
  		if (serial->save_index >= RT_UART_RX_BUFFER_SIZE)
  			serial->save_index = 0;
  
  		/* if the next position is read index, discard this 'read char' */
  		if (serial->save_index == serial->read_index)
  		{
  			serial->read_index ++;
  			if (serial->read_index >= RT_UART_RX_BUFFER_SIZE)
  				serial->read_index = 0;
  		}
  		status = serial->hw_base->STAT_REG;
  	}
  	/* enable interrupt */
  	rt_hw_interrupt_enable(level);
  
  	/* indicate to upper layer application */
  	if (device->rx_indicate != RT_NULL)
  		device->rx_indicate(device, 1);
  
  }
  
  static rt_err_t rt_serial_init (rt_device_t dev)
  {
  	struct rt_mb_uart_lite* serial = (struct rt_mb_uart_lite*) dev;
  
  	RT_ASSERT(serial != RT_NULL);
  
  	RT_ASSERT(serial->peripheral_id != XPAR_UARTLITE_1_DEVICE_ID);
  
  
  	/* reset rx index */
  	serial->save_index = 0;
  	serial->read_index = 0;
  
  	/* reset rx buffer */
  	rt_memset(serial->rx_buffer, 0, RT_UART_RX_BUFFER_SIZE);
  
  	return RT_EOK;
  }
  
  static rt_err_t rt_serial_open(rt_device_t dev, rt_uint16_t oflag)
  {
  	struct rt_mb_uart_lite *serial = (struct rt_mb_uart_lite*)dev;
  	RT_ASSERT(serial != RT_NULL);
  
  	if (dev->flag & RT_DEVICE_FLAG_INT_RX)
  	{
  		/* enable UART rx interrupt */
  		serial->hw_base->CTRL_REG = XUL_CR_ENABLE_INTR; 		/* enable interrupt */
  
  		/* install UART handler */
  		rt_hw_interrupt_install(serial->peripheral_id, (rt_isr_handler_t)rt_hw_serial_isr, RT_NULL);
  		rt_hw_interrupt_umask(serial->peripheral_id);
  	}
  
  	return RT_EOK;
  }
  
  static rt_err_t rt_serial_close(rt_device_t dev)
  {
  	struct rt_mb_uart_lite *serial = (struct rt_mb_uart_lite*)dev;
  	RT_ASSERT(serial != RT_NULL);
  
  	if (dev->flag & RT_DEVICE_FLAG_INT_RX)
  	{
  		/* disable interrupt */
  		serial->hw_base->CTRL_REG =  0; 		/* RxReady interrupt */
  	}
  
  	return RT_EOK;
  }
  
  static rt_size_t rt_serial_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
  {
  	rt_uint8_t* ptr;
  	struct rt_mb_uart_lite *serial = (struct rt_mb_uart_lite*)dev;
  	RT_ASSERT(serial != RT_NULL);
  
  	/* point to buffer */
  	ptr = (rt_uint8_t*) buffer;
  
  	if (dev->flag & RT_DEVICE_FLAG_INT_RX)
  	{
  		while (size)
  		{
  			/* interrupt receive */
  			rt_base_t level;
  
  			/* disable interrupt */
  			level = rt_hw_interrupt_disable();
  			if (serial->read_index != serial->save_index)
  			{
  				*ptr = serial->rx_buffer[serial->read_index];
  
  				serial->read_index ++;
  				if (serial->read_index >= RT_UART_RX_BUFFER_SIZE)
  					serial->read_index = 0;
  			}
  			else
  			{
  				/* no data in rx buffer */
  
  				/* enable interrupt */
  				rt_hw_interrupt_enable(level);
  				break;
  			}
  
  			/* enable interrupt */
  			rt_hw_interrupt_enable(level);
  
  			ptr ++; size --;
  		}
  
  		return (rt_uint32_t)ptr - (rt_uint32_t)buffer;
  	}
  	else if (dev->flag & RT_DEVICE_FLAG_DMA_RX)
  	{
  		/* not support right now */
  		RT_ASSERT(0);
  	}
  	else
  	{
  		/* poll mode */
  		while (size)
  		{
  			/* Wait for Full Rx Buffer */
  			while (!(serial->hw_base->STAT_REG & XUL_SR_RX_FIFO_VALID_DATA));
  
  			/* Read Character */
  			*ptr = serial->hw_base->Rx_FIFO;
  			ptr ++;
  			size --;
  		}
  
  		return (rt_size_t)ptr - (rt_size_t)buffer;
  	}
  
  	return 0;
  }
  
  static rt_size_t rt_serial_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
  {
  	rt_uint8_t* ptr;
  	struct rt_mb_uart_lite *serial = (struct rt_mb_uart_lite*)dev;
  	RT_ASSERT(serial != RT_NULL);
  
  	ptr = (rt_uint8_t*) buffer;
  	if (dev->open_flag & RT_DEVICE_OFLAG_WRONLY)
  	{
  		if (dev->flag & RT_DEVICE_FLAG_STREAM)
  		{
  			/* it's a stream mode device */
  			while (size)
  			{
  				/* stream mode */
  				if (*ptr == '\n')
  				{
  					while (!(serial->hw_base->STAT_REG & XUL_SR_TX_FIFO_EMPTY));
  					serial->hw_base->Tx_FIFO = '\r';
  				}
  
  				/* Wait for Empty Tx Buffer */
  				while (!(serial->hw_base->STAT_REG & XUL_SR_TX_FIFO_EMPTY));
  
  				/* Transmit Character */
  				serial->hw_base->Tx_FIFO = *ptr;
  				if (*ptr & 1)
  					rt_hw_board_led_on(2);
  				else
  					rt_hw_board_led_off(2);
  				ptr ++; size --;
  			}
  		}
  		else
  		{
  			while (size)
  			{
  				/* Wait for Empty Tx Buffer */
  				while (!(serial->hw_base->STAT_REG & XUL_SR_TX_FIFO_EMPTY));
  
  				/* Transmit Character */
  				serial->hw_base->Tx_FIFO = *ptr;
  				if (*ptr & 1)
  					rt_hw_board_led_on(2);
  				else
  					rt_hw_board_led_off(2);
  				ptr ++; size --;
  			}
  		}
  	}
  
  	return (rt_size_t)ptr - (rt_size_t)buffer;
  }
  
  static rt_err_t rt_serial_control (rt_device_t dev, rt_uint8_t cmd, void *args)
  {
  	return RT_EOK;
  }
  
  rt_err_t rt_hw_serial_init()
  {
  	rt_device_t device;
  
  #ifndef RT_USING_CONSOLE
  	int Status;
  
  	/*
  	 * Initialize the UartLite driver so that it is ready to use.
  	 */
  	Status = XUartLite_Initialize(&uart_lite, RS232_DEVICE_ID);
  	if (Status != XST_SUCCESS)
  	{
  		return;
  	}
  
  #endif
  
  #ifdef RT_USING_UART1
  	device = (rt_device_t) &serial1;
  
  	/* init serial device private data */
  	serial1.hw_base 		= (struct rt_mb_uart_lite_hw*)XPAR_USB_UART_BASEADDR;
  	serial1.peripheral_id 	= XPAR_UARTLITE_1_DEVICE_ID;
  	serial1.baudrate		= 115200;
  
  	/* set device virtual interface */
  	device->init 	= rt_serial_init;
  	device->open 	= rt_serial_open;
  	device->close 	= rt_serial_close;
  	device->read 	= rt_serial_read;
  	device->write 	= rt_serial_write;
  	device->control = rt_serial_control;
  
  	/* register uart1 on device subsystem */
  	rt_device_register(device, "uart1", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
  #endif
  
  
  	return RT_EOK;
  }
  
  
  