summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/mpc5xx/console-generic/console-generic.c
blob: 2625bb56bfd50baa7a8a715b3a5ee41729bf8da7 (plain) (tree)

























































































































































































































































































































































                                                                               
/*
 *  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.
 *
 *  $Id$
 */

#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 <libcpu/irq.h>


extern rtems_cpu_table Cpu_table;	/* for CPU clock speed */

/*
 * 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 */
};

  
/*
 * 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;
}

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

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

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

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

void
m5xx_uart_reserve_resources(
  rtems_configuration_table *configuration
)
{
  rtems_termios_reserve_resources (configuration, NUM_PORTS);
}

int 
m5xx_uart_setAttributes(
  int minor,
  const struct termios *t
)
{
  rtems_unsigned16 sccr0 = sci_descs[minor].regs->sccr0;
  rtems_unsigned16 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 */
  switch (t->c_cflag & CBAUD) {
    default:      baud = -1;      break;
    case B50:     baud = 50;      break;
    case B75:     baud = 75;      break;
    case B110:    baud = 110;     break;
    case B134:    baud = 134;     break;
    case B150:    baud = 150;     break;
    case B200:    baud = 200;     break;
    case B300:    baud = 300;     break;
    case B600:    baud = 600;     break;
    case B1200:   baud = 1200;    break;
    case B1800:   baud = 1800;    break;
    case B2400:   baud = 2400;    break;
    case B4800:   baud = 4800;    break;
    case B9600:   baud = 9600;    break;
    case B19200:  baud = 19200;   break;
    case B38400:  baud = 38400;   break;
    case B57600:  baud = 57600;   break;
    case B115200: baud = 115200;  break;
    case B230400: baud = 230400;  break;
    case B460800: baud = 460800;  break;
  }
  if (baud > 0) {
    sccr0 &= ~QSMCM_SCI_BAUD(-1);
    sccr0 |= 
      QSMCM_SCI_BAUD((Cpu_table.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 (void)
{
  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));
  }
}