sunzilog.c

来自「linux 内核源代码」· C语言 代码 · 共 1,597 行 · 第 1/3 页

C
1,597
字号
	struct uart_sunzilog_port *up = dev_id;	while (up) {		struct zilog_channel __iomem *channel			= ZILOG_CHANNEL_FROM_PORT(&up->port);		struct tty_struct *tty;		unsigned char r3;		spin_lock(&up->port.lock);		r3 = read_zsreg(channel, R3);		/* Channel A */		tty = NULL;		if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {			writeb(RES_H_IUS, &channel->control);			ZSDELAY();			ZS_WSYNC(channel);			if (r3 & CHARxIP)				tty = sunzilog_receive_chars(up, channel);			if (r3 & CHAEXT)				sunzilog_status_handle(up, channel);			if (r3 & CHATxIP)				sunzilog_transmit_chars(up, channel);		}		spin_unlock(&up->port.lock);		if (tty)			tty_flip_buffer_push(tty);		/* Channel B */		up = up->next;		channel = ZILOG_CHANNEL_FROM_PORT(&up->port);		spin_lock(&up->port.lock);		tty = NULL;		if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {			writeb(RES_H_IUS, &channel->control);			ZSDELAY();			ZS_WSYNC(channel);			if (r3 & CHBRxIP)				tty = sunzilog_receive_chars(up, channel);			if (r3 & CHBEXT)				sunzilog_status_handle(up, channel);			if (r3 & CHBTxIP)				sunzilog_transmit_chars(up, channel);		}		spin_unlock(&up->port.lock);		if (tty)			tty_flip_buffer_push(tty);		up = up->next;	}	return IRQ_HANDLED;}/* A convenient way to quickly get R0 status.  The caller must _not_ hold the * port lock, it is acquired here. */static __inline__ unsigned char sunzilog_read_channel_status(struct uart_port *port){	struct zilog_channel __iomem *channel;	unsigned char status;	channel = ZILOG_CHANNEL_FROM_PORT(port);	status = readb(&channel->control);	ZSDELAY();	return status;}/* The port lock is not held.  */static unsigned int sunzilog_tx_empty(struct uart_port *port){	unsigned long flags;	unsigned char status;	unsigned int ret;	spin_lock_irqsave(&port->lock, flags);	status = sunzilog_read_channel_status(port);	spin_unlock_irqrestore(&port->lock, flags);	if (status & Tx_BUF_EMP)		ret = TIOCSER_TEMT;	else		ret = 0;	return ret;}/* The port lock is held and interrupts are disabled.  */static unsigned int sunzilog_get_mctrl(struct uart_port *port){	unsigned char status;	unsigned int ret;	status = sunzilog_read_channel_status(port);	ret = 0;	if (status & DCD)		ret |= TIOCM_CAR;	if (status & SYNC)		ret |= TIOCM_DSR;	if (status & CTS)		ret |= TIOCM_CTS;	return ret;}/* The port lock is held and interrupts are disabled.  */static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl){	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);	unsigned char set_bits, clear_bits;	set_bits = clear_bits = 0;	if (mctrl & TIOCM_RTS)		set_bits |= RTS;	else		clear_bits |= RTS;	if (mctrl & TIOCM_DTR)		set_bits |= DTR;	else		clear_bits |= DTR;	/* NOTE: Not subject to 'transmitter active' rule.  */ 	up->curregs[R5] |= set_bits;	up->curregs[R5] &= ~clear_bits;	write_zsreg(channel, R5, up->curregs[R5]);}/* The port lock is held and interrupts are disabled.  */static void sunzilog_stop_tx(struct uart_port *port){	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;	up->flags |= SUNZILOG_FLAG_TX_STOPPED;}/* The port lock is held and interrupts are disabled.  */static void sunzilog_start_tx(struct uart_port *port){	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);	unsigned char status;	up->flags |= SUNZILOG_FLAG_TX_ACTIVE;	up->flags &= ~SUNZILOG_FLAG_TX_STOPPED;	status = readb(&channel->control);	ZSDELAY();	/* TX busy?  Just wait for the TX done interrupt.  */	if (!(status & Tx_BUF_EMP))		return;	/* Send the first character to jump-start the TX done	 * IRQ sending engine.	 */	if (port->x_char) {		writeb(port->x_char, &channel->data);		ZSDELAY();		ZS_WSYNC(channel);		port->icount.tx++;		port->x_char = 0;	} else {		struct circ_buf *xmit = &port->info->xmit;		writeb(xmit->buf[xmit->tail], &channel->data);		ZSDELAY();		ZS_WSYNC(channel);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		port->icount.tx++;		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)			uart_write_wakeup(&up->port);	}}/* The port lock is held.  */static void sunzilog_stop_rx(struct uart_port *port){	struct uart_sunzilog_port *up = UART_ZILOG(port);	struct zilog_channel __iomem *channel;	if (ZS_IS_CONS(up))		return;	channel = ZILOG_CHANNEL_FROM_PORT(port);	/* Disable all RX interrupts.  */	up->curregs[R1] &= ~RxINT_MASK;	sunzilog_maybe_update_regs(up, channel);}/* The port lock is held.  */static void sunzilog_enable_ms(struct uart_port *port){	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);	unsigned char new_reg;	new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE);	if (new_reg != up->curregs[R15]) {		up->curregs[R15] = new_reg;		/* NOTE: Not subject to 'transmitter active' rule.  */ 		write_zsreg(channel, R15, up->curregs[R15] & ~WR7pEN);	}}/* The port lock is not held.  */static void sunzilog_break_ctl(struct uart_port *port, int break_state){	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);	unsigned char set_bits, clear_bits, new_reg;	unsigned long flags;	set_bits = clear_bits = 0;	if (break_state)		set_bits |= SND_BRK;	else		clear_bits |= SND_BRK;	spin_lock_irqsave(&port->lock, flags);	new_reg = (up->curregs[R5] | set_bits) & ~clear_bits;	if (new_reg != up->curregs[R5]) {		up->curregs[R5] = new_reg;		/* NOTE: Not subject to 'transmitter active' rule.  */ 		write_zsreg(channel, R5, up->curregs[R5]);	}	spin_unlock_irqrestore(&port->lock, flags);}static void __sunzilog_startup(struct uart_sunzilog_port *up){	struct zilog_channel __iomem *channel;	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);	up->prev_status = readb(&channel->control);	/* Enable receiver and transmitter.  */	up->curregs[R3] |= RxENAB;	up->curregs[R5] |= TxENAB;	up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;	sunzilog_maybe_update_regs(up, channel);}static int sunzilog_startup(struct uart_port *port){	struct uart_sunzilog_port *up = UART_ZILOG(port);	unsigned long flags;	if (ZS_IS_CONS(up))		return 0;	spin_lock_irqsave(&port->lock, flags);	__sunzilog_startup(up);	spin_unlock_irqrestore(&port->lock, flags);	return 0;}/* * The test for ZS_IS_CONS is explained by the following e-mail: ***** * From: Russell King <rmk@arm.linux.org.uk> * Date: Sun, 8 Dec 2002 10:18:38 +0000 * * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote: * > I boot my 2.5 boxes using "console=ttyS0,9600" argument, * > and I noticed that something is not right with reference * > counting in this case. It seems that when the console * > is open by kernel initially, this is not accounted * > as an open, and uart_startup is not called. * * That is correct.  We are unable to call uart_startup when the serial * console is initialised because it may need to allocate memory (as * request_irq does) and the memory allocators may not have been * initialised. * * 1. initialise the port into a state where it can send characters in the *    console write method. * * 2. don't do the actual hardware shutdown in your shutdown() method (but *    do the normal software shutdown - ie, free irqs etc) ***** */static void sunzilog_shutdown(struct uart_port *port){	struct uart_sunzilog_port *up = UART_ZILOG(port);	struct zilog_channel __iomem *channel;	unsigned long flags;	if (ZS_IS_CONS(up))		return;	spin_lock_irqsave(&port->lock, flags);	channel = ZILOG_CHANNEL_FROM_PORT(port);	/* Disable receiver and transmitter.  */	up->curregs[R3] &= ~RxENAB;	up->curregs[R5] &= ~TxENAB;	/* Disable all interrupts and BRK assertion.  */	up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);	up->curregs[R5] &= ~SND_BRK;	sunzilog_maybe_update_regs(up, channel);	spin_unlock_irqrestore(&port->lock, flags);}/* Shared by TTY driver and serial console setup.  The port lock is held * and local interrupts are disabled. */static voidsunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag,		       unsigned int iflag, int brg){	up->curregs[R10] = NRZ;	up->curregs[R11] = TCBR | RCBR;	/* Program BAUD and clock source. */	up->curregs[R4] &= ~XCLK_MASK;	up->curregs[R4] |= X16CLK;	up->curregs[R12] = brg & 0xff;	up->curregs[R13] = (brg >> 8) & 0xff;	up->curregs[R14] = BRSRC | BRENAB;	/* Character size, stop bits, and parity. */	up->curregs[R3] &= ~RxN_MASK;	up->curregs[R5] &= ~TxN_MASK;	switch (cflag & CSIZE) {	case CS5:		up->curregs[R3] |= Rx5;		up->curregs[R5] |= Tx5;		up->parity_mask = 0x1f;		break;	case CS6:		up->curregs[R3] |= Rx6;		up->curregs[R5] |= Tx6;		up->parity_mask = 0x3f;		break;	case CS7:		up->curregs[R3] |= Rx7;		up->curregs[R5] |= Tx7;		up->parity_mask = 0x7f;		break;	case CS8:	default:		up->curregs[R3] |= Rx8;		up->curregs[R5] |= Tx8;		up->parity_mask = 0xff;		break;	};	up->curregs[R4] &= ~0x0c;	if (cflag & CSTOPB)		up->curregs[R4] |= SB2;	else		up->curregs[R4] |= SB1;	if (cflag & PARENB)		up->curregs[R4] |= PAR_ENAB;	else		up->curregs[R4] &= ~PAR_ENAB;	if (!(cflag & PARODD))		up->curregs[R4] |= PAR_EVEN;	else		up->curregs[R4] &= ~PAR_EVEN;	up->port.read_status_mask = Rx_OVR;	if (iflag & INPCK)		up->port.read_status_mask |= CRC_ERR | PAR_ERR;	if (iflag & (BRKINT | PARMRK))		up->port.read_status_mask |= BRK_ABRT;	up->port.ignore_status_mask = 0;	if (iflag & IGNPAR)		up->port.ignore_status_mask |= CRC_ERR | PAR_ERR;	if (iflag & IGNBRK) {		up->port.ignore_status_mask |= BRK_ABRT;		if (iflag & IGNPAR)			up->port.ignore_status_mask |= Rx_OVR;	}	if ((cflag & CREAD) == 0)		up->port.ignore_status_mask = 0xff;}/* The port lock is not held.  */static voidsunzilog_set_termios(struct uart_port *port, struct ktermios *termios,		     struct ktermios *old){	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;	unsigned long flags;	int baud, brg;	baud = uart_get_baud_rate(port, termios, old, 1200, 76800);	spin_lock_irqsave(&up->port.lock, flags);	brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);	sunzilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg);	if (UART_ENABLE_MS(&up->port, termios->c_cflag))		up->flags |= SUNZILOG_FLAG_MODEM_STATUS;	else		up->flags &= ~SUNZILOG_FLAG_MODEM_STATUS;	up->cflag = termios->c_cflag;	sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port));	uart_update_timeout(port, termios->c_cflag, baud);	spin_unlock_irqrestore(&up->port.lock, flags);}static const char *sunzilog_type(struct uart_port *port){	struct uart_sunzilog_port *up = UART_ZILOG(port);	return (up->flags & SUNZILOG_FLAG_ESCC) ? "zs (ESCC)" : "zs";}/* We do not request/release mappings of the registers here, this * happens at early serial probe time. */static void sunzilog_release_port(struct uart_port *port){}static int sunzilog_request_port(struct uart_port *port){	return 0;}/* These do not need to do anything interesting either.  */static void sunzilog_config_port(struct uart_port *port, int flags){}/* We do not support letting the user mess with the divisor, IRQ, etc. */static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *ser){	return -EINVAL;}static struct uart_ops sunzilog_pops = {	.tx_empty	=	sunzilog_tx_empty,	.set_mctrl	=	sunzilog_set_mctrl,	.get_mctrl	=	sunzilog_get_mctrl,	.stop_tx	=	sunzilog_stop_tx,	.start_tx	=	sunzilog_start_tx,	.stop_rx	=	sunzilog_stop_rx,	.enable_ms	=	sunzilog_enable_ms,	.break_ctl	=	sunzilog_break_ctl,	.startup	=	sunzilog_startup,	.shutdown	=	sunzilog_shutdown,	.set_termios	=	sunzilog_set_termios,	.type		=	sunzilog_type,	.release_port	=	sunzilog_release_port,	.request_port	=	sunzilog_request_port,	.config_port	=	sunzilog_config_port,	.verify_port	=	sunzilog_verify_port,};static struct uart_sunzilog_port *sunzilog_port_table;static struct zilog_layout __iomem **sunzilog_chip_regs;static struct uart_sunzilog_port *sunzilog_irq_chain;static struct uart_driver sunzilog_reg = {	.owner		=	THIS_MODULE,	.driver_name	=	"ttyS",	.dev_name	=	"ttyS",	.major		=	TTY_MAJOR,};static int __init sunzilog_alloc_tables(int num_sunzilog){	struct uart_sunzilog_port *up;	unsigned long size;	int num_channels = num_sunzilog * 2;	int i;	size = num_channels * sizeof(struct uart_sunzilog_port);	sunzilog_port_table = kzalloc(size, GFP_KERNEL);	if (!sunzilog_port_table)		return -ENOMEM;	for (i = 0; i < num_channels; i++) {		up = &sunzilog_port_table[i];		spin_lock_init(&up->port.lock);		if (i == 0)			sunzilog_irq_chain = up;		if (i < num_channels - 1)			up->next = up + 1;		else			up->next = NULL;	}	size = num_sunzilog * sizeof(struct zilog_layout __iomem *);	sunzilog_chip_regs = kzalloc(size, GFP_KERNEL);	if (!sunzilog_chip_regs) {		kfree(sunzilog_port_table);		sunzilog_irq_chain = NULL;		return -ENOMEM;	}	return 0;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?