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

📄 sgiserial.c

📁 移植到2410开发板上的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* sgiserial.c: Serial port driver for SGI machines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) *//* * Note: This driver seems to have been derived from some * version of the sbus/char/zs.c driver.  A lot of clean-up * and bug fixes seem to have happened to the Sun driver in * the intervening time.  As of 21.09.1999, I have merged in * ONLY the changes necessary to fix observed functional * problems on the Indy.  Someone really ought to do a * thorough pass to merge in the rest of the updates. * Better still, someone really ought to make it a common * code module for both platforms.   kevink@mips.com * * 20010616 - Klaus Naumann <spock@mgnet.de> : Make serial console work with *                                             any speed - not only 9600 */#include <linux/config.h> /* for CONFIG_REMOTE_DEBUG */#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/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/console.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/sgialib.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/sgi/sgihpc.h>#include <asm/sgi/sgint23.h>#include <asm/uaccess.h>#include "sgiserial.h"#define NUM_SERIAL 1     /* One chip on board. */#define NUM_CHANNELS (NUM_SERIAL * 2)extern wait_queue_head_t keypress_wait;struct sgi_zslayout *zs_chips[NUM_SERIAL] = { 0, };struct sgi_zschannel *zs_channels[NUM_CHANNELS] = { 0, 0, };struct sgi_zschannel *zs_conschan;struct sgi_zschannel *zs_kgdbchan;struct sgi_serial zs_soft[NUM_CHANNELS];struct sgi_serial *zs_chain;  /* IRQ servicing chain */static int zilog_irq = SGI_SERIAL_IRQ;/* Console hooks... */static int zs_cons_chanout;static int zs_cons_chanin;struct sgi_serial *zs_consinfo;static unsigned char kgdb_regs[16] = {	0, 0, 0,                     /* write 0, 1, 2 */	(Rx8 | RxENABLE),            /* write 3 */	(X16CLK | SB1 | PAR_EVEN),   /* write 4 */	(Tx8 | TxENAB),              /* write 5 */	0, 0, 0,                     /* write 6, 7, 8 */	(NV),                        /* write 9 */	(NRZ),                       /* write 10 */	(TCBR | RCBR),               /* write 11 */	0, 0,                        /* BRG time constant, write 12 + 13 */	(BRENABL),                   /* write 14 */	(DCDIE)                      /* write 15 */};static unsigned char zscons_regs[16] = {	0,                           /* write 0 */	(EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */	0,                           /* write 2 */	(Rx8 | RxENABLE),            /* write 3 */	(X16CLK),                    /* write 4 */	(DTR | Tx8 | TxENAB),        /* write 5 */	0, 0, 0,                     /* write 6, 7, 8 */	(NV | MIE),                  /* write 9 */	(NRZ),                       /* write 10 */	(TCBR | RCBR),               /* write 11 */	0, 0,                        /* BRG time constant, write 12 + 13 */	(BRENABL),                   /* write 14 */	(DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */};#define ZS_CLOCK         3672000   /* Zilog input clock rate */DECLARE_TASK_QUEUE(tq_serial);struct tty_driver serial_driver, callout_driver;struct console *sgisercon;static int serial_refcount;/* serial subtype definitions */#define SERIAL_TYPE_NORMAL	1#define SERIAL_TYPE_CALLOUT	2  /* 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_STROBE_TIME 10#define RS_ISR_PASS_LIMIT 256#define _INLINE_ inlinestatic void change_speed(struct sgi_serial *info);static struct tty_struct *serial_table[NUM_CHANNELS];static struct termios *serial_termios[NUM_CHANNELS];static struct termios *serial_termios_locked[NUM_CHANNELS];#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endif/* * 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[4096]; /* This is cheating */static DECLARE_MUTEX(tmp_buf_sem);static inline int serial_paranoia_check(struct sgi_serial *info,					dev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK	static const char *badmagic = KERN_WARNING		"Warning: bad magic number for serial struct (%d, %d) in %s\n";	static const char *badinfo = KERN_WARNING		"Warning: null sgi_serial for (%d, %d) in %s\n";	if (!info) {		printk(badinfo, MAJOR(device), MINOR(device), routine);		return 1;	}	if (info->magic != SERIAL_MAGIC) {		printk(badmagic, MAJOR(device), MINOR(device), 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 };/*  * Reading and writing Zilog8530 registers.  The delays are to make this * driver work on the Sun4 which needs a settling delay after each chip * register access, other machines handle this in hardware via auxiliary * flip-flops which implement the settle time we do in software. * * read_zsreg() and write_zsreg() may get called from rs_kgdb_hook() before * interrupts are enabled. Therefore we have to check ioc_iocontrol before we * access it. */static inline unsigned char read_zsreg(struct sgi_zschannel *channel,                                       unsigned char reg){	unsigned char retval;	volatile unsigned char junk;	udelay(2);	channel->control = reg;	if (ioc_icontrol)		junk = ioc_icontrol->istat0;	udelay(1);	retval = channel->control;	return retval;}static inline void write_zsreg(struct sgi_zschannel *channel,                               unsigned char reg, unsigned char value){	volatile unsigned char junk;	udelay(2);	channel->control = reg;	if (ioc_icontrol)		junk = ioc_icontrol->istat0;	udelay(1);	channel->control = value;	if (ioc_icontrol)		junk = ioc_icontrol->istat0;	return;}static inline void load_zsregs(struct sgi_zschannel *channel, unsigned char *regs){	ZS_CLEARERR(channel);	ZS_CLEARFIFO(channel);	/* Load 'em up */	write_zsreg(channel, R4, regs[R4]);	write_zsreg(channel, R10, regs[R10]);	write_zsreg(channel, R3, regs[R3] & ~RxENABLE);	write_zsreg(channel, R5, regs[R5] & ~TxENAB);	write_zsreg(channel, R1, regs[R1]);	write_zsreg(channel, R9, regs[R9]);	write_zsreg(channel, R11, regs[R11]);	write_zsreg(channel, R12, regs[R12]);	write_zsreg(channel, R13, regs[R13]);	write_zsreg(channel, R14, regs[R14]);	write_zsreg(channel, R15, regs[R15]);	write_zsreg(channel, R3, regs[R3]);	write_zsreg(channel, R5, regs[R5]);	return;}/* Sets or clears DTR/RTS on the requested line */static inline void zs_rtsdtr(struct sgi_serial *ss, int set){	if(set) {		ss->curregs[5] |= (RTS | DTR);		ss->pendregs[5] = ss->curregs[5];		write_zsreg(ss->zs_channel, 5, ss->curregs[5]);	} else {		ss->curregs[5] &= ~(RTS | DTR);		ss->pendregs[5] = ss->curregs[5];		write_zsreg(ss->zs_channel, 5, ss->curregs[5]);	}	return;}static inline void kgdb_chaninit(struct sgi_serial *ss, int intson, int bps){	int brg;	if(intson) {		kgdb_regs[R1] = INT_ALL_Rx;		kgdb_regs[R9] |= MIE;	} else {		kgdb_regs[R1] = 0;		kgdb_regs[R9] &= ~MIE;	}	brg = BPS_TO_BRG(bps, ZS_CLOCK/ss->clk_divisor);	kgdb_regs[R12] = (brg & 255);	kgdb_regs[R13] = ((brg >> 8) & 255);	load_zsregs(ss->zs_channel, kgdb_regs);}/* Utility routines for the Zilog */static inline int get_zsbaud(struct sgi_serial *ss){	struct sgi_zschannel *channel = ss->zs_channel;	int brg;	/* The baud rate is split up between two 8-bit registers in	 * what is termed 'BRG time constant' format in my docs for	 * the chip, it is a function of the clk rate the chip is	 * receiving which happens to be constant.	 */	brg = ((read_zsreg(channel, 13)&0xff) << 8);	brg |= (read_zsreg(channel, 12)&0xff);	return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor)));}/* * ------------------------------------------------------------ * 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 sgi_serial *info = (struct sgi_serial *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->device, "rs_stop"))		return;		save_flags(flags); cli();	if (info->curregs[5] & TxENAB) {		info->curregs[5] &= ~TxENAB;		info->pendregs[5] &= ~TxENAB;		write_zsreg(info->zs_channel, 5, info->curregs[5]);	}	restore_flags(flags);}static void rs_start(struct tty_struct *tty){	struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;	unsigned long flags;		if (serial_paranoia_check(info, tty->device, "rs_start"))		return;		save_flags(flags); cli();	if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {		info->curregs[5] |= TxENAB;		info->pendregs[5] = info->curregs[5];		write_zsreg(info->zs_channel, 5, info->curregs[5]);	}	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){	ArcEnterInteractiveMode();#if 0	/* If we are doing kadb, we call the debugger	 * else we just drop into the boot monitor.	 * Note that we must flush the user windows	 * first before giving up control.	 */	printk("\n");	if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&	   (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))		sp_enter_debugger();	else		prom_halt();	/* XXX We want to notify the keyboard driver that all	 * XXX keys are in the up state or else weird things	 * XXX happen...	 */#endif	return;}/* On receive, this clears errors and the receiver interrupts */static inline void rs_recv_clear(struct sgi_zschannel *zsc){	volatile unsigned char junk;	udelay(2);	zsc->control = ERR_RES;	junk = ioc_icontrol->istat0;	udelay(2);	zsc->control = RES_H_IUS;	junk = ioc_icontrol->istat0;}/* * ---------------------------------------------------------------------- * * 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(struct sgi_serial *info,				    int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_serial);	mark_bh(SERIAL_BH);}#ifdef CONFIG_REMOTE_DEBUGextern void set_async_breakpoint(unsigned int epc);#endifstatic _INLINE_ void receive_chars(struct sgi_serial *info, struct pt_regs *regs){	struct tty_struct *tty = info->tty;	volatile unsigned char junk;	unsigned char ch, stat;	udelay(2);	ch = info->zs_channel->data;	junk = ioc_icontrol->istat0;	udelay(2);	stat = read_zsreg(info->zs_channel, R1);	/* If this is the console keyboard, we need to handle	 * L1-A's here.	 */	if(info->is_cons) {		if(ch==0) { /* whee, break received */			batten_down_hatches();			rs_recv_clear(info->zs_channel);			return;		} else if (ch == 1) {			show_state();			return;		} else if (ch == 2) {			show_buffers();			return;		}		/* It is a 'keyboard interrupt' ;-) */		wake_up(&keypress_wait);	}	/* Look for kgdb 'stop' character, consult the gdb documentation	 * for remote target debugging and arch/sparc/kernel/sparc-stub.c	 * to see how all this works.	 */#ifdef CONFIG_REMOTE_DEBUG	if((info->kgdb_channel) && (ch =='\003')) {		set_async_breakpoint(read_32bit_cp0_register(CP0_EPC));		goto clear_and_exit;	}#endif	if(!tty)		goto clear_and_exit;	if (tty->flip.count >= TTY_FLIPBUF_SIZE)		queue_task(&tty->flip.tqueue, &tq_timer);	tty->flip.count++;	if(stat & PAR_ERR)		*tty->flip.flag_buf_ptr++ = TTY_PARITY;	else if(stat & Rx_OVR)		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;	else if(stat & CRC_ERR)		*tty->flip.flag_buf_ptr++ = TTY_FRAME;	else		*tty->flip.flag_buf_ptr++ = 0; /* XXX */	*tty->flip.char_buf_ptr++ = ch;	queue_task(&tty->flip.tqueue, &tq_timer);clear_and_exit:	rs_recv_clear(info->zs_channel);	return;}static _INLINE_ void transmit_chars(struct sgi_serial *info){	volatile unsigned char junk;	/* P3: In theory we have to test readiness here because a	 * serial console can clog the chip through zs_cons_put_char().	 * David did not do this. I think he relies on 3-chars FIFO in 8530.	 * Let's watch for lost _output_ characters. XXX	 */	/* SGI ADDENDUM: On most SGI machines, the Zilog does possess	 *               a 16 or 17 byte fifo, so no worries. -dm	 */	if (info->x_char) {		/* Send next char */		udelay(2);		info->zs_channel->data = info->x_char;		junk = ioc_icontrol->istat0;		info->x_char = 0;		goto clear_and_return;	}	if((info->xmit_cnt <= 0) || info->tty->stopped) {		/* That's peculiar... */		udelay(2);		info->zs_channel->control = RES_Tx_P;		junk = ioc_icontrol->istat0;		goto clear_and_return;	}	/* Send char */	udelay(2);	info->zs_channel->data = info->xmit_buf[info->xmit_tail++];	junk = ioc_icontrol->istat0;	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);	info->xmit_cnt--;	if (info->xmit_cnt < WAKEUP_CHARS)		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);	if(info->xmit_cnt <= 0) {		udelay(2);		info->zs_channel->control = RES_Tx_P;		junk = ioc_icontrol->istat0;		goto clear_and_return;	}clear_and_return:	/* Clear interrupt */	udelay(2);	info->zs_channel->control = RES_H_IUS;	junk = ioc_icontrol->istat0;	return;}static _INLINE_ void status_handle(struct sgi_serial *info){	volatile unsigned char junk;	unsigned char status;	/* Get status from Read Register 0 */	udelay(2);	status = info->zs_channel->control;	junk = ioc_icontrol->istat0;	/* Clear status condition... */	udelay(2);	info->zs_channel->control = RES_EXT_INT;	junk = ioc_icontrol->istat0;	/* Clear the interrupt */	udelay(2);	info->zs_channel->control = RES_H_IUS;	junk = ioc_icontrol->istat0;#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->zs_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->zs_channel, 3, info->curregs[3]);		}	}#endif	/* Whee, if this is console input and this is a	 * 'break asserted' status change interrupt, call	 * the boot prom.	 */	if((status & BRK_ABRT) && info->break_abort)		batten_down_hatches();	/* XXX Whee, put in a buffer somewhere, the status information	 * XXX whee whee whee... Where does the information go...	 */	return;}/* * This is the serial driver's generic interrupt routine

⌨️ 快捷键说明

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