summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/sparc/leon3/console/console.c
blob: 3870685b5063df30de9bb9e52499a4f4e13cd33c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                                                                       

   










                                                                             




                        
                 
 








                                                                    
  
                          



                                                     
                                   
                            




                       
 
                                 

  
                              



                                       
                                                                 


                            
                     
                            
                       





                          


                                                                 
 






























































                                                                 




                                        
                                                                    
 





                             

                        
                                                              




                
                               







                             


                                                         

      


































































                                                              





                                                                             
                                                           


                                 





















                                                                              
 





                                      






                                       
        
                        


                             


                       














                                                                              
 









                                                                               
                                                              

                                         
   





                                                                  











                                  


                                            
 













                                                           

                                                
                                               
                                            
                                                    



                                                           
      
 

                                                        
                                
 
                                                         



                             
                                        
      













                                                                           
 




                                                                     
                                  


                                                                    


                          
 





                                  



















                                                               
 
 





                                  
                                 
 
 





                                  
                                  
 
 





                                    
                                  

 
/*
 *  This file contains the TTY driver for the serial ports on the LEON.
 *
 *  This driver uses the termios pseudo driver.
 *
 *  COPYRIGHT (c) 1989-1998.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  Modified for LEON3 BSP.
 *  COPYRIGHT (c) 2004.
 *  Gaisler Research.
 *
 *  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/LICENSE.
 */

/* Define CONSOLE_USE_INTERRUPTS to enable APBUART interrupt handling instead
 * of polling mode.
 *
 * Note that it is not possible to use the interrupt mode of the driver
 * together with the "old" APBUART and -u to GRMON. However the new
 * APBUART core (from GRLIB 1.0.17-b2710) has the GRMON debug bit and can 
 * handle interrupts.
 *
 * NOTE: This can be defined in the make/custom/leon3.cfg file.
 */

#include <bsp.h>
#include <rtems/libio.h>
#include <stdlib.h>
#include <assert.h>
#include <rtems/bspIo.h>
#include <amba.h>

/* Let user override which on-chip APBUART will be debug UART
 * 0 = Default APBUART. On MP system CPU0=APBUART0, CPU1=APBUART1...
 * 1 = APBUART[0]
 * 2 = APBUART[1]
 * 3 = APBUART[2]
 * ...
 */
int syscon_uart_index __attribute__((weak)) = 0;

/*
 *  apbuart_outbyte_polled
 *
 *  This routine transmits a character using polling.
 */

extern void apbuart_outbyte_polled(
  struct apbuart_regs *regs,
  unsigned char ch,
  int do_cr_on_newline,
  int wait_sent
  );


/* body is in printk_support.c */

/*
 *  apbuart_inbyte_nonblocking
 *
 *  This routine polls for a character.
 */

extern int apbuart_inbyte_nonblocking(struct apbuart_regs *regs);

/* body is in debugputs.c */

struct apbuart_priv {
  struct apbuart_regs *regs;
  unsigned int freq_hz;
#if CONSOLE_USE_INTERRUPTS
  int irq;
  void *cookie;
  volatile int sending;
  char *buf;
#endif
};
static struct apbuart_priv apbuarts[BSP_NUMBER_OF_TERMIOS_PORTS];
static int uarts = 0;

#if CONSOLE_USE_INTERRUPTS

/* Handle UART interrupts */
void console_isr(void *arg)
{
  struct apbuart_priv *uart = arg;
  unsigned int status;
  char data;

  /* Get all received characters */
  while ((status=uart->regs->status) & LEON_REG_UART_STATUS_DR) {
    /* Data has arrived, get new data */
    data = uart->regs->data;

    /* Tell termios layer about new character */
    rtems_termios_enqueue_raw_characters(uart->cookie, &data, 1);
  }

  if (status & LEON_REG_UART_STATUS_THE) {
    /* Sent the one char, we disable TX interrupts */
    uart->regs->ctrl &= ~LEON_REG_UART_CTRL_TI;

    /* Tell close that we sent everything */
    uart->sending = 0;

    /* write_interrupt will get called from this function */
    rtems_termios_dequeue_characters(uart->cookie, 1);
  }
}

/*
 *  Console Termios Write-Buffer Support Entry Point
 *
 */
int console_write_interrupt(int minor, const char *buf, int len)
{
  struct apbuart_priv *uart;
  unsigned int oldLevel;

  if (minor == 0)
    uart = &apbuarts[syscon_uart_index];
  else
    uart = &apbuarts[minor - 1];

  /* Remember what position in buffer */

  rtems_interrupt_disable(oldLevel);

  /* Enable TX interrupt */
  uart->regs->ctrl |= LEON_REG_UART_CTRL_TI;

  /* start UART TX, this will result in an interrupt when done */
  uart->regs->data = *buf;

  uart->sending = 1;

  rtems_interrupt_enable(oldLevel);

  return 0;
}

