]>
Commit | Line | Data |
---|---|---|
1c43771b WD |
1 | /*********************************************************************** |
2 | * | |
200779e3 | 3 | * (C) Copyright 2004-2009 |
1c43771b WD |
4 | * DENX Software Engineering |
5 | * Wolfgang Denk, wd@denx.de | |
1c43771b WD |
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 | ||
1c43771b WD |
17 | #include <asm/io.h> |
18 | #include <asm/atomic.h> | |
19 | #include <ps2mult.h> | |
200779e3 DZ |
20 | /* This is needed for ns16550.h */ |
21 | #ifndef CONFIG_SYS_NS16550_REG_SIZE | |
22 | #define CONFIG_SYS_NS16550_REG_SIZE 1 | |
bc8bb6d4 | 23 | #endif |
200779e3 | 24 | #include <ns16550.h> |
1c43771b | 25 | |
d87080b7 WD |
26 | DECLARE_GLOBAL_DATA_PTR; |
27 | ||
1c43771b WD |
28 | /* #define DEBUG */ |
29 | ||
30 | #define PS2SER_BAUD 57600 | |
31 | ||
bc8bb6d4 | 32 | #if CONFIG_PS2SERIAL == 1 |
6d0f6bcf | 33 | #define COM_BASE (CONFIG_SYS_CCSRBAR+0x4500) |
bc8bb6d4 | 34 | #elif CONFIG_PS2SERIAL == 2 |
6d0f6bcf | 35 | #define COM_BASE (CONFIG_SYS_CCSRBAR+0x4600) |
bc8bb6d4 WD |
36 | #else |
37 | #error CONFIG_PS2SERIAL must be in 1 ... 2 | |
38 | #endif | |
39 | ||
1c43771b WD |
40 | static int ps2ser_getc_hw(void); |
41 | static void ps2ser_interrupt(void *dev_id); | |
42 | ||
43 | extern struct serial_state rs_table[]; /* in serial.c */ | |
1c43771b WD |
44 | |
45 | static u_char ps2buf[PS2BUF_SIZE]; | |
46 | static atomic_t ps2buf_cnt; | |
47 | static int ps2buf_in_idx; | |
48 | static int ps2buf_out_idx; | |
49 | ||
bc8bb6d4 WD |
50 | int ps2ser_init(void) |
51 | { | |
52 | NS16550_t com_port = (NS16550_t)COM_BASE; | |
53 | ||
54 | com_port->ier = 0x00; | |
200779e3 | 55 | com_port->lcr = UART_LCR_BKSE | UART_LCR_8N1; |
6d0f6bcf JCPV |
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; | |
200779e3 DZ |
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); | |
bc8bb6d4 WD |
61 | |
62 | return (0); | |
63 | } | |
64 | ||
1c43771b WD |
65 | void ps2ser_putc(int chr) |
66 | { | |
bc8bb6d4 | 67 | NS16550_t com_port = (NS16550_t)COM_BASE; |
77efe35f | 68 | debug(">>>> 0x%02x\n", chr); |
1c43771b | 69 | |
200779e3 | 70 | while ((com_port->lsr & UART_LSR_THRE) == 0); |
bc8bb6d4 | 71 | com_port->thr = chr; |
1c43771b WD |
72 | } |
73 | ||
74 | static int ps2ser_getc_hw(void) | |
75 | { | |
bc8bb6d4 | 76 | NS16550_t com_port = (NS16550_t)COM_BASE; |
1c43771b WD |
77 | int res = -1; |
78 | ||
200779e3 | 79 | if (com_port->lsr & UART_LSR_DR) { |
bc8bb6d4 WD |
80 | res = com_port->rbr; |
81 | } | |
1c43771b WD |
82 | |
83 | return res; | |
84 | } | |
85 | ||
86 | int ps2ser_getc(void) | |
87 | { | |
88 | volatile int chr; | |
89 | int flags; | |
90 | ||
77efe35f | 91 | debug("<< "); |
1c43771b WD |
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 | ||
77efe35f WD |
106 | if (flags) |
107 | enable_interrupts(); | |
1c43771b | 108 | |
77efe35f | 109 | debug("0x%02x\n", chr); |
1c43771b WD |
110 | |
111 | return chr; | |
112 | } | |
113 | ||
114 | int 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 | ||
125 | static void ps2ser_interrupt(void *dev_id) | |
126 | { | |
bc8bb6d4 | 127 | NS16550_t com_port = (NS16550_t)COM_BASE; |
1c43771b | 128 | int chr; |
7e6bf358 | 129 | int status; |
1c43771b WD |
130 | |
131 | do { | |
132 | chr = ps2ser_getc_hw(); | |
77efe35f | 133 | status = com_port->lsr; |
1c43771b WD |
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 | } | |
77efe35f | 143 | } while (status & UART_LSR_DR); |
1c43771b WD |
144 | if (atomic_read(&ps2buf_cnt)) { |
145 | ps2mult_callback(atomic_read(&ps2buf_cnt)); | |
146 | } | |
147 | } |