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 + -
显示快捷键?