From d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Thu, 19 Apr 2018 06:28:01 +0200 Subject: bsps: Move console drivers to bsps This patch is a part of the BSP source reorganization. Update #3285. --- bsps/powerpc/gen5200/console/console.c | 835 +++++++++++++++++++++++++++++++++ 1 file changed, 835 insertions(+) create mode 100644 bsps/powerpc/gen5200/console/console.c (limited to 'bsps/powerpc/gen5200/console/console.c') 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 */ +/* and Charles-Antoine Gauthier */ +/* Copyright (c) 1999, National Research Council of Canada */ +/* */ +/* Modifications by Andy Dachs 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 +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + + +#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); +} -- cgit v1.2.3