⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cirrus.c

📁 《ARM嵌入式Linux设备驱动实例开发》
💻 C
📖 第 1 页 / 共 2 页
字号:

	set_irq_type(dev->irq, IRQT_RISING);

	/* enable the ethernet controller */
	cirrus_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
	cirrus_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);
	cirrus_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
	cirrus_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
	cirrus_set (dev,PP_LineCTL,SerRxON | SerTxON);
	cirrus_set (dev,PP_BusCTL,EnableRQ);

#ifdef FULL_DUPLEX
	cirrus_set (dev,PP_TestCTL,FDX);
#endif	/* #ifdef FULL_DUPLEX */

	/* start the queue */
	netif_start_queue (dev);

	return (0);
}

static int cirrus_stop (struct net_device *dev)
{
	/* disable ethernet controller */
	cirrus_write (dev,PP_BusCTL,0);
	cirrus_write (dev,PP_TestCTL,0);
	cirrus_write (dev,PP_SelfCTL,0);
	cirrus_write (dev,PP_LineCTL,0);
	cirrus_write (dev,PP_BufCFG,0);
	cirrus_write (dev,PP_TxCFG,0);
	cirrus_write (dev,PP_RxCTL,0);
	cirrus_write (dev,PP_RxCFG,0);

	/* uninstall interrupt handler */
	free_irq (dev->irq,dev);

	/* stop the queue */
	netif_stop_queue (dev);

	return (0);
}

static int cirrus_set_mac_address (struct net_device *dev, void *p)
{
	cirrus_t *priv = netdev_priv(dev);
	struct sockaddr *addr = (struct sockaddr *)p;
	int i;

	if (netif_running(dev))
		return -EBUSY;

	spin_lock(&priv->lock);

	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);

	/* configure MAC address */
	for (i = 0; i < ETH_ALEN; i += 2)
		cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));

	spin_unlock(&priv->lock);

	return 0;
}

static struct net_device_stats *cirrus_get_stats (struct net_device *dev)
{
	cirrus_t *priv = netdev_priv(dev);
	return (&priv->stats);
}

static void cirrus_set_receive_mode (struct net_device *dev)
{
	cirrus_t *priv = netdev_priv(dev);

	spin_lock(&priv->lock);

	if ((dev->flags & IFF_PROMISC))
		cirrus_set (dev,PP_RxCTL,PromiscuousA);
	else
		cirrus_clear (dev,PP_RxCTL,PromiscuousA);

	if ((dev->flags & IFF_ALLMULTI) || dev->mc_list)
		cirrus_set (dev,PP_RxCTL,MulticastA);
	else
		cirrus_clear (dev,PP_RxCTL,MulticastA);

	spin_unlock(&priv->lock);
}

static int cirrus_eeprom_wait (struct net_device *dev)
{
	int i;

	for (i = 0; i < 200; i++) {
		if (!(cirrus_read (dev,PP_SelfST) & SIBUSY))
			return (0);
		udelay (1);
	}

	return (-1);
}

static int cirrus_eeprom_read (struct net_device *dev,u16 *value,u16 offset)
{
	if (cirrus_eeprom_wait (dev) < 0)
		return (-1);

	cirrus_write (dev,PP_EEPROMCommand,offset | EEReadRegister);

	if (cirrus_eeprom_wait (dev) < 0)
		return (-1);

	*value = cirrus_read (dev,PP_EEPROMData);

	return (0);
}

static int cirrus_eeprom (struct net_device *dev,cirrus_eeprom_t *eeprom)
{
	u16 offset,buf[16],*word;
	u8 checksum = 0,*byte;

	if (cirrus_eeprom_read (dev,buf,0) < 0) {
		read_timed_out:
		printk (KERN_DEBUG "%s: EEPROM read timed out\n",dev->name);
		return (-ETIMEDOUT);
	}

	if ((buf[0] >> 8) != 0xa1) {
		printk (KERN_DEBUG "%s: No EEPROM present\n",dev->name);
		return (-ENODEV);
	}

	if ((buf[0] & 0xff) < sizeof (buf)) {
		eeprom_too_small:
		printk (KERN_DEBUG "%s: EEPROM too small\n",dev->name);
		return (-ENODEV);
	}

	for (offset = 1; offset < ((buf[0] & 0xff) >> 1); offset++) {
		if (cirrus_eeprom_read (dev,buf + offset,offset) < 0)
			goto read_timed_out;

		if (buf[offset] == 0xffff)
			goto eeprom_too_small;
	}

	if (buf[1] != 0x2020) {
		printk (KERN_DEBUG "%s: Group Header #1 mismatch\n",dev->name);
		return (-EIO);
	}

	if (buf[5] != 0x502c) {
		printk (KERN_DEBUG "%s: Group Header #2 mismatch\n",dev->name);
		return (-EIO);
	}

	if (buf[12] != 0x2158) {
		printk (KERN_DEBUG "%s: Group Header #3 mismatch\n",dev->name);
		return (-EIO);
	}

	eeprom->io_base = buf[2];
	eeprom->irq = buf[3];
	eeprom->dma = buf[4];
	eeprom->mem_base = (buf[7] << 16) | buf[6];
	eeprom->rom_base = (buf[9] << 16) | buf[8];
	eeprom->rom_mask = (buf[11] << 16) | buf[10];

	word = (u16 *) eeprom->mac;
	for (offset = 0; offset < 3; offset++) word[offset] = buf[13 + offset];

	byte = (u8 *) buf;
	for (offset = 0; offset < sizeof (buf); offset++) checksum += byte[offset];

	if (cirrus_eeprom_read (dev,&offset,0x10) < 0)
		goto read_timed_out;

	if ((offset >> 8) != (u8) (0x100 - checksum)) {
		printk (KERN_DEBUG "%s: Checksum mismatch (expected 0x%.2x, got 0x%.2x instead\n",
				dev->name,
				(u8) (0x100 - checksum),
				offset >> 8);
		return (-EIO);
	}

	return (0);
}

