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