| /* | 
 |  * (C) Copyright 2003 | 
 |  * | 
 |  * Pantelis Antoniou <panto@intracom.gr> | 
 |  * Intracom S.A. | 
 |  * | 
 |  * 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 | 
 |  */ | 
 |  | 
 | #include <common.h> | 
 | #include <watchdog.h> | 
 |  | 
 | DECLARE_GLOBAL_DATA_PTR; | 
 |  | 
 | /**************************************************************/ | 
 |  | 
 | /* convienient macros */ | 
 | #define MAX3100_SPI_RXD() (MAX3100_SPI_RXD_PORT & MAX3100_SPI_RXD_BIT) | 
 |  | 
 | #define MAX3100_SPI_TXD(x) \ | 
 | 	do { \ | 
 | 		if (x) \ | 
 | 			MAX3100_SPI_TXD_PORT |=  MAX3100_SPI_TXD_BIT; \ | 
 | 		else \ | 
 | 			MAX3100_SPI_TXD_PORT &= ~MAX3100_SPI_TXD_BIT; \ | 
 | 	} while(0) | 
 |  | 
 | #define MAX3100_SPI_CLK(x) \ | 
 | 	do { \ | 
 | 		if (x) \ | 
 | 			MAX3100_SPI_CLK_PORT |=  MAX3100_SPI_CLK_BIT; \ | 
 | 		else \ | 
 | 			MAX3100_SPI_CLK_PORT &= ~MAX3100_SPI_CLK_BIT; \ | 
 | 	} while(0) | 
 |  | 
 | #define MAX3100_SPI_CLK_TOGGLE() (MAX3100_SPI_CLK_PORT ^= MAX3100_SPI_CLK_BIT) | 
 |  | 
 | #define MAX3100_CS(x) \ | 
 | 	do { \ | 
 | 		if (x) \ | 
 | 			MAX3100_CS_PORT |=  MAX3100_CS_BIT; \ | 
 | 		else \ | 
 | 			MAX3100_CS_PORT &= ~MAX3100_CS_BIT; \ | 
 | 	} while(0) | 
 |  | 
 | /**************************************************************/ | 
 |  | 
 | /* MAX3100 definitions */ | 
 |  | 
 | #define MAX3100_WC	(3 << 14)		/* write configuration */ | 
 | #define MAX3100_RC	(1 << 14)		/* read  configuration */ | 
 | #define MAX3100_WD	(2 << 14)		/* write data          */ | 
 | #define MAX3100_RD	(0 << 14)		/* read  data          */ | 
 |  | 
 | /* configuration register bits */ | 
 | #define MAX3100_FEN	(1 << 13)		/* FIFO enable           */ | 
 | #define MAX3100_SHDN    (1 << 12)		/* shutdown bit          */ | 
 | #define MAX3100_TM	(1 << 11)		/* T bit irq mask        */ | 
 | #define MAX3100_RM	(1 << 10)		/* R bit irq mask        */ | 
 | #define MAX3100_PM	(1 <<  9)		/* P bit irq mask        */ | 
 | #define MAX3100_RAM	(1 <<  8)		/* mask for RA/FE bit    */ | 
 | #define MAX3100_IR	(1 <<  7)		/* IRDA timing mode      */ | 
 | #define MAX3100_ST	(1 <<  6)		/* transmit stop bit     */ | 
 | #define MAX3100_PE	(1 <<  5)		/* parity enable bit     */ | 
 | #define MAX3100_L	(1 <<  4)		/* Length bit            */ | 
 | #define MAX3100_B_MASK	(0x000F)		/* baud rate bits mask   */ | 
 | #define MAX3100_B(x)	((x) & 0x000F)	/* baud rate select bits */ | 
 |  | 
 | /* data register bits (write) */ | 
 | #define MAX3100_TE	(1 << 10)		/* transmit enable bit (active low)        */ | 
 | #define MAX3100_RTS	(1 <<  9)		/* request-to-send bit (inverted ~RTS pin) */ | 
 |  | 
 | /* data register bits (read) */ | 
 | #define MAX3100_RA	(1 << 10)		/* receiver activity when in shutdown mode */ | 
 | #define MAX3100_FE	(1 << 10)		/* framing error when in normal mode       */ | 
 | #define MAX3100_CTS	(1 <<  9)		/* clear-to-send bit (inverted ~CTS pin)   */ | 
 |  | 
 | /* data register bits (both directions) */ | 
 | #define MAX3100_R	(1 << 15)		/* receive bit    */ | 
 | #define MAX3100_T	(1 << 14)		/* transmit bit   */ | 
 | #define MAX3100_P	(1 <<  8)		/* parity bit     */ | 
 | #define MAX3100_D_MASK	0x00FF                  /* data bits mask */ | 
 | #define MAX3100_D(x)	((x) & 0x00FF)		/* data bits      */ | 
 |  | 
 | /* these definitions are valid only for fOSC = 3.6864MHz */ | 
 | #define MAX3100_B_230400        MAX3100_B(0) | 
 | #define MAX3100_B_115200        MAX3100_B(1) | 
 | #define MAX3100_B_57600         MAX3100_B(2) | 
 | #define MAX3100_B_38400         MAX3100_B(9) | 
 | #define MAX3100_B_19200         MAX3100_B(10) | 
 | #define MAX3100_B_9600          MAX3100_B(11) | 
 | #define MAX3100_B_4800          MAX3100_B(12) | 
 | #define MAX3100_B_2400          MAX3100_B(13) | 
 | #define MAX3100_B_1200          MAX3100_B(14) | 
 | #define MAX3100_B_600           MAX3100_B(15) | 
 |  | 
 | /**************************************************************/ | 
 |  | 
 | static inline unsigned int max3100_transfer(unsigned int val) | 
 | { | 
 | 	unsigned int rx; | 
 | 	int b; | 
 |  | 
 | 	MAX3100_SPI_CLK(0); | 
 | 	MAX3100_CS(0); | 
 |  | 
 | 	rx = 0; b = 16; | 
 | 	while (--b >= 0) { | 
 | 		MAX3100_SPI_TXD(val & 0x8000); | 
 | 		val <<= 1; | 
 | 		MAX3100_SPI_CLK_TOGGLE(); | 
 | 		udelay(1); | 
 | 		rx <<= 1; | 
 | 		if (MAX3100_SPI_RXD()) | 
 | 			rx |= 1; | 
 | 		MAX3100_SPI_CLK_TOGGLE(); | 
 | 		udelay(1); | 
 | 	} | 
 |  | 
 | 	MAX3100_SPI_CLK(1); | 
 | 	MAX3100_CS(1); | 
 |  | 
 | 	return rx; | 
 | } | 
 |  | 
 | /**************************************************************/ | 
 |  | 
 | /* must be power of 2 */ | 
 | #define RXFIFO_SZ	16 | 
 |  | 
 | static int rxfifo_cnt; | 
 | static int rxfifo_in; | 
 | static int rxfifo_out; | 
 | static unsigned char rxfifo_buf[16]; | 
 |  | 
 | static void max3100_putc(int c) | 
 | { | 
 | 	unsigned int rx; | 
 |  | 
 | 	while (((rx = max3100_transfer(MAX3100_RC)) & MAX3100_T) == 0) | 
 | 		WATCHDOG_RESET(); | 
 |  | 
 | 	rx = max3100_transfer(MAX3100_WD | (c & 0xff)); | 
 | 	if ((rx & MAX3100_RD) != 0 && rxfifo_cnt < RXFIFO_SZ) { | 
 | 		rxfifo_cnt++; | 
 | 		rxfifo_buf[rxfifo_in++] = rx & 0xff; | 
 | 		rxfifo_in &= RXFIFO_SZ - 1; | 
 | 	} | 
 | } | 
 |  | 
 | static int max3100_getc(void) | 
 | { | 
 | 	int c; | 
 | 	unsigned int rx; | 
 |  | 
 | 	while (rxfifo_cnt == 0) { | 
 | 		rx = max3100_transfer(MAX3100_RD); | 
 | 		if ((rx & MAX3100_R) != 0) { | 
 | 			do { | 
 | 				rxfifo_cnt++; | 
 | 				rxfifo_buf[rxfifo_in++] = rx & 0xff; | 
 | 				rxfifo_in &= RXFIFO_SZ - 1; | 
 |  | 
 | 				if (rxfifo_cnt >= RXFIFO_SZ) | 
 | 					break; | 
 | 			} while (((rx = max3100_transfer(MAX3100_RD)) & MAX3100_R) != 0); | 
 | 		} | 
 | 		WATCHDOG_RESET(); | 
 | 	} | 
 |  | 
 | 	rxfifo_cnt--; | 
 | 	c = rxfifo_buf[rxfifo_out++]; | 
 | 	rxfifo_out &= RXFIFO_SZ - 1; | 
 | 	return c; | 
 | } | 
 |  | 
 | static int max3100_tstc(void) | 
 | { | 
 | 	unsigned int rx; | 
 |  | 
 | 	if (rxfifo_cnt > 0) | 
 | 		return 1; | 
 |  | 
 | 	rx = max3100_transfer(MAX3100_RD); | 
 | 	if ((rx & MAX3100_R) == 0) | 
 | 		return 0; | 
 |  | 
 | 	do { | 
 | 		rxfifo_cnt++; | 
 | 		rxfifo_buf[rxfifo_in++] = rx & 0xff; | 
 | 		rxfifo_in &= RXFIFO_SZ - 1; | 
 |  | 
 | 		if (rxfifo_cnt >= RXFIFO_SZ) | 
 | 			break; | 
 | 	} while (((rx = max3100_transfer(MAX3100_RD)) & MAX3100_R) != 0); | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | int serial_init(void) | 
 | { | 
 | 	unsigned int wconf, rconf; | 
 | 	int i; | 
 |  | 
 | 	wconf = 0; | 
 |  | 
 | 	/* Set baud rate */ | 
 | 	switch (gd->baudrate) { | 
 | 		case 1200: | 
 | 			wconf = MAX3100_B_1200; | 
 | 			break; | 
 | 		case 2400: | 
 | 			wconf = MAX3100_B_2400; | 
 | 			break; | 
 | 		case 4800: | 
 | 			wconf = MAX3100_B_4800; | 
 | 			break; | 
 | 		case 9600: | 
 | 			wconf = MAX3100_B_9600; | 
 | 			break; | 
 | 		case 19200: | 
 | 			wconf = MAX3100_B_19200; | 
 | 			break; | 
 | 		case 38400: | 
 | 			wconf = MAX3100_B_38400; | 
 | 			break; | 
 | 		case 57600: | 
 | 			wconf = MAX3100_B_57600; | 
 | 			break; | 
 | 		default: | 
 | 		case 115200: | 
 | 			wconf = MAX3100_B_115200; | 
 | 			break; | 
 | 		case 230400: | 
 | 			wconf = MAX3100_B_230400; | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	/* try for 10ms, with a 100us gap */ | 
 | 	for (i = 0; i < 10000; i += 100) { | 
 |  | 
 | 		max3100_transfer(MAX3100_WC | wconf); | 
 | 		rconf = max3100_transfer(MAX3100_RC) & 0x3fff; | 
 |  | 
 | 		if (rconf == wconf) | 
 | 			break; | 
 | 		udelay(100); | 
 | 	} | 
 |  | 
 | 	rxfifo_in = rxfifo_out = rxfifo_cnt = 0; | 
 |  | 
 | 	return (0); | 
 | } | 
 |  | 
 | void serial_putc(const char c) | 
 | { | 
 | 	if (c == '\n') | 
 | 		max3100_putc('\r'); | 
 |  | 
 | 	max3100_putc(c); | 
 | } | 
 |  | 
 | void serial_puts(const char *s) | 
 | { | 
 | 	while (*s) | 
 | 		serial_putc (*s++); | 
 | } | 
 |  | 
 | int serial_getc(void) | 
 | { | 
 | 	return max3100_getc(); | 
 | } | 
 |  | 
 | int serial_tstc(void) | 
 | { | 
 | 	return max3100_tstc(); | 
 | } | 
 |  | 
 | /* XXX WTF? */ | 
 | void serial_setbrg(void) | 
 | { | 
 | } |