⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sb1250-duart.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	drivers/serial/sb1250-duart.c * *	Support for the asynchronous serial interface (DUART) included *	in the BCM1250 and derived System-On-a-Chip (SOC) devices. * *	Copyright (c) 2007  Maciej W. Rozycki * *	Derived from drivers/char/sb1250_duart.c for which the following *	copyright applies: * *	Copyright (c) 2000, 2001, 2002, 2003, 2004  Broadcom Corporation * *	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. * *	References: * *	"BCM1250/BCM1125/BCM1125H User Manual", Broadcom Corporation */#if defined(CONFIG_SERIAL_SB1250_DUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/compiler.h>#include <linux/console.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <linux/spinlock.h>#include <linux/sysrq.h>#include <linux/tty.h>#include <linux/types.h>#include <asm/atomic.h>#include <asm/io.h>#include <asm/war.h>#include <asm/sibyte/sb1250.h>#include <asm/sibyte/sb1250_uart.h>#include <asm/sibyte/swarm.h>#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)#include <asm/sibyte/bcm1480_regs.h>#include <asm/sibyte/bcm1480_int.h>#define SBD_CHANREGS(line)	A_BCM1480_DUART_CHANREG((line), 0)#define SBD_CTRLREGS(line)	A_BCM1480_DUART_CTRLREG((line), 0)#define SBD_INT(line)		(K_BCM1480_INT_UART_0 + (line))#define DUART_CHANREG_SPACING	BCM1480_DUART_CHANREG_SPACING#define R_DUART_IMRREG(line)	R_BCM1480_DUART_IMRREG(line)#define R_DUART_INCHREG(line)	R_BCM1480_DUART_INCHREG(line)#define R_DUART_ISRREG(line)	R_BCM1480_DUART_ISRREG(line)#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)#include <asm/sibyte/sb1250_regs.h>#include <asm/sibyte/sb1250_int.h>#define SBD_CHANREGS(line)	A_DUART_CHANREG((line), 0)#define SBD_CTRLREGS(line)	A_DUART_CTRLREG(0)#define SBD_INT(line)		(K_INT_UART_0 + (line))#else#error invalid SB1250 UART configuration#endifMODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");MODULE_DESCRIPTION("BCM1xxx on-chip DUART serial driver");MODULE_LICENSE("GPL");#define DUART_MAX_CHIP 2#define DUART_MAX_SIDE 2/* * Per-port state. */struct sbd_port {	struct sbd_duart	*duart;	struct uart_port	port;	unsigned char __iomem	*memctrl;	int			tx_stopped;	int			initialised;};/* * Per-DUART state for the shared register space. */struct sbd_duart {	struct sbd_port		sport[2];	unsigned long		mapctrl;	atomic_t		map_guard;};#define to_sport(uport) container_of(uport, struct sbd_port, port)static struct sbd_duart sbd_duarts[DUART_MAX_CHIP];/* * Reading and writing SB1250 DUART registers. * * There are three register spaces: two per-channel ones and * a shared one.  We have to define accessors appropriately. * All registers are 64-bit and all but the Baud Rate Clock * registers only define 8 least significant bits.  There is * also a workaround to take into account.  Raw accessors use * the full register width, but cooked ones truncate it * intentionally so that the rest of the driver does not care. */static u64 __read_sbdchn(struct sbd_port *sport, int reg){	void __iomem *csr = sport->port.membase + reg;	return __raw_readq(csr);}static u64 __read_sbdshr(struct sbd_port *sport, int reg){	void __iomem *csr = sport->memctrl + reg;	return __raw_readq(csr);}static void __write_sbdchn(struct sbd_port *sport, int reg, u64 value){	void __iomem *csr = sport->port.membase + reg;	__raw_writeq(value, csr);}static void __write_sbdshr(struct sbd_port *sport, int reg, u64 value){	void __iomem *csr = sport->memctrl + reg;	__raw_writeq(value, csr);}/* * In bug 1956, we get glitches that can mess up uart registers.  This * "read-mode-reg after any register access" is an accepted workaround. */static void __war_sbd1956(struct sbd_port *sport){	__read_sbdchn(sport, R_DUART_MODE_REG_1);	__read_sbdchn(sport, R_DUART_MODE_REG_2);}static unsigned char read_sbdchn(struct sbd_port *sport, int reg){	unsigned char retval;	retval = __read_sbdchn(sport, reg);	if (SIBYTE_1956_WAR)		__war_sbd1956(sport);	return retval;}static unsigned char read_sbdshr(struct sbd_port *sport, int reg){	unsigned char retval;	retval = __read_sbdshr(sport, reg);	if (SIBYTE_1956_WAR)		__war_sbd1956(sport);	return retval;}static void write_sbdchn(struct sbd_port *sport, int reg, unsigned int value){	__write_sbdchn(sport, reg, value);	if (SIBYTE_1956_WAR)		__war_sbd1956(sport);}static void write_sbdshr(struct sbd_port *sport, int reg, unsigned int value){	__write_sbdshr(sport, reg, value);	if (SIBYTE_1956_WAR)		__war_sbd1956(sport);}static int sbd_receive_ready(struct sbd_port *sport){	return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_RX_RDY;}static int sbd_receive_drain(struct sbd_port *sport){	int loops = 10000;	while (sbd_receive_ready(sport) && loops--)		read_sbdchn(sport, R_DUART_RX_HOLD);	return loops;}static int __maybe_unused sbd_transmit_ready(struct sbd_port *sport){	return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_TX_RDY;}static int __maybe_unused sbd_transmit_drain(struct sbd_port *sport){	int loops = 10000;	while (!sbd_transmit_ready(sport) && loops--)		udelay(2);	return loops;}static int sbd_transmit_empty(struct sbd_port *sport){	return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_TX_EMT;}static int sbd_line_drain(struct sbd_port *sport){	int loops = 10000;	while (!sbd_transmit_empty(sport) && loops--)		udelay(2);	return loops;}static unsigned int sbd_tx_empty(struct uart_port *uport){	struct sbd_port *sport = to_sport(uport);	return sbd_transmit_empty(sport) ? TIOCSER_TEMT : 0;}static unsigned int sbd_get_mctrl(struct uart_port *uport){	struct sbd_port *sport = to_sport(uport);	unsigned int mctrl, status;	status = read_sbdshr(sport, R_DUART_IN_PORT);	status >>= (uport->line) % 2;	mctrl = (!(status & M_DUART_IN_PIN0_VAL) ? TIOCM_CTS : 0) |		(!(status & M_DUART_IN_PIN4_VAL) ? TIOCM_CAR : 0) |		(!(status & M_DUART_RIN0_PIN) ? TIOCM_RNG : 0) |		(!(status & M_DUART_IN_PIN2_VAL) ? TIOCM_DSR : 0);	return mctrl;}static void sbd_set_mctrl(struct uart_port *uport, unsigned int mctrl){	struct sbd_port *sport = to_sport(uport);	unsigned int clr = 0, set = 0, mode2;	if (mctrl & TIOCM_DTR)		set |= M_DUART_SET_OPR2;	else		clr |= M_DUART_CLR_OPR2;	if (mctrl & TIOCM_RTS)		set |= M_DUART_SET_OPR0;	else		clr |= M_DUART_CLR_OPR0;	clr <<= (uport->line) % 2;	set <<= (uport->line) % 2;	mode2 = read_sbdchn(sport, R_DUART_MODE_REG_2);	mode2 &= ~M_DUART_CHAN_MODE;	if (mctrl & TIOCM_LOOP)		mode2 |= V_DUART_CHAN_MODE_LCL_LOOP;	else		mode2 |= V_DUART_CHAN_MODE_NORMAL;	write_sbdshr(sport, R_DUART_CLEAR_OPR, clr);	write_sbdshr(sport, R_DUART_SET_OPR, set);	write_sbdchn(sport, R_DUART_MODE_REG_2, mode2);}static void sbd_stop_tx(struct uart_port *uport){	struct sbd_port *sport = to_sport(uport);	write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS);	sport->tx_stopped = 1;};static void sbd_start_tx(struct uart_port *uport){	struct sbd_port *sport = to_sport(uport);	unsigned int mask;	/* Enable tx interrupts.  */	mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2));	mask |= M_DUART_IMR_TX;	write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask);	/* Go!, go!, go!...  */	write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN);	sport->tx_stopped = 0;};static void sbd_stop_rx(struct uart_port *uport){	struct sbd_port *sport = to_sport(uport);	write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), 0);};static void sbd_enable_ms(struct uart_port *uport){	struct sbd_port *sport = to_sport(uport);	write_sbdchn(sport, R_DUART_AUXCTL_X,		     M_DUART_CIN_CHNG_ENA | M_DUART_CTS_CHNG_ENA);}static void sbd_break_ctl(struct uart_port *uport, int break_state){	struct sbd_port *sport = to_sport(uport);	if (break_state == -1)		write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_START_BREAK);	else		write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_STOP_BREAK);}static void sbd_receive_chars(struct sbd_port *sport){	struct uart_port *uport = &sport->port;	struct uart_icount *icount;	unsigned int status, ch, flag;	int count;	for (count = 16; count; count--) {		status = read_sbdchn(sport, R_DUART_STATUS);		if (!(status & M_DUART_RX_RDY))			break;		ch = read_sbdchn(sport, R_DUART_RX_HOLD);		flag = TTY_NORMAL;		icount = &uport->icount;		icount->rx++;		if (unlikely(status &			     (M_DUART_RCVD_BRK | M_DUART_FRM_ERR |			      M_DUART_PARITY_ERR | M_DUART_OVRUN_ERR))) {			if (status & M_DUART_RCVD_BRK) {				icount->brk++;				if (uart_handle_break(uport))					continue;			} else if (status & M_DUART_FRM_ERR)				icount->frame++;			else if (status & M_DUART_PARITY_ERR)				icount->parity++;			if (status & M_DUART_OVRUN_ERR)				icount->overrun++;			status &= uport->read_status_mask;			if (status & M_DUART_RCVD_BRK)				flag = TTY_BREAK;			else if (status & M_DUART_FRM_ERR)				flag = TTY_FRAME;			else if (status & M_DUART_PARITY_ERR)				flag = TTY_PARITY;		}		if (uart_handle_sysrq_char(uport, ch))			continue;		uart_insert_char(uport, status, M_DUART_OVRUN_ERR, ch, flag);	}	tty_flip_buffer_push(uport->info->tty);}static void sbd_transmit_chars(struct sbd_port *sport){	struct uart_port *uport = &sport->port;	struct circ_buf *xmit = &sport->port.info->xmit;	unsigned int mask;	int stop_tx;	/* XON/XOFF chars.  */	if (sport->port.x_char) {		write_sbdchn(sport, R_DUART_TX_HOLD, sport->port.x_char);		sport->port.icount.tx++;		sport->port.x_char = 0;		return;	}	/* If nothing to do or stopped or hardware stopped.  */	stop_tx = (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port));	/* Send char.  */	if (!stop_tx) {		write_sbdchn(sport, R_DUART_TX_HOLD, xmit->buf[xmit->tail]);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		sport->port.icount.tx++;		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)			uart_write_wakeup(&sport->port);	}	/* Are we are done?  */	if (stop_tx || uart_circ_empty(xmit)) {		/* Disable tx interrupts.  */		mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2));		mask &= ~M_DUART_IMR_TX;		write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask);	}}static void sbd_status_handle(struct sbd_port *sport){	struct uart_port *uport = &sport->port;	unsigned int delta;	delta = read_sbdshr(sport, R_DUART_INCHREG((uport->line) % 2));	delta >>= (uport->line) % 2;	if (delta & (M_DUART_IN_PIN0_VAL << S_DUART_IN_PIN_CHNG))		uart_handle_cts_change(uport, !(delta & M_DUART_IN_PIN0_VAL));	if (delta & (M_DUART_IN_PIN2_VAL << S_DUART_IN_PIN_CHNG))		uport->icount.dsr++;	if (delta & ((M_DUART_IN_PIN2_VAL | M_DUART_IN_PIN0_VAL) <<		     S_DUART_IN_PIN_CHNG))		wake_up_interruptible(&uport->info->delta_msr_wait);}static irqreturn_t sbd_interrupt(int irq, void *dev_id){	struct sbd_port *sport = dev_id;	struct uart_port *uport = &sport->port;	irqreturn_t status = IRQ_NONE;	unsigned int intstat;	int count;	for (count = 16; count; count--) {		intstat = read_sbdshr(sport,				      R_DUART_ISRREG((uport->line) % 2));		intstat &= read_sbdshr(sport,				       R_DUART_IMRREG((uport->line) % 2));		intstat &= M_DUART_ISR_ALL;		if (!intstat)			break;		if (intstat & M_DUART_ISR_RX)			sbd_receive_chars(sport);		if (intstat & M_DUART_ISR_IN)			sbd_status_handle(sport);		if (intstat & M_DUART_ISR_TX)			sbd_transmit_chars(sport);		status = IRQ_HANDLED;	}	return status;}static int sbd_startup(struct uart_port *uport){	struct sbd_port *sport = to_sport(uport);	unsigned int mode1;	int ret;	ret = request_irq(sport->port.irq, sbd_interrupt,			  IRQF_SHARED, "sb1250-duart", sport);	if (ret)		return ret;	/* Clear the receive FIFO.  */	sbd_receive_drain(sport);

⌨️ 快捷键说明

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