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

📄 uart.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  UART driver for MPC860 CPM SCC or SMC *  Copyright (c) 1997 Dan Malek (dmalek@jlc.net) * * I used the serial.c driver as the framework for this driver. * Give credit to those guys. * The original code was written for the MBX860 board.  I tried to make * it generic, but there may be some assumptions in the structures that * have to be fixed later. * To save porting time, I did not bother to change any object names * that are not accessed outside of this file. * It still needs lots of work........When it was easy, I included code * to support the SCCs, but this has never been tested, nor is it complete. * Only the SCCs support modem control, so that is not complete either. */#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial.h>#include <linux/serialP.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <asm/8xx_immap.h>#include <asm/mpc8xx.h>#include <asm/commproc.h>#ifdef CONFIG_MAGIC_SYSRQ#include <linux/sysrq.h>#endif#ifdef CONFIG_KGDB#include <asm/kgdb.h>#endif#ifdef CONFIG_SERIAL_CONSOLE#include <linux/console.h>/* this defines the index into rs_table for the port to use*/# ifndef CONFIG_SERIAL_CONSOLE_PORT#  ifdef CONFIG_SCC3_ENET#   ifdef CONFIG_CONS_SMC2#    define CONFIG_SERIAL_CONSOLE_PORT	0	/* Console on SMC2 is 1st port */#   else#    error "Can't use SMC1 for console with Ethernet on SCC3"#   endif#  else	/* ! CONFIG_SCC3_ENET */#   ifdef CONFIG_CONS_SMC2			/* Console on SMC2 */#    define CONFIG_SERIAL_CONSOLE_PORT	1#   else					/* Console on SMC1 */#    define CONFIG_SERIAL_CONSOLE_PORT	0#   endif /* CONFIG_CONS_SMC2 */#  endif  /* CONFIG_SCC3_ENET */# endif	  /* CONFIG_SERIAL_CONSOLE_PORT */#endif	  /* CONFIG_SERIAL_CONSOLE */#if 0/* SCC2 for console*/#undef CONFIG_SERIAL_CONSOLE_PORT#define CONFIG_SERIAL_CONSOLE_PORT	2#endif#define TX_WAKEUP	ASYNC_SHARE_IRQstatic char *serial_name = "CPM UART driver";static char *serial_version = "0.03";static DECLARE_TASK_QUEUE(tq_serial);static struct tty_driver *serial_driver;static int serial_console_setup(struct console *co, char *options);static void serial_console_write(struct console *c, const char *s,				unsigned count);static struct tty_driver *serial_console_device(struct console *c, int *index)#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)static unsigned long break_pressed; /* break, really ... */#endif/* * Serial driver configuration section.  Here are the various options: */#define SERIAL_PARANOIA_CHECK#define CONFIG_SERIAL_NOPAUSE_IO#define SERIAL_DO_RESTART/* Set of debugging defines */#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT#define _INLINE_ inline#define DBG_CNT(s)/* We overload some of the items in the data structure to meet our * needs.  For example, the port address is the CPM parameter ram * offset for the SCC or SMC.  The maximum number of ports is 4 SCCs and * 2 SMCs.  The "hub6" field is used to indicate the channel number, with * a flag indicating SCC or SMC, and the number is used as an index into * the CPM parameter area for this device. * The "type" field is currently set to 0, for PORT_UNKNOWN.  It is * not currently used.  I should probably use it to indicate the port * type of SMC or SCC. * The SMCs do not support any modem control signals. */#define smc_scc_num	hub6#define NUM_IS_SCC	((int)0x00010000)#define PORT_NUM(P)	((P) & 0x0000ffff)/* The choice of serial port to use for KGDB.  If the system has * two ports, you can use one for console and one for KGDB (which * doesn't make sense to me, but people asked for it). */#ifdef CONFIG_KGDB_TTYS1#define KGDB_SER_IDX 1		/* SCC2/SMC2 */#else#define KGDB_SER_IDX 0		/* SCC1/SMC1 */#endif/* Processors other than the 860 only get SMCs configured by default. * Either they don't have SCCs or they are allocated somewhere else. * Of course, there are now 860s without some SCCs, so we will need to * address that someday. * The Embedded Planet Multimedia I/O cards use TDM interfaces to the * stereo codec parts, and we use SMC2 to help support that. */static struct serial_state rs_table[] = {	/* UART CLK   PORT          IRQ      FLAGS  NUM   */#ifndef CONFIG_SCC3_ENET	/* SMC1 not usable with Ethernet on SCC3 */  	{ 0,     0, PROFF_SMC1, CPMVEC_SMC1,   0,    0 },    /* SMC1 ttyS0 */#endif#if !defined(CONFIG_USB_MPC8xx) && !defined(CONFIG_USB_CLIENT_MPC8xx)# ifdef CONFIG_SMC2_UART  	{ 0,     0, PROFF_SMC2, CPMVEC_SMC2,   0,    1 },    /* SMC2 ttyS1 */# endif# ifdef CONFIG_USE_SCC_IO  	{ 0,     0, PROFF_SCC2, CPMVEC_SCC2,   0,    (NUM_IS_SCC | 1) },    /* SCC2 ttyS2 */  	{ 0,     0, PROFF_SCC3, CPMVEC_SCC3,   0,    (NUM_IS_SCC | 2) },    /* SCC3 ttyS3 */# endif  #else /* CONFIG_USB_xxx */# ifdef CONFIG_USE_SCC_IO  	{ 0,     0, PROFF_SCC3, CPMVEC_SCC3,   0,    (NUM_IS_SCC | 2) },    /* SCC3 ttyS3 */# endif#endif	/* CONFIG_USB_xxx */};#define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))/* The number of buffer descriptors and their sizes.*/#define RX_NUM_FIFO	4#define RX_BUF_SIZE	32#define TX_NUM_FIFO	4#define TX_BUF_SIZE	32/* The async_struct in serial.h does not really give us what we * need, so define our own here. */typedef struct serial_info {	int			magic;	int			flags;	struct serial_state	*state;	struct tty_struct 	*tty;	int			read_status_mask;	int			ignore_status_mask;	int			timeout;	int			line;	int			x_char;	/* xon/xoff character */	int			close_delay;	unsigned short		closing_wait;	unsigned short		closing_wait2;	unsigned long		event;	unsigned long		last_active;	int			blocked_open; /* # of blocked opens */	struct tq_struct	tqueue;	struct tq_struct	tqueue_hangup;	wait_queue_head_t	open_wait;	wait_queue_head_t	close_wait;	/* CPM Buffer Descriptor pointers.	*/	cbd_t			*rx_bd_base;	cbd_t			*rx_cur;	cbd_t			*tx_bd_base;	cbd_t			*tx_cur;	/* Virtual addresses for the FIFOs because we can't __va() a	 * physical address anymore.	 */	 unsigned char		*rx_va_base;	 unsigned char		*tx_va_base;} ser_info_t;static struct console sercons = {	.name =		"ttyS",	.write =	serial_console_write,	.device =	serial_console_device,	.setup =	serial_console_setup,	.flags =	CON_PRINTBUFFER,	.index =	CONFIG_SERIAL_CONSOLE_PORT,};static void change_speed(ser_info_t *info);static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout);static inline int serial_paranoia_check(ser_info_t *info,					char *name, const char *routine){#ifdef SERIAL_PARANOIA_CHECK	static const char *badmagic =		"Warning: bad magic number for serial struct (%s) in %s\n";	static const char *badinfo =		"Warning: null async_struct for (%s) in %s\n";	if (!info) {		printk(badinfo, name, routine);		return 1;	}	if (info->magic != SERIAL_MAGIC) {		printk(badmagic, name, routine);		return 1;	}#endif	return 0;}/* * This is used to figure out the divisor speeds and the timeouts, * indexed by the termio value.  The generic CPM functions are responsible * for setting and assigning baud rate generators for us. */static int baud_table[] = {	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };/* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void rs_8xx_stop(struct tty_struct *tty){	ser_info_t *info = (ser_info_t *)tty->driver_data;	int	idx;	unsigned long flags;	volatile scc_t	*sccp;	volatile smc_t	*smcp;	if (serial_paranoia_check(info, tty->name, "rs_stop"))		return;	save_flags(flags); cli();	idx = PORT_NUM(info->state->smc_scc_num);	if (info->state->smc_scc_num & NUM_IS_SCC) {		sccp = &cpmp->cp_scc[idx];		sccp->scc_sccm &= ~UART_SCCM_TX;	}	else {		smcp = &cpmp->cp_smc[idx];		smcp->smc_smcm &= ~SMCM_TX;	}	restore_flags(flags);}static void rs_8xx_start(struct tty_struct *tty){	ser_info_t *info = (ser_info_t *)tty->driver_data;	int	idx;	unsigned long flags;	volatile scc_t	*sccp;	volatile smc_t	*smcp;	if (serial_paranoia_check(info, tty->name, "rs_stop"))		return;	idx = PORT_NUM(info->state->smc_scc_num);	save_flags(flags); cli();	if (info->state->smc_scc_num & NUM_IS_SCC) {		sccp = &cpmp->cp_scc[idx];		sccp->scc_sccm |= UART_SCCM_TX;	}	else {		smcp = &cpmp->cp_smc[idx];		smcp->smc_smcm |= SMCM_TX;	}	restore_flags(flags);}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines.  All of the following * subroutines are declared as inline and are folded into * rs_interrupt().  They were separated out for readability's sake. * * Note: rs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off.  People who may want to modify * rs_interrupt() should try to keep the interrupt handler as fast as * possible.  After you are done making modifications, it is not a bad * idea to do: * * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c * * and look at the resulting assemble code in serial.s. * * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93 * ----------------------------------------------------------------------- *//* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static _INLINE_ void rs_sched_event(ser_info_t *info,				  int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_serial);	mark_bh(SERIAL_BH);}static _INLINE_ void receive_chars(ser_info_t *info, struct pt_regs *regs){	struct tty_struct *tty = info->tty;	unsigned char ch, *cp;	/*int	ignored = 0;*/	int	i;	ushort	status;	struct	async_icount *icount;	volatile cbd_t	*bdp;	icount = &info->state->icount;	/* Just loop through the closed BDs and copy the characters into	 * the buffer.	 */	bdp = info->rx_cur;	for (;;) {		if (bdp->cbd_sc & BD_SC_EMPTY)	/* If this one is empty */			break;			/*   we are all done */		/* The read status mask tell us what we should do with		 * incoming characters, especially if errors occur.		 * One special case is the use of BD_SC_EMPTY.  If		 * this is not set, we are supposed to be ignoring		 * inputs.  In this case, just mark the buffer empty and		 * continue.		if (!(info->read_status_mask & BD_SC_EMPTY)) {			bdp->cbd_sc |= BD_SC_EMPTY;			bdp->cbd_sc &=				~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);			if (bdp->cbd_sc & BD_SC_WRAP)				bdp = info->rx_bd_base;			else				bdp++;			continue;		}		 */		/* Get the number of characters and the buffer pointer.		*/		i = bdp->cbd_datlen;		cp = info->rx_va_base + ((bdp - info->rx_bd_base) * RX_BUF_SIZE);		status = bdp->cbd_sc;#ifdef CONFIG_KGDB		if (info->state->smc_scc_num == KGDB_SER_IDX) {			if (*cp == 0x03 || *cp == '$')				breakpoint();			return;		}#endif		/* Check to see if there is room in the tty buffer for		 * the characters in our BD buffer.  If not, we exit		 * now, leaving the BD with the characters.  We'll pick		 * them up again on the next receive interrupt (which could		 * be a timeout).		 */		if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE)			break;		while (i-- > 0) {			ch = *cp++;			*tty->flip.char_buf_ptr = ch;			icount->rx++;#ifdef SERIAL_DEBUG_INTR			printk("DR%02x:%02x...", ch, status);#endif			*tty->flip.flag_buf_ptr = 0;			if (status & (BD_SC_BR | BD_SC_FR |				       BD_SC_PR | BD_SC_OV)) {				/*				 * For statistics only				 */				if (status & BD_SC_BR)					icount->brk++;				else if (status & BD_SC_PR)					icount->parity++;				else if (status & BD_SC_FR)					icount->frame++;				if (status & BD_SC_OV)					icount->overrun++;				/*				 * Now check to see if character should be				 * ignored, and mask off conditions which				 * should be ignored.				if (status & info->ignore_status_mask) {					if (++ignored > 100)						break;					continue;				}				 */				status &= info->read_status_mask;				if (status & (BD_SC_BR)) {#ifdef SERIAL_DEBUG_INTR					printk("handling break....");#endif					*tty->flip.flag_buf_ptr = TTY_BREAK;					if (info->flags & ASYNC_SAK)						do_SAK(tty);				} else if (status & BD_SC_PR)					*tty->flip.flag_buf_ptr = TTY_PARITY;				else if (status & BD_SC_FR)					*tty->flip.flag_buf_ptr = TTY_FRAME;				if (status & BD_SC_OV) {					/*					 * Overrun is special, since it's					 * reported immediately, and doesn't					 * affect the current character					 */					if (tty->flip.count < TTY_FLIPBUF_SIZE) {						tty->flip.count++;						tty->flip.flag_buf_ptr++;						tty->flip.char_buf_ptr++;						*tty->flip.flag_buf_ptr =								TTY_OVERRUN;					}				}			}#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)			if (break_pressed && info->line == sercons.index) {				if (ch != 0 && time_before(jiffies,							break_pressed + HZ*5)) {					handle_sysrq(ch, regs, NULL);					break_pressed = 0;					goto ignore_char;				} else					break_pressed = 0;			}#endif			if (tty->flip.count >= TTY_FLIPBUF_SIZE)				break;			tty->flip.flag_buf_ptr++;			tty->flip.char_buf_ptr++;			tty->flip.count++;		}#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)	ignore_char:#endif		/* This BD is ready to be used again.  Clear status.		 * Get next BD.		 */		bdp->cbd_sc |= BD_SC_EMPTY;		bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);		if (bdp->cbd_sc & BD_SC_WRAP)			bdp = info->rx_bd_base;		else			bdp++;	}	info->rx_cur = (cbd_t *)bdp;	queue_task(&tty->flip.tqueue, &tq_timer);}static _INLINE_ void receive_break(ser_info_t *info, struct pt_regs *regs){	struct tty_struct *tty = info->tty;	info->state->icount.brk++;#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)	if (info->line == sercons.index) {		if (!break_pressed) {			break_pressed = jiffies;			return;		} else			break_pressed = 0;	}#endif	/* Check to see if there is room in the tty buffer for

⌨️ 快捷键说明

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