📄 st16552sio.c
字号:
/* st16552Sio.c - ST 16C552 DUART tty driver *//* Copyright 1984-1999 Wind River Systems, Inc. */#include "copyright_wrs.h"/*modification history--------------------01h,30jun99,jpd improved interrupt efficiency with FIFOs (SPR #27952).01g,23jul98,cdp added ARM big-endian support.01f,04dec97,jpd updated to latest standards. Fix max/min baud setting.01e,03oct97,jpd Expanded comments/documentation.01d,04apr97,jpd Added multiplexed interrupt handler.01c,18feb97,jpd Tidied comments/documentation.01b,03dec96,jpd Added case IIR_TIMEOUT in st16550Int routine as per change to version 01f of ns16550Sio.c01a,18jul96,jpd Created from ns1550Sio.c version 01e, by adding setting of INT bit in MCR in InitChannel().*//*DESCRIPTIONThis is the device driver for the Startech ST16C552 DUART, similar, butnot quite identical to the National Semiconductor 16550 UART.The chip is a dual universal asynchronous receiver/transmitter with 16byte transmit and receive FIFOs and a programmable baud-rategenerator. Full modem control capability is included and control overthe four interrupts that can be generated: Tx, Rx, Line status, andmodem status. Only the Rx and Tx interrupts are used by this driver.The FIFOs are enabled for both Tx and Rx by this driver.Only asynchronous serial operation is supported by the UART whichsupports 5 to 8 bit bit word lengths with or without parity and withone or two stop bits. The only serial word format supported by thedriver is 8 data bits, 1 stop bit, no parity, The default baud rate isdetermined by the BSP by filling in the ST16552_CHAN structure beforecalling st16552DevInit().The exact baud rates supported by this driver will depend on thecrystal fitted (and consequently the input clock to the baud-rategenerator), but in general, baud rates from about 50 to about 115200are possible..SH DATA STRUCTURESAn ST16552_CHAN data structure is used to describe the two channels ofthe chip and, if necessary, an ST16552_MUX structure is used todescribe the multiplexing of the interrupts for the two channels of theDUART. These structures are described in h/drv/sio/st16552Sio.h..SH CALLBACKSServicing a "transmitter ready" interrupt involves making a callback toa higher level library in order to get a character to transmit. Bydefault, 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_CALLBACKioctl command. Likewise, a receiver interrupt handler makes a callbackto pass the character to the higher layer library..SH MODESThis driver supports both polled and interrupt modes..SH USAGEThe driver is typically only called by the BSP. The directly callableroutines in this module are st16552DevInit(), st16552Int(),st16552IntRd(), st16552IntWr(), and st16552MuxInt.The BSP's sysHwInit() routine typically calls sysSerialHwInit(), whichinitialises all the hardware-specific values in the ST16552_CHANstructure before calling st16552DevInit() which resets the device andinstalls the driver function pointers. After this the UART will beenabled and ready to generate interrupts, but those interrupts will bedisabled in the interrupt controller.The following example shows the first parts of the initialisation:.CS#include "drv/sio/st16552Sio.h"LOCAL ST16552_CHAN st16552Chan[N_16552_CHANNELS];void sysSerialHwInit (void) { int i; for (i = 0; i < N_16552_CHANNELS; i++) { st16552Chan[i].regDelta = devParas[i].regSpace; st16552Chan[i].regs = devParas[i].baseAdrs; st16552Chan[i].baudRate = CONSOLE_BAUD_RATE; st16552Chan[i].xtal = UART_XTAL_FREQ; st16552Chan[i].level = devParas[i].intLevel; /@ * Initialise driver functions, getTxChar, putRcvChar and * channelMode and init UART. @/ st16552DevInit(&st16552Chan[i]); } }.CEThe BSP's sysHwInit2() routine typically calls sysSerialHwInit2(),which connects the chips interrupts via intConnect() (either the singleinterrupt `st16552Int', the three interrupts `st16552IntWr',`st16552IntRd', and `st16552IntEx', or the multiplexed interrupthandler `st16552MuxInt' which will cope with both channels of a DUARTproducing the same interrupt). It then enables those interrupts in theinterrupt controller as shown in the following example:.CSvoid sysSerialHwInit2 (void) { /@ Connect the multiplexed interrupt handler @/ (void) intConnect (INUM_TO_IVEC(devParas[0].vector), st16552MuxInt, (int) &st16552Mux); intEnable (devParas[0].intLevel); }.CE.SH BSPBy convention all the BSP-specific serial initialisation is performedin 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 firsttwo have been described above, the others work as follows:sysSerialChanGet is called by usrRoot to get the serial channeldescriptor associated with a serial channel number. The routine takes asingle parameter which is a channel number ranging between zero andNUM_TTY. It returns a pointer to the corresponding channel descriptor,SIO_CHAN *, which is just the address of the ST16552_CHAN strucure.sysSerialReset is called from sysToMonitor() and should reset theserial devices to an inactive state (prevent them from generating anyinterrupts)..SH INCLUDE FILES:drv/sio/st16552Sio.h sioLib.hSEE ALSO:.I "Startech ST16C552 Data Sheet"*/#include "vxWorks.h"#include "intLib.h"#include "errnoLib.h"#include "errno.h"#include "sioLib.h"#include "drv/sio/st16552Sio.h"/* local defines */#ifndef ST16552_REG_READ#if (CPU_FAMILY == ARM) && (_BYTE_ORDER == _BIG_ENDIAN)#define ST16552_REG_READ(pChan, reg, result) \ result = \ (*(volatile UINT8 *)(3+(UINT32)pChan->regs + (reg*pChan->regDelta)))#else#define ST16552_REG_READ(pChan, reg, result) \ result = \ (*(volatile UINT8 *)((UINT32)pChan->regs + (reg*pChan->regDelta)))#endif /* _BYTE_ORDER == _BIG_ENDIAN */#endif /* ifndef ST16552_REG_READ */#ifndef ST16552_REG_WRITE#define ST16552_REG_WRITE(pChan, reg, data) \ (*(volatile UINT8 *)((UINT32)pChan->regs + (reg*pChan->regDelta))) = \ (data)#endif/* locals */LOCAL BOOL multiplexed = FALSE; /* whether int from mux-ed handler */LOCAL char intStatus;/* function prototypes */LOCAL STATUS st16552DummyCallback ();LOCAL void st16552InitChannel (ST16552_CHAN *);LOCAL STATUS st16552Ioctl (SIO_CHAN * pSioChan, int request, int arg);LOCAL int st16552TxStartup (SIO_CHAN * pSioChan);LOCAL int st16552CallbackInstall (SIO_CHAN * pSioChan, int callbackType, STATUS (*callback)(), void * callbackArg);LOCAL int st16552PollInput (SIO_CHAN * pSioChan, char *);LOCAL int st16552PollOutput (SIO_CHAN * pSioChan, char);/* driver functions */LOCAL SIO_DRV_FUNCS st16552SioDrvFuncs = { (int (*)())st16552Ioctl, st16552TxStartup, st16552CallbackInstall, st16552PollInput, st16552PollOutput };/********************************************************************************* st16552DummyCallback - dummy callback routine.** RETURNS: ERROR, always.*/LOCAL STATUS st16552DummyCallback (void) { return ERROR; }/********************************************************************************* st16552DevInit - initialise an ST16552 channel** This routine initialises some SIO_CHAN function pointers and then resets* the chip in a quiescent state. Before this routine is called, the BSP* must already have initialised all the device addresses, etc. in the* ST16552_CHAN structure.** RETURNS: N/A*/void st16552DevInit ( ST16552_CHAN * pChan ) { int oldlevel = intLock(); /* initialise the driver function pointers in the SIO_CHAN */ pChan->sio.pDrvFuncs = &st16552SioDrvFuncs; /* set the non BSP-specific constants */ pChan->getTxChar = st16552DummyCallback; pChan->putRcvChar = st16552DummyCallback; pChan->channelMode = 0; /* undefined */ /* reset the chip */ st16552InitChannel(pChan); intUnlock(oldlevel); }/********************************************************************************* st16552InitChannel - initialise UART** This routine performs hardware initialisation of the UART channel.** RETURNS: N/A */LOCAL void st16552InitChannel ( ST16552_CHAN * pChan /* ptr to ST16552 struct describing channel */ ) { /* Configure Port - Set 8 bits, 1 stop bit, no parity. */ /* keep soft copy */ pChan->lcr = (UINT8)(CHAR_LEN_8 | ONE_STOP | PARITY_NONE); /* Set Line Control Register */ ST16552_REG_WRITE(pChan, LCR, pChan->lcr); /* Reset/Enable the FIFOs */ ST16552_REG_WRITE(pChan, FCR, RxCLEAR | TxCLEAR | FIFO_ENABLE | FCR_DMA | FCR_RXTRIG_H | FCR_RXTRIG_L); /* Enable access to the divisor latches by setting DLAB in LCR. */ ST16552_REG_WRITE(pChan, LCR, LCR_DLAB | pChan->lcr); /* Set divisor latches to set baud rate */ ST16552_REG_WRITE(pChan, DLL, pChan->xtal/(16*pChan->baudRate)); ST16552_REG_WRITE(pChan, DLM, (pChan->xtal/(16*pChan->baudRate)) >> 8); /* Restore Line Control Register */ ST16552_REG_WRITE(pChan, LCR, pChan->lcr); /* * This appears to be different from the NS 16550, which defines Bit 3 to * be a general purpose output. The ST 16552 datasheet defines it to set * the INT output pin from tri-state to normal active mode. If this is not * done, then no interrupts are generated, at least on the ARM PID7T * board. */ /* Set INT output pin to normal/active operating mode and assert DTR */ /* ST16552_REG_WRITE(pChan, MCR, MCR_INT | MCR_DTR); */ ST16552_REG_WRITE(pChan, MCR, 0); /* modify by zou 03-3-3 9:43 */ /* Make a copy of Interrupt Enable Register */ /* pChan->ier = (UINT8)(RxFIFO_BIT | TxFIFO_BIT | Rx_BIT); */ pChan->ier = (UINT8)(RxFIFO_BIT | TxFIFO_BIT ); /* modify by zou 03-3-1 10:03 */ /* Disable interrupts */ ST16552_REG_WRITE(pChan, IER, 0); }/********************************************************************************* st16552Ioctl - special device control** This routine handles the IOCTL messages from the user.** RETURNS: OK on success, EIO on device error, ENOSYS on unsupported* request.*/LOCAL STATUS st16552Ioctl ( 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 */ ST16552_CHAN * pChan = (ST16552_CHAN *)pSioChan; UINT32 brd; /* baud rate divisor */ status = OK; /* preset to return OK */ switch (request) { case SIO_BAUD_SET: /* * Set the baud rate. Return EIO for an invalid baud rate, or * OK on success. * * baudrate divisor must be non-zero and must fit in a 16-bit * register. */ brd = pChan->xtal/(16*arg); /* calculate baudrate divisor */ if ((brd < 1) || (brd > 0xFFFF)) { status = EIO; /* baud rate out of range */ break; } /* disable interrupts during chip access */ oldlevel = intLock (); /* Enable access to the divisor latches by setting DLAB in LCR. */ ST16552_REG_WRITE(pChan, LCR, LCR_DLAB | pChan->lcr); /* Set divisor latches. */ ST16552_REG_WRITE(pChan, DLL, brd); ST16552_REG_WRITE(pChan, DLM, brd >> 8); /* Restore Line Control Register */ ST16552_REG_WRITE(pChan, LCR, pChan->lcr); pChan->baudRate = arg; intUnlock (oldlevel); break; case SIO_BAUD_GET: *(int *)arg = pChan->baudRate; break; case SIO_MODE_SET: if ((arg != SIO_MODE_POLL) && (arg != SIO_MODE_INT)) { status = EIO; break; } oldlevel = intLock (); if (arg == SIO_MODE_INT) { /* Enable appropriate interrupts | Rx_BIT*/ ST16552_REG_WRITE(pChan, IER, pChan->ier | RxFIFO_BIT | TxFIFO_BIT ); } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -