diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-19 06:28:01 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 13:08:32 +0200 |
commit | d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc (patch) | |
tree | caa54b4229e86a68c84ab5961af34e087dce5302 /bsps/powerpc | |
parent | bsps/powerpc: Move shared btimer support (diff) | |
download | rtems-d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc.tar.bz2 |
bsps: Move console drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/powerpc')
19 files changed, 6408 insertions, 0 deletions
diff --git a/bsps/powerpc/gen5200/console/console.c b/bsps/powerpc/gen5200/console/console.c new file mode 100644 index 0000000000..d7325032c4 --- /dev/null +++ b/bsps/powerpc/gen5200/console/console.c @@ -0,0 +1,835 @@ +/*===============================================================*\ +| Project: RTEMS generic MPC5200 BSP | ++-----------------------------------------------------------------+ +| Partially based on the code references which are named below. | +| Adaptions, modifications, enhancements and any recent parts of | +| the code are: | +| Copyright (c) 2005 | +| 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.org/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +| this file contains the console driver functions | +\*===============================================================*/ +/***********************************************************************/ +/* */ +/* Module: console.c */ +/* Date: 07/17/2003 */ +/* Purpose: RTEMS MPC5x00 console driver */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Description: */ +/* */ +/* The PSCs of mpc5200 are assigned as follows */ +/* */ +/* Channel Device Minor Note */ +/* PSC1 /dev/tty0 0 */ +/* PSC2 /dev/tty1 1 */ +/* PSC3 /dev/tty2 2 */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Code */ +/* References: Serial driver for MPC8260ads */ +/* Module: console-generic.c */ +/* Project: RTEMS 4.6.0pre1 / MPC8260ads BSP */ +/* Version 1.3 */ +/* Date: 2002/11/04 */ +/* */ +/* Author(s) / Copyright(s): */ +/* */ +/* 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-2008. */ +/* 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 */ +/* */ +/* Modifications by Andy Dachs <a.dachs@sstl.co.uk> to add MPC8260 */ +/* support. */ +/* Copyright (c) 2001, Surrey Satellite Technology Ltd */ +/* */ +/* 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. */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Partially based on the code references which are named above. */ +/* Adaptions, modifications, enhancements and any recent parts of */ +/* the code are under the right of */ +/* */ +/* IPR Engineering, Dachauer Straße 38, D-80335 München */ +/* Copyright(C) 2003 */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* IPR Engineering makes no representation or warranties with */ +/* respect to the performance of this computer program, and */ +/* specifically disclaims any responsibility for any damages, */ +/* special or consequential, connected with the use of this program. */ +/* */ +/*---------------------------------------------------------------------*/ +/* */ +/* Version history: 1.0 */ +/* */ +/***********************************************************************/ + +#include <assert.h> +#include <string.h> + +#include <rtems.h> +#include <bsp/mpc5200.h> +#include <bsp.h> +#include <bsp/irq.h> + +#include <rtems/console.h> +#include <rtems/bspIo.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> + + +#define NUM_PORTS MPC5200_PSC_NO + +#define PSC1_MINOR 0 +#define PSC2_MINOR 1 +#define PSC3_MINOR 2 +#define PSC4_MINOR 3 +#define PSC5_MINOR 4 +#define PSC6_MINOR 5 + +uint32_t mpc5200_uart_avail_mask = BSP_UART_AVAIL_MASK; + +#if defined(UARTS_USE_TERMIOS_INT) + uint8_t psc_minor_to_irqname[NUM_PORTS] = { + BSP_SIU_IRQ_PSC1, + BSP_SIU_IRQ_PSC2, + BSP_SIU_IRQ_PSC3, + BSP_SIU_IRQ_PSC4, + BSP_SIU_IRQ_PSC5, + BSP_SIU_IRQ_PSC6 + }; + + static int mpc5200_psc_irqname_to_minor(int name) { + int minor; + uint8_t *chrptr; + + chrptr = memchr(psc_minor_to_irqname, name, sizeof(psc_minor_to_irqname)); + if (chrptr != NULL) { + minor = chrptr - psc_minor_to_irqname; + } else { + minor = -1; + } + return minor; + } +#endif + +static void A_BSP_output_char(char c); +static int A_BSP_get_char(void); +BSP_output_char_function_type BSP_output_char = A_BSP_output_char; + +BSP_polling_getchar_function_type BSP_poll_char = A_BSP_get_char; + +/* Used to handle premature outputs of printk */ +bool console_initialized = false; + +/* per channel info structure */ +struct per_channel_info { + uint16_t shadow_imr; + uint8_t shadow_mode1; + uint8_t shadow_mode2; + int cur_tx_len; + int rx_interrupts; + int tx_interrupts; + int rx_characters; + int tx_characters; + int breaks_detected; + int framing_errors; + int parity_errors; + int overrun_errors; +}; + +/* Used to handle more than one channel */ +struct per_channel_info channel_info[NUM_PORTS]; + +/* + * XXX: there are only 6 PSCs, but PSC6 has an extra register gap + * from PSC5, therefore we instantiate seven(!) PSC register sets + */ +uint8_t psc_minor_to_regset[MPC5200_PSC_NO] = {0,1,2,3,4,6}; + +/* Used to track termios private data for callbacks */ +struct rtems_termios_tty *ttyp[NUM_PORTS]; + +static int mpc5200_psc_setAttributes( + int minor, + const struct termios *t +) +{ + int baud; + uint8_t csize=0, cstopb, parenb, parodd; + struct mpc5200_psc *psc = + (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + + /* Baud rate */ + baud = rtems_termios_baud_to_number(t->c_ospeed); + if (baud > 0) { + /* + * Calculate baud rate + * round divider to nearest! + */ + baud = (IPB_CLOCK + baud *16) / (baud * 32); + } + + /* Number of data bits */ + switch ( t->c_cflag & CSIZE ) { + case CS5: csize = 0x00; break; + case CS6: csize = 0x01; break; + case CS7: csize = 0x02; break; + case CS8: csize = 0x03; break; + } + + /* Stop bits */ + if(csize == 0) { + if(t->c_cflag & CSTOPB) + cstopb = 0x0F; /* Two stop bits */ + else + cstopb = 0x00; /* One stop bit */ + } else { + if(t->c_cflag & CSTOPB) + cstopb = 0x0F; /* Two stop bits */ + else + cstopb = 0x07; /* One stop bit */ + } + + /* Parity */ + if (t->c_cflag & PARENB) + parenb = 0x00; /* Parity enabled on Tx and Rx */ + else + parenb = 0x10; /* No parity on Tx and Rx */ + + if (t->c_cflag & PARODD) + parodd = 0x04; /* Odd parity */ + else + parodd = 0x00; + + /* + * Set upper timer counter + */ + psc->ctur = (uint8_t) (baud >> 8); + + /* + * Set lower timer counter + */ + psc->ctlr = (uint8_t) baud; + + /* + * Reset mode pointer + */ + psc->cr = ((1 << 4) << 8); + + /* + * Set mode1 register + */ + channel_info[minor].shadow_mode1 &= ~(0x1F); + psc->mr = channel_info[minor].shadow_mode1 | (csize | parenb | parodd); + + /* + * Set mode2 register + */ + channel_info[minor].shadow_mode2 &= ~(0x0F); + psc->mr = channel_info[minor].shadow_mode2 | cstopb; + + return 0; + +} + + +static int mpc5200_uart_setAttributes(int minor, const struct termios *t) +{ + /* + * Check that port number is valid + */ + if( (minor < PSC1_MINOR) || (minor > NUM_PORTS-1) ) + return 0; + + return mpc5200_psc_setAttributes(minor, t); + +} + +#ifdef UARTS_USE_TERMIOS_INT +/* + * Interrupt handlers + */ +static void mpc5200_psc_interrupt_handler(rtems_irq_hdl_param handle) +{ + unsigned char c; + uint16_t isr; + int minor = (int)handle; + struct mpc5200_psc *psc = + (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + + /* + * get content of psc interrupt status + */ + isr = psc->isr_imr; + + /* + * Character received? + */ + if (isr & ISR_RX_RDY_FULL) { + + channel_info[minor].rx_interrupts++; + + +#ifndef SINGLE_CHAR_MODE + while(psc->rfnum) { +#endif + /* + * get the character + */ + c = (psc->rb_tb >> 24); + + if (ttyp[minor] != NULL) { + rtems_termios_enqueue_raw_characters( + (void *)ttyp[minor], (char *)&c, (int)1); + channel_info[minor].rx_characters++; + } + +#ifndef SINGLE_CHAR_MODE + } +#endif + + } + + /* + * Character transmitted ? + */ + if (isr & ISR_TX_RDY & channel_info[minor].shadow_imr) { + channel_info[minor].tx_interrupts++; + + if (ttyp[minor] != NULL) { + #ifndef SINGLE_CHAR_MODE + rtems_termios_dequeue_characters( + (void *)ttyp[minor], channel_info[minor].cur_tx_len); + channel_info[minor].tx_characters += channel_info[minor].cur_tx_len; + #else + rtems_termios_dequeue_characters((void *)ttyp[minor], (int)1); + channel_info[minor].tx_characters++; + #endif + } + } + + if(isr & ISR_ERROR) { + if(isr & ISR_RB) + channel_info[minor].breaks_detected++; + if(isr & ISR_FE) + channel_info[minor].framing_errors++; + if(isr & ISR_PE) + channel_info[minor].parity_errors++; + if(isr & ISR_OE) + channel_info[minor].overrun_errors++; + + /* + * Reset error status + */ + psc->cr = ((4 << 4) << 8); + } +} + +static void mpc5200_psc_enable( + const rtems_irq_connect_data* ptr +) +{ + struct mpc5200_psc *psc; + int minor = mpc5200_psc_irqname_to_minor(ptr->name); + + if (minor >= 0) { + psc = (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + psc->isr_imr = channel_info[minor].shadow_imr |= + (IMR_RX_RDY_FULL | IMR_TX_RDY); + } +} + + +static void mpc5200_psc_disable( + const rtems_irq_connect_data* ptr +) +{ + struct mpc5200_psc *psc; + int minor = mpc5200_psc_irqname_to_minor(ptr->name); + + if (minor >= 0) { + psc = (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + psc->isr_imr = channel_info[minor].shadow_imr &= + ~(IMR_RX_RDY_FULL | IMR_TX_RDY); + } +} + +static int mpc5200_psc_isOn( + const rtems_irq_connect_data* ptr +) +{ + struct mpc5200_psc *psc; + int minor = mpc5200_psc_irqname_to_minor(ptr->name); + + if (minor >= 0) { + psc = (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + return ((psc->isr_imr & IMR_RX_RDY_FULL) & (psc->isr_imr & IMR_TX_RDY)); + } + return false; +} + + +static rtems_irq_connect_data consoleIrqData; +#endif + +static void mpc5200_uart_psc_initialize( + int minor +) +{ + uint32_t baud_divider; + struct mpc5200_psc *psc = + (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + + /* + * Check that minor number is valid + */ + if ((minor < PSC1_MINOR) || (minor >= (PSC1_MINOR + NUM_PORTS))) + return; + + /* + * Clear per channel info + */ + memset((void *)&channel_info[minor], 0, sizeof(struct per_channel_info)); + + /* + * Reset receiver and transmitter + */ + psc->cr = ((2 << 4) << 8); + psc->cr = ((3 << 4) << 8); + + /* + * Reset mode pointer + */ + psc->cr = ((1 << 4) << 8); + + /* + * Set clock select register + */ + psc->sr_csr = 0; + + /* + * Set mode1 register + */ + psc->mr = channel_info[minor].shadow_mode1 = 0x33; /* 8Bit / no parity */ + + /* + * Set mode2 register + */ + psc->mr = channel_info[minor].shadow_mode2 = 7; /* 1 stop bit */ + + /* + * Set rx FIFO alarm + */ + psc->rfalarm = RX_FIFO_SIZE - 1; + + /* + * Set tx FIFO alarm + */ + psc->tfalarm = 1; + + baud_divider = + (IPB_CLOCK + GEN5200_CONSOLE_BAUD *16) / (GEN5200_CONSOLE_BAUD * 32); + + /* + * Set upper timer counter + */ + psc->ctur = baud_divider >> 16; + + /* + * Set lower timer counter + */ + + psc->ctlr = baud_divider & 0x0000ffff; + + /* + * Disable Frame mode / set granularity 0 + */ + psc->tfcntl = 0; + +#ifdef UARTS_USE_TERMIOS_INT + /* + * Tie interrupt dependent routines + */ + consoleIrqData.on = mpc5200_psc_enable; + consoleIrqData.off = mpc5200_psc_disable; + consoleIrqData.isOn = mpc5200_psc_isOn; + consoleIrqData.handle = (rtems_irq_hdl_param)minor; + consoleIrqData.hdl = (rtems_irq_hdl)mpc5200_psc_interrupt_handler; + + /* + * Tie interrupt handler + */ + consoleIrqData.name = psc_minor_to_irqname[minor]; + + /* + * Install rtems irq handler + */ + assert(BSP_install_rtems_irq_handler(&consoleIrqData) == 1); +#endif + + /* + * Reset rx fifo errors Error/UF/OF + */ + psc->rfstat |= 0x70; + + /* + * Reset tx fifo errors Error/UF/OF + */ + psc->tfstat |= 0x70; + +#ifdef UARTS_USE_TERMIOS_INT + /* + * Unmask receive interrupt + */ + psc->isr_imr = channel_info[minor].shadow_imr = IMR_RX_RDY_FULL; +#endif + + /* + * Enable receiver + */ + psc->cr = ((1 << 0) << 8); + + /* + * Enable transmitter + */ + psc->cr = ((1 << 2) << 8); +} + + +static int mpc5200_uart_pollRead( + int minor +) +{ + unsigned char c; + struct mpc5200_psc *psc = + (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + + if (psc->sr_csr & (1 << 8)) + c = (psc->rb_tb >> 24); + else + return -1; + + return c; +} + + +static ssize_t mpc5200_uart_pollWrite( + int minor, + const char *buf, + size_t len +) +{ + size_t retval = len; + const char *tmp_buf = buf; + struct mpc5200_psc *psc = + (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + + while(len--) { + while(!(psc->sr_csr & (1 << 11))) + continue; + + /*rtems_cache_flush_multiple_data_lines( (void *)buf, 1);*/ + + psc->rb_tb = (*tmp_buf << 24); + + tmp_buf++; + + } + return retval; + +} + +static ssize_t mpc5200_uart_write( + int minor, + const char *buf, + size_t len +) +{ + struct mpc5200_psc *psc = + (struct mpc5200_psc *)(&mpc5200.psc[psc_minor_to_regset[minor]]); + + if (len > 0) { + int frame_len = len; + const char *frame_buf = buf; + + /* + * Check tx fifo space + */ + if(len > (TX_FIFO_SIZE - psc->tfnum)) + frame_len = TX_FIFO_SIZE - psc->tfnum; + +#ifndef SINGLE_CHAR_MODE + channel_info[minor].cur_tx_len = frame_len; +#else + frame_len = 1; +#endif + + /*rtems_cache_flush_multiple_data_lines( (void *)frame_buf, frame_len);*/ + + while (frame_len--) + /* perform byte write to avoid extra NUL characters */ + (* (volatile char *)&(psc->rb_tb)) = *frame_buf++; + + /* + * unmask interrupt + */ + psc->isr_imr = channel_info[minor].shadow_imr |= IMR_TX_RDY; + } else { + /* + * mask interrupt + */ + psc->isr_imr = channel_info[minor].shadow_imr &= ~(IMR_TX_RDY); + } + + return 0; +} + +/* + * Print functions prototyped in bspIo.h + */ +static void A_BSP_output_char( + char c +) +{ + /* + * If we are using U-Boot, then the console is already initialized + * and we can just poll bytes out at any time. + */ + #if !defined(HAS_UBOOT) + if (console_initialized == false) + return; + #endif + +#define PRINTK_WRITE mpc5200_uart_pollWrite + + PRINTK_WRITE(PRINTK_MINOR, &c, 1 ); +} + +static int A_BSP_get_char(void) +{ + /* + * If we are using U-Boot, then the console is already initialized + * and we can just poll bytes in at any time. + */ + #if !defined(HAS_UBOOT) + if (console_initialized == false) + return -1; + #endif + + return mpc5200_uart_pollRead(0); +} + +/* + *************** + * BOILERPLATE * + *************** + * + * All these functions are prototyped in rtems/c/src/lib/include/console.h. + */ + +/* + * Initialize and register the device + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + rtems_device_minor_number console_minor; + char dev_name[] = "/dev/ttyx"; + uint32_t tty_num = 0; + bool first = true; + + /* + * Always use and set up TERMIOS + */ + console_minor = PSC1_MINOR; + rtems_termios_initialize(); + + for (console_minor = PSC1_MINOR; + console_minor < PSC1_MINOR + NUM_PORTS; + console_minor++) { + /* + * check, whether UART is available for this board + */ + if (0 != ((1 << console_minor) & (mpc5200_uart_avail_mask))) { + /* + * Do device-specific initialization and registration for Motorola IceCube + */ + mpc5200_uart_psc_initialize(console_minor); /* /dev/tty0 */ + dev_name[8] = '0' + tty_num; + status = rtems_io_register_name (dev_name, major, console_minor); + assert(status == RTEMS_SUCCESSFUL); + + #ifdef MPC5200_PSC_INDEX_FOR_GPS_MODULE + if (console_minor == MPC5200_PSC_INDEX_FOR_GPS_MODULE) { + status = rtems_io_register_name("/dev/gps", major, console_minor); + assert(status == RTEMS_SUCCESSFUL); + } + #endif + + if (first) { + first = false; + + /* Now register the RTEMS console */ + status = rtems_io_register_name ("/dev/console", major, console_minor); + assert(status == RTEMS_SUCCESSFUL); + } + + tty_num++; + } + } + + console_initialized = true; + return RTEMS_SUCCESSFUL; +} + +/* + * Open the device + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_open_close_args_t *args = arg; + rtems_status_code sc; + +#ifdef UARTS_USE_TERMIOS_INT + static const rtems_termios_callbacks intrCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + NULL, /* pollRead */ + mpc5200_uart_write, /* write */ + mpc5200_uart_setAttributes, /* setAttributes */ + NULL, + NULL, + 1 /* outputUsesInterrupts */ + }; +#else + static const rtems_termios_callbacks pollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + mpc5200_uart_pollRead, /* pollRead */ + mpc5200_uart_pollWrite, /* write */ + mpc5200_uart_setAttributes, /* setAttributes */ + NULL, + NULL, + 0 /* output don't use Interrupts */ + }; +#endif + + if(minor > NUM_PORTS - 1) + return RTEMS_INVALID_NUMBER; + +#ifdef UARTS_USE_TERMIOS_INT + sc = rtems_termios_open( major, minor, arg, &intrCallbacks ); +#else /* RTEMS polled I/O with termios */ + sc = rtems_termios_open( major, minor, arg, &pollCallbacks ); +#endif + + ttyp[minor] = args->iop->data1; /* Keep cookie returned by termios_open */ + + if ( !sc ) + rtems_termios_set_initial_baud( ttyp[minor], GEN5200_CONSOLE_BAUD ); + + return sc; +} + + +/* + * Close the device + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + + ttyp[minor] = NULL; /* mark for int handler: tty no longer open */ + + return rtems_termios_close( arg ); +} + + +/* + * Read from the device + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if(minor > NUM_PORTS-1) + return RTEMS_INVALID_NUMBER; + + return rtems_termios_read(arg); +} + +/* + * Write to the device + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + return rtems_termios_write(arg); +} + +/* + * Handle ioctl request. + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + + return rtems_termios_ioctl(arg); +} diff --git a/bsps/powerpc/gen83xx/console/console-config.c b/bsps/powerpc/gen83xx/console/console-config.c new file mode 100644 index 0000000000..d0071cd220 --- /dev/null +++ b/bsps/powerpc/gen83xx/console/console-config.c @@ -0,0 +1,107 @@ +/** + * @file + * + * @brief Console configuration. + */ + +/* + * Copyright (c) 2008-2014 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 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.org/license/LICENSE. + */ + +#include <rtems/bspIo.h> + +#include <libchip/ns16550.h> + +#include <mpc83xx/mpc83xx.h> + +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/console-termios.h> + +#ifdef BSP_USE_UART_INTERRUPTS + #define DEVICE_FNS &ns16550_handler_interrupt +#else + #define DEVICE_FNS &ns16550_handler_polled +#endif + +static uint8_t gen83xx_console_get_register(uintptr_t addr, uint8_t i) +{ + volatile uint8_t *reg = (volatile uint8_t *) addr; + + return reg [i]; +} + +static void gen83xx_console_set_register(uintptr_t addr, uint8_t i, uint8_t val) +{ + volatile uint8_t *reg = (volatile uint8_t *) addr; + + reg [i] = val; +} + +static ns16550_context gen83xx_uart_context_0 = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART 0"), + .get_reg = gen83xx_console_get_register, + .set_reg = gen83xx_console_set_register, + .port = (uintptr_t) &mpc83xx.duart[0], +#if MPC83XX_CHIP_TYPE / 10 == 830 + .irq = BSP_IPIC_IRQ_UART, +#else + .irq = BSP_IPIC_IRQ_UART1, +#endif + .initial_baud = BSP_CONSOLE_BAUD +}; + +#ifdef BSP_USE_UART2 +static ns16550_context gen83xx_uart_context_1 = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART 1"), + .get_reg = gen83xx_console_get_register, + .set_reg = gen83xx_console_set_register, + .port = (uintptr_t) &mpc83xx.duart[1], +#if MPC83XX_CHIP_TYPE / 10 == 830 + .irq = BSP_IPIC_IRQ_UART, +#else + .irq = BSP_IPIC_IRQ_UART2, +#endif + .initial_baud = BSP_CONSOLE_BAUD +}; +#endif + +const console_device console_device_table[] = { + { + .device_file = "/dev/ttyS0", + .probe = ns16550_probe, + .handler = DEVICE_FNS, + .context = &gen83xx_uart_context_0.base + } +#ifdef BSP_USE_UART2 + , { + .device_file = "/dev/ttyS1", + .probe = ns16550_probe, + .handler = DEVICE_FNS, + .context = &gen83xx_uart_context_1.base + } +#endif +}; + +const size_t console_device_count = RTEMS_ARRAY_SIZE(console_device_table); + +static void gen83xx_output_char(char c) +{ + rtems_termios_device_context *ctx = console_device_table[0].context; + + ns16550_polled_putchar(ctx, c); +} + +BSP_output_char_function_type BSP_output_char = gen83xx_output_char; + +BSP_polling_getchar_function_type BSP_poll_char = NULL; diff --git a/bsps/powerpc/mpc55xxevb/console/console-config.c b/bsps/powerpc/mpc55xxevb/console/console-config.c new file mode 100644 index 0000000000..d0ea250917 --- /dev/null +++ b/bsps/powerpc/mpc55xxevb/console/console-config.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011-2012 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 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.org/license/LICENSE. + */ + +#include <bsp.h> +#include <bsp/console-generic.h> +#include <bsp/console-esci.h> +#include <bsp/console-linflex.h> + +CONSOLE_GENERIC_INFO_TABLE = { + #ifdef MPC55XX_HAS_ESCI + CONSOLE_GENERIC_INFO(mpc55xx_esci_devices + 0, &mpc55xx_esci_callbacks, "/dev/ttyS0") + #ifdef ESCI_B + , CONSOLE_GENERIC_INFO(mpc55xx_esci_devices + 1, &mpc55xx_esci_callbacks, "/dev/ttyS1") + #endif + #ifdef ESCI_C + , CONSOLE_GENERIC_INFO(mpc55xx_esci_devices + 2, &mpc55xx_esci_callbacks, "/dev/ttyS2") + #endif + #ifdef ESCI_D + , CONSOLE_GENERIC_INFO(mpc55xx_esci_devices + 3, &mpc55xx_esci_callbacks, "/dev/ttyS3") + #endif + #endif + #ifdef MPC55XX_HAS_LINFLEX + CONSOLE_GENERIC_INFO(mpc55xx_linflex_devices + 0, &mpc55xx_linflex_callbacks, "/dev/ttyS0"), + CONSOLE_GENERIC_INFO(mpc55xx_linflex_devices + 1, &mpc55xx_linflex_callbacks, "/dev/ttyS1") + #endif +}; + +CONSOLE_GENERIC_INFO_COUNT; + +CONSOLE_GENERIC_MINOR(MPC55XX_CONSOLE_MINOR); diff --git a/bsps/powerpc/mpc55xxevb/console/console-esci.c b/bsps/powerpc/mpc55xxevb/console/console-esci.c new file mode 100644 index 0000000000..9e6646fb65 --- /dev/null +++ b/bsps/powerpc/mpc55xxevb/console/console-esci.c @@ -0,0 +1,354 @@ +/** + * @file + * + * @brief Console ESCI implementation. + */ + +/* + * Copyright (c) 2008-2012 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 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.org/license/LICENSE. + */ + +#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 */ diff --git a/bsps/powerpc/mpc55xxevb/console/console-generic.c b/bsps/powerpc/mpc55xxevb/console/console-generic.c new file mode 100644 index 0000000000..71385adf2b --- /dev/null +++ b/bsps/powerpc/mpc55xxevb/console/console-generic.c @@ -0,0 +1,168 @@ +/** + * @file + * + * @brief Generic console driver implementation. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 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.org/license/LICENSE. + */ + +#include <sys/cdefs.h> + +#include <bsp.h> +#include <bsp/console-generic.h> +#include <bsp/fatal.h> + +#include <rtems/bspIo.h> +#include <rtems/console.h> + +static const struct termios console_generic_termios = { + .c_cflag = CS8 | CREAD | CLOCAL | __CONCAT(B, BSP_DEFAULT_BAUD_RATE) +}; + +static void console_generic_char_out(char c) +{ + int minor = (int) console_generic_minor; + const console_generic_callbacks *cb = + console_generic_info_table [minor].callbacks; + + (*cb->poll_write)(minor, c); +} + +static int console_generic_char_in(void) +{ + int minor = (int) console_generic_minor; + const console_generic_callbacks *cb = + console_generic_info_table [minor].callbacks; + + return (*cb->poll_read)(minor); +} + +static void console_generic_char_out_do_init(void) +{ + int minor = (int) console_generic_minor; + const console_generic_callbacks *cb = + console_generic_info_table [minor].callbacks; + const struct termios *term = &console_generic_termios; + + BSP_output_char = console_generic_char_out; + (*cb->termios_callbacks.setAttributes)(minor, term); +} + +static void console_generic_char_out_init(char c) +{ + console_generic_char_out_do_init(); + console_generic_char_out(c); +} + +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + const console_generic_info *info_table = console_generic_info_table; + rtems_device_minor_number count = console_generic_info_count; + rtems_device_minor_number console = console_generic_minor; + + if (count <= 0) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_GENERIC_COUNT); + } + + rtems_termios_initialize(); + + for (minor = 0; minor < count; ++minor) { + const console_generic_info *info = info_table + minor; + + sc = rtems_io_register_name(info->device_path, major, minor); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_GENERIC_REGISTER); + } + } + + sc = rtems_io_register_name(CONSOLE_DEVICE_NAME, major, console); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_GENERIC_REGISTER_CONSOLE); + } + + console_generic_char_out_do_init(); + + return sc; +} + +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_device_minor_number count = console_generic_info_count; + + if (minor < count) { + const console_generic_info *info = &console_generic_info_table [minor]; + + sc = rtems_termios_open( + major, + minor, + arg, + &info->callbacks->termios_callbacks + ); + } else { + sc = RTEMS_INVALID_ID; + } + + return sc; +} + +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_close(arg); +} + +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_read(arg); +} + +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_write(arg); +} + +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_ioctl(arg); +} + +BSP_output_char_function_type BSP_output_char = console_generic_char_out_init; + +BSP_polling_getchar_function_type BSP_poll_char = console_generic_char_in; diff --git a/bsps/powerpc/mpc55xxevb/console/console-linflex.c b/bsps/powerpc/mpc55xxevb/console/console-linflex.c new file mode 100644 index 0000000000..02978be524 --- /dev/null +++ b/bsps/powerpc/mpc55xxevb/console/console-linflex.c @@ -0,0 +1,417 @@ +/** + * @file + * + * @brief Console LINFlexD implementation. + */ + +/* + * Copyright (c) 2011-2014 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 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.org/license/LICENSE. + */ + +#include <bsp/console-linflex.h> + +#include <bsp.h> +#include <bsp/fatal.h> +#include <bsp/irq.h> + +#ifdef MPC55XX_HAS_LINFLEX + +mpc55xx_linflex_context mpc55xx_linflex_devices [] = { + { + .regs = &LINFLEX0, + .irq_rxi = MPC55XX_IRQ_LINFLEX_RXI(0), + .irq_txi = MPC55XX_IRQ_LINFLEX_TXI(0), + .irq_err = MPC55XX_IRQ_LINFLEX_ERR(0), + .tx_pcr_register = &((SIU_tag *) &SIUL)->PCR18, + .tx_pa_value = 1, + .rx_pcr_register = &((SIU_tag *) &SIUL)->PCR19, + .rx_psmi_register = &((SIU_tag *) &SIUL)->PSMI31, + .rx_padsel_value = 0 + }, { + .regs = &LINFLEX1, + .irq_rxi = MPC55XX_IRQ_LINFLEX_RXI(1), + .irq_txi = MPC55XX_IRQ_LINFLEX_TXI(1), + .irq_err = MPC55XX_IRQ_LINFLEX_ERR(1), + .tx_pcr_register = &((SIU_tag *) &SIUL)->PCR94, + .tx_pa_value = 1, + .rx_pcr_register = &((SIU_tag *) &SIUL)->PCR95, + .rx_psmi_register = &((SIU_tag *) &SIUL)->PSMI32, + .rx_padsel_value = 2 + } +}; + +static void enter_init_mode(volatile LINFLEX_tag *regs) +{ + LINFLEX_LINCR1_32B_tag cr1 = { .R = regs->LINCR1.R }; + cr1.B.SLEEP = 0; + cr1.B.INIT = 1; + regs->LINCR1.R = cr1.R; +} + +static void enter_active_mode(volatile LINFLEX_tag *regs) +{ + LINFLEX_LINCR1_32B_tag cr1 = { .R = regs->LINCR1.R }; + cr1.B.SLEEP = 0; + cr1.B.INIT = 0; + regs->LINCR1.R = cr1.R; +} + +static void enter_sleep_mode(volatile LINFLEX_tag *regs) +{ + LINFLEX_LINCR1_32B_tag cr1 = { .R = regs->LINCR1.R }; + cr1.B.SLEEP = 1; + cr1.B.INIT = 0; + regs->LINCR1.R = cr1.R; +} + +static void mpc55xx_linflex_poll_write(int minor, char c) +{ + mpc55xx_linflex_context *self = console_generic_get_context(minor); + volatile LINFLEX_tag *regs = self->regs; + const LINFLEX_UARTSR_32B_tag clear_dtf = { .B = { .DTF_TFF = 1 } }; + rtems_interrupt_level level; + bool done = false; + bool wait_for_transmit_done = false; + + rtems_interrupt_disable(level); + if (self->transmit_nest_level == 0) { + LINFLEX_LINIER_32B_tag ier = { .R = regs->LINIER.R }; + + if (ier.B.DTIE != 0) { + ier.B.DTIE = 0; + regs->LINIER.R = ier.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->UARTSR.B.DTF_TFF)) { + regs->UARTSR.R = clear_dtf.R; + regs->BDRL.B.DATA0 = 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->UARTSR.B.DTF_TFF) { + regs->UARTSR.R = clear_dtf.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) { + LINFLEX_LINIER_32B_tag ier = { .R = regs->LINIER.R }; + + ier.B.DTIE = 1; + regs->LINIER.R = ier.R; + } + } + rtems_interrupt_enable(level); + } +} + +static void mpc55xx_linflex_rx_interrupt_handler(void *arg) +{ + mpc55xx_linflex_context *self = arg; + volatile LINFLEX_tag *regs = self->regs; + char c = regs->BDRM.B.DATA4; + const LINFLEX_UARTSR_32B_tag clear_flags = { .B = { .RMB = 1, .DRF_RFE = 1 } }; + + regs->UARTSR.R = clear_flags.R; + + rtems_termios_enqueue_raw_characters(self->tty, &c, 1); +} + +static void mpc55xx_linflex_tx_interrupt_handler(void *arg) +{ + mpc55xx_linflex_context *self = arg; + volatile LINFLEX_tag *regs = self->regs; + + regs->UARTSR.B.DTF_TFF = 1; /* clear flag */ + self->transmit_in_progress = false; + + rtems_termios_dequeue_characters(self->tty, 1); +} + +/* +static void mpc55xx_linflex_err_interrupt_handler(void *arg) +{ + mpc55xx_linflex_context *self = arg; +} +*/ + +static int mpc55xx_linflex_set_attributes(int minor, const struct termios *t) +{ + mpc55xx_linflex_context *self = console_generic_get_context(minor); + volatile LINFLEX_tag *regs = self->regs; + LINFLEX_UARTCR_32B_tag uartcr = { .R = 0 }; + LINFLEX_GCR_32B_tag gcr = { .R = 0 }; + LINFLEX_LINIER_32B_tag ier = { .R = 0 }; + rtems_termios_baud_t br = rtems_termios_baud_to_number(t->c_ospeed); + LINFLEX_LINFBRR_32B_tag fbrr = { .R = 0 }; + LINFLEX_LINIBRR_32B_tag ibrr = { .R = 0 }; + + enter_init_mode(regs); + + /* Set to UART-mode */ + uartcr.B.UART = 1; + regs->UARTCR.R = uartcr.R; + + /* Set to buffer mode with size 1 */ + uartcr.B.TDFL_TFC = 0; + uartcr.B.RDFL_RFC0 = 0; + uartcr.B.RFBM = 0; + uartcr.B.TFBM = 0; + + /* Enable receiver and transmitter */ + uartcr.B.RXEN = 1; + uartcr.B.TXEN = 1; + + /* Number of data bits */ + uartcr.B.WL1 = 0; + if ((t->c_cflag & CSIZE) == CS8) { + uartcr.B.WL0 = 1; + } else if ((t->c_cflag & CSIZE) == CS7) { + uartcr.B.WL0 = 0; + } else { + return -1; + } + + /* Parity */ + uartcr.B.PCE = (t->c_cflag & PARENB) ? 1 : 0; + uartcr.B.PC1 = 0; + uartcr.B.PC0 = (t->c_cflag & PARODD) ? 1 : 0; + + /* Stop bits */ + gcr.B.STOP = (t->c_cflag & CSTOPB) ? 1 : 0; + + /* Set control registers */ + regs->UARTCR.R = uartcr.R; + regs->GCR.R = gcr.R; + + /* Interrupts */ + ier.B.DTIE = 1; + ier.B.DRIE = 1; + regs->LINIER.R = ier.R; + + /* Baud rate */ + if (br > 0) { + uint32_t lfdiv_mult_32 = bsp_clock_speed * 2 / br; + if((lfdiv_mult_32 & 0x1) != 0) { + ++lfdiv_mult_32; + } + fbrr.B.FBR = (lfdiv_mult_32 >> 1) & 0xF; + ibrr.B.IBR = lfdiv_mult_32 >> 5; + } else { + return -1; + } + regs->LINFBRR.R = fbrr.R; + regs->LINIBRR.R = ibrr.R; + + enter_active_mode(regs); + + return 0; +} + +static int mpc55xx_linflex_first_open(int major, int minor, void *arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + int rv = 0; + mpc55xx_linflex_context *self = console_generic_get_context(minor); + struct rtems_termios_tty *tty = console_generic_get_tty_at_open(arg); + SIU_PCR_tag pcr = { .R = 0 }; + SIUL_PSMI_8B_tag psmi = { .R = 0 }; + + self->tty = tty; + + pcr.B.IBE = 1; + self->rx_pcr_register->R = pcr.R; + psmi.B.PADSEL = self->rx_padsel_value; + self->rx_psmi_register->R = psmi.R; + pcr.R = 0; + pcr.B.OBE = 1; + pcr.B.PA = self->tx_pa_value; + self->tx_pcr_register->R = pcr.R; + + rv = rtems_termios_set_initial_baud(tty, BSP_DEFAULT_BAUD_RATE); + if (rv != 0) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_BAUD); + } + + rv = mpc55xx_linflex_set_attributes(minor, &tty->termios); + if (rv != 0) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_ATTRIBUTES); + } + + sc = mpc55xx_interrupt_handler_install( + self->irq_rxi, + "LINFlexD RXI", + RTEMS_INTERRUPT_UNIQUE, + MPC55XX_INTC_DEFAULT_PRIORITY, + mpc55xx_linflex_rx_interrupt_handler, + self + ); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_RX_IRQ_INSTALL); + } + + sc = mpc55xx_interrupt_handler_install( + self->irq_txi, + "LINFlexD TXI", + RTEMS_INTERRUPT_UNIQUE, + MPC55XX_INTC_DEFAULT_PRIORITY, + mpc55xx_linflex_tx_interrupt_handler, + self + ); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_TX_IRQ_INSTALL); + } + + /* + sc = mpc55xx_interrupt_handler_install( + self->irq_err, + "LINFlexD ERR", + RTEMS_INTERRUPT_UNIQUE, + MPC55XX_INTC_DEFAULT_PRIORITY, + mpc55xx_linflex_err_interrupt_handler, + self + ); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_ERR_IRQ_INSTALL); + } + */ + + return 0; +} + +static int mpc55xx_linflex_last_close(int major, int minor, void* arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + mpc55xx_linflex_context *self = console_generic_get_context(minor); + volatile LINFLEX_tag *regs = self->regs; + SIU_PCR_tag pcr = { .R = 0 }; + SIUL_PSMI_8B_tag psmi = { .R = 0 }; + + /* enter initialization mode */ + enter_init_mode(regs); + + /* disable interrupts */ + regs->LINIER.R = 0; + + /* set module to sleep mode */ + enter_sleep_mode(regs); + + sc = rtems_interrupt_handler_remove( + self->irq_rxi, + mpc55xx_linflex_rx_interrupt_handler, + self + ); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_RX_IRQ_REMOVE); + } + + sc = rtems_interrupt_handler_remove( + self->irq_txi, + mpc55xx_linflex_tx_interrupt_handler, + self + ); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_TX_IRQ_REMOVE); + } + + /* + sc = rtems_interrupt_handler_remove( + self->irq_err, + mpc55xx_linflex_err_interrupt_handler, + self + ); + if (sc != RTEMS_SUCCESSFUL) { + bsp_fatal(MPC55XX_FATAL_CONSOLE_LINFLEX_ERR_IRQ_REMOVE); + } + */ + + pcr.B.IBE = 1; + self->rx_pcr_register->R = pcr.R; + self->tx_pcr_register->R = pcr.R; + psmi.R = 0; + self->rx_psmi_register->R = psmi.R; + + self->tty = NULL; + + return 0; +} + +static int mpc55xx_linflex_poll_read(int minor) +{ + mpc55xx_linflex_context *self = console_generic_get_context(minor); + volatile LINFLEX_tag *regs = self->regs; + rtems_interrupt_level level; + int c = -1; + + rtems_interrupt_disable(level); + if (regs->UARTSR.B.DRF_RFE != 0) { + /* Clear flag */ + regs->UARTSR.B.DRF_RFE = 1; + + /* Read */ + c = regs->BDRM.B.DATA4; + } + rtems_interrupt_enable(level); + + return c; +} + +static int mpc55xx_linflex_write(int minor, const char *out, size_t n) +{ + if (n > 0) { + mpc55xx_linflex_context *self = console_generic_get_context(minor); + volatile LINFLEX_tag *regs = self->regs; + + regs->BDRL.B.DATA0 = out [0]; + self->transmit_in_progress = true; + /* TODO: send more then one byte */ + } + + return 0; +} + +const console_generic_callbacks mpc55xx_linflex_callbacks = { + .termios_callbacks = { + .firstOpen = mpc55xx_linflex_first_open, + .lastClose = mpc55xx_linflex_last_close, + .write = mpc55xx_linflex_write, + .setAttributes = mpc55xx_linflex_set_attributes, + .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN + }, + .poll_read = mpc55xx_linflex_poll_read, + .poll_write = mpc55xx_linflex_poll_write +}; + +#endif /* MPC55XX_HAS_LINFLEX */ diff --git a/bsps/powerpc/mpc8260ads/console/console.c b/bsps/powerpc/mpc8260ads/console/console.c new file mode 100644 index 0000000000..873c38dc3e --- /dev/null +++ b/bsps/powerpc/mpc8260ads/console/console.c @@ -0,0 +1,458 @@ +/* + * This file contains the MBX8xx termios serial I/O package. + * Only asynchronous I/O is supported. + * + * The SCCs and SMCs are assigned as follows + * + * Channel Device Minor Note + * SMC1 /dev/tty0 0 + * SMC2 /dev/tty1 1 + * SCC1 2 N/A. Hardwired as ethernet port + * SCC2 /dev/tty2 3 + * SCC3 /dev/tty3 4 + * SCC4 /dev/tty4 5 + * + * The SCCs and SMCs on the eval board are assigned as follows + * + * Channel Device Minor Termios + * SMC1 /dev/tty3 4 no + * SMC2 /dev/tty4 5 no + * SCC1 /dev/tty0 0 no + * SCC2 /dev/console 1 yes + * SCC3 /dev/tty1 2 no * USED FOR NETWORK I/F + * SCC4 /dev/tty2 3 no * USED FOR NETWORK I/F + * + * All ports support termios. The use of termios is recommended for real-time + * applications. Termios provides buffering and input processing. When not + * using termios, processing is limited to the substitution of LF for CR on + * input, and the output of a CR following the output of a LF character. + * Note that the terminal should not send CR/LF pairs when the return key + * is pressed, and that output lines are terminated with LF/CR, not CR/LF + * (although that would be easy to change). + * + * I/O may be interrupt-driven (recommended for real-time applications) or + * polled. Polled I/O may be performed by this device driver entirely, or + * in part by EPPCBug. With EPPCBug 1.1, polled I/O is limited to the + * EPPCBug debug console. This is a limitation of the firmware. Later + * firmware may be able to do I/O through any port. This code assumes + * that the EPPCBug console is the default: SMC1. If the console and + * printk ports are set to anything else with EPPCBug polled I/O, the + * system will hang. Only port SMC1 is usable with EPPCBug polled I/O. + * + * LIMITATIONS: + * + * It is not possible to use different I/O modes on the different ports. The + * exception is with printk. The printk port can use a different mode from + * the other ports. If this is done, it is important not to open the printk + * port from an RTEMS application. + * + * Currently, the I/O modes are determined at build time. It would be much + * better to have the mode selected at boot time based on parameters in + * NVRAM. + * + * Interrupt-driven I/O requires termios. + * + * TESTS: + * + * TO RUN THE TESTS, USE POLLED I/O WITHOUT TERMIOS SUPPORT. Some tests + * play with the interrupt masks and turn off I/O. Those tests will hang + * when interrupt-driven I/O is used. Other tests, such as cdtest, do I/O + * from the static constructors before the console is open. This test + * will not work with interrupt-driven I/O. Because of the buffering + * performed in termios, test output may not be in sequence.The tests + * should all be fixed to work with interrupt-driven I/O and to + * produce output in the expected sequence. Obviously, the termios test + * requires termios support in the driver. + * + * Set CONSOLE_MINOR to the appropriate device minor number in the + * config file. This allows the RTEMS application console to be different + * from the EPPBug debug console or the GDB port. + * + * This driver handles all five available serial ports: it distinguishes + * the sub-devices using minor device numbers. It is not possible to have + * other protocols running on the other ports when this driver is used as + * currently written. + */ + +/* + * Based on code (alloc860.c in eth_comm port) by + * Jay Monkman (jmonkman@frasca.com), + * Copyright (C) 1998 by Frasca International, Inc. + * + * Modifications by Darlene Stewart <Darlene.Stewart@iit.nrc.ca> + * and Charles-Antoine Gauthier <charles.gauthier@iit.nrc.ca>. + * Copyright (c) 2000, National Research Council of Canada + * + * Modifications by Andy Dachs <iwe@fsmal.net> for MPC8260 + * support. + */ +#include <stdarg.h> +#include <stdio.h> +#include <termios.h> + +#include <rtems/console.h> +#include <rtems/bspIo.h> +#include <rtems/libio.h> +#include <bsp.h> + +static void _BSP_output_char( char c ); +static rtems_status_code do_poll_read( rtems_device_major_number major, rtems_device_minor_number minor, void * arg); +static rtems_status_code do_poll_write( rtems_device_major_number major, rtems_device_minor_number minor, void * arg); + +BSP_output_char_function_type BSP_output_char = _BSP_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +/* + * do_poll_read + * + * Input characters through polled I/O. Returns has soon as a character has + * been received. Otherwise, if we wait for the number of requested characters, + * we could be here forever! + * + * CR is converted to LF on input. The terminal should not send a CR/LF pair + * when the return or enter key is pressed. + * + * Input parameters: + * major - ignored. Should be the major number for this driver. + * minor - selected channel. + * arg->buffer - where to put the received characters. + * arg->count - number of characters to receive before returning--Ignored. + * + * Output parameters: + * arg->bytes_moved - the number of characters read. Always 1. + * + * Return value: RTEMS_SUCCESSFUL + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +static rtems_status_code do_poll_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args = arg; + int c; + +#define BSP_READ m8xx_uart_pollRead + + while( (c = BSP_READ(minor)) == -1 ); + rw_args->buffer[0] = (uint8_t)c; + if( rw_args->buffer[0] == '\r' ) + rw_args->buffer[0] = '\n'; + rw_args->bytes_moved = 1; + return RTEMS_SUCCESSFUL; +} + +/* + * do_poll_write + * + * Output characters through polled I/O. Returns only once every character has + * been sent. + * + * CR is transmitted AFTER a LF on output. + * + * Input parameters: + * major - ignored. Should be the major number for this driver. + * minor - selected channel + * arg->buffer - where to get the characters to transmit. + * arg->count - the number of characters to transmit before returning. + * + * Output parameters: + * arg->bytes_moved - the number of characters read + * + * Return value: RTEMS_SUCCESSFUL + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +static rtems_status_code do_poll_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args = arg; + uint32_t i; + char cr ='\r'; + +#define BSP_WRITE m8xx_uart_pollWrite + + for( i = 0; i < rw_args->count; i++ ) { + BSP_WRITE(minor, &(rw_args->buffer[i]), 1); + if ( rw_args->buffer[i] == '\n' ) + BSP_WRITE(minor, &cr, 1); + } + rw_args->bytes_moved = i; + return RTEMS_SUCCESSFUL; + +} + +/* + * Print functions prototyped in bspIo.h + */ + +static void _BSP_output_char( char c ) +{ + /* + * Can't rely on console_initialize having been called before this function + * is used, so it may fail unless output is done through EPPC-Bug. + */ +#define PRINTK_WRITE m8xx_uart_pollWrite + + PRINTK_WRITE( PRINTK_MINOR, &c, 1 ); +} + +/* + *************** + * BOILERPLATE * + *************** + * + * All these functions are prototyped in rtems/c/src/lib/include/console.h. + */ + +/* + * Initialize and register the device + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + rtems_device_minor_number console_minor; + + /* + * Set up TERMIOS if needed + */ + + console_minor = CONSOLE_MINOR; + +#if UARTS_USE_TERMIOS == 1 + + rtems_termios_initialize (); +#else + rtems_termios_initialize (); +#endif /* UARTS_USE_TERMIOS */ + + /* + * Do common initialization. + */ + m8xx_uart_initialize(); + + /* + * Do device-specific initialization + */ +#if 0 + m8xx_uart_smc_initialize(SMC1_MINOR); /* /dev/tty4 */ + m8xx_uart_smc_initialize(SMC2_MINOR); /* /dev/tty5 */ +#endif + + m8xx_uart_scc_initialize(SCC1_MINOR); /* /dev/tty0 */ + m8xx_uart_scc_initialize(SCC2_MINOR); /* /dev/tty1 */ + +#if 0 /* used as network connections */ + m8xx_uart_scc_initialize(SCC3_MINOR); /* /dev/tty2 */ + m8xx_uart_scc_initialize(SCC4_MINOR); /* /dev/tty3 */ +#endif + + /* + * Set up interrupts + */ + m8xx_uart_interrupts_initialize(); + + status = rtems_io_register_name ("/dev/tty0", major, SCC1_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + chmod("/dev/tty0",0660); + chown("/dev/tty0",2,0); + + status = rtems_io_register_name ("/dev/tty1", major, SCC2_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + chmod("/dev/tty1",0660); + chown("/dev/tty1",2,0); + +#if 0 + status = rtems_io_register_name ("/dev/tty2", major, SCC3_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty3", major, SCC4_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty4", major, SMC1_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + status = rtems_io_register_name ("/dev/tty5", major, SMC2_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); +#endif + /* Now register the RTEMS console */ + status = rtems_io_register_name ("/dev/console", major, console_minor); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + chmod("/dev/console",0666); + chown("/dev/console",2,0); + + return RTEMS_SUCCESSFUL; +} + +/* + * Open the device + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ +#if UARTS_IO_MODE == 1 /* RTEMS interrupt-driven I/O with termios */ + /* Used to track termios private data for callbacks */ + extern struct rtems_termios_tty *ttyp[]; + rtems_libio_open_close_args_t *args = arg; + + static const rtems_termios_callbacks intrCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + NULL, /* pollRead */ + m8xx_uart_write, /* write */ + m8xx_uart_setAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 1 /* outputUsesInterrupts */ + }; +#else +#if (UARTS_USE_TERMIOS == 1) && (UARTS_IO_MODE != 1) + static const rtems_termios_callbacks pollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + m8xx_uart_pollRead, /* pollRead */ + m8xx_uart_pollWrite, /* write */ + m8xx_uart_setAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 0 /* outputUsesInterrupts */ + }; +#endif + +#endif + + rtems_status_code sc; + + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + +#if UARTS_USE_TERMIOS == 1 + +#if UARTS_IO_MODE == 1 /* RTEMS interrupt-driven I/O with termios */ + sc = rtems_termios_open( major, minor, arg, &intrCallbacks ); + ttyp[minor] = args->iop->data1; /* Keep cookie returned by termios_open */ +#else /* RTEMS polled I/O with termios */ + sc = rtems_termios_open( major, minor, arg, &pollCallbacks ); +#endif + +#else /* UARTS_USE_TERMIOS != 1 */ + /* no termios -- default to polled I/O */ + sc = RTEMS_SUCCESSFUL; +#endif /* UARTS_USE_TERMIOS != 1 */ + + return sc; + +} + +/* + * Close the device + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + +#if UARTS_USE_TERMIOS == 1 + return rtems_termios_close( arg ); +#else + return RTEMS_SUCCESSFUL; +#endif + +} + +/* + * Read from the device + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + +#if UARTS_USE_TERMIOS == 1 + return rtems_termios_read( arg ); +#else + return do_poll_read( major, minor, arg ); +#endif + +} + +/* + * Write to the device + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + +#if UARTS_USE_TERMIOS == 1 + return rtems_termios_write( arg ); +#else + /* no termios -- default to polled */ + return do_poll_write( major, minor, arg ); +#endif + +} + +/* + * Handle ioctl request. + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + +#if UARTS_USE_TERMIOS == 1 + return rtems_termios_ioctl( arg ); +#else + return RTEMS_SUCCESSFUL; +#endif + +} + +/* + * Support routine for console-generic + */ +int mbx8xx_console_get_configuration(void) +{ +#if UARTS_IO_MODE == 1 + return 0x02; +#else + return 0; +#endif + +} diff --git a/bsps/powerpc/psim/console/console-io.c b/bsps/powerpc/psim/console/console-io.c new file mode 100644 index 0000000000..512b90dde2 --- /dev/null +++ b/bsps/powerpc/psim/console/console-io.c @@ -0,0 +1,78 @@ +/* + * This file contains the hardware specific portions of the TTY driver + * for the simulated serial port on the PowerPC simulator. + */ + +/* + * COPYRIGHT (c) 1989-2004. + * On-Line Applications Research Corporation (OAR). + * + * 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 <bsp.h> +#include <bsp/console-polled.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <assert.h> + +/* + * console_initialize_hardware + * + * This routine initializes the console hardware. + * + */ + +void console_initialize_hardware(void) +{ +} + +/* external prototypes for monitor interface routines */ + +void outbyte( char ); +char inbyte( void ); + +/* + * console_outbyte_polled + * + * This routine transmits a character using polling. + */ + +void console_outbyte_polled( + int port, + char ch +) +{ + outbyte( ch ); +} + +/* + * console_inbyte_nonblocking + * + * This routine polls for a character. + */ + +int console_inbyte_nonblocking( + int port +) +{ + char c; + + c = inbyte(); + if (!c) + return -1; + return c; +} + +/* + * To support printk + */ + +#include <rtems/bspIo.h> + +static void PSIM_output_char(char c) { console_outbyte_polled( 0, c ); } + +BSP_output_char_function_type BSP_output_char = PSIM_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; diff --git a/bsps/powerpc/psim/console/consupp.S b/bsps/powerpc/psim/console/consupp.S new file mode 100644 index 0000000000..bb9e834fc6 --- /dev/null +++ b/bsps/powerpc/psim/console/consupp.S @@ -0,0 +1,33 @@ +/* + * Adapted from the mvme-inbyte.S and mvme-outbyte.S files in libgloss. + * These should work on all targets using the ppcbug monitor. + * + * Copyright (c) 1995 Cygnus Support + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + */ + +#include "ppc-asm.h" + + .file "support.s" + .text +FUNC_START(outbyte) + li r10,0x20 + sc + blr +FUNC_END(outbyte) + + .text +FUNC_START(inbyte) + li r10,0x0 + sc + blr +FUNC_END(inbyte) diff --git a/bsps/powerpc/qemuppc/console/console-io.c b/bsps/powerpc/qemuppc/console/console-io.c new file mode 100644 index 0000000000..738bd27966 --- /dev/null +++ b/bsps/powerpc/qemuppc/console/console-io.c @@ -0,0 +1,77 @@ +/* + * This file contains the hardware specific portions of the TTY driver + * for the serial ports on the qemuppc. + */ + +/* + * COPYRIGHT (c) 1989-2008. + * On-Line Applications Research Corporation (OAR). + * + * 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 <bsp.h> +#include <bsp/console-polled.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <assert.h> + +static void +__outb(int port, unsigned char v) +{ + *((volatile unsigned char *)(0x80000000 + port)) = v; +} + +static unsigned char +__inb(int port) +{ + return *((volatile unsigned char *)(0x80000000 + port)); +} + +/* + * console_initialize_hardware + * + * This routine initializes the console hardware. + * + */ +void console_initialize_hardware(void) +{ + return; +} + +/* + * console_outbyte_polled + * + * This routine transmits a character using polling. + */ +void console_outbyte_polled( + int port, + char ch +) +{ + __outb(0x3f8 + 0x00, ch); +} + +/* + * console_inbyte_nonblocking + * + * This routine polls for a character. + */ +int console_inbyte_nonblocking( + int port +) +{ + + if ( __inb(0x3f8 + 0x05) & 0x01 ) + return __inb(0x3f8 + 0x00); + return -1; +} + +#include <rtems/bspIo.h> + +static void simBSP_output_char(char c) { console_outbyte_polled( 0, c ); } + +BSP_output_char_function_type BSP_output_char = simBSP_output_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; diff --git a/bsps/powerpc/qoriq/console/console-config.c b/bsps/powerpc/qoriq/console/console-config.c new file mode 100644 index 0000000000..4c1ca1d3f6 --- /dev/null +++ b/bsps/powerpc/qoriq/console/console-config.c @@ -0,0 +1,330 @@ +/** + * @file + * + * @ingroup QorIQ + * + * @brief Console configuration. + */ + +/* + * Copyright (c) 2010, 2017 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 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.org/license/LICENSE. + */ + +#include <string.h> + +#include <libfdt.h> + +#include <rtems/bspIo.h> + +#include <libchip/ns16550.h> + +#include <asm/epapr_hcalls.h> + +#include <bsp.h> +#include <bsp/fdt.h> +#include <bsp/irq.h> +#include <bsp/qoriq.h> +#include <bsp/intercom.h> +#include <bsp/uart-bridge.h> +#include <bsp/console-termios.h> + +static void output_char(char c); + +#ifdef QORIQ_IS_HYPERVISOR_GUEST +typedef struct { + rtems_termios_device_context base; + uint32_t handle; +} qoriq_bc_context; + +static bool qoriq_bc_probe(rtems_termios_device_context *base) +{ + qoriq_bc_context *ctx; + const void *fdt; + int node; + const uint32_t *handle; + int len; + + fdt = bsp_fdt_get(); + + node = fdt_node_offset_by_compatible(fdt, -1, "epapr,hv-byte-channel"); + if (node < 0) { + return false; + } + + handle = fdt_getprop(fdt, node, "hv-handle", &len); + if (handle == NULL || len != 4) { + return false; + } + + ctx = (qoriq_bc_context *) base; + ctx->handle = fdt32_to_cpu(*handle); + + BSP_output_char = output_char; + return true; +} + +static int qoriq_bc_read_polled(rtems_termios_device_context *base) +{ + qoriq_bc_context *ctx; + char buf[EV_BYTE_CHANNEL_MAX_BYTES]; + unsigned int count; + unsigned int status; + + ctx = (qoriq_bc_context *) base; + count = 1; + status = ev_byte_channel_receive(ctx->handle, &count, buf); + + if (status != EV_SUCCESS || count == 0) { + return -1; + } + + return (unsigned char) buf[0]; +} + +static void qoriq_bc_write_polled( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ + qoriq_bc_context *ctx; + uint32_t handle; + + ctx = (qoriq_bc_context *) base; + handle = ctx->handle; + + while (len > 0) { + unsigned int count; + unsigned int status; + char buf2[EV_BYTE_CHANNEL_MAX_BYTES]; + const char *out; + + if (len < EV_BYTE_CHANNEL_MAX_BYTES) { + count = len; + out = memcpy(buf2, buf, len); + } else { + count = EV_BYTE_CHANNEL_MAX_BYTES; + out = buf; + } + + status = ev_byte_channel_send(handle, &count, out); + + if (status == EV_SUCCESS) { + len -= count; + buf += count; + } + } +} + +static const rtems_termios_device_handler qoriq_bc_handler_polled = { + .poll_read = qoriq_bc_read_polled, + .write = qoriq_bc_write_polled, + .mode = TERMIOS_POLLED +}; + +static qoriq_bc_context qoriq_bc_context_0 = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("BC 0"), +}; +#endif /* QORIQ_IS_HYPERVISOR_GUEST */ + +#if (QORIQ_UART_0_ENABLE + QORIQ_UART_BRIDGE_0_ENABLE == 2) \ + || (QORIQ_UART_1_ENABLE + QORIQ_UART_BRIDGE_1_ENABLE == 2) + #define BRIDGE_MASTER +#elif QORIQ_UART_BRIDGE_0_ENABLE || QORIQ_UART_BRIDGE_1_ENABLE + #define BRIDGE_SLAVE +#endif + +#ifdef BSP_USE_UART_INTERRUPTS + #define DEVICE_FNS &ns16550_handler_interrupt +#else + #define DEVICE_FNS &ns16550_handler_polled +#endif + +#if QORIQ_UART_0_ENABLE || QORIQ_UART_1_ENABLE + static bool uart_probe(rtems_termios_device_context *base) + { + ns16550_context *ctx = (ns16550_context *) base; + + ctx->clock = BSP_bus_frequency; + + return ns16550_probe(base); + } + + static uint8_t get_register(uintptr_t addr, uint8_t i) + { + volatile uint8_t *reg = (uint8_t *) addr; + + return reg [i]; + } + + static void set_register(uintptr_t addr, uint8_t i, uint8_t val) + { + volatile uint8_t *reg = (uint8_t *) addr; + + reg [i] = val; + } +#endif + +#if QORIQ_UART_0_ENABLE +static ns16550_context qoriq_uart_context_0 = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART 0"), + .get_reg = get_register, + .set_reg = set_register, + .port = (uintptr_t) &qoriq.uart_0, + .irq = QORIQ_IRQ_DUART_1, + .initial_baud = BSP_CONSOLE_BAUD +}; +#endif + +#if QORIQ_UART_1_ENABLE +static ns16550_context qoriq_uart_context_1 = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART 1"), + .get_reg = get_register, + .set_reg = set_register, + .port = (uintptr_t) &qoriq.uart_1, + .irq = QORIQ_IRQ_DUART_1, + .initial_baud = BSP_CONSOLE_BAUD +}; +#endif + +#ifdef BRIDGE_MASTER + #define BRIDGE_PROBE qoriq_uart_bridge_master_probe + #define BRIDGE_FNS &qoriq_uart_bridge_master + #if QORIQ_UART_BRIDGE_0_ENABLE + static uart_bridge_master_context bridge_0_context = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART Bridge 0"), + .device_path = "/dev/ttyS0", + .type = INTERCOM_TYPE_UART_0, + .transmit_fifo = RTEMS_CHAIN_INITIALIZER_EMPTY( + bridge_0_context.transmit_fifo + ) + }; + #define BRIDGE_0_CONTEXT &bridge_0_context.base + #endif + #if QORIQ_UART_BRIDGE_1_ENABLE + static uart_bridge_master_context bridge_1_context = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART Bridge 1"), + .device_path = "/dev/ttyS1", + .type = INTERCOM_TYPE_UART_1, + .transmit_fifo = RTEMS_CHAIN_INITIALIZER_EMPTY( + bridge_1_context.transmit_fifo + ) + }; + #define BRIDGE_1_CONTEXT &bridge_1_context.base + #endif +#endif + +#ifdef BRIDGE_SLAVE + #define BRIDGE_PROBE console_device_probe_default + #define BRIDGE_FNS &qoriq_uart_bridge_slave + #if QORIQ_UART_BRIDGE_0_ENABLE + static uart_bridge_slave_context bridge_0_context = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART Bridge 0"), + .type = INTERCOM_TYPE_UART_0, + .transmit_fifo = RTEMS_CHAIN_INITIALIZER_EMPTY( + bridge_0_context.transmit_fifo + ) + }; + #define BRIDGE_0_CONTEXT &bridge_0_context.base + #endif + #if QORIQ_UART_BRIDGE_1_ENABLE + static uart_bridge_slave_context bridge_1_context = { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("UART Bridge 1"), + .type = INTERCOM_TYPE_UART_1, + .transmit_fifo = RTEMS_CHAIN_INITIALIZER_EMPTY( + bridge_1_context.transmit_fifo + ) + }; + #define BRIDGE_1_CONTEXT &bridge_1_context.base + #endif +#endif + +const console_device console_device_table[] = { + #ifdef QORIQ_IS_HYPERVISOR_GUEST + { + .device_file = "/dev/ttyBC0", + .probe = qoriq_bc_probe, + .handler = &qoriq_bc_handler_polled, + .context = &qoriq_bc_context_0.base + }, + #endif + #if QORIQ_UART_0_ENABLE + { + .device_file = "/dev/ttyS0", + .probe = uart_probe, + .handler = DEVICE_FNS, + .context = &qoriq_uart_context_0.base + }, + #endif + #if QORIQ_UART_1_ENABLE + { + .device_file = "/dev/ttyS1", + .probe = uart_probe, + .handler = DEVICE_FNS, + .context = &qoriq_uart_context_1.base + }, + #endif + #if QORIQ_UART_BRIDGE_0_ENABLE + { + #if QORIQ_UART_1_ENABLE + .device_file = "/dev/ttyB0", + #else + .device_file = "/dev/ttyS0", + #endif + .probe = BRIDGE_PROBE, + .handler = BRIDGE_FNS, + .context = BRIDGE_0_CONTEXT + }, + #endif + #if QORIQ_UART_BRIDGE_1_ENABLE + { + #if QORIQ_UART_1_ENABLE + .device_file = "/dev/ttyB1", + #else + .device_file = "/dev/ttyS1", + #endif + .probe = BRIDGE_PROBE, + .handler = BRIDGE_FNS, + .context = BRIDGE_1_CONTEXT + } + #endif +}; + +const size_t console_device_count = RTEMS_ARRAY_SIZE(console_device_table); + +static void output_char(char c) +{ + rtems_termios_device_context *base = console_device_table[0].context; + +#ifdef QORIQ_IS_HYPERVISOR_GUEST + qoriq_bc_write_polled(base, &c, 1); +#else + ns16550_polled_putchar(base, c); +#endif +} + +#ifdef QORIQ_IS_HYPERVISOR_GUEST +static void qoriq_bc_output_char_init(char c) +{ + rtems_termios_device_context *base = console_device_table[0].context; + + qoriq_bc_probe(base); + output_char(c); +} + +BSP_output_char_function_type BSP_output_char = qoriq_bc_output_char_init; +#else +BSP_output_char_function_type BSP_output_char = output_char; +#endif + +BSP_polling_getchar_function_type BSP_poll_char = NULL; diff --git a/bsps/powerpc/qoriq/console/uart-bridge-master.c b/bsps/powerpc/qoriq/console/uart-bridge-master.c new file mode 100644 index 0000000000..588e0a42ad --- /dev/null +++ b/bsps/powerpc/qoriq/console/uart-bridge-master.c @@ -0,0 +1,181 @@ +/** + * @file + * + * @ingroup QorIQUartBridge + * + * @brief UART bridge master implementation. + */ + +/* + * Copyright (c) 2011-2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 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.org/license/LICENSE. + */ + +#include <sys/stat.h> +#include <assert.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> + +#include <bspopts.h> +#include <bsp/uart-bridge.h> + +#define TRANSMIT_EVENT RTEMS_EVENT_13 + +static void serial_settings(int fd) +{ + struct termios term; + int rv = tcgetattr(fd, &term); + assert(rv == 0); + + term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + term.c_oflag &= ~OPOST; + term.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + term.c_cflag &= ~(CSIZE | PARENB); + term.c_cflag |= CS8; + + term.c_cc [VMIN] = 1; + term.c_cc [VTIME] = 1; + + rv = tcsetattr(fd, TCSANOW, &term); + assert(rv == 0); +} + +static void uart_bridge_master_service(intercom_packet *packet, void *arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uart_bridge_master_context *ctx = arg; + + sc = rtems_chain_append_with_notification( + &ctx->transmit_fifo, + &packet->glue.node, + ctx->transmit_task, + TRANSMIT_EVENT + ); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void receive_task(rtems_task_argument arg) +{ + uart_bridge_master_context *ctx = (uart_bridge_master_context *) arg; + intercom_type type = ctx->type; + + int fd = open(ctx->device_path, O_RDONLY); + assert(fd >= 0); + + serial_settings(fd); + + while (true) { + intercom_packet *packet = qoriq_intercom_allocate_packet( + type, + INTERCOM_SIZE_64 + ); + ssize_t in = read(fd, packet->data, packet->size - 1); + if (in > 0) { + packet->size = (size_t) in; + qoriq_intercom_send_packet(QORIQ_UART_BRIDGE_SLAVE_CORE, packet); + } else { + qoriq_intercom_free_packet(packet); + } + } +} + +static void transmit_task(rtems_task_argument arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uart_bridge_master_context *ctx = (uart_bridge_master_context *) arg; + rtems_chain_control *fifo = &ctx->transmit_fifo; + + int fd = open(ctx->device_path, O_WRONLY); + assert(fd >= 0); + + serial_settings(fd); + + while (true) { + intercom_packet *packet = NULL; + sc = rtems_chain_get_with_wait( + fifo, + TRANSMIT_EVENT, + RTEMS_NO_TIMEOUT, + (rtems_chain_node **) &packet + ); + assert(sc == RTEMS_SUCCESSFUL); + write(fd, packet->data, packet->size); + qoriq_intercom_free_packet(packet); + } +} + +static rtems_id create_task( + char name, + rtems_task_entry entry, + uart_bridge_master_context *ctx +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_id task = RTEMS_ID_NONE; + char index = (char) ('0' + ctx->type - INTERCOM_TYPE_UART_0); + + sc = rtems_task_create( + rtems_build_name('U', 'B', name, index), + QORIQ_UART_BRIDGE_TASK_PRIORITY, + 0, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &task + ); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start( + task, + entry, + (rtems_task_argument) ctx + ); + assert(sc == RTEMS_SUCCESSFUL); + + return task; +} + +bool qoriq_uart_bridge_master_probe(rtems_termios_device_context *base) +{ + uart_bridge_master_context *ctx = (uart_bridge_master_context *) base; + intercom_type type = ctx->type; + + qoriq_intercom_service_install(type, uart_bridge_master_service, ctx); + create_task('R', receive_task, ctx); + ctx->transmit_task = create_task('T', transmit_task, ctx); + + return true; +} + +static bool first_open( + struct rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + return false; +} + +static bool set_attributes( + rtems_termios_device_context *base, + const struct termios *term +) +{ + return false; +} + +const rtems_termios_device_handler qoriq_uart_bridge_master = { + .first_open = first_open, + .set_attributes = set_attributes, + .mode = TERMIOS_POLLED +}; diff --git a/bsps/powerpc/qoriq/console/uart-bridge-slave.c b/bsps/powerpc/qoriq/console/uart-bridge-slave.c new file mode 100644 index 0000000000..44d4cfb712 --- /dev/null +++ b/bsps/powerpc/qoriq/console/uart-bridge-slave.c @@ -0,0 +1,195 @@ +/** + * @file + * + * @ingroup QorIQUartBridge + * + * @brief UART bridge slave implementation. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 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.org/license/LICENSE. + */ + +#include <assert.h> + +#include <libchip/sersupp.h> + +#include <bspopts.h> +#include <bsp/uart-bridge.h> + +#define TRANSMIT_EVENT RTEMS_EVENT_13 + +static rtems_mode disable_preemption(void) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_mode prev_mode = 0; + + sc = rtems_task_mode (RTEMS_NO_PREEMPT, RTEMS_PREEMPT_MASK, &prev_mode); + assert(sc == RTEMS_SUCCESSFUL); + + return prev_mode; +} + +static void restore_preemption(rtems_mode prev_mode) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + sc = rtems_task_mode (prev_mode, RTEMS_PREEMPT_MASK, &prev_mode); + assert(sc == RTEMS_SUCCESSFUL); +} + +static void uart_bridge_slave_service(intercom_packet *packet, void *arg) +{ + uart_bridge_slave_context *ctx = arg; + struct rtems_termios_tty *tty = ctx->tty; + + /* Workaround for https://www.rtems.org/bugzilla/show_bug.cgi?id=1736 */ + rtems_mode prev_mode = disable_preemption(); + + rtems_termios_enqueue_raw_characters(tty, packet->data, (int) packet->size); + qoriq_intercom_free_packet(packet); + + restore_preemption(prev_mode); +} + +static void transmit_task(rtems_task_argument arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) arg; + rtems_chain_control *fifo = &ctx->transmit_fifo; + struct rtems_termios_tty *tty = ctx->tty; + + while (true) { + intercom_packet *packet = NULL; + sc = rtems_chain_get_with_wait( + fifo, + TRANSMIT_EVENT, + RTEMS_NO_TIMEOUT, + (rtems_chain_node **) &packet + ); + assert(sc == RTEMS_SUCCESSFUL); + + /* Workaround for https://www.rtems.org/bugzilla/show_bug.cgi?id=1736 */ + rtems_mode prev_mode = disable_preemption(); + + size_t size = packet->size; + qoriq_intercom_send_packet(QORIQ_UART_BRIDGE_MASTER_CORE, packet); + rtems_termios_dequeue_characters(tty, (int) size); + + restore_preemption(prev_mode); + } +} + +static void create_transmit_task( + uart_bridge_slave_context *ctx +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_id task = RTEMS_ID_NONE; + char index = (char) ('0' + ctx->type - INTERCOM_TYPE_UART_0); + + sc = rtems_task_create( + rtems_build_name('U', 'B', 'T', index), + QORIQ_UART_BRIDGE_TASK_PRIORITY, + 0, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &task + ); + assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start( + task, + transmit_task, + (rtems_task_argument) ctx + ); + assert(sc == RTEMS_SUCCESSFUL); + + ctx->transmit_task = task; +} + +static bool first_open( + struct rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; + intercom_type type = ctx->type; + + ctx->tty = tty; + rtems_termios_set_initial_baud(tty, 115200); + create_transmit_task(ctx); + qoriq_intercom_service_install(type, uart_bridge_slave_service, ctx); + + return true; +} + +static void last_close( + struct rtems_termios_tty *tty, + rtems_termios_device_context *base, + rtems_libio_open_close_args_t *args +) +{ + uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; + + qoriq_intercom_service_remove(ctx->type); +} + +static void write_with_interrupts( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ + if (len > 0) { + rtems_status_code sc = RTEMS_SUCCESSFUL; + uart_bridge_slave_context *ctx = (uart_bridge_slave_context *) base; + intercom_packet *packet = qoriq_intercom_allocate_packet( + ctx->type, + INTERCOM_SIZE_64 + ); + + packet->size = len; + memcpy(packet->data, buf, len); + + /* + * Due to the lovely Termios implementation we have to hand this over to + * another context. + */ + sc = rtems_chain_append_with_notification( + &ctx->transmit_fifo, + &packet->glue.node, + ctx->transmit_task, + TRANSMIT_EVENT + ); + assert(sc == RTEMS_SUCCESSFUL); + } +} + +static bool set_attributes( + rtems_termios_device_context *base, + const struct termios *term +) +{ + return false; +} + +const rtems_termios_device_handler qoriq_uart_bridge_slave = { + .first_open = first_open, + .last_close = last_close, + .write = write_with_interrupts, + .set_attributes = set_attributes, + .mode = TERMIOS_IRQ_DRIVEN +}; diff --git a/bsps/powerpc/shared/console/console.c b/bsps/powerpc/shared/console/console.c new file mode 100644 index 0000000000..f275683cc2 --- /dev/null +++ b/bsps/powerpc/shared/console/console.c @@ -0,0 +1,314 @@ +/* + * console.c -- console I/O package + * + * Copyright (C) 1999 Eric Valette. valette@crf.canon.fr + * + * This code is based on the pc386 BSP console.c so the following + * copyright also applies : + * + * (C) Copyright 1997 - + * - NavIST Group - Real-Time Distributed Systems and Industrial Automation + * + * Till Straumann, <strauman@slac.stanford.edu>, 12/20/2001 + * separate BSP specific stuff from generics... + * + * http://pandora.ist.utl.pt + * + * Instituto Superior Tecnico * Lisboa * PORTUGAL + * 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 <assert.h> +#include <inttypes.h> + +#include <bsp.h> +#include <bsp/irq.h> +#include <rtems/bspIo.h> +#include <rtems/libio.h> +#include <rtems/console.h> +#include <rtems/termiostypes.h> +#include <termios.h> +#include <bsp/uart.h> +#include <rtems/bspIo.h> /* printk */ + +/* Definitions for BSPConsolePort */ +/* + * Possible value for console input/output : + * BSP_CONSOLE_PORT_CONSOLE + * BSP_UART_COM1 + * BSP_UART_COM2 + */ +int BSPConsolePort = BSP_CONSOLE_PORT; + +int BSPBaseBaud = BSP_UART_BAUD_BASE; + +/* + * TERMIOS_OUTPUT_MODE should be a 'bspopts.h/configure'-able option; + * we could even make it a link-time option (but that would require + * small changes)... + */ +#if defined(USE_POLLED_IO) + #define TERMIOS_OUTPUT_MODE TERMIOS_POLLED +#elif defined(USE_TASK_DRIVEN_IO) + #define TERMIOS_OUTPUT_MODE TERMIOS_TASK_DRIVEN +#else + #define TERMIOS_OUTPUT_MODE TERMIOS_IRQ_DRIVEN +#endif + +/*-------------------------------------------------------------------------+ +| External Prototypes ++--------------------------------------------------------------------------*/ + +static int conSetAttr(int minor, const struct termios *); + +typedef struct TtySTblRec_ { + char *name; + rtems_irq_hdl isr; +} TtySTblRec, *TtySTbl; + +static TtySTblRec ttyS[]={ + { "/dev/ttyS0", +#ifdef BSP_UART_IOBASE_COM1 + BSP_uart_termios_isr_com1 +#else + 0 +#endif + }, + { "/dev/ttyS1", +#ifdef BSP_UART_IOBASE_COM2 + BSP_uart_termios_isr_com2 +#else + 0 +#endif + }, +}; + +/*-------------------------------------------------------------------------+ +| Console device driver INITIALIZE entry point. ++--------------------------------------------------------------------------+ +| Initilizes the I/O console (keyboard + VGA display) driver. ++--------------------------------------------------------------------------*/ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + + /* + * The video was initialized in the start.s code and does not need + * to be reinitialized. + */ + + /* + * Set up TERMIOS + */ + rtems_termios_initialize(); + + /* + * Do device-specific initialization + */ + + /* RTEMS calls this routine once with 'minor'==0; loop through + * all known instances... + */ + + for (minor=0; minor < sizeof(ttyS)/sizeof(ttyS[0]); minor++) { + char *nm; + /* + * Skip ports (possibly not supported by BSP...) we have no ISR for + */ + if ( ! ttyS[minor].isr ) + continue; + /* + * Register the device + */ + status = rtems_io_register_name ((nm=ttyS[minor].name), major, minor); + if ( RTEMS_SUCCESSFUL==status && BSPConsolePort == minor) { + printk("Registering /dev/console as minor %" PRIu32 " (==%s)\n", + minor, + ttyS[minor].name); + /* also register an alias */ + status = rtems_io_register_name ( (nm="/dev/console"), major, minor); + } + + if (status != RTEMS_SUCCESSFUL) { + printk("Error registering %s!\n",nm); + rtems_fatal_error_occurred (status); + } + } + + return RTEMS_SUCCESSFUL; +} /* console_initialize */ + +#if !defined(USE_POLLED_IO) +static int console_first_open(int major, int minor, void *arg) +{ + rtems_status_code status; + + /* must not open a minor device we have no ISR for */ + assert( minor>=0 && minor < sizeof(ttyS)/sizeof(ttyS[0]) && ttyS[minor].isr ); + + /* 9600-8-N-1 */ + BSP_uart_init(minor, 9600, 0); + status = BSP_uart_install_isr(minor, ttyS[minor].isr); + if (!status) { + printk("Error installing serial console interrupt handler for '%s'!\n", + ttyS[minor].name); + rtems_fatal_error_occurred(status); + } + + /* + * Pass data area info down to driver + */ + BSP_uart_termios_set(minor, ((rtems_libio_open_close_args_t *)arg)->iop->data1); + + /* Enable interrupts on channel */ + BSP_uart_intr_ctrl(minor, BSP_UART_INTR_CTRL_TERMIOS); + + return 0; +} +#endif + +#if !defined(USE_POLLED_IO) +static int console_last_close(int major, int minor, void *arg) +{ + BSP_uart_remove_isr(minor, ttyS[minor].isr); + return 0; +} +#endif + +/*-------------------------------------------------------------------------+ +| Console device driver OPEN entry point ++--------------------------------------------------------------------------*/ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + static rtems_termios_callbacks cb = +#if defined(USE_POLLED_IO) + { + NULL, /* firstOpen */ + NULL, /* lastClose */ + NULL, /* pollRead */ + BSP_uart_termios_write_polled, /* write */ + conSetAttr, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* outputUsesInterrupts */ + }; +#else + { + console_first_open, /* firstOpen */ + console_last_close, /* lastClose */ +#ifdef USE_TASK_DRIVEN_IO + BSP_uart_termios_read_com, /* pollRead */ +#else + NULL, /* pollRead */ +#endif + BSP_uart_termios_write_com, /* write */ + conSetAttr, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_OUTPUT_MODE /* outputUsesInterrupts */ + }; +#endif + + status = rtems_termios_open (major, minor, arg, &cb); + + if (status != RTEMS_SUCCESSFUL) { + printk("Error opening console device\n"); + return status; + } + + return RTEMS_SUCCESSFUL; +} + +/*-------------------------------------------------------------------------+ +| Console device driver CLOSE entry point ++--------------------------------------------------------------------------*/ +rtems_device_driver +console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_device_driver res = RTEMS_SUCCESSFUL; + + res = rtems_termios_close (arg); + + return res; +} /* console_close */ + +/*-------------------------------------------------------------------------+ +| Console device driver READ entry point. ++--------------------------------------------------------------------------+ +| Read characters from the I/O console. We only have stdin. ++--------------------------------------------------------------------------*/ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_read (arg); +} /* console_read */ + +/*-------------------------------------------------------------------------+ +| Console device driver WRITE entry point. ++--------------------------------------------------------------------------+ +| Write characters to the I/O console. Stderr and stdout are the same. ++--------------------------------------------------------------------------*/ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_write (arg); +} /* console_write */ + +/* + * Handle ioctl request. + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ +/* does the BSP support break callbacks ? */ +#if defined(BIOCSETBREAKCB) && defined(BIOCGETBREAKCB) + rtems_libio_ioctl_args_t *ioa=arg; + switch (ioa->command) { + case BIOCSETBREAKCB: return BSP_uart_set_break_cb(minor, ioa); + case BIOCGETBREAKCB: return BSP_uart_get_break_cb(minor, ioa); + default: break; + } +#endif + return rtems_termios_ioctl (arg); +} + +static int conSetAttr( + int minor, + const struct termios *t +) +{ + rtems_termios_baud_t baud; + + baud = rtems_termios_baud_to_number(t->c_ospeed); + if ( baud > 115200 ) + rtems_fatal_error_occurred (RTEMS_INTERNAL_ERROR); + + BSP_uart_set_baud(minor, baud); + + return 0; +} diff --git a/bsps/powerpc/shared/console/uart.c b/bsps/powerpc/shared/console/uart.c new file mode 100644 index 0000000000..62212b98db --- /dev/null +++ b/bsps/powerpc/shared/console/uart.c @@ -0,0 +1,781 @@ +/* + * This software is Copyright (C) 1998 by T.sqware - all rights limited + * It is provided in to the public domain "as is", can be freely modified + * as far as this copyight notice is kept unchanged, but does not imply + * an endorsement by T.sqware of the product in which it is included. + */ + +#include <stdint.h> +#include <stdio.h> +#include <bsp.h> +#include <bsp/irq.h> +#include <bsp/uart.h> +#include <rtems/libio.h> +#include <rtems/bspIo.h> +#include <rtems/termiostypes.h> +#include <termios.h> +#include <assert.h> + +/* + * Basic 16552 driver + */ + +struct uart_data +{ + unsigned long ioBase; + int irq; + int hwFlow; + int baud; + BSP_UartBreakCbRec breakCallback; + int ioMode; +}; + +/* + * Initialization of BSP specific data. + * The constants are pulled in from a BSP + * specific file, whereas all of the code + * in this file is generic and makes no + * assumptions about addresses, irq vectors + * etc... + */ + +#define UART_UNSUPP ((unsigned long)(-1)) + +static struct uart_data uart_data[2] = { + { +#ifdef BSP_UART_IOBASE_COM1 + BSP_UART_IOBASE_COM1, + BSP_UART_COM1_IRQ, +#else + UART_UNSUPP, + -1, +#endif + }, + { +#ifdef BSP_UART_IOBASE_COM2 + BSP_UART_IOBASE_COM2, + BSP_UART_COM2_IRQ, +#else + UART_UNSUPP, + -1, +#endif + }, +}; + +#define MAX_UARTS (sizeof(uart_data)/sizeof(uart_data[0])) +#define SANITY_CHECK(uart) \ + assert( MAX_UARTS > (unsigned)(uart) && uart_data[(uart)].ioBase != UART_UNSUPP ) +/* + * Macros to read/write register of uart, if configuration is + * different just rewrite these macros + */ + +static inline unsigned char +uread(int uart, unsigned int reg) +{ + return in_8((uint8_t*)(uart_data[uart].ioBase + reg)); +} + +static inline void +uwrite(int uart, int reg, unsigned int val) +{ + out_8((uint8_t*)(uart_data[uart].ioBase + reg), val); +} + + +static void +uartError(int uart, void *termiosPrivate) +{ + unsigned char uartStatus, dummy; + BSP_UartBreakCbProc h; + + uartStatus = uread(uart, LSR); + dummy = uread(uart, RBR); + +#ifdef UARTDEBUG + if (uartStatus & OE) + printk("********* Over run Error **********\n"); + if (uartStatus & PE) + printk("********* Parity Error **********\n"); + if (uartStatus & FE) + printk("********* Framing Error **********\n"); + if (uartStatus & BI) { + printk("********* BREAK INTERRUPT *********\n"); +#endif + if ((h=uart_data[uart].breakCallback.handler)) { + h(uart, + (dummy<<8)|uartStatus, + termiosPrivate, + uart_data[uart].breakCallback.private); + } +#ifdef UARTDEBUG + if (uartStatus & ERFIFO) + printk("********* Error receive Fifo **********\n"); +#endif +} + +/* + * Uart initialization, it is hardcoded to 8 bit, no parity, + * one stop bit, FIFO, things to be changed + * are baud rate and nad hw flow control, + * and longest rx fifo setting + */ +void +BSP_uart_init(int uart, int baud, int hwFlow) +{ + unsigned char tmp; + + /* Sanity check */ + SANITY_CHECK(uart); + + /* Make sure any printk activity drains before + * re-initializing. + */ + while ( ! (uread(uart, LSR) & TEMT) ) + ; + + switch(baud) + { + case 50: + case 75: + case 110: + case 134: + case 300: + case 600: + case 1200: + case 2400: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + break; + default: + assert(0); + return; + } + + /* Set DLAB bit to 1 */ + uwrite(uart, LCR, DLAB); + + if ( (int)BSPBaseBaud <= 0 ) { + /* Use current divisor assuming BSPBaseBaud gives us the current speed */ + BSPBaseBaud = BSPBaseBaud ? -BSPBaseBaud : 9600; + BSPBaseBaud *= ((uread(uart, DLM) << 8) | uread(uart, DLL)); + } + + /* Set baud rate */ + uwrite(uart, DLL, (BSPBaseBaud/baud) & 0xff); + uwrite(uart, DLM, ((BSPBaseBaud/baud) >> 8) & 0xff); + + /* 8-bit, no parity , 1 stop */ + uwrite(uart, LCR, CHR_8_BITS); + + /* Set DTR, RTS and OUT2 high */ + uwrite(uart, MCR, DTR | RTS | OUT_2); + + /* Enable FIFO */ + uwrite(uart, FCR, FIFO_EN | XMIT_RESET | RCV_RESET | RECEIVE_FIFO_TRIGGER12); + + /* Disable Interrupts */ + uwrite(uart, IER, 0); + + /* Read status to clear them */ + tmp = uread(uart, LSR); + tmp = uread(uart, RBR); + tmp = uread(uart, MSR); + (void) tmp; /* avoid set but not used warning */ + + /* Remember state */ + uart_data[uart].hwFlow = hwFlow; + uart_data[uart].baud = baud; + return; +} + +/* + * Set baud + */ +void +BSP_uart_set_baud(int uart, int baud) +{ + unsigned char mcr, ier; + + /* Sanity check */ + SANITY_CHECK(uart); + + /* + * This function may be called whenever TERMIOS parameters + * are changed, so we have to make sure that baud change is + * indeed required. + */ + + if(baud == uart_data[uart].baud) + { + return; + } + + mcr = uread(uart, MCR); + ier = uread(uart, IER); + + BSP_uart_init(uart, baud, uart_data[uart].hwFlow); + + uwrite(uart, MCR, mcr); + uwrite(uart, IER, ier); + + return; +} + +/* + * Enable/disable interrupts + */ +void +BSP_uart_intr_ctrl(int uart, int cmd) +{ + + SANITY_CHECK(uart); + + switch(cmd) + { + case BSP_UART_INTR_CTRL_DISABLE: + uwrite(uart, IER, INTERRUPT_DISABLE); + break; + case BSP_UART_INTR_CTRL_ENABLE: + if(uart_data[uart].hwFlow) + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + TRANSMIT_ENABLE | + RECEIVER_LINE_ST_ENABLE | + MODEM_ENABLE + ) + ); + } + else + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + TRANSMIT_ENABLE | + RECEIVER_LINE_ST_ENABLE + ) + ); + } + break; + case BSP_UART_INTR_CTRL_TERMIOS: + if(uart_data[uart].hwFlow) + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + RECEIVER_LINE_ST_ENABLE | + MODEM_ENABLE + ) + ); + } + else + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + RECEIVER_LINE_ST_ENABLE + ) + ); + } + break; + case BSP_UART_INTR_CTRL_GDB: + uwrite(uart, IER, RECEIVE_ENABLE); + break; + default: + assert(0); + break; + } + + return; +} + +void +BSP_uart_throttle(int uart) +{ + unsigned int mcr; + + SANITY_CHECK(uart); + + if(!uart_data[uart].hwFlow) + { + /* Should not happen */ + assert(0); + return; + } + mcr = uread (uart, MCR); + /* RTS down */ + mcr &= ~RTS; + uwrite(uart, MCR, mcr); + + return; +} + +void +BSP_uart_unthrottle(int uart) +{ + unsigned int mcr; + + SANITY_CHECK(uart); + + if(!uart_data[uart].hwFlow) + { + /* Should not happen */ + assert(0); + return; + } + mcr = uread (uart, MCR); + /* RTS up */ + mcr |= RTS; + uwrite(uart, MCR, mcr); + + return; +} + +/* + * Status function, -1 if error + * detected, 0 if no received chars available, + * 1 if received char available, 2 if break + * is detected, it will eat break and error + * chars. It ignores overruns - we cannot do + * anything about - it execpt count statistics + * and we are not counting it. + */ +int +BSP_uart_polled_status(int uart) +{ + unsigned char val; + + SANITY_CHECK(uart); + + val = uread(uart, LSR); + + if(val & BI) + { + /* BREAK found, eat character */ + uread(uart, RBR); + return BSP_UART_STATUS_BREAK; + } + + if((val & (DR | OE | FE)) == 1) + { + /* No error, character present */ + return BSP_UART_STATUS_CHAR; + } + + if((val & (DR | OE | FE)) == 0) + { + /* Nothing */ + return BSP_UART_STATUS_NOCHAR; + } + + /* + * Framing or parity error + * eat character + */ + uread(uart, RBR); + + return BSP_UART_STATUS_ERROR; +} + +/* + * Polled mode write function + */ +void +BSP_uart_polled_write(int uart, int val) +{ + unsigned char val1; + + /* Sanity check */ + SANITY_CHECK(uart); + + for(;;) + { + if((val1=uread(uart, LSR)) & THRE) + { + break; + } + } + + if(uart_data[uart].hwFlow) + { + for(;;) + { + if(uread(uart, MSR) & CTS) + { + break; + } + } + } + + uwrite(uart, THR, val & 0xff); + + return; +} + +void +BSP_output_char_via_serial(const char val) +{ + BSP_uart_polled_write(BSPConsolePort, val); +} + +/* + * Polled mode read function + */ +int +BSP_uart_polled_read(int uart) +{ + unsigned char val; + + SANITY_CHECK(uart); + + for(;;) + { + if(uread(uart, LSR) & DR) + { + break; + } + } + + val = uread(uart, RBR); + + return (int)(val & 0xff); +} + +unsigned +BSP_poll_char_via_serial() +{ + return BSP_uart_polled_read(BSPConsolePort); +} + +static void +uart_noop(const rtems_irq_connect_data *unused) +{ + return; +} + +/* note that the IRQ names contain _ISA_ for legacy + * reasons. They can be any interrupt, depending + * on the particular BSP... + */ + +static int +uart_isr_is_on(const rtems_irq_connect_data *irq) +{ + int uart; + + uart = (irq->name == BSP_UART_COM1_IRQ) ? + BSP_UART_COM1 : BSP_UART_COM2; + + return uread(uart,IER); +} + +static int +doit(int uart, rtems_irq_hdl handler, int (*p)(const rtems_irq_connect_data*)) +{ + rtems_irq_connect_data d={0}; + d.name = uart_data[uart].irq; + d.off = d.on = uart_noop; + d.isOn = uart_isr_is_on; + d.hdl = handler; + return p(&d); +} + +int +BSP_uart_install_isr(int uart, rtems_irq_hdl handler) +{ +/* Using shared interrupts by default might break things.. the + * shared IRQ installer uses malloc() and if a BSP had called this + * during early init it might not work... + */ +#ifdef BSP_UART_USE_SHARED_IRQS + return doit(uart, handler, BSP_install_rtems_shared_irq_handler); +#else + return doit(uart, handler, BSP_install_rtems_irq_handler); +#endif +} + +int +BSP_uart_remove_isr(int uart, rtems_irq_hdl handler) +{ + return doit(uart, handler, BSP_remove_rtems_irq_handler); +} + +/* ================ Termios support =================*/ + +static volatile int termios_stopped_com[2] = {0,0}; +static volatile int termios_tx_active_com[2] = {0,0}; +static void* termios_ttyp_com[2] = {NULL,NULL}; +static char termios_tx_hold_com[2] = {0,0}; +static volatile char termios_tx_hold_valid_com[2] = {0,0}; + +/* + * Set channel parameters + */ +void +BSP_uart_termios_set(int uart, void *p) +{ + struct rtems_termios_tty *ttyp = p; + unsigned char val; + SANITY_CHECK(uart); + + if(uart_data[uart].hwFlow) + { + val = uread(uart, MSR); + + termios_stopped_com[uart] = (val & CTS) ? 0 : 1; + } + else + { + termios_stopped_com[uart] = 0; + } + termios_tx_active_com[uart] = 0; + termios_ttyp_com[uart] = ttyp; + termios_tx_hold_com[uart] = 0; + termios_tx_hold_valid_com[uart] = 0; + + uart_data[uart].ioMode = ttyp->device.outputUsesInterrupts; + + return; +} + +ssize_t +BSP_uart_termios_write_polled(int minor, const char *buf, size_t len) +{ + int uart=minor; /* could differ, theoretically */ + int nwrite; + const char *b = buf; + + for (nwrite=0 ; nwrite < len ; nwrite++) { + BSP_uart_polled_write(uart, *b++); + } + return nwrite; +} + +ssize_t +BSP_uart_termios_write_com(int minor, const char *buf, size_t len) +{ + int uart=minor; /* could differ, theoretically */ + + if(len <= 0) + { + return 0; + } + + /* If the TX buffer is busy - something is royally screwed up */ + /* assert((uread(BSP_UART_COM1, LSR) & THRE) != 0); */ + + if(termios_stopped_com[uart]) + { + /* CTS low */ + termios_tx_hold_com[uart] = *buf; + termios_tx_hold_valid_com[uart] = 1; + return 0; + } + + /* Write character */ + uwrite(uart, THR, *buf & 0xff); + + /* Enable interrupts if necessary */ + if(!termios_tx_active_com[uart] && uart_data[uart].hwFlow) + { + termios_tx_active_com[uart] = 1; + uwrite(uart, IER, + (RECEIVE_ENABLE | + TRANSMIT_ENABLE | + RECEIVER_LINE_ST_ENABLE | + MODEM_ENABLE + ) + ); + } + else if(!termios_tx_active_com[uart]) + { + termios_tx_active_com[uart] = 1; + uwrite(uart, IER, + (RECEIVE_ENABLE | + TRANSMIT_ENABLE | + RECEIVER_LINE_ST_ENABLE + ) + ); + } + + return 0; +} + +int +BSP_uart_termios_read_com(int uart) +{ + int off = (int)0; + char buf[40]; + rtems_interrupt_level l; + + /* read bytes */ + while (( off < sizeof(buf) ) && ( uread(uart, LSR) & DR )) { + buf[off++] = uread(uart, RBR); + } + + /* write out data */ + if ( off > 0 ) { + rtems_termios_enqueue_raw_characters(termios_ttyp_com[uart], buf, off); + } + + /* enable receive interrupts */ + rtems_interrupt_disable(l); + uwrite(uart, IER, uread(uart, IER) | (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE)); + rtems_interrupt_enable(l); + + return ( EOF ); +} + +static void +BSP_uart_termios_isr_com(int uart) +{ + unsigned char buf[40]; + unsigned char val, ier; + int off, ret, vect; + + off = 0; + + for(;;) + { + vect = uread(uart, IIR) & 0xf; + + switch(vect) + { + case MODEM_STATUS : + val = uread(uart, MSR); + if(uart_data[uart].hwFlow) + { + if(val & CTS) + { + /* CTS high */ + termios_stopped_com[uart] = 0; + if(termios_tx_hold_valid_com[uart]) + { + termios_tx_hold_valid_com[uart] = 0; + BSP_uart_termios_write_com(uart, &termios_tx_hold_com[uart], + 1); + } + } + else + { + /* CTS low */ + termios_stopped_com[uart] = 1; + } + } + break; + case NO_MORE_INTR : + /* No more interrupts */ + if(off != 0) + { + /* Update rx buffer */ + rtems_termios_enqueue_raw_characters(termios_ttyp_com[uart], + (char *)buf, + off); + } + return; + case TRANSMITTER_HODING_REGISTER_EMPTY : + /* + * TX holding empty: we have to disable these interrupts + * if there is nothing more to send. + */ + + ret = rtems_termios_dequeue_characters(termios_ttyp_com[uart], 1); + + /* If nothing else to send disable interrupts */ + if(ret == 0 && uart_data[uart].hwFlow) + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + RECEIVER_LINE_ST_ENABLE | + MODEM_ENABLE + ) + ); + termios_tx_active_com[uart] = 0; + } + else if(ret == 0) + { + uwrite(uart, IER, + (RECEIVE_ENABLE | + RECEIVER_LINE_ST_ENABLE + ) + ); + termios_tx_active_com[uart] = 0; + } + break; + case RECEIVER_DATA_AVAIL : + case CHARACTER_TIMEOUT_INDICATION: + if ( uart_data[uart].ioMode == TERMIOS_TASK_DRIVEN ) + { + /* ensure interrupts are enabled */ + if ( (ier = uread(uart,IER)) & RECEIVE_ENABLE ) + { + /* disable interrupts and notify termios */ + ier &= ~(RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE); + uwrite(uart, IER, ier); + rtems_termios_rxirq_occured(termios_ttyp_com[uart]); + } + } + else + { + /* RX data ready */ + assert(off < sizeof(buf)); + while ( off < sizeof(buf) && ( DR & uread(uart, LSR) ) ) + buf[off++] = uread(uart, RBR); + } + break; + case RECEIVER_ERROR: + /* RX error: eat character */ + uartError(uart, termios_ttyp_com[uart]); + break; + default: + /* Should not happen */ + assert(0); + return; + } + } +} + +/* + * XXX - Note that this can now be one isr with the uart + * passed as the parameter. + */ +void +BSP_uart_termios_isr_com1(void *unused) +{ + BSP_uart_termios_isr_com(BSP_UART_COM1); +} + +void +BSP_uart_termios_isr_com2(void *unused) +{ + BSP_uart_termios_isr_com(BSP_UART_COM2); +} + +/* retrieve 'break' handler info */ +int +BSP_uart_get_break_cb(int uart, rtems_libio_ioctl_args_t *arg) +{ +BSP_UartBreakCb cb=arg->buffer; +unsigned long flags; + SANITY_CHECK(uart); + rtems_interrupt_disable(flags); + *cb = uart_data[uart].breakCallback; + rtems_interrupt_enable(flags); + arg->ioctl_return=0; + return RTEMS_SUCCESSFUL; +} + +/* install 'break' handler */ +int +BSP_uart_set_break_cb(int uart, rtems_libio_ioctl_args_t *arg) +{ +BSP_UartBreakCb cb=arg->buffer; +unsigned long flags; + SANITY_CHECK(uart); + rtems_interrupt_disable(flags); + uart_data[uart].breakCallback = *cb; + rtems_interrupt_enable(flags); + arg->ioctl_return=0; + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/powerpc/ss555/console/console.c b/bsps/powerpc/ss555/console/console.c new file mode 100644 index 0000000000..e13e4734ea --- /dev/null +++ b/bsps/powerpc/ss555/console/console.c @@ -0,0 +1,371 @@ +/* + * console.c + * + * This file contains the Intec SS555 termios serial I/O package. + * + * The SCI channels are assigned as follows + * + * Channel Device Minor + * SCI1 /dev/tty0 0 + * SCI2 /dev/tty1 1 + * + * All ports support termios. The use of termios is recommended for real-time + * applications. Termios provides buffering and input processing. When not + * using termios, processing is limited to the substitution of LF for CR on + * input, and the output of a CR following the output of a LF character. + * Note that the terminal should not send CR/LF pairs when the return key + * is pressed, and that output lines are terminated with LF/CR, not CR/LF + * (although that would be easy to change). + * + * I/O may be interrupt-driven (recommended for real-time applications) or + * polled. + * + * LIMITATIONS: + * + * It is not possible to use different I/O modes on the different ports. The + * exception is with printk. The printk port can use a different mode from + * the other ports. If this is done, it is important not to open the printk + * port from an RTEMS application. + * + * Currently, the I/O modes are determined at build time. It would be much + * better to have the mode selected at boot time based on parameters in + * NVRAM. + * + * Interrupt-driven I/O requires termios. + * + * TESTS: + * + * TO RUN THE TESTS, USE POLLED I/O WITHOUT TERMIOS SUPPORT. Some tests + * play with the interrupt masks and turn off I/O. Those tests will hang + * when interrupt-driven I/O is used. Other tests, such as cdtest, do I/O + * from the static constructors before the console is open. This test + * will not work with interrupt-driven I/O. Because of the buffering + * performed in termios, test output may not be in sequence.The tests + * should all be fixed to work with interrupt-driven I/O and to + * produce output in the expected sequence. Obviously, the termios test + * requires termios support in the driver. + * + * Set CONSOLE_MINOR to the appropriate device minor number in the + * config file. This allows the RTEMS application console to be different + * from the GDB port. + * + * This driver handles both available serial ports: it distinguishes + * the sub-devices using minor device numbers. It is not possible to have + * other protocols running on the other ports when this driver is used as + * currently written. + * + * + * SS555 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/libbsp/powerpc/mbx8xx/console/console.c: + * + * Based on code (alloc860.c in eth_comm port) by + * Jay Monkman (jmonkman@frasca.com), + * Copyright (C) 1998 by Frasca International, Inc. + * + * Modifications by Darlene Stewart <Darlene.Stewart@iit.nrc.ca> + * and Charles-Antoine Gauthier <charles.gauthier@iit.nrc.ca>. + * Copyright (c) 2000, National Research Council of Canada + * + */ +#include <stdarg.h> +#include <stdio.h> +#include <termios.h> + +#include <rtems/console.h> +#include <rtems/bspIo.h> +#include <rtems/libio.h> +#include <bsp.h> /* Must be before libio.h */ + +static void _BSP_output_char( char c ); +static rtems_status_code do_poll_read( rtems_device_major_number major, rtems_device_minor_number minor, void * arg); +static rtems_status_code do_poll_write( rtems_device_major_number major, rtems_device_minor_number minor, void * arg); + +static void _BSP_null_char( char c ) {return;} + +BSP_output_char_function_type BSP_output_char = _BSP_null_char; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + +/* + * do_poll_read + * + * Input characters through polled I/O. Returns as soon as a character has + * been received. Otherwise, if we wait for the number of requested + * characters, we could be here forever! + * + * CR is converted to LF on input. The terminal should not send a CR/LF pair + * when the return or enter key is pressed. + * + * Input parameters: + * major - ignored. Should be the major number for this driver. + * minor - selected channel. + * arg->buffer - where to put the received characters. + * arg->count - number of characters to receive before returning--Ignored. + * + * Output parameters: + * arg->bytes_moved - the number of characters read. Always 1. + * + * Return value: RTEMS_SUCCESSFUL + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +static rtems_status_code do_poll_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args = arg; + int c; + + while( (c = m5xx_uart_pollRead(minor)) == -1 ); + rw_args->buffer[0] = (uint8_t)c; + if( rw_args->buffer[0] == '\r' ) + rw_args->buffer[0] = '\n'; + rw_args->bytes_moved = 1; + + return RTEMS_SUCCESSFUL; +} + +/* + * do_poll_write + * + * Output characters through polled I/O. Returns only once every character has + * been sent. + * + * CR is transmitted AFTER a LF on output. + * + * Input parameters: + * major - ignored. Should be the major number for this driver. + * minor - selected channel + * arg->buffer - where to get the characters to transmit. + * arg->count - the number of characters to transmit before returning. + * + * Output parameters: + * arg->bytes_moved - the number of characters read + * + * Return value: RTEMS_SUCCESSFUL + * + * CANNOT BE COMBINED WITH INTERRUPT DRIVEN I/O! + */ +static rtems_status_code do_poll_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg +) +{ + rtems_libio_rw_args_t *rw_args = arg; + uint32_t i; + char cr ='\r'; + + for( i = 0; i < rw_args->count; i++ ) { + m5xx_uart_pollWrite(minor, &(rw_args->buffer[i]), 1); + if ( rw_args->buffer[i] == '\n' ) + m5xx_uart_pollWrite(minor, &cr, 1); + } + rw_args->bytes_moved = i; + + return RTEMS_SUCCESSFUL; +} + +/* + * Print functions prototyped in bspIo.h + */ + +static void _BSP_output_char( char c ) +{ + char cr = '\r'; + + /* + * Can't rely on console_initialize having been called before this + * function is used, so it may fail. + */ + + m5xx_uart_pollWrite( PRINTK_MINOR, &c, 1 ); + if( c == '\n' ) + m5xx_uart_pollWrite( PRINTK_MINOR, &cr, 1 ); +} + +/* + *************** + * BOILERPLATE * + *************** + * + * All these functions are prototyped in rtems/c/src/lib/include/console.h. + */ + +/* + * Initialize and register the device + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + + /* + * Set up TERMIOS if needed + */ + #if UARTS_USE_TERMIOS == 1 + rtems_termios_initialize (); + #endif /* UARTS_USE_TERMIOS */ + + /* + * Do device-specific initialization + */ + BSP_output_char = _BSP_output_char; + + m5xx_uart_initialize(SCI1_MINOR); + status = rtems_io_register_name ("/dev/tty0", major, SCI1_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + m5xx_uart_initialize(SCI2_MINOR); + status = rtems_io_register_name ("/dev/tty1", major, SCI2_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + /* Now register the RTEMS console */ + status = rtems_io_register_name ("/dev/console", major, CONSOLE_MINOR); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred (status); + + return RTEMS_SUCCESSFUL; +} + +/* + * Open the device + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + + if ( minor > NUM_PORTS - 1 ) + return RTEMS_INVALID_NUMBER; + + #if (UARTS_USE_TERMIOS == 1) + { + #if (UARTS_IO_MODE == 1) /* RTEMS interrupt-driven I/O with termios */ + + static const rtems_termios_callbacks callbacks = { + m5xx_uart_firstOpen, /* firstOpen */ + m5xx_uart_lastClose, /* lastClose */ + NULL, /* pollRead */ + m5xx_uart_write, /* write */ + m5xx_uart_setAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */ + }; + sc = rtems_termios_open( major, minor, arg, &callbacks ); + + #else /* UARTS_IO_MODE != 1 */ /* RTEMS polled I/O with termios */ + + static const rtems_termios_callbacks callbacks = { + m5xx_uart_firstOpen, /* firstOpen */ + m5xx_uart_lastClose, /* lastClose */ + m5xx_uart_pollRead, /* pollRead */ + m5xx_uart_pollWrite, /* write */ + m5xx_uart_setAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_POLLED /* outputUsesInterrupts */ + }; + sc = rtems_termios_open( major, minor, arg, &callbacks ); + + #endif + + return sc; + } + + #else /* no termios -- default to polled I/O */ + { + sc = RTEMS_SUCCESSFUL; + } + #endif + + return sc; +} + +/* + * Close the device + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + + #if UARTS_USE_TERMIOS == 1 + return rtems_termios_close( arg ); + #else + return RTEMS_SUCCESSFUL; + #endif +} + +/* + * Read from the device + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + + #if UARTS_USE_TERMIOS == 1 + return rtems_termios_read( arg ); + #else + return do_poll_read( major, minor, arg ); + #endif +} + +/* + * Write to the device + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + + #if UARTS_USE_TERMIOS == 1 + return rtems_termios_write( arg ); + #else + return do_poll_write( major, minor, arg ); + #endif +} + +/* + * Handle ioctl request. + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + if ( minor > NUM_PORTS-1 ) + return RTEMS_INVALID_NUMBER; + + #if UARTS_USE_TERMIOS == 1 + return rtems_termios_ioctl( arg ); + #else + return RTEMS_SUCCESSFUL; + #endif +} diff --git a/bsps/powerpc/t32mppc/console/console.c b/bsps/powerpc/t32mppc/console/console.c new file mode 100644 index 0000000000..5fbd648765 --- /dev/null +++ b/bsps/powerpc/t32mppc/console/console.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2012, 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 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.org/license/LICENSE. + */ + +/* + * Console driver for Lauterbach Trace32 Simulator. The implementation is + * based on the example in "demo/powerpc/etc/terminal/terminal_mpc85xx.cmm" in + * the Trace32 system directory. + */ + +#include <rtems/bspIo.h> +#include <rtems/console.h> +#include <rtems/termiostypes.h> + +volatile unsigned char messagebufferin[256]; + +volatile unsigned char messagebufferout[256]; + +typedef struct { + rtems_termios_device_context base; + int input_size; + int input_index; +} t32_console_context; + +static t32_console_context t32_console_instance; + +static bool t32_console_first_open( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + rtems_termios_set_initial_baud(tty, 115200); + + return true; +} + +static int t32_console_read_polled(rtems_termios_device_context *base) +{ + t32_console_context *ctx = (t32_console_context *) base; + int c; + + if (ctx->input_size == 0) { + int new_bufsize = messagebufferin[0]; + + if (new_bufsize != 0) { + ctx->input_size = new_bufsize; + ctx->input_index = 0; + } else { + return -1; + } + } + + c = messagebufferin[4 + ctx->input_index]; + + ++ctx->input_index; + if (ctx->input_index >= ctx->input_size) { + messagebufferin[0] = 0; + ctx->input_size = 0; + } + + return c; +} + +static void t32_console_write_char_polled(char c) +{ + while (messagebufferout[0] != 0) { + /* Wait for ready */ + } + + messagebufferout[4] = (unsigned char) c; + messagebufferout[0] = 1; +} + +static void t32_console_write_polled( + rtems_termios_device_context *base, + const char *s, + size_t n +) +{ + size_t i; + + for (i = 0; i < n; ++i) { + t32_console_write_char_polled(s[i]); + } +} + +const rtems_termios_device_handler t32_console_handler = { + .first_open = t32_console_first_open, + .poll_read = t32_console_read_polled, + .write = t32_console_write_polled, + .mode = TERMIOS_POLLED +}; + +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + t32_console_context *ctx = &t32_console_instance; + + rtems_termios_initialize(); + rtems_termios_device_context_initialize(&ctx->base, "T32 Console"); + rtems_termios_device_install( + CONSOLE_DEVICE_NAME, + &t32_console_handler, + NULL, + &ctx->base + ); + + return RTEMS_SUCCESSFUL; +} + +BSP_output_char_function_type BSP_output_char = t32_console_write_char_polled; + +BSP_polling_getchar_function_type BSP_poll_char = NULL; diff --git a/bsps/powerpc/tqm8xx/console/console.c b/bsps/powerpc/tqm8xx/console/console.c new file mode 100644 index 0000000000..5a681b19fb --- /dev/null +++ b/bsps/powerpc/tqm8xx/console/console.c @@ -0,0 +1,1115 @@ +/*===============================================================*\ +| Project: RTEMS TQM8xx BSP | ++-----------------------------------------------------------------+ +| This file has been adapted to MPC8xx by | +| Thomas Doerfler <Thomas.Doerfler@embedded-brains.de> | +| Copyright (c) 2008 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | +| | +| See the other copyright notice below for the original parts. | ++-----------------------------------------------------------------+ +| 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. | +| | ++-----------------------------------------------------------------+ +| this file contains the console driver | +\*===============================================================*/ +/* derived from: */ +/* + * SMC1/2 SCC1..4 raw console serial I/O. + * adapted to work with up to 4 SCC and 2 SMC + * + * This driver is an example of `TASK DRIVEN' `POLLING' or `INTERRUPT' I/O. + * + * To run with interrupt-driven I/O, ensure m8xx_smc1_interrupt + * is set before calling the initialization routine. + * + * Author: + * 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). + * Copyright assigned to U.S. Government, 1994. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <termios.h> +#include <unistd.h> + +#include <rtems.h> +#include <rtems/console.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <rtems/bspIo.h> +#include <rtems/error.h> +#include <rtems/irq.h> + +#include <bsp.h> +#include <mpc8xx.h> +#include <bsp/irq.h> + +/* + * Interrupt-driven input buffer + */ +#define RXBUFSIZE 16 + +#define M8xx_SICR_BRG1 (0) +#define M8xx_SICR_BRG2 (1) +#define M8xx_SICR_BRG3 (2) +#define M8xx_SICR_BRG4 (3) + +#define M8xx_SICR_SCCRX_MSK(scc) (( 7) << (((scc))*8+3)) +#define M8xx_SICR_SCCRX(scc,clk) ((clk) << (((scc))*8+3)) + +#define M8xx_SICR_SCCTX_MSK(scc) (( 7) << (((scc))*8+0)) +#define M8xx_SICR_SCCTX(scc,clk) ((clk) << (((scc))*8+0)) + +#define M8xx_SIMODE_SMCCS(smc,clk) ((clk) << ((smc)*16+12)) +#define M8xx_SIMODE_SMCCS_MSK(smc) M8xx_SIMODE_SMCCS(smc,7) + +#define CONS_CHN_CNT 6 +#define CONS_CHN_SCC1 0 +#define CONS_CHN_SCC2 1 +#define CONS_CHN_SCC3 2 +#define CONS_CHN_SCC4 3 +#define CONS_CHN_SMC1 4 +#define CONS_CHN_SMC2 5 +#define CONS_CHN_NONE -1 + +/* + * possible identifiers for bspopts.h: CONS_SxCy_MODE + */ +#define CONS_MODE_UNUSED -1 +#define CONS_MODE_POLLED TERMIOS_POLLED +#define CONS_MODE_IRQ TERMIOS_IRQ_DRIVEN + +#define CHN_IS_SCC(chan) ((chan) < CONS_CHN_SMC1) + +#define BRG_CNT 4 + +#define MAX_IDL_DEFAULT 10 +#define DEVICEPREFIX "tty" + +/* + * Interrupt-driven callback + */ +static int m8xx_scc_mode[CONS_CHN_CNT]; +static void *sccttyp[CONS_CHN_CNT]; +typedef struct m8xx_console_chan_desc_s { + bool is_scc; /* true for SCC */ + struct { + volatile m8xxSCCparms_t *sccp; + volatile m8xxSMCparms_t *smcp; + } parms; + struct { + volatile m8xxSCCRegisters_t *sccr; + volatile m8xxSMCRegisters_t *smcr; + } regs; + int ivec_src; + int cr_chan_code; + int brg_used; +} m8xx_console_chan_desc_t; + +m8xx_console_chan_desc_t m8xx_console_chan_desc[CONS_CHN_CNT] = { + /* SCC1 */ + {TRUE, + {(m8xxSCCparms_t *)&(m8xx.scc1p),NULL}, + {&(m8xx.scc1),NULL}, + BSP_CPM_IRQ_SCC1, + M8xx_CR_CHAN_SCC1, + -1}, + /* SCC2 */ + {TRUE, + {&(m8xx.scc2p),NULL}, + {&(m8xx.scc2),NULL}, + BSP_CPM_IRQ_SCC2, + M8xx_CR_CHAN_SCC2, + -1}, + /* SCC3 */ + {TRUE, + {&(m8xx.scc3p),NULL}, + {&(m8xx.scc3),NULL}, + BSP_CPM_IRQ_SCC3, + M8xx_CR_CHAN_SCC3, + -1}, + /* SCC4 */ + {TRUE, + {&(m8xx.scc4p),NULL}, + {&(m8xx.scc4),NULL}, + BSP_CPM_IRQ_SCC4, + M8xx_CR_CHAN_SCC4, + -1}, + /* SMC1 */ + {FALSE, + {NULL,&(m8xx.smc1p)}, + {NULL,&(m8xx.smc1)}, + BSP_CPM_IRQ_SMC1, + M8xx_CR_CHAN_SMC1, + -1}, + /* SMC2 */ + {FALSE, + {NULL,&(m8xx.smc2p)}, + {NULL,&(m8xx.smc2)}, + BSP_CPM_IRQ_SMC2_OR_PIP, + M8xx_CR_CHAN_SMC2, + -1}}; + +#define CHN_PARAM_GET(chan,param) \ + (m8xx_console_chan_desc[chan].is_scc \ + ? m8xx_console_chan_desc[chan].parms.sccp->param \ + : m8xx_console_chan_desc[chan].parms.smcp->param) + +#define CHN_PARAM_SET(chan,param,value) \ + do {if (m8xx_console_chan_desc[chan].is_scc) \ + m8xx_console_chan_desc[chan].parms.sccp->param = value; \ + else \ + m8xx_console_chan_desc[chan].parms.smcp->param = value; \ + } while (0) + +#define CHN_EVENT_GET(chan) \ + (m8xx_console_chan_desc[chan].is_scc \ + ? m8xx_console_chan_desc[chan].regs.sccr->scce \ + : m8xx_console_chan_desc[chan].regs.smcr->smce) + +#define CHN_EVENT_CLR(chan,mask) \ + do { \ + if (m8xx_console_chan_desc[chan].is_scc) \ + m8xx_console_chan_desc[chan].regs.sccr->scce = (mask); \ + else \ + m8xx_console_chan_desc[chan].regs.smcr->smce = (mask); \ + }while (0) + +#define CHN_MASK_GET(chan) \ + (m8xx_console_chan_desc[chan].is_scc \ + ? m8xx_console_chan_desc[chan].regs.sccr->sccm \ + : m8xx_console_chan_desc[chan].regs.smcr->smcm) + +#define CHN_MASK_SET(chan,mask) \ + do { \ + if (m8xx_console_chan_desc[chan].is_scc) \ + m8xx_console_chan_desc[chan].regs.sccr->sccm = (mask); \ + else \ + m8xx_console_chan_desc[chan].regs.smcr->smcm = (mask); \ + }while (0) + + +/* + * I/O buffers and pointers to buffer descriptors + */ +#define SCC_RXBD_CNT 4 +#define SCC_TXBD_CNT 4 +typedef volatile char sccRxBuf_t[SCC_RXBD_CNT][RXBUFSIZE]; +static sccRxBuf_t *rxBuf[CONS_CHN_CNT]; + +static volatile m8xxBufferDescriptor_t *sccFrstRxBd[CONS_CHN_CNT]; +static volatile m8xxBufferDescriptor_t *sccCurrRxBd[CONS_CHN_CNT]; +static volatile m8xxBufferDescriptor_t *sccFrstTxBd[CONS_CHN_CNT]; +static volatile m8xxBufferDescriptor_t *sccPrepTxBd[CONS_CHN_CNT]; +static volatile m8xxBufferDescriptor_t *sccDequTxBd[CONS_CHN_CNT]; + +/* + * Compute baud-rate-generator configuration register value + */ +static uint32_t +sccBRGval (int baud) +{ + int divisor; + int div16 = 0; + + divisor = ((BSP_bus_frequency / 16) + (baud / 2)) / baud; + if (divisor > 4096) { + div16 = 1; + divisor = (divisor + 8) / 16; + } + return M8xx_BRG_EN | M8xx_BRG_EXTC_BRGCLK | ((divisor - 1) << 1) | div16; +} + +typedef struct { + uint32_t reg_content; + int link_cnt; +}brg_state_t; +brg_state_t scc_brg_state[BRG_CNT]; + +/* + * initialize brg_state + */ +static void sccBRGinit(void) +{ + int brg_idx; + + for (brg_idx = 0;brg_idx < BRG_CNT;brg_idx++) { + scc_brg_state[brg_idx].reg_content = 0; + scc_brg_state[brg_idx].link_cnt = 0; + } +#ifndef MDE360 + /* + * on ZEM40, init CLK4/5 inputs + */ + m8xx.papar |= ((1 << 11) | (1 << 12)); + m8xx.padir &= ~((1 << 11) | (1 << 12)); +#endif +} + +#if CONS_USE_EXT_CLK +/* + * input clock frq for CPM clock inputs + */ +static uint32_t clkin_frq[2][4] = { +#ifdef MDE360 + {0,0,0,0}, + {0,0,0,0} +#else + {0,0,0,1843000}, + {1843000,0,0,0} +#endif +}; +#endif + +/* + * allocate, set and connect baud rate generators + * FIXME: or clock input + * FIXME: set pin to be clock input + */ + +static int sccBRGalloc(int chan,int baud) +{ + rtems_interrupt_level level; + m8xx_console_chan_desc_t *chan_desc = &(m8xx_console_chan_desc[chan]); + uint32_t reg_val; + int old_brg; + int new_brg = -1; + int brg_idx; +#if CONS_USE_EXT_CLK + int clk_group; + int clk_sel; +#endif + + old_brg = chan_desc->brg_used; + /* compute brg register contents needed */ + reg_val = sccBRGval(baud); + +#if CONS_EXT_CLK + /* search for clock input with this frq */ + clk_group = ((chan == CONS_CHN_SCC3) || + (chan == CONS_CHN_SCC4) || + (chan == CONS_CHN_SMC2)) ? 1 : 0; + + for (clk_sel = 0, new_brg = -1; + (clk_sel < 4) && (new_brg < 0); + clk_sel++) { + if (baud == (clkin_frq[clk_group][clk_sel] / 16)) { + new_brg = clk_sel + 4; + } + } +#endif + + rtems_interrupt_disable(level); + + if (new_brg < 0) { + /* search for brg with this settings */ + for (brg_idx = 0; + (new_brg < 0) && (brg_idx < BRG_CNT); + brg_idx++) { + if (scc_brg_state[brg_idx].reg_content == reg_val) { + new_brg = brg_idx; + } + } + /* + * if not found: check, whether brg currently in use + * is linked only from our channel + */ + if ((new_brg < 0) && + (old_brg >= 0) && + (scc_brg_state[old_brg].link_cnt == 1)) { + new_brg = old_brg; + } + /* if not found: search for unused brg, set it */ + for (brg_idx = 0; + (new_brg < 0) && (brg_idx < BRG_CNT); + brg_idx++) { + if (scc_brg_state[brg_idx].link_cnt == 0) { + new_brg = brg_idx; + } + } + } + + /* decrease old link count */ + if ((old_brg >= 0) && + (old_brg < 4)) { + scc_brg_state[old_brg].link_cnt--; + } + /* increase new brg link count, set brg */ + if ((new_brg >= 0) && + (new_brg < 4)) { + scc_brg_state[new_brg].link_cnt++; + scc_brg_state[new_brg].reg_content = reg_val; + (&m8xx.brgc1)[new_brg] = reg_val; + } + rtems_interrupt_enable(level); + + /* connect to scc/smc */ + if (new_brg >= 0) { + m8xx_console_chan_desc[chan].brg_used = new_brg; + /* + * Put SCC in NMSI mode, connect SCC to BRG or CLKx + */ + if (m8xx_console_chan_desc[chan].is_scc) { + m8xx.sicr = ((m8xx.sicr & ~(M8xx_SICR_SCCRX_MSK(chan) | + M8xx_SICR_SCCTX_MSK(chan))) | + M8xx_SICR_SCCRX(chan,new_brg)| + M8xx_SICR_SCCTX(chan,new_brg)); + } + else { + /* connect SMC to BRGx or CLKx... */ + m8xx.simode = ((m8xx.simode & ~(M8xx_SIMODE_SMCCS_MSK(chan - CONS_CHN_SMC1)))| + M8xx_SIMODE_SMCCS(chan - CONS_CHN_SMC1,new_brg)); + } + } + return (new_brg < 0); +} + + +/* + * Hardware-dependent portion of tcsetattr(). + */ +static int +sccSetAttributes (int minor, const struct termios *t) +{ + int baud; + + switch (t->c_ospeed) { + default: baud = -1; break; + case B50: baud = 50; break; + case B75: baud = 75; break; + case B110: baud = 110; break; + case B134: baud = 134; break; + case B150: baud = 150; break; + case B200: baud = 200; break; + case B300: baud = 300; break; + case B600: baud = 600; break; + case B1200: baud = 1200; break; + case B1800: baud = 1800; break; + case B2400: baud = 2400; break; + case B4800: baud = 4800; break; + case B9600: baud = 9600; break; + case B19200: baud = 19200; break; + case B38400: baud = 38400; break; + case B57600: baud = 57600; break; + case B115200: baud = 115200; break; + case B230400: baud = 230400; break; + case B460800: baud = 460800; break; + } + return sccBRGalloc(minor,baud); + return 0; +} + +/* + * Interrupt handler + */ +static rtems_isr +sccInterruptHandler (void *arg) +{ + int chan = (int)arg; + + /* + * Buffer received? + */ + if (CHN_EVENT_GET(chan) & 0x1) { + /* + * clear SCC event flag + */ + CHN_EVENT_CLR(chan,0x01); + /* + * process event + */ + while ((sccCurrRxBd[chan]->status & M8xx_BD_EMPTY) == 0) { + if (sccttyp[chan] != NULL) { + rtems_cache_invalidate_multiple_data_lines((void *)sccCurrRxBd[chan]->buffer, + sccCurrRxBd[chan]->length); + rtems_termios_enqueue_raw_characters (sccttyp[chan], + (char *)sccCurrRxBd[chan]->buffer, + sccCurrRxBd[chan]->length); + } + /* + * clear status + */ + sccCurrRxBd[chan]->status = + (sccCurrRxBd[chan]->status + & (M8xx_BD_WRAP | M8xx_BD_INTERRUPT)) + | M8xx_BD_EMPTY; + /* + * advance to next BD + */ + if ((sccCurrRxBd[chan]->status & M8xx_BD_WRAP) != 0) { + sccCurrRxBd[chan] = sccFrstRxBd[chan]; + } + else { + sccCurrRxBd[chan]++; + } + } + } + /* + * Buffer transmitted? + */ + if (CHN_EVENT_GET(chan) & 0x2) { + /* + * then clear interrupt event bit + */ + CHN_EVENT_CLR(chan,0x2); + /* + * and signal successful transmit to termios + */ + /* + * FIXME: multiple dequeue calls for multiple buffers + */ + while((sccDequTxBd[chan] != sccPrepTxBd[chan]) && + ((sccDequTxBd[chan]->status & M8xx_BD_READY) == 0)) { + if (sccttyp[chan] != NULL) { + rtems_termios_dequeue_characters (sccttyp[chan], + sccDequTxBd[chan]->length); + } + /* + * advance to next BD + */ + if ((sccDequTxBd[chan]->status & M8xx_BD_WRAP) != 0) { + sccDequTxBd[chan] = sccFrstTxBd[chan]; + } + else { + sccDequTxBd[chan]++; + } + } + } +} + +static void +mpc8xx_console_irq_on(const rtems_irq_connect_data *irq) +{ + CHN_MASK_SET(irq->name - BSP_CPM_IRQ_LOWEST_OFFSET, + 3); /* Enable TX and RX interrupts */ +} + +static void +mpc8xx_console_irq_off(const rtems_irq_connect_data *irq) +{ + CHN_MASK_SET(irq->name - BSP_CPM_IRQ_LOWEST_OFFSET, + 0); /* Disable TX and RX interrupts */ +} + +static int +mpc8xx_console_irq_isOn(const rtems_irq_connect_data *irq) +{ + return (0 != CHN_MASK_GET(irq->name - BSP_CPM_IRQ_LOWEST_OFFSET)); /* Check TX and RX interrupts */ +} + +static void +sccInitialize (int chan) +{ + int i; + /* + * allocate buffers + * FIXME: use a cache-line size boundary alloc here + */ + rxBuf[chan] = malloc(sizeof(*rxBuf[chan]) + 2*PPC_CACHE_ALIGNMENT); + if (rxBuf[chan] == NULL) { + rtems_panic("Cannot allocate console rx buffer\n"); + } + else { + /* + * round up rxBuf[chan] to start at a cache line size + */ + rxBuf[chan] = (sccRxBuf_t *) + (((uint32_t)rxBuf[chan]) + + (PPC_CACHE_ALIGNMENT + - ((uint32_t)rxBuf[chan]) % PPC_CACHE_ALIGNMENT)); + } + /* + * Allocate buffer descriptors + */ + sccCurrRxBd[chan] = + sccFrstRxBd[chan] = m8xx_bd_allocate(SCC_RXBD_CNT); + sccPrepTxBd[chan] = + sccDequTxBd[chan] = + sccFrstTxBd[chan] = m8xx_bd_allocate(SCC_TXBD_CNT); + switch(chan) { + case CONS_CHN_SCC1: + /* + * Configure port A pins to enable TXD1 and RXD1 pins + * FIXME: add setup for modem control lines.... + */ + m8xx.papar |= 0x03; + m8xx.padir &= ~0x03; + + /* + * Configure port C pins to enable RTS1 pins (static active low) + */ + m8xx.pcpar &= ~0x01; + m8xx.pcso &= ~0x01; + m8xx.pcdir |= 0x01; + m8xx.pcdat &= ~0x01; + break; + case CONS_CHN_SCC2: + /* + * Configure port A pins to enable TXD2 and RXD2 pins + * FIXME: add setup for modem control lines.... + */ + m8xx.papar |= 0x0C; + m8xx.padir &= ~0x0C; + + /* + * Configure port C pins to enable RTS2 pins (static active low) + */ + m8xx.pcpar &= ~0x02; + m8xx.pcso &= ~0x02; + m8xx.pcdir |= 0x02; + m8xx.pcdat &= ~0x02; + break; + case CONS_CHN_SCC3: + /* + * Configure port A pins to enable TXD3 and RXD3 pins + * FIXME: add setup for modem control lines.... + */ + m8xx.papar |= 0x30; + m8xx.padir &= ~0x30; + + /* + * Configure port C pins to enable RTS3 (static active low) + */ + m8xx.pcpar &= ~0x04; + m8xx.pcso &= ~0x04; + m8xx.pcdir |= 0x04; + m8xx.pcdat &= ~0x04; + break; + case CONS_CHN_SCC4: + /* + * Configure port A pins to enable TXD4 and RXD4 pins + * FIXME: add setup for modem control lines.... + */ + m8xx.papar |= 0xC0; + m8xx.padir &= ~0xC0; + + /* + * Configure port C pins to enable RTS4 pins (static active low) + */ + m8xx.pcpar &= ~0x08; + m8xx.pcso &= ~0x08; + m8xx.pcdir |= 0x08; + m8xx.pcdat &= ~0x08; + break; + case CONS_CHN_SMC1: + /* + * Configure port B pins to enable SMTXD1 and SMRXD1 pins + */ + m8xx.pbpar |= 0xC0; + m8xx.pbdir &= ~0xC0; + break; + case CONS_CHN_SMC2: + /* + * Configure port B pins to enable SMTXD2 and SMRXD2 pins + */ + m8xx.pbpar |= 0xC00; + m8xx.pbdir &= ~0xC00; + break; + } + /* + * allocate and connect BRG + */ + sccBRGalloc(chan,9600); + + + /* + * Set up SCCx parameter RAM common to all protocols + */ + CHN_PARAM_SET(chan,rbase,(char *)sccFrstRxBd[chan] - (char *)&m8xx); + CHN_PARAM_SET(chan,tbase,(char *)sccFrstTxBd[chan] - (char *)&m8xx); + CHN_PARAM_SET(chan,rfcr ,M8xx_RFCR_MOT | M8xx_RFCR_DMA_SPACE(0)); + CHN_PARAM_SET(chan,tfcr ,M8xx_TFCR_MOT | M8xx_TFCR_DMA_SPACE(0)); + if (m8xx_scc_mode[chan] != TERMIOS_POLLED) + CHN_PARAM_SET(chan,mrblr,RXBUFSIZE); + else + CHN_PARAM_SET(chan,mrblr,1); + + /* + * Set up SCCx parameter RAM UART-specific parameters + */ + CHN_PARAM_SET(chan,un.uart.max_idl ,MAX_IDL_DEFAULT); + CHN_PARAM_SET(chan,un.uart.brkln ,0); + CHN_PARAM_SET(chan,un.uart.brkec ,0); + CHN_PARAM_SET(chan,un.uart.brkcr ,0); + if (m8xx_console_chan_desc[chan].is_scc) { + m8xx_console_chan_desc[chan].parms.sccp->un.uart.character[0]=0x8000; /* no char filter */ + m8xx_console_chan_desc[chan].parms.sccp->un.uart.rccm=0x80FF; /* control character mask */ + } + + /* + * Set up the Receive Buffer Descriptors + */ + for (i = 0;i < SCC_RXBD_CNT;i++) { + sccFrstRxBd[chan][i].status = M8xx_BD_EMPTY | M8xx_BD_INTERRUPT; + if (i == SCC_RXBD_CNT-1) { + sccFrstRxBd[chan][i].status |= M8xx_BD_WRAP; + } + sccFrstRxBd[chan][i].length = 0; + sccFrstRxBd[chan][i].buffer = (*rxBuf[chan])[i]; + } + /* + * Setup the Transmit Buffer Descriptor + */ + for (i = 0;i < SCC_TXBD_CNT;i++) { + sccFrstTxBd[chan][i].status = M8xx_BD_INTERRUPT; + if (i == SCC_TXBD_CNT-1) { + sccFrstTxBd[chan][i].status |= M8xx_BD_WRAP; + } + sccFrstTxBd[chan][i].length = 0; + sccFrstTxBd[chan][i].buffer = NULL; + } + + /* + * Set up SCC general and protocol-specific mode registers + */ + CHN_EVENT_CLR(chan,~0); /* Clear any pending events */ + CHN_MASK_SET(chan,0); /* Mask all interrupt/event sources */ + + if (m8xx_console_chan_desc[chan].is_scc) { + m8xx_console_chan_desc[chan].regs.sccr->psmr = 0xb000; /* 8N1, CTS flow control */ + m8xx_console_chan_desc[chan].regs.sccr->gsmr_h = 0x00000000; + m8xx_console_chan_desc[chan].regs.sccr->gsmr_l = 0x00028004; /* UART mode */ + } + else { + m8xx_console_chan_desc[chan].regs.smcr->smcmr = 0x4820; + } + /* + * Send "Init parameters" command + */ + m8xx_cp_execute_cmd(M8xx_CR_OP_INIT_RX_TX + | m8xx_console_chan_desc[chan].cr_chan_code); + + /* + * Enable receiver and transmitter + */ + if (m8xx_console_chan_desc[chan].is_scc) { + m8xx_console_chan_desc[chan].regs.sccr->gsmr_l |= 0x00000030; + } + else { + m8xx_console_chan_desc[chan].regs.smcr->smcmr |= 0x0003; + } + + if (m8xx_scc_mode[chan] != TERMIOS_POLLED) { + + rtems_irq_connect_data irq_conn_data = { + m8xx_console_chan_desc[chan].ivec_src, + sccInterruptHandler, /* rtems_irq_hdl */ + (rtems_irq_hdl_param)chan, /* (rtems_irq_hdl_param) */ + mpc8xx_console_irq_on, /* (rtems_irq_enable) */ + mpc8xx_console_irq_off, /* (rtems_irq_disable) */ + mpc8xx_console_irq_isOn /* (rtems_irq_is_enabled) */ + }; + if (!BSP_install_rtems_irq_handler (&irq_conn_data)) { + rtems_panic("console: cannot install IRQ handler"); + } + } +} + +/* + * polled scc read function + */ +static int +sccPollRead (int minor) +{ + int c = -1; + int chan = minor; + + while(1) { + if ((sccCurrRxBd[chan]->status & M8xx_BD_EMPTY) != 0) { + return -1; + } + + if (0 == (sccCurrRxBd[chan]->status & (M8xx_BD_OVERRUN + | M8xx_BD_PARITY_ERROR + | M8xx_BD_FRAMING_ERROR + | M8xx_BD_BREAK + | M8xx_BD_IDLE))) { + /* character received and no error detected */ + rtems_cache_invalidate_multiple_data_lines((void *)sccCurrRxBd[chan]->buffer, + sccCurrRxBd[chan]->length); + c = (unsigned)*((char *)sccCurrRxBd[chan]->buffer); + /* + * clear status + */ + } + sccCurrRxBd[chan]->status = + (sccCurrRxBd[chan]->status + & (M8xx_BD_WRAP | M8xx_BD_INTERRUPT)) + | M8xx_BD_EMPTY; + /* + * advance to next BD + */ + if ((sccCurrRxBd[chan]->status & M8xx_BD_WRAP) != 0) { + sccCurrRxBd[chan] = sccFrstRxBd[chan]; + } + else { + sccCurrRxBd[chan]++; + } + if (c >= 0) { + return c; + } + } +} + + +/* + * Device-dependent write routine + * Interrupt-driven devices: + * Begin transmission of as many characters as possible (minimum is 1). + * Polling devices: + * Transmit all characters. + */ +static ssize_t +sccInterruptWrite (int minor, const char *buf, size_t len) +{ + if (len > 0) { + int chan = minor; + + if ((sccPrepTxBd[chan]->status & M8xx_BD_READY) == 0) { + sccPrepTxBd[chan]->buffer = (char *)buf; + sccPrepTxBd[chan]->length = len; + rtems_cache_flush_multiple_data_lines((const void *)buf,len); + /* + * clear status, set ready bit + */ + sccPrepTxBd[chan]->status = + (sccPrepTxBd[chan]->status + & M8xx_BD_WRAP) + | M8xx_BD_READY | M8xx_BD_INTERRUPT; + if ((sccPrepTxBd[chan]->status & M8xx_BD_WRAP) != 0) { + sccPrepTxBd[chan] = sccFrstTxBd[chan]; + } + else { + sccPrepTxBd[chan]++; + } + } + } + + return 0; +} + +static ssize_t +sccPollWrite (int minor, const char *buf, size_t len) +{ + static char txBuf[CONS_CHN_CNT][SCC_TXBD_CNT]; + int chan = minor; + int bd_used; + size_t retval = len; + + while (len--) { + while (sccPrepTxBd[chan]->status & M8xx_BD_READY) + continue; + bd_used = sccPrepTxBd[chan]-sccFrstTxBd[chan]; + txBuf[chan][bd_used] = *buf++; + rtems_cache_flush_multiple_data_lines((const void *)&txBuf[chan][bd_used], + sizeof(txBuf[chan][bd_used])); + sccPrepTxBd[chan]->buffer = &(txBuf[chan][bd_used]); + sccPrepTxBd[chan]->length = 1; + sccPrepTxBd[chan]->status = + (sccPrepTxBd[chan]->status + & M8xx_BD_WRAP) + | M8xx_BD_READY; + if ((sccPrepTxBd[chan]->status & M8xx_BD_WRAP) != 0) { + sccPrepTxBd[chan] = sccFrstTxBd[chan]; + } + else { + sccPrepTxBd[chan]++; + } + } + return retval; +} + +/* + * printk basic support + */ +int BSP_output_chan = CONS_CHN_NONE; /* channel used for printk operation */ + +static void console_debug_putc_onlcr(const char c) +{ + rtems_interrupt_level irq_level; + + if (BSP_output_chan != CONS_CHN_NONE) { + rtems_interrupt_disable(irq_level); + + sccPollWrite (BSP_output_chan,&c,1); + rtems_interrupt_enable(irq_level); + } +} + +BSP_output_char_function_type BSP_output_char = console_debug_putc_onlcr; +BSP_polling_getchar_function_type BSP_poll_char = NULL; + + +/* +*************** +* BOILERPLATE * +*************** +*/ + +struct { + rtems_device_minor_number minor; + int driver_mode; +} channel_list[] = { + {CONS_CHN_SMC1,CONS_SMC1_MODE}, + {CONS_CHN_SMC2,CONS_SMC2_MODE}, + {CONS_CHN_SCC1,CONS_SCC1_MODE}, + {CONS_CHN_SCC2,CONS_SCC2_MODE}, + {CONS_CHN_SCC3,CONS_SCC3_MODE}, + {CONS_CHN_SCC4,CONS_SCC4_MODE} +}; + + +/* + * Initialize and register the device + */ +rtems_device_driver console_initialize(rtems_device_major_number major, + rtems_device_minor_number minor,/* ignored */ + void *arg + ) +{ + rtems_status_code status = RTEMS_SUCCESSFUL; + int chan,entry,ttynum; + char tty_name[] = "/dev/tty00"; + + /* + * Set up TERMIOS + */ + rtems_termios_initialize (); + /* + * init BRG allocataion + */ + sccBRGinit(); + ttynum = 0; + for (entry = 0; + (entry < sizeof(channel_list)/sizeof(channel_list[0])) + && (status == RTEMS_SUCCESSFUL); + entry++) { + if (channel_list[entry].driver_mode != CONS_MODE_UNUSED) { + /* + * Do device-specific initialization + */ + chan = channel_list[entry].minor; + m8xx_scc_mode[chan] = channel_list[entry].driver_mode; + sccInitialize (chan); + + /* + * build device name + */ + tty_name[sizeof(tty_name)-2] = '0'+ttynum; + ttynum++; + /* + * Register the device + */ + status = rtems_io_register_name (tty_name, + major, + channel_list[entry].minor); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred (status); + } + } + } + /* + * register /dev/console + */ + status = rtems_io_register_name ("/dev/console", + major, + CONSOLE_CHN); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred (status); + } + /* + * enable printk support + */ + BSP_output_chan = PRINTK_CHN; + + return RTEMS_SUCCESSFUL; +} + +/* + * Open the device + */ +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + rtems_status_code status; + int chan = minor; + rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *)arg; + static const rtems_termios_callbacks interruptCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + NULL, /* pollRead */ + sccInterruptWrite, /* write */ + sccSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */ + }; + static const rtems_termios_callbacks pollCallbacks = { + NULL, /* firstOpen */ + NULL, /* lastClose */ + sccPollRead, /* pollRead */ + sccPollWrite, /* write */ + sccSetAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + 0 /* outputUsesInterrupts */ + }; + + if (m8xx_scc_mode[chan] == TERMIOS_IRQ_DRIVEN) { + status = rtems_termios_open (major, minor, arg, &interruptCallbacks); + sccttyp[chan] = args->iop->data1; + } + else { + status = rtems_termios_open (major, minor, arg, &pollCallbacks); + sccttyp[chan] = args->iop->data1; + } + return status; +} + +/* + * Close the device + */ +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + rtems_status_code rc; + + rc = rtems_termios_close (arg); + sccttyp[minor] = NULL; + + return rc; + +} + +/* + * Read from the device + */ +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + return rtems_termios_read (arg); +} + +/* + * Write to the device + */ +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + return rtems_termios_write (arg); +} + +#if 0 +static int scc_io_set_trm_char(rtems_device_minor_number minor, + rtems_libio_ioctl_args_t *ioa) +{ + rtems_status_code rc = RTEMS_SUCCESSFUL; + con360_io_trm_char_t *trm_char_info = ioa->buffer; + + /* + * check, that parameter is non-NULL + */ + if ((rc == RTEMS_SUCCESSFUL) && + (trm_char_info == NULL)) { + rc = RTEMS_INVALID_ADDRESS; + } + /* + * transfer max_idl + */ + if (rc == RTEMS_SUCCESSFUL) { + if (trm_char_info->max_idl >= 0x10000) { + rc = RTEMS_INVALID_NUMBER; + } + else if (trm_char_info->max_idl > 0) { + CHN_PARAM_SET(minor,un.uart.max_idl ,trm_char_info->max_idl); + } + else if (trm_char_info->max_idl == 0) { + CHN_PARAM_SET(minor,un.uart.max_idl ,MAX_IDL_DEFAULT); + } + } + /* + * transfer characters + */ + if (rc == RTEMS_SUCCESSFUL) { + if (trm_char_info->char_cnt > CON8XX_TRM_CHAR_CNT) { + rc = RTEMS_TOO_MANY; + } + else if (trm_char_info->char_cnt >= 0) { + /* + * check, whether device is a SCC + */ + if ((rc == RTEMS_SUCCESSFUL) && + !m8xx_console_chan_desc[minor].is_scc) { + rc = RTEMS_UNSATISFIED; + } + else { + int idx = 0; + for(idx = 0;idx < trm_char_info->char_cnt;idx++) { + m8xx_console_chan_desc[minor].parms.sccp->un.uart.character[idx] = + trm_char_info->character[idx] & 0x00ff; + } + if (trm_char_info->char_cnt < CON8XX_TRM_CHAR_CNT) { + m8xx_console_chan_desc[minor].parms.sccp + ->un.uart.character[trm_char_info->char_cnt] = 0x8000; + } + } + } + } + + return rc; +} +#endif + +/* + * Handle ioctl request. + */ +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + rtems_libio_ioctl_args_t *ioa=arg; + + switch (ioa->command) { +#if 0 + case CON8XX_IO_SET_TRM_CHAR: + return scc_io_set_trm_char(minor, ioa); +#endif + default: + return rtems_termios_ioctl (arg); + break; + } +} + diff --git a/bsps/powerpc/virtex/console/consolelite.c b/bsps/powerpc/virtex/console/consolelite.c new file mode 100644 index 0000000000..4d0b2db17f --- /dev/null +++ b/bsps/powerpc/virtex/console/consolelite.c @@ -0,0 +1,425 @@ +/* + * This file contains the console driver for the xilinx uart lite. + * + * Author: Keith Robertson <kjrobert@alumni.uwaterloo.ca> + * COPYRIGHT (c) 2005 by Linn Products Ltd, Scotland. + * + * Derived from libbsp/no_cpu/no_bsp/console.c and therefore also: + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * 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 <assert.h> + +#include <rtems.h> +#include <rtems/libio.h> +#include <bsp/irq.h> + +#include <bsp.h> +#include <libchip/serial.h> +#include <libchip/sersupp.h> + +#include RTEMS_XPARAMETERS_H + +/* Status Register Masks */ +#define PARITY_ERROR 0x80 /* Parity Error */ +#define FRAME_ERROR 0x40 /* Frame Error */ +#define OVERRUN_ERROR 0x20 /* Overrun Error */ +#define STATUS_REG_ERROR_MASK ( PARITY_ERROR | FRAME_ERROR | OVERRUN_ERROR ) + +#define INTR_ENABLED 0x10 /* Interrupts are enabled */ +#define TX_FIFO_FULL 0x08 /* Transmit FIFO is full */ +#define TX_FIFO_EMPTY 0x04 /* Transmit FIFO is empty */ +#define RX_FIFO_FULL 0x02 /* Receive FIFO is full */ +#define RX_FIFO_VALID_DATA 0x01 /* Receive FIFO has valid data */ +/* Control Register Masks*/ +#define ENABLE_INTR 0x10 /* Enable interrupts */ +#define RST_RX_FIFO 0x02 /* Reset and clear RX FIFO */ +#define RST_TX_FIFO 0x01 /* Reset and clear TX FIFO */ + +/* General Defines */ +#define TX_FIFO_SIZE 16 +#define RX_FIFO_SIZE 16 + + + + +#define RECV_REG 0 +#define TRAN_REG 4 +#define STAT_REG 8 +#define CTRL_REG 12 + + + +RTEMS_INLINE_ROUTINE uint32_t xlite_uart_control(uint32_t base) +{ + uint32_t c = *((volatile uint32_t*)(base+CTRL_REG)); + return c; +} + + +RTEMS_INLINE_ROUTINE uint32_t xlite_uart_status(uint32_t base) +{ + uint32_t c = *((volatile uint32_t*)(base+STAT_REG)); + return c; +} + + +RTEMS_INLINE_ROUTINE uint32_t xlite_uart_read(uint32_t base) +{ + uint32_t c = *((volatile uint32_t*)(base+RECV_REG)); + return c; +} + + +RTEMS_INLINE_ROUTINE void xlite_uart_write(uint32_t base, char ch) +{ + *(volatile uint32_t*)(base+TRAN_REG) = (uint32_t)ch; + return; +} + + + +static int xlite_write_char(uint32_t base, char ch) +{ + uint32_t retrycount= 0, idler, status; + + while( ((status = xlite_uart_status(base)) & TX_FIFO_FULL) != 0 ) + { + ++retrycount; + + /* uart tx is busy */ + if( retrycount == 0x4000 ) + { + /* retrycount is arbitrary- just make it big enough so the uart is sure to be timed out before it trips */ + return -1; + } + + /* spin for a bit so we can sample the register rather than + * continually reading it */ + for( idler= 0; idler < 0x2000; idler++); + } + + xlite_uart_write(base, ch); + + return 1; +} + +static void xlite_init(int minor ) +{ + /* Nothing to do */ +} + +#if VIRTEX_CONSOLE_USE_INTERRUPTS +static void xlite_interrupt_handler(void *arg) +{ + int minor = (int) arg; + const console_tbl *ct = Console_Port_Tbl[minor]; + console_data *cd = &Console_Port_Data[minor]; + uint32_t base = ct->ulCtrlPort1; + uint32_t status = xlite_uart_status(base); + + while ((status & RX_FIFO_VALID_DATA) != 0) { + char c = (char) xlite_uart_read(base); + + rtems_termios_enqueue_raw_characters(cd->termios_data, &c, 1); + + status = xlite_uart_status(base); + } + + if (cd->bActive) { + rtems_termios_dequeue_characters(cd->termios_data, 1); + } +} +#endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ + +static int xlite_open( + int major, + int minor, + void *arg +) +{ + const console_tbl *ct = Console_Port_Tbl[minor]; + uint32_t base = ct->ulCtrlPort1; +#if VIRTEX_CONSOLE_USE_INTERRUPTS + rtems_status_code sc; +#endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ + + /* clear status register */ + *((volatile uint32_t*)(base+STAT_REG)) = 0; + + /* clear control register; reset fifos */ + *((volatile uint32_t*)(base+CTRL_REG)) = RST_RX_FIFO | RST_TX_FIFO; + +#if VIRTEX_CONSOLE_USE_INTERRUPTS + *((volatile uint32_t*)(base+CTRL_REG)) = ENABLE_INTR; + + sc = rtems_interrupt_handler_install( + ct->ulIntVector, + "xlite", + RTEMS_INTERRUPT_UNIQUE, + xlite_interrupt_handler, + (void *) minor + ); + assert(sc == RTEMS_SUCCESSFUL); +#endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ + + return 0; +} + +static int xlite_close( + int major, + int minor, + void *arg +) +{ + const console_tbl *ct = Console_Port_Tbl[minor]; + uint32_t base = ct->ulCtrlPort1; +#if VIRTEX_CONSOLE_USE_INTERRUPTS + rtems_status_code sc; +#endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ + + *((volatile uint32_t*)(base+CTRL_REG)) = 0; + +#if VIRTEX_CONSOLE_USE_INTERRUPTS + sc = rtems_interrupt_handler_remove( + ct->ulIntVector, + xlite_interrupt_handler, + (void *) minor + ); + assert(sc == RTEMS_SUCCESSFUL); +#endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ + + return 0; +} + + + +static int xlite_read_polled (int minor ) +{ + uint32_t base = Console_Port_Tbl[minor]->ulCtrlPort1; + + unsigned int status = xlite_uart_status(base); + + if(status & RX_FIFO_VALID_DATA) + return (int)xlite_uart_read(base); + else + return -1; +} + +#if VIRTEX_CONSOLE_USE_INTERRUPTS + +static ssize_t xlite_write_interrupt_driven( + int minor, + const char *buf, + size_t len +) +{ + console_data *cd = &Console_Port_Data[minor]; + + if (len > 0) { + const console_tbl *ct = Console_Port_Tbl[minor]; + uint32_t base = ct->ulCtrlPort1; + + xlite_uart_write(base, buf[0]); + + cd->bActive = true; + } else { + cd->bActive = false; + } + + return 0; +} + +#else /* VIRTEX_CONSOLE_USE_INTERRUPTS */ + +static ssize_t xlite_write_buffer_polled( + int minor, + const char *buf, + size_t len +) +{ + uint32_t base = Console_Port_Tbl[minor]->ulCtrlPort1; + int nwrite = 0; + + /* + * poll each byte in the string out of the port. + */ + while (nwrite < len) + { + if( xlite_write_char(base, *buf++) < 0 ) break; + nwrite++; + } + + /* + * return the number of bytes written. + */ + return nwrite; +} + +#endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ + +static void xlite_write_char_polled( + int minor, + char c +) +{ + uint32_t base = Console_Port_Tbl[minor]->ulCtrlPort1; + xlite_write_char(base, c); + return; +} + +static int xlite_set_attributes(int minor, const struct termios *t) +{ + return RTEMS_SUCCESSFUL; +} + + + + + + + +static const console_fns xlite_fns_polled = +{ + .deviceProbe = libchip_serial_default_probe, + .deviceFirstOpen = xlite_open, + .deviceLastClose = xlite_close, + .deviceRead = xlite_read_polled, + .deviceInitialize = xlite_init, + .deviceWritePolled = xlite_write_char_polled, + .deviceSetAttributes = xlite_set_attributes, +#if VIRTEX_CONSOLE_USE_INTERRUPTS + .deviceWrite = xlite_write_interrupt_driven, + .deviceOutputUsesInterrupts = true +#else /* VIRTEX_CONSOLE_USE_INTERRUPTS */ + .deviceWrite = xlite_write_buffer_polled, + .deviceOutputUsesInterrupts = false +#endif /* VIRTEX_CONSOLE_USE_INTERRUPTS */ +}; + + + + + + +/* +** Set ulCtrlPort1 to the base address of each UART Lite instance. Set in vhdl model. +*/ + + +console_tbl Console_Configuration_Ports[] = { +{ + "/dev/ttyS0", /* sDeviceName */ + SERIAL_CUSTOM, /* deviceType */ + &xlite_fns_polled, /* pDeviceFns */ + NULL, /* deviceProbe, assume it is there */ + NULL, /* pDeviceFlow */ + 16, /* ulMargin */ + 8, /* ulHysteresis */ + (void *) NULL, /* NULL */ /* pDeviceParams */ + STDIN_BASEADDRESS, /* ulCtrlPort1 */ + 0, /* ulCtrlPort2 */ + 0, /* ulDataPort */ + NULL, /* getRegister */ + NULL, /* setRegister */ + NULL, /* unused */ /* getData */ + NULL, /* unused */ /* setData */ + 0, /* ulClock */ + #ifdef XPAR_XPS_INTC_0_RS232_UART_INTERRUPT_INTR + .ulIntVector = XPAR_XPS_INTC_0_RS232_UART_INTERRUPT_INTR + #else + .ulIntVector = 0 + #endif +}, +#ifdef XPAR_UARTLITE_1_BASEADDR +{ + "/dev/ttyS1", /* sDeviceName */ + SERIAL_CUSTOM, /* deviceType */ + &xlite_fns_polled, /* pDeviceFns */ + NULL, /* deviceProbe, assume it is there */ + NULL, /* pDeviceFlow */ + 16, /* ulMargin */ + 8, /* ulHysteresis */ + (void *) NULL, /* NULL */ /* pDeviceParams */ + XPAR_UARTLITE_1_BASEADDR, /* ulCtrlPort1 */ + 0, /* ulCtrlPort2 */ + 0, /* ulDataPort */ + NULL, /* getRegister */ + NULL, /* setRegister */ + NULL, /* unused */ /* getData */ + NULL, /* unused */ /* setData */ + 0, /* ulClock */ + 0 /* ulIntVector -- base for port */ +}, +#endif +#ifdef XPAR_UARTLITE_2_BASEADDR +{ + "/dev/ttyS2", /* sDeviceName */ + SERIAL_CUSTOM, /* deviceType */ + &xlite_fns_polled, /* pDeviceFns */ + NULL, /* deviceProbe, assume it is there */ + NULL, /* pDeviceFlow */ + 16, /* ulMargin */ + 8, /* ulHysteresis */ + (void *) NULL, /* NULL */ /* pDeviceParams */ + XPAR_UARTLITE_2_BASEADDR, /* ulCtrlPort1 */ + 0, /* ulCtrlPort2 */ + 0, /* ulDataPort */ + NULL, /* getRegister */ + NULL, /* setRegister */ + NULL, /* unused */ /* getData */ + NULL, /* unused */ /* setData */ + 0, /* ulClock */ + 0 /* ulIntVector -- base for port */ +}, +#endif +#ifdef XPAR_UARTLITE_2_BASEADDR +{ + "/dev/ttyS3", /* sDeviceName */ + SERIAL_CUSTOM, /* deviceType */ + &xlite_fns_polled, /* pDeviceFns */ + NULL, /* deviceProbe, assume it is there */ + NULL, /* pDeviceFlow */ + 16, /* ulMargin */ + 8, /* ulHysteresis */ + (void *) NULL, /* NULL */ /* pDeviceParams */ + XPAR_UARTLITE_3_BASEADDR, /* ulCtrlPort1 */ + 0, /* ulCtrlPort2 */ + 0, /* ulDataPort */ + NULL, /* getRegister */ + NULL, /* setRegister */ + NULL, /* unused */ /* getData */ + NULL, /* unused */ /* setData */ + 0, /* ulClock */ + 0 /* ulIntVector -- base for port */ +} +#endif +}; + +unsigned long Console_Configuration_Count = + RTEMS_ARRAY_SIZE(Console_Configuration_Ports); + + +#include <rtems/bspIo.h> + +static void outputChar(char ch) +{ + xlite_write_char_polled( 0, ch ); +} + +static int inputChar(void) +{ + return xlite_read_polled(0); +} + +BSP_output_char_function_type BSP_output_char = outputChar; +BSP_polling_getchar_function_type BSP_poll_char = inputChar; + + |