Files
rtems/bsps/powerpc/shared/console/uart.c
Peter Dufault f8b91c9503 powerpc/shared/console: "termios" first open sets console baud to 9600
When the PowerPC shared console baud rate starts at anything other than
9600 the termios code will set it to 9600 at the first open.
2021-04-27 16:46:52 -06:00

786 lines
15 KiB
C

/*
* This software is Copyright (C) 1998 by T.sqware - all rights limited
* It is provided in to the public domain "as is", can be freely modified
* as far as this copyight notice is kept unchanged, but does not imply
* an endorsement by T.sqware of the product in which it is included.
*/
#include <stdint.h>
#include <stdio.h>
#include <bsp.h>
#include <bsp/irq.h>
#include <bsp/uart.h>
#include <rtems/libio.h>
#include <rtems/bspIo.h>
#include <rtems/termiostypes.h>
#include <termios.h>
#include <assert.h>
/*
* Basic 16552 driver
*/
struct uart_data
{
unsigned long ioBase;
int irq;
int hwFlow;
int baud;
BSP_UartBreakCbRec breakCallback;
int ioMode;
};
/*
* Initialization of BSP specific data.
* The constants are pulled in from a BSP
* specific file, whereas all of the code
* in this file is generic and makes no
* assumptions about addresses, irq vectors
* etc...
*/
#define UART_UNSUPP ((unsigned long)(-1))
static struct uart_data uart_data[2] = {
{
#ifdef BSP_UART_IOBASE_COM1
BSP_UART_IOBASE_COM1,
BSP_UART_COM1_IRQ,
#else
UART_UNSUPP,
-1,
#endif
},
{
#ifdef BSP_UART_IOBASE_COM2
BSP_UART_IOBASE_COM2,
BSP_UART_COM2_IRQ,
#else
UART_UNSUPP,
-1,
#endif
},
};
#define MAX_UARTS (sizeof(uart_data)/sizeof(uart_data[0]))
#define SANITY_CHECK(uart) \
assert( MAX_UARTS > (unsigned)(uart) && uart_data[(uart)].ioBase != UART_UNSUPP )
/*
* Macros to read/write register of uart, if configuration is
* different just rewrite these macros
*/
static inline unsigned char
uread(int uart, unsigned int reg)
{
return in_8((uint8_t*)(uart_data[uart].ioBase + reg));
}
static inline void
uwrite(int uart, int reg, unsigned int val)
{
out_8((uint8_t*)(uart_data[uart].ioBase + reg), val);
}
static void
uartError(int uart, void *termiosPrivate)
{
unsigned char uartStatus, dummy;
BSP_UartBreakCbProc h;
uartStatus = uread(uart, LSR);
dummy = uread(uart, RBR);
#ifdef UARTDEBUG
if (uartStatus & OE)
printk("********* Over run Error **********\n");
if (uartStatus & PE)
printk("********* Parity Error **********\n");
if (uartStatus & FE)
printk("********* Framing Error **********\n");
if (uartStatus & BI) {
printk("********* BREAK INTERRUPT *********\n");
#endif
if ((h=uart_data[uart].breakCallback.handler)) {
h(uart,
(dummy<<8)|uartStatus,
termiosPrivate,
uart_data[uart].breakCallback.private);
}
#ifdef UARTDEBUG
if (uartStatus & ERFIFO)
printk("********* Error receive Fifo **********\n");
#endif
}
/*
* Uart initialization, it is hardcoded to 8 bit, no parity,
* one stop bit, FIFO, things to be changed
* are baud rate and nad hw flow control,
* and longest rx fifo setting
*/
void
BSP_uart_init(int uart, int baud, int hwFlow)
{
unsigned char tmp;
/* Sanity check */
SANITY_CHECK(uart);
/* Make sure any printk activity drains before
* re-initializing.
*/
while ( ! (uread(uart, LSR) & TEMT) )
;
switch(baud)
{
case 50:
case 75:
case 110:
case 134:
case 300:
case 600:
case 1200:
case 2400:
case 9600:
case 19200:
case 38400:
case 57600:
case 115200:
break;
default:
assert(0);
return;
}
/* Set DLAB bit to 1 */
uwrite(uart, LCR, DLAB);
if ( (int)BSPBaseBaud <= 0 ) {
/* Use current divisor assuming BSPBaseBaud gives us the current speed */
BSPBaseBaud = BSPBaseBaud ? -BSPBaseBaud : BSP_CONSOLE_BAUD;
BSPBaseBaud *= ((uread(uart, DLM) << 8) | uread(uart, DLL));
}
/* Set baud rate */
uwrite(uart, DLL, (BSPBaseBaud/baud) & 0xff);
uwrite(uart, DLM, ((BSPBaseBaud/baud) >> 8) & 0xff);
/* 8-bit, no parity , 1 stop */
uwrite(uart, LCR, CHR_8_BITS);
/* Set DTR, RTS and OUT2 high */
uwrite(uart, MCR, DTR | RTS | OUT_2);
/* Enable FIFO */
uwrite(uart, FCR, FIFO_EN | XMIT_RESET | RCV_RESET | RECEIVE_FIFO_TRIGGER12);
/* Disable Interrupts */
uwrite(uart, IER, 0);
/* Read status to clear them */
tmp = uread(uart, LSR);
tmp = uread(uart, RBR);
tmp = uread(uart, MSR);
(void) tmp; /* avoid set but not used warning */
/* Remember state */
uart_data[uart].hwFlow = hwFlow;
uart_data[uart].baud = baud;
return;
}
/*
* Set baud
*/
void
BSP_uart_set_baud(int uart, int baud)
{
unsigned char mcr, ier;
/* Sanity check */
SANITY_CHECK(uart);
/*
* This function may be called whenever TERMIOS parameters
* are changed, so we have to make sure that baud change is
* indeed required.
*/
if(baud == uart_data[uart].baud)
{
return;
}
mcr = uread(uart, MCR);
ier = uread(uart, IER);
BSP_uart_init(uart, baud, uart_data[uart].hwFlow);
uwrite(uart, MCR, mcr);
uwrite(uart, IER, ier);
return;
}
/*
* Enable/disable interrupts
*/
void
BSP_uart_intr_ctrl(int uart, int cmd)
{
SANITY_CHECK(uart);
switch(cmd)
{
case BSP_UART_INTR_CTRL_DISABLE:
uwrite(uart, IER, INTERRUPT_DISABLE);
break;
case BSP_UART_INTR_CTRL_ENABLE:
if(uart_data[uart].hwFlow)
{
uwrite(uart, IER,
(RECEIVE_ENABLE |
TRANSMIT_ENABLE |
RECEIVER_LINE_ST_ENABLE |
MODEM_ENABLE
)
);
}
else
{
uwrite(uart, IER,
(RECEIVE_ENABLE |
TRANSMIT_ENABLE |
RECEIVER_LINE_ST_ENABLE
)
);
}
break;
case BSP_UART_INTR_CTRL_TERMIOS:
if(uart_data[uart].hwFlow)
{
uwrite(uart, IER,
(RECEIVE_ENABLE |
RECEIVER_LINE_ST_ENABLE |
MODEM_ENABLE
)
);
}
else
{
uwrite(uart, IER,
(RECEIVE_ENABLE |
RECEIVER_LINE_ST_ENABLE
)
);
}
break;
case BSP_UART_INTR_CTRL_GDB:
uwrite(uart, IER, RECEIVE_ENABLE);
break;
default:
assert(0);
break;
}
return;
}
void
BSP_uart_throttle(int uart)
{
unsigned int mcr;
SANITY_CHECK(uart);
if(!uart_data[uart].hwFlow)
{
/* Should not happen */
assert(0);
return;
}
mcr = uread (uart, MCR);
/* RTS down */
mcr &= ~RTS;
uwrite(uart, MCR, mcr);
return;
}
void
BSP_uart_unthrottle(int uart)
{
unsigned int mcr;
SANITY_CHECK(uart);
if(!uart_data[uart].hwFlow)
{
/* Should not happen */
assert(0);
return;
}
mcr = uread (uart, MCR);
/* RTS up */
mcr |= RTS;
uwrite(uart, MCR, mcr);
return;
}
/*
* Status function, -1 if error
* detected, 0 if no received chars available,
* 1 if received char available, 2 if break
* is detected, it will eat break and error
* chars. It ignores overruns - we cannot do
* anything about - it execpt count statistics
* and we are not counting it.
*/
int
BSP_uart_polled_status(int uart)
{
unsigned char val;
SANITY_CHECK(uart);
val = uread(uart, LSR);
if(val & BI)
{
/* BREAK found, eat character */
uread(uart, RBR);
return BSP_UART_STATUS_BREAK;
}
if((val & (DR | OE | FE)) == 1)
{
/* No error, character present */
return BSP_UART_STATUS_CHAR;
}
if((val & (DR | OE | FE)) == 0)
{
/* Nothing */
return BSP_UART_STATUS_NOCHAR;
}
/*
* Framing or parity error
* eat character
*/
uread(uart, RBR);
return BSP_UART_STATUS_ERROR;
}
/*
* Polled mode write function
*/
void
BSP_uart_polled_write(int uart, int val)
{
unsigned char val1;
/* Sanity check */
SANITY_CHECK(uart);
for(;;)
{
if((val1=uread(uart, LSR)) & THRE)
{
break;
}
}
if(uart_data[uart].hwFlow)
{
for(;;)
{
if(uread(uart, MSR) & CTS)
{
break;
}
}
}
uwrite(uart, THR, val & 0xff);
return;
}
void
BSP_output_char_via_serial(const char val)
{
BSP_uart_polled_write(BSPConsolePort, val);
}
/*
* Polled mode read function
*/
int
BSP_uart_polled_read(int uart)
{
unsigned char val;
SANITY_CHECK(uart);
for(;;)
{
if(uread(uart, LSR) & DR)
{
break;
}
}
val = uread(uart, RBR);
return (int)(val & 0xff);
}
unsigned
BSP_poll_char_via_serial()
{
return BSP_uart_polled_read(BSPConsolePort);
}
static void
uart_noop(const rtems_irq_connect_data *unused)
{
return;
}
/* note that the IRQ names contain _ISA_ for legacy
* reasons. They can be any interrupt, depending
* on the particular BSP...
*/
static int
uart_isr_is_on(const rtems_irq_connect_data *irq)
{
int uart;
uart = (irq->name == BSP_UART_COM1_IRQ) ?
BSP_UART_COM1 : BSP_UART_COM2;
return uread(uart,IER);
}
static int
doit(int uart, rtems_irq_hdl handler, int (*p)(const rtems_irq_connect_data*))
{
rtems_irq_connect_data d={0};
d.name = uart_data[uart].irq;
d.off = d.on = uart_noop;
d.isOn = uart_isr_is_on;
d.hdl = handler;
return p(&d);
}
int
BSP_uart_install_isr(int uart, rtems_irq_hdl handler)
{
/* Using shared interrupts by default might break things.. the
* shared IRQ installer uses malloc() and if a BSP had called this
* during early init it might not work...
*/
#ifdef BSP_UART_USE_SHARED_IRQS
return doit(uart, handler, BSP_install_rtems_shared_irq_handler);
#else
return doit(uart, handler, BSP_install_rtems_irq_handler);
#endif
}
int
BSP_uart_remove_isr(int uart, rtems_irq_hdl handler)
{
return doit(uart, handler, BSP_remove_rtems_irq_handler);
}
/* ================ Termios support =================*/
static volatile int termios_stopped_com[2] = {0,0};
static volatile int termios_tx_active_com[2] = {0,0};
static void* termios_ttyp_com[2] = {NULL,NULL};
static char termios_tx_hold_com[2] = {0,0};
static volatile char termios_tx_hold_valid_com[2] = {0,0};
/*
* Set channel parameters
*/
void
BSP_uart_termios_set(int uart, void *p)
{
struct rtems_termios_tty *ttyp = p;
unsigned char val;
SANITY_CHECK(uart);
if(uart_data[uart].hwFlow)
{
val = uread(uart, MSR);
termios_stopped_com[uart] = (val & CTS) ? 0 : 1;
}
else
{
termios_stopped_com[uart] = 0;
}
termios_tx_active_com[uart] = 0;
termios_ttyp_com[uart] = ttyp;
termios_tx_hold_com[uart] = 0;
termios_tx_hold_valid_com[uart] = 0;
uart_data[uart].ioMode = ttyp->device.outputUsesInterrupts;
/* Convert from the baud number to the "speed_t" termios setting. */
ttyp->termios.c_ispeed = ttyp->termios.c_ospeed =
rtems_termios_number_to_baud(uart_data[uart].baud);
return;
}
ssize_t
BSP_uart_termios_write_polled(int minor, const char *buf, size_t len)
{
int uart=minor; /* could differ, theoretically */
int nwrite;
const char *b = buf;
for (nwrite=0 ; nwrite < len ; nwrite++) {
BSP_uart_polled_write(uart, *b++);
}
return nwrite;
}
ssize_t
BSP_uart_termios_write_com(int minor, const char *buf, size_t len)
{
int uart=minor; /* could differ, theoretically */
if(len <= 0)
{
return 0;
}
/* If the TX buffer is busy - something is royally screwed up */
/* assert((uread(BSP_UART_COM1, LSR) & THRE) != 0); */
if(termios_stopped_com[uart])
{
/* CTS low */
termios_tx_hold_com[uart] = *buf;
termios_tx_hold_valid_com[uart] = 1;
return 0;
}
/* Write character */
uwrite(uart, THR, *buf & 0xff);
/* Enable interrupts if necessary */
if(!termios_tx_active_com[uart] && uart_data[uart].hwFlow)
{
termios_tx_active_com[uart] = 1;
uwrite(uart, IER,
(RECEIVE_ENABLE |
TRANSMIT_ENABLE |
RECEIVER_LINE_ST_ENABLE |
MODEM_ENABLE
)
);
}
else if(!termios_tx_active_com[uart])
{
termios_tx_active_com[uart] = 1;
uwrite(uart, IER,
(RECEIVE_ENABLE |
TRANSMIT_ENABLE |
RECEIVER_LINE_ST_ENABLE
)
);
}
return 0;
}
int
BSP_uart_termios_read_com(int uart)
{
int off = (int)0;
char buf[40];
rtems_interrupt_level l;
/* read bytes */
while (( off < sizeof(buf) ) && ( uread(uart, LSR) & DR )) {
buf[off++] = uread(uart, RBR);
}
/* write out data */
if ( off > 0 ) {
rtems_termios_enqueue_raw_characters(termios_ttyp_com[uart], buf, off);
}
/* enable receive interrupts */
rtems_interrupt_disable(l);
uwrite(uart, IER, uread(uart, IER) | (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE));
rtems_interrupt_enable(l);
return ( EOF );
}
static void
BSP_uart_termios_isr_com(int uart)
{
unsigned char buf[40];
unsigned char val, ier;
int off, ret, vect;
off = 0;
for(;;)
{
vect = uread(uart, IIR) & 0xf;
switch(vect)
{
case MODEM_STATUS :
val = uread(uart, MSR);
if(uart_data[uart].hwFlow)
{
if(val & CTS)
{
/* CTS high */
termios_stopped_com[uart] = 0;
if(termios_tx_hold_valid_com[uart])
{
termios_tx_hold_valid_com[uart] = 0;
BSP_uart_termios_write_com(uart, &termios_tx_hold_com[uart],
1);
}
}
else
{
/* CTS low */
termios_stopped_com[uart] = 1;
}
}
break;
case NO_MORE_INTR :
/* No more interrupts */
if(off != 0)
{
/* Update rx buffer */
rtems_termios_enqueue_raw_characters(termios_ttyp_com[uart],
(char *)buf,
off);
}
return;
case TRANSMITTER_HODING_REGISTER_EMPTY :
/*
* TX holding empty: we have to disable these interrupts
* if there is nothing more to send.
*/
ret = rtems_termios_dequeue_characters(termios_ttyp_com[uart], 1);
/* If nothing else to send disable interrupts */
if(ret == 0 && uart_data[uart].hwFlow)
{
uwrite(uart, IER,
(RECEIVE_ENABLE |
RECEIVER_LINE_ST_ENABLE |
MODEM_ENABLE
)
);
termios_tx_active_com[uart] = 0;
}
else if(ret == 0)
{
uwrite(uart, IER,
(RECEIVE_ENABLE |
RECEIVER_LINE_ST_ENABLE
)
);
termios_tx_active_com[uart] = 0;
}
break;
case RECEIVER_DATA_AVAIL :
case CHARACTER_TIMEOUT_INDICATION:
if ( uart_data[uart].ioMode == TERMIOS_TASK_DRIVEN )
{
/* ensure interrupts are enabled */
if ( (ier = uread(uart,IER)) & RECEIVE_ENABLE )
{
/* disable interrupts and notify termios */
ier &= ~(RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
uwrite(uart, IER, ier);
rtems_termios_rxirq_occured(termios_ttyp_com[uart]);
}
}
else
{
/* RX data ready */
assert(off < sizeof(buf));
while ( off < sizeof(buf) && ( DR & uread(uart, LSR) ) )
buf[off++] = uread(uart, RBR);
}
break;
case RECEIVER_ERROR:
/* RX error: eat character */
uartError(uart, termios_ttyp_com[uart]);
break;
default:
/* Should not happen */
assert(0);
return;
}
}
}
/*
* XXX - Note that this can now be one isr with the uart
* passed as the parameter.
*/
void
BSP_uart_termios_isr_com1(void *unused)
{
BSP_uart_termios_isr_com(BSP_UART_COM1);
}
void
BSP_uart_termios_isr_com2(void *unused)
{
BSP_uart_termios_isr_com(BSP_UART_COM2);
}
/* retrieve 'break' handler info */
int
BSP_uart_get_break_cb(int uart, rtems_libio_ioctl_args_t *arg)
{
BSP_UartBreakCb cb=arg->buffer;
unsigned long flags;
SANITY_CHECK(uart);
rtems_interrupt_disable(flags);
*cb = uart_data[uart].breakCallback;
rtems_interrupt_enable(flags);
arg->ioctl_return=0;
return RTEMS_SUCCESSFUL;
}
/* install 'break' handler */
int
BSP_uart_set_break_cb(int uart, rtems_libio_ioctl_args_t *arg)
{
BSP_UartBreakCb cb=arg->buffer;
unsigned long flags;
SANITY_CHECK(uart);
rtems_interrupt_disable(flags);
uart_data[uart].breakCallback = *cb;
rtems_interrupt_enable(flags);
arg->ioctl_return=0;
return RTEMS_SUCCESSFUL;
}