forked from Imagelibrary/rtems
318 lines
7.0 KiB
C
318 lines
7.0 KiB
C
/*
|
|
* 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));
|
|
}
|
|
}
|