diff options
Diffstat (limited to 'bsps/powerpc/ss555/dev/console-generic.c')
-rw-r--r-- | bsps/powerpc/ss555/dev/console-generic.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/bsps/powerpc/ss555/dev/console-generic.c b/bsps/powerpc/ss555/dev/console-generic.c new file mode 100644 index 0000000000..7e1629b714 --- /dev/null +++ b/bsps/powerpc/ss555/dev/console-generic.c @@ -0,0 +1,317 @@ +/* + * 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.org/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 */ + 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)); + } +} |