tsi108_eth.c
来自「linux 内核源代码」· C语言 代码 · 共 1,704 行 · 第 1/4 页
C
1,704 行
while (!((phyval = tsi108_read_mii(data, MII_BMSR)) & BMSR_LSTATUS)) { if (i++ > (MII_READ_DELAY / 10)) { data->link_up = 0; break; } spin_unlock_irqrestore(&phy_lock, flags); msleep(10); spin_lock_irqsave(&phy_lock, flags); } printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyval); data->phy_ok = 1; data->init_media = 1; spin_unlock_irqrestore(&phy_lock, flags);}static void tsi108_kill_phy(struct net_device *dev){ struct tsi108_prv_data *data = netdev_priv(dev); unsigned long flags; spin_lock_irqsave(&phy_lock, flags); tsi108_write_mii(data, MII_BMCR, BMCR_PDOWN); data->phy_ok = 0; spin_unlock_irqrestore(&phy_lock, flags);}static int tsi108_open(struct net_device *dev){ int i; struct tsi108_prv_data *data = netdev_priv(dev); unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc); unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc); i = request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev); if (i != 0) { printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n", data->id, data->irq_num); return i; } else { dev->irq = data->irq_num; printk(KERN_NOTICE "tsi108_open : Port %d Assigned IRQ %d to %s\n", data->id, dev->irq, dev->name); } data->rxring = dma_alloc_coherent(NULL, rxring_size, &data->rxdma, GFP_KERNEL); if (!data->rxring) { printk(KERN_DEBUG "TSI108_ETH: failed to allocate memory for rxring!\n"); return -ENOMEM; } else { memset(data->rxring, 0, rxring_size); } data->txring = dma_alloc_coherent(NULL, txring_size, &data->txdma, GFP_KERNEL); if (!data->txring) { printk(KERN_DEBUG "TSI108_ETH: failed to allocate memory for txring!\n"); pci_free_consistent(0, rxring_size, data->rxring, data->rxdma); return -ENOMEM; } else { memset(data->txring, 0, txring_size); } for (i = 0; i < TSI108_RXRING_LEN; i++) { data->rxring[i].next0 = data->rxdma + (i + 1) * sizeof(rx_desc); data->rxring[i].blen = TSI108_RXBUF_SIZE; data->rxring[i].vlan = 0; } data->rxring[TSI108_RXRING_LEN - 1].next0 = data->rxdma; data->rxtail = 0; data->rxhead = 0; for (i = 0; i < TSI108_RXRING_LEN; i++) { struct sk_buff *skb = dev_alloc_skb(TSI108_RXBUF_SIZE + NET_IP_ALIGN); if (!skb) { /* Bah. No memory for now, but maybe we'll get * some more later. * For now, we'll live with the smaller ring. */ printk(KERN_WARNING "%s: Could only allocate %d receive skb(s).\n", dev->name, i); data->rxhead = i; break; } data->rxskbs[i] = skb; /* Align the payload on a 4-byte boundary */ skb_reserve(skb, 2); data->rxskbs[i] = skb; data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data); data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT; } data->rxfree = i; TSI_WRITE(TSI108_EC_RXQ_PTRLOW, data->rxdma); for (i = 0; i < TSI108_TXRING_LEN; i++) { data->txring[i].next0 = data->txdma + (i + 1) * sizeof(tx_desc); data->txring[i].misc = 0; } data->txring[TSI108_TXRING_LEN - 1].next0 = data->txdma; data->txtail = 0; data->txhead = 0; data->txfree = TSI108_TXRING_LEN; TSI_WRITE(TSI108_EC_TXQ_PTRLOW, data->txdma); tsi108_init_phy(dev); napi_enable(&data->napi); setup_timer(&data->timer, tsi108_timed_checker, (unsigned long)dev); mod_timer(&data->timer, jiffies + 1); tsi108_restart_rx(data, dev); TSI_WRITE(TSI108_EC_INTSTAT, ~0); TSI_WRITE(TSI108_EC_INTMASK, ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR | TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 | TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT | TSI108_INT_SFN | TSI108_INT_STATCARRY)); TSI_WRITE(TSI108_MAC_CFG1, TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN); netif_start_queue(dev); return 0;}static int tsi108_close(struct net_device *dev){ struct tsi108_prv_data *data = netdev_priv(dev); netif_stop_queue(dev); napi_disable(&data->napi); del_timer_sync(&data->timer); tsi108_stop_ethernet(dev); tsi108_kill_phy(dev); TSI_WRITE(TSI108_EC_INTMASK, ~0); TSI_WRITE(TSI108_MAC_CFG1, 0); /* Check for any pending TX packets, and drop them. */ while (!data->txfree || data->txhead != data->txtail) { int tx = data->txtail; struct sk_buff *skb; skb = data->txskbs[tx]; data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN; data->txfree++; dev_kfree_skb(skb); } synchronize_irq(data->irq_num); free_irq(data->irq_num, dev); /* Discard the RX ring. */ while (data->rxfree) { int rx = data->rxtail; struct sk_buff *skb; skb = data->rxskbs[rx]; data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN; data->rxfree--; dev_kfree_skb(skb); } dma_free_coherent(0, TSI108_RXRING_LEN * sizeof(rx_desc), data->rxring, data->rxdma); dma_free_coherent(0, TSI108_TXRING_LEN * sizeof(tx_desc), data->txring, data->txdma); return 0;}static void tsi108_init_mac(struct net_device *dev){ struct tsi108_prv_data *data = netdev_priv(dev); TSI_WRITE(TSI108_MAC_CFG2, TSI108_MAC_CFG2_DFLT_PREAMBLE | TSI108_MAC_CFG2_PADCRC); TSI_WRITE(TSI108_EC_TXTHRESH, (192 << TSI108_EC_TXTHRESH_STARTFILL) | (192 << TSI108_EC_TXTHRESH_STOPFILL)); TSI_WRITE(TSI108_STAT_CARRYMASK1, ~(TSI108_STAT_CARRY1_RXBYTES | TSI108_STAT_CARRY1_RXPKTS | TSI108_STAT_CARRY1_RXFCS | TSI108_STAT_CARRY1_RXMCAST | TSI108_STAT_CARRY1_RXALIGN | TSI108_STAT_CARRY1_RXLENGTH | TSI108_STAT_CARRY1_RXRUNT | TSI108_STAT_CARRY1_RXJUMBO | TSI108_STAT_CARRY1_RXFRAG | TSI108_STAT_CARRY1_RXJABBER | TSI108_STAT_CARRY1_RXDROP)); TSI_WRITE(TSI108_STAT_CARRYMASK2, ~(TSI108_STAT_CARRY2_TXBYTES | TSI108_STAT_CARRY2_TXPKTS | TSI108_STAT_CARRY2_TXEXDEF | TSI108_STAT_CARRY2_TXEXCOL | TSI108_STAT_CARRY2_TXTCOL | TSI108_STAT_CARRY2_TXPAUSE)); TSI_WRITE(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATEN); TSI_WRITE(TSI108_MAC_CFG1, 0); TSI_WRITE(TSI108_EC_RXCFG, TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE); TSI_WRITE(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT | TSI108_EC_TXQ_CFG_EOQ_OWN_INT | TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT << TSI108_EC_TXQ_CFG_SFNPORT)); TSI_WRITE(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT | TSI108_EC_RXQ_CFG_EOQ_OWN_INT | TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT << TSI108_EC_RXQ_CFG_SFNPORT)); TSI_WRITE(TSI108_EC_TXQ_BUFCFG, TSI108_EC_TXQ_BUFCFG_BURST256 | TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT << TSI108_EC_TXQ_BUFCFG_SFNPORT)); TSI_WRITE(TSI108_EC_RXQ_BUFCFG, TSI108_EC_RXQ_BUFCFG_BURST256 | TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT << TSI108_EC_RXQ_BUFCFG_SFNPORT)); TSI_WRITE(TSI108_EC_INTMASK, ~0);}static int tsi108_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct tsi108_prv_data *data = netdev_priv(dev); return generic_mii_ioctl(&data->mii_if, if_mii(rq), cmd, NULL);}static inttsi108_init_one(struct platform_device *pdev){ struct net_device *dev = NULL; struct tsi108_prv_data *data = NULL; hw_info *einfo; int err = 0; DECLARE_MAC_BUF(mac); einfo = pdev->dev.platform_data; if (NULL == einfo) { printk(KERN_ERR "tsi-eth %d: Missing additional data!\n", pdev->id); return -ENODEV; } /* Create an ethernet device instance */ dev = alloc_etherdev(sizeof(struct tsi108_prv_data)); if (!dev) { printk("tsi108_eth: Could not allocate a device structure\n"); return -ENOMEM; } printk("tsi108_eth%d: probe...\n", pdev->id); data = netdev_priv(dev); data->dev = dev; pr_debug("tsi108_eth%d:regs:phyresgs:phy:irq_num=0x%x:0x%x:0x%x:0x%x\n", pdev->id, einfo->regs, einfo->phyregs, einfo->phy, einfo->irq_num); data->regs = ioremap(einfo->regs, 0x400); if (NULL == data->regs) { err = -ENOMEM; goto regs_fail; } data->phyregs = ioremap(einfo->phyregs, 0x400); if (NULL == data->phyregs) { err = -ENOMEM; goto regs_fail; }/* MII setup */ data->mii_if.dev = dev; data->mii_if.mdio_read = tsi108_mdio_read; data->mii_if.mdio_write = tsi108_mdio_write; data->mii_if.phy_id = einfo->phy; data->mii_if.phy_id_mask = 0x1f; data->mii_if.reg_num_mask = 0x1f; data->mii_if.supports_gmii = mii_check_gmii_support(&data->mii_if); data->phy = einfo->phy; data->phy_type = einfo->phy_type; data->irq_num = einfo->irq_num; data->id = pdev->id; dev->open = tsi108_open; dev->stop = tsi108_close; dev->hard_start_xmit = tsi108_send_packet; dev->set_mac_address = tsi108_set_mac; dev->set_multicast_list = tsi108_set_rx_mode; dev->get_stats = tsi108_get_stats; netif_napi_add(dev, &data->napi, tsi108_poll, 64); dev->do_ioctl = tsi108_do_ioctl; /* Apparently, the Linux networking code won't use scatter-gather * if the hardware doesn't do checksums. However, it's faster * to checksum in place and use SG, as (among other reasons) * the cache won't be dirtied (which then has to be flushed * before DMA). The checksumming is done by the driver (via * a new function skb_csum_dev() in net/core/skbuff.c). */ dev->features = NETIF_F_HIGHDMA; spin_lock_init(&data->txlock); spin_lock_init(&data->misclock); tsi108_reset_ether(data); tsi108_kill_phy(dev); if ((err = tsi108_get_mac(dev)) != 0) { printk(KERN_ERR "%s: Invalid MAC address. Please correct.\n", dev->name); goto register_fail; } tsi108_init_mac(dev); err = register_netdev(dev); if (err) { printk(KERN_ERR "%s: Cannot register net device, aborting.\n", dev->name); goto register_fail; } printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: %s\n", dev->name, print_mac(mac, dev->dev_addr));#ifdef DEBUG data->msg_enable = DEBUG; dump_eth_one(dev);#endif return 0;register_fail: iounmap(data->regs); iounmap(data->phyregs);regs_fail: free_netdev(dev); return err;}/* There's no way to either get interrupts from the PHY when * something changes, or to have the Tsi108 automatically communicate * with the PHY to reconfigure itself. * * Thus, we have to do it using a timer. */static void tsi108_timed_checker(unsigned long dev_ptr){ struct net_device *dev = (struct net_device *)dev_ptr; struct tsi108_prv_data *data = netdev_priv(dev); tsi108_check_phy(dev); tsi108_check_rxring(dev); mod_timer(&data->timer, jiffies + CHECK_PHY_INTERVAL);}static int tsi108_ether_init(void){ int ret; ret = platform_driver_register (&tsi_eth_driver); if (ret < 0){ printk("tsi108_ether_init: error initializing ethernet " "device\n"); return ret; } return 0;}static int tsi108_ether_remove(struct platform_device *pdev){ struct net_device *dev = platform_get_drvdata(pdev); struct tsi108_prv_data *priv = netdev_priv(dev); unregister_netdev(dev); tsi108_stop_ethernet(dev); platform_set_drvdata(pdev, NULL); iounmap(priv->regs); iounmap(priv->phyregs); free_netdev(dev); return 0;}static void tsi108_ether_exit(void){ platform_driver_unregister(&tsi_eth_driver);}module_init(tsi108_ether_init);module_exit(tsi108_ether_exit);MODULE_AUTHOR("Tundra Semiconductor Corporation");MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?