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

📄 smc91x.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 4 页
字号:
	SMC_SELECT_BANK(0);	old_carrier = netif_carrier_ok(dev) ? 1 : 0;	new_carrier = SMC_inw(ioaddr, EPH_STATUS_REG) & ES_LINK_OK ? 1 : 0;	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");	}	SMC_SELECT_BANK(old_bank);}static void smc_eph_interrupt(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	unsigned int old_bank, ctl;	smc_10bt_check_media(dev, 0);	old_bank = SMC_CURRENT_BANK();	SMC_SELECT_BANK(1);	ctl = SMC_GET_CTL();	SMC_SET_CTL(ctl & ~CTL_LE_ENABLE);	SMC_SET_CTL(ctl);	SMC_SELECT_BANK(old_bank);}/* * 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 pt_regs *regs){	struct net_device *dev = dev_id;	unsigned long ioaddr = dev->base_addr;	struct smc_local *lp = netdev_priv(dev);	int status, mask, timeout, card_stats;	int saved_bank, saved_pointer;	DBG(3, "%s: %s\n", dev->name, __FUNCTION__);	spin_lock(&lp->lock);	saved_bank = SMC_CURRENT_BANK();	SMC_SELECT_BANK(2);	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 = 8;	do {		status = SMC_GET_INT();		DBG(2, "%s: IRQ 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_RCV_INT) {			DBG(3, "%s: RX irq\n", dev->name);			smc_rcv(dev);		} else if (status & IM_TX_INT) {			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);#endif		} else if (status & IM_ALLOC_INT) {			DBG(3, "%s: Allocation irq\n", dev->name);			smc_hardware_send_packet(dev);			mask |= (IM_TX_INT | IM_TX_EMPTY_INT);			mask &= ~IM_ALLOC_INT;#if ! THROTTLE_TX_PKTS			netif_wake_queue(dev);#endif		} 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 */			lp->stats.collisions += card_stats & 0xF;			card_stats >>= 4;			/* multiple collisions */			lp->stats.collisions += card_stats & 0xF;		} else if (status & IM_RX_OVRN_INT) {			DBG(1, "%s: RX overrun\n", dev->name);			SMC_ACK_INT(IM_RX_OVRN_INT);			lp->stats.rx_errors++;			lp->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);#if defined(CONFIG_MACH_OMAP_H4)    /* HACKALERT!: This requires investigation     * The current issue is that during boot up on 2420 H4 Rev b.4,     * if root file system is mounted using nfs, this issue is seen.     */	udelay(50);#endif	/* restore register states */	SMC_SET_INT_MASK(mask);	SMC_SET_PTR(saved_pointer);	SMC_SELECT_BANK(saved_bank);#if defined(CONFIG_MACH_OMAP_H4)	barrier();    // mem bus is way slow, seems like cpu might be getting ahead this plus read should kill it.	mask = SMC_GET_INT_MASK();#endif	DBG(3, "%s: Interrupt done (%d loops)\n", dev->name, 8-timeout);	spin_unlock(&lp->lock);	/*	 * 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;}/* Our watchdog timed out. Called by the networking layer */static void smc_timeout(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	unsigned long flags;	spin_lock_irqsave(&lp->lock, flags);	DBG(2, "%s: %s\n", dev->name, __FUNCTION__);	smc_reset(dev);	smc_enable(dev);#if 0	/*	 * Reconfiguring the PHY doesn't seem like a bad idea here, but	 * it introduced a problem.  Now that this is a timeout routine,	 * we are getting called from within an interrupt context.	 * smc_phy_configure() calls msleep() which calls	 * schedule_timeout() which calls schedule().  When schedule()	 * is called from an interrupt context, it prints out	 * "Scheduling in interrupt" and then calls BUG().  This is	 * obviously not desirable.  This was worked around by removing	 * the call to smc_phy_configure() here because it didn't seem	 * absolutely necessary.  Ultimately, if msleep() is	 * supposed to be usable from an interrupt context (which it	 * looks like it thinks it should handle), it should be fixed.	 */	if (lp->phy_type != 0)		smc_phy_configure(dev);#endif	/* clear anything saved */	if (lp->saved_skb != NULL) {		dev_kfree_skb (lp->saved_skb);		lp->saved_skb = NULL;		lp->stats.tx_errors++;		lp->stats.tx_aborted_errors++;	}	/* We can accept TX packets again */	dev->trans_start = jiffies;	spin_unlock_irqrestore(&lp->lock, flags);	netif_wake_queue(dev);}/* *    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. * *    This routine is based very heavily on the one provided by Peter Cammaert. */static voidsmc_setmulticast(unsigned long ioaddr, int count, struct dev_mc_list *addrs){	int i;	unsigned char multicast_table[8];	struct dev_mc_list *cur_addr;	/* table for flipping the order of 3 bits */	static 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 = addrs;	for (i = 0; i < 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]);	}	/* now, the table can be loaded into the chipset */	SMC_SELECT_BANK(3);	SMC_SET_MCAST(multicast_table);}/* * 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);	unsigned long ioaddr = dev->base_addr;	DBG(2, "%s: %s\n", dev->name, __FUNCTION__);	SMC_SELECT_BANK(0);	if (dev->flags & IFF_PROMISC) {		DBG(2, "%s: RCR_PRMS\n", dev->name);		lp->rcr_cur_mode |= RCR_PRMS;		SMC_SET_RCR(lp->rcr_cur_mode);	}/* 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) {		lp->rcr_cur_mode |= RCR_ALMUL;		SMC_SET_RCR(lp->rcr_cur_mode);		DBG(2, "%s: RCR_ALMUL\n", dev->name);	}	/*	 * We just get all multicast packets even if we only want them	 * from one source.  This will be changed at some future point.	 */	else if (dev->mc_count)  {		/* support hardware multicasting */		/* be sure I get rid of flags I might have set */		lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);		SMC_SET_RCR(lp->rcr_cur_mode);		/*		 * NOTE: this has to set the bank, so make sure it is the		 * last thing called.  The bank is set to zero at the top		 */		smc_setmulticast(ioaddr, dev->mc_count, dev->mc_list);	} else  {		DBG(2, "%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name);		lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);		SMC_SET_RCR(lp->rcr_cur_mode);		/*		 * since I'm disabling all multicast entirely, I need to		 * clear the multicast list		 */		SMC_SELECT_BANK(3);		SMC_CLEAR_MCAST();	}}/* * 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);	unsigned long ioaddr = dev->base_addr;	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)) {		DBG(2, "smc_open: no valid ethernet hw addr\n");		return -EINVAL;	}	/* clear out all the junk that was put here before... */	lp->saved_skb = NULL;	/* 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);	SMC_SELECT_BANK(1);	SMC_SET_MAC_ADDR(dev->dev_addr);	/* Configure the PHY */	if (lp->phy_type != 0)		smc_phy_configure(dev);	else {		spin_lock_irq(&lp->lock);		smc_10bt_check_media(dev, 1);		spin_unlock_irq(&lp->lock);	}	/*	 * make sure to initialize the link state with netif_carrier_off()	 * somewhere, too --jgarzik	 *	 * smc_phy_configure() and smc_10bt_check_media() does that. --rmk	 */	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->base_addr);	if (lp->phy_type != 0)		smc_phy_powerdown(dev, lp->mii.phy_id);	return 0;}/* * Get the current statistics. * This may be called with the card open or closed. */static struct net_device_stats *smc_query_statistics(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	DBG(2, "%s: %s\n", dev->name, __FUNCTION__);	return &lp->stats;}/* * 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->class_dev.dev->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 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(unsigned long 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);	/*	 * Wait until positive that the interrupt has been generated	 */	do {		int int_status;		udelay(10);		int_status = SMC_GET_INT();		if (int_status & IM_ALLOC_INT)			break;		/* got the interrupt */	} while (--timeout);	/*	 * there is really nothing that I can do here if timeout fails,	 * as autoirq_report will return a 0 anyway, which is what I	 * want in this case.   Plus, the clean up is needed in both	 * cases.

⌨️ 快捷键说明

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