summaryrefslogblamecommitdiffstats
path: root/bsps/powerpc/mpc55xxevb/console/console-esci.c
blob: 52cde2e46d8fd376e26eff003c79f3c86942fddf (plain) (tree)
1
2
3
4
5
6
7
8
9

                                           






                                      
                                                         
  



















                                                                              




                             
                      







                                                
   

















                                












































































































































                                                                              
                                                                      











































































                                                                       
                                                                  
                
                                               



                                                         
                                                     










                                         
                                                      









































                                                                   

                                                                    
 


                                      
















                                                          
/* SPDX-License-Identifier: BSD-2-Clause */

/**
 * @file
 *
 * @brief Console ESCI implementation.
 */

/*
 * Copyright (C) 2008, 2012 embedded brains GmbH & Co. KG
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <bsp/console-esci.h>

#include <bsp.h>
#include <bsp/fatal.h>
#include <bsp/irq.h>

#ifdef MPC55XX_HAS_ESCI

mpc55xx_esci_context mpc55xx_esci_devices [] = {
  {
    .regs = &ESCI_A,
    .irq = MPC55XX_IRQ_ESCI(0)
  }
  #ifdef ESCI_B
    , {
      .regs = &ESCI_B,
      .irq = MPC55XX_IRQ_ESCI(1)
    }
  #endif
  #ifdef ESCI_C
    , {
      .regs = &ESCI_C,
      .irq = MPC55XX_IRQ_ESCI(2)
    }
  #endif
  #ifdef ESCI_D
    , {
      .regs = &ESCI_D,
      .irq = MPC55XX_IRQ_ESCI(3)
    }
  #endif
};

static void mpc55xx_esci_poll_write(int minor, char c)
{
  mpc55xx_esci_context *self = console_generic_get_context(minor);
  const union ESCI_SR_tag clear_tdre = { .B = { .TDRE = 1 } };
  volatile struct ESCI_tag *regs = self->regs;
  rtems_interrupt_level level;
  bool done = false;
  bool wait_for_transmit_done = false;

  rtems_interrupt_disable(level);
  if (self->transmit_nest_level == 0) {
    union ESCI_CR1_tag cr1 = { .R = regs->CR1.R };

    if (cr1.B.TIE != 0) {
      cr1.B.TIE = 0;
      regs->CR1.R = cr1.R;
      wait_for_transmit_done = !self->transmit_in_progress;
      self->transmit_nest_level = 1;
    }
  } else {
    ++self->transmit_nest_level;
  }
  rtems_interrupt_enable(level);

  while (!done) {
    rtems_interrupt_disable(level);
    bool tx = self->transmit_in_progress;
    if (!tx || (tx && regs->SR.B.TDRE)) {
      regs->SR.R = clear_tdre.R;
      regs->DR.B.D = c;
      self->transmit_in_progress = true;
      done = true;
    }
    rtems_interrupt_enable(level);
  }

  done = false;
  while (!done) {
    rtems_interrupt_disable(level);
    if (wait_for_transmit_done) {
      if (regs->SR.B.TDRE) {
        regs->SR.R = clear_tdre.R;
        self->transmit_in_progress = false;
        done = true;
      }
    } else {
      done = true;
    }

    if (done && self->transmit_nest_level > 0) {
      --self->transmit_nest_level;

      if (self->transmit_nest_level == 0) {
        union ESCI_CR1_tag cr1 = { .R = regs->CR1.R };

        cr1.B.TIE = 1;
        regs->CR1.R = cr1.R;
      }
    }
    rtems_interrupt_enable(level);
  }
}

static inline void mpc55xx_esci_interrupts_clear_and_enable(
  mpc55xx_esci_context *self
)
{
  volatile struct ESCI_tag *regs = self->regs;
  union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
  rtems_interrupt_level level;

  rtems_interrupt_disable(level);
  cr1.R = regs->CR1.R;
  cr1.B.RIE = 1;
  cr1.B.TIE = 1;
  regs->CR1.R = cr1.R;
  regs->SR.R = regs->SR.R;
  rtems_interrupt_enable(level);
}

static inline void mpc55xx_esci_interrupts_disable(mpc55xx_esci_context *self)
{
  volatile struct ESCI_tag *regs = self->regs;
  union ESCI_CR1_tag cr1 = MPC55XX_ZERO_FLAGS;
  rtems_interrupt_level level;

  rtems_interrupt_disable(level);
  cr1.R = regs->CR1.R;
  cr1.B.RIE = 0;
  cr1.B.TIE = 0;
  regs->CR1.R = cr1.R;
  rtems_interrupt_enable(level);
}

static void mpc55xx_esci_interrupt_handler(void *arg)
{
  mpc55xx_esci_context *self = arg;
  volatile struct ESCI_tag *regs = self->regs;
  union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
  union ESCI_SR_tag active = MPC55XX_ZERO_FLAGS;
  rtems_interrupt_level level;

  /* Status */
  sr.R = regs->SR.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 */
  rtems_interrupt_disable(level);
  regs->SR.R = active.R;
  self->transmit_in_progress = false;
  rtems_interrupt_enable(level);

  /* Enqueue */
  if (active.B.RDRF != 0) {
    char c = regs->DR.B.D;
    rtems_termios_enqueue_raw_characters(self->tty, &c, 1);
  }

  /* Dequeue */
  if (active.B.TDRE != 0) {
    rtems_termios_dequeue_characters(self->tty, 1);
  }
}

