summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/mpc8xx/console-generic/console-generic.c
blob: fccbc65b315e291aea2fba252858bbe5ce843204 (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
 *  SMCs and SCCs they want. Originally, all the stuff was in
 *  this file, and it caused problems with one BSP using SCC2
 *  as /dev/console, others using SMC1 for /dev/console, etc.
 *
 *  On-chip resources used:
 *   resource   minor                note
 *    SMC1       0
 *    SMC2       1
 *    SCC1       2                   N/A. Hardwired as ethernet port
 *    SCC2       3
 *    SCC3       4
 *    SCC4       5
 *    BRG1
 *    BRG2
 *    BRG3
 *    BRG4
 *  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.OARcorp.com/rtems/license.html.
 *
 *  $Id$
 */

#include <rtems.h>
#include <rtems/libio.h>
#include <mpc8xx.h>
#include <mpc8xx/console.h>
#include <mpc8xx/cpm.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>

extern rtems_cpu_table Cpu_table;

/* BSP supplied routine */
extern int mbx8xx_console_use_maximum_buffer_size(void);

#ifdef EPPCBUG_SMC1
extern unsigned32 simask_copy;
#endif

/*
 * Interrupt-driven input buffer
 */
#define RXBUFSIZE       16

/*
 *  I/O buffers and pointers to buffer descriptors.
 *  Currently, single buffered input is done. This will work only
 *  if the Rx interrupts are serviced quickly.
 *
 *  TODO: Add a least double buffering for safety.
 */
static volatile char rxBuf[NUM_PORTS][RXBUFSIZE];
static volatile char txBuf[NUM_PORTS];

/* SCC/SMC buffer descriptors */
static volatile m8xxBufferDescriptor_t *RxBd[NUM_PORTS], *TxBd[NUM_PORTS];

/* Used to track the usage of the baud rate generators */
static unsigned long brg_spd[4];
static char  brg_used[4];

/* Used to track termios private data for callbacks */
struct rtems_termios_tty *ttyp[NUM_PORTS];

/* Used to record previous ISR */
static rtems_isr_entry old_handler[NUM_PORTS];

/*
 * Device-specific routines
 */
static int m8xx_get_brg_cd(int);
static unsigned char m8xx_get_brg_clk(int);
void m8xx_console_reserve_resources(rtems_configuration_table *);
static int m8xx_smc_set_attributes(int, const struct termios*);
static int m8xx_scc_set_attributes(int, const struct termios*);
static rtems_isr m8xx_smc1_interrupt_handler(rtems_vector_number);
static rtems_isr m8xx_smc2_interrupt_handler(rtems_vector_number);
static rtems_isr m8xx_scc2_interrupt_handler(rtems_vector_number);
#if defined(mpc860)
static rtems_isr m8xx_scc3_interrupt_handler(rtems_vector_number);
static rtems_isr m8xx_scc4_interrupt_handler(rtems_vector_number);
#endif

/*
 * Compute baud-rate-generator configuration register value
 */
static int
m8xx_get_brg_cd (int baud)
{
  int divisor;
  int div16 = 0;

  divisor = ((Cpu_table.clock_speed / 16) + (baud / 2)) / baud;
  if (divisor > 4096) {
    div16 = 1;
    divisor = (divisor + 8) / 16;
  }
  return M8xx_BRG_EN | M8xx_BRG_EXTC_BRGCLK |
    ((divisor - 1) << 1) | div16;
}


/* 
 *  This function will fail if more that 4 baud rates have been selected
 *  at any time since the OS started. It needs to be fixed. FIXME
 */
static unsigned
char m8xx_get_brg_clk(int baud)
{
  int i;

  /* first try to find a BRG that is already at the right speed */
  for ( i = 0; i < 4; i++ ) {
    if ( brg_spd[i] == baud ) {
      break;
    }
  }

  if ( i == 4 ) { /* I guess we didn't find one */
    for ( i = 0; i < 4; i++ ) {
      if ( brg_used[i] == 0 ) {
        break;
      }
    }
  }
  if (i != 4) {
    brg_used[i]++;
    brg_spd[i]=baud;
    switch (i) {
    case 0:
      m8xx.brgc1 = M8xx_BRG_RST;
      m8xx.brgc1 = m8xx_get_brg_cd(baud);
      break;
    case 1:
      m8xx.brgc2 = M8xx_BRG_RST;
      m8xx.brgc2 = m8xx_get_brg_cd(baud);
      break;
    case 2:
      m8xx.brgc3 = M8xx_BRG_RST;
      m8xx.brgc3 = m8xx_get_brg_cd(baud);
      break;
    case 3:
      m8xx.brgc4 = M8xx_BRG_RST;
      m8xx.brgc4 = m8xx_get_brg_cd(baud);
      break;
    }
    return i;
  }

  else
    return 0xff;
}


/*
 * Hardware-dependent portion of tcsetattr().
 */
static int
m8xx_smc_set_attributes (int minor, const struct termios *t)
{
  int baud, brg=0, csize=0, ssize, psize;
  rtems_unsigned16 clen=0, cstopb, parenb, parodd, cread; 

  /* 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)
    brg = m8xx_get_brg_clk(baud);     /* 4 BRGs, 6 serial ports - hopefully */
                                      /*  at least 2 ports will be the same */
 
  /* Number of data bits */
  switch ( t->c_cflag & CSIZE ) {
    case CS5:     csize = 5;       break;
    case CS6:     csize = 6;       break;
    case CS7:     csize = 7;       break;
    case CS8:     csize = 8;       break;
  }

  /* Stop bits */
  if ( t->c_cflag & CSTOPB ) {
    cstopb = 0x0400;              /* Two stop bits */
    ssize  = 2;
  } else {
    cstopb = 0x0000;              /* One stop bit */
    ssize  = 1;
  }

  /* Parity */
  if ( t->c_cflag & PARENB ) {
    parenb = 0x0200;              /* Parity enabled on Tx and Rx */
    psize  = 1;
  } else {
    parenb = 0x0000;              /* No parity on Tx and Rx */
    psize  = 0;
  }
  
  if ( t->c_cflag & PARODD )
    parodd = 0x0000;              /* Odd parity */
  else
    parodd = 0x0100;

  /* 
   * Character Length = start + data + parity + stop - 1
   */ 
  switch ( 1 + csize + psize + ssize - 1 ) {
    case 6:     clen = 0x3000;       break;
    case 7:     clen = 0x3800;       break;
    case 8:     clen = 0x4000;       break;
    case 9:     clen = 0x4800;       break;
    case 10:    clen = 0x5000;       break;
    case 11:    clen = 0x5800;       break;
  }

  if ( t->c_cflag & CREAD )
    cread = 0x0023;		/* UART normal operation, enable Rx and Tx */
  else
    cread = 0x0021;		/* UART normal operation, enable Tx */
    
  /* Write the SIMODE/SMCMR registers */
  switch (minor) {
    case SMC1_MINOR:
      m8xx.simode = ( (m8xx.simode & 0xffff8fff) | (brg << 12) );
      m8xx.smc1.smcmr = clen | cstopb | parenb | parodd | cread;
      break;
    case SMC2_MINOR:
      m8xx.simode = ( (m8xx.simode & 0x8fffffff) | (brg << 28) );
      m8xx.smc2.smcmr = clen | cstopb | parenb | parodd | cread;
      break;
  }
  return 0;
}


static int
m8xx_scc_set_attributes (int minor, const struct termios *t)
{
  int baud, brg=0;
  rtems_unsigned16 csize=0, cstopb, parenb, parodd;

  /* 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)
    brg = m8xx_get_brg_clk(baud);     /* 4 BRGs, 5 serial ports - hopefully */
                                      /*  at least 2 ports will be the same */
                                      /*  Write the SICR register below */
    
  /* Number of data bits */
  switch ( t->c_cflag & CSIZE ) {
    case CS5:     csize = 0x0000;       break;
    case CS6:     csize = 0x1000;       break;
    case CS7:     csize = 0x2000;       break;
    case CS8:     csize = 0x3000;       break;
  }

  /* Stop bits */
  if ( t->c_cflag & CSTOPB )
    cstopb = 0x4000;              /* Two stop bits */
  else
    cstopb = 0x0000;              /* One stop bit */
    
  /* Parity */
  if ( t->c_cflag & PARENB )
    parenb = 0x0010;              /* Parity enabled on Tx and Rx */
  else
    parenb = 0x0000;              /* No parity on Tx and Rx */
    
  if ( t->c_cflag & PARODD )
    parodd = 0x0000;              /* Odd parity */
  else
    parodd = 0x000a;

  /* Write the SICR/PSMR Registers */
  switch (minor) {
    case SCC2_MINOR:
      m8xx.sicr = ( (m8xx.sicr & 0xffffc0ff) | (brg << 11) | (brg << 8) );
      m8xx.scc2.psmr = ( (cstopb | csize | parenb | parodd) | (m8xx.scc2.psmr & 0x8fe0) );
      break;
  #if defined(mpc860)
    case SCC3_MINOR:
      m8xx.sicr = ( (m8xx.sicr & 0xffc0ffff) | (brg << 19) | (brg << 16) );
      m8xx.scc3.psmr = ( (cstopb | csize | parenb | parodd) | (m8xx.scc3.psmr & 0x8fe0) );
      break;
    case SCC4_MINOR:
      m8xx.sicr = ( (m8xx.sicr & 0xc0ffffff) | (brg << 27) | (brg << 24) );
      m8xx.scc4.psmr = ( (cstopb | csize | parenb | parodd) | (m8xx.scc4.psmr & 0x8fe0) );
      break;
  #endif
  }
  
  return 0;
}


int 
m8xx_uart_setAttributes(
  int minor,
  const struct termios *t
)
{
  /*
   * Check that port number is valid
   */
  if ( (minor < SMC1_MINOR) || (minor > NUM_PORTS-1) ) 
    return 0;

  switch (minor) {
    case SMC1_MINOR:
    case SMC2_MINOR:
      return m8xx_smc_set_attributes( minor, t );

    case SCC2_MINOR:
    case SCC3_MINOR:
    case SCC4_MINOR:
      return m8xx_scc_set_attributes( minor, t );
  }
  return 0;
}


/*
 * Interrupt handlers
 */
static rtems_isr
m8xx_scc2_interrupt_handler (rtems_vector_number v)
{
  int nb_overflow;

  /*
   * Buffer received?
   */
  if ((m8xx.scc2.sccm & M8xx_SCCE_RX) && (m8xx.scc2.scce & M8xx_SCCE_RX)) {
    m8xx.scc2.scce = M8xx_SCCE_RX;    /* Clear the event */


    /* Check that the buffer is ours */
    if ((RxBd[SCC2_MINOR]->status & M8xx_BD_EMPTY) == 0) {
      rtems_cache_invalidate_multiple_data_lines( 
        (const void *) RxBd[SCC2_MINOR]->buffer, 
        RxBd[SCC2_MINOR]->length );
      nb_overflow = rtems_termios_enqueue_raw_characters(
        (void *)ttyp[SCC2_MINOR],
        (char *)RxBd[SCC2_MINOR]->buffer,
        (int)RxBd[SCC2_MINOR]->length );
      RxBd[SCC2_MINOR]->status = M8xx_BD_EMPTY | M8xx_BD_WRAP |
                                 M8xx_BD_INTERRUPT;
    }
  }

  /*
   * Buffer transmitted?
   */
  if (m8xx.scc2.scce & M8xx_SCCE_TX) {
    m8xx.scc2.scce = M8xx_SCCE_TX;  /* Clear the event */

    /* Check that the buffer is ours */
    if ((TxBd[SCC2_MINOR]->status & M8xx_BD_READY) == 0)
      rtems_termios_dequeue_characters (
        (void *)ttyp[SCC2_MINOR],
        (int)TxBd[SCC2_MINOR]->length);
  }
  m8xx.cisr = 1UL << 29;  /* Clear SCC2 interrupt-in-service bit */
}


#ifdef mpc860
static rtems_isr
m8xx_scc3_interrupt_handler (rtems_vector_number v)
{
  int nb_overflow;

  /*
   * Buffer received?
   */
  if ((m8xx.scc3.sccm & M8xx_SCCE_RX) && (m8xx.scc3.scce & M8xx_SCCE_RX)) {
    m8xx.scc3.scce = M8xx_SCCE_RX;  /* Clear the event */


    /* Check that the buffer is ours */
    if ((RxBd[SCC3_MINOR]->status & M8xx_BD_EMPTY) == 0) {
      rtems_cache_invalidate_multiple_data_lines( 
        (const void *) RxBd[SCC3_MINOR]->buffer, 
        RxBd[SCC3_MINOR]->length );
      nb_overflow = rtems_termios_enqueue_raw_characters(
        (void *)ttyp[SCC3_MINOR],
        (char *)RxBd[SCC3_MINOR]->buffer,
        (int)RxBd[SCC3_MINOR]->length );
      RxBd[SCC3_MINOR]->status = M8xx_BD_EMPTY | M8xx_BD_WRAP |
                                 M8xx_BD_INTERRUPT;
    }
  }

  /*
   * Buffer transmitted?
   */
  if (m8xx.scc3.scce & M8xx_SCCE_TX) {
    m8xx.scc3.scce = M8xx_SCCE_TX;    /* Clear the event */

    /* Check that the buffer is ours */
    if ((TxBd[SCC3_MINOR]->status & M8xx_BD_READY) == 0)
      rtems_termios_dequeue_characters (
        (void *)ttyp[SCC3_MINOR],
        (int)TxBd[SCC3_MINOR]->length);
  }
  m8xx.cisr = 1UL << 28;  /* Clear SCC3 interrupt-in-service bit */
}


static rtems_isr
m8xx_scc4_interrupt_handler (rtems_vector_number v)
{
  int nb_overflow;

  /*
   * Buffer received?
   */
  if ((m8xx.scc4.sccm & M8xx_SCCE_RX) && (m8xx.scc4.scce & M8xx_SCCE_RX)) {
    m8xx.scc4.scce = M8xx_SCCE_RX;  /* Clear the event */


    /* Check that the buffer is ours */
    if ((RxBd[SCC4_MINOR]->status & M8xx_BD_EMPTY) == 0) {
      rtems_cache_invalidate_multiple_data_lines( 
        (const void *) RxBd[SCC4_MINOR]->buffer, 
        RxBd[SCC4_MINOR]->length );
      nb_overflow = rtems_termios_enqueue_raw_characters(
        (void *)ttyp[SCC4_MINOR],
        (char *)RxBd[SCC4_MINOR]->buffer,
        (int)RxBd[SCC4_MINOR]->length );
      RxBd[SCC4_MINOR]->status = M8xx_BD_EMPTY | M8xx_BD_WRAP |
                                 M8xx_BD_INTERRUPT;
    }
  }

  /*
   * Buffer transmitted?
   */
  if (m8xx.scc4.scce & M8xx_SCCE_TX) {
    m8xx.scc4.scce = M8xx_SCCE_TX;    /* Clear the event */

    /* Check that the buffer is ours */
    if ((TxBd[SCC4_MINOR]->status & M8xx_BD_READY) == 0)
      rtems_termios_dequeue_characters (
        (void *)ttyp[SCC4_MINOR],
        (int)TxBd[SCC4_MINOR]->length);
  }
  m8xx.cisr = 1UL << 27;  /* Clear SCC4 interrupt-in-service bit */
}
#endif

static rtems_isr
m8xx_smc1_interrupt_handler (rtems_vector_number v)
{
  int nb_overflow;

  /*
   * Buffer received?
   */
  if (m8xx.smc1.smce & M8xx_SMCE_RX) {
    m8xx.smc1.smce = M8xx_SMCE_RX;  /* Clear the event */


    /* Check that the buffer is ours */
    if ((RxBd[SMC1_MINOR]->status & M8xx_BD_EMPTY) == 0) {
      rtems_cache_invalidate_multiple_data_lines( 
        (const void *) RxBd[SMC1_MINOR]->buffer, 
        RxBd[SMC1_MINOR]->length );
      nb_overflow = rtems_termios_enqueue_raw_characters(
        (void *)ttyp[SMC1_MINOR],
        (char *)RxBd[SMC1_MINOR]->buffer,
        (int)RxBd[SMC1_MINOR]->length );
      RxBd[SMC1_MINOR]->status = M8xx_BD_EMPTY | M8xx_BD_WRAP |
                                 M8xx_BD_INTERRUPT;
    }
  }

  /*
   * Buffer transmitted?
   */
  if (m8xx.smc1.smce & M8xx_SMCE_TX) {
    m8xx.smc1.smce = M8xx_SMCE_TX;    /* Clear the event */

    /* Check that the buffer is ours */
    if ((TxBd[SMC1_MINOR]->status & M8xx_BD_READY) == 0)
      rtems_termios_dequeue_characters (
        (void *)ttyp[SMC1_MINOR],
        (int)TxBd[SMC1_MINOR]->length);
  }
  m8xx.cisr = 1UL << 4;  /* Clear SMC1 interrupt-in-service bit */
}


static rtems_isr
m8xx_smc2_interrupt_handler (rtems_vector_number v)
{
  int nb_overflow;

  /*
   * Buffer received?
   */
  if (m8xx.smc2.smce & M8xx_SMCE_RX) {
    m8xx.smc2.smce = M8xx_SMCE_RX;  /* Clear the event */


    /* Check that the buffer is ours */
    if ((RxBd[SMC2_MINOR]->status & M8xx_BD_EMPTY) == 0) {
      rtems_cache_invalidate_multiple_data_lines( 
        (const void *) RxBd[SMC2_MINOR]->buffer, 
        RxBd[SMC2_MINOR]->length );
      nb_overflow = rtems_termios_enqueue_raw_characters(
        (void *)ttyp[SMC2_MINOR],
        (char *)RxBd[SMC2_MINOR]->buffer,
        (int)RxBd[SMC2_MINOR]->length );
      RxBd[SMC2_MINOR]->status = M8xx_BD_EMPTY | M8xx_BD_WRAP |
                                 M8xx_BD_INTERRUPT;
    }
  }

  /*
   * Buffer transmitted?
   */
  if (m8xx.smc2.smce & M8xx_SMCE_TX) {
    m8xx.smc2.smce = M8xx_SMCE_TX;    /* Clear the event */

    /* Check that the buffer is ours */
    if ((TxBd[SMC2_MINOR]->status & M8xx_BD_READY) == 0)
      rtems_termios_dequeue_characters (
        (void *)ttyp[SMC2_MINOR],
        (int)TxBd[SMC2_MINOR]->length);
  }
  m8xx.cisr = 1UL << 3;  /* Clear SMC2 interrupt-in-service bit */
}


void
m8xx_uart_scc_initialize (int minor)
{
  unsigned char brg;
  volatile m8xxSCCparms_t *sccparms = 0;
  volatile m8xxSCCRegisters_t *sccregs = 0;

  /*
   * Check that minor number is valid
   */
  if ( (minor < SCC2_MINOR) || (minor > NUM_PORTS-1) ) 
    return;

  /* Get the sicr clock source bit values for 9600 bps */
  brg = m8xx_get_brg_clk(9600);

  /*
   * Allocate buffer descriptors
   */
  RxBd[minor] = m8xx_bd_allocate(1);
  TxBd[minor] = m8xx_bd_allocate(1);

  /*
   *  Get the address of the parameter RAM for the specified port,
   *  configure I/O port A,C & D and put SMC in NMSI mode, connect
   *  the SCC to the appropriate BRG.
   *
   *  SCC2 TxD is shared with port A bit 12
   *  SCC2 RxD is shared with port A bit 13
   *  SCC1 TxD is shared with port A bit 14
   *  SCC1 RxD is shared with port A bit 15
   *  SCC4 DCD is shared with port C bit 4
   *  SCC4 CTS is shared with port C bit 5
   *  SCC3 DCD is shared with port C bit 6
   *  SCC3 CTS is shared with port C bit 7
   *  SCC2 DCD is shared with port C bit 8
   *  SCC2 CTS is shared with port C bit 9
   *  SCC1 DCD is shared with port C bit 10
   *  SCC1 CTS is shared with port C bit 11
   *  SCC2 RTS is shared with port C bit 14
   *  SCC1 RTS is shared with port C bit 15
   *  SCC4 RTS is shared with port D bit 6
   *  SCC3 RTS is shared with port D bit 7
   *  SCC4 TxD is shared with port D bit 8
   *  SCC4 RxD is shared with port D bit 9
   *  SCC3 TxD is shared with port D bit 10
   *  SCC3 RxD is shared with port D bit 11
   */
  switch (minor) {
    case SCC2_MINOR:
      sccparms = &m8xx.scc2p;
      sccregs = &m8xx.scc2;
      
      m8xx.papar |=  0x000C;        /* PA12 & PA13 are dedicated peripheral pins */
      m8xx.padir &= ~0x000C;        /* PA13 & PA12 must not drive the UART lines */
      m8xx.paodr &= ~0x000C;        /* PA12 & PA13 are not open drain */
      m8xx.pcpar |=  0x0002;        /* PC14 is SCC2 RTS */
      m8xx.pcpar &= ~0x00C0;        /* PC8 & PC9 are SCC2 DCD and CTS */
      m8xx.pcdir &= ~0x00C2;        /* PC8, PC9 & PC14 must not drive the UART lines */
      m8xx.pcso  |=  0x00C0;        /* Enable DCD and CTS inputs */
      
      m8xx.sicr &= 0xFFFF00FF;      /* Clear TCS2 & RCS2, GR2=no grant, SC2=NMSI mode */
      m8xx.sicr |= (brg<<11) | (brg<<8); /* TCS2 = RCS2 = brg */
      break;

#ifdef mpc860
    case SCC3_MINOR:
      sccparms = &m8xx.scc3p;
      sccregs = &m8xx.scc3;
      
      m8xx.pcpar &= ~0x0300;        /* PC6 & PC7 are SCC3 DCD and CTS */
      m8xx.pcdir &= ~0x0300;        /* PC6 & PC7 must not drive the UART lines */
      m8xx.pcso  |=  0x0300;        /* Enable DCD and CTS inputs */
      m8xx.pdpar |=  0x0130;        /* PD7, PD10 & PD11 are dedicated peripheral pins */
      
      m8xx.sicr &= 0xFF00FFFF;      /* Clear TCS3 & RCS3, GR3=no grant, SC3=NMSI mode */
      m8xx.sicr |= (brg<<19) | (brg<<16); /* TCS3 = RCS3 = brg */
      break;

    case SCC4_MINOR:
      sccparms = &m8xx.scc4p;
      sccregs = &m8xx.scc4;
      
      m8xx.pcpar &= ~0x0C00;        /* PC4 & PC5 are SCC4 DCD and CTS */
      m8xx.pcdir &= ~0x0C00;        /* PC4 & PC5 must not drive the UART lines */
      m8xx.pcso  |=  0x0C00;        /* Enable DCD and CTS inputs */
      m8xx.pdpar |=  0x02C0;        /* PD6, PD8 & PD9 are dedicated peripheral pins */
       
      m8xx.sicr &= 0x00FFFFFF;      /* Clear TCS4 & RCS4, GR4=no grant, SC4=NMSI mode */
      m8xx.sicr |= (brg<<27) | (brg<<24); /* TCS4 = RCS4 = brg */
      break;
#endif
  }

  /*
   *  Set up SDMA
   */
  m8xx.sdcr = 0x01;                 /* as per section 16.10.2.1 MPC821UM/AD */

  /*
   *  Set up the SCC parameter RAM.
   */
  sccparms->rbase = (char *)RxBd[minor] - (char *)&m8xx;
  sccparms->tbase = (char *)TxBd[minor] - (char *)&m8xx;

  sccparms->rfcr = M8xx_RFCR_MOT | M8xx_RFCR_DMA_SPACE(0);
  sccparms->tfcr = M8xx_TFCR_MOT | M8xx_TFCR_DMA_SPACE(0);
  if ( mbx8xx_console_use_maximum_buffer_size() )
    sccparms->mrblr = RXBUFSIZE;    /* Maximum Rx buffer size */
  else
    sccparms->mrblr = 1;            /* Maximum Rx buffer size */
  sccparms->un.uart.max_idl = 10;   /* Set nb of idle chars to close buffer */
  sccparms->un.uart.brkcr = 0;      /* Set nb of breaks to send for STOP Tx */

  sccparms->un.uart.parec = 0;      /* Clear parity error counter */
  sccparms->un.uart.frmec = 0;      /* Clear framing error counter */
  sccparms->un.uart.nosec = 0;      /* Clear noise counter */
  sccparms->un.uart.brkec = 0;      /* Clear break counter */

  sccparms->un.uart.uaddr[0] = 0;   /* Not in multidrop mode, so clear */
  sccparms->un.uart.uaddr[1] = 0;   /* Not in multidrop mode, so clear */
  sccparms->un.uart.toseq  = 0;     /* Tx Out-Of-SEQuence--no XON/XOFF now */

  sccparms->un.uart.character[0] = 0x8000; /* Entry is invalid */
  sccparms->un.uart.character[1] = 0x8000; /* Entry is invalid */
  sccparms->un.uart.character[2] = 0x8000; /* Entry is invalid */
  sccparms->un.uart.character[3] = 0x8000; /* Entry is invalid */
  sccparms->un.uart.character[4] = 0x8000; /* Entry is invalid */
  sccparms->un.uart.character[5] = 0x8000; /* Entry is invalid */
  sccparms->un.uart.character[6] = 0x8000; /* Entry is invalid */
  sccparms->un.uart.character[7] = 0x8000; /* Entry is invalid */


  sccparms->un.uart.rccm = 0xc0ff;  /* No masking */

  /*
   * Set up the Receive Buffer Descriptor
   */
  RxBd[minor]->status = M8xx_BD_EMPTY | M8xx_BD_WRAP | M8xx_BD_INTERRUPT;
  RxBd[minor]->length = 0;
  RxBd[minor]->buffer = rxBuf[minor];

  /*
   * Setup the Transmit Buffer Descriptor
   */
  TxBd[minor]->status = M8xx_BD_WRAP;

 /*
   * Set up SCCx general and protocol-specific mode registers
   */
  sccregs->gsmr_h = 0x00000020;     /* RFW=low latency operation */
  sccregs->gsmr_l = 0x00028004;     /* TDCR=RDCR=16x clock mode, MODE=uart*/
  sccregs->scce = ~0;               /* Clear any pending event */
  sccregs->sccm = 0;                /* Mask all interrupt/event sources */
  sccregs->psmr = 0x3000;           /* Normal operation & mode, 1 stop bit,
                                       8 data bits, no parity */
  sccregs->dsr = 0x7E7E;            /* No fractional stop bits */
  sccregs->gsmr_l = 0x00028034;     /* ENT=enable Tx, ENR=enable Rx */

  /*
   *  Initialize the Rx and Tx with the new parameters.
   */
  switch (minor) {
    case SCC2_MINOR:
      m8xx_cp_execute_cmd (M8xx_CR_OP_INIT_RX_TX | M8xx_CR_CHAN_SCC2);
      break;

#ifdef mpc860
    case SCC3_MINOR:
      m8xx_cp_execute_cmd (M8xx_CR_OP_INIT_RX_TX | M8xx_CR_CHAN_SCC3);
      break;
    case SCC4_MINOR:
      m8xx_cp_execute_cmd (M8xx_CR_OP_INIT_RX_TX | M8xx_CR_CHAN_SCC4);
      break;
#endif
  }
#if NVRAM_CONFIGURE == 1
  if ( (nvram->console_mode & 0x06) == 0x02 ) {
    switch (minor) {
      case SCC2_MINOR:
        rtems_interrupt_catch (m8xx_scc2_interrupt_handler,
                               PPC_IRQ_CPM_SCC2,
                               &old_handler[minor]);

        sccregs->sccm = 3;            /* Enable SCC2 Rx & Tx interrupts */
        m8xx.cimr |= 1UL <<  29;      /* Enable SCC2 interrupts */
        break;

#ifdef mpc860
      case SCC3_MINOR:
        rtems_interrupt_catch (m8xx_scc3_interrupt_handler,
                               PPC_IRQ_CPM_SCC3,
                               &old_handler[minor]);

        sccregs->sccm = 3;            /* Enable SCC2 Rx & Tx interrupts */
        m8xx.cimr |= 1UL <<  28;      /* Enable SCC2 interrupts */
        break;
      
      case SCC4_MINOR:
        rtems_interrupt_catch (m8xx_scc4_interrupt_handler,
                               PPC_IRQ_CPM_SCC4,
                               &old_handler[minor]);

        sccregs->sccm = 3;            /* Enable SCC2 Rx & Tx interrupts */
        m8xx.cimr |= 1UL <<  27;      /* Enable SCC2 interrupts */
        break;
#endif /* mpc860 */
    }
  }
  
#else /* NVRAM_CONFIGURE != 1 */

#if UARTS_IO_MODE == 1
  switch (minor) {
    case SCC2_MINOR:
      rtems_interrupt_catch (m8xx_scc2_interrupt_handler,
                             PPC_IRQ_CPM_SCC2,
                             &old_handler[minor]);

      sccregs->sccm = 3;            /* Enable SCC2 Rx & Tx interrupts */
      m8xx.cimr |= 1UL <<  29;      /* Enable SCC2 interrupts */
      break;

#ifdef mpc860
    case SCC3_MINOR:
      rtems_interrupt_catch (m8xx_scc3_interrupt_handler,
                             PPC_IRQ_CPM_SCC3,
                             &old_handler[minor]);

      sccregs->sccm = 3;            /* Enable SCC2 Rx & Tx interrupts */
      m8xx.cimr |= 1UL <<  28;      /* Enable SCC2 interrupts */
      break;
      
    case SCC4_MINOR:
      rtems_interrupt_catch (m8xx_scc4_interrupt_handler,
                             PPC_IRQ_CPM_SCC4,
                             &old_handler[minor]);

      sccregs->sccm = 3;            /* Enable SCC2 Rx & Tx interrupts */
      m8xx.cimr |= 1UL <<  27;      /* Enable SCC2 interrupts */
      break;
#endif /* mpc860 */
  }
#endif /* UARTS_IO_MODE */

#endif /* NVRAM_CONFIGURE */
}


void
m8xx_uart_smc_initialize (int minor)
{
  unsigned char brg;
  volatile m8xxSMCparms_t *smcparms = 0;
  volatile m8xxSMCRegisters_t *smcregs = 0;

  /*
   * Check that minor number is valid
   */
  if ( (minor < SMC1_MINOR) || (minor > SMC2_MINOR) ) 
    return;

  /* Get the simode clock source bit values for 9600 bps */
  brg = m8xx_get_brg_clk(9600);

  /*
   * Allocate buffer descriptors
   */
  RxBd[minor] = m8xx_bd_allocate (1);
  TxBd[minor] = m8xx_bd_allocate (1);

  /*
   *  Get the address of the parameter RAM for the specified port,
   *  configure I/O port B and put SMC in NMSI mode, connect the
   *  SMC to the appropriate BRG.
   *
   *  SMC2 RxD is shared with port B bit 20
   *  SMC2 TxD is shared with port B bit 21
   *  SMC1 RxD is shared with port B bit 24
   *  SMC1 TxD is shared with port B bit 25
   */
  switch (minor) {
    case SMC1_MINOR:
      smcparms = &m8xx.smc1p;
      smcregs = &m8xx.smc1;
      
      m8xx.pbpar |=  0x000000C0;    /* PB24 & PB25 are dedicated peripheral pins */
      m8xx.pbdir &= ~0x000000C0;    /* PB24 & PB25 must not drive UART lines */
      m8xx.pbodr &= ~0x000000C0;    /* PB24 & PB25 are not open drain */

      m8xx.simode &= 0xFFFF0FFF;    /* Clear SMC1CS & SMC1 for NMSI mode */
      m8xx.simode |= brg << 12;     /* SMC1CS = brg */
      break;

    case SMC2_MINOR:
      smcparms = &m8xx.smc2p;
      smcregs = &m8xx.smc2;
      
      m8xx.pbpar |=  0x00000C00;    /* PB20 & PB21 are dedicated peripheral pins */
      m8xx.pbdir &= ~0x00000C00;    /* PB20 & PB21 must not drive the UART lines */
      m8xx.pbodr &= ~0x00000C00;    /* PB20 & PB21 are not open drain */
      
      m8xx.simode &= 0x0FFFFFFF;    /* Clear SMC2CS & SMC2 for NMSI mode */
      m8xx.simode |= brg << 28;     /* SMC2CS = brg */
      break;
  }
  
  /*
   * Set up SMC1 parameter RAM common to all protocols
   */
  smcparms->rbase = (char *)RxBd[minor] - (char *)&m8xx;
  smcparms->tbase = (char *)TxBd[minor] - (char *)&m8xx;
  smcparms->rfcr = M8xx_RFCR_MOT | M8xx_RFCR_DMA_SPACE(0);
  smcparms->tfcr = M8xx_TFCR_MOT | M8xx_TFCR_DMA_SPACE(0);
#if NVRAM_CONFIGURE == 1
  if ( (nvram->console_mode & 0x06) == 0x02 )
    smcparms->mrblr = RXBUFSIZE;    /* Maximum Rx buffer size */
  else
    smcparms->mrblr = 1;            /* Maximum Rx buffer size */
#else
#if UARTS_IO_MODE == 1
  smcparms->mrblr = RXBUFSIZE;      /* Maximum Rx buffer size */
#else
  smcparms->mrblr = 1;              /* Maximum Rx buffer size */
#endif
#endif

  /*
   * Set up SMC1 parameter RAM UART-specific parameters
   */
  smcparms->un.uart.max_idl = 10;   /* Set nb of idle chars to close buffer */
  smcparms->un.uart.brkcr = 0;      /* Set nb of breaks to send for STOP Tx */
  smcparms->un.uart.brkec = 0;      /* Clear break counter */

  /*
   * Set up the Receive Buffer Descriptor
   */
  RxBd[minor]->status = M8xx_BD_EMPTY | M8xx_BD_WRAP | M8xx_BD_INTERRUPT;
  RxBd[minor]->length = 0;
  RxBd[minor]->buffer = rxBuf[minor];

  /*
   * Setup the Transmit Buffer Descriptor
   */
  TxBd[minor]->status = M8xx_BD_WRAP;

  /*
   * Set up SMCx general and protocol-specific mode registers
   */
  smcregs->smce = ~0;               /* Clear any pending events */
  smcregs->smcm = 0;                /* Enable SMC Rx & Tx interrupts */
  smcregs->smcmr = M8xx_SMCMR_CLEN(9) | M8xx_SMCMR_SM_UART;

  /*
   * Send "Init parameters" command
   */
  switch (minor) {
    case SMC1_MINOR:
      m8xx_cp_execute_cmd (M8xx_CR_OP_INIT_RX_TX | M8xx_CR_CHAN_SMC1);
      break;

    case SMC2_MINOR:
      m8xx_cp_execute_cmd (M8xx_CR_OP_INIT_RX_TX | M8xx_CR_CHAN_SMC2);
      break;
  }
  
  /*
   * Enable receiver and transmitter
   */
  smcregs->smcmr |= M8xx_SMCMR_TEN | M8xx_SMCMR_REN;
#if NVRAM_CONFIGURE == 1
  if ( (nvram->console_mode & 0x06) == 0x02 ) {
    switch (minor) {
      case SMC1_MINOR:
        rtems_interrupt_catch (m8xx_smc1_interrupt_handler,
                                    PPC_IRQ_CPM_SMC1,
                                    &old_handler[minor]);

        smcregs->smcm = 3;            /* Enable SMC1 Rx & Tx interrupts */
        m8xx.cimr |= 1UL <<  4;       /* Enable SMC1 interrupts */
        break;
      
      case SMC2_MINOR:
        rtems_interrupt_catch (m8xx_smc2_interrupt_handler,
                                    PPC_IRQ_CPM_SMC2,
                                    &old_handler[minor]);

        smcregs->smcm = 3;            /* Enable SMC2 Rx & Tx interrupts */
        m8xx.cimr |= 1UL <<  3;       /* Enable SMC2 interrupts */
        break;
    }
  }
#else
#if UARTS_IO_MODE == 1
  switch (minor) {
    case SMC1_MINOR:
      rtems_interrupt_catch (m8xx_smc1_interrupt_handler,
                                  PPC_IRQ_CPM_SMC1,
                                  &old_handler[minor]);

      smcregs->smcm = 3;            /* Enable SMC1 Rx & Tx interrupts */
      m8xx.cimr |= 1UL <<  4;       /* Enable SMC1 interrupts */
      break;
      
    case SMC2_MINOR:
      rtems_interrupt_catch (m8xx_smc2_interrupt_handler,
                                  PPC_IRQ_CPM_SMC2,
                                  &old_handler[minor]);

      smcregs->smcm = 3;            /* Enable SMC2 Rx & Tx interrupts */
      m8xx.cimr |= 1UL <<  3;       /* Enable SMC2 interrupts */
      break;
  }
#endif

#endif
}

void
m8xx_uart_initialize(void)
{
  int i;
  
  for (i=0; i < 4; i++) {
    brg_spd[i] = 0;
    brg_used[i] = 0;
  }
}


void 
m8xx_uart_interrupts_initialize(void)
{
#ifdef mpc860
  m8xx.cicr = 0x00E43F80;           /* SCaP=SCC1, SCbP=SCC2, SCcP=SCC3,
                                       SCdP=SCC4, IRL=1, HP=PC15, IEN=1 */
#else
  m8xx.cicr = 0x00043F80;           /* SCaP=SCC1, SCbP=SCC2, IRL=1, HP=PC15, IEN=1 */
#endif
  m8xx.simask |= M8xx_SIMASK_LVM1;  /* Enable level interrupts */
#ifdef EPPCBUG_SMC1
  simask_copy = m8xx.simask;
#endif
}


int
m8xx_uart_pollRead(
  int minor
)
{
  unsigned char c;

  if (RxBd[minor]->status & M8xx_BD_EMPTY) {
    return -1;
  }
  rtems_cache_invalidate_multiple_data_lines( 
    (const void *) RxBd[minor]->buffer,
    RxBd[minor]->length
  );
  c = ((char *)RxBd[minor]->buffer)[0];
  RxBd[minor]->status = M8xx_BD_EMPTY | M8xx_BD_WRAP;
  return c;
}


/*
 *  TODO: Get a free buffer and set it up.
 */
int 
m8xx_uart_write(
  int minor,
  const char *buf,
  int len
)
{
  rtems_cache_flush_multiple_data_lines( buf, len );
  TxBd[minor]->buffer = (char *) buf;
  TxBd[minor]->length = len;
  TxBd[minor]->status = M8xx_BD_READY | M8xx_BD_WRAP | M8xx_BD_INTERRUPT;
  return 0;
}
  

int
m8xx_uart_pollWrite(
  int minor,
  const char *buf,
  int len
)
{
  while (len--) {
    while (TxBd[minor]->status & M8xx_BD_READY)
      continue;
    txBuf[minor] = *buf++;
    rtems_cache_flush_multiple_data_lines( (void *)&txBuf[minor], 1 );
    TxBd[minor]->buffer = &txBuf[minor];
    TxBd[minor]->length = 1;
    TxBd[minor]->status = M8xx_BD_READY | M8xx_BD_WRAP;
  }
  return 0;
}

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