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 #ifdef CONFIG_MPC5xxx 3316b195c8SJean-Christophe PLAGNIOL-VILLARD #if CONFIG_PS2SERIAL == 1 3416b195c8SJean-Christophe PLAGNIOL-VILLARD #define PSC_BASE MPC5XXX_PSC1 3516b195c8SJean-Christophe PLAGNIOL-VILLARD #elif CONFIG_PS2SERIAL == 2 3616b195c8SJean-Christophe PLAGNIOL-VILLARD #define PSC_BASE MPC5XXX_PSC2 3716b195c8SJean-Christophe PLAGNIOL-VILLARD #elif CONFIG_PS2SERIAL == 3 3816b195c8SJean-Christophe PLAGNIOL-VILLARD #define PSC_BASE MPC5XXX_PSC3 3916b195c8SJean-Christophe PLAGNIOL-VILLARD #elif CONFIG_PS2SERIAL == 4 4016b195c8SJean-Christophe PLAGNIOL-VILLARD #define PSC_BASE MPC5XXX_PSC4 4116b195c8SJean-Christophe PLAGNIOL-VILLARD #elif CONFIG_PS2SERIAL == 5 4216b195c8SJean-Christophe PLAGNIOL-VILLARD #define PSC_BASE MPC5XXX_PSC5 4316b195c8SJean-Christophe PLAGNIOL-VILLARD #elif CONFIG_PS2SERIAL == 6 4416b195c8SJean-Christophe PLAGNIOL-VILLARD #define PSC_BASE MPC5XXX_PSC6 4516b195c8SJean-Christophe PLAGNIOL-VILLARD #else 4616b195c8SJean-Christophe PLAGNIOL-VILLARD #error CONFIG_PS2SERIAL must be in 1 ... 6 4716b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 4816b195c8SJean-Christophe PLAGNIOL-VILLARD 49*77efe35fSWolfgang Denk #else 5016b195c8SJean-Christophe PLAGNIOL-VILLARD 5116b195c8SJean-Christophe PLAGNIOL-VILLARD #if CONFIG_PS2SERIAL == 1 526d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #define COM_BASE (CONFIG_SYS_CCSRBAR+0x4500) 5316b195c8SJean-Christophe PLAGNIOL-VILLARD #elif CONFIG_PS2SERIAL == 2 546d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #define COM_BASE (CONFIG_SYS_CCSRBAR+0x4600) 5516b195c8SJean-Christophe PLAGNIOL-VILLARD #else 5616b195c8SJean-Christophe PLAGNIOL-VILLARD #error CONFIG_PS2SERIAL must be in 1 ... 2 5716b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 5816b195c8SJean-Christophe PLAGNIOL-VILLARD 59*77efe35fSWolfgang Denk #endif /* CONFIG_MPC5xxx / other */ 6016b195c8SJean-Christophe PLAGNIOL-VILLARD 6116b195c8SJean-Christophe PLAGNIOL-VILLARD static int ps2ser_getc_hw(void); 6216b195c8SJean-Christophe PLAGNIOL-VILLARD static void ps2ser_interrupt(void *dev_id); 6316b195c8SJean-Christophe PLAGNIOL-VILLARD 6416b195c8SJean-Christophe PLAGNIOL-VILLARD extern struct serial_state rs_table[]; /* in serial.c */ 6516b195c8SJean-Christophe PLAGNIOL-VILLARD 6616b195c8SJean-Christophe PLAGNIOL-VILLARD static u_char ps2buf[PS2BUF_SIZE]; 6716b195c8SJean-Christophe PLAGNIOL-VILLARD static atomic_t ps2buf_cnt; 6816b195c8SJean-Christophe PLAGNIOL-VILLARD static int ps2buf_in_idx; 6916b195c8SJean-Christophe PLAGNIOL-VILLARD static int ps2buf_out_idx; 7016b195c8SJean-Christophe PLAGNIOL-VILLARD 7116b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5xxx 7216b195c8SJean-Christophe PLAGNIOL-VILLARD int ps2ser_init(void) 7316b195c8SJean-Christophe PLAGNIOL-VILLARD { 7416b195c8SJean-Christophe PLAGNIOL-VILLARD volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; 7516b195c8SJean-Christophe PLAGNIOL-VILLARD unsigned long baseclk; 7616b195c8SJean-Christophe PLAGNIOL-VILLARD int div; 7716b195c8SJean-Christophe PLAGNIOL-VILLARD 7816b195c8SJean-Christophe PLAGNIOL-VILLARD /* reset PSC */ 7916b195c8SJean-Christophe PLAGNIOL-VILLARD psc->command = PSC_SEL_MODE_REG_1; 8016b195c8SJean-Christophe PLAGNIOL-VILLARD 8116b195c8SJean-Christophe PLAGNIOL-VILLARD /* select clock sources */ 8216b195c8SJean-Christophe PLAGNIOL-VILLARD psc->psc_clock_select = 0; 8316b195c8SJean-Christophe PLAGNIOL-VILLARD baseclk = (gd->ipb_clk + 16) / 32; 8416b195c8SJean-Christophe PLAGNIOL-VILLARD 8516b195c8SJean-Christophe PLAGNIOL-VILLARD /* switch to UART mode */ 8616b195c8SJean-Christophe PLAGNIOL-VILLARD psc->sicr = 0; 8716b195c8SJean-Christophe PLAGNIOL-VILLARD 8816b195c8SJean-Christophe PLAGNIOL-VILLARD /* configure parity, bit length and so on */ 8916b195c8SJean-Christophe PLAGNIOL-VILLARD psc->mode = PSC_MODE_8_BITS | PSC_MODE_PARNONE; 9016b195c8SJean-Christophe PLAGNIOL-VILLARD psc->mode = PSC_MODE_ONE_STOP; 9116b195c8SJean-Christophe PLAGNIOL-VILLARD 9216b195c8SJean-Christophe PLAGNIOL-VILLARD /* set up UART divisor */ 9316b195c8SJean-Christophe PLAGNIOL-VILLARD div = (baseclk + (PS2SER_BAUD/2)) / PS2SER_BAUD; 9416b195c8SJean-Christophe PLAGNIOL-VILLARD psc->ctur = (div >> 8) & 0xff; 9516b195c8SJean-Christophe PLAGNIOL-VILLARD psc->ctlr = div & 0xff; 9616b195c8SJean-Christophe PLAGNIOL-VILLARD 9716b195c8SJean-Christophe PLAGNIOL-VILLARD /* disable all interrupts */ 9816b195c8SJean-Christophe PLAGNIOL-VILLARD psc->psc_imr = 0; 9916b195c8SJean-Christophe PLAGNIOL-VILLARD 10016b195c8SJean-Christophe PLAGNIOL-VILLARD /* reset and enable Rx/Tx */ 10116b195c8SJean-Christophe PLAGNIOL-VILLARD psc->command = PSC_RST_RX; 10216b195c8SJean-Christophe PLAGNIOL-VILLARD psc->command = PSC_RST_TX; 10316b195c8SJean-Christophe PLAGNIOL-VILLARD psc->command = PSC_RX_ENABLE | PSC_TX_ENABLE; 10416b195c8SJean-Christophe PLAGNIOL-VILLARD 10516b195c8SJean-Christophe PLAGNIOL-VILLARD return (0); 10616b195c8SJean-Christophe PLAGNIOL-VILLARD } 10716b195c8SJean-Christophe PLAGNIOL-VILLARD 108*77efe35fSWolfgang Denk #else 109*77efe35fSWolfgang Denk 11016b195c8SJean-Christophe PLAGNIOL-VILLARD int ps2ser_init(void) 11116b195c8SJean-Christophe PLAGNIOL-VILLARD { 11216b195c8SJean-Christophe PLAGNIOL-VILLARD NS16550_t com_port = (NS16550_t)COM_BASE; 11316b195c8SJean-Christophe PLAGNIOL-VILLARD 11416b195c8SJean-Christophe PLAGNIOL-VILLARD com_port->ier = 0x00; 115200779e3SDetlev Zundel com_port->lcr = UART_LCR_BKSE | UART_LCR_8N1; 1166d0f6bcfSJean-Christophe PLAGNIOL-VILLARD com_port->dll = (CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) & 0xff; 1176d0f6bcfSJean-Christophe PLAGNIOL-VILLARD com_port->dlm = ((CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) >> 8) & 0xff; 118200779e3SDetlev Zundel com_port->lcr = UART_LCR_8N1; 119200779e3SDetlev Zundel com_port->mcr = (UART_MCR_DTR | UART_MCR_RTS); 120200779e3SDetlev Zundel com_port->fcr = (UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR); 12116b195c8SJean-Christophe PLAGNIOL-VILLARD 12216b195c8SJean-Christophe PLAGNIOL-VILLARD return (0); 12316b195c8SJean-Christophe PLAGNIOL-VILLARD } 12416b195c8SJean-Christophe PLAGNIOL-VILLARD 125*77efe35fSWolfgang Denk #endif /* CONFIG_MPC5xxx / other */ 12616b195c8SJean-Christophe PLAGNIOL-VILLARD 12716b195c8SJean-Christophe PLAGNIOL-VILLARD void ps2ser_putc(int chr) 12816b195c8SJean-Christophe PLAGNIOL-VILLARD { 12916b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5xxx 13016b195c8SJean-Christophe PLAGNIOL-VILLARD volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; 131*77efe35fSWolfgang Denk #else 13216b195c8SJean-Christophe PLAGNIOL-VILLARD NS16550_t com_port = (NS16550_t)COM_BASE; 13316b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 134*77efe35fSWolfgang Denk debug(">>>> 0x%02x\n", chr); 13516b195c8SJean-Christophe PLAGNIOL-VILLARD 13616b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5xxx 13716b195c8SJean-Christophe PLAGNIOL-VILLARD while (!(psc->psc_status & PSC_SR_TXRDY)); 13816b195c8SJean-Christophe PLAGNIOL-VILLARD 13916b195c8SJean-Christophe PLAGNIOL-VILLARD psc->psc_buffer_8 = chr; 140*77efe35fSWolfgang Denk #else 141200779e3SDetlev Zundel while ((com_port->lsr & UART_LSR_THRE) == 0); 14216b195c8SJean-Christophe PLAGNIOL-VILLARD com_port->thr = chr; 14316b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 14416b195c8SJean-Christophe PLAGNIOL-VILLARD } 14516b195c8SJean-Christophe PLAGNIOL-VILLARD 14616b195c8SJean-Christophe PLAGNIOL-VILLARD static int ps2ser_getc_hw(void) 14716b195c8SJean-Christophe PLAGNIOL-VILLARD { 14816b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5xxx 14916b195c8SJean-Christophe PLAGNIOL-VILLARD volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; 150*77efe35fSWolfgang Denk #else 15116b195c8SJean-Christophe PLAGNIOL-VILLARD NS16550_t com_port = (NS16550_t)COM_BASE; 15216b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 15316b195c8SJean-Christophe PLAGNIOL-VILLARD int res = -1; 15416b195c8SJean-Christophe PLAGNIOL-VILLARD 15516b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5xxx 15616b195c8SJean-Christophe PLAGNIOL-VILLARD if (psc->psc_status & PSC_SR_RXRDY) { 15716b195c8SJean-Christophe PLAGNIOL-VILLARD res = (psc->psc_buffer_8); 15816b195c8SJean-Christophe PLAGNIOL-VILLARD } 159*77efe35fSWolfgang Denk #else 160200779e3SDetlev Zundel if (com_port->lsr & UART_LSR_DR) { 16116b195c8SJean-Christophe PLAGNIOL-VILLARD res = com_port->rbr; 16216b195c8SJean-Christophe PLAGNIOL-VILLARD } 16316b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 16416b195c8SJean-Christophe PLAGNIOL-VILLARD 16516b195c8SJean-Christophe PLAGNIOL-VILLARD return res; 16616b195c8SJean-Christophe PLAGNIOL-VILLARD } 16716b195c8SJean-Christophe PLAGNIOL-VILLARD 16816b195c8SJean-Christophe PLAGNIOL-VILLARD int ps2ser_getc(void) 16916b195c8SJean-Christophe PLAGNIOL-VILLARD { 17016b195c8SJean-Christophe PLAGNIOL-VILLARD volatile int chr; 17116b195c8SJean-Christophe PLAGNIOL-VILLARD int flags; 17216b195c8SJean-Christophe PLAGNIOL-VILLARD 173*77efe35fSWolfgang Denk debug("<< "); 17416b195c8SJean-Christophe PLAGNIOL-VILLARD 17516b195c8SJean-Christophe PLAGNIOL-VILLARD flags = disable_interrupts(); 17616b195c8SJean-Christophe PLAGNIOL-VILLARD 17716b195c8SJean-Christophe PLAGNIOL-VILLARD do { 17816b195c8SJean-Christophe PLAGNIOL-VILLARD if (atomic_read(&ps2buf_cnt) != 0) { 17916b195c8SJean-Christophe PLAGNIOL-VILLARD chr = ps2buf[ps2buf_out_idx++]; 18016b195c8SJean-Christophe PLAGNIOL-VILLARD ps2buf_out_idx &= (PS2BUF_SIZE - 1); 18116b195c8SJean-Christophe PLAGNIOL-VILLARD atomic_dec(&ps2buf_cnt); 18216b195c8SJean-Christophe PLAGNIOL-VILLARD } else { 18316b195c8SJean-Christophe PLAGNIOL-VILLARD chr = ps2ser_getc_hw(); 18416b195c8SJean-Christophe PLAGNIOL-VILLARD } 18516b195c8SJean-Christophe PLAGNIOL-VILLARD } 18616b195c8SJean-Christophe PLAGNIOL-VILLARD while (chr < 0); 18716b195c8SJean-Christophe PLAGNIOL-VILLARD 188*77efe35fSWolfgang Denk if (flags) 189*77efe35fSWolfgang Denk enable_interrupts(); 19016b195c8SJean-Christophe PLAGNIOL-VILLARD 191*77efe35fSWolfgang Denk debug("0x%02x\n", chr); 19216b195c8SJean-Christophe PLAGNIOL-VILLARD 19316b195c8SJean-Christophe PLAGNIOL-VILLARD return chr; 19416b195c8SJean-Christophe PLAGNIOL-VILLARD } 19516b195c8SJean-Christophe PLAGNIOL-VILLARD 19616b195c8SJean-Christophe PLAGNIOL-VILLARD int ps2ser_check(void) 19716b195c8SJean-Christophe PLAGNIOL-VILLARD { 19816b195c8SJean-Christophe PLAGNIOL-VILLARD int flags; 19916b195c8SJean-Christophe PLAGNIOL-VILLARD 20016b195c8SJean-Christophe PLAGNIOL-VILLARD flags = disable_interrupts(); 20116b195c8SJean-Christophe PLAGNIOL-VILLARD ps2ser_interrupt(NULL); 20216b195c8SJean-Christophe PLAGNIOL-VILLARD if (flags) enable_interrupts(); 20316b195c8SJean-Christophe PLAGNIOL-VILLARD 20416b195c8SJean-Christophe PLAGNIOL-VILLARD return atomic_read(&ps2buf_cnt); 20516b195c8SJean-Christophe PLAGNIOL-VILLARD } 20616b195c8SJean-Christophe PLAGNIOL-VILLARD 20716b195c8SJean-Christophe PLAGNIOL-VILLARD static void ps2ser_interrupt(void *dev_id) 20816b195c8SJean-Christophe PLAGNIOL-VILLARD { 20916b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5xxx 21016b195c8SJean-Christophe PLAGNIOL-VILLARD volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE; 211*77efe35fSWolfgang Denk #else 21216b195c8SJean-Christophe PLAGNIOL-VILLARD NS16550_t com_port = (NS16550_t)COM_BASE; 21316b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 21416b195c8SJean-Christophe PLAGNIOL-VILLARD int chr; 21516b195c8SJean-Christophe PLAGNIOL-VILLARD int status; 21616b195c8SJean-Christophe PLAGNIOL-VILLARD 21716b195c8SJean-Christophe PLAGNIOL-VILLARD do { 21816b195c8SJean-Christophe PLAGNIOL-VILLARD chr = ps2ser_getc_hw(); 21916b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5xxx 22016b195c8SJean-Christophe PLAGNIOL-VILLARD status = psc->psc_status; 22116b195c8SJean-Christophe PLAGNIOL-VILLARD #else 222*77efe35fSWolfgang Denk status = com_port->lsr; 22316b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 22416b195c8SJean-Christophe PLAGNIOL-VILLARD if (chr < 0) continue; 22516b195c8SJean-Christophe PLAGNIOL-VILLARD 22616b195c8SJean-Christophe PLAGNIOL-VILLARD if (atomic_read(&ps2buf_cnt) < PS2BUF_SIZE) { 22716b195c8SJean-Christophe PLAGNIOL-VILLARD ps2buf[ps2buf_in_idx++] = chr; 22816b195c8SJean-Christophe PLAGNIOL-VILLARD ps2buf_in_idx &= (PS2BUF_SIZE - 1); 22916b195c8SJean-Christophe PLAGNIOL-VILLARD atomic_inc(&ps2buf_cnt); 23016b195c8SJean-Christophe PLAGNIOL-VILLARD } else { 23116b195c8SJean-Christophe PLAGNIOL-VILLARD printf ("ps2ser.c: buffer overflow\n"); 23216b195c8SJean-Christophe PLAGNIOL-VILLARD } 23316b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5xxx 23416b195c8SJean-Christophe PLAGNIOL-VILLARD } while (status & PSC_SR_RXRDY); 23516b195c8SJean-Christophe PLAGNIOL-VILLARD #else 236*77efe35fSWolfgang Denk } while (status & UART_LSR_DR); 23716b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 23816b195c8SJean-Christophe PLAGNIOL-VILLARD if (atomic_read(&ps2buf_cnt)) { 23916b195c8SJean-Christophe PLAGNIOL-VILLARD ps2mult_callback(atomic_read(&ps2buf_cnt)); 24016b195c8SJean-Christophe PLAGNIOL-VILLARD } 24116b195c8SJean-Christophe PLAGNIOL-VILLARD } 242