www.pudn.com > nxplpc2204bsp.rar > lpc2204Sio.c
/* Copyright 1984-2001 Wind River Systems, Inc. */
#include "copyright_wrs.h"
/*
modification history
--------------------
01o,27jul04,a_m BSP定制 for 精英arm7开发板
01c,30nov01,m_h Save pChan->baudRate when setting baud
01b,26apr01,m_h convert tabs to spaces for readability
01a,12apr01,m_h created from snds100 template.
*/
#include "vxWorks.h"
#include "sioLib.h"
#include "intLib.h"
#include "errno.h"
#include "lpc2210Sio.h"
#include "ioLib.h"
#define OFFSETRBR 0
#define OFFSETTHR 0
#define OFFSETIER 4
#define OFFSETIIR 8
#define OFFSETFCR 8
#define OFFSETLCR 0x0C
#define OFFSETLSR 0x14
#define OFFSETSCR 0x1C
#define OFFSETDLL 0
#define OFFSETDLM 4
#define UARTRBR(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETRBR)))
#define UARTTHR(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETTHR)))
#define UARTIER(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETIER)))
#define UARTIIR(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETIIR)))
#define UARTFCR(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETFCR)))
#define UARTLCR(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETLCR)))
#define UARTLSR(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETLSR)))
#define UARTSCR(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETSCR)))
#define UARTDLL(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETDLL)))
#define UARTDLM(BaseAddress) (*((volatile unsigned char *)((DWORD)BaseAddress+OFFSETDLM)))
#define LPC2210_BAUD_MIN 1200
#define LPC2210_BAUD_MAX 460860
#define LPC2210_SIO_DEFAULT_BAUD 9600
/* Hardware abstraction macros */
/* local defines */
/* for backward compatibility */
/* forward static declarations */
LOCAL int lpc2210TxStartup (SIO_CHAN * pSioChan);
LOCAL int lpc2210CallbackInstall (SIO_CHAN *pSioChan, int callbackType,
STATUS (*callback)(), void *callbackArg);
LOCAL int lpc2210PollOutput (SIO_CHAN *pSioChan, char outChar);
LOCAL int lpc2210PollInput (SIO_CHAN *pSioChan, char *thisChar);
LOCAL int lpc2210Ioctl (SIO_CHAN *pSioChan, int request, void *arg);
LOCAL STATUS dummyCallback (void);
/* local variables */
LOCAL SIO_DRV_FUNCS lpc2210DrvFuncs =
{
lpc2210Ioctl,
lpc2210TxStartup,
lpc2210CallbackInstall,
lpc2210PollInput,
lpc2210PollOutput
};
LOCAL BOOL lpc2210IntrMode = FALSE; /* interrupt mode allowed flag */
static int g_sending = 0; /* : added */
void lpc2210DevInit
(
LPC2210_CHAN * pChan
)
{
/* initialize each channel's driver function pointers */
pChan->sio.pDrvFuncs = &lpc2210DrvFuncs;
/* install dummy driver callbacks */
pChan->getTxChar = dummyCallback;
pChan->putRcvChar = dummyCallback;
/* reset the chip */
UARTLCR(pChan->regs) = 0x03; /* 禁止访问分频因子寄存器且设置为8,1,n */
UARTFCR(pChan->regs) = 0x87; /* 初始化FIFO */
/*UARTFCR(pChan->regs) = 0x06; */ /* 禁止FIFO */
UARTIER(pChan->regs) = 0x05; /* 禁止发送中断,允许接收和状态中断 */
/* setting polled mode is one way to make the device quiet */
lpc2210Ioctl ((SIO_CHAN *)pChan, SIO_MODE_SET, (void *)SIO_MODE_POLL);
lpc2210Ioctl ((SIO_CHAN *)pChan, SIO_BAUD_SET, (void *)LPC2210_SIO_DEFAULT_BAUD);
}
void lpc2210DevInit2
(
LPC2210_CHAN * pChan /* device to initialize */
)
{
/* Interrupt mode is allowed */
lpc2210IntrMode = TRUE;
}
/******************************************************************************
*
* lpc2210UARTInt - handle a channel's interrupt
*
* RETURNS: N/A
*/
void lpc2210UARTInt
(
LPC2210_CHAN * pChan /* channel generating the interrupt */
)
{
BYTE byteData;
DWORD dwIIR;
DWORD dwIntState;
FAST int oldlevel; /* : added */
BYTE temp;
oldlevel = intLock (); /* : added */
dwIIR = UARTIIR(pChan->regs);
if (dwIIR & 0x1) /* 没有挂起的中断*/
return;
dwIntState = (dwIIR & 0x0E) >> 1;
switch(dwIntState)
{
case 3: /* 线状态中断*/
temp = UARTLSR(pChan->regs);
break;
case 2: /* 接收到数据中断*/
case 6:
while(UARTLSR(pChan->regs) & 0x01)
{
byteData = UARTRBR(pChan->regs);
(pChan->putRcvChar) (pChan->putRcvArg, byteData);
}
break;
case 1: /* 发送数据中断*/
if ((pChan->getTxChar) (pChan->getTxArg, &byteData) != ERROR)
{
UARTTHR(pChan->regs) = byteData;
}
else
{
temp = UARTIER(pChan->regs);
temp &= ~0x02;
UARTIER(pChan->regs) = temp;
g_sending = 0;
}
break;
default:
break;
}
intUnlock (oldlevel); /* : added */
}
/******************************************************************************
*
* lpc2210TxStartup - start the interrupt transmitter
*
* RETURNS: OK on success, ENOSYS if the device is polled-only, or
* EIO on hardware error.
*/
LOCAL int lpc2210TxStartup
(
SIO_CHAN * pSioChan /* channel to start */
)
{
char outChar; /* : added */
FAST int oldlevel; /* : added */
LPC2210_CHAN * pChan = (LPC2210_CHAN *)pSioChan;
if(g_sending == 1)
return OK;
oldlevel = intLock ();
UARTIER(pChan->regs) = UARTIER(pChan->regs) | 0x02;
g_sending = 1;
if ((*pChan->getTxChar) (pChan->getTxArg, &outChar) != ERROR)
UARTTHR(pChan->regs) = outChar;
intUnlock (oldlevel);
return (OK);
}
/******************************************************************************
*
* lpc2210CallbackInstall - 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 lpc2210CallbackInstall
(
SIO_CHAN * pSioChan, /* channel */
int callbackType, /* type of callback */
STATUS (*callback)(), /* callback */
void * callbackArg /* parameter to callback */
)
{
LPC2210_CHAN * pChan = (LPC2210_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);
}
}
/*******************************************************************************
*
* lpc2210PollOutput - 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 lpc2210PollOutput
(
SIO_CHAN *pSioChan,
char outChar
)
{
LPC2210_CHAN * pChan = (LPC2210_CHAN *)pSioChan;
UINT32 status;
/* is the transmitter ready to accept a character? */
status = UARTLSR(pChan->regs);
if ((status & (1<<5)) == 0)
return (EAGAIN);
/* write out the character */
UARTTHR(pChan->regs) = outChar;
return (OK);
}
/******************************************************************************
*
* lpc2210PollInput - 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 lpc2210PollInput
(
SIO_CHAN * pSioChan,
char * thisChar
)
{
LPC2210_CHAN * pChan = (LPC2210_CHAN *)pSioChan;
UINT32 status;
status = UARTLSR(pChan->regs);
if ((status & 1) == 0x00)
return (EAGAIN); /* no input available at this time */
/* got a character */
*thisChar = (char)UARTRBR(pChan->regs);
return (OK);
}
LOCAL int lpc2210ModeSet
(
LPC2210_CHAN * pChan, /* channel */
uint_t 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) && (!lpc2210IntrMode))
return (EIO);
/* set the new mode */
pChan->mode = newMode;
if (pChan->mode == SIO_MODE_INT)
{
temp = UARTFCR(pChan->regs);
temp |= 6; /*Reset RX and TX mode bits*/
UARTFCR(pChan->regs) = temp;
intEnable(pChan->intLevel);
}
else
{
temp = UARTFCR(pChan->regs);
temp |= 6; /*Reset RX and TX mode bits*/
UARTFCR(pChan->regs) = temp;
intDisable (pChan->intLevel);
}
return (OK);
}
LOCAL STATUS lpc2210Hup
(
LPC2210_CHAN * pChan /* pointer to channel */
)
{
FAST int oldlevel; /* current interrupt level mask */
oldlevel = intLock ();
intUnlock (oldlevel);
return (OK);
}
/*******************************************************************************
*
* lpc2210Open - Set the modem control lines
*
* Set the modem control lines(RTS, DTR) TRUE if not already set.
*
* RETURNS: OK
*/
LOCAL STATUS lpc2210Open
(
LPC2210_CHAN * pChan /* pointer to channel */
)
{
FAST int oldlevel; /* current interrupt level mask */
oldlevel = intLock ();
/* set RTS and DTR active */
/* SNGKS32C_SIO_REG_WRITE(pChan, SNGKS32C_USTAT, USTAT_DTR_HIGH); */
intUnlock (oldlevel);
return (OK);
}
LOCAL int lpc2210OptSet
(
LPC2210_CHAN * pChan, /* channel */
uint_t newOpts /* new options */
)
{
UINT8 dataBits = 0x03;
UINT8 stopBits = 0x00;
int lvl;
UINT8 temp=PARITY_NONE;
UINT32 result;
if (pChan == NULL || newOpts & 0xffffff00)
return EIO;
/* do nothing if options already set */
if (pChan->options == newOpts)
return OK;
/* ignore requests for unsupported options */
/* decode individual request elements */
switch (newOpts & CSIZE)
{
case CS5:
dataBits = 0x00; break;
case CS6:
dataBits = 0x01; break;
case CS7:
dataBits = 0x02; break;
default:
case CS8:
dataBits = 0x03; break;
}
if (newOpts & STOPB)
stopBits = 0x01;
else
stopBits = 0x00;
switch (newOpts & (PARENB|PARODD))
{
case PARENB|PARODD:
/* enable odd parity */
temp=PARITY_ODD;
break;
case PARENB:
/* enable even parity */
temp=PARITY_EVEN;
break;
case PARODD:
/* invalid mode, not normally used. */
break;
default:
case 0:
temp=PARITY_NONE ;/* no parity */
break;
}
lvl = intLock ();
/*
* Reset the device according to dataBits, stopBits, hdweFlowCtrl,
* rcvrEnable, and parity selections.
*/
result = UARTLCR(pChan->regs);
result &= 0xC0;
UARTLCR(pChan->regs) = result | dataBits | (stopBits<<2) | (temp<<3);
intUnlock (lvl);
pChan->options = newOpts;
return (OK);
}
/*******************************************************************************
*
* lpc2210Ioctl - 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 int lpc2210Ioctl
(
SIO_CHAN * pSioChan, /* device to control */
int request, /* request code */
void * someArg /* some argument */
)
{
LPC2210_CHAN *pChan = (LPC2210_CHAN *) pSioChan;
int oldlevel; /* current interrupt level mask */
DWORD arg = (DWORD)someArg;
switch (request)
{
case SIO_BAUD_SET:
{
DWORD Fdiv = (Fpclk / 16) / arg;
DWORD temp = 0;
if (arg == 0)
return lpc2210Hup (pChan);
if (arg < LPC2210_BAUD_MIN || arg > LPC2210_BAUD_MAX)
{
return (EIO);
}
oldlevel = intLock ();
temp = UARTLCR(pChan->regs);
UARTLCR(pChan->regs) = temp | 0x80;
U0DLM = Fdiv / 256;
U0DLL = Fdiv % 256;
UARTLCR(pChan->regs) = temp;
intUnlock(oldlevel);
pChan->baudRate = arg;
return (OK);
}
break;
case SIO_BAUD_GET:
/* Get the baud rate and return OK */
*(DWORD *)arg = pChan->baudRate;
break;
case SIO_MODE_SET:
return (lpc2210ModeSet (pChan, arg));
case SIO_MODE_GET:
*(int *)arg = pChan->mode;
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:
return (lpc2210OptSet (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 (lpc2210Hup (pChan));
return (OK);
case SIO_OPEN:
return (lpc2210Open (pChan)); /* always open */
default:
return (ENOSYS);
}
return (ENOSYS);
}
/*******************************************************************************
*
* dummyCallback - dummy callback routine
*
* RETURNS: ERROR.
*/
LOCAL STATUS dummyCallback (void)
{
return (ERROR);
}