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