/* * (C) Copyright 2003 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * (C) Copyright 2009 * Infineon Technologies AG, http://www.infineon.com * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include #include "ifx_asc.h" #define SET_BIT(reg, mask) asc_writel(reg, asc_readl(reg) | (mask)) #define CLEAR_BIT(reg, mask) asc_writel(reg, asc_readl(reg) & (~mask)) #define SET_BITFIELD(reg, mask, off, val) asc_writel(reg, (asc_readl(reg) & (~mask)) | (val << off) ) #undef DEBUG_ASC_RAW #ifdef DEBUG_ASC_RAW #define DEBUG_ASC_RAW_RX_BUF 0xA0800000 #define DEBUG_ASC_RAW_TX_BUF 0xA0900000 #endif DECLARE_GLOBAL_DATA_PTR; static IfxAsc_t *pAsc = (IfxAsc_t *)CKSEG1ADDR(CONFIG_SYS_IFX_ASC_BASE); /* * FDV fASC * BaudRate = ----- * -------------------- * 512 16 * (ReloadValue+1) */ /* * FDV fASC * ReloadValue = ( ----- * --------------- ) - 1 * 512 16 * BaudRate */ static void serial_divs(u32 baudrate, u32 fasc, u32 *pfdv, u32 *preload) { u32 clock = fasc / 16; u32 fdv; /* best fdv */ u32 reload = 0; /* best reload */ u32 diff; /* smallest diff */ u32 idiff; /* current diff */ u32 ireload; /* current reload */ u32 i; /* current fdv */ u32 result; /* current resulting baudrate */ if (clock > 0x7FFFFF) clock /= 512; else baudrate *= 512; fdv = 512; /* start with 1:1 fraction */ diff = baudrate; /* highest possible */ /* i is the test fdv value -- start with the largest possible */ for (i = 512; i > 0; i--) { ireload = (clock * i) / baudrate; if (ireload < 1) break; /* already invalid */ result = (clock * i) / ireload; idiff = (result > baudrate) ? (result - baudrate) : (baudrate - result); if (idiff == 0) { fdv = i; reload = ireload; break; /* can't do better */ } else if (idiff < diff) { fdv = i; /* best so far */ reload = ireload; diff = idiff; /* update lowest diff*/ } } *pfdv = (fdv == 512) ? 0 : fdv; *preload = reload - 1; } void serial_setbrg (void) { u32 ReloadValue, fdv; serial_divs(gd->baudrate, get_bus_freq(0), &fdv, &ReloadValue); /* Disable Baud Rate Generator; BG should only be written when R=0 */ CLEAR_BIT(asc_con, ASCCON_R); /* Enable Fractional Divider */ SET_BIT(asc_con, ASCCON_FDE); /* FDE = 1 */ /* Set fractional divider value */ asc_writel(asc_fdv, fdv & ASCFDV_VALUE_MASK); /* Set reload value in BG */ asc_writel(asc_bg, ReloadValue); /* Enable Baud Rate Generator */ SET_BIT(asc_con, ASCCON_R); /* R = 1 */ } int serial_init (void) { /* and we have to set CLC register*/ CLEAR_BIT(asc_clc, ASCCLC_DISS); SET_BITFIELD(asc_clc, ASCCLC_RMCMASK, ASCCLC_RMCOFFSET, 0x0001); /* initialy we are in async mode */ asc_writel(asc_con, ASCCON_M_8ASYNC); /* select input port */ asc_writel(asc_pisel, CONSOLE_TTY & 0x1); /* TXFIFO's filling level */ SET_BITFIELD(asc_txfcon, ASCTXFCON_TXFITLMASK, ASCTXFCON_TXFITLOFF, ASC_TXFIFO_FL); /* enable TXFIFO */ SET_BIT(asc_txfcon, ASCTXFCON_TXFEN); /* RXFIFO's filling level */ SET_BITFIELD(asc_txfcon, ASCRXFCON_RXFITLMASK, ASCRXFCON_RXFITLOFF, ASC_RXFIFO_FL); /* enable RXFIFO */ SET_BIT(asc_rxfcon, ASCRXFCON_RXFEN); /* set baud rate */ serial_setbrg(); /* enable error signals & Receiver enable */ SET_BIT(asc_whbstate, ASCWHBSTATE_SETREN|ASCCON_FEN|ASCCON_TOEN|ASCCON_ROEN); return 0; } void serial_putc (const char c) { u32 txFl = 0; #ifdef DEBUG_ASC_RAW static u8 * debug = (u8 *) DEBUG_ASC_RAW_TX_BUF; *debug++=c; #endif if (c == '\n') serial_putc ('\r'); /* check do we have a free space in the TX FIFO */ /* get current filling level */ do { txFl = ( asc_readl(asc_fstat) & ASCFSTAT_TXFFLMASK ) >> ASCFSTAT_TXFFLOFF; } while ( txFl == ASC_TXFIFO_FULL ); asc_writel(asc_tbuf, c); /* write char to Transmit Buffer Register */ /* check for errors */ if ( asc_readl(asc_state) & ASCSTATE_TOE ) { SET_BIT(asc_whbstate, ASCWHBSTATE_CLRTOE); return; } } void serial_puts (const char *s) { while (*s) { serial_putc (*s++); } } int serial_getc (void) { char c; while ((asc_readl(asc_fstat) & ASCFSTAT_RXFFLMASK) == 0 ); c = (char)(asc_readl(asc_rbuf) & 0xff); #ifdef DEBUG_ASC_RAW static u8* debug=(u8*)(DEBUG_ASC_RAW_RX_BUF); *debug++=c; #endif return c; } int serial_tstc (void) { int res = 1; if ( (asc_readl(asc_fstat) & ASCFSTAT_RXFFLMASK) == 0 ) { res = 0; } return res; }