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

📄 smc91x.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	DBG(2, "%s: phy advertised caps=%x\n", dev->name, my_ad_caps);	/* Restart auto-negotiation process in order to advertise my caps */	smc_phy_write(dev, phyaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);	smc_phy_check_media(dev, 1);smc_phy_configure_exit:	SMC_SELECT_BANK(2);	spin_unlock_irq(&lp->lock);	lp->work_pending = 0;}/* * smc_phy_interrupt * * Purpose:  Handle interrupts relating to PHY register 18. This is *  called from the "hard" interrupt handler under our private spinlock. */static void smc_phy_interrupt(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	int phyaddr = lp->mii.phy_id;	int phy18;	DBG(2, "%s: %s\n", dev->name, __FUNCTION__);	if (lp->phy_type == 0)		return;	for(;;) {		smc_phy_check_media(dev, 0);		/* Read PHY Register 18, Status Output */		phy18 = smc_phy_read(dev, phyaddr, PHY_INT_REG);		if ((phy18 & PHY_INT_INT) == 0)			break;	}}/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/static void smc_10bt_check_media(struct net_device *dev, int init){	struct smc_local *lp = netdev_priv(dev);	void __iomem *ioaddr = lp->base;	unsigned int old_carrier, new_carrier;	old_carrier = netif_carrier_ok(dev) ? 1 : 0;	SMC_SELECT_BANK(0);	new_carrier = (SMC_GET_EPH_STATUS() & ES_LINK_OK) ? 1 : 0;	SMC_SELECT_BANK(2);	if (init || (old_carrier != new_carrier)) {		if (!new_carrier) {			netif_carrier_off(dev);		} else {			netif_carrier_on(dev);		}		if (netif_msg_link(lp))			printk(KERN_INFO "%s: link %s\n", dev->name,			       new_carrier ? "up" : "down");	}}static void smc_eph_interrupt(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	void __iomem *ioaddr = lp->base;	unsigned int ctl;	smc_10bt_check_media(dev, 0);	SMC_SELECT_BANK(1);	ctl = SMC_GET_CTL();	SMC_SET_CTL(ctl & ~CTL_LE_ENABLE);	SMC_SET_CTL(ctl);	SMC_SELECT_BANK(2);}/* * This is the main routine of the driver, to handle the device when * it needs some attention. */static irqreturn_t smc_interrupt(int irq, void *dev_id){	struct net_device *dev = dev_id;	struct smc_local *lp = netdev_priv(dev);	void __iomem *ioaddr = lp->base;	int status, mask, timeout, card_stats;	int saved_pointer;	DBG(3, "%s: %s\n", dev->name, __FUNCTION__);	spin_lock(&lp->lock);	/* A preamble may be used when there is a potential race	 * between the interruptible transmit functions and this	 * ISR. */	SMC_INTERRUPT_PREAMBLE;	saved_pointer = SMC_GET_PTR();	mask = SMC_GET_INT_MASK();	SMC_SET_INT_MASK(0);	/* set a timeout value, so I don't stay here forever */	timeout = MAX_IRQ_LOOPS;	do {		status = SMC_GET_INT();		DBG(2, "%s: INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n",			dev->name, status, mask,			({ int meminfo; SMC_SELECT_BANK(0);			   meminfo = SMC_GET_MIR();			   SMC_SELECT_BANK(2); meminfo; }),			SMC_GET_FIFO());		status &= mask;		if (!status)			break;		if (status & IM_TX_INT) {			/* do this before RX as it will free memory quickly */			DBG(3, "%s: TX int\n", dev->name);			smc_tx(dev);			SMC_ACK_INT(IM_TX_INT);			if (THROTTLE_TX_PKTS)				netif_wake_queue(dev);		} else if (status & IM_RCV_INT) {			DBG(3, "%s: RX irq\n", dev->name);			smc_rcv(dev);		} else if (status & IM_ALLOC_INT) {			DBG(3, "%s: Allocation irq\n", dev->name);			tasklet_hi_schedule(&lp->tx_task);			mask &= ~IM_ALLOC_INT;		} else if (status & IM_TX_EMPTY_INT) {			DBG(3, "%s: TX empty\n", dev->name);			mask &= ~IM_TX_EMPTY_INT;			/* update stats */			SMC_SELECT_BANK(0);			card_stats = SMC_GET_COUNTER();			SMC_SELECT_BANK(2);			/* single collisions */			dev->stats.collisions += card_stats & 0xF;			card_stats >>= 4;			/* multiple collisions */			dev->stats.collisions += card_stats & 0xF;		} else if (status & IM_RX_OVRN_INT) {			DBG(1, "%s: RX overrun (EPH_ST 0x%04x)\n", dev->name,			       ({ int eph_st; SMC_SELECT_BANK(0);				  eph_st = SMC_GET_EPH_STATUS();				  SMC_SELECT_BANK(2); eph_st; }) );			SMC_ACK_INT(IM_RX_OVRN_INT);			dev->stats.rx_errors++;			dev->stats.rx_fifo_errors++;		} else if (status & IM_EPH_INT) {			smc_eph_interrupt(dev);		} else if (status & IM_MDINT) {			SMC_ACK_INT(IM_MDINT);			smc_phy_interrupt(dev);		} else if (status & IM_ERCV_INT) {			SMC_ACK_INT(IM_ERCV_INT);			PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", dev->name);		}	} while (--timeout);	/* restore register states */	SMC_SET_PTR(saved_pointer);	SMC_SET_INT_MASK(mask);	spin_unlock(&lp->lock);	if (timeout == MAX_IRQ_LOOPS)		PRINTK("%s: spurious interrupt (mask = 0x%02x)\n",		       dev->name, mask);	DBG(3, "%s: Interrupt done (%d loops)\n",	       dev->name, MAX_IRQ_LOOPS - timeout);	/*	 * We return IRQ_HANDLED unconditionally here even if there was	 * nothing to do.  There is a possibility that a packet might	 * get enqueued into the chip right after TX_EMPTY_INT is raised	 * but just before the CPU acknowledges the IRQ.	 * Better take an unneeded IRQ in some occasions than complexifying	 * the code for all cases.	 */	return IRQ_HANDLED;}#ifdef CONFIG_NET_POLL_CONTROLLER/* * Polling receive - used by netconsole and other diagnostic tools * to allow network i/o with interrupts disabled. */static void smc_poll_controller(struct net_device *dev){	disable_irq(dev->irq);	smc_interrupt(dev->irq, dev);	enable_irq(dev->irq);}#endif/* Our watchdog timed out. Called by the networking layer */static void smc_timeout(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	void __iomem *ioaddr = lp->base;	int status, mask, eph_st, meminfo, fifo;	DBG(2, "%s: %s\n", dev->name, __FUNCTION__);	spin_lock_irq(&lp->lock);	status = SMC_GET_INT();	mask = SMC_GET_INT_MASK();	fifo = SMC_GET_FIFO();	SMC_SELECT_BANK(0);	eph_st = SMC_GET_EPH_STATUS();	meminfo = SMC_GET_MIR();	SMC_SELECT_BANK(2);	spin_unlock_irq(&lp->lock);	PRINTK( "%s: TX timeout (INT 0x%02x INTMASK 0x%02x "		"MEM 0x%04x FIFO 0x%04x EPH_ST 0x%04x)\n",		dev->name, status, mask, meminfo, fifo, eph_st );	smc_reset(dev);	smc_enable(dev);	/*	 * Reconfiguring the PHY doesn't seem like a bad idea here, but	 * smc_phy_configure() calls msleep() which calls schedule_timeout()	 * which calls schedule().  Hence we use a work queue.	 */	if (lp->phy_type != 0) {		if (schedule_work(&lp->phy_configure)) {			lp->work_pending = 1;		}	}	/* We can accept TX packets again */	dev->trans_start = jiffies;	netif_wake_queue(dev);}/* * This routine will, depending on the values passed to it, * either make it accept multicast packets, go into * promiscuous mode (for TCPDUMP and cousins) or accept * a select set of multicast packets */static void smc_set_multicast_list(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	void __iomem *ioaddr = lp->base;	unsigned char multicast_table[8];	int update_multicast = 0;	DBG(2, "%s: %s\n", dev->name, __FUNCTION__);	if (dev->flags & IFF_PROMISC) {		DBG(2, "%s: RCR_PRMS\n", dev->name);		lp->rcr_cur_mode |= RCR_PRMS;	}/* BUG?  I never disable promiscuous mode if multicasting was turned on.   Now, I turn off promiscuous mode, but I don't do anything to multicasting   when promiscuous mode is turned on.*/	/*	 * Here, I am setting this to accept all multicast packets.	 * I don't need to zero the multicast table, because the flag is	 * checked before the table is	 */	else if (dev->flags & IFF_ALLMULTI || dev->mc_count > 16) {		DBG(2, "%s: RCR_ALMUL\n", dev->name);		lp->rcr_cur_mode |= RCR_ALMUL;	}	/*	 * This sets the internal hardware table to filter out unwanted	 * multicast packets before they take up memory.	 *	 * The SMC chip uses a hash table where the high 6 bits of the CRC of	 * address are the offset into the table.  If that bit is 1, then the	 * multicast packet is accepted.  Otherwise, it's dropped silently.	 *	 * To use the 6 bits as an offset into the table, the high 3 bits are	 * the number of the 8 bit register, while the low 3 bits are the bit	 * within that register.	 */	else if (dev->mc_count)  {		int i;		struct dev_mc_list *cur_addr;		/* table for flipping the order of 3 bits */		static const unsigned char invert3[] = {0, 4, 2, 6, 1, 5, 3, 7};		/* start with a table of all zeros: reject all */		memset(multicast_table, 0, sizeof(multicast_table));		cur_addr = dev->mc_list;		for (i = 0; i < dev->mc_count; i++, cur_addr = cur_addr->next) {			int position;			/* do we have a pointer here? */			if (!cur_addr)				break;			/* make sure this is a multicast address -		   	   shouldn't this be a given if we have it here ? */			if (!(*cur_addr->dmi_addr & 1))				continue;			/* only use the low order bits */			position = crc32_le(~0, cur_addr->dmi_addr, 6) & 0x3f;			/* do some messy swapping to put the bit in the right spot */			multicast_table[invert3[position&7]] |=				(1<<invert3[(position>>3)&7]);		}		/* be sure I get rid of flags I might have set */		lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);		/* now, the table can be loaded into the chipset */		update_multicast = 1;	} else  {		DBG(2, "%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name);		lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);		/*		 * since I'm disabling all multicast entirely, I need to		 * clear the multicast list		 */		memset(multicast_table, 0, sizeof(multicast_table));		update_multicast = 1;	}	spin_lock_irq(&lp->lock);	SMC_SELECT_BANK(0);	SMC_SET_RCR(lp->rcr_cur_mode);	if (update_multicast) {		SMC_SELECT_BANK(3);		SMC_SET_MCAST(multicast_table);	}	SMC_SELECT_BANK(2);	spin_unlock_irq(&lp->lock);}/* * Open and Initialize the board * * Set up everything, reset the card, etc.. */static intsmc_open(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	DBG(2, "%s: %s\n", dev->name, __FUNCTION__);	/*	 * Check that the address is valid.  If its not, refuse	 * to bring the device up.  The user must specify an	 * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx	 */	if (!is_valid_ether_addr(dev->dev_addr)) {		PRINTK("%s: no valid ethernet hw addr\n", __FUNCTION__);		return -EINVAL;	}	/* Setup the default Register Modes */	lp->tcr_cur_mode = TCR_DEFAULT;	lp->rcr_cur_mode = RCR_DEFAULT;	lp->rpc_cur_mode = RPC_DEFAULT;	/*	 * If we are not using a MII interface, we need to	 * monitor our own carrier signal to detect faults.	 */	if (lp->phy_type == 0)		lp->tcr_cur_mode |= TCR_MON_CSN;	/* reset the hardware */	smc_reset(dev);	smc_enable(dev);	/* Configure the PHY, initialize the link state */	if (lp->phy_type != 0)		smc_phy_configure(&lp->phy_configure);	else {		spin_lock_irq(&lp->lock);		smc_10bt_check_media(dev, 1);		spin_unlock_irq(&lp->lock);	}	netif_start_queue(dev);	return 0;}/* * smc_close * * this makes the board clean up everything that it can * and not talk to the outside world.   Caused by * an 'ifconfig ethX down' */static int smc_close(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	DBG(2, "%s: %s\n", dev->name, __FUNCTION__);	netif_stop_queue(dev);	netif_carrier_off(dev);	/* clear everything */	smc_shutdown(dev);	tasklet_kill(&lp->tx_task);	smc_phy_powerdown(dev);	return 0;}/* * Ethtool support */static intsmc_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd){	struct smc_local *lp = netdev_priv(dev);	int ret;	cmd->maxtxpkt = 1;	cmd->maxrxpkt = 1;	if (lp->phy_type != 0) {		spin_lock_irq(&lp->lock);		ret = mii_ethtool_gset(&lp->mii, cmd);		spin_unlock_irq(&lp->lock);	} else {		cmd->supported = SUPPORTED_10baseT_Half |				 SUPPORTED_10baseT_Full |				 SUPPORTED_TP | SUPPORTED_AUI;		if (lp->ctl_rspeed == 10)			cmd->speed = SPEED_10;		else if (lp->ctl_rspeed == 100)			cmd->speed = SPEED_100;		cmd->autoneg = AUTONEG_DISABLE;		cmd->transceiver = XCVR_INTERNAL;		cmd->port = 0;		cmd->duplex = lp->tcr_cur_mode & TCR_SWFDUP ? DUPLEX_FULL : DUPLEX_HALF;		ret = 0;	}	return ret;}static intsmc_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd){	struct smc_local *lp = netdev_priv(dev);	int ret;	if (lp->phy_type != 0) {		spin_lock_irq(&lp->lock);		ret = mii_ethtool_sset(&lp->mii, cmd);		spin_unlock_irq(&lp->lock);	} else {		if (cmd->autoneg != AUTONEG_DISABLE ||		    cmd->speed != SPEED_10 ||		    (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) ||		    (cmd->port != PORT_TP && cmd->port != PORT_AUI))			return -EINVAL;//		lp->port = cmd->port;		lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL;//		if (netif_running(dev))//			smc_set_port(dev);		ret = 0;	}	return ret;}static voidsmc_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info){	strncpy(info->driver, CARDNAME, sizeof(info->driver));	strncpy(info->version, version, sizeof(info->version));	strncpy(info->bus_info, dev->dev.parent->bus_id, sizeof(info->bus_info));}static int smc_ethtool_nwayreset(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	int ret = -EINVAL;	if (lp->phy_type != 0) {		spin_lock_irq(&lp->lock);		ret = mii_nway_restart(&lp->mii);		spin_unlock_irq(&lp->lock);	}	return ret;}static u32 smc_ethtool_getmsglevel(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	return lp->msg_enable;}static void smc_ethtool_setmsglevel(struct net_device *dev, u32 level){	struct smc_local *lp = netdev_priv(dev);	lp->msg_enable = level;}static const struct ethtool_ops smc_ethtool_ops = {	.get_settings	= smc_ethtool_getsettings,	.set_settings	= smc_ethtool_setsettings,	.get_drvinfo	= smc_ethtool_getdrvinfo,	.get_msglevel	= smc_ethtool_getmsglevel,	.set_msglevel	= smc_ethtool_setmsglevel,	.nway_reset	= smc_ethtool_nwayreset,	.get_link	= ethtool_op_get_link,//	.get_eeprom	= smc_ethtool_geteeprom,//	.set_eeprom	= smc_ethtool_seteeprom,};/* * smc_findirq * * This routine has a simple purpose -- make the SMC chip generate an * interrupt, so an auto-detect routine can detect it, and find the IRQ, *//* * does this still work? * * I just deleted auto_irq.c, since it was never built... *   --jgarzik */static int __init smc_findirq(void __iomem *ioaddr){	int timeout = 20;	unsigned long cookie;	DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__);	cookie = probe_irq_on();	/*	 * What I try to do here is trigger an ALLOC_INT. This is done	 * by allocating a small chunk of memory, which will give an interrupt	 * when done.	 */	/* enable ALLOCation interrupts ONLY */	SMC_SELECT_BANK(2);	SMC_SET_INT_MASK(IM_ALLOC_INT);	/* 	 * Allocate 512 bytes of memory.  Note that the chip was just	 * reset so all the memory is available	 */	SMC_SET_MMU_CMD(MC_ALLOC | 1);

⌨️ 快捷键说明

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