static int cirrus_remove(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct resource *res;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) BUG();

	iounmap((void __iomem *)(ndev->base_addr));
	release_mem_region(res->start, res->end - res->start);

	dev_set_drvdata(dev, NULL);
	free_netdev(ndev);

	printk("cirrus-cs89x0: removed.\n");
	return 0;
}

static int cirrus_suspend(struct device *dev, pm_message_t state)
{
	printk("%s: not implemented\n",__FUNCTION__);
	return 0;
}

static int cirrus_resume(struct device *dev)
{
	printk("%s: not implemented\n",__FUNCTION__);
	return 0;
}

/*
 * Driver initialization routines
 */

void cirrus_parse_mac (const char *macstr, char *mac)
{
	int i;
	if (strlen(macstr) != 17)
		printk("invalid MAC string format\n");
	for (i = 0; i < 6; i++) {
		mac[i] = simple_strtoul(macstr + i * 3, NULL, 16);
	}
}

int __init cirrus_probe (struct net_device *dev, unsigned long ioaddr)
{
	cirrus_t *priv = netdev_priv(dev);
	int i;
	u16 value;

	ether_setup (dev);

	dev->open               = cirrus_start;
	dev->stop               = cirrus_stop;
	dev->hard_start_xmit    = cirrus_send_start;
	dev->get_stats          = cirrus_get_stats;
	dev->set_multicast_list = cirrus_set_receive_mode;
	dev->set_mac_address	= cirrus_set_mac_address;
	dev->tx_timeout         = cirrus_transmit_timeout;
	dev->watchdog_timeo     = HZ;

	dev->if_port   = IF_PORT_10BASET;
	dev->priv      = (void *)priv;

	spin_lock_init(&priv->lock);

	SET_MODULE_OWNER (dev);

	dev->base_addr = ioaddr;

	/* if an EEPROM is present, use it's MAC address */
	if (!cirrus_eeprom(dev,&priv->eeprom))
		for (i = 0; i < 6; i++)
			dev->dev_addr[i] = priv->eeprom.mac[i];
	else
		cirrus_parse_mac(cirrus_mac, dev->dev_addr);

	/* verify EISA registration number for Cirrus Logic */
	if ((value = cirrus_read (dev,PP_ProductID)) != EISA_REG_CODE) {
		printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value);
		return (-ENXIO);
	}

	/* verify chip version */
	value = cirrus_read (dev,PP_ProductID + 2);
	if (VERSION (value) != CS8900A) {
		printk (KERN_ERR "%s: unknown chip version 0x%08x\n",dev->name,VERSION (value));
		return (-ENXIO);
	}
	printk (KERN_INFO "%s: CS8900A rev %c detected\n",dev->name,'B' + REVISION (value) - REV_B);

	/* setup interrupt number */
	cirrus_write (dev,PP_IntNum,0);

	/* configure MAC address */
	for (i = 0; i < ETH_ALEN; i += 2)
		cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));

	return register_netdev(dev);
}

int __init cirrus_drv_probe (struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct resource *res;
	unsigned int *addr;

	int ret;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		ret = -ENODEV;
		goto out;
	}

	/* Request the regions. */
	if (!request_mem_region(res->start, 16, "cirrus-cs89x0")) {
		ret = -EBUSY;
		goto out;
	}

	/* remap it. */
	addr = ioremap(res->start, res->end - res->start);
	if (!addr) {
		ret = -ENOMEM;
		goto release_1;
	}

	ndev = alloc_etherdev(sizeof(cirrus_t));
	if (!ndev) {
		printk("cirrus-cs89x0: could not allocate device.\n");
		ret = -ENOMEM;
		goto release_2;
	}

	SET_NETDEV_DEV(ndev, dev);

	ndev->irq = platform_get_irq(pdev, 0);
	printk(KERN_DEBUG "cirrus: irq:%d\n",ndev->irq);

	dev_set_drvdata(dev, ndev);

	ret = cirrus_probe(ndev, (unsigned long)addr);
	if (ret != 0)
		goto release_3;
	return 0;

release_3:
	dev_set_drvdata(dev, NULL);
	free_netdev(ndev);
release_2:
	iounmap(addr);
release_1:
	release_mem_region(res->start, res->end - res->start);
out:
	printk("cirrus-cs89x0: not found (%d).\n", ret);
	return ret;
}

static struct device_driver cirrus_driver = {
	.name		= "cirrus-cs89x0",
	.bus		= &platform_bus_type,
	.probe		= cirrus_drv_probe,
	.remove		= cirrus_remove,
	.suspend	= cirrus_suspend,
	.resume		= cirrus_resume,
};

static int __init cirrus_init(void)
{
	return driver_register(&cirrus_driver);
}

static void __exit cirrus_cleanup(void)
{
	driver_unregister(&cirrus_driver);
}

MODULE_AUTHOR ("Abraham van der Merwe <abraham at 2d3d.co.za>");
MODULE_DESCRIPTION ("Cirrus Logic CS8900A driver for Linux (V0.02)");
MODULE_LICENSE ("GPL");

module_init (cirrus_init);
module_exit (cirrus_cleanup);

⌨️ 快捷键说明

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