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

📄 macserial.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * macserial.c: Serial port driver for Power Macintoshes. * * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras. * * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>. * * $Id: macserial.c,v 1.24.2.4 1999/10/19 04:36:42 paulus Exp $ */#include <linux/config.h>#include <linux/errno.h>#include <linux/module.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/init.h>#ifdef CONFIG_SERIAL_CONSOLE#include <linux/console.h>#endif#include <linux/slab.h>#include <asm/init.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/prom.h>#include <asm/system.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/feature.h>#include <linux/adb.h>#include <linux/pmu.h>#ifdef CONFIG_KGDB#include <asm/kgdb.h>#endif#include <asm/dbdma.h>#include "macserial.h"#ifdef CONFIG_PMAC_PBOOKstatic int serial_notify_sleep(struct pmu_sleep_notifier *self, int when);static struct pmu_sleep_notifier serial_sleep_notifier = {	serial_notify_sleep,	SLEEP_LEVEL_MISC,};#endif#define SUPPORT_SERIAL_DMA/* * It would be nice to dynamically allocate everything that * depends on NUM_SERIAL, so we could support any number of * Z8530s, but for now... */#define NUM_SERIAL	2		/* Max number of ZS chips supported */#define NUM_CHANNELS	(NUM_SERIAL * 2)	/* 2 channels per chip *//* On PowerMacs, the hardware takes care of the SCC recovery time,   but we need the eieio to make sure that the accesses occur   in the order we want. */#define RECOVERY_DELAY	eieio()struct mac_zschannel zs_channels[NUM_CHANNELS];struct mac_serial zs_soft[NUM_CHANNELS];int zs_channels_found;struct mac_serial *zs_chain;	/* list of all channels */struct tty_struct zs_ttys[NUM_CHANNELS];static int is_powerbook;#ifdef CONFIG_SERIAL_CONSOLEstatic struct console sercons;#endif#ifdef CONFIG_KGDBstruct mac_zschannel *zs_kgdbchan;static unsigned char scc_inittab[] = {	9,  0x80,	/* reset A side (CHRA) */	13, 0,		/* set baud rate divisor */	12, 1,	14, 1,		/* baud rate gen enable, src=rtxc (BRENABL) */	11, 0x50,	/* clocks = br gen (RCBR | TCBR) */	5,  0x6a,	/* tx 8 bits, assert RTS (Tx8 | TxENAB | RTS) */	4,  0x44,	/* x16 clock, 1 stop (SB1 | X16CLK)*/	3,  0xc1,	/* rx enable, 8 bits (RxENABLE | Rx8)*/};#endif#define ZS_CLOCK         3686400 	/* Z8530 RTxC input clock rate */static DECLARE_TASK_QUEUE(tq_serial);static struct tty_driver serial_driver, callout_driver;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. */#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#undef SERIAL_DEBUG_POWER#undef SERIAL_DEBUG_THROTTLE#undef SERIAL_DEBUG_STOP#undef SERIAL_DEBUG_BAUDS#define RS_STROBE_TIME 10#define RS_ISR_PASS_LIMIT 256#define _INLINE_ inline#ifdef SERIAL_DEBUG_OPEN#define OPNDBG(fmt, arg...)	printk(KERN_DEBUG fmt , ## arg)#else#define OPNDBG(fmt, arg...)	do { } while (0)#endif#ifdef SERIAL_DEBUG_POWER#define PWRDBG(fmt, arg...)	printk(KERN_DEBUG fmt , ## arg)#else#define PWRDBG(fmt, arg...)	do { } while (0)#endif#ifdef SERIAL_DEBUG_BAUDS#define BAUDBG(fmt, arg...)	printk(fmt , ## arg)#else#define BAUDBG(fmt, arg...)	do { } while (0)#endifstatic void probe_sccs(void);static void change_speed(struct mac_serial *info, struct termios *old);static void rs_wait_until_sent(struct tty_struct *tty, int timeout);static int set_scc_power(struct mac_serial * info, int state);static int setup_scc(struct mac_serial * info);static void dbdma_reset(volatile struct dbdma_regs *dma);static void dbdma_flush(volatile struct dbdma_regs *dma);static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs);static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs);static void dma_init(struct mac_serial * info);static void rxdma_start(struct mac_serial * info, int current);static void rxdma_to_tty(struct mac_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 copy_from_user 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;static DECLARE_MUTEX(tmp_buf_sem);static inline int __pmacserial_paranoia_check(struct mac_serial *info,		      dev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK	static const char *badmagic =		"Warning: bad magic number for serial struct (%d, %d) in %s\n";	static const char *badinfo =		"Warning: null mac_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;}/*  * Reading and writing Z8530 registers. */static inline unsigned char __pmac read_zsreg(struct mac_zschannel *channel,					      unsigned char reg){	unsigned char retval;	unsigned long flags;	/*	 * We have to make this atomic.	 */	spin_lock_irqsave(&channel->lock, flags);	if (reg != 0) {		*channel->control = reg;		RECOVERY_DELAY;	}	retval = *channel->control;	RECOVERY_DELAY;	spin_unlock_irqrestore(&channel->lock, flags);	return retval;}static inline void __pmac write_zsreg(struct mac_zschannel *channel,				      unsigned char reg, unsigned char value){	unsigned long flags;	spin_lock_irqsave(&channel->lock, flags);	if (reg != 0) {		*channel->control = reg;		RECOVERY_DELAY;	}	*channel->control = value;	RECOVERY_DELAY;	spin_unlock_irqrestore(&channel->lock, flags);	return;}static inline unsigned char __pmac read_zsdata(struct mac_zschannel *channel){	unsigned char retval;	retval = *channel->data;	RECOVERY_DELAY;	return retval;}static inline void write_zsdata(struct mac_zschannel *channel,				unsigned char value){	*channel->data = value;	RECOVERY_DELAY;	return;}static inline void load_zsregs(struct mac_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 mac_serial *ss, int set){	if (set)		ss->curregs[5] |= (RTS | DTR);	else		ss->curregs[5] &= ~(RTS | DTR);	write_zsreg(ss->zs_channel, 5, ss->curregs[5]);	return;}/* Utility routines for the Zilog */static inline int get_zsbaud(struct mac_serial *ss){	struct mac_zschannel *channel = ss->zs_channel;	int brg;	if ((ss->curregs[R11] & TCBR) == 0) {		/* higher rates don't use the baud rate generator */		return (ss->curregs[R4] & X32CLK)? ZS_CLOCK/32: ZS_CLOCK/16;	}	/* 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) << 8);	brg |= read_zsreg(channel, 12);	return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor)));}/* On receive, this clears errors and the receiver interrupts */static inline void rs_recv_clear(struct mac_zschannel *zsc){	write_zsreg(zsc, 0, ERR_RES);	write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */}/* * Reset a Descriptor-Based DMA channel. */static void dbdma_reset(volatile struct dbdma_regs *dma){	int i;	out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);	/*	 * Yes this looks peculiar, but apparently it needs to be this	 * way on some machines.  (We need to make sure the DBDMA	 * engine has actually got the write above and responded	 * to it. - paulus)	 */	for (i = 200; i > 0; --i)		if (ld_le32(&dma->status) & RUN)			udelay(1);}/* * Tells a DBDMA channel to stop and write any buffered data * it might have to memory. */static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma){	int i = 0;	out_le32(&dma->control, (FLUSH << 16) | FLUSH);	while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100))		udelay(1);}/* * ---------------------------------------------------------------------- * * 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. * * 				- 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 mac_serial *info,				  int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_serial);	mark_bh(MACSERIAL_BH);}/* Work out the flag value for a z8530 status value. */static _INLINE_ int stat_to_flag(int stat){	int flag;	if (stat & Rx_OVR) {		flag = TTY_OVERRUN;	} else if (stat & FRM_ERR) {		flag = TTY_FRAME;	} else if (stat & PAR_ERR) {		flag = TTY_PARITY;	} else		flag = 0;	return flag;}static _INLINE_ void receive_chars(struct mac_serial *info,				   struct pt_regs *regs){	struct tty_struct *tty = info->tty;	unsigned char ch, stat, flag;	while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) != 0) {		stat = read_zsreg(info->zs_channel, R1);		ch = read_zsdata(info->zs_channel);#ifdef CONFIG_KGDB		if (info->kgdb_channel) {			if (ch == 0x03 || ch == '$')				breakpoint();			if (stat & (Rx_OVR|FRM_ERR|PAR_ERR))				write_zsreg(info->zs_channel, 0, ERR_RES);			return;		}#endif		if (!tty)			continue;		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			tty_flip_buffer_push(tty);		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {			static int flip_buf_ovf;			if (++flip_buf_ovf <= 1)				printk("FB. overflow: %d\n", flip_buf_ovf);			break;		}		tty->flip.count++;		{			static int flip_max_cnt;			if (flip_max_cnt < tty->flip.count)				flip_max_cnt = tty->flip.count;		}		flag = stat_to_flag(stat);		if (flag)			/* reset the error indication */			write_zsreg(info->zs_channel, 0, ERR_RES);		*tty->flip.flag_buf_ptr++ = flag;		*tty->flip.char_buf_ptr++ = ch;	}	if (tty)		tty_flip_buffer_push(tty);}static void transmit_chars(struct mac_serial *info){	unsigned long flags;	save_flags(flags);	cli();	if ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0)		goto out;	info->tx_active = 0;	if (info->x_char) {		/* Send next char */		write_zsdata(info->zs_channel, info->x_char);		info->x_char = 0;		info->tx_active = 1;		goto out;	}	if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tx_stopped) {		write_zsreg(info->zs_channel, 0, RES_Tx_P);		goto out;	}	/* Send char */	write_zsdata(info->zs_channel, info->xmit_buf[info->xmit_tail++]);	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);	info->xmit_cnt--;	info->tx_active = 1;	if (info->xmit_cnt < WAKEUP_CHARS)		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); out:	restore_flags(flags);}static _INLINE_ void status_handle(struct mac_serial *info){	unsigned char status;	/* Get status from Read Register 0 */	status = read_zsreg(info->zs_channel, 0);	/* Check for DCD transitions */	if (((status ^ info->read_reg_zero) & DCD) != 0	    && info->tty && !C_CLOCAL(info->tty)) {		if (status & DCD) {			wake_up_interruptible(&info->open_wait);		} else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) {			if (info->tty)				tty_hangup(info->tty);		}	}	/* Check for CTS transitions */

⌨️ 快捷键说明

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