www.pudn.com > at91rm9200bsp.rar > at91uart.c
/* sndsSio.c - Samsung SNDS100 serial driver */
/*
modification history
--------------------
*/
/*
DESCRIPTION
INCLUDES:
sndsSio.h sioLib.h
*/
#include "sioLib.h"
#include "intLib.h"
#include "errno.h"
#include "ioLib.h"
#define AT91UART_BAUD_MIN 1200
#define AT91UART_BAUD_MAX 460800
/* for backward compatibility */
#ifndef SIO_HUP
# define SIO_OPEN 0x100A /* open channel, raise DTR, RTS */
# define SIO_HUP 0x100B /* hang-up, lower DTR, RTS */
#endif
/* forward static declarations */
LOCAL int AT91UartTxStartup (SIO_CHAN * pSioChan);
LOCAL int AT91UartCallbackInstall (SIO_CHAN *pSioChan, int callbackType,
STATUS (*callback)(), void *callbackArg);
LOCAL int AT91UartPollOutput (SIO_CHAN *pSioChan, char outChar);
LOCAL int AT91UartPollInput (SIO_CHAN *pSioChan, char *thisChar);
LOCAL int AT91UartIoctl (SIO_CHAN *pSioChan, int request, void *arg );
/* local variables */
LOCAL SIO_DRV_FUNCS AT91UartDrvFuncs =
{
AT91UartIoctl,
AT91UartTxStartup,
AT91UartCallbackInstall,
AT91UartPollInput,
AT91UartPollOutput,
};
/******************************************************************************
*
* AT91DevInit - initialize a SNDS_DUSART
*
* This routine initializes the driver
* function pointers and then resets the chip in a quiescent state.
* The BSP must have already initialized all the device addresses and the
* baudFreq fields in the SNDS_DUSART structure before passing it to
* this routine.
*
* RETURNS: N/A
*/
void AT91UartDevInit
(
AT91_CHAN * pChan
)
{
UINT32 dummy;
/* initialize each channel's driver function pointers */
int oldlevel;
pChan->sio.pDrvFuncs = &AT91UartDrvFuncs;
/* install dummy driver callbacks */
pChan->getTxChar = dummyCallback;
pChan->putRcvChar = dummyCallback;
/* reset the chip */
oldlevel = intLock ();
pChan->regs->US_CR = UART_CR_RESET_RECV | UART_CR_RESET_TRAN | UART_CR_RX_DIS | UART_CR_TX_DIS;
pChan->regs->US_MR = UART_MR_CHAR_8BIT | UART_MR_STOP_1BIT | UART_MR_PARITY_NONE
| UART_MR_CLK_USE_MCK | UART_MR_TEST_NORMAL_MODE| UART_MR_UART_MODE_NORMAL;
/*
* disable all interrupt source first
*/
pChan->regs->US_IDR = 0xFFFFFFFF;
pChan->regs->US_TTGR = UART_TANS_TIME_GUARD;
dummy = pChan ->regs->US_RHR;
intUnlock ( oldlevel );
pChan->intrmode = FALSE;
/*
* Init PDC, disable all pdc function
*/
AT91_PDC_Close( &(pChan->regs->US_PDC) );
/*
* make a dummy read emtpy the receive buf
*/
/* setting polled mode is one way to make the device quiet */
AT91UartIoctl ( (SIO_CHAN *)pChan, SIO_BAUD_SET, (void *)pChan->baudRate );
AT91UartIoctl ( (SIO_CHAN *)pChan, SIO_MODE_SET,(void *)SIO_MODE_POLL );
pChan->regs->US_CR = UART_CR_RX_ENA | UART_CR_TX_DIS | UART_CR_RESET_STATUS;
}
/******************************************************************************
*
* sndsDevInit2 - initialize a SNDS_DUSART, part 2
*
* This routine is called by the BSP after interrupts have been connected.
* The driver can now operate in interrupt mode. Before this routine is
* called only polled mode operations should be allowed.
*
* RETURNS: N/A
* ARGSUSED
*/
void AT91UartDevInit2
(
AT91_CHAN * pChan /* device to initialize */
)
{
pChan ->errcount = 0;
pChan->regs->US_CR = UART_CR_RX_ENA | UART_CR_TX_DIS | UART_CR_RESET_STATUS;
/*
* enable UART interrupt now.
*/
pChan->regs->US_IER = UART_IE_DR_TXRDY | UART_IE_DR_RXRDY | UART_IE_DR_FRAME
| UART_IE_DR_PARE| UART_IE_DR_OVRE;
pChan->intrmode = TRUE;
}
/******************************************************************************
*
* sndsIntRcv - handle a channel's receive-character interrupt
*
* RETURNS: N/A
*/
void AT91UartInt
(
AT91_CHAN * pChan /* channel generating the interrupt */
)
{
char outchar;
UINT32 status;
UART_S* udev = pChan ->regs;
status = udev->US_CSR;
if( status&( UART_CSR_FRAME | UART_CSR_OVRE| UART_CSR_PARE ) )
{
pChan->errcount ++;
udev->US_CR = UART_CR_RESET_STATUS;
}
if( status&(UART_CSR_RXRDY ) )
{
(*pChan->putRcvChar) ( pChan->putRcvArg, udev ->US_RHR );
}
if( status&( UART_CSR_TXRDY ) )
{
if( (*pChan->getTxChar) (pChan->getTxArg, &outchar) != ERROR )
{
udev ->US_THR = (AT91_REG)(outchar);
}
else
{
udev ->US_CR = UART_CR_TX_DIS;
}
}
}
/******************************************************************************
*
* sndsTxStartup - start the interrupt transmitter
*
* RETURNS: OK on success, ENOSYS if the device is polled-only, or
* EIO on hardware error.
*/
LOCAL int AT91UartTxStartup
(
SIO_CHAN * pSioChan /* channel to start */
)
{
AT91_CHAN * pChan = (AT91_CHAN *)pSioChan;
if( pChan ->intrmode == TRUE )
{
pChan ->regs->US_CR = UART_CR_TX_ENA;
return OK;
}
else
return ENOSYS;
}
/******************************************************************************
*
* sndsCallbackInstall - install ISR callbacks to get/put chars
*
* This driver allows interrupt callbacks for transmitting characters
* and receiving characters. In general, drivers may support other
* types of callbacks too.
*
* RETURNS: OK on success, or ENOSYS for an unsupported callback type.
*/
LOCAL int AT91UartCallbackInstall
(
SIO_CHAN * pSioChan, /* channel */
int callbackType, /* type of callback */
STATUS (*callback)(), /* callback */
void * callbackArg /* parameter to callback */
)
{
AT91_CHAN * pChan = (AT91_CHAN *)pSioChan;
switch (callbackType)
{
case SIO_CALLBACK_GET_TX_CHAR:
pChan->getTxChar = callback;
pChan->getTxArg = callbackArg;
return (OK);
case SIO_CALLBACK_PUT_RCV_CHAR:
pChan->putRcvChar = callback;
pChan->putRcvArg = callbackArg;
return (OK);
default:
return (ENOSYS);
}
}
/*******************************************************************************
*
* sndsPollOutput - output a character in polled mode
*
* RETURNS: OK if a character arrived, EIO on device error, EAGAIN
* if the output buffer if full. ENOSYS if the device is
* interrupt-only.
*/
LOCAL int AT91UartPollOutput
(
SIO_CHAN * pSioChan,
char outChar
)
{
AT91_CHAN * pChan = (AT91_CHAN *)pSioChan;
UART_S* udev;
UINT32 status;
udev = pChan ->regs;
/* is the transmitter ready to accept a character? */
status = udev ->US_CSR;
if( status&( UART_CSR_FRAME | UART_CSR_OVRE| UART_CSR_PARE ) )
{
pChan->errcount ++;
udev->US_CR = UART_CR_RESET_STATUS;
}
if( status&(UART_CSR_TXRDY) )
{
udev ->US_THR = (AT91_REG)(outChar);
return (OK);
}
else
return (EAGAIN);
}
/******************************************************************************
*
* sndsPollInput - poll the device for input
*
* RETURNS: OK if a character arrived, EIO on device error, EAGAIN
* if the input buffer if empty, ENOSYS if the device is
* interrupt-only.
*/
LOCAL int AT91UartPollInput
(
SIO_CHAN * pSioChan,
char * thisChar
)
{
AT91_CHAN * pChan = (AT91_CHAN *)pSioChan;
UART_S* udev;
UINT32 status;
udev = pChan ->regs;
status = udev ->US_CSR;
if( status&( UART_CSR_FRAME | UART_CSR_OVRE| UART_CSR_PARE ) )
{
pChan->errcount ++;
udev->US_CR = UART_CR_RESET_STATUS;
}
if( status&(UART_CSR_RXRDY) )
{
status = udev ->US_RHR;
(*thisChar) = (BYTE)status;
return (OK);
}
else
return (EAGAIN);
}
/******************************************************************************
*
* sndsModeSet - toggle between interrupt and polled mode
*
* RETURNS: OK on success, EIO on unsupported mode.
*/
LOCAL int At91UartModeSet
(
AT91_CHAN * pChan, /* channel */
int newMode /* new mode */
)
{
UINT32 temp;
if ( (newMode != SIO_MODE_POLL) && (newMode != SIO_MODE_INT) )
return (EIO);
/* Don't enter interrupt mode unless it is allowed. */
if ( (newMode == SIO_MODE_INT) && ( pChan ->intrmode ) )
return (EIO);
/* set the new mode */
if( newMode == SIO_MODE_INT )
pChan->intrmode = TRUE;
else
pChan->intrmode = FALSE;
if ( pChan->intrmode )
{
/*
* TX int still not enabled
*/
pChan->regs->US_CR = UART_CR_RX_ENA | UART_CR_TX_DIS | UART_CR_RESET_STATUS;
pChan->regs->US_IER = UART_IE_DR_TXRDY | UART_IE_DR_RXRDY | UART_IE_DR_FRAME
| UART_IE_DR_PARE| UART_IE_DR_OVRE;
intEnable( pChan ->level );
}
else
{
/*
* disable all interrupts
*/
pChan->regs->US_IDR = 0xFFFFFFFF;
pChan->regs->US_CR = UART_CR_RX_ENA | UART_CR_TX_ENA | UART_CR_RESET_STATUS;
intDisable( pChan ->level );
}
return (OK);
}
/*******************************************************************************
*
* sndsHup - hang up the modem control lines
*
* Resets the RTS and DTR signals.
*
* RETURNS: OK
*/
LOCAL STATUS AT91UartHup
(
AT91_CHAN * pChan /* pointer to channel */
)
{
/*
FAST int oldlevel; / * current interrupt level mask * /
oldlevel = intLock ();
/ * set RTS and DTR low * /
/ * SNDS_REG_WRITE(pChan, SNDS_USTAT, USTAT_DTR_LOW); * /
intUnlock (oldlevel);
*/
pChan ->regs->US_CR = UART_CR_RTS_ENA | UART_CR_DTR_ENA;
return (OK);
}
/*******************************************************************************
*
* sndsOpen - Set the modem control lines
*
* Set the modem control lines(RTS, DTR) TRUE if not already set.
*
* RETURNS: OK
*/
LOCAL STATUS AT91UartOpen
(
AT91_CHAN * pChan /* pointer to channel */
)
{
/*
FAST int oldlevel; / * current interrupt level mask * /
oldlevel = intLock ();
/ * set RTS and DTR active * /
/ * SNDS_REG_WRITE(pChan, SNDS_USTAT, USTAT_DTR_HIGH); * /
intUnlock (oldlevel);
*/
pChan ->regs->US_CR = UART_CR_RTS_DIS | UART_CR_DTR_DIS;
return (OK);
}
/******************************************************************************
*
* sndsOptSet - set hardware options
*
* This routine sets up the hardware according to the specified option
* argument. If the hardware cannot support a particular option value, then
* it should ignore that portion of the request.
*
* RETURNS: OK upon success, or EIO for invalid arguments.
*/
LOCAL int AT91UartOptSet
(
AT91_CHAN * pChan, /* channel */
UINT32 newOpts /* new options */
)
{
UINT32 umode = UART_MR_CLK_USE_MCKDIV| UART_MR_TEST_NORMAL_MODE| UART_MR_UART_MODE_NORMAL;
UINT32 dataBits = UART_MR_CHAR_8BIT;
UINT32 stopBits = UART_MR_STOP_1BIT;
UINT32 paritybits = UART_MR_PARITY_NONE;
UINT32 hdweFlowCtrl = UART_MR_UART_MODE_HWHANDS;
UINT32 rcvrEnable = UART_CR_RX_ENA;
int lvl;
if (pChan == NULL || newOpts & 0xffffff00)
return EIO;
/* do nothing if options already set */
if (pChan->options == newOpts)
return OK;
/* decode individual request elements */
switch (newOpts & CSIZE)
{
case CS5:
dataBits = UART_MR_CHAR_5BIT; break;
case CS6:
dataBits = UART_MR_CHAR_6BIT; break;
case CS7:
dataBits = UART_MR_CHAR_7BIT; break;
/* case CS9:
dataBits = UART_MR_CHAR_9BIT; break;*/
default:
case CS8:
dataBits = UART_MR_CHAR_8BIT; break;
}
if ( newOpts & STOPB )
stopBits = UART_MR_STOP_2BIT;
else
stopBits = UART_MR_STOP_1BIT;
switch ( newOpts & (PARENB|PARODD) )
{
case PARENB|PARODD:
/* enable odd parity */
paritybits = UART_MR_PARITY_ODD;
break;
case PARENB:
/* enable even parity */
paritybits = UART_MR_PARITY_EVEN;
break;
case PARODD:
/* invalid mode, not normally used. */
break;
default:
case 0:
paritybits = UART_MR_PARITY_NONE;
break;
}
if ( newOpts & CLOCAL )
{
/* clocal disables hardware flow control */
hdweFlowCtrl = 0;
}
if ( (newOpts & CREAD) == 0 )
rcvrEnable = UART_CR_RX_DIS;
lvl = intLock ();
/*
* Reset the device according to dataBits, stopBits, hdweFlowCtrl,
* rcvrEnable, and parity selections.
*/
pChan ->regs->US_CR = rcvrEnable;
pChan ->regs->US_MR = (AT91_REG)( umode | dataBits | stopBits
| paritybits | hdweFlowCtrl );
intUnlock (lvl);
pChan->options = newOpts;
return (OK);
}
/*******************************************************************************
*
* sndsIoctl - special device control
*
* This routine handles the IOCTL messages from the user. It supports commands
* to get/set baud rate, mode(INT,POLL), hardware options(parity, number of
* data bits) and modem control(RTS/CTS and DTR/DSR handshakes).
* The ioctl commands SIO_HUP and SIO_OPEN are used to implement the HUPCL(hang
* up on last close) function.
*
* As on a UNIX system, requesting a baud rate of zero is translated into
* a hangup request. The DTR and RTS lines are dropped. This should cause
* a connected modem to drop the connection. The SIO_HUP command will only
* hangup if the HUPCL option is active. The SIO_OPEN function will raise
* DTR and RTS lines whenever it is called. Use the BAUD_RATE=0 function
* to hangup when HUPCL is not active.
*
* The CLOCAL option will disable hardware flow control. When selected,
* hardware flow control is not used. When not selected hardware flow control
* is based on the RTS/CTS signals. CTS is the clear to send input
* from the other end. It must be true for this end to begin sending new
* characters. In most drivers, the RTS signal will be assumed to be connected
* to the opposite end's CTS signal and can be used to control output from
* the other end. Raising RTS asserts CTS at the other end and the other end
* can send data. Lowering RTS de-asserts CTS and the other end will stop
* sending data. (This is non-EIA defined use of RTS).
*
* RETURNS: OK on success, ENOSYS on unsupported request, EIO on failed
* request.
*/
LOCAL STATUS At91UartSetNewBaudRate( AT91_CHAN * pChan, UINT32 baud )
{
int oldlevel;
UINT32 count = ( MASTER_CLK + baud*8*(pChan->clkdiv) )/(pChan->clkdiv*16*baud);
pChan->baudRate = baud;
oldlevel = intLock ();
pChan ->regs ->US_BRGR = count;
intUnlock (oldlevel);
return (OK);
}
LOCAL int AT91UartIoctl
(
SIO_CHAN * pSioChan, /* device to control */
int request, /* request code */
void * someArg /* some argument */
)
{
AT91_CHAN *pChan = ( AT91_CHAN * )pSioChan;
int oldlevel; /* current interrupt level mask */
int arg = (int)someArg;
UART_S* udev = pChan ->regs;
switch (request)
{
case SIO_BAUD_SET:
/*
* like unix, a baud request for 0 is really a request to
* hangup.
*/
if (arg == 0)
return AT91UartHup (pChan);
/*
* Set the baud rate. Return EIO for an invalid baud rate, or
* OK on success.
*/
if (arg < AT91UART_BAUD_MIN || arg > AT91UART_BAUD_MAX )
{
return (EIO);
}
/* Calculate the baud rate constant for the new baud rate */
switch(arg)
{
case 1200:
case 2400:
case 4800:
case 9600:
case 19200:
case 38400:
case 57600:
case 115200:
case 230400:
case 460800:
return ( At91UartSetNewBaudRate( pChan, arg ) );
default:
}
return(OK);
case SIO_BAUD_GET:
/* Get the baud rate and return OK */
*(int *)arg = pChan->baudRate;
return (OK);
case SIO_MODE_SET:
/*
* Set the mode (e.g., to interrupt or polled). Return OK
* or EIO for an unknown or unsupported mode.
*/
return ( At91UartModeSet (pChan, arg) );
case SIO_MODE_GET:
/* Get the current mode and return OK. */
if( pChan ->intrmode )
*(int *)arg = SIO_MODE_INT;
else
*(int *)arg = SIO_MODE_POLL;
return (OK);
case SIO_AVAIL_MODES_GET:
/* Get the available modes and return OK. */
*(int *)arg = SIO_MODE_INT | SIO_MODE_POLL;
return (OK);
case SIO_HW_OPTS_SET:
/*
* Optional command to set the hardware options (as defined
* in sioLib.h).
* Return OK, or ENOSYS if this command is not implemented.
* Note: several hardware options are specified at once.
* This routine should set as many as it can and then return
* OK. The SIO_HW_OPTS_GET is used to find out which options
* were actually set.
*/
return ( AT91UartOptSet (pChan, arg) );
case SIO_HW_OPTS_GET:
/*
* Optional command to get the hardware options (as defined
* in sioLib.h). Return OK or ENOSYS if this command is not
* implemented. Note: if this command is unimplemented, it
* will be assumed that the driver options are CREAD | CS8
* (e.g., eight data bits, one stop bit, no parity, ints enabled).
*/
*(int *)arg = pChan->options;
return (OK);
case SIO_HUP:
/* check if hupcl option is enabled */
if ( pChan->options & HUPCL )
return ( AT91UartHup (pChan) );
return (OK);
case SIO_OPEN:
return ( AT91UartOpen (pChan) ); /* always open */
default:
return (ENOSYS);
}
return (ENOSYS);
}
/*******************************************************************************
*
* dummyCallback - dummy callback routine
*
* RETURNS: ERROR.
*/
LOCAL STATUS dummyCallback (void)
{
return (ERROR);
}