diff options
Diffstat (limited to 'cpu/i386/serial.c')
-rw-r--r-- | cpu/i386/serial.c | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/cpu/i386/serial.c b/cpu/i386/serial.c new file mode 100644 index 000000000..c0d2e8aa1 --- /dev/null +++ b/cpu/i386/serial.c @@ -0,0 +1,460 @@ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * 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 + */ +/*------------------------------------------------------------------------------+ */ +/* + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + */ +/*------------------------------------------------------------------------------- */ + +#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <asm/ibmpc.h> + +#if CONFIG_SERIAL_SOFTWARE_FIFO +#include <malloc.h> +#endif + +#define UART_RBR 0x00 +#define UART_THR 0x00 +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_LSR 0x05 +#define UART_MSR 0x06 +#define UART_SCR 0x07 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/*-----------------------------------------------------------------------------+ + | Line Status Register. + +-----------------------------------------------------------------------------*/ +#define asyncLSRDataReady1 0x01 +#define asyncLSROverrunError1 0x02 +#define asyncLSRParityError1 0x04 +#define asyncLSRFramingError1 0x08 +#define asyncLSRBreakInterrupt1 0x10 +#define asyncLSRTxHoldEmpty1 0x20 +#define asyncLSRTxShiftEmpty1 0x40 +#define asyncLSRRxFifoError1 0x80 + + + +#if CONFIG_SERIAL_SOFTWARE_FIFO +/*-----------------------------------------------------------------------------+ + | Fifo + +-----------------------------------------------------------------------------*/ +typedef struct { + char *rx_buffer; + ulong rx_put; + ulong rx_get; +} serial_buffer_t; + +volatile static serial_buffer_t buf_info; +#endif + + +static int serial_div (int baudrate ) +{ + + switch (baudrate) { + case 1200: + return 96; + case 9600: + return 12; + case 19200: + return 6; + case 38400: + return 3; + case 57600: + return 2; + case 115200: + return 1; + } + hang (); +} + + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +int serial_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile char val; + + int bdiv = serial_div(gd->baudrate); + + + outb(0x80, UART0_BASE + UART_LCR); /* set DLAB bit */ + outb(bdiv, UART0_BASE + UART_DLL); /* set baudrate divisor */ + outb(bdiv >> 8, UART0_BASE + UART_DLM);/* set baudrate divisor */ + outb(0x03, UART0_BASE + UART_LCR); /* clear DLAB; set 8 bits, no parity */ + outb(0x00, UART0_BASE + UART_FCR); /* disable FIFO */ + outb(0x00, UART0_BASE + UART_MCR); /* no modem control DTR RTS */ + val = inb(UART0_BASE + UART_LSR); /* clear line status */ + val = inb(UART0_BASE + UART_RBR); /* read receive buffer */ + outb(0x00, UART0_BASE + UART_SCR); /* set scratchpad */ + outb(0x00, UART0_BASE + UART_IER); /* set interrupt enable reg */ + + return (0); +} + + +void serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned short bdiv; + + bdiv = serial_div (gd->baudrate); + + outb(0x80, UART0_BASE + UART_LCR); /* set DLAB bit */ + outb(bdiv&0xff, UART0_BASE + UART_DLL); /* set baudrate divisor */ + outb(bdiv >> 8, UART0_BASE + UART_DLM);/* set baudrate divisor */ + outb(0x03, UART0_BASE + UART_LCR); /* clear DLAB; set 8 bits, no parity */ +} + + +void serial_putc (const char c) +{ + int i; + + if (c == '\n') + serial_putc ('\r'); + + /* check THRE bit, wait for transmiter available */ + for (i = 1; i < 3500; i++) { + if ((inb (UART0_BASE + UART_LSR) & 0x20) == 0x20) + break; + udelay (100); + } + outb(c, UART0_BASE + UART_THR); /* put character out */ +} + + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + + +int serial_getc () +{ + unsigned char status = 0; + + while (1) { +#if defined(CONFIG_HW_WATCHDOG) + WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ +#endif /* CONFIG_HW_WATCHDOG */ + status = inb (UART0_BASE + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) { + break; + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + outb(asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1, UART0_BASE + UART_LSR); + } + } + return (0x000000ff & (int) inb (UART0_BASE)); +} + + +int serial_tstc () +{ + unsigned char status; + + status = inb (UART0_BASE + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) { + return (1); + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + outb(asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1, UART0_BASE + UART_LSR); + } + return 0; +} + + +#if CONFIG_SERIAL_SOFTWARE_FIFO + +void serial_isr (void *arg) +{ + int space; + int c; + const int rx_get = buf_info.rx_get; + int rx_put = buf_info.rx_put; + + if (rx_get <= rx_put) { + space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get); + } else { + space = rx_get - rx_put; + } + while (serial_tstc ()) { + c = serial_getc (); + if (space) { + buf_info.rx_buffer[rx_put++] = c; + space--; + } + if (rx_put == CONFIG_SERIAL_SOFTWARE_FIFO) + rx_put = 0; + if (space < CONFIG_SERIAL_SOFTWARE_FIFO / 4) { + /* Stop flow by setting RTS inactive */ + outb(inb (UART0_BASE + UART_MCR) & (0xFF ^ 0x02), + UART0_BASE + UART_MCR); + } + } + buf_info.rx_put = rx_put; +} + +void serial_buffered_init (void) +{ + serial_puts ("Switching to interrupt driven serial input mode.\n"); + buf_info.rx_buffer = malloc (CONFIG_SERIAL_SOFTWARE_FIFO); + buf_info.rx_put = 0; + buf_info.rx_get = 0; + + if (inb (UART0_BASE + UART_MSR) & 0x10) { + serial_puts ("Check CTS signal present on serial port: OK.\n"); + } else { + serial_puts ("WARNING: CTS signal not present on serial port.\n"); + } + + irq_install_handler ( VECNUM_U0 /*UART0 *//*int vec */ , + serial_isr /*interrupt_handler_t *handler */ , + (void *) &buf_info /*void *arg */ ); + + /* Enable "RX Data Available" Interrupt on UART */ + /* outb(inb(UART0_BASE + UART_IER) |0x01, UART0_BASE + UART_IER); */ + outb(0x01, UART0_BASE + UART_IER); + /* Set DTR active */ + outb(inb (UART0_BASE + UART_MCR) | 0x01, UART0_BASE + UART_MCR); + /* Start flow by setting RTS active */ + outb(inb (UART0_BASE + UART_MCR) | 0x02, UART0_BASE + UART_MCR); + /* Setup UART FIFO: RX trigger level: 4 byte, Enable FIFO */ + outb((1 << 6) | 1, UART0_BASE + UART_FCR); +} + +void serial_buffered_putc (const char c) +{ + /* Wait for CTS */ +#if defined(CONFIG_HW_WATCHDOG) + while (!(inb (UART0_BASE + UART_MSR) & 0x10)) + WATCHDOG_RESET (); +#else + while (!(inb (UART0_BASE + UART_MSR) & 0x10)); +#endif + serial_putc (c); +} + +void serial_buffered_puts (const char *s) +{ + serial_puts (s); +} + +int serial_buffered_getc (void) +{ + int space; + int c; + int rx_get = buf_info.rx_get; + int rx_put; + +#if defined(CONFIG_HW_WATCHDOG) + while (rx_get == buf_info.rx_put) + WATCHDOG_RESET (); +#else + while (rx_get == buf_info.rx_put); +#endif + c = buf_info.rx_buffer[rx_get++]; + if (rx_get == CONFIG_SERIAL_SOFTWARE_FIFO) + rx_get = 0; + buf_info.rx_get = rx_get; + + rx_put = buf_info.rx_put; + if (rx_get <= rx_put) { + space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get); + } else { + space = rx_get - rx_put; + } + if (space > CONFIG_SERIAL_SOFTWARE_FIFO / 2) { + /* Start flow by setting RTS active */ + outb(inb (UART0_BASE + UART_MCR) | 0x02, UART0_BASE + UART_MCR); + } + + return c; +} + +int serial_buffered_tstc (void) +{ + return (buf_info.rx_get != buf_info.rx_put) ? 1 : 0; +} + +#endif /* CONFIG_SERIAL_SOFTWARE_FIFO */ + + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) +/* + AS HARNOIS : according to CONFIG_KGDB_SER_INDEX kgdb uses serial port + number 0 or number 1 + - if CONFIG_KGDB_SER_INDEX = 1 => serial port number 0 : + configuration has been already done + - if CONFIG_KGDB_SER_INDEX = 2 => serial port number 1 : + configure port 1 for serial I/O with rate = CONFIG_KGDB_BAUDRATE +*/ +#if (CONFIG_KGDB_SER_INDEX & 2) +void kgdb_serial_init (void) +{ + DECLARE_GLOBAL_DATA_PTR; + + volatile char val; + bdiv = serial_div (CONFIG_KGDB_BAUDRATE); + + /* + * Init onboard 16550 UART + */ + outb(0x80, UART1_BASE + UART_LCR); /* set DLAB bit */ + outb(bdiv & 0xff), UART1_BASE + UART_DLL); /* set divisor for 9600 baud */ + outb(bdiv >> 8), UART1_BASE + UART_DLM); /* set divisor for 9600 baud */ + outb(0x03, UART1_BASE + UART_LCR); /* line control 8 bits no parity */ + outb(0x00, UART1_BASE + UART_FCR); /* disable FIFO */ + outb(0x00, UART1_BASE + UART_MCR); /* no modem control DTR RTS */ + val = inb(UART1_BASE + UART_LSR); /* clear line status */ + val = inb(UART1_BASE + UART_RBR); /* read receive buffer */ + outb(0x00, UART1_BASE + UART_SCR); /* set scratchpad */ + outb(0x00, UART1_BASE + UART_IER); /* set interrupt enable reg */ +} + + +void putDebugChar (const char c) +{ + if (c == '\n') + serial_putc ('\r'); + + outb(c, UART1_BASE + UART_THR); /* put character out */ + + /* check THRE bit, wait for transfer done */ + while ((inb(UART1_BASE + UART_LSR) & 0x20) != 0x20); +} + + +void putDebugStr (const char *s) +{ + while (*s) { + serial_putc(*s++); + } +} + + +int getDebugChar (void) +{ + unsigned char status = 0; + + while (1) { + status = inb(UART1_BASE + UART_LSR); + if ((status & asyncLSRDataReady1) != 0x0) { + break; + } + if ((status & ( asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1 )) != 0) { + outb(asyncLSRFramingError1 | + asyncLSROverrunError1 | + asyncLSRParityError1 | + asyncLSRBreakInterrupt1, UART1_BASE + UART_LSR); + } + } + return (0x000000ff & (int) inb(UART1_BASE)); +} + + +void kgdb_interruptible (int yes) +{ + return; +} + +#else /* ! (CONFIG_KGDB_SER_INDEX & 2) */ + +void kgdb_serial_init (void) +{ + serial_printf ("[on serial] "); +} + +void putDebugChar (int c) +{ + serial_putc (c); +} + +void putDebugStr (const char *str) +{ + serial_puts (str); +} + +int getDebugChar (void) +{ + return serial_getc (); +} + +void kgdb_interruptible (int yes) +{ + return; +} +#endif /* (CONFIG_KGDB_SER_INDEX & 2) */ +#endif /* CFG_CMD_KGDB */ + |