/* * 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 * and Charles-Antoine Gauthier * 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.org/license/LICENSE. */ #include #include #include #include #include #include /* for printk */ #include #include #include /* * 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 */ 0, /* local mode flags */ 0, /* line discipline */ { 0 }, /* control characters */ CS8 | CREAD | CLOCAL | B9600, /* control mode flags */ }; 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 ) { if (len > 0) { 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_ospeed ); 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); } } } static void m5xx_sci_nop(const rtems_irq_connect_data* ptr) { } static 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)); } }