diff options
Diffstat (limited to 'bsps/powerpc/tqm8xx/console/console.c')
-rw-r--r-- | bsps/powerpc/tqm8xx/console/console.c | 1115 |
1 files changed, 1115 insertions, 0 deletions
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; + } +} + |