summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/arm/csb336/console/uart.c
blob: 4dc409b40c35e1c2a5aa71e514242f711b369deb (plain) (tree)
1
2
3
4
5
6
7
8
9
  
                                      
  

                                                    
  


                                                          

                
                        

                            
                        

                      
                    
                      



                                                     


                            

                                                     
 





                                                           
                                                              
 
                           




                                                                 
                                                              




                       
                           








                                                
  
     










                                                
 
                           


                                                             
 















                                               
 



























                                                                   

 






                                    
 







                                                                 
 




                                    
 

                                    
 







                                    
 







                                    
 






                                    

 
                                    
 



                                       

                     
                           


                                                            
                                   
                                                           
                            
                           


                                                            
                                   
                                                           
            

                                                           

     
                           




























                                                                

                                     



                                    

 


                                                               
 
                                                  

                           









                                                                
 
                           


                                                               
 


             
                                        


                                                                  
            

                  


 
                                                                          
 










                                                                             

 
                           
                                                                          
 


                                   
 


                                                                  
 
      
 
 
                                               
                                                                 
 

             
                                                            

                                   


             
                           





















































                                                                       
  








                                                
   


















                                                                  
                                                



                                                         
 






                                           
  


                                      
 
                                     

 
  





                                                 

  
                                      
   


                             
                    
                         


     
                                                                 
 
                        


                       
 
                                                                 

 
/*
 * Console driver for MC9328XML UARTs.
 *
 * Written Jay Monkman <jtm@lopingdog.com>
 * 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 <bsp.h>
#include <rtems/libio.h>
#include <libchip/sersupp.h>
#include <rtems/error.h>
#include <rtems/bspIo.h>
#include <termios.h>
#include <rtems/irq.h>
#include <bsp/irq.h>
#include <mc9328mxl.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)

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;