summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/mpc5xx/console-generic/console-generic.c
blob: f3e7ff081e5601ce34b6fb8ca430455807e70d87 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16















                                                                               
                



















                                                                           
                                         









                                           
                    

 































                                                                        
 

                                





                             

            















                                                                         
            


















                                                                              
 








                                                
                        

                  
            








                                                                
                            

                  
            


                                                            
                      





                                                

                

 
   




                         

                                                
           
 


                                    
                                                     


                                
                                                            

                                 
                                                                           
   
 



















                                                            
 










                                       
 


                                       
 



                          
  


                      
                                                       

            
 



                                                 

      


































                                                              
                                                     





                                                   
 










                                                                              
 








                                                                         
/*
 *  General Serial I/O functions.
 *
 *  This file contains the functions for performing serial I/O.  The actual
 *  system calls (console_*) should be in the BSP part of the source tree.
 *  That way different BSPs can use whichever SCI they wish for /dev/console.
 *
 *  On-chip resources used:
 *   resource   minor                note
 *	SCI1	  0
 *	SCI2	  1
 *
 *
 *  MPC5xx port sponsored by Defence Research and Development Canada - Suffield
 *  Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
 *
 *  Derived from
 *    c/src/lib/libcpu/powerpc/mpc8xx/console_generic/console_generic.c:
 *  Author: Jay Monkman (jmonkman@frasca.com)
 *  Copyright (C) 1998 by Frasca International, Inc.
 *
 *  Derived from c/src/lib/libbsp/m68k/gen360/console/console.c written by:
 *    W. Eric Norum
 *    Saskatchewan Accelerator Laboratory
 *    University of Saskatchewan
 *    Saskatoon, Saskatchewan, CANADA
 *    eric@skatter.usask.ca
 *
 *  COPYRIGHT (c) 1989-1998.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  Modifications by Darlene Stewart <Darlene.Stewart@iit.nrc.ca>
 *  and Charles-Antoine Gauthier <charles.gauthier@iit.nrc.ca>
 *  Copyright (c) 1999, National Research Council of Canada
 *
 *  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.
 */

#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/bspIo.h>   /* for printk */
#include <mpc5xx.h>
#include <mpc5xx/console.h>
#include <bsp/irq.h>


/*
 * SCI port descriptor table.
 */
typedef struct
{
  volatile m5xxSCIRegisters_t *regs;	/* hardware registers */
  struct rtems_termios_tty *ttyp;	/* termios data for this port */
} sci_desc;

static sci_desc sci_descs[] = {
  { &imb.qsmcm.sci1, 0 },		/* SCI 1 */
  { &imb.qsmcm.sci2, 0 },		/* SCI 2 */
};

/*
 * Number of SCI port initialization calls made so far.  Used to avoid
 * installing the common interrupt handler more than once.
 */
int init_calls = 0;

/*
 * Default configuration.
 */
static struct termios default_termios = {
  0, 					/* input mode flags */
  0, 					/* output mode flags */
  CS8 | CREAD | CLOCAL | B9600, 	/* control mode flags */
  0, 					/* local mode flags */
  0,					/* line discipline */
  { 0 }					/* control characters */
};


extern uint32_t bsp_clock_speed;

/*
 * Termios callback functions
 */

int
m5xx_uart_firstOpen(
  int major,
  int minor,
  void *arg
)
{
  rtems_libio_open_close_args_t *args = arg;
  sci_desc* desc = &sci_descs[minor];
  struct rtems_termios_tty *tty = args->iop->data1;

  desc->ttyp = tty;				/* connect tty */
  if ( tty->device.outputUsesInterrupts == TERMIOS_IRQ_DRIVEN)
    desc->regs->sccr1 |= QSMCM_SCI_RIE;		/* enable rx interrupt */

  return RTEMS_SUCCESSFUL;
}

int
m5xx_uart_lastClose(
  int major,
  int minor,
  void* arg
)
{
  sci_desc* desc = &sci_descs[minor];

  desc->regs->sccr1 &= ~(QSMCM_SCI_RIE | QSMCM_SCI_TIE);  /* disable all */
  desc->ttyp = NULL;					  /* disconnect tty */

  return RTEMS_SUCCESSFUL;
}

int
m5xx_uart_pollRead(
  int minor
)
{
  volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;
  int c = -1;

  if ( regs ) {
    while ( (regs->scsr & QSMCM_SCI_RDRF) == 0 )
      ;
    c = regs->scdr;
  }

  return c;
}

ssize_t m5xx_uart_write(
  int minor,
  const char *buf,
  size_t len
)
{
  volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;

  regs->scdr = *buf;			/* start transmission */
  regs->sccr1 |= QSMCM_SCI_TIE;		/* enable interrupt */
  return 0;
}

