/* * Console driver for MC9328XML UARTs. * * Written Jay Monkman * Copyright (c) 2005 by Loping Dog Embedded Systems * * 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 */ #include #include #include #include #include #include #include #include #include /* 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) 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 ssize_t imx_uart_poll_write(int, const char *, size_t); #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 ssize_t imx_uart_intr_write(int, const char *, size_t); #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 #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 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 ) { 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; 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 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); } static void imx_uart_init(int minor) { 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 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 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 { 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; 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) { #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 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 { return -1; } } static ssize_t imx_uart_poll_write(int minor, const char *buf, size_t len) { 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; } #if defined(USE_INTERRUPTS) static ssize_t imx_uart_intr_write(int minor, const char *buf, size_t len) { 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 imx_uart_set_attrs(int minor, const struct termios *t) { int baud; baud = rtems_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 /* * 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; } /* * Polled, non-blocking read from UART */ int imx_uart_poll_read_char(int 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); } /* * Functions for printk() and friends. */ void _BSP_output_char(char c) { poll_write(c); if (c == '\n') { poll_write('\r'); } } BSP_output_char_function_type BSP_output_char = _BSP_output_char; int _BSP_poll_char(void) { return poll_read(); } BSP_polling_getchar_function_type BSP_poll_char = _BSP_poll_char;