sgiseeq.c

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

C
740
字号
	 * adapter.  The following code is only safe iff the HPCDMA	 * is not active!	 */	while ((td->tdma.cntinfo & (HPCDMA_XIU | HPCDMA_ETXD)) ==	      (HPCDMA_XIU | HPCDMA_ETXD))		td = (struct sgiseeq_tx_desc *)(long) KSEG1ADDR(td->tdma.pnext);	if (td->tdma.cntinfo & HPCDMA_XIU) {		hregs->tx_ndptr = CPHYSADDR(td);		hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE;	}}static inline void sgiseeq_tx(struct net_device *dev, struct sgiseeq_private *sp,			      struct hpc3_ethregs *hregs,			      struct sgiseeq_regs *sregs){	struct sgiseeq_tx_desc *td;	unsigned long status = hregs->tx_ctrl;	int j;	tx_maybe_reset_collisions(sp, sregs);	if (!(status & (HPC3_ETXCTRL_ACTIVE | SEEQ_TSTAT_PTRANS))) {		/* Oops, HPC detected some sort of error. */		if (status & SEEQ_TSTAT_R16)			sp->stats.tx_aborted_errors++;		if (status & SEEQ_TSTAT_UFLOW)			sp->stats.tx_fifo_errors++;		if (status & SEEQ_TSTAT_LCLS)			sp->stats.collisions++;	}	/* Ack 'em... */	for (j = sp->tx_old; j != sp->tx_new; j = NEXT_TX(j)) {		td = &sp->tx_desc[j];		if (!(td->tdma.cntinfo & (HPCDMA_XIU)))			break;		if (!(td->tdma.cntinfo & (HPCDMA_ETXD))) {			if (!(status & HPC3_ETXCTRL_ACTIVE)) {				hregs->tx_ndptr = CPHYSADDR(td);				hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE;			}			break;		}		sp->stats.tx_packets++;		sp->tx_old = NEXT_TX(sp->tx_old);		td->tdma.cntinfo &= ~(HPCDMA_XIU | HPCDMA_XIE);		td->tdma.cntinfo |= HPCDMA_EOX;	}}static irqreturn_t sgiseeq_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct net_device *dev = (struct net_device *) dev_id;	struct sgiseeq_private *sp = netdev_priv(dev);	struct hpc3_ethregs *hregs = sp->hregs;	struct sgiseeq_regs *sregs = sp->sregs;	spin_lock(&sp->tx_lock);	/* Ack the IRQ and set software state. */	hregs->rx_reset = HPC3_ERXRST_CLRIRQ;	/* Always check for received packets. */	sgiseeq_rx(dev, sp, hregs, sregs);	/* Only check for tx acks if we have something queued. */	if (sp->tx_old != sp->tx_new)		sgiseeq_tx(dev, sp, hregs, sregs);	if ((TX_BUFFS_AVAIL(sp) > 0) && netif_queue_stopped(dev)) {		netif_wake_queue(dev);	}	spin_unlock(&sp->tx_lock);	return IRQ_HANDLED;}static int sgiseeq_open(struct net_device *dev){	struct sgiseeq_private *sp = netdev_priv(dev);	struct sgiseeq_regs *sregs = sp->sregs;	unsigned int irq = dev->irq;	int err;	if (request_irq(irq, sgiseeq_interrupt, 0, sgiseeqstr, dev)) {		printk(KERN_ERR "Seeq8003: Can't get irq %d\n", dev->irq);		err = -EAGAIN;	}	err = init_seeq(dev, sp, sregs);	if (err)		goto out_free_irq;	netif_start_queue(dev);	return 0;out_free_irq:	free_irq(irq, dev);	return err;}static int sgiseeq_close(struct net_device *dev){	struct sgiseeq_private *sp = netdev_priv(dev);	struct sgiseeq_regs *sregs = sp->sregs;	netif_stop_queue(dev);	/* Shutdown the Seeq. */	reset_hpc3_and_seeq(sp->hregs, sregs);	return 0;}static inline int sgiseeq_reset(struct net_device *dev){	struct sgiseeq_private *sp = netdev_priv(dev);	struct sgiseeq_regs *sregs = sp->sregs;	int err;	err = init_seeq(dev, sp, sregs);	if (err)		return err;	dev->trans_start = jiffies;	netif_wake_queue(dev);	return 0;}void sgiseeq_my_reset(void){	printk("RESET!\n");	sgiseeq_reset(gdev);}static int sgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct sgiseeq_private *sp = netdev_priv(dev);	struct hpc3_ethregs *hregs = sp->hregs;	unsigned long flags;	struct sgiseeq_tx_desc *td;	int skblen, len, entry;	spin_lock_irqsave(&sp->tx_lock, flags);	/* Setup... */	skblen = skb->len;	len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen;	sp->stats.tx_bytes += len;	entry = sp->tx_new;	td = &sp->tx_desc[entry];	/* Create entry.  There are so many races with adding a new	 * descriptor to the chain:	 * 1) Assume that the HPC is off processing a DMA chain while	 *    we are changing all of the following.	 * 2) Do no allow the HPC to look at a new descriptor until	 *    we have completely set up it's state.  This means, do	 *    not clear HPCDMA_EOX in the current last descritptor	 *    until the one we are adding looks consistent and could	 *    be processes right now.	 * 3) The tx interrupt code must notice when we've added a new	 *    entry and the HPC got to the end of the chain before we	 *    added this new entry and restarted it.	 */	memcpy((char *)(long)td->buf_vaddr, skb->data, skblen);	if (len != skblen)		memset((char *)(long)td->buf_vaddr + skb->len, 0, len-skblen);	td->tdma.cntinfo = (len & HPCDMA_BCNT) |	                   HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX;	if (sp->tx_old != sp->tx_new) {		struct sgiseeq_tx_desc *backend;		backend = &sp->tx_desc[PREV_TX(sp->tx_new)];		backend->tdma.cntinfo &= ~HPCDMA_EOX;	}	sp->tx_new = NEXT_TX(sp->tx_new); /* Advance. */	/* Maybe kick the HPC back into motion. */	if (!(hregs->tx_ctrl & HPC3_ETXCTRL_ACTIVE))		kick_tx(&sp->tx_desc[sp->tx_old], hregs);	dev->trans_start = jiffies;	dev_kfree_skb(skb);	if (!TX_BUFFS_AVAIL(sp))		netif_stop_queue(dev);	spin_unlock_irqrestore(&sp->tx_lock, flags);	return 0;}static void timeout(struct net_device *dev){	printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name);	sgiseeq_reset(dev);	dev->trans_start = jiffies;	netif_wake_queue(dev);}static struct net_device_stats *sgiseeq_get_stats(struct net_device *dev){	struct sgiseeq_private *sp = netdev_priv(dev);	return &sp->stats;}static void sgiseeq_set_multicast(struct net_device *dev){}static inline void setup_tx_ring(struct sgiseeq_tx_desc *buf, int nbufs){	int i = 0;	while (i < (nbufs - 1)) {		buf[i].tdma.pnext = CPHYSADDR(buf + i + 1);		buf[i].tdma.pbuf = 0;		i++;	}	buf[i].tdma.pnext = CPHYSADDR(buf);}static inline void setup_rx_ring(struct sgiseeq_rx_desc *buf, int nbufs){	int i = 0;	while (i < (nbufs - 1)) {		buf[i].rdma.pnext = CPHYSADDR(buf + i + 1);		buf[i].rdma.pbuf = 0;		i++;	}	buf[i].rdma.pbuf = 0;	buf[i].rdma.pnext = CPHYSADDR(buf);}#define ALIGNED(x)  ((((unsigned long)(x)) + 0xf) & ~(0xf))static int sgiseeq_init(struct hpc3_regs* regs, int irq){	struct sgiseeq_init_block *sr;	struct sgiseeq_private *sp;	struct net_device *dev;	int err, i;	dev = alloc_etherdev(sizeof (struct sgiseeq_private));	if (!dev) {		printk(KERN_ERR "Sgiseeq: Etherdev alloc failed, aborting.\n");		err = -ENOMEM;		goto err_out;	}	sp = netdev_priv(dev);	/* Make private data page aligned */	sr = (struct sgiseeq_init_block *) get_zeroed_page(GFP_KERNEL);	if (!sr) {		printk(KERN_ERR "Sgiseeq: Page alloc failed, aborting.\n");		err = -ENOMEM;		goto err_out_free_dev;	}	sp->srings = sr;#define EADDR_NVOFS     250	for (i = 0; i < 3; i++) {		unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i);		dev->dev_addr[2 * i]     = tmp >> 8;		dev->dev_addr[2 * i + 1] = tmp & 0xff;	}#ifdef DEBUG	gpriv = sp;	gdev = dev;#endif	sp->sregs = (struct sgiseeq_regs *) &hpc3c0->eth_ext[0];	sp->hregs = &hpc3c0->ethregs;	sp->name = sgiseeqstr;	sp->rx_desc = (struct sgiseeq_rx_desc *)	              KSEG1ADDR(ALIGNED(&sp->srings->rxvector[0]));	dma_cache_wback_inv((unsigned long)&sp->srings->rxvector,	                    sizeof(sp->srings->rxvector));	sp->tx_desc = (struct sgiseeq_tx_desc *)	              KSEG1ADDR(ALIGNED(&sp->srings->txvector[0]));	dma_cache_wback_inv((unsigned long)&sp->srings->txvector,	                    sizeof(sp->srings->txvector));	/* A couple calculations now, saves many cycles later. */	setup_rx_ring(sp->rx_desc, SEEQ_RX_BUFFERS);	setup_tx_ring(sp->tx_desc, SEEQ_TX_BUFFERS);	/* Reset the chip. */	hpc3_eth_reset(sp->hregs);	sp->is_edlc = !(sp->sregs->rw.rregs.collision_tx[0] & 0xff);	if (sp->is_edlc)		sp->control = SEEQ_CTRL_XCNT | SEEQ_CTRL_ACCNT |			      SEEQ_CTRL_SFLAG | SEEQ_CTRL_ESHORT |			      SEEQ_CTRL_ENCARR;	dev->open		= sgiseeq_open;	dev->stop		= sgiseeq_close;	dev->hard_start_xmit	= sgiseeq_start_xmit;	dev->tx_timeout		= timeout;	dev->watchdog_timeo	= (200 * HZ) / 1000;	dev->get_stats		= sgiseeq_get_stats;	dev->set_multicast_list	= sgiseeq_set_multicast;	dev->irq		= irq;	if (register_netdev(dev)) {		printk(KERN_ERR "Sgiseeq: Cannot register net device, "		       "aborting.\n");		err = -ENODEV;		goto err_out_free_page;	}	printk(KERN_INFO "%s: SGI Seeq8003 ", dev->name);	for (i = 0; i < 6; i++)		printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':');	sp->next_module = root_sgiseeq_dev;	root_sgiseeq_dev = dev;	return 0;err_out_free_page:	free_page((unsigned long) sp);err_out_free_dev:	kfree(dev);err_out:	return err;}static int __init sgiseeq_probe(void){	printk(version);	/* On board adapter on 1st HPC is always present */	return sgiseeq_init(hpc3c0, SGI_ENET_IRQ);}static void __exit sgiseeq_exit(void){	struct net_device *next, *dev;	struct sgiseeq_private *sp;	int irq;	for (dev = root_sgiseeq_dev; dev; dev = next) {		sp = (struct sgiseeq_private *) netdev_priv(dev);		next = sp->next_module;		irq = dev->irq;		unregister_netdev(dev);		free_irq(irq, dev);		free_page((unsigned long) sp);		free_netdev(dev);	}}module_init(sgiseeq_probe);module_exit(sgiseeq_exit);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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