natsemi.c

来自「linux 内核源代码」· C语言 代码 · 共 2,280 行 · 第 1/5 页

C
2,280
字号
#define RX_DRTH_VAL		(128/8)enum ClkRun_bits {	PMEEnable		= 0x100,	PMEStatus		= 0x8000,};enum WolCmd_bits {	WakePhy			= 0x1,	WakeUnicast		= 0x2,	WakeMulticast		= 0x4,	WakeBroadcast		= 0x8,	WakeArp			= 0x10,	WakePMatch0		= 0x20,	WakePMatch1		= 0x40,	WakePMatch2		= 0x80,	WakePMatch3		= 0x100,	WakeMagic		= 0x200,	WakeMagicSecure		= 0x400,	SecureHack		= 0x100000,	WokePhy			= 0x400000,	WokeUnicast		= 0x800000,	WokeMulticast		= 0x1000000,	WokeBroadcast		= 0x2000000,	WokeArp			= 0x4000000,	WokePMatch0		= 0x8000000,	WokePMatch1		= 0x10000000,	WokePMatch2		= 0x20000000,	WokePMatch3		= 0x40000000,	WokeMagic		= 0x80000000,	WakeOptsSummary		= 0x7ff};enum RxFilterAddr_bits {	RFCRAddressMask		= 0x3ff,	AcceptMulticast		= 0x00200000,	AcceptMyPhys		= 0x08000000,	AcceptAllPhys		= 0x10000000,	AcceptAllMulticast	= 0x20000000,	AcceptBroadcast		= 0x40000000,	RxFilterEnable		= 0x80000000};enum StatsCtrl_bits {	StatsWarn		= 0x1,	StatsFreeze		= 0x2,	StatsClear		= 0x4,	StatsStrobe		= 0x8,};enum MIntrCtrl_bits {	MICRIntEn		= 0x2,};enum PhyCtrl_bits {	PhyAddrMask		= 0x1f,};#define PHY_ADDR_NONE		32#define PHY_ADDR_INTERNAL	1/* values we might find in the silicon revision register */#define SRR_DP83815_C	0x0302#define SRR_DP83815_D	0x0403#define SRR_DP83816_A4	0x0504#define SRR_DP83816_A5	0x0505/* The Rx and Tx buffer descriptors. *//* Note that using only 32 bit fields simplifies conversion to big-endian   architectures. */struct netdev_desc {	u32 next_desc;	s32 cmd_status;	u32 addr;	u32 software_use;};/* Bits in network_desc.status */enum desc_status_bits {	DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000,	DescNoCRC=0x10000000, DescPktOK=0x08000000,	DescSizeMask=0xfff,	DescTxAbort=0x04000000, DescTxFIFO=0x02000000,	DescTxCarrier=0x01000000, DescTxDefer=0x00800000,	DescTxExcDefer=0x00400000, DescTxOOWCol=0x00200000,	DescTxExcColl=0x00100000, DescTxCollCount=0x000f0000,	DescRxAbort=0x04000000, DescRxOver=0x02000000,	DescRxDest=0x01800000, DescRxLong=0x00400000,	DescRxRunt=0x00200000, DescRxInvalid=0x00100000,	DescRxCRC=0x00080000, DescRxAlign=0x00040000,	DescRxLoop=0x00020000, DesRxColl=0x00010000,};struct netdev_private {	/* Descriptor rings first for alignment */	dma_addr_t ring_dma;	struct netdev_desc *rx_ring;	struct netdev_desc *tx_ring;	/* The addresses of receive-in-place skbuffs */	struct sk_buff *rx_skbuff[RX_RING_SIZE];	dma_addr_t rx_dma[RX_RING_SIZE];	/* address of a sent-in-place packet/buffer, for later free() */	struct sk_buff *tx_skbuff[TX_RING_SIZE];	dma_addr_t tx_dma[TX_RING_SIZE];	struct net_device *dev;	struct napi_struct napi;	struct net_device_stats stats;	/* Media monitoring timer */	struct timer_list timer;	/* Frequently used values: keep some adjacent for cache effect */	struct pci_dev *pci_dev;	struct netdev_desc *rx_head_desc;	/* Producer/consumer ring indices */	unsigned int cur_rx, dirty_rx;	unsigned int cur_tx, dirty_tx;	/* Based on MTU+slack. */	unsigned int rx_buf_sz;	int oom;	/* Interrupt status */	u32 intr_status;	/* Do not touch the nic registers */	int hands_off;	/* Don't pay attention to the reported link state. */	int ignore_phy;	/* external phy that is used: only valid if dev->if_port != PORT_TP */	int mii;	int phy_addr_external;	unsigned int full_duplex;	/* Rx filter */	u32 cur_rx_mode;	u32 rx_filter[16];	/* FIFO and PCI burst thresholds */	u32 tx_config, rx_config;	/* original contents of ClkRun register */	u32 SavedClkRun;	/* silicon revision */	u32 srr;	/* expected DSPCFG value */	u16 dspcfg;	int dspcfg_workaround;	/* parms saved in ethtool format */	u16	speed;		/* The forced speed, 10Mb, 100Mb, gigabit */	u8	duplex;		/* Duplex, half or full */	u8	autoneg;	/* Autonegotiation enabled */	/* MII transceiver section */	u16 advertising;	unsigned int iosize;	spinlock_t lock;	u32 msg_enable;	/* EEPROM data */	int eeprom_size;};static void move_int_phy(struct net_device *dev, int addr);static int eeprom_read(void __iomem *ioaddr, int location);static int mdio_read(struct net_device *dev, int reg);static void mdio_write(struct net_device *dev, int reg, u16 data);static void init_phy_fixup(struct net_device *dev);static int miiport_read(struct net_device *dev, int phy_id, int reg);static void miiport_write(struct net_device *dev, int phy_id, int reg, u16 data);static int find_mii(struct net_device *dev);static void natsemi_reset(struct net_device *dev);static void natsemi_reload_eeprom(struct net_device *dev);static void natsemi_stop_rxtx(struct net_device *dev);static int netdev_open(struct net_device *dev);static void do_cable_magic(struct net_device *dev);static void undo_cable_magic(struct net_device *dev);static void check_link(struct net_device *dev);static void netdev_timer(unsigned long data);static void dump_ring(struct net_device *dev);static void tx_timeout(struct net_device *dev);static int alloc_ring(struct net_device *dev);static void refill_rx(struct net_device *dev);static void init_ring(struct net_device *dev);static void drain_tx(struct net_device *dev);static void drain_ring(struct net_device *dev);static void free_ring(struct net_device *dev);static void reinit_ring(struct net_device *dev);static void init_registers(struct net_device *dev);static int start_tx(struct sk_buff *skb, struct net_device *dev);static irqreturn_t intr_handler(int irq, void *dev_instance);static void netdev_error(struct net_device *dev, int intr_status);static int natsemi_poll(struct napi_struct *napi, int budget);static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do);static void netdev_tx_done(struct net_device *dev);static int natsemi_change_mtu(struct net_device *dev, int new_mtu);#ifdef CONFIG_NET_POLL_CONTROLLERstatic void natsemi_poll_controller(struct net_device *dev);#endifstatic void __set_rx_mode(struct net_device *dev);static void set_rx_mode(struct net_device *dev);static void __get_stats(struct net_device *dev);static struct net_device_stats *get_stats(struct net_device *dev);static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static int netdev_set_wol(struct net_device *dev, u32 newval);static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur);static int netdev_set_sopass(struct net_device *dev, u8 *newval);static int netdev_get_sopass(struct net_device *dev, u8 *data);static int netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd);static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd);static void enable_wol_mode(struct net_device *dev, int enable_intr);static int netdev_close(struct net_device *dev);static int netdev_get_regs(struct net_device *dev, u8 *buf);static int netdev_get_eeprom(struct net_device *dev, u8 *buf);static const struct ethtool_ops ethtool_ops;#define NATSEMI_ATTR(_name) \static ssize_t natsemi_show_##_name(struct device *dev, \         struct device_attribute *attr, char *buf); \	 static ssize_t natsemi_set_##_name(struct device *dev, \		struct device_attribute *attr, \	        const char *buf, size_t count); \	 static DEVICE_ATTR(_name, 0644, natsemi_show_##_name, natsemi_set_##_name)#define NATSEMI_CREATE_FILE(_dev, _name) \         device_create_file(&_dev->dev, &dev_attr_##_name)#define NATSEMI_REMOVE_FILE(_dev, _name) \         device_remove_file(&_dev->dev, &dev_attr_##_name)NATSEMI_ATTR(dspcfg_workaround);static ssize_t natsemi_show_dspcfg_workaround(struct device *dev,				  	      struct device_attribute *attr, 					      char *buf){	struct netdev_private *np = netdev_priv(to_net_dev(dev));	return sprintf(buf, "%s\n", np->dspcfg_workaround ? "on" : "off");}static ssize_t natsemi_set_dspcfg_workaround(struct device *dev,					     struct device_attribute *attr,					     const char *buf, size_t count){	struct netdev_private *np = netdev_priv(to_net_dev(dev));	int new_setting;	unsigned long flags;        /* Find out the new setting */        if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))                new_setting = 1;        else if (!strncmp("off", buf, count - 1)                 || !strncmp("0", buf, count - 1))		new_setting = 0;	else                 return count; 	spin_lock_irqsave(&np->lock, flags);	np->dspcfg_workaround = new_setting;	spin_unlock_irqrestore(&np->lock, flags);	return count;}static inline void __iomem *ns_ioaddr(struct net_device *dev){	return (void __iomem *) dev->base_addr;}static inline void natsemi_irq_enable(struct net_device *dev){	writel(1, ns_ioaddr(dev) + IntrEnable);	readl(ns_ioaddr(dev) + IntrEnable);}static inline void natsemi_irq_disable(struct net_device *dev){	writel(0, ns_ioaddr(dev) + IntrEnable);	readl(ns_ioaddr(dev) + IntrEnable);}static void move_int_phy(struct net_device *dev, int addr){	struct netdev_private *np = netdev_priv(dev);	void __iomem *ioaddr = ns_ioaddr(dev);	int target = 31;	/*	 * The internal phy is visible on the external mii bus. Therefore we must	 * move it away before we can send commands to an external phy.	 * There are two addresses we must avoid:	 * - the address on the external phy that is used for transmission.	 * - the address that we want to access. User space can access phys	 *   on the mii bus with SIOCGMIIREG/SIOCSMIIREG, independant from the	 *   phy that is used for transmission.	 */	if (target == addr)		target--;	if (target == np->phy_addr_external)		target--;	writew(target, ioaddr + PhyCtrl);	readw(ioaddr + PhyCtrl);	udelay(1);}static void __devinit natsemi_init_media (struct net_device *dev){	struct netdev_private *np = netdev_priv(dev);	u32 tmp;	if (np->ignore_phy)		netif_carrier_on(dev);	else		netif_carrier_off(dev);	/* get the initial settings from hardware */	tmp            = mdio_read(dev, MII_BMCR);	np->speed      = (tmp & BMCR_SPEED100)? SPEED_100     : SPEED_10;	np->duplex     = (tmp & BMCR_FULLDPLX)? DUPLEX_FULL   : DUPLEX_HALF;	np->autoneg    = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE;	np->advertising= mdio_read(dev, MII_ADVERTISE);	if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL	 && netif_msg_probe(np)) {		printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s "			"10%s %s duplex.\n",			pci_name(np->pci_dev),			(mdio_read(dev, MII_BMCR) & BMCR_ANENABLE)?			  "enabled, advertise" : "disabled, force",			(np->advertising &			  (ADVERTISE_100FULL|ADVERTISE_100HALF))?			    "0" : "",			(np->advertising &			  (ADVERTISE_100FULL|ADVERTISE_10FULL))?			    "full" : "half");	}	if (netif_msg_probe(np))		printk(KERN_INFO			"natsemi %s: Transceiver status %#04x advertising %#04x.\n",			pci_name(np->pci_dev), mdio_read(dev, MII_BMSR),			np->advertising);}static int __devinit natsemi_probe1 (struct pci_dev *pdev,	const struct pci_device_id *ent){	struct net_device *dev;	struct netdev_private *np;	int i, option, irq, chip_idx = ent->driver_data;	static int find_cnt = -1;	unsigned long iostart, iosize;	void __iomem *ioaddr;	const int pcibar = 1; /* PCI base address register */	int prev_eedata;	u32 tmp;	DECLARE_MAC_BUF(mac);/* when built into the kernel, we only print version if device is found */#ifndef MODULE	static int printed_version;	if (!printed_version++)		printk(version);#endif	i = pci_enable_device(pdev);	if (i) return i;	/* natsemi has a non-standard PM control register	 * in PCI config space.  Some boards apparently need	 * to be brought to D0 in this manner.	 */	pci_read_config_dword(pdev, PCIPM, &tmp);	if (tmp & PCI_PM_CTRL_STATE_MASK) {		/* D0 state, disable PME assertion */		u32 newtmp = tmp & ~PCI_PM_CTRL_STATE_MASK;		pci_write_config_dword(pdev, PCIPM, newtmp);	}	find_cnt++;	iostart = pci_resource_start(pdev, pcibar);	iosize = pci_resource_len(pdev, pcibar);	irq = pdev->irq;	pci_set_master(pdev);	dev = alloc_etherdev(sizeof (struct netdev_private));	if (!dev)		return -ENOMEM;	SET_NETDEV_DEV(dev, &pdev->dev);	i = pci_request_regions(pdev, DRV_NAME);	if (i)		goto err_pci_request_regions;	ioaddr = ioremap(iostart, iosize);	if (!ioaddr) {		i = -ENOMEM;		goto err_ioremap;	}	/* Work around the dropped serial bit. */	prev_eedata = eeprom_read(ioaddr, 6);	for (i = 0; i < 3; i++) {		int eedata = eeprom_read(ioaddr, i + 7);		dev->dev_addr[i*2] = (eedata << 1) + (prev_eedata >> 15);		dev->dev_addr[i*2+1] = eedata >> 7;		prev_eedata = eedata;	}	dev->base_addr = (unsigned long __force) ioaddr;	dev->irq = irq;	np = netdev_priv(dev);	netif_napi_add(dev, &np->napi, natsemi_poll, 64);	np->dev = dev;	np->pci_dev = pdev;	pci_set_drvdata(pdev, dev);	np->iosize = iosize;	spin_lock_init(&np->lock);	np->msg_enable = (debug >= 0) ? (1<<debug)-1 : NATSEMI_DEF_MSG;	np->hands_off = 0;	np->intr_status = 0;	np->eeprom_size = natsemi_pci_info[chip_idx].eeprom_size;	if (natsemi_pci_info[chip_idx].flags & NATSEMI_FLAG_IGNORE_PHY)		np->ignore_phy = 1;	else		np->ignore_phy = 0;	np->dspcfg_workaround = dspcfg_workaround;	/* Initial port:	 * - If configured to ignore the PHY set up for external.	 * - If the nic was configured to use an external phy and if find_mii	 *   finds a phy: use external port, first phy that replies.	 * - Otherwise: internal port.	 * Note that the phy address for the internal phy doesn't matter:	 * The address would be used to access a phy over the mii bus, but	 * the internal phy is accessed through mapped registers.	 */	if (np->ignore_phy || readl(ioaddr + ChipConfig) & CfgExtPhy)		dev->if_port = PORT_MII;	else		dev->if_port = PORT_TP;	/* Reset the chip to erase previous misconfiguration. */	natsemi_reload_eeprom(dev);	natsemi_reset(dev);	if (dev->if_port != PORT_TP) {		np->phy_addr_external = find_mii(dev);		/* If we're ignoring the PHY it doesn't matter if we can't		 * find one. */		if (!np->ignore_phy && np->phy_addr_external == PHY_ADDR_NONE) {			dev->if_port = PORT_TP;			np->phy_addr_external = PHY_ADDR_INTERNAL;		}	} else {		np->phy_addr_external = PHY_ADDR_INTERNAL;	}	option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;

⌨️ 快捷键说明

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