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

📄 mac_scc.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * mac_SCC.c: m68k version of * * macserial.c: Serial port driver for Power Macintoshes. *		Extended for the 68K mac by Alan Cox. *              Rewritten to m68k serial design by Michael Schmitz * * 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) *//* * Design note for the m68k rewrite: * The structure of the m68k serial code requires separation of the low-level * functions that talk directly to the hardware from the Linux serial driver  * code interfacing to the tty layer. The reason for this separation is simply * the fact that the m68k serial hardware is, unlike the i386, based on a  * variety of chips, and the rs_* serial routines need to be shared. * * I've tried to make consistent use of the async_struct info populated in the * midlevel code, and introduced an async_private struct to hold the Macintosh  * SCC internals (this was added to the async_struct for the PowerMac driver). * Exception: the console and kgdb hooks still use the zs_soft[] data, and this * is still filled in by the probe_sccs() routine, which provides some data  * for mac_SCC_init as well. Interrupts are registered in mac_SCC_init, so  * the console/kgdb stuff probably won't work before proper serial init, and  * I have to rely on keeping info and zs_soft consistent at least for the  * console/kgdb port. * * Update (16-11-97): The SCC interrupt handling was suffering from the problem * that the autovector SCC interrupt was registered only once, hence only one * async_struct was passed to the interrupt function and only interrupts from  * the corresponding channel could be handled (yes, major design flaw). * The autovector interrupt is now registered by the main interrupt initfunc,  * and uses a handler that will call the registered SCC specific interrupts in  * turn. The SCC init has to register these as machspec interrupts now, as is  * done for the VIA interrupts elsewhere. */#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/config.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 <asm/uaccess.h>#include <asm/setup.h>#include <asm/bootinfo.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/macints.h>#ifndef CONFIG_MAC#include <asm/prom.h>#endif#include <asm/system.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/hwtest.h>#include "mac_SCC.h"/* * 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 */#ifdef CONFIG_MAC/* *	All the Macintosh 68K boxes that have an MMU also have hardware *	recovery delays. */#define RECOVERY_DELAY#else/* 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()#endifstruct mac_zschannel *zs_kgdbchan;struct mac_zschannel zs_channels[NUM_CHANNELS];struct m68k_async_struct  zs_soft[NUM_CHANNELS];struct m68k_async_private zs_soft_private[NUM_CHANNELS];int zs_channels_found;struct m68k_async_struct *zs_chain;	/* list of all channels */struct tty_struct zs_ttys[NUM_CHANNELS];/** struct tty_struct *zs_constty; **//* Console hooks... */static int zs_cons_chanout = 0;static int zs_cons_chanin = 0;struct m68k_async_struct  *zs_consinfo = 0;struct mac_zschannel *zs_conschan;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 */	1, 0,			/* 38400 baud divisor, write 12 + 13 */	(BRENABL),		/* write 14 */	(DCDIE)			/* write 15 */};#define ZS_CLOCK         3686400 	/* Z8530 RTxC input clock rate *//* 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 probe_sccs(void);#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endif/***************************** Prototypes *****************************/static void SCC_init_port( struct m68k_async_struct *info, int type, int channel );#if 0#ifdef MODULEstatic void SCC_deinit_port( struct m68k_async_struct *info, int channel );#endif#endif/* FIXME !!! Currently, only autovector interrupt used! */#if 0static void SCC_rx_int (int irq, void *data, struct pt_regs *fp);static void SCC_spcond_int (int irq, void *data, struct pt_regs *fp);static void SCC_tx_int (int irq, void *data, struct pt_regs *fp);static void SCC_stat_int (int irq, void *data, struct pt_regs *fp);static void SCC_ri_int (int irq, void *data, struct pt_regs *fp);#endifstatic int SCC_check_open( struct m68k_async_struct *info, struct tty_struct                           *tty, struct file *file );static void SCC_init( struct m68k_async_struct *info );static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr );static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag );static int SCC_check_custom_divisor( struct m68k_async_struct *info, int baud_base,				    int divisor );static void SCC_change_speed( struct m68k_async_struct *info );#if 0static int SCC_clocksrc( unsigned baud_base, unsigned channel );#endifstatic void SCC_throttle( struct m68k_async_struct *info, int status );static void SCC_set_break( struct m68k_async_struct *info, int break_flag );static void SCC_get_serial_info( struct m68k_async_struct *info, struct				serial_struct *retinfo );static unsigned int SCC_get_modem_info( struct m68k_async_struct *info );static int SCC_set_modem_info( struct m68k_async_struct *info, int new_dtr, int			      new_rts );static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct		     m68k_async_struct *info, unsigned int cmd, unsigned long arg );static void SCC_stop_receive (struct m68k_async_struct *info);static int SCC_trans_empty (struct m68k_async_struct *info);/************************* End of Prototypes **************************/static SERIALSWITCH SCC_switch = {	SCC_init, SCC_deinit, SCC_enab_tx_int,	SCC_check_custom_divisor, SCC_change_speed,	SCC_throttle, SCC_set_break,	SCC_get_serial_info, SCC_get_modem_info,	SCC_set_modem_info, SCC_ioctl, SCC_stop_receive, SCC_trans_empty,	SCC_check_open};/* * 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 Z8530 registers. */static inline unsigned char read_zsreg(struct mac_zschannel *channel,				       unsigned char reg){	unsigned char retval;	if (reg != 0) {		*channel->control = reg;		RECOVERY_DELAY;	}	retval = *channel->control;	RECOVERY_DELAY;	return retval;}static inline void write_zsreg(struct mac_zschannel *channel,			       unsigned char reg, unsigned char value){	if (reg != 0) {		*channel->control = reg;		RECOVERY_DELAY;	}	*channel->control = value;	RECOVERY_DELAY;	return;}static inline unsigned char 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 m68k_async_struct *ss, int set){	if (set)		ss->private->curregs[5] |= (RTS | DTR);	else		ss->private->curregs[5] &= ~(RTS | DTR);	write_zsreg(ss->private->zs_channel, 5, ss->private->curregs[5]);	return;}static inline void kgdb_chaninit(struct m68k_async_struct *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/16);	kgdb_regs[R12] = brg;	kgdb_regs[R13] = brg >> 8;	load_zsregs(ss->private->zs_channel, kgdb_regs);}/* Utility routines for the Zilog */static inline int get_zsbaud(struct m68k_async_struct *ss){	struct mac_zschannel *channel = ss->private->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) << 8);	brg |= read_zsreg(channel, 12);	return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->private->clk_divisor)));}/* On receive, this clears errors and the receiver interrupts */static inline void SCC_recv_clear(struct mac_zschannel *zsc){	write_zsreg(zsc, 0, ERR_RES);	write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */}/* * ---------------------------------------------------------------------- * * 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 * ----------------------------------------------------------------------- */extern void breakpoint(void);  /* For the KGDB frame character */static /*_INLINE_*/ void receive_chars(struct m68k_async_struct *info,				   struct pt_regs *regs){	struct tty_struct *tty = info->tty;	unsigned char ch, stat, flag;	while ((read_zsreg(info->private->zs_channel, 0) & Rx_CH_AV) != 0) {		stat = read_zsreg(info->private->zs_channel, R1);		ch = read_zsdata(info->private->zs_channel);#ifdef SCC_DEBUG		printk("mac_SCC: receive_chars stat=%X char=%X \n", stat, ch);#endif#if 0	/* KGDB not yet supported */		/* 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.		 */		if ((info->kgdb_channel) && (ch =='\003')) {			breakpoint();			continue;		}#endif		if (!tty)			continue;		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			tty_flip_buffer_push(tty);				if (stat & Rx_OVR) {			flag = TTY_OVERRUN;			/* reset the error indication */			write_zsreg(info->private->zs_channel, 0, ERR_RES);		} else if (stat & FRM_ERR) {			/* this error is not sticky */			flag = TTY_FRAME;		} else if (stat & PAR_ERR) {			flag = TTY_PARITY;			/* reset the error indication */			write_zsreg(info->private->zs_channel, 0, ERR_RES);		} else			flag = 0;		if (tty->flip.buf_num 		    && tty->flip.count >= TTY_FLIPBUF_SIZE) {#ifdef SCC_DEBUG_OVERRUN			printk("mac_SCC: flip buffer overrun!\n");#endif			return;		}		if (!tty->flip.buf_num 		    && tty->flip.count >= 2*TTY_FLIPBUF_SIZE) {			printk("mac_SCC: double flip buffer overrun!\n");			return;		}		tty->flip.count++;		*tty->flip.flag_buf_ptr++ = flag;		*tty->flip.char_buf_ptr++ = ch;		info->icount.rx++;		tty_flip_buffer_push(tty);	}#if 0clear_and_exit:	SCC_recv_clear(info->private->zs_channel);#endif}/* that's SCC_enable_tx_int, basically */static void transmit_chars(struct m68k_async_struct *info){	if ((read_zsreg(info->private->zs_channel, 0) & Tx_BUF_EMP) == 0)		return;	info->private->tx_active = 0;	if (info->x_char) {		/* Send next char */		write_zsdata(info->private->zs_channel, info->x_char);		info->x_char = 0;		info->private->tx_active = 1;		return;	}	if ((info->xmit_cnt <= 0) || info->tty->stopped 	     || info->private->tx_stopped) {		write_zsreg(info->private->zs_channel, 0, RES_Tx_P);		return;	}	/* Send char */	write_zsdata(info->private->zs_channel, info->xmit_buf[info->xmit_tail++]);	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);	info->icount.tx++;	info->xmit_cnt--;	info->private->tx_active = 1;	if (info->xmit_cnt < WAKEUP_CHARS)		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);}static /*_INLINE_*/ void status_handle(struct m68k_async_struct *info){	unsigned char status;	/* Get status from Read Register 0 */	status = read_zsreg(info->private->zs_channel, 0);	/* Check for DCD transitions */	if (((status ^ info->private->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 */	if (info->tty && C_CRTSCTS(info->tty)) {		/*		 * For some reason, on the Power Macintosh,		 * it seems that the CTS bit is 1 when CTS is		 * *negated* and 0 when it is asserted.		 * The DCD bit doesn't seem to be inverted		 * like this.		 */		if ((status & CTS) == 0) {			if (info->private->tx_stopped) {				info->private->tx_stopped = 0;				if (!info->private->tx_active)					transmit_chars(info);			}		} else {			info->private->tx_stopped = 1;		}	}	/* Clear status condition... */	write_zsreg(info->private->zs_channel, 0, RES_EXT_INT);	info->private->read_reg_zero = status;}/* * This is the serial driver's generic interrupt routine */void mac_SCC_interrupt(int irq, void *dev_id, struct pt_regs * regs){	struct m68k_async_struct *info = (struct m68k_async_struct *) dev_id;	unsigned char zs_intreg;	int shift;	/* NOTE: The read register 3, which holds the irq status,	 *       does so for both channels on each chip.  Although	 *       the status value itself must be read from the A	 *       channel and is only valid when read from channel A.	 *       Yes... broken hardware...	 */#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)#ifdef SCC_DEBUG	printk("mac_SCC: interrupt; port: %lx channel: %lx \n", 		info->port, info->private->zs_channel);#endif	if (info->private->zs_chan_a == info->private->zs_channel)		shift = 3;	/* Channel A */

⌨️ 快捷键说明

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