#else

/*
 *  Console Termios Support Entry Points
 *
 */

ssize_t console_write_polled(int minor, const char *buf, size_t len)
{
  int nwrite = 0, port;

  if (minor == 0)
    port = syscon_uart_index;
  else
    port = minor - 1;

  while (nwrite < len) {
    apbuart_outbyte_polled(apbuarts[port].regs, *buf++, 1, 0);
    nwrite++;
  }
  return nwrite;
}

int console_pollRead(int minor)
{
  int port;

  if (minor == 0)
    port = syscon_uart_index;
  else
    port = minor - 1;

  return apbuart_inbyte_nonblocking(apbuarts[port].regs);
}

#endif

int console_set_attributes(int minor, const struct termios *t)
{
  unsigned int scaler;
  unsigned int ctrl;
  int baud;
  struct apbuart_priv *uart;

  switch (t->c_cflag & CSIZE) {
    default:
    case CS5:
    case CS6:
    case CS7:
      /* Hardware doesn't support other than CS8 */
      return -1;
    case CS8:
      break;
  }

  if (minor == 0)
    uart = &apbuarts[syscon_uart_index];
  else
    uart = &apbuarts[minor - 1];

  /* Read out current value */
  ctrl = uart->regs->ctrl;

  switch (t->c_cflag & (PARENB|PARODD)) {
    case (PARENB|PARODD):
      /* Odd parity */
      ctrl |= LEON_REG_UART_CTRL_PE|LEON_REG_UART_CTRL_PS;
      break;

    case PARENB:
      /* Even parity */
      ctrl &= ~LEON_REG_UART_CTRL_PS;
      ctrl |= LEON_REG_UART_CTRL_PE;
      break;

    default:
    case 0:
    case PARODD:
      /* No Parity */
      ctrl &= ~(LEON_REG_UART_CTRL_PS|LEON_REG_UART_CTRL_PE);
  }

  if (!(t->c_cflag & CLOCAL)) {
    ctrl |= LEON_REG_UART_CTRL_FL;
  } else {
    ctrl &= ~LEON_REG_UART_CTRL_FL;
  }

  /* Update new settings */
  uart->regs->ctrl = ctrl;

  /* Baud rate */
  baud = rtems_termios_baud_to_number(t->c_cflag);
  if (baud > 0) {
    /* Calculate Baud rate generator "scaler" number */
    scaler = (((uart->freq_hz * 10) / (baud * 8)) - 5) / 10;

    /* Set new baud rate by setting scaler */
    uart->regs->scaler = scaler;
  }

  return 0;
}

/* AMBA PP find routine. Extract AMBA PnP information into data structure. */
int find_matching_apbuart(struct ambapp_dev *dev, int index, void *arg)
{
  struct ambapp_apb_info *apb = (struct ambapp_apb_info *)dev->devinfo;

  /* Extract needed information of one APBUART */
  apbuarts[uarts].regs = (struct apbuart_regs *)apb->start;
#if CONSOLE_USE_INTERRUPTS
  apbuarts[uarts].irq = apb->irq;
#endif
  /* Get APBUART core frequency, it is assumed that it is the same
   * as Bus frequency where the UART is situated
   */
  apbuarts[uarts].freq_hz = ambapp_freq_get(&ambapp_plb, dev);
  uarts++;

  if (uarts >= BSP_NUMBER_OF_TERMIOS_PORTS)
    return 1; /* Satisfied number of UARTs, stop search */
  else
    return 0; /* Continue searching for more UARTs */
}

/* Find all UARTs */
int console_scan_uarts(void)
{
  memset(apbuarts, 0, sizeof(apbuarts));

  /* Find APBUART cores */
  ambapp_for_each(&ambapp_plb, (OPTIONS_ALL|OPTIONS_APB_SLVS), VENDOR_GAISLER,
                  GAISLER_APBUART, find_matching_apbuart, NULL);

  return uarts;
}

/*
 *  Console Device Driver Entry Points
 *
 */

