/* $Id: xuartlite.c,v 1.17 2003/05/14 13:59:01 robertm Exp $ */
/*****************************************************************************
*
*     Author: Xilinx, Inc.
*     
*     
*     This program is free software; you can redistribute it and/or modify it
*     under the terms of the GNU General Public License as published by the
*     Free Software Foundation; either version 2 of the License, or (at your
*     option) any later version.
*     
*     
*     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
*     COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
*     ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD,
*     XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE
*     FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING
*     ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
*     XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
*     THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY
*     WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM
*     CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND
*     FITNESS FOR A PARTICULAR PURPOSE.
*     
*     
*     Xilinx hardware products are not intended for use in life support
*     appliances, devices, or systems. Use in such applications is
*     expressly prohibited.
*     
*     
*     (c) Copyright 2002-2003 Xilinx Inc.
*     All rights reserved.
*     
*     
*     You should have received a copy of the GNU General Public License along
*     with this program; if not, write to the Free Software Foundation, Inc.,
*     675 Mass Ave, Cambridge, MA 02139, USA.
*
*****************************************************************************/
/****************************************************************************/
/**
*
* @file xuartlite.c
*
* Contains required functions for the XUartLite driver. See the xuartlite.h
* header file for more details on this driver.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- -----------------------------------------------
* 1.00a ecm  08/31/01 First release
* 1.00b jhl  02/21/02 Repartitioned the driver for smaller files
* 1.00b rmm  05/13/03 Fixed diab compiler warnings relating to asserts
* </pre>
*
*****************************************************************************/

/***************************** Include Files ********************************/

#include "xbasic_types.h"
#include "xstatus.h"
#include "xparameters.h"
#include "xuartlite.h"
#include "xuartlite_i.h"
#include "xio.h"

/************************** Constant Definitions ****************************/

/**************************** Type Definitions ******************************/

/***************** Macros (Inline Functions) Definitions ********************/

/************************** Variable Definitions ****************************/

/************************** Function Prototypes *****************************/

static void StubHandler(void *CallBackRef, unsigned int ByteCount);

/****************************************************************************/
/**
*
* Initialize a XUartLite instance.  The receive and transmit FIFOs of the
* UART are not flushed, so the user may want to flush them. The hardware
* device does not have any way to disable the receiver such that any valid
* data may be present in the receive FIFO.  This function disables the UART
* interrupt. The baudrate and format of the data are fixed in the hardware
* at hardware build time.
*
* @param    InstancePtr is a pointer to the XUartLite instance to be worked on.
* @param    DeviceId is the unique id of the device controlled by this
*           XUartLite instance.  Passing in a device id associates the
*           generic XUartLite instance to a specific device, as chosen by
*           the caller or application developer.
*
* @return
*
* - XST_SUCCESS if everything starts up as expected.
* - XST_DEVICE_NOT_FOUND if the device is not found in the configuration table.
*
* @note
*
* None.
*
*****************************************************************************/
XStatus
XUartLite_Initialize(XUartLite * InstancePtr, u16 DeviceId)
{
	XUartLite_Config *UartLiteConfigPtr;

	/*
	 * Assert validates the input arguments
	 */
	XASSERT_NONVOID(InstancePtr != NULL);

	/*
	 * Lookup the device configuration in the configuration table. Use this
	 * configuration info when initializing this component.
	 */
	UartLiteConfigPtr = XUartLite_LookupConfig(DeviceId);

	if (UartLiteConfigPtr == (XUartLite_Config *) NULL) {
		return XST_DEVICE_NOT_FOUND;
	}

	/*
	 * Set some default values, including setting the callback
	 * handlers to stubs.
	 */

	InstancePtr->SendBuffer.NextBytePtr = NULL;
	InstancePtr->SendBuffer.RemainingBytes = 0;
	InstancePtr->SendBuffer.RequestedBytes = 0;

	InstancePtr->ReceiveBuffer.NextBytePtr = NULL;
	InstancePtr->ReceiveBuffer.RemainingBytes = 0;
	InstancePtr->ReceiveBuffer.RequestedBytes = 0;

	InstancePtr->IsReady = XCOMPONENT_IS_READY;
	InstancePtr->RegBaseAddress = UartLiteConfigPtr->RegBaseAddr;
	InstancePtr->RecvHandler = StubHandler;
	InstancePtr->SendHandler = StubHandler;

	/* Write to the control register to disable the interrupts, don't
	 * reset the FIFOs are the user may want the data that's present
	 */
	XIo_Out32(InstancePtr->RegBaseAddress + XUL_CONTROL_REG_OFFSET, 0);

	/*
	 * Clear the statistics for this driver
	 */
	XUartLite_mClearStats(InstancePtr);

	return XST_SUCCESS;
}

