1/*********************************************************************** 2 * 3 * (C) Copyright 2004-2009 4 * DENX Software Engineering 5 * Wolfgang Denk, wd@denx.de 6 * 7 * Simple 16550A serial driver 8 * 9 * Originally from linux source (drivers/char/ps2ser.c) 10 * 11 * Used by the PS/2 multiplexer driver (ps2mult.c) 12 * 13 ***********************************************************************/ 14 15#include <common.h> 16 17#include <asm/io.h> 18#include <asm/atomic.h> 19#include <ps2mult.h> 20/* This is needed for ns16550.h */ 21#ifndef CONFIG_SYS_NS16550_REG_SIZE 22#define CONFIG_SYS_NS16550_REG_SIZE 1 23#endif 24#include <ns16550.h> 25 26DECLARE_GLOBAL_DATA_PTR; 27 28/* #define DEBUG */ 29 30#define PS2SER_BAUD 57600 31 32#if CONFIG_PS2SERIAL == 1 33#define COM_BASE (CONFIG_SYS_CCSRBAR+0x4500) 34#elif CONFIG_PS2SERIAL == 2 35#define COM_BASE (CONFIG_SYS_CCSRBAR+0x4600) 36#else 37#error CONFIG_PS2SERIAL must be in 1 ... 2 38#endif 39 40static int ps2ser_getc_hw(void); 41static void ps2ser_interrupt(void *dev_id); 42 43extern struct serial_state rs_table[]; /* in serial.c */ 44 45static u_char ps2buf[PS2BUF_SIZE]; 46static atomic_t ps2buf_cnt; 47static int ps2buf_in_idx; 48static int ps2buf_out_idx; 49 50int ps2ser_init(void) 51{ 52 NS16550_t com_port = (NS16550_t)COM_BASE; 53 54 com_port->ier = 0x00; 55 com_port->lcr = UART_LCR_BKSE | UART_LCR_8N1; 56 com_port->dll = (CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) & 0xff; 57 com_port->dlm = ((CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) >> 8) & 0xff; 58 com_port->lcr = UART_LCR_8N1; 59 com_port->mcr = (UART_MCR_DTR | UART_MCR_RTS); 60 com_port->fcr = (UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR); 61 62 return (0); 63} 64 65void ps2ser_putc(int chr) 66{ 67 NS16550_t com_port = (NS16550_t)COM_BASE; 68 debug(">>>> 0x%02x\n", chr); 69 70 while ((com_port->lsr & UART_LSR_THRE) == 0); 71 com_port->thr = chr; 72} 73 74static int ps2ser_getc_hw(void) 75{ 76 NS16550_t com_port = (NS16550_t)COM_BASE; 77 int res = -1; 78 79 if (com_port->lsr & UART_LSR_DR) { 80 res = com_port->rbr; 81 } 82 83 return res; 84} 85 86int ps2ser_getc(void) 87{ 88 volatile int chr; 89 int flags; 90 91 debug("<< "); 92 93 flags = disable_interrupts(); 94 95 do { 96 if (atomic_read(&ps2buf_cnt) != 0) { 97 chr = ps2buf[ps2buf_out_idx++]; 98 ps2buf_out_idx &= (PS2BUF_SIZE - 1); 99 atomic_dec(&ps2buf_cnt); 100 } else { 101 chr = ps2ser_getc_hw(); 102 } 103 } 104 while (chr < 0); 105 106 if (flags) 107 enable_interrupts(); 108 109 debug("0x%02x\n", chr); 110 111 return chr; 112} 113 114int ps2ser_check(void) 115{ 116 int flags; 117 118 flags = disable_interrupts(); 119 ps2ser_interrupt(NULL); 120 if (flags) enable_interrupts(); 121 122 return atomic_read(&ps2buf_cnt); 123} 124 125static void ps2ser_interrupt(void *dev_id) 126{ 127 NS16550_t com_port = (NS16550_t)COM_BASE; 128 int chr; 129 int status; 130 131 do { 132 chr = ps2ser_getc_hw(); 133 status = com_port->lsr; 134 if (chr < 0) continue; 135 136 if (atomic_read(&ps2buf_cnt) < PS2BUF_SIZE) { 137 ps2buf[ps2buf_in_idx++] = chr; 138 ps2buf_in_idx &= (PS2BUF_SIZE - 1); 139 atomic_inc(&ps2buf_cnt); 140 } else { 141 printf ("ps2ser.c: buffer overflow\n"); 142 } 143 } while (status & UART_LSR_DR); 144 if (atomic_read(&ps2buf_cnt)) { 145 ps2mult_callback(atomic_read(&ps2buf_cnt)); 146 } 147} 148