rtems_device_driver console_initialize(
  rtems_device_major_number  major,
  rtems_device_minor_number  minor,
  void                      *arg
)
{
  rtems_status_code status;
  int i;
  char console_name[16];

  rtems_termios_initialize();

  /* Find UARTs */
  console_scan_uarts();

  /* Update syscon_uart_index to index used as /dev/console
   * Let user select System console by setting syscon_uart_index. If the
   * BSP is to provide the default UART (syscon_uart_index==0):
   *   non-MP: APBUART[0] is system console
   *   MP: LEON CPU index select UART
   */
  if (syscon_uart_index == 0) {
#if defined(RTEMS_MULTIPROCESSING)
    syscon_uart_index = LEON3_Cpu_Index;
#else
    syscon_uart_index = 0;
#endif
  } else {
    syscon_uart_index = syscon_uart_index - 1; /* User selected sys-console */
  }

  /*  Register Device Names
   *
   *  0 /dev/console   - APBUART[USER-SELECTED, DEFAULT=APBUART[0]]
   *  1 /dev/console_a - APBUART[0] (by default not present because is console)
   *  2 /dev/console_b - APBUART[1]
   *  ...
   *
   * On a MP system one should not open UARTs that other OS instances use.
   */
  if (syscon_uart_index < uarts) {
    status = rtems_io_register_name("/dev/console", major, 0);
    if (status != RTEMS_SUCCESSFUL)
      rtems_fatal_error_occurred(status);
  }
  strcpy(console_name,"/dev/console_a");
  for (i = 0; i < uarts; i++) {
    if (i == syscon_uart_index)
      continue; /* skip UART that is registered as /dev/console */
    console_name[13] = 'a' + i;
    status = rtems_io_register_name( console_name, major, i+1);
  }

  return RTEMS_SUCCESSFUL;
}

rtems_device_driver console_open(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void                    * arg
)
{
  rtems_status_code sc;
  struct apbuart_priv *uart;
#if CONSOLE_USE_INTERRUPTS
  rtems_libio_open_close_args_t *priv = arg;

  /* Interrupt mode routines */
  static const rtems_termios_callbacks Callbacks = {
    NULL,                        /* firstOpen */
    NULL,                        /* lastClose */
    NULL,                        /* pollRead */
    console_write_interrupt,     /* write */
    console_set_attributes,      /* setAttributes */
    NULL,                        /* stopRemoteTx */
    NULL,                        /* startRemoteTx */
    1                            /* outputUsesInterrupts */
  };
#else
  /* Polling mode routines */
  static const rtems_termios_callbacks Callbacks = {
    NULL,                        /* firstOpen */
    NULL,                        /* lastClose */
    console_pollRead,            /* pollRead */
    console_write_polled,        /* write */
    console_set_attributes,      /* setAttributes */
    NULL,                        /* stopRemoteTx */
    NULL,                        /* startRemoteTx */
    0                            /* outputUsesInterrupts */
  };
#endif

  assert(minor <= uarts);
  if (minor > uarts || minor == (syscon_uart_index + 1))
    return RTEMS_INVALID_NUMBER;

  sc = rtems_termios_open(major, minor, arg, &Callbacks);
  if (sc != RTEMS_SUCCESSFUL)
    return sc;

  if (minor == 0)
    uart = &apbuarts[syscon_uart_index];
  else
    uart = &apbuarts[minor - 1];

#if CONSOLE_USE_INTERRUPTS
  if (priv && priv->iop)
    uart->cookie = priv->iop->data1;
  else
    uart->cookie = NULL;

  /* Register Interrupt handler */
  sc = rtems_interrupt_handler_install(uart->irq, "console",
                                       RTEMS_INTERRUPT_SHARED, console_isr,
                                       uart);
  if (sc != RTEMS_SUCCESSFUL)
    return sc;

  uart->sending = 0;
  /* Enable Receiver and transmitter and Turn on RX interrupts */
  uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE |
                      LEON_REG_UART_CTRL_RI;
#else
  /* Initialize UART on opening */
  uart->regs->ctrl |= LEON_REG_UART_CTRL_RE | LEON_REG_UART_CTRL_TE;
#endif
  uart->regs->status = 0;

  return RTEMS_SUCCESSFUL;
}

rtems_device_driver console_close(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void                    * arg
)
{
#if CONSOLE_USE_INTERRUPTS
  struct apbuart_priv *uart;

  if (minor == 0)
    uart = &apbuarts[syscon_uart_index];
  else
    uart = &apbuarts[minor - 1];

  /* Turn off RX interrupts */
  uart->regs->ctrl &= ~(LEON_REG_UART_CTRL_RI);

  /**** Flush device ****/
  while (uart->sending) {
    /* Wait until all data has been sent */
  }

  /* uninstall ISR */
  rtems_interrupt_handler_remove(uart->irq, console_isr, uart);
#endif
  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);
}