diff options
author | Jay Monkman <jtm@smoothsmoothie.com> | 2006-06-03 03:18:46 +0000 |
---|---|---|
committer | Jay Monkman <jtm@smoothsmoothie.com> | 2006-06-03 03:18:46 +0000 |
commit | 9cdfb0e58665911def148a68e17a0ee400aa5414 (patch) | |
tree | bbc0b7e8633837d6aae3d2e6e1a83544774d92e2 /c | |
parent | 2006-06-02 Jay Monkman <jtm@lopingdog.com> (diff) | |
download | rtems-9cdfb0e58665911def148a68e17a0ee400aa5414.tar.bz2 |
2006-06-02 Jay Monkman <jtm@lopingdog.com>
* Makefile.am, console/uart.c, startup/exit.c: Changed UART
driver to be interrupt driven, added support for both UARTs.
Diffstat (limited to 'c')
-rw-r--r-- | c/src/lib/libbsp/arm/csb336/Makefile.am | 2 | ||||
-rw-r--r-- | c/src/lib/libbsp/arm/csb336/console/uart.c | 582 | ||||
-rw-r--r-- | c/src/lib/libbsp/arm/csb336/startup/exit.c | 2 |
3 files changed, 389 insertions, 197 deletions
diff --git a/c/src/lib/libbsp/arm/csb336/Makefile.am b/c/src/lib/libbsp/arm/csb336/Makefile.am index a0363e5cca..6a44fcd771 100644 --- a/c/src/lib/libbsp/arm/csb336/Makefile.am +++ b/c/src/lib/libbsp/arm/csb336/Makefile.am @@ -34,7 +34,7 @@ startup_rel_CPPFLAGS = $(AM_CPPFLAGS) startup_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) noinst_PROGRAMS += console.rel -console_rel_SOURCES = console/uart.c ../../shared/console.c +console_rel_SOURCES = console/uart.c console_rel_CPPFLAGS = $(AM_CPPFLAGS) console_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) diff --git a/c/src/lib/libbsp/arm/csb336/console/uart.c b/c/src/lib/libbsp/arm/csb336/console/uart.c index c3df1382be..05afc3233d 100644 --- a/c/src/lib/libbsp/arm/csb336/console/uart.c +++ b/c/src/lib/libbsp/arm/csb336/console/uart.c @@ -1,262 +1,454 @@ /* - * console driver for MC9328XML UARTs + * Console driver for MC9328XML UARTs. * - * This driver uses the shared console driver in - * ...../libbsp/shared/console.c + * Written Jay Monkman <jtm@lopingdog.com> + * Copyright (c) 2005 by Loping Dog Embedded Systems * - * If you want the driver to be interrupt driven, you - * need to write the ISR, and in the ISR insert the - * chars into termios's queue. + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license * - * Copyright (c) 2004 Cogent Computer Systems - * Written by Jay Monkman <jtm@lopingdog.com> - * - * The license and distribution terms for this file may be - * found in the file LICENSE in this distribution or at - * - * http://www.OARcorp.com/rtems/license.html. - * - * - * $Id$ -*/ -#include <bsp.h> /* Must be before libio.h */ + * $Id$ + */ +#include <bsp.h> #include <rtems/libio.h> -#include <termios.h> +#include <libchip/sersupp.h> +#include <rtems/error.h> #include <rtems/bspIo.h> - -/* Put the CPU (or UART) specific header file #include here */ +#include <termios.h> +#include <rtems/irq.h> +#include <irq.h> #include <mc9328mxl.h> -#include <libchip/serial.h> -#include <libchip/sersupp.h> + + +/* Define this to use interrupt driver UART driver */ +#define USE_INTERRUPTS 1 /* How many serial ports? */ #define NUM_DEVS 2 +#define poll_write(c) imx_uart_poll_write_char(0, c) +#define poll_read() imx_uart_poll_read_char(0) -int uart_poll_read(int minor); - -int dbg_dly; - -/* static function prototypes */ -static int uart_first_open(int major, int minor, void *arg); -static int uart_last_close(int major, int minor, void *arg); -static int uart_read(int minor); -static int uart_write(int minor, const char *buf, int len); -static void uart_init(int minor); -static void uart_write_polled(int minor, char c); -static int uart_set_attributes(int minor, const struct termios *t); - -/* These are used by code in console.c */ -unsigned long Console_Port_Count = NUM_DEVS; -console_data Console_Port_Data[NUM_DEVS]; - -/* rtems console uses the following minor number */ -rtems_device_minor_number Console_Port_Minor = 0; - -/* Pointers to functions for handling the UART. */ -console_fns uart_fns = -{ - libchip_serial_default_probe, - uart_first_open, - uart_last_close, - uart_read, - uart_write, - uart_init, - uart_write_polled, /* not used in this driver */ - uart_set_attributes, - FALSE /* TRUE if interrupt driven, FALSE if not. */ -}; +static int imx_uart_first_open(int, int, void *); +static int imx_uart_last_close(int, int, void *); +static int imx_uart_poll_read(int); +static int imx_uart_set_attrs(int, const struct termios *); +static void imx_uart_init(int minor); +static void imx_uart_set_baud(int, int); +static int imx_uart_poll_write(int, const char *, int); -/* - * There's one item in array for each UART. - * - * Some of these fields are marked "NOT USED". They are not used - * by console.c, but may be used by drivers in libchip - * - */ -console_tbl Console_Port_Tbl[] = { - { - "/dev/com0", /* sDeviceName */ - SERIAL_CUSTOM, /* deviceType */ - &uart_fns, /* pDeviceFns */ - NULL, /* deviceProbe */ - NULL, /* pDeviceFlow */ - 0, /* ulMargin - NOT USED */ - 0, /* ulHysteresis - NOT USED */ - NULL, /* pDeviceParams */ - 0, /* ulCtrlPort1 - NOT USED */ - 0, /* ulCtrlPort2 - NOT USED */ - 0, /* ulDataPort - NOT USED */ - NULL, /* getRegister - NOT USED */ - NULL, /* setRegister - NOT USED */ - NULL, /* getData - NOT USED */ - NULL, /* setData - NOT USED */ - 0, /* ulClock - NOT USED */ - 0 /* ulIntVector - NOT USED */ - }, - { - "/dev/com1", /* sDeviceName */ - SERIAL_CUSTOM, /* deviceType */ - &uart_fns, /* pDeviceFns */ - NULL, /* deviceProbe */ - NULL, /* pDeviceFlow */ - 0, /* ulMargin - NOT USED */ - 0, /* ulHysteresis - NOT USED */ - NULL, /* pDeviceParams */ - 0, /* ulCtrlPort1 - NOT USED */ - 0, /* ulCtrlPort2 - NOT USED */ - 0, /* ulDataPort - NOT USED */ - NULL, /* getRegister - NOT USED */ - NULL, /* setRegister - NOT USED */ - NULL, /* getData - NOT USED */ - NULL, /* setData - NOT USED */ - 0, /* ulClock - NOT USED */ - 0 /* ulIntVector - NOT USED */ - } +#if defined(USE_INTERRUPTS) +static void imx_uart_tx_isr(rtems_irq_hdl_param); +static void imx_uart_rx_isr(rtems_irq_hdl_param); +static void imx_uart_isr_on(const rtems_irq_connect_data *irq); +static void imx_uart_isr_off(const rtems_irq_connect_data *irq); +static int imx_uart_isr_is_on(const rtems_irq_connect_data *irq); +static int imx_uart_intr_write(int, const char *, int); +#endif + + + +/* TERMIOS callbacks */ +#if defined(USE_INTERRUPTS) +rtems_termios_callbacks imx_uart_cbacks = { + .firstOpen = imx_uart_first_open, + .lastClose = imx_uart_last_close, + .pollRead = NULL, + .write = imx_uart_intr_write, + .setAttributes = imx_uart_set_attrs, + .stopRemoteTx = NULL, + .startRemoteTx = NULL, + .outputUsesInterrupts = 1, }; +#else +rtems_termios_callbacks imx_uart_cbacks = { + .firstOpen = imx_uart_first_open, + .lastClose = imx_uart_last_close, + .pollRead = imx_uart_poll_read, + .write = imx_uart_poll_write, + .setAttributes = imx_uart_set_attrs, + .stopRemoteTx = NULL, + .startRemoteTx = NULL, + .outputUsesInterrupts = 0, +}; +#endif -/*********************************************************************/ -/* Functions called via termios callbacks (i.e. the ones in uart_fns */ -/*********************************************************************/ +#if defined(USE_INTERRUPTS) +static rtems_irq_connect_data imx_uart_tx_isr_data[NUM_DEVS]; +static rtems_irq_connect_data imx_uart_rx_isr_data[NUM_DEVS]; +#endif -/* - * This is called the first time each device is opened. If the driver - * is interrupt driven, you should enable interrupts here. Otherwise, - * it's probably safe to do nothing. - * - * Since micromonitor already set up the UART, we do nothing. - */ -static int uart_first_open(int major, int minor, void *arg) +typedef struct { + int minor; + mc9328mxl_uart_regs_t * regs; + volatile const char *buf; + volatile int len; + volatile int idx; + void *tty; +} imx_uart_data_t; + +static imx_uart_data_t imx_uart_data[NUM_DEVS]; + +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) { - return 0; + rtems_status_code status; + int i; + + for (i = 0; i < NUM_DEVS; i++) { + imx_uart_init(i); + } + + rtems_termios_initialize(); + + /* /dev/console and /dev/tty0 are the same */ + status = rtems_io_register_name("/dev/console", major, 0); + if (status != RTEMS_SUCCESSFUL) { + rtems_panic("%s:%d Error registering /dev/console :: %d\n", + __FUNCTION__, __LINE__, status); + } + + status = rtems_io_register_name("/dev/tty0", major, 0); + if (status != RTEMS_SUCCESSFUL) { + rtems_panic("%s:%d Error registering /dev/tty0 :: %d\n", + __FUNCTION__, __LINE__, status); + } + + status = rtems_io_register_name("/dev/tty1", major, 1); + if (status != RTEMS_SUCCESSFUL) { + rtems_panic("%s:%d Error registering /dev/tty1 :: %d\n", + __FUNCTION__, __LINE__, status); + } + return RTEMS_SUCCESSFUL; } +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_status_code rc; -/* - * This is called the last time each device is closed. If the driver - * is interrupt driven, you should disable interrupts here. Otherwise, - * it's probably safe to do nothing. - */ -static int uart_last_close(int major, int minor, void *arg) + if (minor > (NUM_DEVS - 1)) { + return RTEMS_INVALID_NUMBER; + } + + rc = rtems_termios_open(major, minor, arg, &imx_uart_cbacks); + + return rc; +} + +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) { - return 0; + return rtems_termios_close(arg); +} + +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_read(arg); +} + +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_write(arg); +} + +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + return rtems_termios_ioctl(arg); } - -/* - * Read one character from UART. - * - * Return -1 if there's no data, otherwise return - * the character in lowest 8 bits of returned int. - */ -static int uart_read(int minor) +static void imx_uart_init(int minor) { - char c; + imx_uart_data[minor].minor = minor; + imx_uart_data[minor].buf = NULL; + imx_uart_data[minor].len = 0; + imx_uart_data[minor].idx = 0; if (minor == 0) { - if (MC9328MXL_UART1_SR2 & MC9328MXL_UART_SR2_RDR) { - c = MC9328MXL_UART1_RXD & MC9328MXL_UART_RXD_CHARMASK; - return c; - } else { - return -1; - } +#if defined(USE_INTERRUPTS) + imx_uart_tx_isr_data[minor].name = BSP_INT_UART1_TX; + imx_uart_rx_isr_data[minor].name = BSP_INT_UART1_RX; +#endif + imx_uart_data[minor].regs = + (mc9328mxl_uart_regs_t *) MC9328MXL_UART1_BASE; } else if (minor == 1) { - if (MC9328MXL_UART2_SR2 & MC9328MXL_UART_SR2_RDR) { - c = MC9328MXL_UART2_RXD & MC9328MXL_UART_RXD_CHARMASK; - return c; - } else { - return -1; - } +#if defined(USE_INTERRUPTS) + imx_uart_tx_isr_data[minor].name = BSP_INT_UART2_TX; + imx_uart_rx_isr_data[minor].name = BSP_INT_UART2_RX; +#endif + imx_uart_data[minor].regs = + (mc9328mxl_uart_regs_t *) MC9328MXL_UART2_BASE; } else { - printk("Unknown console minor number: %d\n", minor); - return -1; + rtems_panic("%s:%d Unknown UART minor number %d\n", + __FUNCTION__, __LINE__, minor); } +#if defined(USE_INTERRUPTS) + imx_uart_tx_isr_data[minor].hdl = imx_uart_tx_isr; + imx_uart_tx_isr_data[minor].handle = &imx_uart_data[minor]; + imx_uart_tx_isr_data[minor].on = imx_uart_isr_on; + imx_uart_tx_isr_data[minor].off = imx_uart_isr_off; + imx_uart_tx_isr_data[minor].isOn = imx_uart_isr_is_on; + + imx_uart_rx_isr_data[minor].hdl = imx_uart_rx_isr; + imx_uart_rx_isr_data[minor].handle = &imx_uart_data[minor]; + imx_uart_rx_isr_data[minor].on = imx_uart_isr_on; + imx_uart_rx_isr_data[minor].off = imx_uart_isr_off; + imx_uart_rx_isr_data[minor].isOn = imx_uart_isr_is_on; +#endif + + imx_uart_data[minor].regs->cr1 = ( + MC9328MXL_UART_CR1_UARTCLKEN | + MC9328MXL_UART_CR1_UARTEN); + + imx_uart_data[minor].regs->cr2 = ( + MC9328MXL_UART_CR2_IRTS | + MC9328MXL_UART_CR2_WS | + MC9328MXL_UART_CR2_TXEN | + MC9328MXL_UART_CR2_RXEN | + MC9328MXL_UART_CR2_SRST); + + imx_uart_data[minor].regs->cr3 = 0; + + imx_uart_data[minor].regs->cr4 = 0; + + imx_uart_data[minor].regs->fcr = ( + MC9328MXL_UART_FCR_TXTL(32) | + MC9328MXL_UART_FCR_RFDIV_1 | + MC9328MXL_UART_FCR_RXTL(1)); + + imx_uart_set_baud(minor, 38400); + } +static int imx_uart_first_open(int major, int minor, void *arg) +{ + rtems_libio_open_close_args_t *args = arg; -/* - * Write buffer to UART - * - * return 1 on success, -1 on error - */ -static int uart_write(int minor, const char *buf, int len) + imx_uart_data[minor].tty = args->iop->data1; + +#if defined(USE_INTERRUPTS) + BSP_install_rtems_irq_handler(&imx_uart_tx_isr_data[minor]); + BSP_install_rtems_irq_handler(&imx_uart_rx_isr_data[minor]); + + imx_uart_data[minor].regs->cr1 |= MC9328MXL_UART_CR1_RRDYEN; +#endif + + return 0; +} + +static int imx_uart_last_close(int major, int minor, void *arg) { - int i; +#if defined(USE_INTERRUPTS) + BSP_remove_rtems_irq_handler(&imx_uart_tx_isr_data[minor]); + BSP_remove_rtems_irq_handler(&imx_uart_rx_isr_data[minor]); +#endif - if (minor == 0) { - for (i = 0; i < len; i++) { - /* Wait for fifo to have room */ - while(!(MC9328MXL_UART1_SR2 & MC9328MXL_UART_SR2_TXDC)) { - continue; - } - - MC9328MXL_UART1_TXD = (char) buf[i]; - } - } else if (minor == 1) { - for (i = 0; i < len; i++) { - /* Wait for fifo to have room */ - while(!(MC9328MXL_UART2_SR2 & MC9328MXL_UART_SR2_TXDC)) { - continue; - } - - MC9328MXL_UART2_TXD = (char) buf[i]; - } + return 0; +} + +static int imx_uart_poll_read(int minor) +{ + if (imx_uart_data[minor].regs->sr2 & MC9328MXL_UART_SR2_RDR) { + return imx_uart_data[minor].regs->rxd & 0xff; } else { - printk("Unknown console minor number: %d\n", minor); return -1; } - - return 1; } -/* Set up the UART. */ -static void uart_init(int minor) +static int imx_uart_poll_write(int minor, const char *buf, int len) { - /* leave the debug sio port as setup by umon */ + int i; + for (i = 0; i < len; i++) { + /* Wait for there to be room in the fifo */ + while (!(imx_uart_data[minor].regs->sr2 & MC9328MXL_UART_SR2_TXDC)) { + continue; + } + + imx_uart_data[minor].regs->txd = buf[i]; + } + return 1; + } -/* I'm not sure this is needed for the shared console driver. */ -static void uart_write_polled(int minor, char c) +#if defined(USE_INTERRUPTS) +static int imx_uart_intr_write(int minor, const char *buf, int len) { - uart_write(minor, &c, 1); + imx_uart_data[minor].buf = buf; + imx_uart_data[minor].len = len; + imx_uart_data[minor].idx = 0; + + imx_uart_data[minor].regs->cr1 |= MC9328MXL_UART_CR1_TXMPTYEN; + + return 1; } +#endif + /* This is for setting baud rate, bits, etc. */ -static int uart_set_attributes(int minor, const struct termios *t) +static int imx_uart_set_attrs(int minor, const struct termios *t) { + int baud; + + baud = termios_baud_to_number(t->c_cflag & CBAUD); + imx_uart_set_baud(minor, baud); + return 0; } -/***********************************************************************/ +#if defined(USE_INTERRUPTS) +static void imx_uart_isr_on(const rtems_irq_connect_data *irq) +{ + MC9328MXL_AITC_INTENNUM = irq->name; +} +static void imx_uart_isr_off(const rtems_irq_connect_data *irq) +{ + MC9328MXL_AITC_INTDISNUM = irq->name; +} +static int imx_uart_isr_is_on(const rtems_irq_connect_data *irq) +{ + int irq_num = (int)irq->name; + if (irq_num < 32) { + return MC9328MXL_AITC_INTENABLEL & (1 << irq_num); + } else { + return MC9328MXL_AITC_INTENABLEH & (1 << (irq_num - 32)); + } +} + +static void imx_uart_rx_isr(rtems_irq_hdl_param param) +{ + imx_uart_data_t *uart_data = param; + char buf[32]; + int i=0; + + while (uart_data->regs->sr2 & MC9328MXL_UART_SR2_RDR) { + buf[i] = uart_data->regs->rxd & 0xff; + i++; + } + + rtems_termios_enqueue_raw_characters(uart_data->tty, buf, i); +} + +static void imx_uart_tx_isr(rtems_irq_hdl_param param) +{ + imx_uart_data_t *uart_data = param; + int len; + int minor = uart_data->minor; + + + if (uart_data->idx < uart_data->len) { + while ( (uart_data->regs->sr1 & MC9328MXL_UART_SR1_TRDY) && + (uart_data->idx < uart_data->len)) { + uart_data->regs->txd = uart_data->buf[uart_data->idx]; + uart_data->idx++; + } + } else { + len = uart_data->len; + uart_data->len = 0; + imx_uart_data[minor].regs->cr1 &= ~MC9328MXL_UART_CR1_TXMPTYEN; + rtems_termios_dequeue_characters(uart_data->tty, len); + } +} +#endif + /* - * The following functions are not used by TERMIOS, but other RTEMS - * functions use them instead. + * Set the UART's baud rate. The calculation is: + * (baud * 16) / ref_freq = num/demom + * + * ref_freq = perclk1 / RFDIV[2:0] + * BIR = num - 1 + * BMR = demom - 1 + * + * Setting 'num' to 16 yields this equation: + * demom = ref_freq / baud */ -/***********************************************************************/ +static void imx_uart_set_baud(int minor, int baud) +{ + unsigned int perclk1; + unsigned int denom; + unsigned int ref_freq = 0; + uint32_t fcr; + + perclk1 = get_perclk1_freq(); + fcr = imx_uart_data[minor].regs->fcr; + + switch(fcr & MC9328MXL_UART_FCR_RFDIV_MASK) { + case MC9328MXL_UART_FCR_RFDIV_1: ref_freq = perclk1/1; break; + case MC9328MXL_UART_FCR_RFDIV_2: ref_freq = perclk1/2; break; + case MC9328MXL_UART_FCR_RFDIV_3: ref_freq = perclk1/3; break; + case MC9328MXL_UART_FCR_RFDIV_4: ref_freq = perclk1/4; break; + case MC9328MXL_UART_FCR_RFDIV_5: ref_freq = perclk1/5; break; + case MC9328MXL_UART_FCR_RFDIV_6: ref_freq = perclk1/6; break; + case MC9328MXL_UART_FCR_RFDIV_7: ref_freq = perclk1/7; break; + default: + rtems_panic("%s:%d Unknown RFDIV: 0x%x", + __FUNCTION__, __LINE__, + fcr & MC9328MXL_UART_FCR_RFDIV_MASK); + break; + } + + denom = ref_freq / baud; + + imx_uart_data[minor].regs->bir = 0xf; + imx_uart_data[minor].regs->bmr = denom; +} + + /* - * Read from UART. This is used in the exit code, and can't - * rely on interrupts. -*/ -int uart_poll_read(int minor) + * Polled, non-blocking read from UART + */ +int imx_uart_poll_read_char(int minor) { - return uart_read(minor); + return imx_uart_poll_read(minor); } +/* + * Polled, blocking write from UART + */ +void imx_uart_poll_write_char(int minor, char c) +{ + imx_uart_poll_write(minor, &c, 1); +} /* - * Write a character to the console. This is used by printk() and - * maybe other low level functions. It should not use interrupts or any - * RTEMS system calls. It needs to be very simple + * Functions for printk() and friends. */ -static void _BSP_put_char( char c ) { - uart_write_polled(0, c); +void _BSP_output_char(char c) +{ + poll_write(c); if (c == '\n') { - uart_write_polled(0, '\r'); + poll_write('\r'); } } +BSP_output_char_function_type BSP_output_char = _BSP_output_char; -BSP_output_char_function_type BSP_output_char = _BSP_put_char; +char _BSP_poll_char() +{ + return poll_read(); +} +BSP_polling_getchar_function_type BSP_poll_char = _BSP_poll_char; diff --git a/c/src/lib/libbsp/arm/csb336/startup/exit.c b/c/src/lib/libbsp/arm/csb336/startup/exit.c index f25e0ae278..a2ddd2f632 100644 --- a/c/src/lib/libbsp/arm/csb336/startup/exit.c +++ b/c/src/lib/libbsp/arm/csb336/startup/exit.c @@ -29,7 +29,7 @@ void bsp_cleanup(void) */ printk("\n"); printk(line); - while (uart_poll_read(0) < 0) continue; + while (BSP_poll_char() < 0) continue; bsp_reset(); |