/****************************************************************************/
/**
*
* This functions sends the specified buffer of data using the UART in either
* polled or interrupt driven modes. This function is non-blocking such that it
* will return before the data has been sent by the UART. If the UART is busy
* sending data, it will return and indicate zero bytes were sent.
*
* In a polled mode, this function will only send as much data as the UART can
* buffer in the FIFO. The application may need to call it repeatedly to
* send a buffer.
*
* In interrupt mode, this function will start sending the specified buffer and
* then the interrupt handler of the driver will continue sending data until the
* buffer has been sent. A callback function, as specified by the application,
* will be called to indicate the completion of sending the buffer.
*
* @param    InstancePtr is a pointer to the XUartLite instance to be worked on.
* @param    BufferPtr is pointer to a buffer of data to be sent.
* @param    NumBytes contains the number of bytes to be sent. A value of zero
*           will stop a previous send operation that is in progress in interrupt
*           mode. Any data that was already put into the transmit FIFO will be
*           sent.
*
* @return
*
* The number of bytes actually sent.
*
* @note
*
* The number of bytes is not asserted so that this function may be called with
* a value of zero to stop an operation that is already in progress.
*
******************************************************************************/
unsigned int
XUartLite_Send(XUartLite * InstancePtr, u8 * DataBufferPtr,
	       unsigned int NumBytes)
{
	unsigned int BytesSent;
	u32 StatusRegister;

	/*
	 * Assert validates the input arguments
	 */
	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(DataBufferPtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

	/* Enter a critical region by disabling the UART interrupts to
	 * allow this call to stop a previous operation that may be interrupt
	 * driven
	 */
	StatusRegister = XUartLite_mGetStatusReg(InstancePtr->RegBaseAddress);

	XIo_Out32(InstancePtr->RegBaseAddress + XUL_CONTROL_REG_OFFSET, 0);

	/* Setup the specified buffer to be sent by setting the instance
	 * variables so it can be sent with polled or interrupt mode
	 */
	InstancePtr->SendBuffer.RequestedBytes = NumBytes;
	InstancePtr->SendBuffer.RemainingBytes = NumBytes;
	InstancePtr->SendBuffer.NextBytePtr = DataBufferPtr;

	/* Restore the interrupt enable register to it's previous value such
	 * that the critical region is exited.
	 * This is done here to minimize the amount of time the interrupt is
	 * disabled since there is only one interrupt and the receive could
	 * be filling up while interrupts are blocked.
	 */

	StatusRegister &= XUL_CR_ENABLE_INTR;
	XIo_Out32(InstancePtr->RegBaseAddress + XUL_CONTROL_REG_OFFSET,
		  StatusRegister);

	/* Send the buffer using the UART and return the number of bytes sent */

	BytesSent = XUartLite_SendBuffer(InstancePtr);

	return BytesSent;
}

/****************************************************************************/
/**
*
* This function will attempt to receive a specified number of bytes of data
* from the UART and store it into the specified buffer. This function is
* designed for either polled or interrupt driven modes. It is non-blocking
* such that it will return if no data has already received by the UART.
*
* In a polled mode, this function will only receive as much data as the UART
* can buffer in the FIFO. The application may need to call it repeatedly to
* receive a buffer. Polled mode is the default mode of operation for the driver.
*
* In interrupt mode, this function will start receiving and then the interrupt
* handler of the driver will continue receiving data until the buffer has been
* received. A callback function, as specified by the application, will be called
* to indicate the completion of receiving the buffer or when any receive errors
* or timeouts occur. Interrupt mode must be enabled using the SetOptions function.
*
* @param    InstancePtr is a pointer to the XUartLite instance to be worked on.
* @param    BufferPtr is pointer to buffer for data to be received into
* @param    NumBytes is the number of bytes to be received. A value of zero will
*           stop a previous receive operation that is in progress in interrupt mode.
*
* @return
*
* The number of bytes received.
*
* @note
*
* The number of bytes is not asserted so that this function may be called with
* a value of zero to stop an operation that is already in progress.
*
*****************************************************************************/
unsigned int
XUartLite_Recv(XUartLite * InstancePtr, u8 * DataBufferPtr,
	       unsigned int NumBytes)
{
	unsigned int ReceivedCount;
	u32 StatusRegister;

	/*
	 * Assert validates the input arguments
	 */
	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(DataBufferPtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

	/* Enter a critical region by disabling all the UART interrupts to allow
	 * this call to stop a previous operation that may be interrupt driven
	 */
	StatusRegister = XUartLite_mGetStatusReg(InstancePtr->RegBaseAddress);
	XIo_Out32(InstancePtr->RegBaseAddress + XUL_CONTROL_REG_OFFSET, 0);

	/* Setup the specified buffer to be received by setting the instance
	 * variables so it can be received with polled or interrupt mode
	 */
	InstancePtr->ReceiveBuffer.RequestedBytes = NumBytes;
	InstancePtr->ReceiveBuffer.RemainingBytes = NumBytes;
	InstancePtr->ReceiveBuffer.NextBytePtr = DataBufferPtr;

	/* Restore the interrupt enable register to it's previous value such
	 * that the critical region is exited
	 */
	StatusRegister &= XUL_CR_ENABLE_INTR;
	XIo_Out32(InstancePtr->RegBaseAddress + XUL_CONTROL_REG_OFFSET,
		  StatusRegister);

	/* Receive the data from the UART and return the number of bytes
	 * received
	 * This is done here to minimize the amount of time the interrupt is
	 * disabled since there is only one interrupt and the transmit could
	 * be emptying out while interrupts are blocked.
	 */

	ReceivedCount = XUartLite_ReceiveBuffer(InstancePtr);

	return ReceivedCount;

}

/****************************************************************************/
/**
*
* This function resets the FIFOs, both transmit and receive, of the UART such
* that they are emptied.  Since the UART does not have any way to disable it
* from receiving data, it may be necessary for the application to reset the
* FIFOs to get rid of any unwanted data.
*
* @param    InstancePtr is a pointer to the XUartLite instance to be worked on.
*
* @return
*
* None.
*
* @note
*
* None.
*
*****************************************************************************/
void
XUartLite_ResetFifos(XUartLite * InstancePtr)
{
	u32 ControlRegister;

	XASSERT_VOID(InstancePtr != NULL);
	XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

	/* Read the control register 1st such that the next write to the control
	 * register won't destroy the state of the interrupt enable bit
	 */
	ControlRegister = XIo_In32(InstancePtr->RegBaseAddress +
				   XUL_CONTROL_REG_OFFSET);

	/* Write to the control register to reset both FIFOs, these bits are
	 * self-clearing such that there's no need to clear them
	 */
	XIo_Out32(InstancePtr->RegBaseAddress + XUL_CONTROL_REG_OFFSET,
		  ControlRegister | XUL_CR_FIFO_TX_RESET |
		  XUL_CR_FIFO_RX_RESET);
}

/****************************************************************************/
/**
*
* This function determines if the specified UART is sending data. If the
* transmitter register is not empty, it is sending data.
*
* @param    InstancePtr is a pointer to the XUartLite instance to be worked on.
*
* @return
*
* A value of TRUE if the UART is sending data, otherwise FALSE.
*
* @note
*
* None.
*
*****************************************************************************/
u32
XUartLite_IsSending(XUartLite * InstancePtr)
{
	u32 StatusRegister;

	/*
	 * Assert validates the input arguments
	 */
	XASSERT_NONVOID(InstancePtr != NULL);

	/* Read the status register to determine if the transmitter is
	 * empty
	 */
	StatusRegister =
	    XIo_In32(InstancePtr->RegBaseAddress + XUL_STATUS_REG_OFFSET);

	/* If the transmitter is not empty then indicate that the UART is still
	 * sending some data
	 */
	return ((StatusRegister & XUL_SR_TX_FIFO_EMPTY) == 0);
}

/****************************************************************************
*
* This function provides a stub handler such that if the application does not
* define a handler but enables interrupts, this function will be called.
*
* @param    CallBackRef has no purpose but is necessary to match the
*           interface for a handler.
* @param    ByteCount has no purpose but is necessary to match the
*           interface for a handler.
*
* @return
*
* None.
*
* @note
*
* None.
*
*****************************************************************************/
static void
StubHandler(void *CallBackRef, unsigned int ByteCount)
{
	/*
	 * Assert occurs always since this is a stub and should never be called
	 */
	XASSERT_VOID_ALWAYS();
}

/****************************************************************************
*
* Looks up the device configuration based on the unique device ID.  The table
* UartliteConfigTable contains the configuration info for each device in the
* system.
*
* @param DeviceId is the unique device ID to match on.
*
* @return
*
* A pointer to the configuration data for the device, or NULL if no match
* was found.
*
* @note
*
* None.
*
******************************************************************************/
XUartLite_Config *
XUartLite_LookupConfig(u16 DeviceId)
{
	XUartLite_Config *CfgPtr = NULL;
	int i;

	for (i = 0; i < XPAR_XUARTLITE_NUM_INSTANCES; i++) {
		if (XUartLite_ConfigTable[i].DeviceId == DeviceId) {
			CfgPtr = &XUartLite_ConfigTable[i];
			break;
		}
	}

	return CfgPtr;
}

/****************************************************************************/
/**
*
* This function sends a buffer that has been previously specified by setting
* up the instance variables of the instance. This function is designed to be
* an internal function for the XUartLite component such that it may be called
* from a shell function that sets up the buffer or from an interrupt handler.
*
* This function sends the specified buffer of data to the UART in either
* polled or interrupt driven modes. This function is non-blocking such that
* it will return before the data has been sent by the UART.
*
* In a polled mode, this function will only send as much data as the UART can
* buffer, either in the transmitter or in the FIFO if present and enabled.
* The application may need to call it repeatedly to send a buffer.
*
* In interrupt mode, this function will start sending the specified buffer and
* then the interrupt handler of the driver will continue until the buffer
* has been sent. A callback function, as specified by the application, will
* be called to indicate the completion of sending the buffer.
*
* @param    InstancePtr is a pointer to the XUartLite instance to be worked on.
*
* @return
*
* NumBytes is the number of bytes actually sent (put into the UART transmitter
* and/or FIFO).
*
* @note
*
* None.
*
*****************************************************************************/
unsigned int
XUartLite_SendBuffer(XUartLite * InstancePtr)
{
	unsigned int SentCount = 0;
	u32 StatusRegister;

	/* Read the line status register to determine if the transmitter is
	 * full
	 */

	StatusRegister = XUartLite_mGetStatusReg(InstancePtr->RegBaseAddress);

	/*
	 * Fill the FIFO from the the buffer that was specified
	 */

	SentCount = 0;

	while (((StatusRegister & XUL_SR_TX_FIFO_FULL) == 0) &&
	       (SentCount < InstancePtr->SendBuffer.RemainingBytes)) {
		XIo_Out32(InstancePtr->RegBaseAddress + XUL_TX_FIFO_OFFSET,
			  InstancePtr->SendBuffer.NextBytePtr[SentCount]);

		SentCount++;

		StatusRegister =
		    XUartLite_mGetStatusReg(InstancePtr->RegBaseAddress);

	}

	/*
	 * Update the buffer to reflect the bytes that were sent from it
	 */
	InstancePtr->SendBuffer.NextBytePtr += SentCount;
	InstancePtr->SendBuffer.RemainingBytes -= SentCount;

	/*
	 * Increment associated counters
	 */
	InstancePtr->Stats.CharactersTransmitted += SentCount;

	/*
	 * Return the number of bytes that were sent, althought they really were
	 * only put into the FIFO, not completely sent yet
	 */
	return SentCount;
}

/****************************************************************************/
/**
*
* This function receives a buffer that has been previously specified by setting
* up the instance variables of the instance. This function is designed to be
* an internal function for the XUartLite component such that it may be called
* from a shell function that sets up the buffer or from an interrupt handler.
*
* This function will attempt to receive a specified number of bytes of data
* from the UART and store it into the specified buffer. This function is
* designed for either polled or interrupt driven modes. It is non-blocking
* such that it will return if there is no data has already received by the
* UART.
*
* In a polled mode, this function will only receive as much data as the UART
* can buffer, either in the receiver or in the FIFO if present and enabled.
* The application may need to call it repeatedly to receive a buffer. Polled
* mode is the default mode of operation for the driver.
*
* In interrupt mode, this function will start receiving and then the interrupt
* handler of the driver will continue until the buffer has been received. A
* callback function, as specified by the application, will be called to indicate
* the completion of receiving the buffer or when any receive errors or timeouts
* occur. Interrupt mode must be enabled using the SetOptions function.
*
* @param    InstancePtr is a pointer to the XUartLite instance to be worked on.
*
* @return
*
* The number of bytes received.
*
* @note
*
* None.
*
*****************************************************************************/
unsigned int
XUartLite_ReceiveBuffer(XUartLite * InstancePtr)
{
	u8 StatusRegister;
	unsigned int ReceivedCount = 0;

	/* Loop until there is not more data buffered by the UART or the specified
	 * number of bytes is received
	 */

	while (ReceivedCount < InstancePtr->ReceiveBuffer.RemainingBytes) {
		/* Read the Status Register to determine if there is any data in
		 * the receiver/FIFO
		 */
		StatusRegister =
		    XUartLite_mGetStatusReg(InstancePtr->RegBaseAddress);

		/* If there is data ready to be removed, then put the next byte
		 * received into the specified buffer and update the stats to reflect
		 * any receive errors for the byte
		 */
		if (StatusRegister & XUL_SR_RX_FIFO_VALID_DATA) {
			InstancePtr->ReceiveBuffer.
			    NextBytePtr[ReceivedCount++] =
			    XIo_In32(InstancePtr->RegBaseAddress +
				     XUL_RX_FIFO_OFFSET);

			XUartLite_mUpdateStats(InstancePtr, StatusRegister);
		}

		/* There's no more data buffered, so exit such that this function does
		 * not block waiting for data
		 */
		else {
			break;
		}
	}

	/* Update the receive buffer to reflect the number of bytes that was
	 * received
	 */
	InstancePtr->ReceiveBuffer.NextBytePtr += ReceivedCount;
	InstancePtr->ReceiveBuffer.RemainingBytes -= ReceivedCount;

	/*
	 * Increment associated counters in the statistics
	 */
	InstancePtr->Stats.CharactersReceived += ReceivedCount;

	return ReceivedCount;
}
