1 /*********************************************************************** 2 * 3 * (C) Copyright 2004 4 * DENX Software Engineering 5 * Wolfgang Denk, wd@denx.de 6 * All rights reserved. 7 * 8 * Simple 16550A serial driver 9 * 10 * Originally from linux source (drivers/char/ps2ser.c) 11 * 12 * Used by the PS/2 multiplexer driver (ps2mult.c) 13 * 14 ***********************************************************************/ 15 16 #include <common.h> 17 18 #ifdef CONFIG_PS2SERIAL 19 20 #include <asm/io.h> 21 #include <asm/atomic.h> 22 #include <ps2mult.h> 23 #if defined(CFG_NS16550) || defined(CONFIG_MPC85xx) 24 #include <ns16550.h> 25 #endif 26 27 DECLARE_GLOBAL_DATA_PTR; 28 29 /* #define DEBUG */ 30 31 #define PS2SER_BAUD 57600 32 33 #ifdef CONFIG_MPC5xxx 34 #if CONFIG_PS2SERIAL == 1 35 #define PSC_BASE MPC5XXX_PSC1 36 #elif CONFIG_PS2SERIAL == 2 37 #define PSC_BASE MPC5XXX_PSC2 38 #elif CONFIG_PS2SERIAL == 3 39 #define PSC_BASE MPC5XXX_PSC3 40 #elif defined(CONFIG_MGT5100) 41 #error CONFIG_PS2SERIAL must be in 1, 2 or 3 42 #elif CONFIG_PS2SERIAL == 4 43 #define PSC_BASE MPC5XXX_PSC4 44 #elif CONFIG_PS2SERIAL == 5 45 #define PSC_BASE MPC5XXX_PSC5 46 #elif CONFIG_PS2SERIAL == 6 47 #define PSC_BASE MPC5XXX_PSC6 48 #else 49 #error CONFIG_PS2SERIAL must be in 1 ... 6 50 #endif 51 52 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 53 54 #if CONFIG_PS2SERIAL == 1 55 #define COM_BASE (CFG_CCSRBAR+0x4500) 56 #elif CONFIG_PS2SERIAL == 2 57 #define COM_BASE (CFG_CCSRBAR+0x4600) 58 #else 59 #error CONFIG_PS2SERIAL must be in 1 ... 2 60 #endif 61 62 #endif /* CONFIG_MPC5xxx / CONFIG_MPC8540 / other */ 63 64 static int ps2ser_getc_hw(void); 65 static void ps2ser_interrupt(void *dev_id); 66 67 extern struct serial_state rs_table[]; /* in serial.c */ 68 #if !defined(CONFIG_MPC5xxx) && !defined(CONFIG_MPC8540) && !defined(CONFIG_MPC8541) && !defined(CONFIG_MPC8555) 69 static struct serial_state *state; 70 #endif 71 72 static u_char ps2buf[PS2BUF_SIZE]; 73 static atomic_t ps2buf_cnt; 74 static int ps2buf_in_idx; 75 static int ps2buf_out_idx; 76 77 #ifdef CONFIG_MPC5xxx 78 int ps2ser_init(void) 79 { 80 volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; 81 unsigned long baseclk; 82 int div; 83 84 /* reset PSC */ 85 psc->command = PSC_SEL_MODE_REG_1; 86 87 /* select clock sources */ 88 #if defined(CONFIG_MGT5100) 89 psc->psc_clock_select = 0xdd00; 90 baseclk = (CFG_MPC5XXX_CLKIN + 16) / 32; 91 #elif defined(CONFIG_MPC5200) 92 psc->psc_clock_select = 0; 93 baseclk = (gd->ipb_clk + 16) / 32; 94 #endif 95 96 /* switch to UART mode */ 97 psc->sicr = 0; 98 99 /* configure parity, bit length and so on */ 100 #if defined(CONFIG_MGT5100) 101 psc->mode = PSC_MODE_ERR | PSC_MODE_8_BITS | PSC_MODE_PARNONE; 102 #elif defined(CONFIG_MPC5200) 103 psc->mode = PSC_MODE_8_BITS | PSC_MODE_PARNONE; 104 #endif 105 psc->mode = PSC_MODE_ONE_STOP; 106 107 /* set up UART divisor */ 108 div = (baseclk + (PS2SER_BAUD/2)) / PS2SER_BAUD; 109 psc->ctur = (div >> 8) & 0xff; 110 psc->ctlr = div & 0xff; 111 112 /* disable all interrupts */ 113 psc->psc_imr = 0; 114 115 /* reset and enable Rx/Tx */ 116 psc->command = PSC_RST_RX; 117 psc->command = PSC_RST_TX; 118 psc->command = PSC_RX_ENABLE | PSC_TX_ENABLE; 119 120 return (0); 121 } 122 123 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 124 int ps2ser_init(void) 125 { 126 NS16550_t com_port = (NS16550_t)COM_BASE; 127 128 com_port->ier = 0x00; 129 com_port->lcr = LCR_BKSE | LCR_8N1; 130 com_port->dll = (CFG_NS16550_CLK / 16 / PS2SER_BAUD) & 0xff; 131 com_port->dlm = ((CFG_NS16550_CLK / 16 / PS2SER_BAUD) >> 8) & 0xff; 132 com_port->lcr = LCR_8N1; 133 com_port->mcr = (MCR_DTR | MCR_RTS); 134 com_port->fcr = (FCR_FIFO_EN | FCR_RXSR | FCR_TXSR); 135 136 return (0); 137 } 138 139 #else /* !CONFIG_MPC5xxx && !CONFIG_MPC8540 / other */ 140 141 static inline unsigned int ps2ser_in(int offset) 142 { 143 return readb((unsigned long) state->iomem_base + offset); 144 } 145 146 static inline void ps2ser_out(int offset, int value) 147 { 148 writeb(value, (unsigned long) state->iomem_base + offset); 149 } 150 151 int ps2ser_init(void) 152 { 153 int quot; 154 unsigned cval; 155 156 state = rs_table + CONFIG_PS2SERIAL; 157 158 quot = state->baud_base / PS2SER_BAUD; 159 cval = 0x3; /* 8N1 - 8 data bits, no parity bits, 1 stop bit */ 160 161 /* Set speed, enable interrupts, enable FIFO 162 */ 163 ps2ser_out(UART_LCR, cval | UART_LCR_DLAB); 164 ps2ser_out(UART_DLL, quot & 0xff); 165 ps2ser_out(UART_DLM, quot >> 8); 166 ps2ser_out(UART_LCR, cval); 167 ps2ser_out(UART_IER, UART_IER_RDI); 168 ps2ser_out(UART_MCR, UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS); 169 ps2ser_out(UART_FCR, 170 UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); 171 172 /* If we read 0xff from the LSR, there is no UART here 173 */ 174 if (ps2ser_in(UART_LSR) == 0xff) { 175 printf ("ps2ser.c: no UART found\n"); 176 return -1; 177 } 178 179 irq_install_handler(state->irq, ps2ser_interrupt, NULL); 180 181 return 0; 182 } 183 #endif /* CONFIG_MPC5xxx / CONFIG_MPC8540 / other */ 184 185 void ps2ser_putc(int chr) 186 { 187 #ifdef CONFIG_MPC5xxx 188 volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; 189 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 190 NS16550_t com_port = (NS16550_t)COM_BASE; 191 #endif 192 #ifdef DEBUG 193 printf(">>>> 0x%02x\n", chr); 194 #endif 195 196 #ifdef CONFIG_MPC5xxx 197 while (!(psc->psc_status & PSC_SR_TXRDY)); 198 199 psc->psc_buffer_8 = chr; 200 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 201 while ((com_port->lsr & LSR_THRE) == 0); 202 com_port->thr = chr; 203 #else 204 while (!(ps2ser_in(UART_LSR) & UART_LSR_THRE)); 205 206 ps2ser_out(UART_TX, chr); 207 #endif 208 } 209 210 static int ps2ser_getc_hw(void) 211 { 212 #ifdef CONFIG_MPC5xxx 213 volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; 214 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 215 NS16550_t com_port = (NS16550_t)COM_BASE; 216 #endif 217 int res = -1; 218 219 #ifdef CONFIG_MPC5xxx 220 if (psc->psc_status & PSC_SR_RXRDY) { 221 res = (psc->psc_buffer_8); 222 } 223 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 224 if (com_port->lsr & LSR_DR) { 225 res = com_port->rbr; 226 } 227 #else 228 if (ps2ser_in(UART_LSR) & UART_LSR_DR) { 229 res = (ps2ser_in(UART_RX)); 230 } 231 #endif 232 233 return res; 234 } 235 236 int ps2ser_getc(void) 237 { 238 volatile int chr; 239 int flags; 240 241 #ifdef DEBUG 242 printf("<< "); 243 #endif 244 245 flags = disable_interrupts(); 246 247 do { 248 if (atomic_read(&ps2buf_cnt) != 0) { 249 chr = ps2buf[ps2buf_out_idx++]; 250 ps2buf_out_idx &= (PS2BUF_SIZE - 1); 251 atomic_dec(&ps2buf_cnt); 252 } else { 253 chr = ps2ser_getc_hw(); 254 } 255 } 256 while (chr < 0); 257 258 if (flags) enable_interrupts(); 259 260 #ifdef DEBUG 261 printf("0x%02x\n", chr); 262 #endif 263 264 return chr; 265 } 266 267 int ps2ser_check(void) 268 { 269 int flags; 270 271 flags = disable_interrupts(); 272 ps2ser_interrupt(NULL); 273 if (flags) enable_interrupts(); 274 275 return atomic_read(&ps2buf_cnt); 276 } 277 278 static void ps2ser_interrupt(void *dev_id) 279 { 280 #ifdef CONFIG_MPC5xxx 281 volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; 282 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 283 NS16550_t com_port = (NS16550_t)COM_BASE; 284 #endif 285 int chr; 286 int status; 287 288 do { 289 chr = ps2ser_getc_hw(); 290 #ifdef CONFIG_MPC5xxx 291 status = psc->psc_status; 292 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 293 status = com_port->lsr; 294 #else 295 status = ps2ser_in(UART_IIR); 296 #endif 297 if (chr < 0) continue; 298 299 if (atomic_read(&ps2buf_cnt) < PS2BUF_SIZE) { 300 ps2buf[ps2buf_in_idx++] = chr; 301 ps2buf_in_idx &= (PS2BUF_SIZE - 1); 302 atomic_inc(&ps2buf_cnt); 303 } else { 304 printf ("ps2ser.c: buffer overflow\n"); 305 } 306 #ifdef CONFIG_MPC5xxx 307 } while (status & PSC_SR_RXRDY); 308 #elif defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555) 309 } while (status & LSR_DR); 310 #else 311 } while (status & UART_IIR_RDI); 312 #endif 313 314 if (atomic_read(&ps2buf_cnt)) { 315 ps2mult_callback(atomic_read(&ps2buf_cnt)); 316 } 317 } 318 319 #endif /* CONFIG_PS2SERIAL */ 320