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