mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-17 02:08:38 +00:00
ec3fd68952
Replace the in-place ad-hoc implementation of serial_puts() within the drivers with default_serial_puts() call. This cuts down on the code duplication quite a bit. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Marek Vasut <marek.vasut@gmail.com> Cc: Tom Rini <trini@ti.com>
300 lines
7.2 KiB
C
300 lines
7.2 KiB
C
/*
|
|
* (INCA) ASC UART support
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <common.h>
|
|
#include <asm/inca-ip.h>
|
|
#include <serial.h>
|
|
#include <linux/compiler.h>
|
|
#include "asc_serial.h"
|
|
|
|
|
|
#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
|
|
*/
|
|
|
|
static int asc_serial_init(void)
|
|
{
|
|
/* we have to set PMU.EN13 bit to enable an ASC device*/
|
|
INCAASC_PMU_ENABLE(13);
|
|
|
|
/* 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);
|
|
|
|
/* 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);
|
|
|
|
/* enable error signals */
|
|
SET_BIT(pAsc->asc_con, ASCCON_FEN);
|
|
SET_BIT(pAsc->asc_con, ASCCON_OEN);
|
|
|
|
/* acknowledge ASC interrupts */
|
|
ASC_INTERRUPTS_CLEAR(INCAASC_IRQ_LINE_ALL);
|
|
|
|
/* disable ASC interrupts */
|
|
ASC_INTERRUPTS_DISABLE(INCAASC_IRQ_LINE_ALL);
|
|
|
|
/* set FIFOs into the transparent mode */
|
|
SET_BIT(pAsc->asc_txfcon, ASCTXFCON_TXTMEN);
|
|
SET_BIT(pAsc->asc_rxfcon, ASCRXFCON_RXTMEN);
|
|
|
|
/* set baud rate */
|
|
serial_setbrg();
|
|
|
|
/* set the options */
|
|
serial_setopt();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void asc_serial_setbrg(void)
|
|
{
|
|
ulong uiReloadValue, fdv;
|
|
ulong f_ASC;
|
|
|
|
f_ASC = incaip_get_fpiclk();
|
|
|
|
#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;
|
|
}
|
|
|
|
static void asc_serial_putc(const char c)
|
|
{
|
|
uint txFl = 0;
|
|
|
|
if (c == '\n') serial_putc ('\r');
|
|
|
|
/* 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 );
|
|
|
|
pAsc->asc_tbuf = c; /* write char to Transmit Buffer Register */
|
|
|
|
/* check for errors */
|
|
if ( pAsc->asc_con & ASCCON_OE )
|
|
{
|
|
SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLROE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int asc_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);
|
|
|
|
return c;
|
|
}
|
|
|
|
static int asc_serial_tstc(void)
|
|
{
|
|
int res = 1;
|
|
|
|
if ( (pAsc->asc_fstat & ASCFSTAT_RXFFLMASK) == 0 )
|
|
{
|
|
res = 0;
|
|
}
|
|
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;
|
|
}
|
|
|
|
static struct serial_device asc_serial_drv = {
|
|
.name = "asc_serial",
|
|
.start = asc_serial_init,
|
|
.stop = NULL,
|
|
.setbrg = asc_serial_setbrg,
|
|
.putc = asc_serial_putc,
|
|
.puts = default_serial_puts,
|
|
.getc = asc_serial_getc,
|
|
.tstc = asc_serial_tstc,
|
|
};
|
|
|
|
void asc_serial_initialize(void)
|
|
{
|
|
serial_register(&asc_serial_drv);
|
|
}
|
|
|
|
__weak struct serial_device *default_serial_console(void)
|
|
{
|
|
return &asc_serial_drv;
|
|
}
|