blob: be686c2ae8ddbb5e0fc0594f0998cf3b19289da1 [file] [log] [blame]
wdenkbb1b8262003-03-27 12:09:35 +00001/*
2 * (INCA) ASC UART support
3 */
4
wdenkb02744a2003-04-05 00:53:31 +00005#include <config.h>
6
7#ifdef CONFIG_PURPLE
8#define serial_init asc_serial_init
9#define serial_putc asc_serial_putc
10#define serial_puts asc_serial_puts
11#define serial_getc asc_serial_getc
12#define serial_tstc asc_serial_tstc
13#define serial_setbrg asc_serial_setbrg
14#endif
15
wdenkbb1b8262003-03-27 12:09:35 +000016#include <common.h>
17#include <asm/inca-ip.h>
wdenk9b7f3842003-10-09 20:09:04 +000018#include "asc_serial.h"
wdenkbb1b8262003-03-27 12:09:35 +000019
wdenkb02744a2003-04-05 00:53:31 +000020#ifdef CONFIG_PURPLE
21
22#undef ASC_FIFO_PRESENT
23#define TOUT_LOOP 100000
24
25/* Set base address for second FPI interrupt control register bank */
wdenk57b2d802003-06-27 21:31:46 +000026#define SFPI_INTCON_BASEADDR 0xBF0F0000
wdenkb02744a2003-04-05 00:53:31 +000027
28/* Register offset from base address */
29#define FBS_ISR 0x00000000 /* Interrupt status register */
30#define FBS_IMR 0x00000008 /* Interrupt mask register */
31#define FBS_IDIS 0x00000010 /* Interrupt disable register */
32
33/* Interrupt status register bits */
34#define FBS_ISR_AT 0x00000040 /* ASC transmit interrupt */
Wolfgang Denka1be4762008-05-20 16:00:29 +020035#define FBS_ISR_AR 0x00000020 /* ASC receive interrupt */
wdenkb02744a2003-04-05 00:53:31 +000036#define FBS_ISR_AE 0x00000010 /* ASC error interrupt */
37#define FBS_ISR_AB 0x00000008 /* ASC transmit buffer interrupt */
Wolfgang Denka1be4762008-05-20 16:00:29 +020038#define FBS_ISR_AS 0x00000004 /* ASC start of autobaud detection interrupt */
wdenkb02744a2003-04-05 00:53:31 +000039#define FBS_ISR_AF 0x00000002 /* ASC end of autobaud detection interrupt */
40
41#else
42
43#define ASC_FIFO_PRESENT
44
45#endif
46
47
wdenkbb1b8262003-03-27 12:09:35 +000048#define SET_BIT(reg, mask) reg |= (mask)
49#define CLEAR_BIT(reg, mask) reg &= (~mask)
50#define CLEAR_BITS(reg, mask) CLEAR_BIT(reg, mask)
51#define SET_BITS(reg, mask) SET_BIT(reg, mask)
52#define SET_BITFIELD(reg, mask, off, val) {reg &= (~mask); reg |= (val << off);}
53
54extern uint incaip_get_fpiclk(void);
55
56static int serial_setopt (void);
57
58/* pointer to ASC register base address */
59static volatile incaAsc_t *pAsc = (incaAsc_t *)INCA_IP_ASC;
60
61/******************************************************************************
62*
63* serial_init - initialize a INCAASC channel
64*
65* This routine initializes the number of data bits, parity
66* and set the selected baud rate. Interrupts are disabled.
67* Set the modem control signals if the option is selected.
68*
69* RETURNS: N/A
70*/
71
72int serial_init (void)
73{
wdenkb02744a2003-04-05 00:53:31 +000074#ifdef CONFIG_INCA_IP
wdenkbb1b8262003-03-27 12:09:35 +000075 /* we have to set PMU.EN13 bit to enable an ASC device*/
76 INCAASC_PMU_ENABLE(13);
wdenkb02744a2003-04-05 00:53:31 +000077#endif
wdenk57b2d802003-06-27 21:31:46 +000078
wdenkbb1b8262003-03-27 12:09:35 +000079 /* and we have to set CLC register*/
80 CLEAR_BIT(pAsc->asc_clc, ASCCLC_DISS);
81 SET_BITFIELD(pAsc->asc_clc, ASCCLC_RMCMASK, ASCCLC_RMCOFFSET, 0x0001);
wdenk57b2d802003-06-27 21:31:46 +000082
wdenkbb1b8262003-03-27 12:09:35 +000083 /* initialy we are in async mode */
84 pAsc->asc_con = ASCCON_M_8ASYNC;
85
86 /* select input port */
87 pAsc->asc_pisel = (CONSOLE_TTY & 0x1);
88
wdenkb02744a2003-04-05 00:53:31 +000089#ifdef ASC_FIFO_PRESENT
wdenkbb1b8262003-03-27 12:09:35 +000090 /* TXFIFO's filling level */
91 SET_BITFIELD(pAsc->asc_txfcon, ASCTXFCON_TXFITLMASK,
wdenk57b2d802003-06-27 21:31:46 +000092 ASCTXFCON_TXFITLOFF, INCAASC_TXFIFO_FL);
wdenkbb1b8262003-03-27 12:09:35 +000093 /* enable TXFIFO */
94 SET_BIT(pAsc->asc_txfcon, ASCTXFCON_TXFEN);
95
96 /* RXFIFO's filling level */
wdenk57b2d802003-06-27 21:31:46 +000097 SET_BITFIELD(pAsc->asc_txfcon, ASCRXFCON_RXFITLMASK,
98 ASCRXFCON_RXFITLOFF, INCAASC_RXFIFO_FL);
wdenkbb1b8262003-03-27 12:09:35 +000099 /* enable RXFIFO */
100 SET_BIT(pAsc->asc_rxfcon, ASCRXFCON_RXFEN);
wdenkb02744a2003-04-05 00:53:31 +0000101#endif
wdenkbb1b8262003-03-27 12:09:35 +0000102
103 /* enable error signals */
104 SET_BIT(pAsc->asc_con, ASCCON_FEN);
105 SET_BIT(pAsc->asc_con, ASCCON_OEN);
106
wdenkb02744a2003-04-05 00:53:31 +0000107#ifdef CONFIG_INCA_IP
wdenkbb1b8262003-03-27 12:09:35 +0000108 /* acknowledge ASC interrupts */
109 ASC_INTERRUPTS_CLEAR(INCAASC_IRQ_LINE_ALL);
110
111 /* disable ASC interrupts */
112 ASC_INTERRUPTS_DISABLE(INCAASC_IRQ_LINE_ALL);
wdenkb02744a2003-04-05 00:53:31 +0000113#endif
wdenkbb1b8262003-03-27 12:09:35 +0000114
wdenkb02744a2003-04-05 00:53:31 +0000115#ifdef ASC_FIFO_PRESENT
wdenkbb1b8262003-03-27 12:09:35 +0000116 /* set FIFOs into the transparent mode */
117 SET_BIT(pAsc->asc_txfcon, ASCTXFCON_TXTMEN);
118 SET_BIT(pAsc->asc_rxfcon, ASCRXFCON_RXTMEN);
wdenkb02744a2003-04-05 00:53:31 +0000119#endif
wdenkbb1b8262003-03-27 12:09:35 +0000120
121 /* set baud rate */
122 serial_setbrg();
123
124 /* set the options */
125 serial_setopt();
wdenk57b2d802003-06-27 21:31:46 +0000126
wdenkbb1b8262003-03-27 12:09:35 +0000127 return 0;
128}
129
130void serial_setbrg (void)
131{
132 ulong uiReloadValue, fdv;
133 ulong f_ASC;
134
wdenkb02744a2003-04-05 00:53:31 +0000135#ifdef CONFIG_INCA_IP
wdenkbb1b8262003-03-27 12:09:35 +0000136 f_ASC = incaip_get_fpiclk();
wdenkb02744a2003-04-05 00:53:31 +0000137#else
138 f_ASC = ASC_CLOCK_RATE;
139#endif
wdenkbb1b8262003-03-27 12:09:35 +0000140
141#ifndef INCAASC_USE_FDV
142 fdv = 2;
143 uiReloadValue = (f_ASC / (fdv * 16 * CONFIG_BAUDRATE)) - 1;
wdenk57b2d802003-06-27 21:31:46 +0000144#else
wdenkbb1b8262003-03-27 12:09:35 +0000145 fdv = INCAASC_FDV_HIGH_BAUDRATE;
146 uiReloadValue = (f_ASC / (8192 * CONFIG_BAUDRATE / fdv)) - 1;
147#endif /* INCAASC_USE_FDV */
wdenk57b2d802003-06-27 21:31:46 +0000148
wdenkbb1b8262003-03-27 12:09:35 +0000149 if ( (uiReloadValue < 0) || (uiReloadValue > 8191) )
150 {
151#ifndef INCAASC_USE_FDV
wdenk57b2d802003-06-27 21:31:46 +0000152 fdv = 3;
153 uiReloadValue = (f_ASC / (fdv * 16 * CONFIG_BAUDRATE)) - 1;
154#else
155 fdv = INCAASC_FDV_LOW_BAUDRATE;
156 uiReloadValue = (f_ASC / (8192 * CONFIG_BAUDRATE / fdv)) - 1;
wdenkbb1b8262003-03-27 12:09:35 +0000157#endif /* INCAASC_USE_FDV */
wdenk57b2d802003-06-27 21:31:46 +0000158
159 if ( (uiReloadValue < 0) || (uiReloadValue > 8191) )
160 {
161 return; /* can't impossibly generate that baud rate */
162 }
wdenkbb1b8262003-03-27 12:09:35 +0000163 }
164
165 /* Disable Baud Rate Generator; BG should only be written when R=0 */
166 CLEAR_BIT(pAsc->asc_con, ASCCON_R);
167
168#ifndef INCAASC_USE_FDV
169 /*
170 * Disable Fractional Divider (FDE)
171 * Divide clock by reload-value + constant (BRS)
172 */
173 /* FDE = 0 */
174 CLEAR_BIT(pAsc->asc_con, ASCCON_FDE);
175
176 if ( fdv == 2 )
wdenk57b2d802003-06-27 21:31:46 +0000177 CLEAR_BIT(pAsc->asc_con, ASCCON_BRS); /* BRS = 0 */
wdenkbb1b8262003-03-27 12:09:35 +0000178 else
wdenk57b2d802003-06-27 21:31:46 +0000179 SET_BIT(pAsc->asc_con, ASCCON_BRS); /* BRS = 1 */
wdenkbb1b8262003-03-27 12:09:35 +0000180
181#else /* INCAASC_USE_FDV */
182
183 /* Enable Fractional Divider */
184 SET_BIT(pAsc->asc_con, ASCCON_FDE); /* FDE = 1 */
185
186 /* Set fractional divider value */
187 pAsc->asc_fdv = fdv & ASCFDV_VALUE_MASK;
188
189#endif /* INCAASC_USE_FDV */
190
191 /* Set reload value in BG */
192 pAsc->asc_bg = uiReloadValue;
193
194 /* Enable Baud Rate Generator */
195 SET_BIT(pAsc->asc_con, ASCCON_R); /* R = 1 */
196}
197
198/*******************************************************************************
199*
200* serial_setopt - set the serial options
201*
202* Set the channel operating mode to that specified. Following options
203* are supported: CREAD, CSIZE, PARENB, and PARODD.
204*
205* Note, this routine disables the transmitter. The calling routine
206* may have to re-enable it.
207*
208* RETURNS:
209* Returns 0 to indicate success, otherwise -1 is returned
210*/
211
212static int serial_setopt (void)
213{
214 ulong con;
215
216 switch ( ASC_OPTIONS & ASCOPT_CSIZE )
217 {
218 /* 7-bit-data */
219 case ASCOPT_CS7:
wdenk57b2d802003-06-27 21:31:46 +0000220 con = ASCCON_M_7ASYNCPAR; /* 7-bit-data and parity bit */
221 break;
wdenkbb1b8262003-03-27 12:09:35 +0000222
223 /* 8-bit-data */
224 case ASCOPT_CS8:
wdenk57b2d802003-06-27 21:31:46 +0000225 if ( ASC_OPTIONS & ASCOPT_PARENB )
226 con = ASCCON_M_8ASYNCPAR; /* 8-bit-data and parity bit */
227 else
228 con = ASCCON_M_8ASYNC; /* 8-bit-data no parity */
229 break;
230
231 /*
wdenkbb1b8262003-03-27 12:09:35 +0000232 * only 7 and 8-bit frames are supported
wdenk57b2d802003-06-27 21:31:46 +0000233 * if we don't use IOCTL extensions
wdenkbb1b8262003-03-27 12:09:35 +0000234 */
235 default:
wdenk57b2d802003-06-27 21:31:46 +0000236 return -1;
wdenkbb1b8262003-03-27 12:09:35 +0000237 }
238
239 if ( ASC_OPTIONS & ASCOPT_STOPB )
wdenk57b2d802003-06-27 21:31:46 +0000240 SET_BIT(con, ASCCON_STP); /* 2 stop bits */
wdenkbb1b8262003-03-27 12:09:35 +0000241 else
wdenk57b2d802003-06-27 21:31:46 +0000242 CLEAR_BIT(con, ASCCON_STP); /* 1 stop bit */
wdenkbb1b8262003-03-27 12:09:35 +0000243
244 if ( ASC_OPTIONS & ASCOPT_PARENB )
wdenk57b2d802003-06-27 21:31:46 +0000245 SET_BIT(con, ASCCON_PEN); /* enable parity checking */
wdenkbb1b8262003-03-27 12:09:35 +0000246 else
wdenk57b2d802003-06-27 21:31:46 +0000247 CLEAR_BIT(con, ASCCON_PEN); /* disable parity checking */
248
wdenkbb1b8262003-03-27 12:09:35 +0000249 if ( ASC_OPTIONS & ASCOPT_PARODD )
wdenk57b2d802003-06-27 21:31:46 +0000250 SET_BIT(con, ASCCON_ODD); /* odd parity */
wdenkbb1b8262003-03-27 12:09:35 +0000251 else
wdenk57b2d802003-06-27 21:31:46 +0000252 CLEAR_BIT(con, ASCCON_ODD); /* even parity */
wdenkbb1b8262003-03-27 12:09:35 +0000253
254 if ( ASC_OPTIONS & ASCOPT_CREAD )
wdenk57b2d802003-06-27 21:31:46 +0000255 SET_BIT(pAsc->asc_whbcon, ASCWHBCON_SETREN); /* Receiver enable */
wdenkbb1b8262003-03-27 12:09:35 +0000256
257 pAsc->asc_con |= con;
258
259 return 0;
260}
261
262void serial_putc (const char c)
263{
wdenkb02744a2003-04-05 00:53:31 +0000264#ifdef ASC_FIFO_PRESENT
wdenkbb1b8262003-03-27 12:09:35 +0000265 uint txFl = 0;
wdenkb02744a2003-04-05 00:53:31 +0000266#else
267 uint timeout = 0;
268#endif
wdenkbb1b8262003-03-27 12:09:35 +0000269
270 if (c == '\n') serial_putc ('\r');
271
wdenkb02744a2003-04-05 00:53:31 +0000272#ifdef ASC_FIFO_PRESENT
wdenkbb1b8262003-03-27 12:09:35 +0000273 /* check do we have a free space in the TX FIFO */
274 /* get current filling level */
275 do
276 {
277 txFl = ( pAsc->asc_fstat & ASCFSTAT_TXFFLMASK ) >> ASCFSTAT_TXFFLOFF;
278 }
279 while ( txFl == INCAASC_TXFIFO_FULL );
wdenkb02744a2003-04-05 00:53:31 +0000280#else
281
282 while(!(*(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) &
283 FBS_ISR_AB))
284 {
285 if (timeout++ > TOUT_LOOP)
286 {
287 break;
288 }
289 }
290#endif
wdenkbb1b8262003-03-27 12:09:35 +0000291
292 pAsc->asc_tbuf = c; /* write char to Transmit Buffer Register */
wdenkb02744a2003-04-05 00:53:31 +0000293
294#ifndef ASC_FIFO_PRESENT
295 *(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) = FBS_ISR_AB |
wdenk57b2d802003-06-27 21:31:46 +0000296 FBS_ISR_AT;
wdenkb02744a2003-04-05 00:53:31 +0000297#endif
wdenk57b2d802003-06-27 21:31:46 +0000298
wdenkbb1b8262003-03-27 12:09:35 +0000299 /* check for errors */
300 if ( pAsc->asc_con & ASCCON_OE )
301 {
wdenk57b2d802003-06-27 21:31:46 +0000302 SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLROE);
303 return;
wdenkbb1b8262003-03-27 12:09:35 +0000304 }
305}
306
307void serial_puts (const char *s)
308{
309 while (*s)
310 {
311 serial_putc (*s++);
312 }
313}
314
315int serial_getc (void)
316{
317 ulong symbol_mask;
318 char c;
319
320 while (!serial_tstc());
321
322 symbol_mask =
323 ((ASC_OPTIONS & ASCOPT_CSIZE) == ASCOPT_CS7) ? (0x7f) : (0xff);
wdenk57b2d802003-06-27 21:31:46 +0000324
wdenkbb1b8262003-03-27 12:09:35 +0000325 c = (char)(pAsc->asc_rbuf & symbol_mask);
326
wdenkb02744a2003-04-05 00:53:31 +0000327#ifndef ASC_FIFO_PRESENT
328 *(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) = FBS_ISR_AR;
329#endif
330
wdenkbb1b8262003-03-27 12:09:35 +0000331 return c;
332}
333
334int serial_tstc (void)
335{
336 int res = 1;
337
wdenkb02744a2003-04-05 00:53:31 +0000338#ifdef ASC_FIFO_PRESENT
wdenkbb1b8262003-03-27 12:09:35 +0000339 if ( (pAsc->asc_fstat & ASCFSTAT_RXFFLMASK) == 0 )
340 {
wdenk57b2d802003-06-27 21:31:46 +0000341 res = 0;
wdenkbb1b8262003-03-27 12:09:35 +0000342 }
wdenkb02744a2003-04-05 00:53:31 +0000343#else
344 if (!(*(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) &
wdenk57b2d802003-06-27 21:31:46 +0000345 FBS_ISR_AR))
346
wdenkb02744a2003-04-05 00:53:31 +0000347 {
wdenk57b2d802003-06-27 21:31:46 +0000348 res = 0;
wdenkb02744a2003-04-05 00:53:31 +0000349 }
350#endif
wdenkbb1b8262003-03-27 12:09:35 +0000351 else if ( pAsc->asc_con & ASCCON_FE )
352 {
wdenk57b2d802003-06-27 21:31:46 +0000353 SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLRFE);
354 res = 0;
wdenkbb1b8262003-03-27 12:09:35 +0000355 }
356 else if ( pAsc->asc_con & ASCCON_PE )
357 {
wdenk57b2d802003-06-27 21:31:46 +0000358 SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLRPE);
359 res = 0;
wdenkbb1b8262003-03-27 12:09:35 +0000360 }
361 else if ( pAsc->asc_con & ASCCON_OE )
362 {
wdenk57b2d802003-06-27 21:31:46 +0000363 SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLROE);
364 res = 0;
wdenkbb1b8262003-03-27 12:09:35 +0000365 }
366
367 return res;
368}