📄 at91sio.c
字号:
/* at91UsartSio.c - Atmel AT91 USART tty driver */
/* Copyright 1999 ARM Limited */
/* Copyright 2002-2004 Wind River Systems, Inc. */
/*
modification history
--------------------
01b,05oct04,pdr fixed buffer coherency when MMU/cache enabled.
01a,17sep04,pdr adapted for AT91RM9200 with DBGU capabilities.
*/
/*
DESCRIPTION
This is the device driver for the USARTs in the Atmel Corporation
AT91RM9200 microcontroller. The chip contains 4 USARTs, usable in
synchronous or asynchronous mode.
The USARTs are identical in design. They contain a universal
synchronous/asynchronous receiver/transmitter, and a baud-rate
generator, The USARTs contain no FIFOs, but are supported by a
Peripheral Data Controller (PDC) which can be programmed to perform
data transfers to and from the USART without CPU intervention. The
driver is conditionally compilable to support use of the PDC, or simple
character-at-a time operation.
The USART design does not support modem control input or output signals
e.g. DTR, RI, RTS, DCD, CTS and DSR.
Interrupts are programmed to occur when the Receive or Transmitter
Holding Registers are full/empty, when PDC transfers have occurred, or
when receiver timeout has occurred.
Only asynchronous serial operation is supported by the USARTs which
support 5, 6, 7 or 8 bit word lengths with or without parity and with one or
two stop bits. The only serial word format supported by the driver is 8
data bits, 1 stop bit, no parity, The default baud rate is determined
by the BSP by filling in the AT91_SIO_CHAN structure before calling
at91DevInit().
The USART supports baud rates depending on the frequency of the clocks
applied to it, but typically these will range from about 31 bps to
about 2 Mbps.
.SH DATA STRUCTURES
An AT91_SIO_CHAN data structure is used to describe each channel, this
structure is described in at91Sio.h.
.SH CALLBACKS
Servicing a "transmitter ready" interrupt involves making a callback to
a higher level library in order to get a character to transmit. By
default, this driver installs dummy callback routines which do nothing.
A higher layer library that wants to use this driver (e.g. ttyDrv)
will install its own callback routine using the SIO_INSTALL_CALLBACK
ioctl command. Likewise, a receiver interrupt handler makes a callback
to pass the character to the higher layer library.
.SH MODES
This driver supports both polled and interrupt modes.
.SH PERIPHERAL DATA CONTROLLER
If the symbol AT91_USE_PDC is defined, then code is conditionally
compiled to use the peripheral data controller associated with the
USARTs. If the symbol is not defined, then the USART is used in
character-at-a-time mode and the maximum serial speeds that the
hardware can support will be lower. When used with the PDC, the driver
buffers both receive and transmit. The size of the buffers is dictated
by the symbols AT91_RXBUFF_SIZE and AT91_TXBUFF_SIZE, which are
defaulted within at91Sio.h. On the receive side, there are two
swing-buffers used alternately, of the same size.
.SH USAGE
The driver is typically only called by the BSP. The directly callable
routines in this modules are at91SioDevInit(), and at91SioInt().
The BSP's sysHwInit() routine typically calls sysSerialHwInit(), which
initialises the hardware-specific fields in the AT91_SIO_CHAN structure
(e.g. register I/O addresses etc) before calling at91SioDevInit() which
resets the device and installs the driver function pointers. After
this the USART will be enabled and ready to generate interrupts, but
those interrupts will be disabled in the interrupt controller.
The following example shows the first parts of the initialisation:
.CS
#include "at91UsartSio.h"
LOCAL AT91_SIO_CHAN at91SioChan[N_AT91_USART_CHANNELS];
void sysSerialHwInit (void)
{
int i;
for (i = 0; i < N_AT91_USART_CHANNELS; i++)
{
at91SioChan[i].regs = devParas[i].baseAdrs;
at91SioChan[i].baudRate = CONSOLE_BAUD_RATE;
at91SioChan[i].xtal = AT91_USART_FREQ;
at91SioChan[i].level = devParas[i].intLevel;
/@ Set USART to be clocked from MCKI (for example) @/
AT91_USART_REG_WRITE (&at91SioChan[i], AT91_US_MR, 0);
/@
* Initialise driver functions, getTxChar, putRcvChar and
* channelMode and otherwise initialise USART.
@/
at91SioDevInit(&at91SioChan[i]);
}
.CE
The BSP's sysHwInit2() routine typically calls sysSerialHwInit2(),
which connects the chips interrupts via intConnect() and enables those
interrupts, as shown in the following example:
.CS
void sysSerialHwInit2 (void)
{
int i;
for (i = 0; i < N_AT91_USART_CHANNELS; i++)
{
/@ connect and enable interrupts @/
(void)intConnect (INUM_TO_IVEC(devParas[i].vector),
at91SioInt, (int) &at91SioChan[i]);
intEnable (devParas[i].intLevel);
}
}
.CE
.SH BSP
By convention all the BSP-specific serial initialisation is performed
in a file called sysSerial.c, which is #include'ed by sysLib.c.
sysSerial.c implements at least four functions, sysSerialHwInit()
sysSerialHwInit2(), sysSerialChanGet(), and sysSerialReset(). The first
two have been described above, the others work as follows:
sysSerialChanGet is called by usrRoot to get the serial channel
descriptor associated with a serial channel number. The routine takes a
single parameter which is a channel number ranging between zero and
NUM_TTY. It returns a pointer to the corresponding channel descriptor,
(SIO_CHAN), which is just the address of the AT91_SIO_CHAN structure.
sysSerialReset is called from sysToMonitor() and should reset the
serial devices to an inactive state (prevent them from generating any
interrupts).
.SH INCLUDE FILES:
at91Sio.h sioLib.h
.SH SEE ALSO:
.I "Atmel AT92RM9200, Data Sheet."
*/
#include "vxWorks.h"
#include "intLib.h"
#include "errnoLib.h"
#include "errno.h"
#include "sioLib.h"
#include "at91Sio.h"
/* Make all of code in file dependent upon this symbol */
#ifdef INCLUDE_SERIAL
/* defines */
/* globals */
/* function prototypes */
LOCAL STATUS at91SioDummyCallback (void);
LOCAL void at91SioInitChannel (AT91_SIO_CHAN * pChan);
LOCAL STATUS at91SioIoctl (SIO_CHAN * pSioChan, int request, int arg);
LOCAL int at91SioTxStartup (SIO_CHAN * pSioChan);
LOCAL int at91SioCallbackInstall (SIO_CHAN * pSioChan, int callbackType, STATUS (*callback)(), void * callbackArg);
LOCAL int at91SioPollInput (SIO_CHAN * pSioChan, char *);
LOCAL int at91SioPollOutput (SIO_CHAN * pSioChan, char);
/* driver functions */
LOCAL SIO_DRV_FUNCS at91SioDrvFuncs =
{
(int (*)(SIO_CHAN *, int, void *)) at91SioIoctl,
(int (*)(SIO_CHAN *)) at91SioTxStartup,
(int (*)()) at91SioCallbackInstall,
(int (*)(SIO_CHAN *, char*)) at91SioPollInput,
(int (*)(SIO_CHAN *, char)) at91SioPollOutput
};
/*******************************************************************************
*
* at91SioDummyCallback - dummy callback routine.
*
* RETURNS: ERROR, always.
*/
LOCAL STATUS at91SioDummyCallback (void)
{
return ERROR;
}
/*******************************************************************************
*
* at91SioDevInit - initialise an AT91 SIO channel
*
* This routine initialises some SIO_CHAN function pointers and then resets
* the chip to a quiescent state. Before this routine is called, the BSP
* must already have initialised all the device addresses, etc. in the
* AT91_SIO_CHAN structure.
*
* RETURNS: N/A
*/
void at91SioDevInit
(
AT91_SIO_CHAN * pChan /* ptr to AT91_SIO_CHAN describing this chan */
)
{
int oldlevel = intLock();
/* initialise the driver function pointers in the SIO_CHAN */
pChan->sio.pDrvFuncs = &at91SioDrvFuncs;
/* set the non BSP-specific constants */
pChan->getTxChar = at91SioDummyCallback;
pChan->putRcvChar = at91SioDummyCallback;
pChan->channelMode = 0; /* am undefined mode */
/* initialise the chip -> Reset it */
AT91_USART_REG_WRITE (pChan, AT91_US_CR, (AT91_US_RSTRX | AT91_US_RSTTX | AT91_US_RSTSTA));
intUnlock (oldlevel);
return;
}
/*******************************************************************************
*
* at91SioDevInit2 - second initialisation stage of AT91 SIO channel
*
* This routine perform the initualisation required at the second stage
* it allocate required buffers.
*
* RETURNS: OK or ERROR if failed to allocate buffers
*/
void at91SioDevInit2
(
AT91_SIO_CHAN * pChan /* ptr to AT91_SIO_CHAN describing this chan */
)
{
#ifdef AT91_USE_PDC
pChan->rxBuffA = (UINT8 *) cacheDmaMalloc(AT91_RXBUFF_SIZE);
pChan->rxBuffB = (UINT8 *) cacheDmaMalloc(AT91_RXBUFF_SIZE);
pChan->txBuff = (UINT8 *) cacheDmaMalloc(AT91_TXBUFF_SIZE);
#endif /* AT91_USE_PDC */
/* initialise the chip */
at91SioInitChannel (pChan);
return;
}
/*******************************************************************************
*
* at91SioInitChannel - initialise USART
*
* This routine performs hardware initialisation of the USART channel.
*
* RETURNS: N/A
*/
LOCAL void at91SioInitChannel
(
AT91_SIO_CHAN * pChan /* ptr to AT91_SIO_CHAN describing this chan */
)
{
UINT32 temp;
/* reset USART: Tx, Rx and clear error status */
AT91_USART_REG_WRITE (pChan, AT91_US_CR, \
(AT91_US_RSTRX | AT91_US_RSTTX | AT91_US_RSTSTA));
#ifndef AT91_USE_PDC
/* disable Peripheral data Controller */
AT91_USART_REG_WRITE (pChan, AT91_US_RCR, 0);
AT91_USART_REG_WRITE (pChan, AT91_US_RPR, 0);
AT91_USART_REG_WRITE (pChan, AT91_US_TCR, 0);
AT91_USART_REG_WRITE (pChan, AT91_US_TPR, 0);
#else /* AT91_USE_PDC */
if (pChan->isUsart)
{
AT91_USART_REG_WRITE (pChan, AT91_US_RTOR, 8); /* timeout ~= 4 chars */
}
/* start with swing-buffer A */
AT91_USART_REG_WRITE (pChan, AT91_US_RPR, (UINT32)(pChan->rxBuffA));
AT91_USART_REG_WRITE (pChan, AT91_US_RCR, AT91_RXBUFF_SIZE);
pChan->buffInUse = 0;
AT91_USART_REG_WRITE (pChan, AT91_US_TPR, (UINT32)(pChan->txBuff));
AT91_USART_REG_WRITE (pChan, AT91_US_TCR, 0);
#endif /* AT91_USE_PDC */
/* Enable Rx and Tx */
AT91_USART_REG_WRITE (pChan, AT91_US_CR, (AT91_US_RXEN | AT91_US_TXEN));
/*
* Set word format: set 8 bits, 1 stop bit, no parity, in
* normal (non-loopback), asynchronous mode.
*
* USART clocking mode has been configured in sysSerial.c.
*/
if (pChan->isUsart)
{
AT91_USART_REG_READ (pChan, AT91_US_MR, temp);
temp &= ~0x30; /* clear all except USCLKS field */
}
else
{
temp = 0;
}
AT91_USART_REG_WRITE (pChan, AT91_US_MR, (temp | AT91_US_CHRL_8 | AT91_US_NOPAR));
/* Set baud rate divisor */
AT91_USART_REG_WRITE (pChan, AT91_US_BRGR, (pChan->xtal / (16 * pChan->baudRate)));
/* Disable Transmitter Time Guard function */
if (pChan->isUsart)
{
AT91_USART_REG_WRITE (pChan, AT91_US_TTGR, 0);
}
/*
* Enable USART Rx and Tx interrupts: errors and data. First disable
* unwanted interrupts, then enable the desired ones.
*/
#ifndef AT91_USE_PDC
AT91_USART_REG_WRITE (pChan, AT91_US_IDR, 0x000003ff);
AT91_USART_REG_WRITE (pChan, AT91_US_IER, AT91_US_RXRDY);
#else /* AT91_USE_PDC */
AT91_USART_REG_WRITE (pChan, AT91_US_IDR, AT91_US_RXRDY | AT91_US_TXRDY | \
AT91_US_TXEMPTY);
/*
* Enable ENDTX, and ENDRX (Tx, Rx xfer complete), and Rx timeout.
* Also, start the timeout.
*/
if (pChan->isUsart)
{
AT91_USART_REG_WRITE (pChan, AT91_US_IER, AT91_US_ENDRX | AT91_US_ENDTX | AT91_US_TIMEOUT);
}
else
{
/* DBGU has no timeout mechanism, nor break */
AT91_USART_REG_WRITE (pChan, AT91_US_IER, AT91_US_RXRDY | AT91_US_ENDTX);
}
AT91_USART_REG_WRITE (pChan, AT91_US_PTCR, PDC_RXTEN | PDC_TXTEN); /* enable TX and RX on PDC */
if (pChan->isUsart)
{
AT91_USART_REG_WRITE (pChan, AT91_US_CR, AT91_US_STTTO);
}
#endif /* AT91_USE_PDC */
}
/*******************************************************************************
*
* at91SioIoctl - special device control
*
* This routine handles the IOCTL messages from the user.
*
* RETURNS: OK on success, ENOSYS on unsupported request, EIO on failed
* request.
*/
LOCAL STATUS at91SioIoctl
(
SIO_CHAN * pSioChan, /* ptr to SIO_CHAN describing this channel */
int request, /* request code */
int arg /* some argument */
)
{
int oldlevel; /* current interrupt level mask */
STATUS status; /* status to return */
UINT32 brd; /* baud rate divisor */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -