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