550 lines
16 KiB
C
550 lines
16 KiB
C
/* ns16550.c - NS16550D serial driver */
|
|
|
|
/*
|
|
* Copyright (c) 2010, 2012-2015 Wind River Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Wind River Systems nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
This is the driver for the Intel NS16550 UART Chip used on the PC 386.
|
|
It uses the SCCs in asynchronous mode only.
|
|
|
|
|
|
USAGE
|
|
An ns16550 structure is used to describe the chip.
|
|
The BSP's _InitHardware() routine initializes all the
|
|
values in the uart_init_info structure before calling uart_init().
|
|
|
|
A board support package's board.h header must provide definitions for:
|
|
|
|
- the following register access routines:
|
|
|
|
unsigned int inByte(unsigned int address);
|
|
void outByte(unsigned char data, unsigned int address);
|
|
|
|
- and the following macro for the number of bytes between register addresses:
|
|
|
|
UART_REG_ADDR_INTERVAL
|
|
|
|
|
|
INCLUDE FILES: drivers/uart.h
|
|
*/
|
|
|
|
#include <nanokernel.h>
|
|
#include <arch/cpu.h>
|
|
#include <stdint.h>
|
|
|
|
#include <board.h>
|
|
#include <toolchain.h>
|
|
#include <sections.h>
|
|
#include <drivers/uart.h>
|
|
#ifdef CONFIG_PCI
|
|
#include <pci/pci.h>
|
|
#include <pci/pci_mgr.h>
|
|
#endif /* CONFIG_PCI */
|
|
|
|
/* register definitions */
|
|
|
|
#define REG_THR 0x00 /* Transmitter holding reg. */
|
|
#define REG_RDR 0x00 /* Receiver data reg. */
|
|
#define REG_BRDL 0x00 /* Baud rate divisor (LSB) */
|
|
#define REG_BRDH 0x01 /* Baud rate divisor (MSB) */
|
|
#define REG_IER 0x01 /* Interrupt enable reg. */
|
|
#define REG_IIR 0x02 /* Interrupt ID reg. */
|
|
#define REG_FCR 0x02 /* FIFO control reg. */
|
|
#define REG_LCR 0x03 /* Line control reg. */
|
|
#define REG_MDC 0x04 /* Modem control reg. */
|
|
#define REG_LSR 0x05 /* Line status reg. */
|
|
#define REG_MSR 0x06 /* Modem status reg. */
|
|
|
|
/* equates for interrupt enable register */
|
|
|
|
#define IER_RXRDY 0x01 /* receiver data ready */
|
|
#define IER_TBE 0x02 /* transmit bit enable */
|
|
#define IER_LSR 0x04 /* line status interrupts */
|
|
#define IER_MSI 0x08 /* modem status interrupts */
|
|
|
|
/* equates for interrupt identification register */
|
|
|
|
#define IIR_IP 0x01 /* interrupt pending bit */
|
|
#define IIR_MASK 0x07 /* interrupt id bits mask */
|
|
#define IIR_MSTAT 0x00 /* modem status interrupt */
|
|
#define IIR_THRE 0X02 /* transmit holding register empty */
|
|
#define IIR_RBRF 0x04 /* receiver buffer register full */
|
|
#define IIR_ID 0x06 /* interupt ID mask without IP */
|
|
#define IIR_SEOB 0x06 /* serialization error or break */
|
|
|
|
/* equates for FIFO control register */
|
|
|
|
#define FCR_FIFO 0x01 /* enable XMIT and RCVR FIFO */
|
|
#define FCR_RCVRCLR 0x02 /* clear RCVR FIFO */
|
|
#define FCR_XMITCLR 0x04 /* clear XMIT FIFO */
|
|
|
|
/*
|
|
* Per PC16550D (Literature Number: SNLS378B):
|
|
*
|
|
* RXRDY, Mode 0: When in the 16450 Mode (FCR0 = 0) or in
|
|
* the FIFO Mode (FCR0 = 1, FCR3 = 0) and there is at least 1
|
|
* character in the RCVR FIFO or RCVR holding register, the
|
|
* RXRDY pin (29) will be low active. Once it is activated the
|
|
* RXRDY pin will go inactive when there are no more charac-
|
|
* ters in the FIFO or holding register.
|
|
*
|
|
* RXRDY, Mode 1: In the FIFO Mode (FCR0 = 1) when the
|
|
* FCR3 = 1 and the trigger level or the timeout has been
|
|
* reached, the RXRDY pin will go low active. Once it is acti-
|
|
* vated it will go inactive when there are no more characters
|
|
* in the FIFO or holding register.
|
|
*
|
|
* TXRDY, Mode 0: In the 16450 Mode (FCR0 = 0) or in the
|
|
* FIFO Mode (FCR0 = 1, FCR3 = 0) and there are no charac-
|
|
* ters in the XMIT FIFO or XMIT holding register, the TXRDY
|
|
* pin (24) will be low active. Once it is activated the TXRDY
|
|
* pin will go inactive after the first character is loaded into the
|
|
* XMIT FIFO or holding register.
|
|
*
|
|
* TXRDY, Mode 1: In the FIFO Mode (FCR0 = 1) when
|
|
* FCR3 = 1 and there are no characters in the XMIT FIFO, the
|
|
* TXRDY pin will go low active. This pin will become inactive
|
|
* when the XMIT FIFO is completely full.
|
|
*/
|
|
#define FCR_MODE0 0x00 /* set receiver in mode 0 */
|
|
#define FCR_MODE1 0x08 /* set receiver in mode 1 */
|
|
|
|
/* RCVR FIFO interrupt levels: trigger interrupt with this bytes in FIFO */
|
|
#define FCR_FIFO_1 0x00 /* 1 byte in RCVR FIFO */
|
|
#define FCR_FIFO_4 0x40 /* 4 bytes in RCVR FIFO */
|
|
#define FCR_FIFO_8 0x80 /* 8 bytes in RCVR FIFO */
|
|
#define FCR_FIFO_14 0xC0 /* 14 bytes in RCVR FIFO */
|
|
|
|
/* constants for line control register */
|
|
|
|
#define LCR_CS5 0x00 /* 5 bits data size */
|
|
#define LCR_CS6 0x01 /* 6 bits data size */
|
|
#define LCR_CS7 0x02 /* 7 bits data size */
|
|
#define LCR_CS8 0x03 /* 8 bits data size */
|
|
#define LCR_2_STB 0x04 /* 2 stop bits */
|
|
#define LCR_1_STB 0x00 /* 1 stop bit */
|
|
#define LCR_PEN 0x08 /* parity enable */
|
|
#define LCR_PDIS 0x00 /* parity disable */
|
|
#define LCR_EPS 0x10 /* even parity select */
|
|
#define LCR_SP 0x20 /* stick parity select */
|
|
#define LCR_SBRK 0x40 /* break control bit */
|
|
#define LCR_DLAB 0x80 /* divisor latch access enable */
|
|
|
|
/* constants for the modem control register */
|
|
|
|
#define MCR_DTR 0x01 /* dtr output */
|
|
#define MCR_RTS 0x02 /* rts output */
|
|
#define MCR_OUT1 0x04 /* output #1 */
|
|
#define MCR_OUT2 0x08 /* output #2 */
|
|
#define MCR_LOOP 0x10 /* loop back */
|
|
|
|
/* constants for line status register */
|
|
|
|
#define LSR_RXRDY 0x01 /* receiver data available */
|
|
#define LSR_OE 0x02 /* overrun error */
|
|
#define LSR_PE 0x04 /* parity error */
|
|
#define LSR_FE 0x08 /* framing error */
|
|
#define LSR_BI 0x10 /* break interrupt */
|
|
#define LSR_THRE 0x20 /* transmit holding register empty */
|
|
#define LSR_TEMT 0x40 /* transmitter empty */
|
|
|
|
/* constants for modem status register */
|
|
|
|
#define MSR_DCTS 0x01 /* cts change */
|
|
#define MSR_DDSR 0x02 /* dsr change */
|
|
#define MSR_DRI 0x04 /* ring change */
|
|
#define MSR_DDCD 0x08 /* data carrier change */
|
|
#define MSR_CTS 0x10 /* complement of cts */
|
|
#define MSR_DSR 0x20 /* complement of dsr */
|
|
#define MSR_RI 0x40 /* complement of ring signal */
|
|
#define MSR_DCD 0x80 /* complement of dcd */
|
|
|
|
/* convenience defines */
|
|
|
|
#define THR(n) (uart[n].port + REG_THR * UART_REG_ADDR_INTERVAL)
|
|
#define RDR(n) (uart[n].port + REG_RDR * UART_REG_ADDR_INTERVAL)
|
|
#define BRDL(n) (uart[n].port + REG_BRDL * UART_REG_ADDR_INTERVAL)
|
|
#define BRDH(n) (uart[n].port + REG_BRDH * UART_REG_ADDR_INTERVAL)
|
|
#define IER(n) (uart[n].port + REG_IER * UART_REG_ADDR_INTERVAL)
|
|
#define IIR(n) (uart[n].port + REG_IIR * UART_REG_ADDR_INTERVAL)
|
|
#define FCR(n) (uart[n].port + REG_FCR * UART_REG_ADDR_INTERVAL)
|
|
#define LCR(n) (uart[n].port + REG_LCR * UART_REG_ADDR_INTERVAL)
|
|
#define MDC(n) (uart[n].port + REG_MDC * UART_REG_ADDR_INTERVAL)
|
|
#define LSR(n) (uart[n].port + REG_LSR * UART_REG_ADDR_INTERVAL)
|
|
#define MSR(n) (uart[n].port + REG_MSR * UART_REG_ADDR_INTERVAL)
|
|
|
|
#define IIRC(n) uart[n].iirCache
|
|
|
|
#define INBYTE(x) inByte(x)
|
|
#define OUTBYTE(x, d) outByte(d, x)
|
|
|
|
|
|
struct ns16550 {
|
|
uint32_t port; /* base port number or MM base address */
|
|
uint8_t irq; /* interrupt request level */
|
|
uint8_t intPri; /* interrupt priority */
|
|
uint8_t iirCache; /* cache of IIR since it clears when read */
|
|
};
|
|
|
|
#if !(defined(UART_PORTS_CONFIGURE)) && !(defined(CONFIG_PCI))
|
|
|
|
#error "CONFIG_PCI or UART_PORTS_CONFIGURE is needed"
|
|
|
|
#elif !(defined(UART_PORTS_CONFIGURE)) && defined(CONFIG_PCI)
|
|
|
|
static struct ns16550 uart[CONFIG_UART_NUM_SYSTEM_PORTS] = {};
|
|
|
|
static inline void ns16550_uart_init()
|
|
{
|
|
/*
|
|
* This device information is specific to Quark UART
|
|
* for another device it may need to be changed
|
|
*/
|
|
struct pci_dev_info dev_info = {
|
|
.class = PCI_CLASS_COMM_CTLR,
|
|
.vendor_id = CONFIG_UART_PCI_VENDOR_ID,
|
|
.device_id = CONFIG_UART_PCI_DEVICE_ID,
|
|
.bar = CONFIG_UART_PCI_BAR,
|
|
};
|
|
int i;
|
|
|
|
if (uart[0].port && uart[0].irq)
|
|
return;
|
|
|
|
pci_bus_scan_init();
|
|
|
|
for (i = 0; pci_bus_scan(&dev_info) &&
|
|
i < CONFIG_UART_NUM_SYSTEM_PORTS; i++) {
|
|
uart[i].port = dev_info.addr;
|
|
uart[i].irq = dev_info.irq;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
#define ns16550_uart_init() \
|
|
do {} while ((0))
|
|
|
|
UART_PORTS_CONFIGURE(struct ns16550, uart);
|
|
|
|
#endif /* UART_PORTS_CONFIGURE */
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_init - initialize the chip
|
|
*
|
|
* This routine is called to reset the chip in a quiescent state.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void uart_init(int port, /* UART channel to initialize */
|
|
const struct uart_init_info * const init_info
|
|
)
|
|
{
|
|
int oldLevel; /* old interrupt lock level */
|
|
uint32_t divisor; /* baud rate divisor */
|
|
|
|
ns16550_uart_init();
|
|
|
|
uart[port].intPri = init_info->int_pri;
|
|
uart[port].iirCache = 0;
|
|
|
|
oldLevel = irq_lock();
|
|
|
|
/* calculate baud rate divisor */
|
|
divisor = (init_info->sys_clk_freq / init_info->baud_rate) >> 4;
|
|
|
|
/* set the DLAB to access the baud rate divisor registers */
|
|
OUTBYTE(LCR(port), LCR_DLAB);
|
|
OUTBYTE(BRDL(port), (unsigned char)(divisor & 0xff));
|
|
OUTBYTE(BRDH(port), (unsigned char)((divisor >> 8) & 0xff));
|
|
|
|
/* 8 data bits, 1 stop bit, no parity, clear DLAB */
|
|
OUTBYTE(LCR(port), LCR_CS8 | LCR_1_STB | LCR_PDIS);
|
|
|
|
OUTBYTE(MDC(port), MCR_OUT2 | MCR_RTS | MCR_DTR);
|
|
|
|
/*
|
|
* Program FIFO: enabled, mode 0 (set for compatibility with quark),
|
|
* generate the interrupt at 8th byte
|
|
* Clear TX and RX FIFO
|
|
*/
|
|
OUTBYTE(FCR(port),
|
|
FCR_FIFO | FCR_MODE0 | FCR_FIFO_8 | FCR_RCVRCLR | FCR_XMITCLR);
|
|
|
|
/* clear the port */
|
|
INBYTE(RDR(port));
|
|
|
|
/* disable interrupts */
|
|
OUTBYTE(IER(port), 0x00);
|
|
|
|
irq_unlock(oldLevel);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_poll_in - poll the device for input.
|
|
*
|
|
* RETURNS: 0 if a character arrived, -1 if the input buffer if empty.
|
|
*/
|
|
|
|
int uart_poll_in(int port, /* UART channel to select for input */
|
|
unsigned char *pChar /* pointer to char */
|
|
)
|
|
{
|
|
if ((INBYTE(LSR(port)) & LSR_RXRDY) == 0x00)
|
|
return (-1);
|
|
|
|
/* got a character */
|
|
*pChar = INBYTE(RDR(port));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_poll_out - output a character in polled mode.
|
|
*
|
|
* Checks if the transmitter is empty. If empty, a character is written to
|
|
* the data register.
|
|
*
|
|
* If the hardware flow control is enabled then the handshake signal CTS has to
|
|
* be asserted in order to send a character.
|
|
*
|
|
* RETURNS: sent character
|
|
*/
|
|
unsigned char uart_poll_out(
|
|
int port, /* UART channel to select for output */
|
|
unsigned char outChar /* char to send */
|
|
)
|
|
{
|
|
/* wait for transmitter to ready to accept a character */
|
|
while ((INBYTE(LSR(port)) & LSR_TEMT) == 0)
|
|
;
|
|
|
|
OUTBYTE(THR(port), outChar);
|
|
|
|
return outChar;
|
|
}
|
|
|
|
#if CONFIG_UART_INTERRUPT_DRIVEN
|
|
/*******************************************************************************
|
|
*
|
|
* uart_fifo_fill - fill FIFO with data
|
|
*
|
|
* RETURNS: number of bytes sent
|
|
*/
|
|
|
|
int uart_fifo_fill(int port, /* UART on port to send */
|
|
const uint8_t *txData, /* data to transmit */
|
|
int size /* number of bytes to send */
|
|
)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < size && (INBYTE(LSR(port)) & LSR_THRE) != 0; i++) {
|
|
OUTBYTE(THR(port), txData[i]);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_fifo_read - read data from FIFO
|
|
*
|
|
* RETURNS: number of bytes read
|
|
*/
|
|
|
|
int uart_fifo_read(int port, /* UART to receive from */
|
|
uint8_t *rxData, /* data container */
|
|
const int size /* container size */
|
|
)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < size && (INBYTE(LSR(port)) & LSR_RXRDY) != 0; i++) {
|
|
rxData[i] = INBYTE(RDR(port));
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_tx_enable - enable TX interrupt in IER
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void uart_irq_tx_enable(int port /* UART to enable Tx
|
|
interrupt */
|
|
)
|
|
{
|
|
OUTBYTE(IER(port), INBYTE(IER(port)) | IER_TBE);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_tx_disable - disable TX interrupt in IER
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void uart_irq_tx_disable(int port /* UART to disable Tx interrupt */
|
|
)
|
|
{
|
|
OUTBYTE(IER(port), INBYTE(IER(port)) & (~IER_TBE));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_tx_ready - check if Tx IRQ has been raised
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
int uart_irq_tx_ready(int port /* UART to check */
|
|
)
|
|
{
|
|
return ((IIRC(port) & IIR_ID) == IIR_THRE);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _uart_irq_rx_enable - enable RX interrupt in IER
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void uart_irq_rx_enable(int port /* UART to enable Rx
|
|
interrupt */
|
|
)
|
|
{
|
|
OUTBYTE(IER(port), INBYTE(IER(port)) | IER_RXRDY);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_rx_disable - disable RX interrupt in IER
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void uart_irq_rx_disable(int port /* UART to disable Rx interrupt */
|
|
)
|
|
{
|
|
OUTBYTE(IER(port), INBYTE(IER(port)) & (~IER_RXRDY));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_rx_ready - check if Rx IRQ has been raised
|
|
*
|
|
* RETURNS: 1 if an IRQ is ready, 0 otherwise
|
|
*/
|
|
|
|
int uart_irq_rx_ready(int port /* UART to check */
|
|
)
|
|
{
|
|
return ((IIRC(port) & IIR_ID) == IIR_RBRF);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_err_enable - enable error interrupt in IER
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void uart_irq_err_enable(int port /* UART to enable Rx interrupt */
|
|
)
|
|
{
|
|
OUTBYTE(IER(port), INBYTE(IER(port)) | IER_LSR);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_err_disable - disable error interrupt in IER
|
|
*
|
|
* RETURNS: 1 if an IRQ is ready, 0 otherwise
|
|
*/
|
|
|
|
void uart_irq_err_disable(int port /* UART to disable Rx interrupt */
|
|
)
|
|
{
|
|
OUTBYTE(IER(port), INBYTE(IER(port)) & (~IER_LSR));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_is_pending - check if any IRQ is pending
|
|
*
|
|
* RETURNS: 1 if an IRQ is pending, 0 otherwise
|
|
*/
|
|
|
|
int uart_irq_is_pending(int port /* UART to check */
|
|
)
|
|
{
|
|
return (!(IIRC(port) & IIR_IP));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_update - update cached contents of IIR
|
|
*
|
|
* RETURNS: always 1
|
|
*/
|
|
|
|
int uart_irq_update(int port /* UART to update */
|
|
)
|
|
{
|
|
IIRC(port) = INBYTE(IIR(port));
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* uart_irq_get - returns UART interrupt number
|
|
*
|
|
* Returns the IRQ number used by the specified UART port
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
unsigned int uart_irq_get(int port /* UART port */
|
|
)
|
|
{
|
|
return (unsigned int)uart[port].irq;
|
|
}
|
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|