|  | /* | 
|  | * (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) | 
|  | { | 
|  | } |