116b195c8SJean-Christophe PLAGNIOL-VILLARD /*********************************************************************** 216b195c8SJean-Christophe PLAGNIOL-VILLARD * 3200779e3SDetlev Zundel * (C) Copyright 2004-2009 416b195c8SJean-Christophe PLAGNIOL-VILLARD * DENX Software Engineering 516b195c8SJean-Christophe PLAGNIOL-VILLARD * Wolfgang Denk, wd@denx.de 616b195c8SJean-Christophe PLAGNIOL-VILLARD * 716b195c8SJean-Christophe PLAGNIOL-VILLARD * Simple 16550A serial driver 816b195c8SJean-Christophe PLAGNIOL-VILLARD * 916b195c8SJean-Christophe PLAGNIOL-VILLARD * Originally from linux source (drivers/char/ps2ser.c) 1016b195c8SJean-Christophe PLAGNIOL-VILLARD * 1116b195c8SJean-Christophe PLAGNIOL-VILLARD * Used by the PS/2 multiplexer driver (ps2mult.c) 1216b195c8SJean-Christophe PLAGNIOL-VILLARD * 1316b195c8SJean-Christophe PLAGNIOL-VILLARD ***********************************************************************/ 1416b195c8SJean-Christophe PLAGNIOL-VILLARD 1516b195c8SJean-Christophe PLAGNIOL-VILLARD #include <common.h> 1616b195c8SJean-Christophe PLAGNIOL-VILLARD 1716b195c8SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h> 1816b195c8SJean-Christophe PLAGNIOL-VILLARD #include <asm/atomic.h> 1916b195c8SJean-Christophe PLAGNIOL-VILLARD #include <ps2mult.h> 20200779e3SDetlev Zundel /* This is needed for ns16550.h */ 21200779e3SDetlev Zundel #ifndef CONFIG_SYS_NS16550_REG_SIZE 22200779e3SDetlev Zundel #define CONFIG_SYS_NS16550_REG_SIZE 1 2316b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 24200779e3SDetlev Zundel #include <ns16550.h> 2516b195c8SJean-Christophe PLAGNIOL-VILLARD 2616b195c8SJean-Christophe PLAGNIOL-VILLARD DECLARE_GLOBAL_DATA_PTR; 2716b195c8SJean-Christophe PLAGNIOL-VILLARD 2816b195c8SJean-Christophe PLAGNIOL-VILLARD /* #define DEBUG */ 2916b195c8SJean-Christophe PLAGNIOL-VILLARD 3016b195c8SJean-Christophe PLAGNIOL-VILLARD #define PS2SER_BAUD 57600 3116b195c8SJean-Christophe PLAGNIOL-VILLARD 3216b195c8SJean-Christophe PLAGNIOL-VILLARD #if CONFIG_PS2SERIAL == 1 336d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #define COM_BASE (CONFIG_SYS_CCSRBAR+0x4500) 3416b195c8SJean-Christophe PLAGNIOL-VILLARD #elif CONFIG_PS2SERIAL == 2 356d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #define COM_BASE (CONFIG_SYS_CCSRBAR+0x4600) 3616b195c8SJean-Christophe PLAGNIOL-VILLARD #else 3716b195c8SJean-Christophe PLAGNIOL-VILLARD #error CONFIG_PS2SERIAL must be in 1 ... 2 3816b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 3916b195c8SJean-Christophe PLAGNIOL-VILLARD 4016b195c8SJean-Christophe PLAGNIOL-VILLARD static int ps2ser_getc_hw(void); 4116b195c8SJean-Christophe PLAGNIOL-VILLARD static void ps2ser_interrupt(void *dev_id); 4216b195c8SJean-Christophe PLAGNIOL-VILLARD 4316b195c8SJean-Christophe PLAGNIOL-VILLARD extern struct serial_state rs_table[]; /* in serial.c */ 4416b195c8SJean-Christophe PLAGNIOL-VILLARD 4516b195c8SJean-Christophe PLAGNIOL-VILLARD static u_char ps2buf[PS2BUF_SIZE]; 4616b195c8SJean-Christophe PLAGNIOL-VILLARD static atomic_t ps2buf_cnt; 4716b195c8SJean-Christophe PLAGNIOL-VILLARD static int ps2buf_in_idx; 4816b195c8SJean-Christophe PLAGNIOL-VILLARD static int ps2buf_out_idx; 4916b195c8SJean-Christophe PLAGNIOL-VILLARD ps2ser_init(void)5016b195c8SJean-Christophe PLAGNIOL-VILLARDint ps2ser_init(void) 5116b195c8SJean-Christophe PLAGNIOL-VILLARD { 5216b195c8SJean-Christophe PLAGNIOL-VILLARD NS16550_t com_port = (NS16550_t)COM_BASE; 5316b195c8SJean-Christophe PLAGNIOL-VILLARD 5416b195c8SJean-Christophe PLAGNIOL-VILLARD com_port->ier = 0x00; 55200779e3SDetlev Zundel com_port->lcr = UART_LCR_BKSE | UART_LCR_8N1; 566d0f6bcfSJean-Christophe PLAGNIOL-VILLARD com_port->dll = (CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) & 0xff; 576d0f6bcfSJean-Christophe PLAGNIOL-VILLARD com_port->dlm = ((CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) >> 8) & 0xff; 58200779e3SDetlev Zundel com_port->lcr = UART_LCR_8N1; 59200779e3SDetlev Zundel com_port->mcr = (UART_MCR_DTR | UART_MCR_RTS); 60200779e3SDetlev Zundel com_port->fcr = (UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR); 6116b195c8SJean-Christophe PLAGNIOL-VILLARD 6216b195c8SJean-Christophe PLAGNIOL-VILLARD return (0); 6316b195c8SJean-Christophe PLAGNIOL-VILLARD } 6416b195c8SJean-Christophe PLAGNIOL-VILLARD ps2ser_putc(int chr)6516b195c8SJean-Christophe PLAGNIOL-VILLARDvoid ps2ser_putc(int chr) 6616b195c8SJean-Christophe PLAGNIOL-VILLARD { 6716b195c8SJean-Christophe PLAGNIOL-VILLARD NS16550_t com_port = (NS16550_t)COM_BASE; 68*77efe35fSWolfgang Denk debug(">>>> 0x%02x\n", chr); 6916b195c8SJean-Christophe PLAGNIOL-VILLARD 70200779e3SDetlev Zundel while ((com_port->lsr & UART_LSR_THRE) == 0); 7116b195c8SJean-Christophe PLAGNIOL-VILLARD com_port->thr = chr; 7216b195c8SJean-Christophe PLAGNIOL-VILLARD } 7316b195c8SJean-Christophe PLAGNIOL-VILLARD ps2ser_getc_hw(void)7416b195c8SJean-Christophe PLAGNIOL-VILLARDstatic int ps2ser_getc_hw(void) 7516b195c8SJean-Christophe PLAGNIOL-VILLARD { 7616b195c8SJean-Christophe PLAGNIOL-VILLARD NS16550_t com_port = (NS16550_t)COM_BASE; 7716b195c8SJean-Christophe PLAGNIOL-VILLARD int res = -1; 7816b195c8SJean-Christophe PLAGNIOL-VILLARD 79200779e3SDetlev Zundel if (com_port->lsr & UART_LSR_DR) { 8016b195c8SJean-Christophe PLAGNIOL-VILLARD res = com_port->rbr; 8116b195c8SJean-Christophe PLAGNIOL-VILLARD } 8216b195c8SJean-Christophe PLAGNIOL-VILLARD 8316b195c8SJean-Christophe PLAGNIOL-VILLARD return res; 8416b195c8SJean-Christophe PLAGNIOL-VILLARD } 8516b195c8SJean-Christophe PLAGNIOL-VILLARD ps2ser_getc(void)8616b195c8SJean-Christophe PLAGNIOL-VILLARDint ps2ser_getc(void) 8716b195c8SJean-Christophe PLAGNIOL-VILLARD { 8816b195c8SJean-Christophe PLAGNIOL-VILLARD volatile int chr; 8916b195c8SJean-Christophe PLAGNIOL-VILLARD int flags; 9016b195c8SJean-Christophe PLAGNIOL-VILLARD 91*77efe35fSWolfgang Denk debug("<< "); 9216b195c8SJean-Christophe PLAGNIOL-VILLARD 9316b195c8SJean-Christophe PLAGNIOL-VILLARD flags = disable_interrupts(); 9416b195c8SJean-Christophe PLAGNIOL-VILLARD 9516b195c8SJean-Christophe PLAGNIOL-VILLARD do { 9616b195c8SJean-Christophe PLAGNIOL-VILLARD if (atomic_read(&ps2buf_cnt) != 0) { 9716b195c8SJean-Christophe PLAGNIOL-VILLARD chr = ps2buf[ps2buf_out_idx++]; 9816b195c8SJean-Christophe PLAGNIOL-VILLARD ps2buf_out_idx &= (PS2BUF_SIZE - 1); 9916b195c8SJean-Christophe PLAGNIOL-VILLARD atomic_dec(&ps2buf_cnt); 10016b195c8SJean-Christophe PLAGNIOL-VILLARD } else { 10116b195c8SJean-Christophe PLAGNIOL-VILLARD chr = ps2ser_getc_hw(); 10216b195c8SJean-Christophe PLAGNIOL-VILLARD } 10316b195c8SJean-Christophe PLAGNIOL-VILLARD } 10416b195c8SJean-Christophe PLAGNIOL-VILLARD while (chr < 0); 10516b195c8SJean-Christophe PLAGNIOL-VILLARD 106*77efe35fSWolfgang Denk if (flags) 107*77efe35fSWolfgang Denk enable_interrupts(); 10816b195c8SJean-Christophe PLAGNIOL-VILLARD 109*77efe35fSWolfgang Denk debug("0x%02x\n", chr); 11016b195c8SJean-Christophe PLAGNIOL-VILLARD 11116b195c8SJean-Christophe PLAGNIOL-VILLARD return chr; 11216b195c8SJean-Christophe PLAGNIOL-VILLARD } 11316b195c8SJean-Christophe PLAGNIOL-VILLARD ps2ser_check(void)11416b195c8SJean-Christophe PLAGNIOL-VILLARDint ps2ser_check(void) 11516b195c8SJean-Christophe PLAGNIOL-VILLARD { 11616b195c8SJean-Christophe PLAGNIOL-VILLARD int flags; 11716b195c8SJean-Christophe PLAGNIOL-VILLARD 11816b195c8SJean-Christophe PLAGNIOL-VILLARD flags = disable_interrupts(); 11916b195c8SJean-Christophe PLAGNIOL-VILLARD ps2ser_interrupt(NULL); 12016b195c8SJean-Christophe PLAGNIOL-VILLARD if (flags) enable_interrupts(); 12116b195c8SJean-Christophe PLAGNIOL-VILLARD 12216b195c8SJean-Christophe PLAGNIOL-VILLARD return atomic_read(&ps2buf_cnt); 12316b195c8SJean-Christophe PLAGNIOL-VILLARD } 12416b195c8SJean-Christophe PLAGNIOL-VILLARD ps2ser_interrupt(void * dev_id)12516b195c8SJean-Christophe PLAGNIOL-VILLARDstatic void ps2ser_interrupt(void *dev_id) 12616b195c8SJean-Christophe PLAGNIOL-VILLARD { 12716b195c8SJean-Christophe PLAGNIOL-VILLARD NS16550_t com_port = (NS16550_t)COM_BASE; 12816b195c8SJean-Christophe PLAGNIOL-VILLARD int chr; 12916b195c8SJean-Christophe PLAGNIOL-VILLARD int status; 13016b195c8SJean-Christophe PLAGNIOL-VILLARD 13116b195c8SJean-Christophe PLAGNIOL-VILLARD do { 13216b195c8SJean-Christophe PLAGNIOL-VILLARD chr = ps2ser_getc_hw(); 133*77efe35fSWolfgang Denk status = com_port->lsr; 13416b195c8SJean-Christophe PLAGNIOL-VILLARD if (chr < 0) continue; 13516b195c8SJean-Christophe PLAGNIOL-VILLARD 13616b195c8SJean-Christophe PLAGNIOL-VILLARD if (atomic_read(&ps2buf_cnt) < PS2BUF_SIZE) { 13716b195c8SJean-Christophe PLAGNIOL-VILLARD ps2buf[ps2buf_in_idx++] = chr; 13816b195c8SJean-Christophe PLAGNIOL-VILLARD ps2buf_in_idx &= (PS2BUF_SIZE - 1); 13916b195c8SJean-Christophe PLAGNIOL-VILLARD atomic_inc(&ps2buf_cnt); 14016b195c8SJean-Christophe PLAGNIOL-VILLARD } else { 14116b195c8SJean-Christophe PLAGNIOL-VILLARD printf ("ps2ser.c: buffer overflow\n"); 14216b195c8SJean-Christophe PLAGNIOL-VILLARD } 143*77efe35fSWolfgang Denk } while (status & UART_LSR_DR); 14416b195c8SJean-Christophe PLAGNIOL-VILLARD if (atomic_read(&ps2buf_cnt)) { 14516b195c8SJean-Christophe PLAGNIOL-VILLARD ps2mult_callback(atomic_read(&ps2buf_cnt)); 14616b195c8SJean-Christophe PLAGNIOL-VILLARD } 14716b195c8SJean-Christophe PLAGNIOL-VILLARD } 148