vme_scc.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,057 行 · 第 1/2 页

C
1,057
字号
		else if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped ||				port->gs.tty->hw_stopped)			break;		else {			SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]);			port->gs.xmit_tail = port->gs.xmit_tail & (SERIAL_XMIT_SIZE-1);			if (--port->gs.xmit_cnt <= 0)				break;		}	}	if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped ||			port->gs.tty->hw_stopped) {		/* disable tx interrupts */		SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);		SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);   /* disable tx_int on next tx underrun? */		port->gs.flags &= ~GS_TX_INTEN;	}	if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars)		tty_wakeup(port->gs.tty);	SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);	return IRQ_HANDLED;}static irqreturn_t scc_stat_int(int irq, void *data, struct pt_regs *fp){	struct scc_port *port = data;	unsigned channel = port->channel;	unsigned char	last_sr, sr, changed;	SCC_ACCESS_INIT(port);	last_sr = scc_last_status_reg[channel];	sr = scc_last_status_reg[channel] = SCCread_NB(STATUS_REG);	changed = last_sr ^ sr;	if (changed & SR_DCD) {		port->c_dcd = !!(sr & SR_DCD);		if (!(port->gs.flags & ASYNC_CHECK_CD))			;	/* Don't report DCD changes */		else if (port->c_dcd) {			wake_up_interruptible(&port->gs.open_wait);		}		else {			if (port->gs.tty)				tty_hangup (port->gs.tty);		}	}	SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET);	SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);	return IRQ_HANDLED;}/*--------------------------------------------------------------------------- * generic_serial.c callback funtions *--------------------------------------------------------------------------*/static void scc_disable_tx_interrupts(void *ptr){	struct scc_port *port = ptr;	unsigned long	flags;	SCC_ACCESS_INIT(port);	local_irq_save(flags);	SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);	port->gs.flags &= ~GS_TX_INTEN;	local_irq_restore(flags);}static void scc_enable_tx_interrupts(void *ptr){	struct scc_port *port = ptr;	unsigned long	flags;	SCC_ACCESS_INIT(port);	local_irq_save(flags);	SCCmod(INT_AND_DMA_REG, 0xff, IDR_TX_INT_ENAB);	/* restart the transmitter */	scc_tx_int (0, port, 0);	local_irq_restore(flags);}static void scc_disable_rx_interrupts(void *ptr){	struct scc_port *port = ptr;	unsigned long	flags;	SCC_ACCESS_INIT(port);	local_irq_save(flags);	SCCmod(INT_AND_DMA_REG,	    ~(IDR_RX_INT_MASK|IDR_PARERR_AS_SPCOND|IDR_EXTSTAT_INT_ENAB), 0);	local_irq_restore(flags);}static void scc_enable_rx_interrupts(void *ptr){	struct scc_port *port = ptr;	unsigned long	flags;	SCC_ACCESS_INIT(port);	local_irq_save(flags);	SCCmod(INT_AND_DMA_REG, 0xff,		IDR_EXTSTAT_INT_ENAB|IDR_PARERR_AS_SPCOND|IDR_RX_INT_ALL);	local_irq_restore(flags);}static int scc_get_CD(void *ptr){	struct scc_port *port = ptr;	unsigned channel = port->channel;	return !!(scc_last_status_reg[channel] & SR_DCD);}static void scc_shutdown_port(void *ptr){	struct scc_port *port = ptr;	port->gs.flags &= ~ GS_ACTIVE;	if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {		scc_setsignals (port, 0, 0);	}}static int scc_set_real_termios (void *ptr){	/* the SCC has char sizes 5,7,6,8 in that order! */	static int chsize_map[4] = { 0, 2, 1, 3 };	unsigned cflag, baud, chsize, channel, brgval = 0;	unsigned long flags;	struct scc_port *port = ptr;	SCC_ACCESS_INIT(port);	if (!port->gs.tty || !port->gs.tty->termios) return 0;	channel = port->channel;	if (channel == CHANNEL_A)		return 0;		/* Settings controlled by boot PROM */	cflag  = port->gs.tty->termios->c_cflag;	baud = port->gs.baud;	chsize = (cflag & CSIZE) >> 4;	if (baud == 0) {		/* speed == 0 -> drop DTR */		local_irq_save(flags);		SCCmod(TX_CTRL_REG, ~TCR_DTR, 0);		local_irq_restore(flags);		return 0;	}	else if ((MACH_IS_MVME16x && (baud < 50 || baud > 38400)) ||		 (MACH_IS_MVME147 && (baud < 50 || baud > 19200)) ||		 (MACH_IS_BVME6000 &&(baud < 50 || baud > 76800))) {		printk(KERN_NOTICE "SCC: Bad speed requested, %d\n", baud);		return 0;	}	if (cflag & CLOCAL)		port->gs.flags &= ~ASYNC_CHECK_CD;	else		port->gs.flags |= ASYNC_CHECK_CD;#ifdef CONFIG_MVME147_SCC	if (MACH_IS_MVME147)		brgval = (M147_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2;#endif#ifdef CONFIG_MVME162_SCC	if (MACH_IS_MVME16x)		brgval = (MVME_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2;#endif#ifdef CONFIG_BVME6000_SCC	if (MACH_IS_BVME6000)		brgval = (BVME_SCC_RTxC + baud/2) / (16 * 2 * baud) - 2;#endif	/* Now we have all parameters and can go to set them: */	local_irq_save(flags);	/* receiver's character size and auto-enables */	SCCmod(RX_CTRL_REG, ~(RCR_CHSIZE_MASK|RCR_AUTO_ENAB_MODE),			(chsize_map[chsize] << 6) |			((cflag & CRTSCTS) ? RCR_AUTO_ENAB_MODE : 0));	/* parity and stop bits (both, Tx and Rx), clock mode never changes */	SCCmod (AUX1_CTRL_REG,		~(A1CR_PARITY_MASK | A1CR_MODE_MASK),		((cflag & PARENB		  ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN)		  : A1CR_PARITY_NONE)		 | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1)));	/* sender's character size, set DTR for valid baud rate */	SCCmod(TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR);	/* clock sources never change */	/* disable BRG before changing the value */	SCCmod(DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0);	/* BRG value */	SCCwrite(TIMER_LOW_REG, brgval & 0xff);	SCCwrite(TIMER_HIGH_REG, (brgval >> 8) & 0xff);	/* BRG enable, and clock source never changes */	SCCmod(DPLL_CTRL_REG, 0xff, DCR_BRG_ENAB);	local_irq_restore(flags);	return 0;}static int scc_chars_in_buffer (void *ptr){	struct scc_port *port = ptr;	SCC_ACCESS_INIT(port);	return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0  : 1;}/* Comment taken from sx.c (2.4.0):   I haven't the foggiest why the decrement use count has to happen   here. The whole linux serial drivers stuff needs to be redesigned.   My guess is that this is a hack to minimize the impact of a bug   elsewhere. Thinking about it some more. (try it sometime) Try   running minicom on a serial port that is driven by a modularized   driver. Have the modem hangup. Then remove the driver module. Then   exit minicom.  I expect an "oops".  -- REW */static void scc_hungup(void *ptr){	scc_disable_tx_interrupts(ptr);	scc_disable_rx_interrupts(ptr);}static void scc_close(void *ptr){	scc_disable_tx_interrupts(ptr);	scc_disable_rx_interrupts(ptr);}/*--------------------------------------------------------------------------- * Internal support functions *--------------------------------------------------------------------------*/static void scc_setsignals(struct scc_port *port, int dtr, int rts){	unsigned long flags;	unsigned char t;	SCC_ACCESS_INIT(port);	local_irq_save(flags);	t = SCCread(TX_CTRL_REG);	if (dtr >= 0) t = dtr? (t | TCR_DTR): (t & ~TCR_DTR);	if (rts >= 0) t = rts? (t | TCR_RTS): (t & ~TCR_RTS);	SCCwrite(TX_CTRL_REG, t);	local_irq_restore(flags);}static void scc_send_xchar(struct tty_struct *tty, char ch){	struct scc_port *port = (struct scc_port *)tty->driver_data;	port->x_char = ch;	if (ch)		scc_enable_tx_interrupts(port);}/*--------------------------------------------------------------------------- * Driver entrypoints referenced from above *--------------------------------------------------------------------------*/static int scc_open (struct tty_struct * tty, struct file * filp){	int line = tty->index;	int retval;	struct scc_port *port = &scc_ports[line];	int i, channel = port->channel;	unsigned long	flags;	SCC_ACCESS_INIT(port);#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_MVME147_SCC)	static const struct {		unsigned reg, val;	} mvme_init_tab[] = {		/* Values for MVME162 and MVME147 */		/* no parity, 1 stop bit, async, 1:16 */		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },		/* parity error is special cond, ints disabled, no DMA */		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },		/* Rx 8 bits/char, no auto enable, Rx off */		{ RX_CTRL_REG, RCR_CHSIZE_8 },		/* DTR off, Tx 8 bits/char, RTS off, Tx off */		{ TX_CTRL_REG, TCR_CHSIZE_8 },		/* special features off */		{ AUX2_CTRL_REG, 0 },		{ CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG },		{ DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK },		/* Start Rx */		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },		/* Start Tx */		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },		/* Ext/Stat ints: DCD only */		{ INT_CTRL_REG, ICR_ENAB_DCD_INT },		/* Reset Ext/Stat ints */		{ COMMAND_REG, CR_EXTSTAT_RESET },		/* ...again */		{ COMMAND_REG, CR_EXTSTAT_RESET },	};#endif#if defined(CONFIG_BVME6000_SCC)	static const struct {		unsigned reg, val;	} bvme_init_tab[] = {		/* Values for BVME6000 */		/* no parity, 1 stop bit, async, 1:16 */		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },		/* parity error is special cond, ints disabled, no DMA */		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },		/* Rx 8 bits/char, no auto enable, Rx off */		{ RX_CTRL_REG, RCR_CHSIZE_8 },		/* DTR off, Tx 8 bits/char, RTS off, Tx off */		{ TX_CTRL_REG, TCR_CHSIZE_8 },		/* special features off */		{ AUX2_CTRL_REG, 0 },		{ CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG },		{ DPLL_CTRL_REG, DCR_BRG_ENAB },		/* Start Rx */		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },		/* Start Tx */		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },		/* Ext/Stat ints: DCD only */		{ INT_CTRL_REG, ICR_ENAB_DCD_INT },		/* Reset Ext/Stat ints */		{ COMMAND_REG, CR_EXTSTAT_RESET },		/* ...again */		{ COMMAND_REG, CR_EXTSTAT_RESET },	};#endif	if (!(port->gs.flags & ASYNC_INITIALIZED)) {		local_irq_save(flags);#if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC)		if (MACH_IS_MVME147 || MACH_IS_MVME16x) {			for (i=0; i<sizeof(mvme_init_tab)/sizeof(*mvme_init_tab); ++i)				SCCwrite(mvme_init_tab[i].reg, mvme_init_tab[i].val);		}#endif#if defined(CONFIG_BVME6000_SCC)		if (MACH_IS_BVME6000) {			for (i=0; i<sizeof(bvme_init_tab)/sizeof(*bvme_init_tab); ++i)				SCCwrite(bvme_init_tab[i].reg, bvme_init_tab[i].val);		}#endif		/* remember status register for detection of DCD and CTS changes */		scc_last_status_reg[channel] = SCCread(STATUS_REG);		port->c_dcd = 0;	/* Prevent initial 1->0 interrupt */		scc_setsignals (port, 1,1);		local_irq_restore(flags);	}	tty->driver_data = port;	port->gs.tty = tty;	port->gs.count++;	retval = gs_init_port(&port->gs);	if (retval) {		port->gs.count--;		return retval;	}	port->gs.flags |= GS_ACTIVE;	retval = gs_block_til_ready(port, filp);	if (retval) {		port->gs.count--;		return retval;	}	port->c_dcd = scc_get_CD (port);	scc_enable_rx_interrupts(port);	return 0;}static void scc_throttle (struct tty_struct * tty){	struct scc_port *port = (struct scc_port *)tty->driver_data;	unsigned long	flags;	SCC_ACCESS_INIT(port);	if (tty->termios->c_cflag & CRTSCTS) {		local_irq_save(flags);		SCCmod(TX_CTRL_REG, ~TCR_RTS, 0);		local_irq_restore(flags);	}	if (I_IXOFF(tty))		scc_send_xchar(tty, STOP_CHAR(tty));}static void scc_unthrottle (struct tty_struct * tty){	struct scc_port *port = (struct scc_port *)tty->driver_data;	unsigned long	flags;	SCC_ACCESS_INIT(port);	if (tty->termios->c_cflag & CRTSCTS) {		local_irq_save(flags);		SCCmod(TX_CTRL_REG, 0xff, TCR_RTS);		local_irq_restore(flags);	}	if (I_IXOFF(tty))		scc_send_xchar(tty, START_CHAR(tty));}static int scc_ioctl(struct tty_struct *tty, struct file *file,		     unsigned int cmd, unsigned long arg){	return -ENOIOCTLCMD;}static void scc_break_ctl(struct tty_struct *tty, int break_state){	struct scc_port *port = (struct scc_port *)tty->driver_data;	unsigned long	flags;	SCC_ACCESS_INIT(port);	local_irq_save(flags);	SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, 			break_state ? TCR_SEND_BREAK : 0);	local_irq_restore(flags);}/*--------------------------------------------------------------------------- * Serial console stuff... *--------------------------------------------------------------------------*/#define scc_delay() do { __asm__ __volatile__ (" nop; nop"); } while (0)static void scc_ch_write (char ch){	volatile char *p = NULL;	#ifdef CONFIG_MVME147_SCC	if (MACH_IS_MVME147)		p = (volatile char *)M147_SCC_A_ADDR;#endif#ifdef CONFIG_MVME162_SCC	if (MACH_IS_MVME16x)		p = (volatile char *)MVME_SCC_A_ADDR;#endif#ifdef CONFIG_BVME6000_SCC	if (MACH_IS_BVME6000)		p = (volatile char *)BVME_SCC_A_ADDR;#endif	do {		scc_delay();	}	while (!(*p & 4));	scc_delay();	*p = 8;	scc_delay();	*p = ch;}/* The console must be locked when we get here. */static void scc_console_write (struct console *co, const char *str, unsigned count){	unsigned long	flags;	local_irq_save(flags);	while (count--)	{		if (*str == '\n')			scc_ch_write ('\r');		scc_ch_write (*str++);	}	local_irq_restore(flags);}static struct tty_driver *scc_console_device(struct console *c, int *index){	*index = c->index;	return scc_driver;}static int __init scc_console_setup(struct console *co, char *options){	return 0;}static struct console sercons = {	.name		= "ttyS",	.write		= scc_console_write,	.device		= scc_console_device,	.setup		= scc_console_setup,	.flags		= CON_PRINTBUFFER,	.index		= -1,};static int __init vme_scc_console_init(void){	if (vme_brdtype == VME_TYPE_MVME147 ||			vme_brdtype == VME_TYPE_MVME162 ||			vme_brdtype == VME_TYPE_MVME172 ||			vme_brdtype == VME_TYPE_BVME4000 ||			vme_brdtype == VME_TYPE_BVME6000)		register_console(&sercons);	return 0;}console_initcall(vme_scc_console_init);

⌨️ 快捷键说明

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