372 lines
8.8 KiB
C
372 lines
8.8 KiB
C
/*
|
|
* (INCA) ASC UART support
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#if defined(CONFIG_PURPLE) || defined(CONFIG_INCA_IP)
|
|
|
|
#ifdef CONFIG_PURPLE
|
|
#define serial_init asc_serial_init
|
|
#define serial_putc asc_serial_putc
|
|
#define serial_puts asc_serial_puts
|
|
#define serial_getc asc_serial_getc
|
|
#define serial_tstc asc_serial_tstc
|
|
#define serial_setbrg asc_serial_setbrg
|
|
#endif
|
|
|
|
#include <common.h>
|
|
#include <asm/inca-ip.h>
|
|
#include "asc_serial.h"
|
|
|
|
#ifdef CONFIG_PURPLE
|
|
|
|
#undef ASC_FIFO_PRESENT
|
|
#define TOUT_LOOP 100000
|
|
|
|
/* Set base address for second FPI interrupt control register bank */
|
|
#define SFPI_INTCON_BASEADDR 0xBF0F0000
|
|
|
|
/* Register offset from base address */
|
|
#define FBS_ISR 0x00000000 /* Interrupt status register */
|
|
#define FBS_IMR 0x00000008 /* Interrupt mask register */
|
|
#define FBS_IDIS 0x00000010 /* Interrupt disable register */
|
|
|
|
/* Interrupt status register bits */
|
|
#define FBS_ISR_AT 0x00000040 /* ASC transmit interrupt */
|
|
#define FBS_ISR_AR 0x00000020 /* ASC receive interrupt */
|
|
#define FBS_ISR_AE 0x00000010 /* ASC error interrupt */
|
|
#define FBS_ISR_AB 0x00000008 /* ASC transmit buffer interrupt */
|
|
#define FBS_ISR_AS 0x00000004 /* ASC start of autobaud detection interrupt */
|
|
#define FBS_ISR_AF 0x00000002 /* ASC end of autobaud detection interrupt */
|
|
|
|
#else
|
|
|
|
#define ASC_FIFO_PRESENT
|
|
|
|
#endif
|
|
|
|
|
|
#define SET_BIT(reg, mask) reg |= (mask)
|
|
#define CLEAR_BIT(reg, mask) reg &= (~mask)
|
|
#define CLEAR_BITS(reg, mask) CLEAR_BIT(reg, mask)
|
|
#define SET_BITS(reg, mask) SET_BIT(reg, mask)
|
|
#define SET_BITFIELD(reg, mask, off, val) {reg &= (~mask); reg |= (val << off);}
|
|
|
|
extern uint incaip_get_fpiclk(void);
|
|
|
|
static int serial_setopt (void);
|
|
|
|
/* pointer to ASC register base address */
|
|
static volatile incaAsc_t *pAsc = (incaAsc_t *)INCA_IP_ASC;
|
|
|
|
/******************************************************************************
|
|
*
|
|
* serial_init - initialize a INCAASC channel
|
|
*
|
|
* This routine initializes the number of data bits, parity
|
|
* and set the selected baud rate. Interrupts are disabled.
|
|
* Set the modem control signals if the option is selected.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
int serial_init (void)
|
|
{
|
|
#ifdef CONFIG_INCA_IP
|
|
/* we have to set PMU.EN13 bit to enable an ASC device*/
|
|
INCAASC_PMU_ENABLE(13);
|
|
#endif
|
|
|
|
/* and we have to set CLC register*/
|
|
CLEAR_BIT(pAsc->asc_clc, ASCCLC_DISS);
|
|
SET_BITFIELD(pAsc->asc_clc, ASCCLC_RMCMASK, ASCCLC_RMCOFFSET, 0x0001);
|
|
|
|
/* initialy we are in async mode */
|
|
pAsc->asc_con = ASCCON_M_8ASYNC;
|
|
|
|
/* select input port */
|
|
pAsc->asc_pisel = (CONSOLE_TTY & 0x1);
|
|
|
|
#ifdef ASC_FIFO_PRESENT
|
|
/* TXFIFO's filling level */
|
|
SET_BITFIELD(pAsc->asc_txfcon, ASCTXFCON_TXFITLMASK,
|
|
ASCTXFCON_TXFITLOFF, INCAASC_TXFIFO_FL);
|
|
/* enable TXFIFO */
|
|
SET_BIT(pAsc->asc_txfcon, ASCTXFCON_TXFEN);
|
|
|
|
/* RXFIFO's filling level */
|
|
SET_BITFIELD(pAsc->asc_txfcon, ASCRXFCON_RXFITLMASK,
|
|
ASCRXFCON_RXFITLOFF, INCAASC_RXFIFO_FL);
|
|
/* enable RXFIFO */
|
|
SET_BIT(pAsc->asc_rxfcon, ASCRXFCON_RXFEN);
|
|
#endif
|
|
|
|
/* enable error signals */
|
|
SET_BIT(pAsc->asc_con, ASCCON_FEN);
|
|
SET_BIT(pAsc->asc_con, ASCCON_OEN);
|
|
|
|
#ifdef CONFIG_INCA_IP
|
|
/* acknowledge ASC interrupts */
|
|
ASC_INTERRUPTS_CLEAR(INCAASC_IRQ_LINE_ALL);
|
|
|
|
/* disable ASC interrupts */
|
|
ASC_INTERRUPTS_DISABLE(INCAASC_IRQ_LINE_ALL);
|
|
#endif
|
|
|
|
#ifdef ASC_FIFO_PRESENT
|
|
/* set FIFOs into the transparent mode */
|
|
SET_BIT(pAsc->asc_txfcon, ASCTXFCON_TXTMEN);
|
|
SET_BIT(pAsc->asc_rxfcon, ASCRXFCON_RXTMEN);
|
|
#endif
|
|
|
|
/* set baud rate */
|
|
serial_setbrg();
|
|
|
|
/* set the options */
|
|
serial_setopt();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void serial_setbrg (void)
|
|
{
|
|
ulong uiReloadValue, fdv;
|
|
ulong f_ASC;
|
|
|
|
#ifdef CONFIG_INCA_IP
|
|
f_ASC = incaip_get_fpiclk();
|
|
#else
|
|
f_ASC = ASC_CLOCK_RATE;
|
|
#endif
|
|
|
|
#ifndef INCAASC_USE_FDV
|
|
fdv = 2;
|
|
uiReloadValue = (f_ASC / (fdv * 16 * CONFIG_BAUDRATE)) - 1;
|
|
#else
|
|
fdv = INCAASC_FDV_HIGH_BAUDRATE;
|
|
uiReloadValue = (f_ASC / (8192 * CONFIG_BAUDRATE / fdv)) - 1;
|
|
#endif /* INCAASC_USE_FDV */
|
|
|
|
if ( (uiReloadValue < 0) || (uiReloadValue > 8191) )
|
|
{
|
|
#ifndef INCAASC_USE_FDV
|
|
fdv = 3;
|
|
uiReloadValue = (f_ASC / (fdv * 16 * CONFIG_BAUDRATE)) - 1;
|
|
#else
|
|
fdv = INCAASC_FDV_LOW_BAUDRATE;
|
|
uiReloadValue = (f_ASC / (8192 * CONFIG_BAUDRATE / fdv)) - 1;
|
|
#endif /* INCAASC_USE_FDV */
|
|
|
|
if ( (uiReloadValue < 0) || (uiReloadValue > 8191) )
|
|
{
|
|
return; /* can't impossibly generate that baud rate */
|
|
}
|
|
}
|
|
|
|
/* Disable Baud Rate Generator; BG should only be written when R=0 */
|
|
CLEAR_BIT(pAsc->asc_con, ASCCON_R);
|
|
|
|
#ifndef INCAASC_USE_FDV
|
|
/*
|
|
* Disable Fractional Divider (FDE)
|
|
* Divide clock by reload-value + constant (BRS)
|
|
*/
|
|
/* FDE = 0 */
|
|
CLEAR_BIT(pAsc->asc_con, ASCCON_FDE);
|
|
|
|
if ( fdv == 2 )
|
|
CLEAR_BIT(pAsc->asc_con, ASCCON_BRS); /* BRS = 0 */
|
|
else
|
|
SET_BIT(pAsc->asc_con, ASCCON_BRS); /* BRS = 1 */
|
|
|
|
#else /* INCAASC_USE_FDV */
|
|
|
|
/* Enable Fractional Divider */
|
|
SET_BIT(pAsc->asc_con, ASCCON_FDE); /* FDE = 1 */
|
|
|
|
/* Set fractional divider value */
|
|
pAsc->asc_fdv = fdv & ASCFDV_VALUE_MASK;
|
|
|
|
#endif /* INCAASC_USE_FDV */
|
|
|
|
/* Set reload value in BG */
|
|
pAsc->asc_bg = uiReloadValue;
|
|
|
|
/* Enable Baud Rate Generator */
|
|
SET_BIT(pAsc->asc_con, ASCCON_R); /* R = 1 */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* serial_setopt - set the serial options
|
|
*
|
|
* Set the channel operating mode to that specified. Following options
|
|
* are supported: CREAD, CSIZE, PARENB, and PARODD.
|
|
*
|
|
* Note, this routine disables the transmitter. The calling routine
|
|
* may have to re-enable it.
|
|
*
|
|
* RETURNS:
|
|
* Returns 0 to indicate success, otherwise -1 is returned
|
|
*/
|
|
|
|
static int serial_setopt (void)
|
|
{
|
|
ulong con;
|
|
|
|
switch ( ASC_OPTIONS & ASCOPT_CSIZE )
|
|
{
|
|
/* 7-bit-data */
|
|
case ASCOPT_CS7:
|
|
con = ASCCON_M_7ASYNCPAR; /* 7-bit-data and parity bit */
|
|
break;
|
|
|
|
/* 8-bit-data */
|
|
case ASCOPT_CS8:
|
|
if ( ASC_OPTIONS & ASCOPT_PARENB )
|
|
con = ASCCON_M_8ASYNCPAR; /* 8-bit-data and parity bit */
|
|
else
|
|
con = ASCCON_M_8ASYNC; /* 8-bit-data no parity */
|
|
break;
|
|
|
|
/*
|
|
* only 7 and 8-bit frames are supported
|
|
* if we don't use IOCTL extensions
|
|
*/
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if ( ASC_OPTIONS & ASCOPT_STOPB )
|
|
SET_BIT(con, ASCCON_STP); /* 2 stop bits */
|
|
else
|
|
CLEAR_BIT(con, ASCCON_STP); /* 1 stop bit */
|
|
|
|
if ( ASC_OPTIONS & ASCOPT_PARENB )
|
|
SET_BIT(con, ASCCON_PEN); /* enable parity checking */
|
|
else
|
|
CLEAR_BIT(con, ASCCON_PEN); /* disable parity checking */
|
|
|
|
if ( ASC_OPTIONS & ASCOPT_PARODD )
|
|
SET_BIT(con, ASCCON_ODD); /* odd parity */
|
|
else
|
|
CLEAR_BIT(con, ASCCON_ODD); /* even parity */
|
|
|
|
if ( ASC_OPTIONS & ASCOPT_CREAD )
|
|
SET_BIT(pAsc->asc_whbcon, ASCWHBCON_SETREN); /* Receiver enable */
|
|
|
|
pAsc->asc_con |= con;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void serial_putc (const char c)
|
|
{
|
|
#ifdef ASC_FIFO_PRESENT
|
|
uint txFl = 0;
|
|
#else
|
|
uint timeout = 0;
|
|
#endif
|
|
|
|
if (c == '\n') serial_putc ('\r');
|
|
|
|
#ifdef ASC_FIFO_PRESENT
|
|
/* check do we have a free space in the TX FIFO */
|
|
/* get current filling level */
|
|
do
|
|
{
|
|
txFl = ( pAsc->asc_fstat & ASCFSTAT_TXFFLMASK ) >> ASCFSTAT_TXFFLOFF;
|
|
}
|
|
while ( txFl == INCAASC_TXFIFO_FULL );
|
|
#else
|
|
|
|
while(!(*(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) &
|
|
FBS_ISR_AB))
|
|
{
|
|
if (timeout++ > TOUT_LOOP)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pAsc->asc_tbuf = c; /* write char to Transmit Buffer Register */
|
|
|
|
#ifndef ASC_FIFO_PRESENT
|
|
*(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) = FBS_ISR_AB |
|
|
FBS_ISR_AT;
|
|
#endif
|
|
|
|
/* check for errors */
|
|
if ( pAsc->asc_con & ASCCON_OE )
|
|
{
|
|
SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLROE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void serial_puts (const char *s)
|
|
{
|
|
while (*s)
|
|
{
|
|
serial_putc (*s++);
|
|
}
|
|
}
|
|
|
|
int serial_getc (void)
|
|
{
|
|
ulong symbol_mask;
|
|
char c;
|
|
|
|
while (!serial_tstc());
|
|
|
|
symbol_mask =
|
|
((ASC_OPTIONS & ASCOPT_CSIZE) == ASCOPT_CS7) ? (0x7f) : (0xff);
|
|
|
|
c = (char)(pAsc->asc_rbuf & symbol_mask);
|
|
|
|
#ifndef ASC_FIFO_PRESENT
|
|
*(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) = FBS_ISR_AR;
|
|
#endif
|
|
|
|
return c;
|
|
}
|
|
|
|
int serial_tstc (void)
|
|
{
|
|
int res = 1;
|
|
|
|
#ifdef ASC_FIFO_PRESENT
|
|
if ( (pAsc->asc_fstat & ASCFSTAT_RXFFLMASK) == 0 )
|
|
{
|
|
res = 0;
|
|
}
|
|
#else
|
|
if (!(*(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) &
|
|
FBS_ISR_AR))
|
|
|
|
{
|
|
res = 0;
|
|
}
|
|
#endif
|
|
else if ( pAsc->asc_con & ASCCON_FE )
|
|
{
|
|
SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLRFE);
|
|
res = 0;
|
|
}
|
|
else if ( pAsc->asc_con & ASCCON_PE )
|
|
{
|
|
SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLRPE);
|
|
res = 0;
|
|
}
|
|
else if ( pAsc->asc_con & ASCCON_OE )
|
|
{
|
|
SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLROE);
|
|
res = 0;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
#endif /* CONFIG_PURPLE || CONFIG_INCA_IP */
|