/*===============================================================*\
| 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;
}
}