sunlance.c

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

C
1,615
字号
		len -= 1;		if (len == 0)			return;	}	if (len == 1) {		sbus_writeb(0, piobuf);		return;	}	if ((unsigned long)piobuf & 2) {		sbus_writew(0, piobuf);		piobuf += 2;		len -= 2;		if (len == 0)			return;	}	while (len >= 4) {		sbus_writel(0, piobuf);		piobuf += 4;		len -= 4;	}	if (len >= 2) {		sbus_writew(0, piobuf);		piobuf += 2;		len -= 2;	}	if (len >= 1)		sbus_writeb(0, piobuf);}static void lance_tx_timeout(struct net_device *dev){	struct lance_private *lp = netdev_priv(dev);	printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n",	       dev->name, sbus_readw(lp->lregs + RDP));	lance_reset(dev);	netif_wake_queue(dev);}static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct lance_private *lp = netdev_priv(dev);	int entry, skblen, len;	skblen = skb->len;	len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen;	spin_lock_irq(&lp->lock);	dev->stats.tx_bytes += len;	entry = lp->tx_new & TX_RING_MOD_MASK;	if (lp->pio_buffer) {		struct lance_init_block __iomem *ib = lp->init_block_iomem;		sbus_writew((-len) | 0xf000, &ib->btx_ring[entry].length);		sbus_writew(0, &ib->btx_ring[entry].misc);		lance_piocopy_from_skb(&ib->tx_buf[entry][0], skb->data, skblen);		if (len != skblen)			lance_piozero(&ib->tx_buf[entry][skblen], len - skblen);		sbus_writeb(LE_T1_POK | LE_T1_OWN, &ib->btx_ring[entry].tmd1_bits);	} else {		struct lance_init_block *ib = lp->init_block_mem;		ib->btx_ring [entry].length = (-len) | 0xf000;		ib->btx_ring [entry].misc = 0;		skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen);		if (len != skblen)			memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen);		ib->btx_ring [entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN);	}	lp->tx_new = TX_NEXT(entry);	if (TX_BUFFS_AVAIL <= 0)		netif_stop_queue(dev);	/* Kick the lance: transmit now */	sbus_writew(LE_C0_INEA | LE_C0_TDMD, lp->lregs + RDP);	/* Read back CSR to invalidate the E-Cache.	 * This is needed, because DMA_DSBL_WR_INV is set.	 */	if (lp->dregs)		sbus_readw(lp->lregs + RDP);	spin_unlock_irq(&lp->lock);	dev->trans_start = jiffies;	dev_kfree_skb(skb);	return 0;}/* taken from the depca driver */static void lance_load_multicast(struct net_device *dev){	struct lance_private *lp = netdev_priv(dev);	struct dev_mc_list *dmi = dev->mc_list;	char *addrs;	int i;	u32 crc;	u32 val;	/* set all multicast bits */	if (dev->flags & IFF_ALLMULTI)		val = ~0;	else		val = 0;	if (lp->pio_buffer) {		struct lance_init_block __iomem *ib = lp->init_block_iomem;		sbus_writel(val, &ib->filter[0]);		sbus_writel(val, &ib->filter[1]);	} else {		struct lance_init_block *ib = lp->init_block_mem;		ib->filter [0] = val;		ib->filter [1] = val;	}	if (dev->flags & IFF_ALLMULTI)		return;	/* Add addresses */	for (i = 0; i < dev->mc_count; i++) {		addrs = dmi->dmi_addr;		dmi   = dmi->next;		/* multicast address? */		if (!(*addrs & 1))			continue;		crc = ether_crc_le(6, addrs);		crc = crc >> 26;		if (lp->pio_buffer) {			struct lance_init_block __iomem *ib = lp->init_block_iomem;			u16 __iomem *mcast_table = (u16 __iomem *) &ib->filter;			u16 tmp = sbus_readw(&mcast_table[crc>>4]);			tmp |= 1 << (crc & 0xf);			sbus_writew(tmp, &mcast_table[crc>>4]);		} else {			struct lance_init_block *ib = lp->init_block_mem;			u16 *mcast_table = (u16 *) &ib->filter;			mcast_table [crc >> 4] |= 1 << (crc & 0xf);		}	}}static void lance_set_multicast(struct net_device *dev){	struct lance_private *lp = netdev_priv(dev);	struct lance_init_block *ib_mem = lp->init_block_mem;	struct lance_init_block __iomem *ib_iomem = lp->init_block_iomem;	u16 mode;	if (!netif_running(dev))		return;	if (lp->tx_old != lp->tx_new) {		mod_timer(&lp->multicast_timer, jiffies + 4);		netif_wake_queue(dev);		return;	}	netif_stop_queue(dev);	STOP_LANCE(lp);	lp->init_ring(dev);	if (lp->pio_buffer)		mode = sbus_readw(&ib_iomem->mode);	else		mode = ib_mem->mode;	if (dev->flags & IFF_PROMISC) {		mode |= LE_MO_PROM;		if (lp->pio_buffer)			sbus_writew(mode, &ib_iomem->mode);		else			ib_mem->mode = mode;	} else {		mode &= ~LE_MO_PROM;		if (lp->pio_buffer)			sbus_writew(mode, &ib_iomem->mode);		else			ib_mem->mode = mode;		lance_load_multicast(dev);	}	load_csrs(lp);	init_restart_lance(lp);	netif_wake_queue(dev);}static void lance_set_multicast_retry(unsigned long _opaque){	struct net_device *dev = (struct net_device *) _opaque;	lance_set_multicast(dev);}static void lance_free_hwresources(struct lance_private *lp){	if (lp->lregs)		sbus_iounmap(lp->lregs, LANCE_REG_SIZE);	if (lp->init_block_iomem) {		sbus_iounmap(lp->init_block_iomem,			     sizeof(struct lance_init_block));	} else if (lp->init_block_mem) {		sbus_free_consistent(lp->sdev,				     sizeof(struct lance_init_block),				     lp->init_block_mem,				     lp->init_block_dvma);	}}/* Ethtool support... */static void sparc_lance_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){	struct lance_private *lp = netdev_priv(dev);	strcpy(info->driver, "sunlance");	strcpy(info->version, "2.02");	sprintf(info->bus_info, "SBUS:%d",		lp->sdev->slot);}static u32 sparc_lance_get_link(struct net_device *dev){	/* We really do not keep track of this, but this	 * is better than not reporting anything at all.	 */	return 1;}static const struct ethtool_ops sparc_lance_ethtool_ops = {	.get_drvinfo		= sparc_lance_get_drvinfo,	.get_link		= sparc_lance_get_link,};static int __devinit sparc_lance_probe_one(struct sbus_dev *sdev,					   struct sbus_dma *ledma,					   struct sbus_dev *lebuffer){	static unsigned version_printed;	struct device_node *dp = sdev->ofdev.node;	struct net_device *dev;	struct lance_private *lp;	int    i;	DECLARE_MAC_BUF(mac);	dev = alloc_etherdev(sizeof(struct lance_private) + 8);	if (!dev)		return -ENOMEM;	lp = netdev_priv(dev);	if (sparc_lance_debug && version_printed++ == 0)		printk (KERN_INFO "%s", version);	spin_lock_init(&lp->lock);	/* Copy the IDPROM ethernet address to the device structure, later we	 * will copy the address in the device structure to the lance	 * initialization block.	 */	for (i = 0; i < 6; i++)		dev->dev_addr[i] = idprom->id_ethaddr[i];	/* Get the IO region */	lp->lregs = sbus_ioremap(&sdev->resource[0], 0,				 LANCE_REG_SIZE, lancestr);	if (!lp->lregs) {		printk(KERN_ERR "SunLance: Cannot map registers.\n");		goto fail;	}	lp->sdev = sdev;	if (lebuffer) {		/* sanity check */		if (lebuffer->resource[0].start & 7) {			printk(KERN_ERR "SunLance: ERROR: Rx and Tx rings not on even boundary.\n");			goto fail;		}		lp->init_block_iomem =			sbus_ioremap(&lebuffer->resource[0], 0,				     sizeof(struct lance_init_block), "lebuffer");		if (!lp->init_block_iomem) {			printk(KERN_ERR "SunLance: Cannot map PIO buffer.\n");			goto fail;		}		lp->init_block_dvma = 0;		lp->pio_buffer = 1;		lp->init_ring = lance_init_ring_pio;		lp->rx = lance_rx_pio;		lp->tx = lance_tx_pio;	} else {		lp->init_block_mem =			sbus_alloc_consistent(sdev, sizeof(struct lance_init_block),					      &lp->init_block_dvma);		if (!lp->init_block_mem || lp->init_block_dvma == 0) {			printk(KERN_ERR "SunLance: Cannot allocate consistent DMA memory.\n");			goto fail;		}		lp->pio_buffer = 0;		lp->init_ring = lance_init_ring_dvma;		lp->rx = lance_rx_dvma;		lp->tx = lance_tx_dvma;	}	lp->busmaster_regval = of_getintprop_default(dp,  "busmaster-regval",						     (LE_C3_BSWP |						      LE_C3_ACON |						      LE_C3_BCON));	lp->name = lancestr;	lp->ledma = ledma;	lp->burst_sizes = 0;	if (lp->ledma) {		struct device_node *ledma_dp = ledma->sdev->ofdev.node;		const char *prop;		unsigned int sbmask;		u32 csr;		/* Find burst-size property for ledma */		lp->burst_sizes = of_getintprop_default(ledma_dp,							"burst-sizes", 0);		/* ledma may be capable of fast bursts, but sbus may not. */		sbmask = of_getintprop_default(ledma_dp, "burst-sizes",					       DMA_BURSTBITS);		lp->burst_sizes &= sbmask;		/* Get the cable-selection property */		prop = of_get_property(ledma_dp, "cable-selection", NULL);		if (!prop || prop[0] == '\0') {			struct device_node *nd;			printk(KERN_INFO "SunLance: using "			       "auto-carrier-detection.\n");			nd = of_find_node_by_path("/options");			if (!nd)				goto no_link_test;			prop = of_get_property(nd, "tpe-link-test?", NULL);			if (!prop)				goto no_link_test;			if (strcmp(prop, "true")) {				printk(KERN_NOTICE "SunLance: warning: overriding option "				       "'tpe-link-test?'\n");				printk(KERN_NOTICE "SunLance: warning: mail any problems "				       "to ecd@skynet.be\n");				auxio_set_lte(AUXIO_LTE_ON);			}no_link_test:			lp->auto_select = 1;			lp->tpe = 0;		} else if (!strcmp(prop, "aui")) {			lp->auto_select = 0;			lp->tpe = 0;		} else {			lp->auto_select = 0;			lp->tpe = 1;		}		lp->dregs = ledma->regs;		/* Reset ledma */		csr = sbus_readl(lp->dregs + DMA_CSR);		sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR);		udelay(200);		sbus_writel(csr & ~DMA_RST_ENET, lp->dregs + DMA_CSR);	} else		lp->dregs = NULL;	lp->dev = dev;	SET_NETDEV_DEV(dev, &sdev->ofdev.dev);	dev->open = &lance_open;	dev->stop = &lance_close;	dev->hard_start_xmit = &lance_start_xmit;	dev->tx_timeout = &lance_tx_timeout;	dev->watchdog_timeo = 5*HZ;	dev->set_multicast_list = &lance_set_multicast;	dev->ethtool_ops = &sparc_lance_ethtool_ops;	dev->irq = sdev->irqs[0];	dev->dma = 0;	/* We cannot sleep if the chip is busy during a	 * multicast list update event, because such events	 * can occur from interrupts (ex. IPv6).  So we	 * use a timer to try again later when necessary. -DaveM	 */	init_timer(&lp->multicast_timer);	lp->multicast_timer.data = (unsigned long) dev;	lp->multicast_timer.function = &lance_set_multicast_retry;	if (register_netdev(dev)) {		printk(KERN_ERR "SunLance: Cannot register device.\n");		goto fail;	}	dev_set_drvdata(&sdev->ofdev.dev, lp);	printk(KERN_INFO "%s: LANCE %s\n",	       dev->name, print_mac(mac, dev->dev_addr));	return 0;fail:	lance_free_hwresources(lp);	free_netdev(dev);	return -ENODEV;}/* On 4m, find the associated dma for the lance chip */static struct sbus_dma * __devinit find_ledma(struct sbus_dev *sdev){	struct sbus_dma *p;	for_each_dvma(p) {		if (p->sdev == sdev)			return p;	}	return NULL;}#ifdef CONFIG_SUN4#include <asm/sun4paddr.h>#include <asm/machines.h>/* Find all the lance cards on the system and initialize them */static struct sbus_dev sun4_sdev;static int __devinit sparc_lance_init(void){	if ((idprom->id_machtype == (SM_SUN4|SM_4_330)) ||	    (idprom->id_machtype == (SM_SUN4|SM_4_470))) {		memset(&sun4_sdev, 0, sizeof(struct sbus_dev));		sun4_sdev.reg_addrs[0].phys_addr = sun4_eth_physaddr;		sun4_sdev.irqs[0] = 6;		return sparc_lance_probe_one(&sun4_sdev, NULL, NULL);	}	return -ENODEV;}static int __exit sunlance_sun4_remove(void){	struct lance_private *lp = dev_get_drvdata(&sun4_sdev.ofdev.dev);	struct net_device *net_dev = lp->dev;	unregister_netdev(net_dev);	lance_free_hwresources(lp);	free_netdev(net_dev);	dev_set_drvdata(&sun4_sdev.ofdev.dev, NULL);	return 0;}#else /* !CONFIG_SUN4 */static int __devinit sunlance_sbus_probe(struct of_device *dev, const struct of_device_id *match){	struct sbus_dev *sdev = to_sbus_device(&dev->dev);	int err;	if (sdev->parent) {		struct of_device *parent = &sdev->parent->ofdev;		if (!strcmp(parent->node->name, "ledma")) {			struct sbus_dma *ledma = find_ledma(to_sbus_device(&parent->dev));			err = sparc_lance_probe_one(sdev, ledma, NULL);		} else if (!strcmp(parent->node->name, "lebuffer")) {			err = sparc_lance_probe_one(sdev, NULL, to_sbus_device(&parent->dev));		} else			err = sparc_lance_probe_one(sdev, NULL, NULL);	} else		err = sparc_lance_probe_one(sdev, NULL, NULL);	return err;}static int __devexit sunlance_sbus_remove(struct of_device *dev){	struct lance_private *lp = dev_get_drvdata(&dev->dev);	struct net_device *net_dev = lp->dev;	unregister_netdev(net_dev);	lance_free_hwresources(lp);	free_netdev(net_dev);	dev_set_drvdata(&dev->dev, NULL);	return 0;}static struct of_device_id sunlance_sbus_match[] = {	{		.name = "le",	},	{},};MODULE_DEVICE_TABLE(of, sunlance_sbus_match);static struct of_platform_driver sunlance_sbus_driver = {	.name		= "sunlance",	.match_table	= sunlance_sbus_match,	.probe		= sunlance_sbus_probe,	.remove		= __devexit_p(sunlance_sbus_remove),};/* Find all the lance cards on the system and initialize them */static int __init sparc_lance_init(void){	return of_register_driver(&sunlance_sbus_driver, &sbus_bus_type);}#endif /* !CONFIG_SUN4 */static void __exit sparc_lance_exit(void){#ifdef CONFIG_SUN4	sunlance_sun4_remove();#else	of_unregister_driver(&sunlance_sbus_driver);#endif}module_init(sparc_lance_init);module_exit(sparc_lance_exit);

⌨️ 快捷键说明

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