summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/mpc55xx/esci/esci.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/powerpc/mpc55xx/esci/esci.c')
-rw-r--r--c/src/lib/libcpu/powerpc/mpc55xx/esci/esci.c659
1 files changed, 659 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/powerpc/mpc55xx/esci/esci.c b/c/src/lib/libcpu/powerpc/mpc55xx/esci/esci.c
new file mode 100644
index 0000000000..3f1c228aa7
--- /dev/null
+++ b/c/src/lib/libcpu/powerpc/mpc55xx/esci/esci.c
@@ -0,0 +1,659 @@
+/**
+ * @file
+ *
+ * @ingroup mpc55xx_esci
+ *
+ * @brief Source file for the Enhanced Serial Communication Interface (eSCI).
+ */
+
+/*
+ * Copyright (c) 2008
+ * Embedded Brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * rtems@embedded-brains.de
+ *
+ * The license and distribution terms for this file may be found in the file
+ * LICENSE in this distribution or at http://www.rtems.com/license/LICENSE.
+ */
+
+/* Include order is important */
+#include <mpc55xx/regs.h>
+#include <mpc55xx/esci.h>
+#include <bsp/irq.h>
+
+#include <unistd.h>
+#include <termios.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/console.h>
+#include <rtems/bspIo.h>
+
+#define RTEMS_STATUS_CHECKS_USE_PRINTK
+
+#include <rtems/status-checks.h>
+
+/* Evil define conflicts */
+#define TERMIOS_CR1 CR1
+#undef CR1
+#define TERMIOS_CR2 CR2
+#undef CR2
+
+#define MPC55XX_ESCI_IRQ_PRIORITY MPC55XX_INTC_MIN_PRIORITY
+
+#define MPC55XX_ESCI_IS_MINOR_INVALD(minor) ((minor) < 0 || (minor) >= MPC55XX_ESCI_NUMBER)
+
+#define MPC55XX_ESCI_USE_INTERRUPTS(e) (!(e)->console && (e)->use_interrupts)
+
+/**
+ * @brief eSCI driver table.
+ */
+mpc55xx_esci_driver_entry mpc55xx_esci_driver_table [MPC55XX_ESCI_NUMBER] = { {
+ .regs = &ESCI_A,
+ .device_name = "/dev/tty1",
+ .use_termios = 1,
+ .use_interrupts = 0,
+ .console = 1,
+ .tty = NULL,
+ .irq_number = 146
+ }, {
+ .regs = &ESCI_B,
+ .device_name = "/dev/tty2",
+ .use_termios = 1,
+ .use_interrupts = 0,
+ .console = 0,
+ .tty = NULL,
+ .irq_number = 149
+ }
+};
+
+/**
+ * @brief Default termios configuration.
+ */
+static const struct termios mpc55xx_esci_termios_default = {
+ 0,
+ 0,
+ CS8 | CREAD | CLOCAL | B115200,
+ 0,
+ 0,
+ { 0 }
+};
+
+/**
+ * @name Low-Level
+ * @{
+ */
+
+/**
+ * @brief Reads one character from the receive register.
+ *
+ * @note Waits for the receive data register full flag.
+ */
+static inline uint8_t mpc55xx_esci_read_char( mpc55xx_esci_driver_entry *e)
+{
+ volatile union ESCI_SR_tag *status = &e->regs->SR;
+ volatile union ESCI_DR_tag *data = &e->regs->DR;
+ union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
+
+ while (status->B.RDRF == 0) {
+ /* Wait */
+ }
+
+ /* Clear flag */
+ sr.B.RDRF = 1;
+ status->R = sr.R;
+
+ /* Read */
+ return data->B.D;
+}
+
+/**
+ * @brief Writes the character @a c to the transmit register.
+ *
+ * @note Waits for the transmit data register empty flag.
+ */
+static inline void mpc55xx_esci_write_char( mpc55xx_esci_driver_entry *e, uint8_t c)
+{
+ volatile union ESCI_SR_tag *status = &e->regs->SR;
+ volatile union ESCI_DR_tag *data = &e->regs->DR;
+ union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
+
+ while (status->B.TDRE == 0) {
+ /* Wait */
+ }
+
+ /* Clear flag */
+ sr.B.TDRE = 1;
+ status->R = sr.R;
+
+ /* Write */
+ data->B.D = c;
+}
+
+/** @} */
+
+/**
+ * @name Termios Support
+ * @{
+ */
+
+/**
+ * @brief Opens port @a minor.
+ *
+ * @return Status code.
+ */
+static int mpc55xx_esci_termios_first_open( int major, int minor, void *arg)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+ union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
+ struct rtems_termios_tty *tty = ((rtems_libio_open_close_args_t *) arg)->iop->data1;
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ /* Connect TTY */
+ e->tty = tty;
+
+ /* Enable interrupts */
+ if (MPC55XX_ESCI_USE_INTERRUPTS( e)) {
+ cr1.R = e->regs->CR1.R;
+ cr1.B.RIE = 1;
+ cr1.B.TIE = 1;
+ e->regs->CR1.R = cr1.R;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/**
+ * @brief Closes port @a minor.
+ *
+ * @return Status code.
+ */
+static int mpc55xx_esci_termios_last_close( int major, int minor, void* arg)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+ union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ /* Disable interrupts */
+ cr1.R = e->regs->CR1.R;
+ cr1.B.RIE = 0;
+ cr1.B.TIE = 0;
+ e->regs->CR1.R = cr1.R;
+
+ /* Disconnect TTY */
+ e->tty = NULL;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/**
+ * @brief Reads one character from port @a minor.
+ *
+ * @return Returns the character or -1 on error.
+ */
+static int mpc55xx_esci_termios_poll_read( int minor)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return -1;
+ }
+
+ return (int) mpc55xx_esci_read_char( e);
+}
+
+/**
+ * @brief Writes @a n characters from @a out to port @a minor.
+ *
+ * @return Returns 0 on success or -1 otherwise.
+ */
+static int mpc55xx_esci_termios_poll_write( int minor, const char *out, int n)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+ int i = 0;
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return -1;
+ }
+
+ /* Write */
+ for (i = 0; i < n; ++i) {
+ mpc55xx_esci_write_char( e, out [i]);
+ if (out [i] == '\n') {
+ mpc55xx_esci_write_char( e, '\r');
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Writes one character from @a out to port @a minor.
+ *
+ * @return Returns always 0.
+ *
+ * @note The buffer @a out has to provide at least one character.
+ * This function assumes that the transmit data register is empty.
+ */
+static int mpc55xx_esci_termios_write( int minor, const char *out, int n)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+
+ /* Write */
+ e->regs->DR.B.D = out [0];
+
+ return 0;
+}
+
+/**
+ * @brief Sets attributes of port @a minor according to termios attributes @a t.
+ *
+ * @return Status code.
+ */
+static int mpc55xx_esci_termios_set_attributes( int minor, const struct termios *t)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+ volatile struct ESCI_tag *regs = e->regs;
+ union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
+ union ESCI_CR2_tag cr2 = MPC55XX_ZERO_FLAGS;
+ unsigned br = 0;
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ /* Enable module */
+ cr2.B.MDIS = 0;
+
+ /* Interrupts */
+ if (MPC55XX_ESCI_USE_INTERRUPTS( e) && e->tty != NULL) {
+ cr1.B.RIE = 1;
+ cr1.B.TIE = 1;
+ } else {
+ cr1.B.RIE = 0;
+ cr1.B.TIE = 0;
+ }
+ cr1.B.TCIE = 0;
+ cr1.B.ILIE = 0;
+ cr2.B.IEBERR = 0;
+ cr2.B.ORIE = 0;
+ cr2.B.NFIE = 0;
+ cr2.B.FEIE = 0;
+ cr2.B.PFIE = 0;
+
+ /* Disable receiver wake-up standby */
+ cr1.B.RWU = 0;
+
+ /* Disable DMA channels */
+ cr2.B.RXDMA = 0;
+ cr2.B.TXDMA = 0;
+
+ /* Idle line type */
+ cr1.B.ILT = 0;
+
+ /* Disable loops */
+ cr1.B.LOOPS = 0;
+
+ /* Enable or disable receiver */
+ cr1.B.RE = (t->c_cflag & CREAD) ? 1 : 0;
+
+ /* Enable transmitter */
+ cr1.B.TE = 1;
+
+ /* Baud rate */
+ switch (t->c_cflag & CBAUD) {
+ case B50: br = 50; break;
+ case B75: br = 75; break;
+ case B110: br = 110; break;
+ case B134: br = 134; break;
+ case B150: br = 150; break;
+ case B200: br = 200; break;
+ case B300: br = 300; break;
+ case B600: br = 600; break;
+ case B1200: br = 1200; break;
+ case B1800: br = 1800; break;
+ case B2400: br = 2400; break;
+ case B4800: br = 4800; break;
+ case B9600: br = 9600; break;
+ case B19200: br = 19200; break;
+ case B38400: br = 38400; break;
+ case B57600: br = 57600; break;
+ case B115200: br = 115200; break;
+ case B230400: br = 230400; break;
+ case B460800: br = 460800; break;
+ default: br = 0; break;
+ }
+ if (br > 0) {
+ // FIXME, TODO
+ extern uint32_t bsp_clock_speed;
+
+ br = bsp_clock_speed / (16 * br);
+ br = (br > 8191) ? 8191 : br;
+ } else {
+ br = 0;
+ }
+ cr1.B.SBR = br;
+
+ /* Number of data bits */
+ if ((t->c_cflag & CSIZE) != CS8) {
+ return RTEMS_IO_ERROR;
+ }
+ cr1.B.M = 0;
+
+ /* Parity */
+ cr1.B.PE = (t->c_cflag & PARENB) ? 1 : 0;
+ cr1.B.PT = (t->c_cflag & PARODD) ? 1 : 0;
+
+ /* Stop bits */
+ if ( t->c_cflag & CSTOPB ) {
+ /* Two stop bits */
+ return RTEMS_IO_ERROR;
+ }
+
+ /* Set control registers */
+ regs->CR1.R = cr1.R;
+ regs->CR2.R = cr2.R;
+
+ /* Disable LIN */
+ regs->LCR.R = 0;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/**
+ * @brief Interrupt handler.
+ */
+static void mpc55xx_esci_termios_interrupt_handler( rtems_vector_number vector, void *arg)
+{
+ mpc55xx_esci_driver_entry *e = (mpc55xx_esci_driver_entry *) arg;
+ volatile union ESCI_SR_tag *status = &e->regs->SR;
+ volatile union ESCI_DR_tag *data = &e->regs->DR;
+ union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
+ union ESCI_SR_tag active = MPC55XX_ZERO_FLAGS;
+
+ /* Status */
+ sr.R = status->R;
+
+ /* Receive data register full? */
+ if (sr.B.RDRF != 0) {
+ active.B.RDRF = 1;
+ }
+
+ /* Transmit data register empty? */
+ if (sr.B.TDRE != 0) {
+ active.B.TDRE = 1;
+ }
+
+ /* Clear flags */
+ status->R = active.R;
+
+ /* Enqueue */
+ if (active.B.RDRF != 0) {
+ char c = data->B.D;
+ rtems_termios_enqueue_raw_characters( e->tty, &c, 1);
+ }
+
+ /* Dequeue */
+ if (active.B.TDRE != 0) {
+ rtems_termios_dequeue_characters( e->tty, 1);
+ }
+}
+
+/** @} */
+
+/**
+ * @brief Termios callbacks with interrupt support.
+ */
+static const rtems_termios_callbacks mpc55xx_esci_termios_callbacks = {
+ .firstOpen = mpc55xx_esci_termios_first_open,
+ .lastClose = mpc55xx_esci_termios_last_close,
+ .pollRead = NULL,
+ .write = mpc55xx_esci_termios_write,
+ .setAttributes = mpc55xx_esci_termios_set_attributes,
+ .stopRemoteTx = NULL,
+ .startRemoteTx = NULL,
+ .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN
+};
+
+/**
+ * @brief Termios callbacks with polling functions (no interrupts).
+ */
+static const rtems_termios_callbacks mpc55xx_esci_termios_callbacks_polled = {
+ .firstOpen = mpc55xx_esci_termios_first_open,
+ .lastClose = mpc55xx_esci_termios_last_close,
+ .pollRead = mpc55xx_esci_termios_poll_read,
+ .write = mpc55xx_esci_termios_poll_write,
+ .setAttributes = mpc55xx_esci_termios_set_attributes,
+ .stopRemoteTx = NULL,
+ .startRemoteTx = NULL,
+ .outputUsesInterrupts = TERMIOS_POLLED
+};
+
+/**
+ * @name Console Driver
+ * @{
+ */
+
+rtems_device_driver console_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int console_done = 0;
+ int termios_do_init = 1;
+ int i = 0;
+ mpc55xx_esci_driver_entry *e = NULL;
+
+ for (i = 0; i < MPC55XX_ESCI_NUMBER; ++i) {
+ e = &mpc55xx_esci_driver_table [i];
+ sc = rtems_io_register_name ( e->device_name, major, i);
+ CHECK_SC( sc, "Register IO device");
+ if (e->console) {
+ if (console_done) {
+ SYSLOG_WARNING( "Multiple console ports defined\n");
+ } else {
+ console_done = 1;
+ if (e->use_interrupts) {
+ SYSLOG_WARNING( "Cannot use interrupts for console port\n");
+ }
+ sc = rtems_io_register_name( CONSOLE_DEVICE_NAME, major, i);
+ CHECK_SC( sc, "Register IO device");
+ }
+ }
+ if (e->use_termios && termios_do_init) {
+ if (termios_do_init) {
+ termios_do_init = 0;
+ rtems_termios_initialize();
+ }
+ if (MPC55XX_ESCI_USE_INTERRUPTS( e)) {
+ sc = mpc55xx_interrupt_handler_install(
+ e->irq_number,
+ MPC55XX_ESCI_IRQ_PRIORITY,
+ "eSCI",
+ RTEMS_INTERRUPT_UNIQUE,
+ mpc55xx_esci_termios_interrupt_handler,
+ e
+ );
+ CHECK_SC( sc, "Install IRQ handler");
+ }
+ }
+ mpc55xx_esci_termios_set_attributes( minor, &mpc55xx_esci_termios_default);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_device_driver console_open( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int rv = 0;
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ if (e->use_termios) {
+ if (MPC55XX_ESCI_USE_INTERRUPTS( e)) {
+ sc = rtems_termios_open( major, minor, arg, &mpc55xx_esci_termios_callbacks);
+ } else {
+ sc = rtems_termios_open( major, minor, arg, &mpc55xx_esci_termios_callbacks_polled);
+ }
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+ rv = rtems_termios_set_initial_baud( e->tty, 115200);
+ if (rv < 0) {
+ return RTEMS_IO_ERROR;
+ }
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_device_driver console_close( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ if (e->use_termios) {
+ return rtems_termios_close( arg);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_device_driver console_read( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ if (e->use_termios) {
+ return rtems_termios_read( arg);
+ } else {
+ rtems_libio_rw_args_t *rw = (rtems_libio_rw_args_t *) arg;
+ uint32_t i = 0;
+ while (i < rw->count) {
+ rw->buffer [i] = mpc55xx_esci_read_char( e);
+ ++i;
+ }
+ rw->bytes_moved = i;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_device_driver console_write( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ if (e->use_termios) {
+ return rtems_termios_write( arg);
+ } else {
+ rtems_libio_rw_args_t *rw = (rtems_libio_rw_args_t *) arg;
+ uint32_t i = 0;
+ while (i < rw->count) {
+ mpc55xx_esci_write_char( e, rw->buffer [i]);
+ if (rw->buffer [i] == '\n') {
+ mpc55xx_esci_write_char( e, '\r');
+ }
+ ++i;
+ }
+ rw->bytes_moved = i;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_device_driver console_control( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [minor];
+
+ /* Check minor number */
+ if (MPC55XX_ESCI_IS_MINOR_INVALD( minor)) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ if (e->use_termios) {
+ return rtems_termios_ioctl( arg);
+ }
+
+ return RTEMS_NOT_DEFINED;
+}
+
+/** @} */
+
+/**
+ * @brief Port number for the BSP character output function.
+ *
+ * The correct value will be set by mpc55xx_esci_output_char_init().
+ */
+static unsigned mpc55xx_esci_output_char_minor = 0;
+
+/**
+ * @name BSP Character Output
+ * @{
+ */
+
+static void mpc55xx_esci_output_char( char c)
+{
+ mpc55xx_esci_driver_entry *e = &mpc55xx_esci_driver_table [mpc55xx_esci_output_char_minor];
+ mpc55xx_esci_write_char( e, c);
+ if (c == '\n') {
+ mpc55xx_esci_write_char( e, '\r');
+ }
+}
+
+static void mpc55xx_esci_output_char_nop( char c)
+{
+ /* Do nothing */
+}
+
+static void mpc55xx_esci_output_char_init( char c)
+{
+ int console_found = 0;
+ unsigned i = 0;
+ for (i = 0; i < MPC55XX_ESCI_NUMBER; ++i) {
+ if (mpc55xx_esci_driver_table [i].console) {
+ console_found = 1;
+ mpc55xx_esci_output_char_minor = i;
+ break;
+ }
+ }
+ if (console_found) {
+ BSP_output_char = mpc55xx_esci_output_char;
+ mpc55xx_esci_termios_set_attributes( mpc55xx_esci_output_char_minor, &mpc55xx_esci_termios_default);
+ mpc55xx_esci_output_char( c);
+ } else {
+ BSP_output_char = mpc55xx_esci_output_char_nop;
+ }
+}
+
+/** @} */
+
+BSP_output_char_function_type BSP_output_char = mpc55xx_esci_output_char_init;