TEXT_BASE is in board/sandpoint/config.mk so say so...
[u-boot.git] / cpu / i386 / serial.c
1 /*
2  * (C) Copyright 2002
3  * Daniel Engström, Omicron Ceti AB, daniel@omicron.se
4  *
5  * (C) Copyright 2000
6  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7  *
8  * See file CREDITS for list of people who contributed to this
9  * project.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of
14  * the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24  * MA 02111-1307 USA
25  */
26 /*------------------------------------------------------------------------------+ */
27
28 /*
29  * This source code has been made available to you by IBM on an AS-IS
30  * basis.  Anyone receiving this source is licensed under IBM
31  * copyrights to use it in any way he or she deems fit, including
32  * copying it, modifying it, compiling it, and redistributing it either
33  * with or without modifications.  No license under IBM patents or
34  * patent applications is to be implied by the copyright license.
35  *
36  * Any user of this software should understand that IBM cannot provide
37  * technical support for this software and will not be responsible for
38  * any consequences resulting from the use of this software.
39  *
40  * Any person who transfers this source code or any derivative work
41  * must include the IBM copyright notice, this paragraph, and the
42  * preceding two paragraphs in the transferred software.
43  *
44  * COPYRIGHT   I B M   CORPORATION 1995
45  * LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M
46  */
47 /*------------------------------------------------------------------------------- */
48
49 #include <common.h>
50 #include <watchdog.h>
51 #include <asm/io.h>
52 #include <asm/ibmpc.h>
53
54 #ifdef CONFIG_SERIAL_SOFTWARE_FIFO
55 #include <malloc.h>
56 #endif
57
58 #define UART_RBR    0x00
59 #define UART_THR    0x00
60 #define UART_IER    0x01
61 #define UART_IIR    0x02
62 #define UART_FCR    0x02
63 #define UART_LCR    0x03
64 #define UART_MCR    0x04
65 #define UART_LSR    0x05
66 #define UART_MSR    0x06
67 #define UART_SCR    0x07
68 #define UART_DLL    0x00
69 #define UART_DLM    0x01
70
71 /*-----------------------------------------------------------------------------+
72   | Line Status Register.
73   +-----------------------------------------------------------------------------*/
74 #define asyncLSRDataReady1            0x01
75 #define asyncLSROverrunError1         0x02
76 #define asyncLSRParityError1          0x04
77 #define asyncLSRFramingError1         0x08
78 #define asyncLSRBreakInterrupt1       0x10
79 #define asyncLSRTxHoldEmpty1          0x20
80 #define asyncLSRTxShiftEmpty1         0x40
81 #define asyncLSRRxFifoError1          0x80
82
83
84 #ifdef CONFIG_SERIAL_SOFTWARE_FIFO
85 /*-----------------------------------------------------------------------------+
86   | Fifo
87   +-----------------------------------------------------------------------------*/
88 typedef struct {
89         char *rx_buffer;
90         ulong rx_put;
91         ulong rx_get;
92         int cts;
93 } serial_buffer_t;
94
95 volatile serial_buffer_t buf_info;
96 static int serial_buffer_active=0;
97 #endif
98
99
100 static int serial_div(int baudrate)
101 {
102
103         switch (baudrate) {
104         case 1200:
105                 return 96;
106         case 9600:
107                 return 12;
108         case 19200:
109                 return 6;
110         case 38400:
111                 return 3;
112         case 57600:
113                 return 2;
114         case 115200:
115                 return 1;
116         }
117
118         return 12;
119 }
120
121
122 /*
123  * Minimal serial functions needed to use one of the SMC ports
124  * as serial console interface.
125  */
126
127 int serial_init(void)
128 {
129         DECLARE_GLOBAL_DATA_PTR;
130
131         volatile char val;
132
133         int bdiv = serial_div(gd->baudrate);
134
135
136         outb(0x80, UART0_BASE + UART_LCR);      /* set DLAB bit */
137         outb(bdiv, UART0_BASE + UART_DLL);      /* set baudrate divisor */
138         outb(bdiv >> 8, UART0_BASE + UART_DLM);/* set baudrate divisor */
139         outb(0x03, UART0_BASE + UART_LCR);      /* clear DLAB; set 8 bits, no parity */
140         outb(0x01, UART0_BASE + UART_FCR);      /* enable FIFO */
141         outb(0x0b, UART0_BASE + UART_MCR);      /* Set DTR and RTS active */
142         val = inb(UART0_BASE + UART_LSR);       /* clear line status */
143         val = inb(UART0_BASE + UART_RBR);       /* read receive buffer */
144         outb(0x00, UART0_BASE + UART_SCR);      /* set scratchpad */
145         outb(0x00, UART0_BASE + UART_IER);      /* set interrupt enable reg */
146
147         return 0;
148 }
149
150
151 void serial_setbrg(void)
152 {
153         DECLARE_GLOBAL_DATA_PTR;
154
155         unsigned short bdiv;
156
157         bdiv = serial_div(gd->baudrate);
158
159         outb(0x80, UART0_BASE + UART_LCR);      /* set DLAB bit */
160         outb(bdiv&0xff, UART0_BASE + UART_DLL); /* set baudrate divisor */
161         outb(bdiv >> 8, UART0_BASE + UART_DLM);/* set baudrate divisor */
162         outb(0x03, UART0_BASE + UART_LCR);      /* clear DLAB; set 8 bits, no parity */
163 }
164
165
166 void serial_putc(const char c)
167 {
168         int i;
169
170         if (c == '\n')
171                 serial_putc ('\r');
172
173         /* check THRE bit, wait for transmiter available */
174         for (i = 1; i < 3500; i++) {
175                 if ((inb (UART0_BASE + UART_LSR) & 0x20) == 0x20) {
176                         break;
177                 }
178                 udelay(100);
179         }
180         outb(c, UART0_BASE + UART_THR); /* put character out */
181 }
182
183
184 void serial_puts(const char *s)
185 {
186         while (*s) {
187                 serial_putc(*s++);
188         }
189 }
190
191
192 int serial_getc(void)
193 {
194         unsigned char status = 0;
195
196 #ifdef CONFIG_SERIAL_SOFTWARE_FIFO
197         if (serial_buffer_active) {
198                 return serial_buffered_getc();
199         }
200 #endif
201
202         while (1) {
203 #if defined(CONFIG_HW_WATCHDOG)
204                 WATCHDOG_RESET();       /* Reset HW Watchdog, if needed */
205 #endif  /* CONFIG_HW_WATCHDOG */
206                 status = inb(UART0_BASE + UART_LSR);
207                 if ((status & asyncLSRDataReady1) != 0x0) {
208                         break;
209                 }
210                 if ((status & ( asyncLSRFramingError1 |
211                                 asyncLSROverrunError1 |
212                                 asyncLSRParityError1  |
213                                 asyncLSRBreakInterrupt1 )) != 0) {
214                         outb(asyncLSRFramingError1 |
215                               asyncLSROverrunError1 |
216                               asyncLSRParityError1  |
217                               asyncLSRBreakInterrupt1, UART0_BASE + UART_LSR);
218                 }
219         }
220         return (0x000000ff & (int) inb (UART0_BASE));
221 }
222
223
224 int serial_tstc(void)
225 {
226         unsigned char status;
227
228 #ifdef CONFIG_SERIAL_SOFTWARE_FIFO
229         if (serial_buffer_active) {
230                 return serial_buffered_tstc();
231         }
232 #endif
233
234         status = inb(UART0_BASE + UART_LSR);
235         if ((status & asyncLSRDataReady1) != 0x0) {
236                 return (1);
237         }
238         if ((status & ( asyncLSRFramingError1 |
239                         asyncLSROverrunError1 |
240                         asyncLSRParityError1  |
241                         asyncLSRBreakInterrupt1 )) != 0) {
242                 outb(asyncLSRFramingError1 |
243                       asyncLSROverrunError1 |
244                       asyncLSRParityError1  |
245                       asyncLSRBreakInterrupt1, UART0_BASE + UART_LSR);
246         }
247         return 0;
248 }
249
250
251 #ifdef CONFIG_SERIAL_SOFTWARE_FIFO
252
253 void serial_isr(void *arg)
254 {
255         int space;
256         int c;
257         int rx_put = buf_info.rx_put;
258
259         if (buf_info.rx_get <= rx_put) {
260                 space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - buf_info.rx_get);
261         } else {
262                 space = buf_info.rx_get - rx_put;
263         }
264
265         while (inb(UART0_BASE + UART_LSR) & 1) {
266                 c = inb(UART0_BASE);
267                 if (space) {
268                         buf_info.rx_buffer[rx_put++] = c;
269                         space--;
270
271                         if (rx_put == buf_info.rx_get) {
272                                 buf_info.rx_get++;
273                                 if (rx_put == CONFIG_SERIAL_SOFTWARE_FIFO) {
274                                         buf_info.rx_get = 0;
275                                 }
276                         }
277
278                         if (rx_put == CONFIG_SERIAL_SOFTWARE_FIFO) {
279                                 rx_put = 0;
280                                 if (0 == buf_info.rx_get) {
281                                         buf_info.rx_get = 1;
282                                 }
283
284                         }
285
286                 }
287                 if (space < CONFIG_SERIAL_SOFTWARE_FIFO / 4) {
288                         /* Stop flow by setting RTS inactive */
289                         outb(inb(UART0_BASE + UART_MCR) & (0xFF ^ 0x02),
290                               UART0_BASE + UART_MCR);
291                 }
292         }
293         buf_info.rx_put = rx_put;
294 }
295
296 void serial_buffered_init(void)
297 {
298         serial_puts ("Switching to interrupt driven serial input mode.\n");
299         buf_info.rx_buffer = malloc (CONFIG_SERIAL_SOFTWARE_FIFO);
300         buf_info.rx_put = 0;
301         buf_info.rx_get = 0;
302
303         if (inb (UART0_BASE + UART_MSR) & 0x10) {
304                 serial_puts ("Check CTS signal present on serial port: OK.\n");
305                 buf_info.cts = 1;
306         } else {
307                 serial_puts ("WARNING: CTS signal not present on serial port.\n");
308                 buf_info.cts = 0;
309         }
310
311         irq_install_handler ( VECNUM_U0 /*UART0 */ /*int vec */ ,
312                               serial_isr /*interrupt_handler_t *handler */ ,
313                               (void *) &buf_info /*void *arg */ );
314
315         /* Enable "RX Data Available" Interrupt on UART */
316         /* outb(inb(UART0_BASE + UART_IER) |0x01, UART0_BASE + UART_IER); */
317         outb(0x01, UART0_BASE + UART_IER);
318
319         /* Set DTR and RTS active, enable interrupts  */
320         outb(inb (UART0_BASE + UART_MCR) | 0x0b, UART0_BASE + UART_MCR);
321
322         /* Setup UART FIFO: RX trigger level: 1 byte, Enable FIFO */
323         outb( /*(1 << 6) |*/  1, UART0_BASE + UART_FCR);
324
325         serial_buffer_active = 1;
326 }
327
328 void serial_buffered_putc (const char c)
329 {
330         int i;
331         /* Wait for CTS */
332 #if defined(CONFIG_HW_WATCHDOG)
333         while (!(inb (UART0_BASE + UART_MSR) & 0x10))
334                 WATCHDOG_RESET ();
335 #else
336         if (buf_info.cts)  {
337                 for (i=0;i<1000;i++) {
338                         if ((inb (UART0_BASE + UART_MSR) & 0x10)) {
339                                 break;
340                         }
341                 }
342                 if (i!=1000) {
343                         buf_info.cts = 0;
344                 }
345         } else {
346                 if ((inb (UART0_BASE + UART_MSR) & 0x10)) {
347                         buf_info.cts = 1;
348                 }
349         }
350
351 #endif
352         serial_putc (c);
353 }
354
355 void serial_buffered_puts(const char *s)
356 {
357         serial_puts (s);
358 }
359
360 int serial_buffered_getc(void)
361 {
362         int space;
363         int c;
364         int rx_get = buf_info.rx_get;
365         int rx_put;
366
367 #if defined(CONFIG_HW_WATCHDOG)
368         while (rx_get == buf_info.rx_put)
369                 WATCHDOG_RESET ();
370 #else
371         while (rx_get == buf_info.rx_put);
372 #endif
373         c = buf_info.rx_buffer[rx_get++];
374         if (rx_get == CONFIG_SERIAL_SOFTWARE_FIFO) {
375                 rx_get = 0;
376         }
377         buf_info.rx_get = rx_get;
378
379         rx_put = buf_info.rx_put;
380         if (rx_get <= rx_put) {
381                 space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get);
382         } else {
383                 space = rx_get - rx_put;
384         }
385         if (space > CONFIG_SERIAL_SOFTWARE_FIFO / 2) {
386                 /* Start flow by setting RTS active */
387                 outb(inb (UART0_BASE + UART_MCR) | 0x02, UART0_BASE + UART_MCR);
388         }
389
390         return c;
391 }
392
393 int serial_buffered_tstc(void)
394 {
395         return (buf_info.rx_get != buf_info.rx_put) ? 1 : 0;
396 }
397
398 #endif  /* CONFIG_SERIAL_SOFTWARE_FIFO */
399
400
401 #if (CONFIG_COMMANDS & CFG_CMD_KGDB)
402 /*
403   AS HARNOIS : according to CONFIG_KGDB_SER_INDEX kgdb uses serial port
404   number 0 or number 1
405   - if CONFIG_KGDB_SER_INDEX = 1 => serial port number 0 :
406   configuration has been already done
407   - if CONFIG_KGDB_SER_INDEX = 2 => serial port number 1 :
408   configure port 1 for serial I/O with rate = CONFIG_KGDB_BAUDRATE
409 */
410 #if (CONFIG_KGDB_SER_INDEX & 2)
411 void kgdb_serial_init(void)
412 {
413         DECLARE_GLOBAL_DATA_PTR;
414
415         volatile char val;
416         bdiv = serial_div (CONFIG_KGDB_BAUDRATE);
417
418         /*
419          * Init onboard 16550 UART
420          */
421         outb(0x80, UART1_BASE + UART_LCR);      /* set DLAB bit */
422         outb(bdiv & 0xff), UART1_BASE + UART_DLL);      /* set divisor for 9600 baud */
423         outb(bdiv >> 8), UART1_BASE + UART_DLM);        /* set divisor for 9600 baud */
424         outb(0x03, UART1_BASE + UART_LCR);      /* line control 8 bits no parity */
425         outb(0x00, UART1_BASE + UART_FCR);      /* disable FIFO */
426         outb(0x00, UART1_BASE + UART_MCR);      /* no modem control DTR RTS */
427         val = inb(UART1_BASE + UART_LSR);       /* clear line status */
428         val = inb(UART1_BASE + UART_RBR);       /* read receive buffer */
429         outb(0x00, UART1_BASE + UART_SCR);      /* set scratchpad */
430         outb(0x00, UART1_BASE + UART_IER);      /* set interrupt enable reg */
431 }
432
433
434 void putDebugChar(const char c)
435 {
436         if (c == '\n')
437                 serial_putc ('\r');
438
439         outb(c, UART1_BASE + UART_THR); /* put character out */
440
441         /* check THRE bit, wait for transfer done */
442         while ((inb(UART1_BASE + UART_LSR) & 0x20) != 0x20);
443 }
444
445
446 void putDebugStr(const char *s)
447 {
448         while (*s) {
449                 serial_putc(*s++);
450         }
451 }
452
453
454 int getDebugChar(void)
455 {
456         unsigned char status = 0;
457
458         while (1) {
459                 status = inb(UART1_BASE + UART_LSR);
460                 if ((status & asyncLSRDataReady1) != 0x0) {
461                         break;
462                 }
463                 if ((status & ( asyncLSRFramingError1 |
464                                 asyncLSROverrunError1 |
465                                 asyncLSRParityError1  |
466                                 asyncLSRBreakInterrupt1 )) != 0) {
467                         outb(asyncLSRFramingError1 |
468                              asyncLSROverrunError1 |
469                              asyncLSRParityError1  |
470                              asyncLSRBreakInterrupt1, UART1_BASE + UART_LSR);
471                 }
472         }
473         return (0x000000ff & (int) inb(UART1_BASE));
474 }
475
476
477 void kgdb_interruptible(int yes)
478 {
479         return;
480 }
481
482 #else   /* ! (CONFIG_KGDB_SER_INDEX & 2) */
483
484 void kgdb_serial_init(void)
485 {
486         serial_printf ("[on serial] ");
487 }
488
489 void putDebugChar(int c)
490 {
491         serial_putc (c);
492 }
493
494 void putDebugStr(const char *str)
495 {
496         serial_puts (str);
497 }
498
499 int getDebugChar(void)
500 {
501         return serial_getc ();
502 }
503
504 void kgdb_interruptible(int yes)
505 {
506         return;
507 }
508 #endif  /* (CONFIG_KGDB_SER_INDEX & 2) */
509 #endif  /* CFG_CMD_KGDB */