tc35815.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,746 行 · 第 1/4 页

C
1,746
字号
	int tfd_end;	struct RxFD *rfd_base;	struct RxFD *rfd_limit;	struct RxFD *rfd_cur;	struct FrFD *fbl_ptr;	unsigned char fbl_curid;	dma_addr_t data_buf_dma_handle[RX_BUF_PAGES];	void * data_buf[RX_BUF_PAGES];		/* packing */	spinlock_t lock;};/* Index to functions, as function prototypes. */static int __devinit tc35815_probe1(struct pci_dev *pdev, unsigned int base_addr, unsigned int irq);static int	tc35815_open(struct net_device *dev);static int	tc35815_send_packet(struct sk_buff *skb, struct net_device *dev);static void     tc35815_tx_timeout(struct net_device *dev);static irqreturn_t tc35815_interrupt(int irq, void *dev_id, struct pt_regs *regs);static void	tc35815_rx(struct net_device *dev);static void	tc35815_txdone(struct net_device *dev);static int	tc35815_close(struct net_device *dev);static struct	net_device_stats *tc35815_get_stats(struct net_device *dev);static void	tc35815_set_multicast_list(struct net_device *dev);static void 	tc35815_chip_reset(struct net_device *dev);static void 	tc35815_chip_init(struct net_device *dev);static void 	tc35815_phy_chip_init(struct net_device *dev);/* A list of all installed tc35815 devices. */static struct net_device *root_tc35815_dev = NULL;/* * PCI device identifiers for "new style" Linux PCI Device Drivers */static struct pci_device_id tc35815_pci_tbl[] = {    { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },    { 0, }};MODULE_DEVICE_TABLE (pci, tc35815_pci_tbl);inttc35815_probe(struct pci_dev *pdev,		const struct pci_device_id *ent){	int err = 0;	int ret;	unsigned long pci_memaddr;	unsigned int pci_irq_line;	printk(KERN_INFO "tc35815_probe: found device %#08x.%#08x\n", ent->vendor, ent->device);	err = pci_enable_device(pdev);	if (err)		return err;        pci_memaddr = pci_resource_start (pdev, 1);        printk(KERN_INFO "    pci_memaddr=%#08lx  resource_flags=%#08lx\n", pci_memaddr, pci_resource_flags (pdev, 0));	if (!pci_memaddr) {		printk(KERN_WARNING "no PCI MEM resources, aborting\n");		ret = -ENODEV;		goto err_out;	}	pci_irq_line = pdev->irq;	/* irq disabled. */	if (pci_irq_line == 0) {		printk(KERN_WARNING "no PCI irq, aborting\n");		ret = -ENODEV;		goto err_out;	}	ret =  tc35815_probe1(pdev, pci_memaddr, pci_irq_line);	if (ret)		goto err_out;	pci_set_master(pdev);	return 0;err_out:	pci_disable_device(pdev);	return ret;}static int __devinit tc35815_probe1(struct pci_dev *pdev, unsigned int base_addr, unsigned int irq){	static unsigned version_printed = 0;	int i, ret;	struct tc35815_local *lp;	struct tc35815_regs *tr;	struct net_device *dev;	/* Allocate a new 'dev' if needed. */	dev = alloc_etherdev(sizeof(struct tc35815_local));	if (dev == NULL)		return -ENOMEM;	/*	 * alloc_etherdev allocs and zeros dev->priv	 */	lp = dev->priv;	if (tc35815_debug  &&  version_printed++ == 0)		printk(KERN_DEBUG "%s", version);	/* Fill in the 'dev' fields. */	dev->irq = irq;	dev->base_addr = (unsigned long)ioremap(base_addr,						sizeof(struct tc35815_regs));	if (!dev->base_addr) {		ret = -ENOMEM;		goto err_out;	}	tr = (struct tc35815_regs*)dev->base_addr;	tc35815_chip_reset(dev);	/* Retrieve and print the ethernet address. */	while (tc_readl(&tr->PROM_Ctl) & PROM_Busy)		;	for (i = 0; i < 6; i += 2) {		unsigned short data;		tc_writel(PROM_Busy | PROM_Read | (i / 2 + 2), &tr->PROM_Ctl);		while (tc_readl(&tr->PROM_Ctl) & PROM_Busy)			;		data = tc_readl(&tr->PROM_Data);		dev->dev_addr[i] = data & 0xff;		dev->dev_addr[i+1] = data >> 8;	}	/* Initialize the device structure. */	lp->pdev = pdev;	lp->next_module = root_tc35815_dev;	root_tc35815_dev = dev;	spin_lock_init(&lp->lock);	if (dev->mem_start > 0) {		lp->option = dev->mem_start;		if ((lp->option & TC35815_OPT_10M) &&		    (lp->option & TC35815_OPT_100M)) {			/* if both speed speficied, auto select. */			lp->option &= ~(TC35815_OPT_10M | TC35815_OPT_100M);		}	}	//XXX fixme        lp->option |= TC35815_OPT_10M;	/* do auto negotiation */	tc35815_phy_chip_init(dev);	dev->open		= tc35815_open;	dev->stop		= tc35815_close;	dev->tx_timeout         = tc35815_tx_timeout;	dev->watchdog_timeo     = TX_TIMEOUT;	dev->hard_start_xmit	= tc35815_send_packet;	dev->get_stats		= tc35815_get_stats;	dev->set_multicast_list = tc35815_set_multicast_list;	SET_MODULE_OWNER(dev);	SET_NETDEV_DEV(dev, &pdev->dev);	ret = register_netdev(dev);	if (ret)		goto err_out_iounmap;	printk(KERN_INFO "%s: %s found at %#x, irq %d, MAC",	       dev->name, cardname, base_addr, irq);	for (i = 0; i < 6; i++)		printk(" %2.2x", dev->dev_addr[i]);	printk("\n");	printk(KERN_INFO "%s: linkspeed %dMbps, %s Duplex\n",	       dev->name, lp->linkspeed, lp->fullduplex ? "Full" : "Half");	return 0;err_out_iounmap:	iounmap((void *) dev->base_addr);err_out:	free_netdev(dev);	return ret;}static inttc35815_init_queues(struct net_device *dev){	struct tc35815_local *lp = dev->priv;	int i;	unsigned long fd_addr;	if (!lp->fd_buf) {		if (sizeof(struct FDesc) +		    sizeof(struct BDesc) * RX_BUF_PAGES +		    sizeof(struct FDesc) * RX_FD_NUM +		    sizeof(struct TxFD) * TX_FD_NUM > PAGE_SIZE * FD_PAGE_NUM) {			printk(KERN_WARNING "%s: Invalid Queue Size.\n", dev->name);			return -ENOMEM;		}		if ((lp->fd_buf = (void *)__get_free_pages(GFP_KERNEL, FD_PAGE_ORDER)) == 0)			return -ENOMEM;		for (i = 0; i < RX_BUF_PAGES; i++) {			if ((lp->data_buf[i] = (void *)get_zeroed_page(GFP_KERNEL)) == 0) {				while (--i >= 0) {					free_page((unsigned long)lp->data_buf[i]);					lp->data_buf[i] = 0;				}				free_page((unsigned long)lp->fd_buf);				lp->fd_buf = 0;				return -ENOMEM;			}#ifdef __mips__			dma_cache_wback_inv((unsigned long)lp->data_buf[i], PAGE_SIZE * FD_PAGE_NUM);#endif		}#ifdef __mips__		dma_cache_wback_inv((unsigned long)lp->fd_buf, PAGE_SIZE * FD_PAGE_NUM);#endif	} else {		clear_page(lp->fd_buf);#ifdef __mips__		dma_cache_wback_inv((unsigned long)lp->fd_buf, PAGE_SIZE * FD_PAGE_NUM);#endif	}#ifdef __mips__	fd_addr = (unsigned long)vtonocache(lp->fd_buf);  #else	fd_addr = (unsigned long)lp->fd_buf;#endif	/* Free Descriptors (for Receive) */	lp->rfd_base = (struct RxFD *)fd_addr;	fd_addr += sizeof(struct RxFD) * RX_FD_NUM;	for (i = 0; i < RX_FD_NUM; i++) {		lp->rfd_base[i].fd.FDCtl = cpu_to_le32(FD_CownsFD);	}	lp->rfd_cur = lp->rfd_base;	lp->rfd_limit = (struct RxFD *)(fd_addr -					sizeof(struct FDesc) -					sizeof(struct BDesc) * 30);	/* Transmit Descriptors */	lp->tfd_base = (struct TxFD *)fd_addr;	fd_addr += sizeof(struct TxFD) * TX_FD_NUM;	for (i = 0; i < TX_FD_NUM; i++) {		lp->tfd_base[i].fd.FDNext = cpu_to_le32(virt_to_bus(&lp->tfd_base[i+1]));		lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0);		lp->tfd_base[i].fd.FDCtl = cpu_to_le32(0);	}	lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(virt_to_bus(&lp->tfd_base[0]));	lp->tfd_start = 0;	lp->tfd_end = 0;	/* Buffer List (for Receive) */	lp->fbl_ptr = (struct FrFD *)fd_addr;	lp->fbl_ptr->fd.FDNext = cpu_to_le32(virt_to_bus(lp->fbl_ptr));	lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_PAGES | FD_CownsFD);	for (i = 0; i < RX_BUF_PAGES; i++) {		lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(virt_to_bus(lp->data_buf[i]));		/* BDID is index of FrFD.bd[] */		lp->fbl_ptr->bd[i].BDCtl =			cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | PAGE_SIZE);	}	lp->fbl_curid = 0;	return 0;}static voidtc35815_clear_queues(struct net_device *dev){	struct tc35815_local *lp = dev->priv;	int i;	for (i = 0; i < TX_FD_NUM; i++) {		struct sk_buff *skb = (struct sk_buff *)			le32_to_cpu(lp->tfd_base[i].fd.FDSystem);		if (skb)			dev_kfree_skb_any(skb);		lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0);	}	tc35815_init_queues(dev);}static voidtc35815_free_queues(struct net_device *dev){	struct tc35815_local *lp = dev->priv;	int i;	if (lp->tfd_base) {		for (i = 0; i < TX_FD_NUM; i++) {			struct sk_buff *skb = (struct sk_buff *)				le32_to_cpu(lp->tfd_base[i].fd.FDSystem);			if (skb)				dev_kfree_skb_any(skb);			lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0);		}	}	lp->rfd_base = NULL;	lp->rfd_base = NULL;	lp->rfd_limit = NULL;	lp->rfd_cur = NULL;	lp->fbl_ptr = NULL;	for (i = 0; i < RX_BUF_PAGES; i++) {		if (lp->data_buf[i])			free_page((unsigned long)lp->data_buf[i]);		lp->data_buf[i] = 0;	}	if (lp->fd_buf)		__free_pages(lp->fd_buf, FD_PAGE_ORDER);	lp->fd_buf = NULL;}static voiddump_txfd(struct TxFD *fd){	printk("TxFD(%p): %08x %08x %08x %08x\n", fd,	       le32_to_cpu(fd->fd.FDNext),	       le32_to_cpu(fd->fd.FDSystem),	       le32_to_cpu(fd->fd.FDStat),	       le32_to_cpu(fd->fd.FDCtl));	printk("BD: ");	printk(" %08x %08x",	       le32_to_cpu(fd->bd.BuffData),	       le32_to_cpu(fd->bd.BDCtl));	printk("\n");}static intdump_rxfd(struct RxFD *fd){	int i, bd_count = (le32_to_cpu(fd->fd.FDCtl) & FD_BDCnt_MASK) >> FD_BDCnt_SHIFT;	if (bd_count > 8)		bd_count = 8;	printk("RxFD(%p): %08x %08x %08x %08x\n", fd,	       le32_to_cpu(fd->fd.FDNext),	       le32_to_cpu(fd->fd.FDSystem),	       le32_to_cpu(fd->fd.FDStat),	       le32_to_cpu(fd->fd.FDCtl));	if (le32_to_cpu(fd->fd.FDCtl) & FD_CownsFD)	    return 0;	printk("BD: ");	for (i = 0; i < bd_count; i++)		printk(" %08x %08x",		       le32_to_cpu(fd->bd[i].BuffData),		       le32_to_cpu(fd->bd[i].BDCtl));	printk("\n");	return bd_count;}static voiddump_frfd(struct FrFD *fd){	int i;	printk("FrFD(%p): %08x %08x %08x %08x\n", fd,	       le32_to_cpu(fd->fd.FDNext),	       le32_to_cpu(fd->fd.FDSystem),	       le32_to_cpu(fd->fd.FDStat),	       le32_to_cpu(fd->fd.FDCtl));	printk("BD: ");	for (i = 0; i < RX_BUF_PAGES; i++)		printk(" %08x %08x",		       le32_to_cpu(fd->bd[i].BuffData),		       le32_to_cpu(fd->bd[i].BDCtl));	printk("\n");}static voidpanic_queues(struct net_device *dev){	struct tc35815_local *lp = dev->priv;	int i;	printk("TxFD base %p, start %d, end %d\n",	       lp->tfd_base, lp->tfd_start, lp->tfd_end);	printk("RxFD base %p limit %p cur %p\n",	       lp->rfd_base, lp->rfd_limit, lp->rfd_cur);	printk("FrFD %p\n", lp->fbl_ptr);	for (i = 0; i < TX_FD_NUM; i++)		dump_txfd(&lp->tfd_base[i]);	for (i = 0; i < RX_FD_NUM; i++) {		int bd_count = dump_rxfd(&lp->rfd_base[i]);		i += (bd_count + 1) / 2;	/* skip BDs */	}	dump_frfd(lp->fbl_ptr);	panic("%s: Illegal queue state.", dev->name);}#if 0static void print_buf(char *add, int length){	int i;	int len = length;	printk("print_buf(%08x)(%x)\n", (unsigned int) add,length);	if (len > 100)		len = 100;	for (i = 0; i < len; i++) {		printk(" %2.2X", (unsigned char) add[i]);		if (!(i % 16))			printk("\n");	}	printk("\n");}#endifstatic void print_eth(char *add){	int i;	printk("print_eth(%08x)\n", (unsigned int) add);	for (i = 0; i < 6; i++)		printk(" %2.2X", (unsigned char) add[i + 6]);	printk(" =>");	for (i = 0; i < 6; i++)		printk(" %2.2X", (unsigned char) add[i]);	printk(" : %2.2X%2.2X\n", (unsigned char) add[12], (unsigned char) add[13]);}/* * Open/initialize the board. This is called (in the current kernel) * sometime after booting when the 'ifconfig' program is run. * * This routine should set everything up anew at each open, even * registers that "should" only need to be set once at boot, so that * there is non-reboot way to recover if something goes wrong. */static inttc35815_open(struct net_device *dev)

⌨️ 快捷键说明

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