Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:59

0001 /*
0002  *  General Serial I/O functions.
0003  *
0004  *  This file contains the functions for performing serial I/O.  The actual
0005  *  system calls (console_*) should be in the BSP part of the source tree.
0006  *  That way different BSPs can use whichever SCI they wish for /dev/console.
0007  *
0008  *  On-chip resources used:
0009  *   resource   minor                note
0010  *  SCI1      0
0011  *  SCI2      1
0012  */
0013 
0014 /*
0015  *  MPC5xx port sponsored by Defence Research and Development Canada - Suffield
0016  *  Copyright (C) 2004, Real-Time Systems Inc. (querbach@realtime.bc.ca)
0017  *
0018  *  Derived from
0019  *    c/src/lib/libcpu/powerpc/mpc8xx/console_generic/console_generic.c:
0020  *  Author: Jay Monkman (jmonkman@frasca.com)
0021  *  Copyright (C) 1998 by Frasca International, Inc.
0022  *
0023  *  Derived from c/src/lib/libbsp/m68k/gen360/console/console.c written by:
0024  *    Eric Norum <eric@norum.ca>
0025  *
0026  *  COPYRIGHT (c) 1989-1998.
0027  *  On-Line Applications Research Corporation (OAR).
0028  *
0029  *  Modifications by Darlene Stewart <Darlene.Stewart@iit.nrc.ca>
0030  *  and Charles-Antoine Gauthier <charles.gauthier@iit.nrc.ca>
0031  *  Copyright (c) 1999, National Research Council of Canada
0032  *
0033  *  The license and distribution terms for this file may be
0034  *  found in the file LICENSE in this distribution or at
0035  *  http://www.rtems.org/license/LICENSE.
0036  */
0037 
0038 #include <stdlib.h>
0039 #include <unistd.h>
0040 #include <termios.h>
0041 #include <rtems.h>
0042 #include <rtems/libio.h>
0043 #include <rtems/bspIo.h>   /* for printk */
0044 #include <mpc5xx.h>
0045 #include <mpc5xx/console.h>
0046 #include <bsp/irq.h>
0047 
0048 
0049 /*
0050  * SCI port descriptor table.
0051  */
0052 typedef struct
0053 {
0054   volatile m5xxSCIRegisters_t *regs;    /* hardware registers */
0055   struct rtems_termios_tty *ttyp;   /* termios data for this port */
0056 } sci_desc;
0057 
0058 static sci_desc sci_descs[] = {
0059   { &imb.qsmcm.sci1, 0 },       /* SCI 1 */
0060   { &imb.qsmcm.sci2, 0 },       /* SCI 2 */
0061 };
0062 
0063 /*
0064  * Number of SCI port initialization calls made so far.  Used to avoid
0065  * installing the common interrupt handler more than once.
0066  */
0067 int init_calls = 0;
0068 
0069 /*
0070  * Default configuration.
0071  */
0072 static struct termios default_termios = {
0073   0,                    /* input mode flags */
0074   0,                    /* output mode flags */
0075   0,                    /* local mode flags */
0076   0,                    /* line discipline */
0077   { 0 },                /* control characters */
0078   CS8 | CREAD | CLOCAL | B9600,     /* control mode flags */
0079 };
0080 
0081 
0082 extern uint32_t bsp_clock_speed;
0083 
0084 /*
0085  * Termios callback functions
0086  */
0087 
0088 int
0089 m5xx_uart_firstOpen(
0090   int major,
0091   int minor,
0092   void *arg
0093 )
0094 {
0095   rtems_libio_open_close_args_t *args = arg;
0096   sci_desc* desc = &sci_descs[minor];
0097   struct rtems_termios_tty *tty = args->iop->data1;
0098 
0099   desc->ttyp = tty;             /* connect tty */
0100   if ( tty->device.outputUsesInterrupts == TERMIOS_IRQ_DRIVEN)
0101     desc->regs->sccr1 |= QSMCM_SCI_RIE;     /* enable rx interrupt */
0102 
0103   return RTEMS_SUCCESSFUL;
0104 }
0105 
0106 int
0107 m5xx_uart_lastClose(
0108   int major,
0109   int minor,
0110   void* arg
0111 )
0112 {
0113   sci_desc* desc = &sci_descs[minor];
0114 
0115   desc->regs->sccr1 &= ~(QSMCM_SCI_RIE | QSMCM_SCI_TIE);  /* disable all */
0116   desc->ttyp = NULL;                      /* disconnect tty */
0117 
0118   return RTEMS_SUCCESSFUL;
0119 }
0120 
0121 int
0122 m5xx_uart_pollRead(
0123   int minor
0124 )
0125 {
0126   volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;
0127   int c = -1;
0128 
0129   if ( regs ) {
0130     while ( (regs->scsr & QSMCM_SCI_RDRF) == 0 )
0131       ;
0132     c = regs->scdr;
0133   }
0134 
0135   return c;
0136 }
0137 
0138 ssize_t m5xx_uart_write(
0139   int minor,
0140   const char *buf,
0141   size_t len
0142 )
0143 {
0144   if (len > 0) {
0145     volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;
0146 
0147     regs->scdr = *buf;          /* start transmission */
0148     regs->sccr1 |= QSMCM_SCI_TIE;       /* enable interrupt */
0149   }
0150 
0151   return 0;
0152 }
0153 
0154 ssize_t m5xx_uart_pollWrite(
0155   int minor,
0156   const char *buf,
0157   size_t len
0158 )
0159 {
0160   volatile m5xxSCIRegisters_t *regs = sci_descs[minor].regs;
0161   size_t retval = len;
0162 
0163   while ( len-- ) {
0164     while ( (regs->scsr & QSMCM_SCI_TDRE) == 0 )
0165       ;
0166     regs->scdr = *buf++;
0167   }
0168 
0169   return retval;
0170 }
0171 
0172 int
0173 m5xx_uart_setAttributes(
0174   int minor,
0175   const struct termios *t
0176 )
0177 {
0178   uint16_t sccr0 = sci_descs[minor].regs->sccr0;
0179   uint16_t sccr1 = sci_descs[minor].regs->sccr1;
0180   int baud;
0181 
0182   /*
0183    * Check that port number is valid
0184    */
0185   if ( (minor < SCI1_MINOR) || (minor > SCI2_MINOR) )
0186     return RTEMS_INVALID_NUMBER;
0187 
0188   /* Baud rate */
0189   baud = rtems_termios_baud_to_number( t->c_ospeed );
0190   if (baud > 0) {
0191     sccr0 &= ~QSMCM_SCI_BAUD(-1);
0192     sccr0 |= QSMCM_SCI_BAUD((bsp_clock_speed + (16 * baud)) / (32 * baud));
0193   }
0194 
0195   /* Number of data bits -- not available with MPC5xx SCI */
0196   switch ( t->c_cflag & CSIZE ) {
0197     case CS5:     break;
0198     case CS6:     break;
0199     case CS7:     break;
0200     case CS8:     break;
0201   }
0202 
0203   /* Stop bits -- not easily available with MPC5xx SCI */
0204   if ( t->c_cflag & CSTOPB ) {
0205     /* Two stop bits */
0206   } else {
0207     /* One stop bit */
0208   }
0209 
0210   /* Parity */
0211   if ( t->c_cflag & PARENB )
0212     sccr1 |= QSMCM_SCI_PE;
0213   else
0214     sccr1 &= ~QSMCM_SCI_PE;
0215 
0216   if ( t->c_cflag & PARODD )
0217     sccr1 |= QSMCM_SCI_PT;
0218   else
0219     sccr1 &= ~QSMCM_SCI_PT;
0220 
0221   /* Transmitter and receiver enable */
0222   sccr1 |= QSMCM_SCI_TE;
0223   if ( t->c_cflag & CREAD )
0224     sccr1 |= QSMCM_SCI_RE;
0225   else
0226     sccr1 &= ~QSMCM_SCI_RE;
0227 
0228   /* Write hardware registers */
0229   sci_descs[minor].regs->sccr0 = sccr0;
0230   sci_descs[minor].regs->sccr1 = sccr1;
0231 
0232   return RTEMS_SUCCESSFUL;
0233 }
0234 
0235 
0236 /*
0237  * Interrupt handling.
0238  */
0239 static void
0240 m5xx_sci_interrupt_handler (rtems_irq_hdl_param unused)
0241 {
0242   int minor;
0243 
0244   for ( minor = 0; minor < NUM_PORTS; minor++ ) {
0245     sci_desc *desc = &sci_descs[minor];
0246     int sccr1 = desc->regs->sccr1;
0247     int scsr = desc->regs->scsr;
0248 
0249     /*
0250      * Character received?
0251      */
0252     if ((sccr1 & QSMCM_SCI_RIE) && (scsr & QSMCM_SCI_RDRF)) {
0253       char c = desc->regs->scdr;
0254       rtems_termios_enqueue_raw_characters(desc->ttyp, &c, 1);
0255     }
0256     /*
0257      * Transmitter empty?
0258      */
0259     if ((sccr1 & QSMCM_SCI_TIE) && (scsr & QSMCM_SCI_TDRE)) {
0260       desc->regs->sccr1 &= ~QSMCM_SCI_TIE;
0261       rtems_termios_dequeue_characters (desc->ttyp, 1);
0262     }
0263   }
0264 }
0265 
0266 static void m5xx_sci_nop(const rtems_irq_connect_data* ptr)
0267 {
0268 }
0269 
0270 static int m5xx_sci_isOn(const rtems_irq_connect_data* ptr)
0271 {
0272   return 1;
0273 }
0274 
0275 /*
0276  * Basic initialization.
0277  */
0278 
0279 void
0280 m5xx_uart_initialize (int minor)
0281 {
0282   /*
0283    * Check that minor number is valid.
0284    */
0285   if ( (minor < SCI1_MINOR) || (minor > SCI2_MINOR) )
0286     return;
0287 
0288   /*
0289    * Configure and enable receiver and transmitter.
0290    */
0291   m5xx_uart_setAttributes(minor, &default_termios);
0292 
0293   /*
0294    * Connect interrupt if not yet done.
0295    */
0296   if ( init_calls++ == 0 ) {
0297     rtems_irq_connect_data irq_data;
0298 
0299     irq_data.name = CPU_IRQ_SCI;
0300     irq_data.hdl  = m5xx_sci_interrupt_handler;
0301     irq_data.on   = m5xx_sci_nop;   /* can't enable both channels here */
0302     irq_data.off  = m5xx_sci_nop;   /* can't disable both channels here */
0303     irq_data.isOn = m5xx_sci_isOn;
0304 
0305     if (!CPU_install_rtems_irq_handler (&irq_data)) {
0306       printk("Unable to connect SCI Irq handler\n");
0307       rtems_fatal_error_occurred(1);
0308     }
0309 
0310     imb.qsmcm.qdsci_il =        /* set interrupt level in port */
0311       QSMCM_ILDSCI(CPU_irq_level_from_symbolic_name(CPU_IRQ_SCI));
0312   }
0313 }