static int mpc55xx_esci_set_attributes(int minor, const struct termios *t)
{
  mpc55xx_esci_context *self = console_generic_get_context(minor);
  volatile struct ESCI_tag *regs = self->regs;
  union ESCI_CR1_tag cr1 = { .R = regs->CR1.R };
  union ESCI_CR2_tag cr2 = MPC55XX_ZERO_FLAGS;
  rtems_termios_baud_t br = rtems_termios_baud_to_number(t->c_ospeed);

  /* Enable module */
  cr2.B.MDIS = 0;

  /* Interrupts */
  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 */
  if (br > 0) {
    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 -1;
  }
  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 -1;
  }

  /* Disable LIN */
  regs->LCR.R = 0;

  /* Set control registers */
  regs->CR2.R = cr2.R;
  regs->CR1.R = cr1.R;

  return 0;
}

static int mpc55xx_esci_first_open(int major, int minor, void *arg)
{
  rtems_status_code sc = RTEMS_SUCCESSFUL;
  int rv = 0;
  mpc55xx_esci_context *self = console_generic_get_context(minor);
  struct rtems_termios_tty *tty = console_generic_get_tty_at_open(arg);

  self->tty = tty;

  rv = rtems_termios_set_initial_baud(tty, BSP_DEFAULT_BAUD_RATE);
  if (rv != 0) {
    bsp_fatal(MPC55XX_FATAL_CONSOLE_ESCI_BAUD);
  }

  rv = mpc55xx_esci_set_attributes(minor, &tty->termios);
  if (rv != 0) {
    bsp_fatal(MPC55XX_FATAL_CONSOLE_ESCI_ATTRIBUTES);
  }

  sc = mpc55xx_interrupt_handler_install(
    self->irq,
    "eSCI",
    RTEMS_INTERRUPT_UNIQUE,
    MPC55XX_INTC_DEFAULT_PRIORITY,
    mpc55xx_esci_interrupt_handler,
    self
  );
  if (sc != RTEMS_SUCCESSFUL) {
    bsp_fatal(MPC55XX_FATAL_CONSOLE_ESCI_IRQ_INSTALL);
  }

  mpc55xx_esci_interrupts_clear_and_enable(self);
  self->transmit_in_progress = false;

  return 0;
}

static int mpc55xx_esci_last_close(int major, int minor, void* arg)
{
  mpc55xx_esci_context *self = console_generic_get_context(minor);

  mpc55xx_esci_interrupts_disable(self);
  self->tty = NULL;

  return 0;
}

static int mpc55xx_esci_poll_read(int minor)
{
  mpc55xx_esci_context *self = console_generic_get_context(minor);
  volatile struct ESCI_tag *regs = self->regs;
  union ESCI_SR_tag sr = MPC55XX_ZERO_FLAGS;
  rtems_interrupt_level level;
  int c = -1;

  rtems_interrupt_disable(level);
  if (regs->SR.B.RDRF != 0) {
    /* Clear flag */
    sr.B.RDRF = 1;
    regs->SR.R = sr.R;

    /* Read */
    c = regs->DR.B.D;
  }
  rtems_interrupt_enable(level);

  return c;
}

static int mpc55xx_esci_write(int minor, const char *out, size_t n)
{
  if (n > 0) {
    mpc55xx_esci_context *self = console_generic_get_context(minor);

    self->regs->DR.B.D = out [0];
    self->transmit_in_progress = true;
  }

  return 0;
}

const console_generic_callbacks mpc55xx_esci_callbacks = {
  .termios_callbacks = {
    .firstOpen = mpc55xx_esci_first_open,
    .lastClose = mpc55xx_esci_last_close,
    .write = mpc55xx_esci_write,
    .setAttributes = mpc55xx_esci_set_attributes,
    .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN
  },
  .poll_read = mpc55xx_esci_poll_read,
  .poll_write = mpc55xx_esci_poll_write
};

#endif /* MPC55XX_HAS_ESCI */