68328serial.c

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

C
1,628
字号
/* 68328serial.c: Serial port driver for 68328 microcontroller * * Copyright (C) 1995       David S. Miller    <davem@caip.rutgers.edu> * Copyright (C) 1998       Kenneth Albanowski <kjahds@kjahds.com> * Copyright (C) 1998, 1999 D. Jeff Dionne     <jeff@uclinux.org> * Copyright (C) 1999       Vladimir Gurevich  <vgurevic@cisco.com> * Copyright (C) 2002-2003  David McCullough   <davidm@snapgear.com> * Copyright (C) 2002       Greg Ungerer       <gerg@snapgear.com> * * VZ Support/Fixes             Evan Stawnyczy <e@lineo.ca> * Multiple UART support        Daniel Potts <danielp@cse.unsw.edu.au> * Power management support     Daniel Potts <danielp@cse.unsw.edu.au> * VZ Second Serial Port enable Phil Wilshire * 2.4/2.5 port                 David McCullough */#include <asm/dbg.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/config.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/kernel.h>#include <linux/console.h>#include <linux/reboot.h>#include <linux/keyboard.h>#include <linux/init.h>#include <linux/pm.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/delay.h>#include <asm/uaccess.h>/* (es) *//* note: perhaps we can murge these files, so that you can just * 	 define 1 of them, and they can sort that out for themselves */#if defined(CONFIG_M68EZ328)#include <asm/MC68EZ328.h>#else#if defined(CONFIG_M68VZ328)#include <asm/MC68VZ328.h>#else#include <asm/MC68328.h>#endif /* CONFIG_M68VZ328 */#endif /* CONFIG_M68EZ328 */#include "68328serial.h"/* Turn off usage of real serial interrupt code, to "support" Copilot */#ifdef CONFIG_XCOPILOT_BUGS#undef USE_INTS#else#define USE_INTS#endifstatic struct m68k_serial m68k_soft[NR_PORTS];struct m68k_serial *IRQ_ports[NR_IRQS];static unsigned int uart_irqs[NR_PORTS] = UART_IRQ_DEFNS;/* multiple ports are contiguous in memory */m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR;struct tty_struct m68k_ttys;struct m68k_serial *m68k_consinfo = 0;#define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */#ifdef CONFIG_CONSOLEextern wait_queue_head_t keypress_wait; #endifstruct tty_driver *serial_driver;/* serial subtype definitions */#define SERIAL_TYPE_NORMAL	1 /* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256/* Debugging... DEBUG_INTR is bad to use when one of the zs * lines is your console ;( */#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#define RS_ISR_PASS_LIMIT 256#define _INLINE_ inlinestatic void change_speed(struct m68k_serial *info);/* *	Setup for console. Argument comes from the boot command line. */#if defined(CONFIG_M68EZ328ADS) || defined(CONFIG_ALMA_ANS) || defined(CONFIG_DRAGONIXVZ)#define	CONSOLE_BAUD_RATE	115200#define	DEFAULT_CBAUD		B115200#else	/* (es) */	/* note: this is messy, but it works, again, perhaps defined somewhere else?*/	#ifdef CONFIG_M68VZ328	#define CONSOLE_BAUD_RATE	19200	#define DEFAULT_CBAUD		B19200	#endif	/* (/es) */#endif#ifndef CONSOLE_BAUD_RATE#define	CONSOLE_BAUD_RATE	9600#define	DEFAULT_CBAUD		B9600#endifstatic int m68328_console_initted = 0;static int m68328_console_baud    = CONSOLE_BAUD_RATE;static int m68328_console_cbaud   = DEFAULT_CBAUD;/* * tmp_buf is used as a temporary buffer by serial_write.  We need to * lock it in case the memcpy_fromfs 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 unsigned char tmp_buf[SERIAL_XMIT_SIZE]; /* This is cheating */DECLARE_MUTEX(tmp_buf_sem);static inline int serial_paranoia_check(struct m68k_serial *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 m68k_serial 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 */static int baud_table[] = {	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,	9600, 19200, 38400, 57600, 115200, 0 };#define BAUD_TABLE_SIZE (sizeof(baud_table)/sizeof(baud_table[0]))/* Sets or clears DTR/RTS on the requested line */static inline void m68k_rtsdtr(struct m68k_serial *ss, int set){	if (set) {		/* set the RTS/CTS line */	} else {		/* clear it */	}	return;}/* Utility routines */static inline int get_baud(struct m68k_serial *ss){	unsigned long result = 115200;	unsigned short int baud = uart_addr[ss->line].ubaud;	if (GET_FIELD(baud, UBAUD_PRESCALER) == 0x38) result = 38400;	result >>= GET_FIELD(baud, UBAUD_DIVIDE);	return result;}/* * ------------------------------------------------------------ * 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_stop(struct tty_struct *tty){	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;	m68328_uart *uart = &uart_addr[info->line];	unsigned long flags;	if (serial_paranoia_check(info, tty->name, "rs_stop"))		return;		save_flags(flags); cli();	uart->ustcnt &= ~USTCNT_TXEN;	restore_flags(flags);}static void rs_put_char(char ch){        int flags, loops = 0;        save_flags(flags); cli();	while (!(UTX & UTX_TX_AVAIL) && (loops < 1000)) {        	loops++;        	udelay(5);        }	UTX_TXDATA = ch;        udelay(5);        restore_flags(flags);}static void rs_start(struct tty_struct *tty){	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;	m68328_uart *uart = &uart_addr[info->line];	unsigned long flags;		if (serial_paranoia_check(info, tty->name, "rs_start"))		return;		save_flags(flags); cli();	if (info->xmit_cnt && info->xmit_buf && !(uart->ustcnt & USTCNT_TXEN)) {#ifdef USE_INTS		uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK;#else		uart->ustcnt |= USTCNT_TXEN;#endif	}	restore_flags(flags);}/* Drop into either the boot monitor or kadb upon receiving a break * from keyboard/console input. */static void batten_down_hatches(void){	/* Drop into the debugger */}static _INLINE_ void status_handle(struct m68k_serial *info, unsigned short status){#if 0	if(status & DCD) {		if((info->tty->termios->c_cflag & CRTSCTS) &&		   ((info->curregs[3] & AUTO_ENAB)==0)) {			info->curregs[3] |= AUTO_ENAB;			info->pendregs[3] |= AUTO_ENAB;			write_zsreg(info->m68k_channel, 3, info->curregs[3]);		}	} else {		if((info->curregs[3] & AUTO_ENAB)) {			info->curregs[3] &= ~AUTO_ENAB;			info->pendregs[3] &= ~AUTO_ENAB;			write_zsreg(info->m68k_channel, 3, info->curregs[3]);		}	}#endif	/* If this is console input and this is a	 * 'break asserted' status change interrupt	 * see if we can drop into the debugger	 */	if((status & URX_BREAK) && info->break_abort)		batten_down_hatches();	return;}static _INLINE_ void receive_chars(struct m68k_serial *info, struct pt_regs *regs, unsigned short rx){	struct tty_struct *tty = info->tty;	m68328_uart *uart = &uart_addr[info->line];	unsigned char ch;	/*	 * This do { } while() loop will get ALL chars out of Rx FIFO          */#ifndef CONFIG_XCOPILOT_BUGS	do {#endif			ch = GET_FIELD(rx, URX_RXDATA);			if(info->is_cons) {			if(URX_BREAK & rx) { /* whee, break received */				status_handle(info, rx);				return;#ifdef CONFIG_MAGIC_SYSRQ			} else if (ch == 0x10) { /* ^P */				show_state();				show_free_areas();				show_buffers();/*				show_net_buffers(); */				return;			} else if (ch == 0x12) { /* ^R */				machine_restart(NULL);				return;#endif /* CONFIG_MAGIC_SYSRQ */			}			/* It is a 'keyboard interrupt' ;-) */#ifdef CONFIG_CONSOLE			wake_up(&keypress_wait);#endif					}		if(!tty)			goto clear_and_exit;				/*		 * Make sure that we do not overflow the buffer		 */		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {			schedule_work(&tty->flip.work);			return;		}		if(rx & URX_PARITY_ERROR) {			*tty->flip.flag_buf_ptr++ = TTY_PARITY;			status_handle(info, rx);		} else if(rx & URX_OVRUN) {			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;			status_handle(info, rx);		} else if(rx & URX_FRAME_ERROR) {			*tty->flip.flag_buf_ptr++ = TTY_FRAME;			status_handle(info, rx);		} else {			*tty->flip.flag_buf_ptr++ = 0; /* XXX */		}                *tty->flip.char_buf_ptr++ = ch;		tty->flip.count++;#ifndef CONFIG_XCOPILOT_BUGS	} while((rx = uart->urx.w) & URX_DATA_READY);#endif	schedule_work(&tty->flip.work);clear_and_exit:	return;}static _INLINE_ void transmit_chars(struct m68k_serial *info){	m68328_uart *uart = &uart_addr[info->line];	if (info->x_char) {		/* Send next char */		uart->utx.b.txdata = info->x_char;		info->x_char = 0;		goto clear_and_return;	}	if((info->xmit_cnt <= 0) || info->tty->stopped) {		/* That's peculiar... TX ints off */		uart->ustcnt &= ~USTCNT_TX_INTR_MASK;		goto clear_and_return;	}	/* Send char */	uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++];	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);	info->xmit_cnt--;	if (info->xmit_cnt < WAKEUP_CHARS)		schedule_work(&info->tqueue);	if(info->xmit_cnt <= 0) {		/* All done for now... TX ints off */		uart->ustcnt &= ~USTCNT_TX_INTR_MASK;		goto clear_and_return;	}clear_and_return:	/* Clear interrupt (should be auto)*/	return;}/* * This is the serial driver's generic interrupt routine */irqreturn_t rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){	struct m68k_serial * info;	m68328_uart *uart;	unsigned short rx;	unsigned short tx;	info = IRQ_ports[irq];	if(!info)	    return IRQ_NONE;	uart = &uart_addr[info->line];	rx = uart->urx.w;#ifdef USE_INTS	tx = uart->utx.w;	if (rx & URX_DATA_READY) receive_chars(info, regs, rx);	if (tx & UTX_TX_AVAIL)   transmit_chars(info);#else	receive_chars(info, regs, rx);		#endif	return IRQ_HANDLED;}static void do_softint(void *private){	struct m68k_serial	*info = (struct m68k_serial *) private;	struct tty_struct	*tty;		tty = info->tty;	if (!tty)		return;#if 0	if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {		tty_wakeup(tty);	}#endif   }/* * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred.  The path of * hangup processing is: * * 	serial interrupt routine -> (scheduler tqueue) -> * 	do_serial_hangup() -> tty->hangup() -> rs_hangup() *  */static void do_serial_hangup(void *private){	struct m68k_serial	*info = (struct m68k_serial *) private;	struct tty_struct	*tty;		tty = info->tty;	if (!tty)		return;	tty_hangup(tty);}static int startup(struct m68k_serial * info){	m68328_uart *uart = &uart_addr[info->line];	unsigned long flags;		if (info->flags & S_INITIALIZED)		return 0;	if (!info->xmit_buf) {		info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL);		if (!info->xmit_buf)			return -ENOMEM;	}	save_flags(flags); cli();	/*	 * Clear the FIFO buffers and disable them	 * (they will be reenabled in change_speed())	 */	uart->ustcnt = USTCNT_UEN;	info->xmit_fifo_size = 1;	uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN;	(void)uart->urx.w;	/*	 * Finally, enable sequencing and interrupts	 */#ifdef USE_INTS	uart->ustcnt = USTCNT_UEN | USTCNT_RXEN |                  USTCNT_RX_INTR_MASK | USTCNT_TX_INTR_MASK;#else	uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK;#endif	if (info->tty)		clear_bit(TTY_IO_ERROR, &info->tty->flags);	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;	/*	 * and set the speed of the serial port	 */	change_speed(info);	info->flags |= S_INITIALIZED;	restore_flags(flags);	return 0;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static void shutdown(struct m68k_serial * info){	m68328_uart *uart = &uart_addr[info->line];	unsigned long	flags;	uart->ustcnt = 0; /* All off! */	if (!(info->flags & S_INITIALIZED))		return;	save_flags(flags); cli(); /* Disable interrupts */		if (info->xmit_buf) {		free_page((unsigned long) info->xmit_buf);		info->xmit_buf = 0;	}	if (info->tty)		set_bit(TTY_IO_ERROR, &info->tty->flags);		info->flags &= ~S_INITIALIZED;	restore_flags(flags);}struct {

⌨️ 快捷键说明

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