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

📄 ax88796.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* drivers/net/ax88796.c * * Copyright 2005,2007 Simtec Electronics *	Ben Dooks <ben@simtec.co.uk> * * Asix AX88796 10/100 Ethernet controller support *	Based on ne.c, by Donald Becker, et-al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation.*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/isapnp.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/eeprom_93cx6.h>#include <net/ax88796.h>#include <asm/system.h>#include <asm/io.h>static int phy_debug = 0;/* Rename the lib8390.c functions to show that they are in this driver */#define __ei_open       ax_ei_open#define __ei_close      ax_ei_close#define __ei_poll	ax_ei_poll#define __ei_tx_timeout ax_ei_tx_timeout#define __ei_interrupt  ax_ei_interrupt#define ____alloc_ei_netdev ax__alloc_ei_netdev#define __NS8390_init   ax_NS8390_init/* force unsigned long back to 'void __iomem *' */#define ax_convert_addr(_a) ((void __force __iomem *)(_a))#define ei_inb(_a)	readb(ax_convert_addr(_a))#define ei_outb(_v, _a) writeb(_v, ax_convert_addr(_a))#define ei_inb_p(_a)	ei_inb(_a)#define ei_outb_p(_v, _a) ei_outb(_v, _a)/* define EI_SHIFT() to take into account our register offsets */#define EI_SHIFT(x)     (ei_local->reg_offset[(x)])/* Ensure we have our RCR base value */#define AX88796_PLATFORMstatic unsigned char version[] = "ax88796.c: Copyright 2005,2007 Simtec Electronics\n";#include "lib8390.c"#define DRV_NAME "ax88796"#define DRV_VERSION "1.00"/* from ne.c */#define NE_CMD		EI_SHIFT(0x00)#define NE_RESET	EI_SHIFT(0x1f)#define NE_DATAPORT	EI_SHIFT(0x10)#define NE1SM_START_PG	0x20	/* First page of TX buffer */#define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */#define NESM_START_PG	0x40	/* First page of TX buffer */#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring *//* device private data */struct ax_device {	struct timer_list	 mii_timer;	spinlock_t		 mii_lock;	struct mii_if_info	 mii;	u32			 msg_enable;	void __iomem		*map2;	struct platform_device	*dev;	struct resource		*mem;	struct resource		*mem2;	struct ax_plat_data	*plat;	unsigned char		 running;	unsigned char		 resume_open;	u32			 reg_offsets[0x20];};static inline struct ax_device *to_ax_dev(struct net_device *dev){	struct ei_device *ei_local = netdev_priv(dev);	return (struct ax_device *)(ei_local+1);}/* ax_initial_check * * do an initial probe for the card to check wether it exists * and is functional */static int ax_initial_check(struct net_device *dev){	struct ei_device *ei_local = netdev_priv(dev);	void __iomem *ioaddr = ei_local->mem;	int reg0;	int regd;	reg0 = ei_inb(ioaddr);	if (reg0 == 0xFF)		return -ENODEV;	ei_outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);	regd = ei_inb(ioaddr + 0x0d);	ei_outb(0xff, ioaddr + 0x0d);	ei_outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);	ei_inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */	if (ei_inb(ioaddr + EN0_COUNTER0) != 0) {		ei_outb(reg0, ioaddr);		ei_outb(regd, ioaddr + 0x0d);	/* Restore the old values. */		return -ENODEV;	}	return 0;}/* Hard reset the card.  This used to pause for the same period that a   8390 reset command required, but that shouldn't be necessary. */static void ax_reset_8390(struct net_device *dev){	struct ei_device *ei_local = netdev_priv(dev);	unsigned long reset_start_time = jiffies;	void __iomem *addr = (void __iomem *)dev->base_addr;	if (ei_debug > 1)		printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies);	ei_outb(ei_inb(addr + NE_RESET), addr + NE_RESET);	ei_status.txing = 0;	ei_status.dmaing = 0;	/* This check _should_not_ be necessary, omit eventually. */	while ((ei_inb(addr + EN0_ISR) & ENISR_RESET) == 0) {		if (jiffies - reset_start_time > 2*HZ/100) {			printk(KERN_WARNING "%s: %s did not complete.\n",			       __FUNCTION__, dev->name);			break;		}	}	ei_outb(ENISR_RESET, addr + EN0_ISR);	/* Ack intr. */}static void ax_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,			    int ring_page){	struct ei_device *ei_local = netdev_priv(dev);	void __iomem *nic_base = ei_local->mem;	/* This *shouldn't* happen. If it does, it's the last thing you'll see */	if (ei_status.dmaing) {		printk(KERN_EMERG "%s: DMAing conflict in %s [DMAstat:%d][irqlock:%d].\n",			dev->name, __FUNCTION__,		       ei_status.dmaing, ei_status.irqlock);		return;	}	ei_status.dmaing |= 0x01;	ei_outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);	ei_outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);	ei_outb(0, nic_base + EN0_RCNTHI);	ei_outb(0, nic_base + EN0_RSARLO);		/* On page boundary */	ei_outb(ring_page, nic_base + EN0_RSARHI);	ei_outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);	if (ei_status.word16)		readsw(nic_base + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);	else		readsb(nic_base + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));	ei_outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */	ei_status.dmaing &= ~0x01;	le16_to_cpus(&hdr->count);}/* Block input and output, similar to the Crynwr packet driver.  If you   are porting to a new ethercard, look at the packet driver source for hints.   The NEx000 doesn't share the on-board packet memory -- you have to put   the packet out through the "remote DMA" dataport using ei_outb. */static void ax_block_input(struct net_device *dev, int count,			   struct sk_buff *skb, int ring_offset){	struct ei_device *ei_local = netdev_priv(dev);	void __iomem *nic_base = ei_local->mem;	char *buf = skb->data;	if (ei_status.dmaing) {		printk(KERN_EMERG "%s: DMAing conflict in ax_block_input "			"[DMAstat:%d][irqlock:%d].\n",			dev->name, ei_status.dmaing, ei_status.irqlock);		return;	}	ei_status.dmaing |= 0x01;	ei_outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);	ei_outb(count & 0xff, nic_base + EN0_RCNTLO);	ei_outb(count >> 8, nic_base + EN0_RCNTHI);	ei_outb(ring_offset & 0xff, nic_base + EN0_RSARLO);	ei_outb(ring_offset >> 8, nic_base + EN0_RSARHI);	ei_outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);	if (ei_status.word16) {		readsw(nic_base + NE_DATAPORT, buf, count >> 1);		if (count & 0x01)			buf[count-1] = ei_inb(nic_base + NE_DATAPORT);	} else {		readsb(nic_base + NE_DATAPORT, buf, count);	}	ei_status.dmaing &= ~1;}static void ax_block_output(struct net_device *dev, int count,			    const unsigned char *buf, const int start_page){	struct ei_device *ei_local = netdev_priv(dev);	void __iomem *nic_base = ei_local->mem;	unsigned long dma_start;	/* Round the count up for word writes.  Do we need to do this?	   What effect will an odd byte count have on the 8390?	   I should check someday. */	if (ei_status.word16 && (count & 0x01))		count++;	/* This *shouldn't* happen. If it does, it's the last thing you'll see */	if (ei_status.dmaing) {		printk(KERN_EMERG "%s: DMAing conflict in %s."			"[DMAstat:%d][irqlock:%d]\n",			dev->name, __FUNCTION__,		       ei_status.dmaing, ei_status.irqlock);		return;	}	ei_status.dmaing |= 0x01;	/* We should already be in page 0, but to be safe... */	ei_outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);	ei_outb(ENISR_RDC, nic_base + EN0_ISR);	/* Now the normal output. */	ei_outb(count & 0xff, nic_base + EN0_RCNTLO);	ei_outb(count >> 8,   nic_base + EN0_RCNTHI);	ei_outb(0x00, nic_base + EN0_RSARLO);	ei_outb(start_page, nic_base + EN0_RSARHI);	ei_outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);	if (ei_status.word16) {		writesw(nic_base + NE_DATAPORT, buf, count>>1);	} else {		writesb(nic_base + NE_DATAPORT, buf, count);	}	dma_start = jiffies;	while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) {		if (jiffies - dma_start > 2*HZ/100) {		/* 20ms */			printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);			ax_reset_8390(dev);			ax_NS8390_init(dev,1);			break;		}	}	ei_outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */	ei_status.dmaing &= ~0x01;	return;}/* definitions for accessing MII/EEPROM interface */#define AX_MEMR			EI_SHIFT(0x14)#define AX_MEMR_MDC		(1<<0)#define AX_MEMR_MDIR		(1<<1)#define AX_MEMR_MDI		(1<<2)#define AX_MEMR_MDO		(1<<3)#define AX_MEMR_EECS		(1<<4)#define AX_MEMR_EEI		(1<<5)#define AX_MEMR_EEO		(1<<6)#define AX_MEMR_EECLK		(1<<7)/* ax_mii_ei_outbits * * write the specified set of bits to the phy*/static voidax_mii_ei_outbits(struct net_device *dev, unsigned int bits, int len){	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);	void __iomem *memr_addr = (void __iomem *)dev->base_addr + AX_MEMR;	unsigned int memr;	/* clock low, data to output mode */	memr = ei_inb(memr_addr);	memr &= ~(AX_MEMR_MDC | AX_MEMR_MDIR);	ei_outb(memr, memr_addr);	for (len--; len >= 0; len--) {		if (bits & (1 << len))			memr |= AX_MEMR_MDO;		else			memr &= ~AX_MEMR_MDO;		ei_outb(memr, memr_addr);		/* clock high */		ei_outb(memr | AX_MEMR_MDC, memr_addr);		udelay(1);		/* clock low */		ei_outb(memr, memr_addr);	}	/* leaves the clock line low, mdir input */	memr |= AX_MEMR_MDIR;	ei_outb(memr, (void __iomem *)dev->base_addr + AX_MEMR);}/* ax_phy_ei_inbits * * read a specified number of bits from the phy*/static unsigned intax_phy_ei_inbits(struct net_device *dev, int no){	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);	void __iomem *memr_addr = (void __iomem *)dev->base_addr + AX_MEMR;	unsigned int memr;	unsigned int result = 0;	/* clock low, data to input mode */	memr = ei_inb(memr_addr);	memr &= ~AX_MEMR_MDC;	memr |= AX_MEMR_MDIR;	ei_outb(memr, memr_addr);	for (no--; no >= 0; no--) {		ei_outb(memr | AX_MEMR_MDC, memr_addr);		udelay(1);		if (ei_inb(memr_addr) & AX_MEMR_MDI)			result |= (1<<no);		ei_outb(memr, memr_addr);	}	return result;}/* ax_phy_issueaddr * * use the low level bit shifting routines to send the address * and command to the specified phy*/static voidax_phy_issueaddr(struct net_device *dev, int phy_addr, int reg, int opc){	if (phy_debug)		pr_debug("%s: dev %p, %04x, %04x, %d\n",			__FUNCTION__, dev, phy_addr, reg, opc);	ax_mii_ei_outbits(dev, 0x3f, 6);	/* pre-amble */	ax_mii_ei_outbits(dev, 1, 2);		/* frame-start */	ax_mii_ei_outbits(dev, opc, 2);		/* op code */	ax_mii_ei_outbits(dev, phy_addr, 5);	/* phy address */	ax_mii_ei_outbits(dev, reg, 5);		/* reg address */}static intax_phy_read(struct net_device *dev, int phy_addr, int reg){	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);	unsigned long flags; 	unsigned int result;      	spin_lock_irqsave(&ei_local->page_lock, flags);	ax_phy_issueaddr(dev, phy_addr, reg, 2);	result = ax_phy_ei_inbits(dev, 17);	result &= ~(3<<16);      	spin_unlock_irqrestore(&ei_local->page_lock, flags);	if (phy_debug)		pr_debug("%s: %04x.%04x => read %04x\n", __FUNCTION__,			 phy_addr, reg, result);	return result;}static voidax_phy_write(struct net_device *dev, int phy_addr, int reg, int value){	struct ei_device *ei = (struct ei_device *) netdev_priv(dev);	unsigned long flags;	printk(KERN_DEBUG "%s: %p, %04x, %04x %04x\n",	       __FUNCTION__, dev, phy_addr, reg, value);      	spin_lock_irqsave(&ei->page_lock, flags);	ax_phy_issueaddr(dev, phy_addr, reg, 1);	ax_mii_ei_outbits(dev, 2, 2);		/* send TA */	ax_mii_ei_outbits(dev, value, 16);      	spin_unlock_irqrestore(&ei->page_lock, flags);}static void ax_mii_expiry(unsigned long data){	struct net_device *dev = (struct net_device *)data;	struct ax_device  *ax = to_ax_dev(dev);	unsigned long flags;	spin_lock_irqsave(&ax->mii_lock, flags);	mii_check_media(&ax->mii, netif_msg_link(ax), 0);	spin_unlock_irqrestore(&ax->mii_lock, flags);	if (ax->running) {		ax->mii_timer.expires = jiffies + HZ*2;		add_timer(&ax->mii_timer);	}}static int ax_open(struct net_device *dev){	struct ax_device  *ax = to_ax_dev(dev);	struct ei_device *ei_local = netdev_priv(dev);	int ret;	dev_dbg(&ax->dev->dev, "%s: open\n", dev->name);	ret = request_irq(dev->irq, ax_ei_interrupt, 0, dev->name, dev);	if (ret)		return ret;	ret = ax_ei_open(dev);	if (ret)		return ret;	/* turn the phy on (if turned off) */	ei_outb(ax->plat->gpoc_val, ei_local->mem + EI_SHIFT(0x17));	ax->running = 1;	/* start the MII timer */	init_timer(&ax->mii_timer);	ax->mii_timer.expires  = jiffies+1;	ax->mii_timer.data     = (unsigned long) dev;	ax->mii_timer.function = ax_mii_expiry;	add_timer(&ax->mii_timer);	return 0;}static int ax_close(struct net_device *dev){	struct ax_device *ax = to_ax_dev(dev);	struct ei_device *ei_local = netdev_priv(dev);	dev_dbg(&ax->dev->dev, "%s: close\n", dev->name);	/* turn the phy off */	ei_outb(ax->plat->gpoc_val | (1<<6),	       ei_local->mem + EI_SHIFT(0x17));

⌨️ 快捷键说明

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