ssize_t m5xx_uart_pollWrite(
  int minor,
  const char *buf,
  size_t len
)
{
  volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;
  size_t retval = len;

  while ( len-- ) {
    while ( (regs->scsr & QSMCM_SCI_TDRE) == 0 )
      ;
    regs->scdr = *buf++;
  }

  return retval;
}

int
m5xx_uart_setAttributes(
  int minor,
  const struct termios *t
)
{
  uint16_t sccr0 = sci_descs[minor].regs->sccr0;
  uint16_t sccr1 = sci_descs[minor].regs->sccr1;
  int baud;

  /*
   * Check that port number is valid
   */
  if ( (minor < SCI1_MINOR) || (minor > SCI2_MINOR) )
    return RTEMS_INVALID_NUMBER;

  /* Baud rate */
  baud = rtems_termios_baud_to_number( t->c_cflag & CBAUD );
  if (baud > 0) {
    sccr0 &= ~QSMCM_SCI_BAUD(-1);
    sccr0 |= QSMCM_SCI_BAUD((bsp_clock_speed + (16 * baud)) / (32 * baud));
  }

  /* Number of data bits -- not available with MPC5xx SCI */
  switch ( t->c_cflag & CSIZE ) {
    case CS5:     break;
    case CS6:     break;
    case CS7:     break;
    case CS8:     break;
  }

  /* Stop bits -- not easily available with MPC5xx SCI */
  if ( t->c_cflag & CSTOPB ) {
    /* Two stop bits */
  } else {
    /* One stop bit */
  }

  /* Parity */
  if ( t->c_cflag & PARENB )
    sccr1 |= QSMCM_SCI_PE;
  else
    sccr1 &= ~QSMCM_SCI_PE;

  if ( t->c_cflag & PARODD )
    sccr1 |= QSMCM_SCI_PT;
  else
    sccr1 &= ~QSMCM_SCI_PT;

  /* Transmitter and receiver enable */
  sccr1 |= QSMCM_SCI_TE;
  if ( t->c_cflag & CREAD )
    sccr1 |= QSMCM_SCI_RE;
  else
    sccr1 &= ~QSMCM_SCI_RE;

  /* Write hardware registers */
  sci_descs[minor].regs->sccr0 = sccr0;
  sci_descs[minor].regs->sccr1 = sccr1;

  return RTEMS_SUCCESSFUL;
}


/*
 * Interrupt handling.
 */
static void
m5xx_sci_interrupt_handler (rtems_irq_hdl_param unused)
{
  int minor;

  for ( minor = 0; minor < NUM_PORTS; minor++ ) {
    sci_desc *desc = &sci_descs[minor];
    int sccr1 = desc->regs->sccr1;
    int scsr = desc->regs->scsr;

    /*
     * Character received?
     */
    if ((sccr1 & QSMCM_SCI_RIE) && (scsr & QSMCM_SCI_RDRF)) {
      char c = desc->regs->scdr;
      rtems_termios_enqueue_raw_characters(desc->ttyp, &c, 1);
    }
    /*
     * Transmitter empty?
     */
    if ((sccr1 & QSMCM_SCI_TIE) && (scsr & QSMCM_SCI_TDRE)) {
      desc->regs->sccr1 &= ~QSMCM_SCI_TIE;
      rtems_termios_dequeue_characters (desc->ttyp, 1);
    }
  }
}

void m5xx_sci_nop(const rtems_irq_connect_data* ptr)
{
}

int m5xx_sci_isOn(const rtems_irq_connect_data* ptr)
{
  return 1;
}

/*
 * Basic initialization.
 */

void
m5xx_uart_initialize (int minor)
{
  /*
   * Check that minor number is valid.
   */
  if ( (minor < SCI1_MINOR) || (minor > SCI2_MINOR) )
    return;

  /*
   * Configure and enable receiver and transmitter.
   */
  m5xx_uart_setAttributes(minor, &default_termios);

  /*
   * Connect interrupt if not yet done.
   */
  if ( init_calls++ == 0 ) {
    rtems_irq_connect_data irq_data;

    irq_data.name = CPU_IRQ_SCI;
    irq_data.hdl  = m5xx_sci_interrupt_handler;
    irq_data.on   = m5xx_sci_nop;	/* can't enable both channels here */
    irq_data.off  = m5xx_sci_nop;	/* can't disable both channels here */
    irq_data.isOn = m5xx_sci_isOn;

    if (!CPU_install_rtems_irq_handler (&irq_data)) {
      printk("Unable to connect SCI Irq handler\n");
      rtems_fatal_error_occurred(1);
    }

    imb.qsmcm.qdsci_il = 		/* set interrupt level in port */
      QSMCM_ILDSCI(CPU_irq_level_from_symbolic_name(CPU_IRQ_SCI));
  }
}