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 */ + |