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

📄 smc911x.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
 * now, or set the card to generates an interrupt when ready * for the packet. */static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct smc911x_local *lp = netdev_priv(dev);	unsigned long ioaddr = dev->base_addr;	unsigned int free;	unsigned long flags;	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",		dev->name, __FUNCTION__);	BUG_ON(lp->pending_tx_skb != NULL);	free = SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TDFREE_;	DBG(SMC_DEBUG_TX, "%s: TX free space %d\n", dev->name, free);	/* Turn off the flow when running out of space in FIFO */	if (free <= SMC911X_TX_FIFO_LOW_THRESHOLD) {		DBG(SMC_DEBUG_TX, "%s: Disabling data flow due to low FIFO space (%d)\n",			dev->name, free);		spin_lock_irqsave(&lp->lock, flags);		/* Reenable when at least 1 packet of size MTU present */		SMC_SET_FIFO_TDA((SMC911X_TX_FIFO_LOW_THRESHOLD)/64);		lp->tx_throttle = 1;		netif_stop_queue(dev);		spin_unlock_irqrestore(&lp->lock, flags);	}	/* Drop packets when we run out of space in TX FIFO	 * Account for overhead required for:	 *	 *	  Tx command words			 8 bytes	 *	  Start offset				 15 bytes	 *	  End padding				 15 bytes	 */	if (unlikely(free < (skb->len + 8 + 15 + 15))) {		printk("%s: No Tx free space %d < %d\n",			dev->name, free, skb->len);		lp->pending_tx_skb = NULL;		dev->stats.tx_errors++;		dev->stats.tx_dropped++;		dev_kfree_skb(skb);		return 0;	}#ifdef SMC_USE_DMA	{		/* If the DMA is already running then defer this packet Tx until		 * the DMA IRQ starts it		 */		spin_lock_irqsave(&lp->lock, flags);		if (lp->txdma_active) {			DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Tx DMA running, deferring packet\n", dev->name);			lp->pending_tx_skb = skb;			netif_stop_queue(dev);			spin_unlock_irqrestore(&lp->lock, flags);			return 0;		} else {			DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Activating Tx DMA\n", dev->name);			lp->txdma_active = 1;		}		spin_unlock_irqrestore(&lp->lock, flags);	}#endif	lp->pending_tx_skb = skb;	smc911x_hardware_send_pkt(dev);	return 0;}/* * This handles a TX status interrupt, which is only called when: * - a TX error occurred, or * - TX of a packet completed. */static void smc911x_tx(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	struct smc911x_local *lp = netdev_priv(dev);	unsigned int tx_status;	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",		dev->name, __FUNCTION__);	/* Collect the TX status */	while (((SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TSUSED_) >> 16) != 0) {		DBG(SMC_DEBUG_TX, "%s: Tx stat FIFO used 0x%04x\n",			dev->name,			(SMC_GET_TX_FIFO_INF() & TX_FIFO_INF_TSUSED_) >> 16);		tx_status = SMC_GET_TX_STS_FIFO();		dev->stats.tx_packets++;		dev->stats.tx_bytes+=tx_status>>16;		DBG(SMC_DEBUG_TX, "%s: Tx FIFO tag 0x%04x status 0x%04x\n",			dev->name, (tx_status & 0xffff0000) >> 16,			tx_status & 0x0000ffff);		/* count Tx errors, but ignore lost carrier errors when in		 * full-duplex mode */		if ((tx_status & TX_STS_ES_) && !(lp->ctl_rfduplx &&		    !(tx_status & 0x00000306))) {			dev->stats.tx_errors++;		}		if (tx_status & TX_STS_MANY_COLL_) {			dev->stats.collisions+=16;			dev->stats.tx_aborted_errors++;		} else {			dev->stats.collisions+=(tx_status & TX_STS_COLL_CNT_) >> 3;		}		/* carrier error only has meaning for half-duplex communication */		if ((tx_status & (TX_STS_LOC_ | TX_STS_NO_CARR_)) &&		    !lp->ctl_rfduplx) {			dev->stats.tx_carrier_errors++;		}		if (tx_status & TX_STS_LATE_COLL_) {			dev->stats.collisions++;			dev->stats.tx_aborted_errors++;		}	}}/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*//* * Reads a register from the MII Management serial interface */static int smc911x_phy_read(struct net_device *dev, int phyaddr, int phyreg){	unsigned long ioaddr = dev->base_addr;	unsigned int phydata;	SMC_GET_MII(phyreg, phyaddr, phydata);	DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n",		__FUNCTION__, phyaddr, phyreg, phydata);	return phydata;}/* * Writes a register to the MII Management serial interface */static void smc911x_phy_write(struct net_device *dev, int phyaddr, int phyreg,			int phydata){	unsigned long ioaddr = dev->base_addr;	DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",		__FUNCTION__, phyaddr, phyreg, phydata);	SMC_SET_MII(phyreg, phyaddr, phydata);}/* * Finds and reports the PHY address (115 and 117 have external * PHY interface 118 has internal only */static void smc911x_phy_detect(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	struct smc911x_local *lp = netdev_priv(dev);	int phyaddr;	unsigned int cfg, id1, id2;	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);	lp->phy_type = 0;	/*	 * Scan all 32 PHY addresses if necessary, starting at	 * PHY#1 to PHY#31, and then PHY#0 last.	 */	switch(lp->version) {		case 0x115:		case 0x117:			cfg = SMC_GET_HW_CFG();			if (cfg & HW_CFG_EXT_PHY_DET_) {				cfg &= ~HW_CFG_PHY_CLK_SEL_;				cfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;				SMC_SET_HW_CFG(cfg);				udelay(10); /* Wait for clocks to stop */				cfg |= HW_CFG_EXT_PHY_EN_;				SMC_SET_HW_CFG(cfg);				udelay(10); /* Wait for clocks to stop */				cfg &= ~HW_CFG_PHY_CLK_SEL_;				cfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;				SMC_SET_HW_CFG(cfg);				udelay(10); /* Wait for clocks to stop */				cfg |= HW_CFG_SMI_SEL_;				SMC_SET_HW_CFG(cfg);				for (phyaddr = 1; phyaddr < 32; ++phyaddr) {					/* Read the PHY identifiers */					SMC_GET_PHY_ID1(phyaddr & 31, id1);					SMC_GET_PHY_ID2(phyaddr & 31, id2);					/* Make sure it is a valid identifier */					if (id1 != 0x0000 && id1 != 0xffff &&					    id1 != 0x8000 && id2 != 0x0000 &&					    id2 != 0xffff && id2 != 0x8000) {						/* Save the PHY's address */						lp->mii.phy_id = phyaddr & 31;						lp->phy_type = id1 << 16 | id2;						break;					}				}			}		default:			/* Internal media only */			SMC_GET_PHY_ID1(1, id1);			SMC_GET_PHY_ID2(1, id2);			/* Save the PHY's address */			lp->mii.phy_id = 1;			lp->phy_type = id1 << 16 | id2;	}	DBG(SMC_DEBUG_MISC, "%s: phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n",		dev->name, id1, id2, lp->mii.phy_id);}/* * Sets the PHY to a configuration as determined by the user. * Called with spin_lock held. */static int smc911x_phy_fixed(struct net_device *dev){	struct smc911x_local *lp = netdev_priv(dev);	unsigned long ioaddr = dev->base_addr;	int phyaddr = lp->mii.phy_id;	int bmcr;	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);	/* Enter Link Disable state */	SMC_GET_PHY_BMCR(phyaddr, bmcr);	bmcr |= BMCR_PDOWN;	SMC_SET_PHY_BMCR(phyaddr, bmcr);	/*	 * Set our fixed capabilities	 * Disable auto-negotiation	 */	bmcr &= ~BMCR_ANENABLE;	if (lp->ctl_rfduplx)		bmcr |= BMCR_FULLDPLX;	if (lp->ctl_rspeed == 100)		bmcr |= BMCR_SPEED100;	/* Write our capabilities to the phy control register */	SMC_SET_PHY_BMCR(phyaddr, bmcr);	/* Re-Configure the Receive/Phy Control register */	bmcr &= ~BMCR_PDOWN;	SMC_SET_PHY_BMCR(phyaddr, bmcr);	return 1;}/* * smc911x_phy_reset - reset the phy * @dev: net device * @phy: phy address * * Issue a software reset for the specified PHY and * wait up to 100ms for the reset to complete.	 We should * not access the PHY for 50ms after issuing the reset. * * The time to wait appears to be dependent on the PHY. * */static int smc911x_phy_reset(struct net_device *dev, int phy){	struct smc911x_local *lp = netdev_priv(dev);	unsigned long ioaddr = dev->base_addr;	int timeout;	unsigned long flags;	unsigned int reg;	DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __FUNCTION__);	spin_lock_irqsave(&lp->lock, flags);	reg = SMC_GET_PMT_CTRL();	reg &= ~0xfffff030;	reg |= PMT_CTRL_PHY_RST_;	SMC_SET_PMT_CTRL(reg);	spin_unlock_irqrestore(&lp->lock, flags);	for (timeout = 2; timeout; timeout--) {		msleep(50);		spin_lock_irqsave(&lp->lock, flags);		reg = SMC_GET_PMT_CTRL();		spin_unlock_irqrestore(&lp->lock, flags);		if (!(reg & PMT_CTRL_PHY_RST_)) {			/* extra delay required because the phy may			 * not be completed with its reset			 * when PHY_BCR_RESET_ is cleared. 256us			 * should suffice, but use 500us to be safe			 */			udelay(500);		break;		}	}	return reg & PMT_CTRL_PHY_RST_;}/* * smc911x_phy_powerdown - powerdown phy * @dev: net device * @phy: phy address * * Power down the specified PHY */static void smc911x_phy_powerdown(struct net_device *dev, int phy){	unsigned long ioaddr = dev->base_addr;	unsigned int bmcr;	/* Enter Link Disable state */	SMC_GET_PHY_BMCR(phy, bmcr);	bmcr |= BMCR_PDOWN;	SMC_SET_PHY_BMCR(phy, bmcr);}/* * smc911x_phy_check_media - check the media status and adjust BMCR * @dev: net device * @init: set true for initialisation * * Select duplex mode depending on negotiation state.	This * also updates our carrier state. */static void smc911x_phy_check_media(struct net_device *dev, int init){	struct smc911x_local *lp = netdev_priv(dev);	unsigned long ioaddr = dev->base_addr;	int phyaddr = lp->mii.phy_id;	unsigned int bmcr, cr;	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);	if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {		/* duplex state has changed */		SMC_GET_PHY_BMCR(phyaddr, bmcr);		SMC_GET_MAC_CR(cr);		if (lp->mii.full_duplex) {			DBG(SMC_DEBUG_MISC, "%s: Configuring for full-duplex mode\n", dev->name);			bmcr |= BMCR_FULLDPLX;			cr |= MAC_CR_RCVOWN_;		} else {			DBG(SMC_DEBUG_MISC, "%s: Configuring for half-duplex mode\n", dev->name);			bmcr &= ~BMCR_FULLDPLX;			cr &= ~MAC_CR_RCVOWN_;		}		SMC_SET_PHY_BMCR(phyaddr, bmcr);		SMC_SET_MAC_CR(cr);	}}/* * Configures the specified PHY through the MII management interface * using Autonegotiation. * Calls smc911x_phy_fixed() if the user has requested a certain config. * If RPC ANEG bit is set, the media selection is dependent purely on * the selection by the MII (either in the MII BMCR reg or the result * of autonegotiation.)  If the RPC ANEG bit is cleared, the selection * is controlled by the RPC SPEED and RPC DPLX bits. */static void smc911x_phy_configure(struct work_struct *work){	struct smc911x_local *lp = container_of(work, struct smc911x_local,						phy_configure);	struct net_device *dev = lp->netdev;	unsigned long ioaddr = dev->base_addr;	int phyaddr = lp->mii.phy_id;	int my_phy_caps; /* My PHY capabilities */	int my_ad_caps; /* My Advertised capabilities */	int status;	unsigned long flags;	DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __FUNCTION__);	/*	 * We should not be called if phy_type is zero.	 */	if (lp->phy_type == 0)		 goto smc911x_phy_configure_exit_nolock;	if (smc911x_phy_reset(dev, phyaddr)) {		printk("%s: PHY reset timed out\n", dev->name);		goto smc911x_phy_configure_exit_nolock;	}	spin_lock_irqsave(&lp->lock, flags);	/*	 * Enable PHY Interrupts (for register 18)	 * Interrupts listed here are enabled	 */	SMC_SET_PHY_INT_MASK(phyaddr, PHY_INT_MASK_ENERGY_ON_ |		 PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_REMOTE_FAULT_ |		 PHY_INT_MASK_LINK_DOWN_);	/* If the user requested no auto neg, then go set his request */	if (lp->mii.force_media) {		smc911x_phy_fixed(dev);		goto smc911x_phy_configure_exit;	}	/* Copy our capabilities from MII_BMSR to MII_ADVERTISE */	SMC_GET_PHY_BMSR(phyaddr, my_phy_caps);	if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {		printk(KERN_INFO "Auto negotiation NOT supported\n");		smc911x_phy_fixed(dev);		goto smc911x_phy_configure_exit;	}	/* CSMA capable w/ both pauses */	my_ad_caps = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;	if (my_phy_caps & BMSR_100BASE4)		my_ad_caps |= ADVERTISE_100BASE4;	if (my_phy_caps & BMSR_100FULL)		my_ad_caps |= ADVERTISE_100FULL;	if (my_phy_caps & BMSR_100HALF)		my_ad_caps |= ADVERTISE_100HALF;	if (my_phy_caps & BMSR_10FULL)		my_ad_caps |= ADVERTISE_10FULL;	if (my_phy_caps & BMSR_10HALF)		my_ad_caps |= ADVERTISE_10HALF;	/* Disable capabilities not selected by our user */	if (lp->ctl_rspeed != 100)		my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF);	 if (!lp->ctl_rfduplx)		my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL);	/* Update our Auto-Neg Advertisement Register */	SMC_SET_PHY_MII_ADV(phyaddr, my_ad_caps);	lp->mii.advertising = my_ad_caps;	/*	 * Read the register back.	 Without this, it appears that when	 * auto-negotiation is restarted, sometimes it isn't ready and	 * the link does not come up.	 */	udelay(10);	SMC_GET_PHY_MII_ADV(phyaddr, status);	DBG(SMC_DEBUG_MISC, "%s: phy caps=0x%04x\n", dev->name, my_phy_caps);	DBG(SMC_DEBUG_MISC, "%s: phy advertised caps=0x%04x\n", dev->name, my_ad_caps);	/* Restart auto-negotiation process in order to advertise my caps */	SMC_SET_PHY_BMCR(phyaddr, BMCR_ANENABLE | BMCR_ANRESTART);	smc911x_phy_check_media(dev, 1);smc911x_phy_configure_exit:	spin_unlock_irqrestore(&lp->lock, flags);smc911x_phy_configure_exit_nolock:	lp->work_pending = 0;}/* * smc911x_phy_interrupt * * Purpose:  Handle interrupts relating to PHY register 18. This is *	 called from the "hard" interrupt handler under our private spinlock. */static void smc911x_phy_interrupt(struct net_device *dev){	struct smc911x_local *lp = netdev_priv(dev);	unsigned long ioaddr = dev->base_addr;	int phyaddr = lp->mii.phy_id;	int status;	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);	if (lp->phy_type == 0)		return;	smc911x_phy_check_media(dev, 0);	/* read to clear status bits */	SMC_GET_PHY_INT_SRC(phyaddr,status);	DBG(SMC_DEBUG_MISC, "%s: PHY interrupt status 0x%04x\n",		dev->name, status & 0xffff);	DBG(SMC_DEBUG_MISC, "%s: AFC_CFG 0x%08x\n",		dev->name, SMC_GET_AFC_CFG());}/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*//* * This is the main routine of the driver, to handle the device when * it needs some attention. */static irqreturn_t smc911x_interrupt(int irq, void *dev_id){	struct net_device *dev = dev_id;	unsigned long ioaddr = dev->base_addr;	struct smc911x_local *lp = netdev_priv(dev);	unsigned int status, mask, timeout;	unsigned int rx_overrun=0, cr, pkts;	unsigned long flags;	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);	spin_lock_irqsave(&lp->lock, flags);	/* Spurious interrupt check */	if ((SMC_GET_IRQ_CFG() & (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) !=		(INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) {		spin_unlock_irqrestore(&lp->lock, flags);		return IRQ_NONE;	}	mask = SMC_GET_INT_EN();	SMC_SET_INT_EN(0);	/* set a timeout value, so I don't stay here forever */	timeout = 8;	do {		status = SMC_GET_INT();		DBG(SMC_DEBUG_MISC, "%s: INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n",			dev->name, status, mask, status & ~mask);		status &= mask;		if (!status)			break;		/* Handle SW interrupt condition */		if (status & INT_STS_SW_INT_) {			SMC_ACK_INT(INT_STS_SW_INT_);			mask &= ~INT_EN_SW_INT_EN_;		}		/* Handle various error conditions */		if (status & INT_STS_RXE_) {			SMC_ACK_INT(INT_STS_RXE_);			dev->stats.rx_errors++;		}		if (status & INT_STS_RXDFH_INT_) {			SMC_ACK_INT(INT_STS_RXDFH_INT_);			dev->stats.rx_dropped+=SMC_GET_RX_DROP();		 }		/* Undocumented interrupt-what is the right thing to do here? */		if (status & INT_STS_RXDF_INT_) {			SMC_ACK_INT(INT_STS_RXDF_INT_);		}		/* Rx Data FIFO exceeds set level */		if (status & INT_STS_RDFL_) {			if (IS_REV_A(lp->revision)) {				rx_overrun=1;				SMC_GET_MAC_CR(cr);				cr &= ~MAC_CR_RXEN_;				SMC_SET_MAC_CR(cr);				DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);				dev->stats.rx_errors++;				dev->stats.rx_fifo_errors++;			}			SMC_ACK_INT(INT_STS_RDFL_);		}

⌨️ 快捷键说明

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