From 6ec438e8a323cf623cdaecce6f2b3b52b062881a Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 7 Oct 2014 08:29:16 +0200 Subject: libchip/serial: Add alternative NS16550 driver Use the Termios device API. --- c/src/libchip/Makefile.am | 1 + c/src/libchip/serial/ns16550-context.c | 677 +++++++++++++++++++++++++++++++++ c/src/libchip/serial/ns16550.c | 100 ++++- c/src/libchip/serial/ns16550.h | 32 ++ c/src/libchip/serial/ns16550_p.h | 76 ---- 5 files changed, 798 insertions(+), 88 deletions(-) create mode 100644 c/src/libchip/serial/ns16550-context.c (limited to 'c/src/libchip') diff --git a/c/src/libchip/Makefile.am b/c/src/libchip/Makefile.am index 003c3c1268..904561ffde 100644 --- a/c/src/libchip/Makefile.am +++ b/c/src/libchip/Makefile.am @@ -110,6 +110,7 @@ libserialio_a_SOURCES = serial/mc68681.c serial/mc68681_baud.c \ serial/mc68681_reg8.c serial/ns16550.c serial/z85c30.c \ serial/z85c30_reg.c serial/serprobe.c serial/mc68681_p.h \ serial/z85c30_p.h +libserialio_a_SOURCES += serial/ns16550-context.c EXTRA_DIST += serial/README.mc68681 serial/README.ns16550 \ serial/README.xr88681 serial/README.z85c30 serial/STATUS diff --git a/c/src/libchip/serial/ns16550-context.c b/c/src/libchip/serial/ns16550-context.c new file mode 100644 index 0000000000..a812271037 --- /dev/null +++ b/c/src/libchip/serial/ns16550-context.c @@ -0,0 +1,677 @@ +/** + * @file + * + * This file contains the TTY driver for the National Semiconductor NS16550. + * + * This part is widely cloned and second sourced. It is found in a number + * of "Super IO" controllers. + * + * This driver uses the termios pseudo driver. + */ + +/* + * COPYRIGHT (c) 1998 by Radstone Technology + * + * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK + * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU. + * + * You are hereby granted permission to use, copy, modify, and distribute + * this file, provided that this notice, plus the above copyright notice + * and disclaimer, appears in all copies. Radstone Technology will provide + * no support for this code. + * + * COPYRIGHT (c) 1989-2012. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +#include + +#include + +#include "ns16550.h" +#include "ns16550_p.h" + +#if defined(BSP_FEATURE_IRQ_EXTENSION) + #include +#elif defined(BSP_FEATURE_IRQ_LEGACY) + #include +#elif defined(__PPC__) || defined(__i386__) + #include + #define BSP_FEATURE_IRQ_LEGACY + #ifdef BSP_SHARED_HANDLER_SUPPORT + #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT + #endif +#endif + +static uint32_t NS16550_GetBaudDivisor(ns16550_context *ctx, uint32_t baud) +{ + uint32_t clock = ctx->clock; + uint32_t baudDivisor = (clock != 0 ? clock : 115200) / (baud * 16); + + if (ctx->has_fractional_divider_register) { + uint32_t fractionalDivider = 0x10; + uint32_t err = baud; + uint32_t mulVal; + uint32_t divAddVal; + + clock /= 16 * baudDivisor; + for (mulVal = 1; mulVal < 16; ++mulVal) { + for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) { + uint32_t actual = (mulVal * clock) / (mulVal + divAddVal); + uint32_t newErr = actual > baud ? actual - baud : baud - actual; + + if (newErr < err) { + err = newErr; + fractionalDivider = (mulVal << 4) | divAddVal; + } + } + } + + (*ctx->set_reg)( + ctx->port, + NS16550_FRACTIONAL_DIVIDER, + fractionalDivider + ); + } + + return baudDivisor; +} + +/* + * ns16550_enable_interrupts + * + * This routine initializes the port to have the specified interrupts masked. + */ +static void ns16550_enable_interrupts( + ns16550_context *ctx, + int mask +) +{ + (*ctx->set_reg)(ctx->port, NS16550_INTERRUPT_ENABLE, mask); +} + +/* + * ns16550_probe + */ + +bool ns16550_probe(rtems_termios_device_context *base) +{ + ns16550_context *ctx = (ns16550_context *) base; + uintptr_t pNS16550; + uint8_t ucDataByte; + uint32_t ulBaudDivisor; + ns16550_set_reg setReg; + ns16550_get_reg getReg; + + ctx->modem_control = SP_MODEM_IRQ; + + pNS16550 = ctx->port; + setReg = ctx->set_reg; + getReg = ctx->get_reg; + + /* Clear the divisor latch, clear all interrupt enables, + * and reset and + * disable the FIFO's. + */ + + (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0); + ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR ); + + /* Set the divisor latch and set the baud rate. */ + + ulBaudDivisor = NS16550_GetBaudDivisor(ctx, ctx->initial_baud); + ucDataByte = SP_LINE_DLAB; + (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte); + + /* XXX */ + (*setReg)(pNS16550,NS16550_TRANSMIT_BUFFER,(uint8_t)(ulBaudDivisor & 0xffU)); + (*setReg)( + pNS16550,NS16550_INTERRUPT_ENABLE, + (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU ) + ); + + /* Clear the divisor latch and set the character size to eight bits */ + /* with one stop bit and no parity checking. */ + ucDataByte = EIGHT_BITS; + (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte); + + /* Enable and reset transmit and receive FIFOs. TJA */ + ucDataByte = SP_FIFO_ENABLE; + (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte); + + ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST; + (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte); + + ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR); + + /* Set data terminal ready. */ + /* And open interrupt tristate line */ + (*setReg)(pNS16550, NS16550_MODEM_CONTROL,ctx->modem_control); + + (*getReg)(pNS16550, NS16550_LINE_STATUS ); + (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER ); + + return true; +} + +#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY) + +/** + * @brief Process interrupt. + */ +static void ns16550_isr(void *arg) +{ + rtems_termios_tty *tty = arg; + ns16550_context *ctx = rtems_termios_get_device_context(tty); + uint32_t port = ctx->port; + ns16550_get_reg get = ctx->get_reg; + int i = 0; + char buf [SP_FIFO_SIZE]; + + /* Iterate until no more interrupts are pending */ + do { + /* Fetch received characters */ + for (i = 0; i < SP_FIFO_SIZE; ++i) { + if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) { + buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER); + } else { + break; + } + } + + /* Enqueue fetched characters */ + rtems_termios_enqueue_raw_characters(tty, buf, i); + + /* Check if we can dequeue transmitted characters */ + if (ctx->transmit_fifo_chars > 0 + && (get( port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) { + + /* Dequeue transmitted characters */ + rtems_termios_dequeue_characters( + tty, + ctx->transmit_fifo_chars + ); + } + } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0); +} +#endif + +/* + * ns16550_initialize_interrupts + * + * This routine initializes the port to operate in interrupt driver mode. + */ +static void ns16550_initialize_interrupts( + struct rtems_termios_tty *tty, + ns16550_context *ctx +) +{ + #ifdef BSP_FEATURE_IRQ_EXTENSION + { + rtems_status_code sc = RTEMS_SUCCESSFUL; + sc = rtems_interrupt_handler_install( + ctx->irq, + "NS16550", + RTEMS_INTERRUPT_SHARED, + ns16550_isr, + tty + ); + if (sc != RTEMS_SUCCESSFUL) { + /* FIXME */ + printk( "%s: Error: Install interrupt handler\n", __func__); + rtems_fatal_error_occurred( 0xdeadbeef); + } + } + #elif defined(BSP_FEATURE_IRQ_LEGACY) + { + int rv = 0; + #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT + rtems_irq_connect_data cd = { + ctx->irq, + ns16550_isr, + tty, + NULL, + NULL, + NULL, + NULL + }; + rv = BSP_install_rtems_shared_irq_handler( &cd); + #else + rtems_irq_connect_data cd = { + ctx->irq, + ns16550_isr, + tty, + NULL, + NULL, + NULL + }; + rv = BSP_install_rtems_irq_handler( &cd); + #endif + if (rv == 0) { + /* FIXME */ + printk( "%s: Error: Install interrupt handler\n", __func__); + rtems_fatal_error_occurred( 0xdeadbeef); + } + } + #endif +} + +/* + * ns16550_open + */ + +static bool ns16550_open( + struct rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + ns16550_context *ctx = (ns16550_context *) base; + + /* Set initial baud */ + rtems_termios_set_initial_baud(tty, ctx->initial_baud); + + if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) { + ns16550_initialize_interrupts(tty, ctx); + ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX); + } + + return true; +} + +static void ns16550_cleanup_interrupts( + struct rtems_termios_tty *tty, + ns16550_context *ctx +) +{ + #if defined(BSP_FEATURE_IRQ_EXTENSION) + rtems_status_code sc = RTEMS_SUCCESSFUL; + sc = rtems_interrupt_handler_remove( + ctx->irq, + ns16550_isr, + tty + ); + if (sc != RTEMS_SUCCESSFUL) { + /* FIXME */ + printk("%s: Error: Remove interrupt handler\n", __func__); + rtems_fatal_error_occurred(0xdeadbeef); + } + #elif defined(BSP_FEATURE_IRQ_LEGACY) + int rv = 0; + rtems_irq_connect_data cd = { + .name = ctx->irq, + .hdl = ns16550_isr, + .handle = tty + }; + rv = BSP_remove_rtems_irq_handler(&cd); + if (rv == 0) { + /* FIXME */ + printk("%s: Error: Remove interrupt handler\n", __func__); + rtems_fatal_error_occurred(0xdeadbeef); + } + #endif +} + +/* + * ns16550_close + */ + +static void ns16550_close( + struct rtems_termios_tty *tty, + rtems_termios_device_context *base, + rtems_libio_open_close_args_t *args +) +{ + ns16550_context *ctx = (ns16550_context *) base; + + ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR); + + if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) { + ns16550_cleanup_interrupts(tty, ctx); + } +} + +/** + * @brief Polled write for NS16550. + */ +void ns16550_polled_putchar(rtems_termios_device_context *base, char out) +{ + ns16550_context *ctx = (ns16550_context *) base; + uintptr_t port = ctx->port; + ns16550_get_reg get = ctx->get_reg; + ns16550_set_reg set = ctx->set_reg; + uint32_t status = 0; + rtems_interrupt_lock_context lock_context; + + /* Save port interrupt mask */ + uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE); + + /* Disable port interrupts */ + ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR); + + while (true) { + /* Try to transmit the character in a critical section */ + rtems_termios_device_lock_acquire(&ctx->base, &lock_context); + + /* Read the transmitter holding register and check it */ + status = get( port, NS16550_LINE_STATUS); + if ((status & SP_LSR_THOLD) != 0) { + /* Transmit character */ + set( port, NS16550_TRANSMIT_BUFFER, out); + + /* Finished */ + rtems_termios_device_lock_release(&ctx->base, &lock_context); + break; + } else { + rtems_termios_device_lock_release(&ctx->base, &lock_context); + } + + /* Wait for transmitter holding register to be empty */ + do { + status = get( port, NS16550_LINE_STATUS); + } while ((status & SP_LSR_THOLD) == 0); + } + + /* Restore port interrupt mask */ + set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask); +} + +/* + * These routines provide control of the RTS and DTR lines + */ + +/* + * ns16550_assert_RTS + */ + +static void ns16550_assert_RTS(rtems_termios_device_context *base) +{ + ns16550_context *ctx = (ns16550_context *) base; + rtems_interrupt_lock_context lock_context; + + /* + * Assert RTS + */ + rtems_termios_device_lock_acquire(base, &lock_context); + ctx->modem_control |= SP_MODEM_RTS; + (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control); + rtems_termios_device_lock_release(base, &lock_context); +} + +/* + * ns16550_negate_RTS + */ + +static void ns16550_negate_RTS(rtems_termios_device_context *base) +{ + ns16550_context *ctx = (ns16550_context *) base; + rtems_interrupt_lock_context lock_context; + + /* + * Negate RTS + */ + rtems_termios_device_lock_acquire(base, &lock_context); + ctx->modem_control &= ~SP_MODEM_RTS; + (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control); + rtems_termios_device_lock_release(base, &lock_context); +} + +/* + * These flow control routines utilise a connection from the local DTR + * line to the remote CTS line + */ + +/* + * ns16550_assert_DTR + */ + +static void ns16550_assert_DTR(rtems_termios_device_context *base) +{ + ns16550_context *ctx = (ns16550_context *) base; + rtems_interrupt_lock_context lock_context; + + /* + * Assert DTR + */ + rtems_termios_device_lock_acquire(base, &lock_context); + ctx->modem_control |= SP_MODEM_DTR; + (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control); + rtems_termios_device_lock_release(base, &lock_context); +} + +/* + * ns16550_negate_DTR + */ + +static void ns16550_negate_DTR(rtems_termios_device_context *base) +{ + ns16550_context *ctx = (ns16550_context *) base; + rtems_interrupt_lock_context lock_context; + + /* + * Negate DTR + */ + rtems_termios_device_lock_acquire(base, &lock_context); + ctx->modem_control &=~SP_MODEM_DTR; + (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL,ctx->modem_control); + rtems_termios_device_lock_release(base, &lock_context); +} + +/* + * ns16550_set_attributes + * + * This function sets the channel to reflect the requested termios + * port settings. + */ + +static bool ns16550_set_attributes( + rtems_termios_device_context *base, + const struct termios *t +) +{ + ns16550_context *ctx = (ns16550_context *) base; + uint32_t pNS16550; + uint32_t ulBaudDivisor; + uint8_t ucLineControl; + uint32_t baud_requested; + ns16550_set_reg setReg; + rtems_interrupt_lock_context lock_context; + + pNS16550 = ctx->port; + setReg = ctx->set_reg; + + /* + * Calculate the baud rate divisor + */ + + baud_requested = rtems_termios_baud_to_number(t->c_cflag); + ulBaudDivisor = NS16550_GetBaudDivisor(ctx, baud_requested); + + ucLineControl = 0; + + /* + * Parity + */ + + if (t->c_cflag & PARENB) { + ucLineControl |= SP_LINE_PAR; + if (!(t->c_cflag & PARODD)) + ucLineControl |= SP_LINE_ODD; + } + + /* + * Character Size + */ + + if (t->c_cflag & CSIZE) { + switch (t->c_cflag & CSIZE) { + case CS5: ucLineControl |= FIVE_BITS; break; + case CS6: ucLineControl |= SIX_BITS; break; + case CS7: ucLineControl |= SEVEN_BITS; break; + case CS8: ucLineControl |= EIGHT_BITS; break; + } + } else { + ucLineControl |= EIGHT_BITS; /* default to 9600,8,N,1 */ + } + + /* + * Stop Bits + */ + + if (t->c_cflag & CSTOPB) { + ucLineControl |= SP_LINE_STOP; /* 2 stop bits */ + } else { + ; /* 1 stop bit */ + } + + /* + * Now actually set the chip + */ + + rtems_termios_device_lock_acquire(base, &lock_context); + + /* + * Set the baud rate + * + * NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1, + * the transmit buffer and interrupt enable registers + * turn into the LSB and MSB divisor latch registers. + */ + + (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB); + (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff); + (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff); + + /* + * Now write the line control + */ + (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl ); + + rtems_termios_device_lock_release(base, &lock_context); + + return true; +} + +/** + * @brief Transmits up to @a len characters from @a buf. + * + * This routine is invoked either from task context with disabled interrupts to + * start a new transmission process with exactly one character in case of an + * idle output state or from the interrupt handler to refill the transmitter. + * + * Returns always zero. + */ +static void ns16550_write_support_int( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ + ns16550_context *ctx = (ns16550_context *) base; + uint32_t port = ctx->port; + ns16550_set_reg set = ctx->set_reg; + int i = 0; + int out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len; + + for (i = 0; i < out; ++i) { + set( port, NS16550_TRANSMIT_BUFFER, buf [i]); + } + + ctx->transmit_fifo_chars = out; + + if (out > 0) { + ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR); + } else { + ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX); + } +} + +/* + * ns16550_write_support_polled + * + * Console Termios output entry point. + * + */ + +static void ns16550_write_support_polled( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ + size_t nwrite = 0; + + /* + * poll each byte in the string out of the port. + */ + while (nwrite < len) { + /* + * transmit character + */ + ns16550_polled_putchar(base, *buf++); + nwrite++; + } +} + +/* + * Debug gets() support + */ +int ns16550_polled_getchar(rtems_termios_device_context *base) +{ + ns16550_context *ctx = (ns16550_context *) base; + uint32_t pNS16550; + unsigned char ucLineStatus; + uint8_t cChar; + ns16550_get_reg getReg; + + pNS16550 = ctx->port; + getReg = ctx->get_reg; + + ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS); + if (ucLineStatus & SP_LSR_RDY) { + cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER); + return (int)cChar; + } + return -1; +} + +/* + * Flow control is only supported when using interrupts + */ + +const rtems_termios_device_flow ns16550_flow_rtscts = { + .stop_remote_tx = ns16550_negate_RTS, + .start_remote_tx = ns16550_assert_RTS +}; + +const rtems_termios_device_flow ns16550_flow_dtrcts = { + .stop_remote_tx = ns16550_negate_DTR, + .start_remote_tx = ns16550_assert_DTR +}; + +const rtems_termios_device_handler ns16550_handler_interrupt = { + .first_open = ns16550_open, + .last_close = ns16550_close, + .poll_read = NULL, + .write = ns16550_write_support_int, + .set_attributes = ns16550_set_attributes, + .mode = TERMIOS_IRQ_DRIVEN +}; + +const rtems_termios_device_handler ns16550_handler_polled = { + .first_open = ns16550_open, + .last_close = ns16550_close, + .poll_read = ns16550_polled_getchar, + .write = ns16550_write_support_polled, + .set_attributes = ns16550_set_attributes, + .mode = TERMIOS_POLLED +}; diff --git a/c/src/libchip/serial/ns16550.c b/c/src/libchip/serial/ns16550.c index 390c729a54..b0aa38ff92 100644 --- a/c/src/libchip/serial/ns16550.c +++ b/c/src/libchip/serial/ns16550.c @@ -58,6 +58,82 @@ #endif #endif +typedef struct { + uint8_t ucModemCtrl; + int transmitFifoChars; +} NS16550Context; + +/* + * Driver functions + */ + +NS16550_STATIC void ns16550_init(int minor); + +NS16550_STATIC int ns16550_open( + int major, + int minor, + void * arg +); + +NS16550_STATIC int ns16550_close( + int major, + int minor, + void * arg +); + +NS16550_STATIC void ns16550_write_polled( + int minor, + char cChar +); + +NS16550_STATIC int ns16550_assert_RTS( + int minor +); + +NS16550_STATIC int ns16550_negate_RTS( + int minor +); + +NS16550_STATIC int ns16550_assert_DTR( + int minor +); + +NS16550_STATIC int ns16550_negate_DTR( + int minor +); + +NS16550_STATIC void ns16550_initialize_interrupts(int minor); + +NS16550_STATIC void ns16550_cleanup_interrupts(int minor); + +NS16550_STATIC ssize_t ns16550_write_support_int( + int minor, + const char *buf, + size_t len +); + +NS16550_STATIC ssize_t ns16550_write_support_polled( + int minor, + const char *buf, + size_t len + ); + +int ns16550_inbyte_nonblocking_polled( + int minor +); + +NS16550_STATIC void ns16550_enable_interrupts( + console_tbl *c, + int mask +); + +NS16550_STATIC int ns16550_set_attributes( + int minor, + const struct termios *t +); + +NS16550_STATIC void ns16550_isr(void *arg); + static rtems_interrupt_lock ns16550_lock = RTEMS_INTERRUPT_LOCK_INITIALIZER("NS16550"); @@ -142,12 +218,12 @@ void ns16550_init(int minor) uintptr_t pNS16550; uint8_t ucDataByte; uint32_t ulBaudDivisor; - ns16550_context *pns16550Context; + NS16550Context *pns16550Context; setRegister_f setReg; getRegister_f getReg; console_tbl *c = Console_Port_Tbl [minor]; - pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context)); + pns16550Context=(NS16550Context *)malloc(sizeof(NS16550Context)); if (pns16550Context == NULL) { printk( "%s: Error: Not enough memory\n", __func__); @@ -328,10 +404,10 @@ NS16550_STATIC int ns16550_assert_RTS(int minor) { uint32_t pNS16550; rtems_interrupt_lock_context lock_context; - ns16550_context *pns16550Context; + NS16550Context *pns16550Context; setRegister_f setReg; - pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext; + pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext; pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; setReg = Console_Port_Tbl[minor]->setRegister; @@ -354,10 +430,10 @@ NS16550_STATIC int ns16550_negate_RTS(int minor) { uint32_t pNS16550; rtems_interrupt_lock_context lock_context; - ns16550_context *pns16550Context; + NS16550Context *pns16550Context; setRegister_f setReg; - pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext; + pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext; pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; setReg = Console_Port_Tbl[minor]->setRegister; @@ -385,10 +461,10 @@ NS16550_STATIC int ns16550_assert_DTR(int minor) { uint32_t pNS16550; rtems_interrupt_lock_context lock_context; - ns16550_context *pns16550Context; + NS16550Context *pns16550Context; setRegister_f setReg; - pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext; + pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext; pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; setReg = Console_Port_Tbl[minor]->setRegister; @@ -411,10 +487,10 @@ NS16550_STATIC int ns16550_negate_DTR(int minor) { uint32_t pNS16550; rtems_interrupt_lock_context lock_context; - ns16550_context *pns16550Context; + NS16550Context *pns16550Context; setRegister_f setReg; - pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext; + pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext; pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1; setReg = Console_Port_Tbl[minor]->setRegister; @@ -533,7 +609,7 @@ NS16550_STATIC void ns16550_process( int minor) { console_tbl *c = Console_Port_Tbl [minor]; console_data *d = &Console_Port_Data [minor]; - ns16550_context *ctx = d->pDeviceContext; + NS16550Context *ctx = d->pDeviceContext; uint32_t port = c->ulCtrlPort1; getRegister_f get = c->getRegister; int i = 0; @@ -584,7 +660,7 @@ ssize_t ns16550_write_support_int( { console_tbl *c = Console_Port_Tbl [minor]; console_data *d = &Console_Port_Data [minor]; - ns16550_context *ctx = d->pDeviceContext; + NS16550Context *ctx = d->pDeviceContext; uint32_t port = c->ulCtrlPort1; setRegister_f set = c->setRegister; int i = 0; diff --git a/c/src/libchip/serial/ns16550.h b/c/src/libchip/serial/ns16550.h index 28905665bb..ef1f9f9d95 100644 --- a/c/src/libchip/serial/ns16550.h +++ b/c/src/libchip/serial/ns16550.h @@ -27,6 +27,7 @@ #ifndef _NS16550_H_ #define _NS16550_H_ +#include #include #ifdef __cplusplus @@ -53,6 +54,37 @@ extern const console_flow ns16550_flow_DTRCTS; void ns16550_outch_polled(console_tbl *c, char out); int ns16550_inch_polled(console_tbl *c); +/* Alternative NS16550 driver using the Termios device context */ + +typedef uint8_t (*ns16550_get_reg)(uintptr_t port, uint8_t reg); + +typedef void (*ns16550_set_reg)(uintptr_t port, uint8_t reg, uint8_t value); + +typedef struct { + rtems_termios_device_context base; + ns16550_get_reg get_reg; + ns16550_set_reg set_reg; + uintptr_t port; + rtems_vector_number irq; + uint32_t clock; + uint32_t initial_baud; + bool has_fractional_divider_register; + uint8_t modem_control; + size_t transmit_fifo_chars; +} ns16550_context; + +extern const rtems_termios_device_handler ns16550_handler_interrupt; +extern const rtems_termios_device_handler ns16550_handler_polled; + +extern const rtems_termios_device_flow ns16550_flow_rtscts; +extern const rtems_termios_device_flow ns16550_flow_dtrcts; + +void ns16550_polled_putchar(rtems_termios_device_context *base, char out); + +int ns16550_polled_getchar(rtems_termios_device_context *base); + +bool ns16550_probe(rtems_termios_device_context *base); + #ifdef __cplusplus } #endif diff --git a/c/src/libchip/serial/ns16550_p.h b/c/src/libchip/serial/ns16550_p.h index 4e6d784c5f..0fe817ad6b 100644 --- a/c/src/libchip/serial/ns16550_p.h +++ b/c/src/libchip/serial/ns16550_p.h @@ -131,82 +131,6 @@ extern "C" { #define SP_LSR_TX 0x40 #define SP_LSR_EFIFO 0x80 -typedef struct { - uint8_t ucModemCtrl; - int transmitFifoChars; -} ns16550_context; - -/* - * Driver functions - */ - -void ns16550_init(int minor); - -int ns16550_open( - int major, - int minor, - void * arg -); - -int ns16550_close( - int major, - int minor, - void * arg -); - -void ns16550_write_polled( - int minor, - char cChar -); - -NS16550_STATIC int ns16550_assert_RTS( - int minor -); - -NS16550_STATIC int ns16550_negate_RTS( - int minor -); - -NS16550_STATIC int ns16550_assert_DTR( - int minor -); - -NS16550_STATIC int ns16550_negate_DTR( - int minor -); - -NS16550_STATIC void ns16550_initialize_interrupts(int minor); - -NS16550_STATIC void ns16550_cleanup_interrupts(int minor); - -ssize_t ns16550_write_support_int( - int minor, - const char *buf, - size_t len -); - -ssize_t ns16550_write_support_polled( - int minor, - const char *buf, - size_t len - ); - -int ns16550_inbyte_nonblocking_polled( - int minor -); - -NS16550_STATIC void ns16550_enable_interrupts( - console_tbl *c, - int mask -); - -int ns16550_set_attributes( - int minor, - const struct termios *t -); - -void ns16550_isr(void *arg); - #ifdef __cplusplus } #endif -- cgit v1.2.3