📄 serial_sicc.c
字号:
/* * arch/ppc/4xx_io/serial_sicc.c * * Driver for IBM STB3xxx SICC serial port * * Based on drivers/char/serial_amba.c, by ARM Ltd. * * Copyright 2001 IBM Crop. * Author: IBM China Research Lab * Yudong Yang <yangyud@cn.ibm.com> * Yi Ge <geyi@cn.ibm.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * This is a driver for SICC serial port on IBM Redwood 4 evaluation board. * The driver support both as a console device and normal serial device and * is compatible with normal ttyS* devices. */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/circ_buf.h>#include <linux/serial.h>#include <linux/console.h>#include <linux/sysrq.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/serial.h>#include <linux/serialP.h>/* ----------------------------------------------------------------------------- * From STB03xxx SICC UART Specification * ----------------------------------------------------------------------------- * UART Register Offsets. */#define BL_SICC_LSR 0x0000000 /* line status register read/clear */#define BL_SICC_LSRS 0x0000001 /* set line status register read/set */#define BL_SICC_HSR 0x0000002 /* handshake status register r/clear */#define BL_SICC_HSRS 0x0000003 /* set handshake status register r/set */#define BL_SICC_BRDH 0x0000004 /* baudrate divisor high reg r/w */#define BL_SICC_BRDL 0x0000005 /* baudrate divisor low reg r/w */#define BL_SICC_LCR 0x0000006 /* control register r/w */#define BL_SICC_RCR 0x0000007 /* receiver command register r/w */#define BL_SICC_TxCR 0x0000008 /* transmitter command register r/w */#define BL_SICC_RBR 0x0000009 /* receive buffer r */#define BL_SICC_TBR 0x0000009 /* transmit buffer w */#define BL_SICC_CTL2 0x000000A /* added for Vesta */#define BL_SICC_IrCR 0x000000B /* added for Vesta IR *//* masks and definitions for serial port control register */#define _LCR_LM_MASK 0xc0 /* loop back modes */#define _LCR_DTR_MASK 0x20 /* data terminal ready 0-inactive */#define _LCR_RTS_MASK 0x10 /* request to send 0-inactive */#define _LCR_DB_MASK 0x08 /* data bits mask */#define _LCR_PE_MASK 0x04 /* parity enable */#define _LCR_PTY_MASK 0x02 /* parity */#define _LCR_SB_MASK 0x01 /* stop bit mask */#define _LCR_LM_NORM 0x00 /* normal operation */#define _LCR_LM_LOOP 0x40 /* internal loopback mode */#define _LCR_LM_ECHO 0x80 /* automatic echo mode */#define _LCR_LM_RES 0xc0 /* reserved */#define _LCR_DTR_ACTIVE _LCR_DTR_MASK /* DTR is active */#define _LCR_RTS_ACTIVE _LCR_RTS_MASK /* RTS is active */#define _LCR_DB_8_BITS _LCR_DB_MASK /* 8 data bits */#define _LCR_DB_7_BITS 0x00 /* 7 data bits */#define _LCR_PE_ENABLE _LCR_PE_MASK /* parity enabled */#define _LCR_PE_DISABLE 0x00 /* parity disabled */#define _LCR_PTY_EVEN 0x00 /* even parity */#define _LCR_PTY_ODD _LCR_PTY_MASK /* odd parity */#define _LCR_SB_1_BIT 0x00 /* one stop bit */#define _LCR_SB_2_BIT _LCR_SB_MASK /* two stop bit *//* serial port handshake register */#define _HSR_DIS_MASK 0x80 /* DSR input inactive error mask */#define _HSR_CS_MASK 0x40 /* CTS input inactive error mask */#define _HSR_DIS_ACT 0x00 /* dsr input is active */#define _HSR_DIS_INACT _HSR_DIS_MASK /* dsr input is inactive */#define _HSR_CS_ACT 0x00 /* cts input is active */#define _HSR_CS_INACT _HSR_CS_MASK /* cts input is active *//* serial port line status register */#define _LSR_RBR_MASK 0x80 /* receive buffer ready mask */#define _LSR_FE_MASK 0x40 /* framing error */#define _LSR_OE_MASK 0x20 /* overrun error */#define _LSR_PE_MASK 0x10 /* parity error */#define _LSR_LB_MASK 0x08 /* line break */#define _LSR_TBR_MASK 0x04 /* transmit buffer ready */#define _LSR_TSR_MASK 0x02 /* transmit shift register ready */#define _LSR_RBR_FULL _LSR_RBR_MASK /* receive buffer is full */#define _LSR_FE_ERROR _LSR_FE_MASK /* framing error detected */#define _LSR_OE_ERROR _LSR_OE_MASK /* overrun error detected */#define _LSR_PE_ERROR _LSR_PE_MASK /* parity error detected */#define _LSR_LB_BREAK _LSR_LB_MASK /* line break detected */#define _LSR_TBR_EMPTY _LSR_TBR_MASK /* transmit buffer is ready */#define _LSR_TSR_EMPTY _LSR_TSR_MASK /* transmit shift register is empty */#define _LSR_TX_ALL 0x06 /* all physical transmit is done */#define _LSR_RX_ERR (_LSR_LB_BREAK | _LSR_FE_MASK | _LSR_OE_MASK | \ _LSR_PE_MASK )/* serial port receiver command register */#define _RCR_ER_MASK 0x80 /* enable receiver mask */#define _RCR_DME_MASK 0x60 /* dma mode */#define _RCR_EIE_MASK 0x10 /* error interrupt enable mask */#define _RCR_PME_MASK 0x08 /* pause mode mask */#define _RCR_ER_ENABLE _RCR_ER_MASK /* receiver enabled */#define _RCR_DME_DISABLE 0x00 /* dma disabled */#define _RCR_DME_RXRDY 0x20 /* dma disabled, RxRDY interrupt enabled*/#define _RCR_DME_ENABLE2 0x40 /* dma enabled,receiver src channel 2 */#define _RCR_DME_ENABLE3 0x60 /* dma enabled,receiver src channel 3 */#define _RCR_PME_HARD _RCR_PME_MASK /* RTS controlled by hardware */#define _RCR_PME_SOFT 0x00 /* RTS controlled by software *//* serial port transmit command register */#define _TxCR_ET_MASK 0x80 /* transmiter enable mask */#define _TxCR_DME_MASK 0x60 /* dma mode mask */#define _TxCR_TIE_MASK 0x10 /* empty interrupt enable mask */#define _TxCR_EIE_MASK 0x08 /* error interrupt enable mask */#define _TxCR_SPE_MASK 0x04 /* stop/pause mask */#define _TxCR_TB_MASK 0x02 /* transmit break mask */#define _TxCR_ET_ENABLE _TxCR_ET_MASK /* transmiter enabled */#define _TxCR_DME_DISABLE 0x00 /* transmiter disabled, TBR intr disabled */#define _TxCR_DME_TBR 0x20 /* transmiter disabled, TBR intr enabled */#define _TxCR_DME_CHAN_2 0x40 /* dma enabled, destination chann 2 */#define _TxCR_DME_CHAN_3 0x60 /* dma enabled, destination chann 3 *//* serial ctl reg 2 - added for Vesta */#define _CTL2_EXTERN 0x80 /* */#define _CTL2_USEFIFO 0x40 /* */#define _CTL2_RESETRF 0x08 /* */#define _CTL2_RESETTF 0x04 /* */#define SERIAL_SICC_NAME "ttySICC"#define SERIAL_SICC_MAJOR 150#define SERIAL_SICC_MINOR 1#define SERIAL_SICC_NR 1#ifndef TRUE#define TRUE 1#endif#ifndef FALSE#define FALSE 0#endif/* * Things needed by tty driver */static struct tty_driver *siccnormal_driver;#if defined(CONFIG_SERIAL_SICC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif/* * Things needed internally to this driver *//* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. */static u_char *tmp_buf;static DECLARE_MUTEX(tmp_buf_sem);#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256#define SICC_ISR_PASS_LIMIT 256#define EVT_WRITE_WAKEUP 0struct SICC_icount { __u32 cts; __u32 dsr; __u32 rng; __u32 dcd; __u32 rx; __u32 tx; __u32 frame; __u32 overrun; __u32 parity; __u32 brk; __u32 buf_overrun;};/* * Static information about the port */struct SICC_port { unsigned int uart_base; unsigned int uart_base_phys; unsigned int irqrx; unsigned int irqtx; unsigned int uartclk; unsigned int fifosize; unsigned int tiocm_support; void (*set_mctrl)(struct SICC_port *, u_int mctrl);};/* * This is the state information which is persistent across opens */struct SICC_state { struct SICC_icount icount; unsigned int line; unsigned int close_delay; unsigned int closing_wait; unsigned int custom_divisor; unsigned int flags; int count; struct SICC_info *info;};#define SICC_XMIT_SIZE 1024/* * This is the state information which is only valid when the port is open. */struct SICC_info { struct SICC_port *port; struct SICC_state *state; struct tty_struct *tty; unsigned char x_char; unsigned char old_status; unsigned char read_status_mask; unsigned char ignore_status_mask; struct circ_buf xmit; unsigned int flags;#ifdef SUPPORT_SYSRQ unsigned long sysrq;#endif unsigned int event; unsigned int timeout; unsigned int lcr_h; unsigned int mctrl; int blocked_open; struct tasklet_struct tlet; wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait;};#ifdef CONFIG_SERIAL_SICC_CONSOLEstatic struct console siccuart_cons;#endifstatic void siccuart_change_speed(struct SICC_info *info, struct termios *old_termios);static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout);static void powerpcMtcic_cr(unsigned long value){ mtdcr(DCRN_CICCR, value);}static unsigned long powerpcMfcic_cr(void){ return mfdcr(DCRN_CICCR);}static unsigned long powerpcMfclkgpcr(void){ return mfdcr(DCRN_SCCR);}static void sicc_set_mctrl_null(struct SICC_port *port, u_int mctrl){}static struct SICC_port sicc_ports[SERIAL_SICC_NR] = { { .uart_base = 0, .uart_base_phys = SICC0_IO_BASE, .irqrx = SICC0_INTRX, .irqtx = SICC0_INTTX,// .uartclk = 0, .fifosize = 1, .set_mctrl = sicc_set_mctrl_null, }};static struct SICC_state sicc_state[SERIAL_SICC_NR];static void siccuart_enable_rx_interrupt(struct SICC_info *info){ unsigned char cr; cr = readb(info->port->uart_base+BL_SICC_RCR); cr &= ~_RCR_DME_MASK; cr |= _RCR_DME_RXRDY; writeb(cr, info->port->uart_base+BL_SICC_RCR);}static void siccuart_disable_rx_interrupt(struct SICC_info *info){ unsigned char cr; cr = readb(info->port->uart_base+BL_SICC_RCR); cr &= ~_RCR_DME_MASK; cr |= _RCR_DME_DISABLE; writeb(cr, info->port->uart_base+BL_SICC_RCR);}static void siccuart_enable_tx_interrupt(struct SICC_info *info){ unsigned char cr; cr = readb(info->port->uart_base+BL_SICC_TxCR); cr &= ~_TxCR_DME_MASK; cr |= _TxCR_DME_TBR; writeb(cr, info->port->uart_base+BL_SICC_TxCR);}static void siccuart_disable_tx_interrupt(struct SICC_info *info){ unsigned char cr; cr = readb(info->port->uart_base+BL_SICC_TxCR); cr &= ~_TxCR_DME_MASK; cr |= _TxCR_DME_DISABLE; writeb(cr, info->port->uart_base+BL_SICC_TxCR);}static void siccuart_stop(struct tty_struct *tty){ struct SICC_info *info = tty->driver_data; unsigned long flags; save_flags(flags); cli(); siccuart_disable_tx_interrupt(info); restore_flags(flags);}static void siccuart_start(struct tty_struct *tty){ struct SICC_info *info = tty->driver_data; unsigned long flags; save_flags(flags); cli(); if (info->xmit.head != info->xmit.tail && info->xmit.buf) siccuart_enable_tx_interrupt(info); restore_flags(flags);}/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static void siccuart_event(struct SICC_info *info, int event){ info->event |= 1 << event; tasklet_schedule(&info->tlet);}static voidsiccuart_rx_chars(struct SICC_info *info, struct pt_regs *regs){ struct tty_struct *tty = info->tty; unsigned int status, ch, rsr, flg, ignored = 0; struct SICC_icount *icount = &info->state->icount; struct SICC_port *port = info->port; status = readb(port->uart_base+BL_SICC_LSR ); while (status & _LSR_RBR_FULL) { ch = readb(port->uart_base+BL_SICC_RBR); if (tty->flip.count >= TTY_FLIPBUF_SIZE) goto ignore_char; icount->rx++; flg = TTY_NORMAL; /* * Note that the error handling code is * out of the main execution path */ rsr = readb(port->uart_base+BL_SICC_LSR); if (rsr & _LSR_RX_ERR) goto handle_error;#ifdef SUPPORT_SYSRQ if (info->sysrq) { if (ch && time_before(jiffies, info->sysrq)) { handle_sysrq(ch, regs, NULL); info->sysrq = 0; goto ignore_char; } info->sysrq = 0; }#endif error_return: *tty->flip.flag_buf_ptr++ = flg; *tty->flip.char_buf_ptr++ = ch; tty->flip.count++; ignore_char: status = readb(port->uart_base+BL_SICC_LSR ); }out: tty_flip_buffer_push(tty); return;handle_error: if (rsr & _LSR_LB_BREAK) { rsr &= ~(_LSR_FE_MASK | _LSR_PE_MASK); icount->brk++;#ifdef SUPPORT_SYSRQ if (info->state->line == siccuart_cons.index) { if (!info->sysrq) { info->sysrq = jiffies + HZ*5; goto ignore_char; } }#endif } else if (rsr & _LSR_PE_MASK) icount->parity++; else if (rsr & _LSR_FE_MASK) icount->frame++; if (rsr & _LSR_OE_MASK) icount->overrun++; if (rsr & info->ignore_status_mask) { if (++ignored > 100) goto out; goto ignore_char; } rsr &= info->read_status_mask; if (rsr & _LSR_LB_BREAK) flg = TTY_BREAK; else if (rsr & _LSR_PE_MASK) flg = TTY_PARITY; else if (rsr & _LSR_FE_MASK) flg = TTY_FRAME; if (rsr & _LSR_OE_MASK) { /* * CHECK: does overrun affect the current character? * ASSUMPTION: it does not. */ *tty->flip.flag_buf_ptr++ = flg; *tty->flip.char_buf_ptr++ = ch; tty->flip.count++; if (tty->flip.count >= TTY_FLIPBUF_SIZE) goto ignore_char; ch = 0; flg = TTY_OVERRUN;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -