Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | /*********************************************************************** * * (C) Copyright 2004 * DENX Software Engineering * Wolfgang Denk, wd@denx.de * All rights reserved. * * Simple 16550A serial driver * * Originally from linux source (drivers/char/ps2ser.c) * * Used by the PS/2 multiplexer driver (ps2mult.c) * ***********************************************************************/ #include <common.h> #ifdef CONFIG_PS2SERIAL #include <asm/io.h> #include <asm/atomic.h> #include <ps2mult.h> /* #define DEBUG */ #define PS2SER_BAUD 57600 static int ps2ser_getc_hw(void); static void ps2ser_interrupt(void *dev_id); extern struct serial_state rs_table[]; /* in serial.c */ static struct serial_state *state; static u_char ps2buf[PS2BUF_SIZE]; static atomic_t ps2buf_cnt; static int ps2buf_in_idx; static int ps2buf_out_idx; static inline unsigned int ps2ser_in(int offset) { return readb((unsigned long) state->iomem_base + offset); } static inline void ps2ser_out(int offset, int value) { writeb(value, (unsigned long) state->iomem_base + offset); } int ps2ser_init(void) { int quot; unsigned cval; state = rs_table + CONFIG_PS2SERIAL; quot = state->baud_base / PS2SER_BAUD; cval = 0x3; /* 8N1 - 8 data bits, no parity bits, 1 stop bit */ /* Set speed, enable interrupts, enable FIFO */ ps2ser_out(UART_LCR, cval | UART_LCR_DLAB); ps2ser_out(UART_DLL, quot & 0xff); ps2ser_out(UART_DLM, quot >> 8); ps2ser_out(UART_LCR, cval); ps2ser_out(UART_IER, UART_IER_RDI); ps2ser_out(UART_MCR, UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS); ps2ser_out(UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); /* If we read 0xff from the LSR, there is no UART here */ if (ps2ser_in(UART_LSR) == 0xff) { printf ("ps2ser.c: no UART found\n"); return -1; } irq_install_handler(state->irq, ps2ser_interrupt, NULL); return 0; } void ps2ser_putc(int chr) { #ifdef DEBUG printf(">>>> 0x%02x\n", chr); #endif while (!(ps2ser_in(UART_LSR) & UART_LSR_THRE)); ps2ser_out(UART_TX, chr); } static int ps2ser_getc_hw(void) { int res = -1; if (ps2ser_in(UART_LSR) & UART_LSR_DR) { res = (ps2ser_in(UART_RX)); } return res; } int ps2ser_getc(void) { volatile int chr; int flags; #ifdef DEBUG printf("<< "); #endif flags = disable_interrupts(); do { if (atomic_read(&ps2buf_cnt) != 0) { chr = ps2buf[ps2buf_out_idx++]; ps2buf_out_idx &= (PS2BUF_SIZE - 1); atomic_dec(&ps2buf_cnt); } else { chr = ps2ser_getc_hw(); } } while (chr < 0); if (flags) enable_interrupts(); #ifdef DEBUG printf("0x%02x\n", chr); #endif return chr; } int ps2ser_check(void) { int flags; flags = disable_interrupts(); ps2ser_interrupt(NULL); if (flags) enable_interrupts(); return atomic_read(&ps2buf_cnt); } static void ps2ser_interrupt(void *dev_id) { int chr; int iir; do { chr = ps2ser_getc_hw(); iir = ps2ser_in(UART_IIR); if (chr < 0) continue; if (atomic_read(&ps2buf_cnt) < PS2BUF_SIZE) { ps2buf[ps2buf_in_idx++] = chr; ps2buf_in_idx &= (PS2BUF_SIZE - 1); atomic_inc(&ps2buf_cnt); } else { printf ("ps2ser.c: buffer overflow\n"); } } while (iir & UART_IIR_RDI); if (atomic_read(&ps2buf_cnt)) { ps2mult_callback(atomic_read(&ps2buf_cnt)); } } #endif /* CONFIG_PS2SERIAL */ |