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

📄 z85230.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	This program is free software; you can redistribute it and/or *	modify it under the terms of the GNU General Public License *	as published by the Free Software Foundation; either version *	2 of the License, or (at your option) any later version. * *	(c) Copyright 1998 Building Number Three Ltd * *	Development of this driver was funded by Equiinet Ltd *			http://www.equiinet.com * *	ChangeLog: * *	Asynchronous mode dropped for 2.2. For 2.3 we will attempt the *	unification of all the Z85x30 asynchronous drivers for real. * *	To Do: *	 *	Finish DMA mode support. * *	Performance * *	Z85230: *	Non DMA you want a 486DX50 or better to do 64Kbits. 9600 baud *	X.25 is not unrealistic on all machines. DMA mode can in theory *	handle T1/E1 quite nicely. * *	Z85C30: *	64K will take DMA, 9600 baud X.25 should be ok. * *	Z8530: *	Synchronous mode without DMA is unlikely to pass about 2400 baud. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/net.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/delay.h>#include <linux/ioport.h>#include <asm/dma.h>#include <asm/io.h>#define RT_LOCK#define RT_UNLOCK#include <asm/spinlock.h>#include "z85230.h"static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED;/* *	Provided port access methods. The Comtrol SV11 requires no delays *	between accesses and uses PC I/O. Some drivers may need a 5uS delay */extern __inline__ int z8530_read_port(int p){	u8 r=inb(Z8530_PORT_OF(p));	if(p&Z8530_PORT_SLEEP)	/* gcc should figure this out efficiently ! */		udelay(5);	return r;}extern __inline__ void z8530_write_port(int p, u8 d){	outb(d,Z8530_PORT_OF(p));	if(p&Z8530_PORT_SLEEP)		udelay(5);}static void z8530_rx_done(struct z8530_channel *c);static void z8530_tx_done(struct z8530_channel *c);/* *	Port accesses */ extern inline u8 read_zsreg(struct z8530_channel *c, u8 reg){	u8 r;	unsigned long flags;	save_flags(flags);	cli();	if(reg)		z8530_write_port(c->ctrlio, reg);	r=z8530_read_port(c->ctrlio);	restore_flags(flags);	return r;}extern inline u8 read_zsdata(struct z8530_channel *c){	u8 r;	r=z8530_read_port(c->dataio);	return r;}extern inline void write_zsreg(struct z8530_channel *c, u8 reg, u8 val){	unsigned long flags;	save_flags(flags);	cli();	if(reg)		z8530_write_port(c->ctrlio, reg);	z8530_write_port(c->ctrlio, val);	restore_flags(flags);}extern inline void write_zsctrl(struct z8530_channel *c, u8 val){	z8530_write_port(c->ctrlio, val);}extern inline void write_zsdata(struct z8530_channel *c, u8 val){	z8530_write_port(c->dataio, val);}/* *	Register loading parameters for a dead port */ u8 z8530_dead_port[]={	255};EXPORT_SYMBOL(z8530_dead_port);/* *	Register loading parameters for currently supported circuit types *//* *	Data clocked by telco end. This is the correct data for the UK *	"kilostream" service, and most other similar services. */ u8 z8530_hdlc_kilostream[]={	4,	SYNC_ENAB|SDLC|X1CLK,	2,	0,	/* No vector */	1,	0,	3,	ENT_HM|RxCRC_ENAB|Rx8,	5,	TxCRC_ENAB|RTS|TxENAB|Tx8|DTR,	9,	0,		/* Disable interrupts */	6,	0xFF,	7,	FLAG,	10,	ABUNDER|NRZ|CRCPS,/*MARKIDLE ??*/	11,	TCTRxCP,	14,	DISDPLL,	15,	DCDIE|SYNCIE|CTSIE|TxUIE|BRKIE,	1,	EXT_INT_ENAB|TxINT_ENAB|INT_ALL_Rx,	9,	NV|MIE|NORESET,	255};EXPORT_SYMBOL(z8530_hdlc_kilostream);/* *	As above but for enhanced chips. */u8 z8530_hdlc_kilostream_85230[]={	4,	SYNC_ENAB|SDLC|X1CLK,	2,	0,	/* No vector */	1,	0,	3,	ENT_HM|RxCRC_ENAB|Rx8,	5,	TxCRC_ENAB|RTS|TxENAB|Tx8|DTR,	9,	0,		/* Disable interrupts */	6,	0xFF,	7,	FLAG,	10,	ABUNDER|NRZ|CRCPS,	/* MARKIDLE?? */	11,	TCTRxCP,	14,	DISDPLL,	15,	DCDIE|SYNCIE|CTSIE|TxUIE|BRKIE,	1,	EXT_INT_ENAB|TxINT_ENAB|INT_ALL_Rx,	9,	NV|MIE|NORESET,	23,	3,		/* Extended mode AUTO TX and EOM*/	31,	3,		/* Extended mode AUTO TX and EOM*/		255};EXPORT_SYMBOL(z8530_hdlc_kilostream_85230);/* *	Flush the FIFO */ static void z8530_flush_fifo(struct z8530_channel *c){	read_zsreg(c, R1);	read_zsreg(c, R1);	read_zsreg(c, R1);	read_zsreg(c, R1);	if(c->dev->type==Z85230)	{		read_zsreg(c, R1);		read_zsreg(c, R1);		read_zsreg(c, R1);		read_zsreg(c, R1);	}}	/* Sets or clears DTR/RTS on the requested line */static void z8530_rtsdtr(struct z8530_channel *c, int set){	if (set)		c->regs[5] |= (RTS | DTR);	else		c->regs[5] &= ~(RTS | DTR);	write_zsreg(c, R5, c->regs[5]);}/* *	Receive handler. This is much like the async one but not quite the *	same or as complex * *	Note: Its intended that this handler can easily be separated from *	the main code to run realtime. That'll be needed for some machines *	(eg to ever clock 64kbits on a sparc ;)). * *	The RT_LOCK macros don't do anything now. Keep the code covered *	by them as short as possible in all circumstances - clocks cost *	baud. The interrupt handler is assumed to be atomic w.r.t. to *	other code - this is true in the RT case too. * *	We only cover the sync cases for this. If you want 2Mbit async *	do it yourself but consider medical assistance first. * *	This non DMA synchronous mode is portable code. */ static void z8530_rx(struct z8530_channel *c){	u8 ch,stat;	 	while(1)	{		/* FIFO empty ? */		if(!(read_zsreg(c, R0)&1))			break;		ch=read_zsdata(c);		stat=read_zsreg(c, R1);			/*		 *	Overrun ?		 */		if(c->count < c->max)		{			*c->dptr++=ch;			c->count++;		}		if(stat&END_FR)		{					/*			 *	Error ?			 */			if(stat&(Rx_OVR|CRC_ERR))			{				/* Rewind the buffer and return */				if(c->skb)					c->dptr=c->skb->data;				c->count=0;				if(stat&Rx_OVR)				{					printk(KERN_WARNING "%s: overrun\n", c->dev->name);					c->rx_overrun++;				}				if(stat&CRC_ERR)				{					c->rx_crc_err++;					/* printk("crc error\n"); */				}				/* Shove the frame upstream */			}			else			{				z8530_rx_done(c);				write_zsctrl(c, RES_Rx_CRC);			}		}	}	/*	 *	Clear irq	 */	write_zsctrl(c, ERR_RES);	write_zsctrl(c, RES_H_IUS);}/* *	Z8530 transmit interrupt handler */ static void z8530_tx(struct z8530_channel *c){	while(c->txcount)	{		/* FIFO full ? */		if(!(read_zsreg(c, R0)&4))			break;		c->txcount--;		/*		 *	Shovel out the byte		 */		write_zsreg(c, R8, *c->tx_ptr++);		write_zsctrl(c, RES_H_IUS);		/* We are about to underflow */		if(c->txcount==0)		{			write_zsctrl(c, RES_EOM_L);			write_zsreg(c, R10, c->regs[10]&~ABUNDER);		}		return;	}		/*	 *	End of frame TX - fire another one	 */	 	write_zsctrl(c, RES_Tx_P);	z8530_tx_done(c);	 /*	write_zsreg(c, R8, *c->tx_ptr++); */	write_zsctrl(c, RES_H_IUS);}static void z8530_status(struct z8530_channel *chan){	u8 status=read_zsreg(chan, R0);	u8 altered=chan->status^status;		chan->status=status;		if(status&TxEOM)	{/*		printk("%s: Tx underrun.\n", chan->dev->name); */		chan->stats.tx_fifo_errors++;		write_zsctrl(chan, ERR_RES);		z8530_tx_done(chan);	}			if(altered&DCD)	{		if(status&DCD)		{			printk(KERN_INFO "%s: DCD raised\n", chan->dev->name);			write_zsreg(chan, R3, chan->regs[3]|RxENABLE);			if(chan->netdevice)				sppp_reopen(chan->netdevice);		}		else		{			printk(KERN_INFO "%s: DCD lost\n", chan->dev->name);			write_zsreg(chan, R3, chan->regs[3]&~RxENABLE);			z8530_flush_fifo(chan);		}			}		write_zsctrl(chan, RES_EXT_INT);	write_zsctrl(chan, RES_H_IUS);}struct z8530_irqhandler z8530_sync={	z8530_rx,	z8530_tx,	z8530_status};EXPORT_SYMBOL(z8530_sync);/* *	Non bus mastering DMA interfaces for the Z8x30 devices. This *	is really pretty PC specific. */ static void z8530_dma_rx(struct z8530_channel *chan){	if(chan->rxdma_on)	{		/* Special condition check only */		u8 status;		read_zsreg(chan, R7);		read_zsreg(chan, R6);				status=read_zsreg(chan, R1);		if(status&END_FR)		{			z8530_rx_done(chan);	/* Fire up the next one */		}				write_zsctrl(chan, ERR_RES);		write_zsctrl(chan, RES_H_IUS);	}	else	{		/* DMA is off right now, drain the slow way */		z8530_rx(chan);	}	}static void z8530_dma_tx(struct z8530_channel *chan){	if(!chan->dma_tx)	{		printk("Hey who turned the DMA off?\n");		z8530_tx(chan);		return;	}	/* This shouldnt occur in DMA mode */	printk(KERN_ERR "DMA tx ??\n");	z8530_tx(chan);}static void z8530_dma_status(struct z8530_channel *chan){	unsigned long flags;	u8 status=read_zsreg(chan, R0);	u8 altered=chan->status^status;		chan->status=status;	if(chan->dma_tx)	{		if(status&TxEOM)		{			flags=claim_dma_lock();			/* Transmit underrun */			disable_dma(chan->txdma);			clear_dma_ff(chan->txdma);				chan->txdma_on=0;			release_dma_lock(flags);			z8530_tx_done(chan);		}	}	if(altered&DCD)	{		if(status&DCD)		{			printk(KERN_INFO "%s: DCD raised\n", chan->dev->name);			write_zsreg(chan, R3, chan->regs[3]|RxENABLE);			if(chan->netdevice)				sppp_reopen(chan->netdevice);		}		else		{			printk(KERN_INFO "%s:DCD lost\n", chan->dev->name);			write_zsreg(chan, R3, chan->regs[3]&~RxENABLE);			z8530_flush_fifo(chan);		}	}		write_zsctrl(chan, RES_EXT_INT);	write_zsctrl(chan, RES_H_IUS);}struct z8530_irqhandler z8530_dma_sync={	z8530_dma_rx,	z8530_dma_tx,	z8530_dma_status};EXPORT_SYMBOL(z8530_dma_sync);struct z8530_irqhandler z8530_txdma_sync={	z8530_rx,	z8530_dma_tx,	z8530_dma_status};EXPORT_SYMBOL(z8530_txdma_sync);/* *	Interrupt vectors for a Z8530 that is in 'parked' mode. *	For machines with PCI Z85x30 cards, or level triggered interrupts *	(eg the MacII) we must clear the interrupt cause or die. */static void z8530_rx_clear(struct z8530_channel *c){	/*	 *	Data and status bytes	 */	u8 stat;	read_zsdata(c);	stat=read_zsreg(c, R1);		if(stat&END_FR)		write_zsctrl(c, RES_Rx_CRC);	/*	 *	Clear irq	 */	write_zsctrl(c, ERR_RES);	write_zsctrl(c, RES_H_IUS);}static void z8530_tx_clear(struct z8530_channel *c){	write_zsctrl(c, RES_Tx_P);	write_zsctrl(c, RES_H_IUS);}static void z8530_status_clear(struct z8530_channel *chan){	u8 status=read_zsreg(chan, R0);	if(status&TxEOM)		write_zsctrl(chan, ERR_RES);	write_zsctrl(chan, RES_EXT_INT);	write_zsctrl(chan, RES_H_IUS);}struct z8530_irqhandler z8530_nop={	z8530_rx_clear,	z8530_tx_clear,	z8530_status_clear};EXPORT_SYMBOL(z8530_nop);/* *	A Z85[2]30 device has stuck its hand in the air for attention */void z8530_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct z8530_dev *dev=dev_id;	u8 intr;	static volatile int locker=0;	int work=0;		if(locker)	{		printk(KERN_ERR "IRQ re-enter\n");		return;	}	locker=1;		while(++work<5000)	{		struct z8530_irqhandler *irqs=dev->chanA.irqs;				intr = read_zsreg(&dev->chanA, R3);		if(!(intr & (CHARxIP|CHATxIP|CHAEXT|CHBRxIP|CHBTxIP|CHBEXT)))			break;			/* This holds the IRQ status. On the 8530 you must read it from chan 		   A even though it applies to the whole chip */				/* Now walk the chip and see what it is wanting - it may be		   an IRQ for someone else remember */		   		if(intr & (CHARxIP|CHATxIP|CHAEXT))		{			if(intr&CHARxIP)				irqs->rx(&dev->chanA);			if(intr&CHATxIP)				irqs->tx(&dev->chanA);			if(intr&CHAEXT)				irqs->status(&dev->chanA);		}		irqs=dev->chanB.irqs;		if(intr & (CHBRxIP|CHBTxIP|CHBEXT))		{			if(intr&CHBRxIP)				irqs->rx(&dev->chanB);			if(intr&CHBTxIP)				irqs->tx(&dev->chanB);			if(intr&CHBEXT)				irqs->status(&dev->chanB);		}	}	if(work==5000)		printk(KERN_ERR "%s: interrupt jammed - abort(0x%X)!\n", dev->name, intr);	/* Ok all done */	locker=0;}EXPORT_SYMBOL(z8530_interrupt);static char reg_init[16]={	0,0,0,0,	0,0,0,0,	0,0,0,0,	0x55,0,0,0};int z8530_sync_open(struct device *dev, struct z8530_channel *c){	c->sync = 1;	c->mtu = dev->mtu+64;	c->count = 0;	c->skb = NULL;	c->skb2 = NULL;	c->irqs = &z8530_sync;	/* This loads the double buffer up */	z8530_rx_done(c);	/* Load the frame ring */	z8530_rx_done(c);	/* Load the backup frame */	z8530_rtsdtr(c,1);	c->dma_tx = 0;	c->regs[R1]|=TxINT_ENAB;	write_zsreg(c, R1, c->regs[R1]);	write_zsreg(c, R3, c->regs[R3]|RxENABLE);	return 0;}EXPORT_SYMBOL(z8530_sync_open);int z8530_sync_close(struct device *dev, struct z8530_channel *c){	u8 chk;	c->irqs = &z8530_nop;	c->max = 0;	c->sync = 0;		chk=read_zsreg(c,R0);	write_zsreg(c, R3, c->regs[R3]);	z8530_rtsdtr(c,0);	return 0;}EXPORT_SYMBOL(z8530_sync_close);int z8530_sync_dma_open(struct device *dev, struct z8530_channel *c){	unsigned long flags;		c->sync = 1;	c->mtu = dev->mtu+64;	c->count = 0;	c->skb = NULL;	c->skb2 = NULL;	/*	 *	Load the DMA interfaces up	 */	c->rxdma_on = 0;	c->txdma_on = 0;		/*	 *	Allocate the DMA flip buffers	 */	 	c->rx_buf[0]=kmalloc(c->mtu, GFP_KERNEL|GFP_DMA);	if(c->rx_buf[0]==NULL)		return -ENOBUFS;	c->rx_buf[1]=kmalloc(c->mtu, GFP_KERNEL|GFP_DMA);	if(c->rx_buf[1]==NULL)	{		kfree(c->rx_buf[0]);		c->rx_buf[0]=NULL;		return -ENOBUFS;	}		c->tx_dma_buf[0]=kmalloc(c->mtu, GFP_KERNEL|GFP_DMA);	if(c->tx_dma_buf[0]==NULL)	{		kfree(c->rx_buf[0]);		kfree(c->rx_buf[1]);		c->rx_buf[0]=NULL;		return -ENOBUFS;	}	c->tx_dma_buf[1]=kmalloc(c->mtu, GFP_KERNEL|GFP_DMA);	if(c->tx_dma_buf[1]==NULL)	{		kfree(c->tx_dma_buf[0]);		kfree(c->rx_buf[0]);		kfree(c->rx_buf[1]);		c->rx_buf[0]=NULL;		c->rx_buf[1]=NULL;		c->tx_dma_buf[0]=NULL;		return -ENOBUFS;	}	c->tx_dma_used=0;	c->dma_tx = 1;	c->dma_num=0;	c->dma_ready=1;		/*	 *	Enable DMA control mode	 */	 	/*	 *	TX DMA via DIR/REQ	 */	 	c->regs[R14]|= DTRREQ;	write_zsreg(c, R14, c->regs[R14]);     	c->regs[R1]&= ~TxINT_ENAB;	write_zsreg(c, R1, c->regs[R1]);		/*	 *	RX DMA via W/Req	 */	 	c->regs[R1]|= WT_FN_RDYFN;	c->regs[R1]|= WT_RDY_RT;	c->regs[R1]|= INT_ERR_Rx;	c->regs[R1]&= ~TxINT_ENAB;	write_zsreg(c, R1, c->regs[R1]);	c->regs[R1]|= WT_RDY_ENAB;	write_zsreg(c, R1, c->regs[R1]);            		/*

⌨️ 快捷键说明

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