summaryrefslogtreecommitdiffstats
path: root/bsps/powerpc/ss555/dev/console-generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/powerpc/ss555/dev/console-generic.c')
-rw-r--r--bsps/powerpc/ss555/dev/console-generic.c317
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));
+ }
+}