ep93xx_eth.c

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

C
917
字号
							ep->descs_dma_addr);}/* * The hardware enforces a sub-2K maximum packet size, so we put * two buffers on every hardware page. */static int ep93xx_alloc_buffers(struct ep93xx_priv *ep){	int i;	ep->descs = dma_alloc_coherent(NULL, sizeof(struct ep93xx_descs),				&ep->descs_dma_addr, GFP_KERNEL | GFP_DMA);	if (ep->descs == NULL)		return 1;	for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {		void *page;		dma_addr_t d;		page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);		if (page == NULL)			goto err;		d = dma_map_single(NULL, page, PAGE_SIZE, DMA_FROM_DEVICE);		if (dma_mapping_error(d)) {			free_page((unsigned long)page);			goto err;		}		ep->rx_buf[i] = page;		ep->descs->rdesc[i].buf_addr = d;		ep->descs->rdesc[i].rdesc1 = (i << 16) | PKT_BUF_SIZE;		ep->rx_buf[i + 1] = page + PKT_BUF_SIZE;		ep->descs->rdesc[i + 1].buf_addr = d + PKT_BUF_SIZE;		ep->descs->rdesc[i + 1].rdesc1 = ((i + 1) << 16) | PKT_BUF_SIZE;	}	for (i = 0; i < TX_QUEUE_ENTRIES; i += 2) {		void *page;		dma_addr_t d;		page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);		if (page == NULL)			goto err;		d = dma_map_single(NULL, page, PAGE_SIZE, DMA_TO_DEVICE);		if (dma_mapping_error(d)) {			free_page((unsigned long)page);			goto err;		}		ep->tx_buf[i] = page;		ep->descs->tdesc[i].buf_addr = d;		ep->tx_buf[i + 1] = page + PKT_BUF_SIZE;		ep->descs->tdesc[i + 1].buf_addr = d + PKT_BUF_SIZE;	}	return 0;err:	ep93xx_free_buffers(ep);	return 1;}static int ep93xx_start_hw(struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	unsigned long addr;	int i;	wrl(ep, REG_SELFCTL, REG_SELFCTL_RESET);	for (i = 0; i < 10; i++) {		if ((rdl(ep, REG_SELFCTL) & REG_SELFCTL_RESET) == 0)			break;		msleep(1);	}	if (i == 10) {		printk(KERN_CRIT DRV_MODULE_NAME ": hw failed to reset\n");		return 1;	}	wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9));	/* Does the PHY support preamble suppress?  */	if ((ep93xx_mdio_read(dev, ep->mii.phy_id, MII_BMSR) & 0x0040) != 0)		wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9) | (1 << 8));	/* Receive descriptor ring.  */	addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rdesc);	wrl(ep, REG_RXDQBADD, addr);	wrl(ep, REG_RXDCURADD, addr);	wrw(ep, REG_RXDQBLEN, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rdesc));	/* Receive status ring.  */	addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rstat);	wrl(ep, REG_RXSTSQBADD, addr);	wrl(ep, REG_RXSTSQCURADD, addr);	wrw(ep, REG_RXSTSQBLEN, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rstat));	/* Transmit descriptor ring.  */	addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tdesc);	wrl(ep, REG_TXDQBADD, addr);	wrl(ep, REG_TXDQCURADD, addr);	wrw(ep, REG_TXDQBLEN, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tdesc));	/* Transmit status ring.  */	addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tstat);	wrl(ep, REG_TXSTSQBADD, addr);	wrl(ep, REG_TXSTSQCURADD, addr);	wrw(ep, REG_TXSTSQBLEN, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tstat));	wrl(ep, REG_BMCTL, REG_BMCTL_ENABLE_TX | REG_BMCTL_ENABLE_RX);	wrl(ep, REG_INTEN, REG_INTEN_TX | REG_INTEN_RX);	wrl(ep, REG_GIINTMSK, 0);	for (i = 0; i < 10; i++) {		if ((rdl(ep, REG_BMSTS) & REG_BMSTS_RX_ACTIVE) != 0)			break;		msleep(1);	}	if (i == 10) {		printk(KERN_CRIT DRV_MODULE_NAME ": hw failed to start\n");		return 1;	}	wrl(ep, REG_RXDENQ, RX_QUEUE_ENTRIES);	wrl(ep, REG_RXSTSENQ, RX_QUEUE_ENTRIES);	wrb(ep, REG_INDAD0, dev->dev_addr[0]);	wrb(ep, REG_INDAD1, dev->dev_addr[1]);	wrb(ep, REG_INDAD2, dev->dev_addr[2]);	wrb(ep, REG_INDAD3, dev->dev_addr[3]);	wrb(ep, REG_INDAD4, dev->dev_addr[4]);	wrb(ep, REG_INDAD5, dev->dev_addr[5]);	wrl(ep, REG_AFP, 0);	wrl(ep, REG_MAXFRMLEN, (MAX_PKT_SIZE << 16) | MAX_PKT_SIZE);	wrl(ep, REG_RXCTL, REG_RXCTL_DEFAULT);	wrl(ep, REG_TXCTL, REG_TXCTL_ENABLE);	return 0;}static void ep93xx_stop_hw(struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	int i;	wrl(ep, REG_SELFCTL, REG_SELFCTL_RESET);	for (i = 0; i < 10; i++) {		if ((rdl(ep, REG_SELFCTL) & REG_SELFCTL_RESET) == 0)			break;		msleep(1);	}	if (i == 10)		printk(KERN_CRIT DRV_MODULE_NAME ": hw failed to reset\n");}static int ep93xx_open(struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	int err;	if (ep93xx_alloc_buffers(ep))		return -ENOMEM;	if (is_zero_ether_addr(dev->dev_addr)) {		random_ether_addr(dev->dev_addr);		printk(KERN_INFO "%s: generated random MAC address "			"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", dev->name,			dev->dev_addr[0], dev->dev_addr[1],			dev->dev_addr[2], dev->dev_addr[3],			dev->dev_addr[4], dev->dev_addr[5]);	}	napi_enable(&ep->napi);	if (ep93xx_start_hw(dev)) {		napi_disable(&ep->napi);		ep93xx_free_buffers(ep);		return -EIO;	}	spin_lock_init(&ep->rx_lock);	ep->rx_pointer = 0;	ep->tx_clean_pointer = 0;	ep->tx_pointer = 0;	spin_lock_init(&ep->tx_pending_lock);	ep->tx_pending = 0;	err = request_irq(ep->irq, ep93xx_irq, IRQF_SHARED, dev->name, dev);	if (err) {		napi_disable(&ep->napi);		ep93xx_stop_hw(dev);		ep93xx_free_buffers(ep);		return err;	}	wrl(ep, REG_GIINTMSK, REG_GIINTMSK_ENABLE);	netif_start_queue(dev);	return 0;}static int ep93xx_close(struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	napi_disable(&ep->napi);	netif_stop_queue(dev);	wrl(ep, REG_GIINTMSK, 0);	free_irq(ep->irq, dev);	ep93xx_stop_hw(dev);	ep93xx_free_buffers(ep);	return 0;}static int ep93xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){	struct ep93xx_priv *ep = netdev_priv(dev);	struct mii_ioctl_data *data = if_mii(ifr);	return generic_mii_ioctl(&ep->mii, data, cmd, NULL);}static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg){	struct ep93xx_priv *ep = netdev_priv(dev);	int data;	int i;	wrl(ep, REG_MIICMD, REG_MIICMD_READ | (phy_id << 5) | reg);	for (i = 0; i < 10; i++) {		if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0)			break;		msleep(1);	}	if (i == 10) {		printk(KERN_INFO DRV_MODULE_NAME ": mdio read timed out\n");		data = 0xffff;	} else {		data = rdl(ep, REG_MIIDATA);	}	return data;}static void ep93xx_mdio_write(struct net_device *dev, int phy_id, int reg, int data){	struct ep93xx_priv *ep = netdev_priv(dev);	int i;	wrl(ep, REG_MIIDATA, data);	wrl(ep, REG_MIICMD, REG_MIICMD_WRITE | (phy_id << 5) | reg);	for (i = 0; i < 10; i++) {		if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0)			break;		msleep(1);	}	if (i == 10)		printk(KERN_INFO DRV_MODULE_NAME ": mdio write timed out\n");}static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){	strcpy(info->driver, DRV_MODULE_NAME);	strcpy(info->version, DRV_MODULE_VERSION);}static int ep93xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd){	struct ep93xx_priv *ep = netdev_priv(dev);	return mii_ethtool_gset(&ep->mii, cmd);}static int ep93xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd){	struct ep93xx_priv *ep = netdev_priv(dev);	return mii_ethtool_sset(&ep->mii, cmd);}static int ep93xx_nway_reset(struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	return mii_nway_restart(&ep->mii);}static u32 ep93xx_get_link(struct net_device *dev){	struct ep93xx_priv *ep = netdev_priv(dev);	return mii_link_ok(&ep->mii);}static struct ethtool_ops ep93xx_ethtool_ops = {	.get_drvinfo		= ep93xx_get_drvinfo,	.get_settings		= ep93xx_get_settings,	.set_settings		= ep93xx_set_settings,	.nway_reset		= ep93xx_nway_reset,	.get_link		= ep93xx_get_link,};struct net_device *ep93xx_dev_alloc(struct ep93xx_eth_data *data){	struct net_device *dev;	dev = alloc_etherdev(sizeof(struct ep93xx_priv));	if (dev == NULL)		return NULL;	memcpy(dev->dev_addr, data->dev_addr, ETH_ALEN);	dev->get_stats = ep93xx_get_stats;	dev->ethtool_ops = &ep93xx_ethtool_ops;	dev->hard_start_xmit = ep93xx_xmit;	dev->open = ep93xx_open;	dev->stop = ep93xx_close;	dev->do_ioctl = ep93xx_ioctl;	dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;	return dev;}static int ep93xx_eth_remove(struct platform_device *pdev){	struct net_device *dev;	struct ep93xx_priv *ep;	dev = platform_get_drvdata(pdev);	if (dev == NULL)		return 0;	platform_set_drvdata(pdev, NULL);	ep = netdev_priv(dev);	/* @@@ Force down.  */	unregister_netdev(dev);	ep93xx_free_buffers(ep);	if (ep->base_addr != NULL)		iounmap(ep->base_addr);	if (ep->res != NULL) {		release_resource(ep->res);		kfree(ep->res);	}	free_netdev(dev);	return 0;}static int ep93xx_eth_probe(struct platform_device *pdev){	struct ep93xx_eth_data *data;	struct net_device *dev;	struct ep93xx_priv *ep;	int err;	if (pdev == NULL)		return -ENODEV;	data = pdev->dev.platform_data;	dev = ep93xx_dev_alloc(data);	if (dev == NULL) {		err = -ENOMEM;		goto err_out;	}	ep = netdev_priv(dev);	ep->dev = dev;	netif_napi_add(dev, &ep->napi, ep93xx_poll, 64);	platform_set_drvdata(pdev, dev);	ep->res = request_mem_region(pdev->resource[0].start,			pdev->resource[0].end - pdev->resource[0].start + 1,			pdev->dev.bus_id);	if (ep->res == NULL) {		dev_err(&pdev->dev, "Could not reserve memory region\n");		err = -ENOMEM;		goto err_out;	}	ep->base_addr = ioremap(pdev->resource[0].start,			pdev->resource[0].end - pdev->resource[0].start);	if (ep->base_addr == NULL) {		dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");		err = -EIO;		goto err_out;	}	ep->irq = pdev->resource[1].start;	ep->mii.phy_id = data->phy_id;	ep->mii.phy_id_mask = 0x1f;	ep->mii.reg_num_mask = 0x1f;	ep->mii.dev = dev;	ep->mii.mdio_read = ep93xx_mdio_read;	ep->mii.mdio_write = ep93xx_mdio_write;	ep->mdc_divisor = 40;	/* Max HCLK 100 MHz, min MDIO clk 2.5 MHz.  */	err = register_netdev(dev);	if (err) {		dev_err(&pdev->dev, "Failed to register netdev\n");		goto err_out;	}	printk(KERN_INFO "%s: ep93xx on-chip ethernet, IRQ %d, "			 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", dev->name,			ep->irq, data->dev_addr[0], data->dev_addr[1],			data->dev_addr[2], data->dev_addr[3],			data->dev_addr[4], data->dev_addr[5]);	return 0;err_out:	ep93xx_eth_remove(pdev);	return err;}static struct platform_driver ep93xx_eth_driver = {	.probe		= ep93xx_eth_probe,	.remove		= ep93xx_eth_remove,	.driver		= {		.name	= "ep93xx-eth",	},};static int __init ep93xx_eth_init_module(void){	printk(KERN_INFO DRV_MODULE_NAME " version " DRV_MODULE_VERSION " loading\n");	return platform_driver_register(&ep93xx_eth_driver);}static void __exit ep93xx_eth_cleanup_module(void){	platform_driver_unregister(&ep93xx_eth_driver);}module_init(ep93xx_eth_init_module);module_exit(ep93xx_eth_cleanup_module);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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