lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 29 Oct 2007 22:51:42 +0100
From:	Florian Fainelli <florian.fainelli@...ecomint.eu>
To:	netdev@...r.kernel.org
Subject: [PATCH][RFC] Add support for the RDC R6040 Fast Ethernet controller

This patch adds support for the RDC R6040 MAC we can find in the RDC R-321x System-on-chips.
This driver really needs improvements especially on the NAPI part which probably does not
fully use the new NAPI structure.
You will need the RDC PCI identifiers if you want to test this driver which are the following ones :

RDC_PCI_VENDOR_ID = 0x17f3
RDC_PCI_DEVICE_ID_RDC_R6040 = 0x6040

Thank you very much in advance for your comments.

Signed-off-by: Sten Wang <sten.wang@....com.tw>
Signed-off-by: Daniel Gimpelevich <daniel@...pelevich.san-francisco.ca.us>
Signed-off-by: Florian Fainelli <florian.fainelli@...ecomint.eu>
---
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ce34b53..c8a5eef 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1643,6 +1643,24 @@ config 8139_OLD_RX_RESET
 	  experience problems, you can enable this option to restore the
 	  old RX-reset behavior.  If unsure, say N.
 
+config R6040
+	tristate "RDC R6040 Fast Ethernet Adapter support (EXPERIMENTAL)"
+	depends on NET_PCI && PCI && EXPERIMENTAL
+	select MII
+	help
+	  This is a driver for the R6040 Fast Ethernet MACs found in the
+	  the RDC R-321x System-on-chips.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called r6040. This is recommended.
+
+config R6040_NAPI
+	bool "NAPI support for R6040"
+	depends on R6040
+	default y
+	help
+	  Enable the NAPI polling for the R6040 driver.
+
 config SIS900
 	tristate "SiS 900/7016 PCI Fast Ethernet Adapter support"
 	depends on NET_PCI && PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 22f78cb..5fb95ca 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_VIA_RHINE) += via-rhine.o
 obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o
 obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
 obj-$(CONFIG_RIONET) += rionet.o
+obj-$(CONFIG_R6040) += r6040.o
 
 #
 # end link order section
diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c
new file mode 100644
index 0000000..ada1b7f
--- /dev/null
+++ b/drivers/net/r6040.c
@@ -0,0 +1,970 @@
+/*
+ * RDC R6040 Fast Ethernet MAC support
+ *
+ * Copyright (C) 2004 Sten Wang <sten.wang@....com.tw>
+ * Copyright (C) 2007 Daniel Gimpelevich <daniel@...pelevich.san-francisco.ca.us>
+ * 			Florian Fainelli <florian@...nwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * 	Changelog :
+ *	----------	------------------------------------------------
+ *	10-07-2007	Clean up the driver using checkpatch
+ *	08-24-2006	Support at linux 2.6.10 above
+ *	03-24-2006	Support NAPI
+ *	03-21-2006	change spin_lock_irqsave(lp->lock, flags)
+ *			to spin_lock_irqsave(&lp->lock, flags)
+ *			in set_multicast_list
+ *	03-15-2006      Modify the set_multicast_list ,due to when re-plug the ethernet,
+ *			it will forget the previous setting
+ *	07-12-2005	modify the set_multicast_list
+ *	03-28-2005	modify some error mac register offset in
+ *			function set_multicast_list
+ *	03-27-2005	Add the internal state machine reset
+ *			If multicast address more than 4, enter PROM mode
+ *			Changed rdc to r6040
+ *	12-22-2004	Sten Init MAC MBCR register=0x012A
+ *			PHY_CAP = 0x01E1
+ *
+ *	Need to Do LIst:
+ *	1. If multicast address more than 4, use the multicast address hash
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/crc32.h>
+#include <linux/spinlock.h>
+
+#include <asm/processor.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define DRV_NAME	"r6040"
+#define DRV_VERSION	"0.14"
+#define DRV_RELDATE	"29Oct2007"
+
+/* PHY CHIP Address */
+#define PHY1_ADDR	1	/* For MAC1 */
+#define PHY2_ADDR	2	/* For MAC2 */
+#define PHY_MODE	0x3100	/* PHY CHIP Register 0 */
+#define PHY_CAP		0x01E1	/* PHY CHIP Register 4 */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  	(6000 * HZ / 1000)
+#define TIMER_WUT	(jiffies + HZ * 1)/* timer wakeup time : 1 second */
+
+/* RDC MAC ID */
+#define RDC_MAC_ID	0x6040
+
+/* RDC MAC I/O Size */
+#define R6040_IO_SIZE	256
+
+/* RDC Chip PCI Command */
+#define R6040_PCI_CMD	0x0005	/* IO, Master */
+
+/* MAX RDC MAC */
+#define MAX_MAC		2
+
+/* MAC setting */
+#define TX_DCNT		0x80	/* TX descriptor count */
+#define RX_DCNT		0x80	/* RX descriptor count */
+#define MAX_BUF_SIZE	0x600
+#define ALLOC_DESC_SIZE	((TX_DCNT+RX_DCNT) * sizeof(struct r6040_descriptor) + 0x10)
+#define MBCR_DEFAULT	0x012A	/* MAC Bus Control Register */
+
+/* PHY settings */
+#define ICPLUS_PHY_ID	0x0243
+
+MODULE_AUTHOR("Sten Wang <sten.wang@....com.tw>, Daniel Gimpelevich <daniel@...pelevich.san-francisco.ca.us>, Florian Fainelli <florian@...nwrt.org>");
+MODULE_LICENSE("GPL");
+#ifdef CONFIG_R6040_NAPI
+MODULE_DESCRIPTION("RDC R6040 NAPI PCI FastEthernet Driver");
+#else
+MODULE_DESCRIPTION("RDC R6040 PCI FastEthernet Driver");
+#endif
+
+#define RX_INT                         0x0001
+#define TX_INT                         0x0010
+#define RX_NO_DESC_INT                 0x0002
+#define R6040_INT_MASK                 (RX_INT | TX_INT)
+
+struct r6040_descriptor {
+	u16	status, len;		/* 0-3 */
+	u32	buf;			/* 4-7 */
+	u32	ndesc;			/* 8-B */
+	u32	rev1;			/* C-F */
+	char	*vbufp;			/* 10-13 */
+	struct r6040_descriptor *vndescp;	/* 14-17 */
+	struct sk_buff *skb_ptr;	/* 18-1B */
+	u32	rev2;			/* 1C-1F */
+} __attribute__((aligned(32)));
+
+struct r6040_private {
+	spinlock_t lock;		/* driver lock */
+	struct timer_list timer;
+	struct pci_dev *pdev;
+
+	struct r6040_descriptor *rx_insert_ptr;
+	struct r6040_descriptor *rx_remove_ptr;
+	struct r6040_descriptor *tx_insert_ptr;
+	struct r6040_descriptor *tx_remove_ptr;
+	u16	tx_free_desc, rx_free_desc, phy_addr, phy_mode;
+	u16	mcr0, mcr1;
+	dma_addr_t desc_dma;
+	char	*desc_pool;
+	u16	switch_sig;
+	struct net_device *dev;
+	struct mii_if_info mii_if;
+	struct napi_struct napi;
+};
+
+struct r6040_chip_info {
+	const char *name;
+	u16 pci_flags;
+	int io_size;
+	int drv_flags;
+};
+
+#ifdef CONFIG_R6040_NAPI
+static int NAPI_status;
+#endif
+
+#ifdef CONFIG_R6040_NAPI
+static char version[] __devinitdata =
+	KERN_INFO DRV_NAME ": RDC R6040 NAPI net driver, version "DRV_VERSION " (" DRV_RELDATE ")\n";
+#else
+static char version[] __devinitdata =
+	KERN_INFO DRV_NAME ": RDC R6040 net driver, version "DRV_VERSION " (" DRV_RELDATE ")\n";
+#endif
+static struct r6040_chip_info r6040_chip_info[] __devinitdata =
+{
+	{ "RDC R6040 Knight", R6040_PCI_CMD, R6040_IO_SIZE, 0}
+};
+static char *parent;
+
+static int NUM_MAC_TABLE = 2 ;
+module_param(parent, charp, 0444);
+MODULE_PARM_DESC(parent, "Parent network device name");
+
+static int phy_table[] = { 0x1, 0x2};
+
+static u8 adr_table[2][8] = {
+	{0x00, 0x00, 0x60, 0x00, 0x00, 0x01},
+	{0x00, 0x00, 0x60, 0x00, 0x00, 0x02}
+};
+
+module_param_array(adr_table, int, &NUM_MAC_TABLE, 0644);
+MODULE_PARM_DESC(adr_table, "MAC Address (assigned)");
+
+static int  mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static int r6040_open(struct net_device *dev);
+static int r6040_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t r6040_interrupt(int irq, void *dev_id);
+static int r6040_close(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static struct ethtool_ops netdev_ethtool_ops;
+static int r6040_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void r6040_down(struct net_device *dev);
+static void r6040_up(struct net_device *dev);
+static void r6040_tx_timeout(struct net_device *dev);
+static void r6040_timer(unsigned long);
+static void r6040_mac_address(struct net_device *dev);
+
+static int phy_mode_chk(struct net_device *dev);
+static int phy_read(int ioaddr, int phy_adr, int reg_idx);
+static void phy_write(int ioaddr, int phy_adr, int reg_idx, int dat);
+static void rx_buf_alloc(struct r6040_private *lp, struct net_device *dev);
+#ifdef CONFIG_R6040_NAPI
+static int r6040_poll(struct napi_struct *napi, int budget);
+#endif
+
+static int mdio_read(struct net_device *dev, int phy_id, int regnum)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+	long ioaddr = dev->base_addr;
+	return  (phy_read(ioaddr, lp->phy_addr, regnum)) ;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int regnum, int value)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+	long ioaddr = dev->base_addr;
+
+	phy_write(ioaddr, lp->phy_addr, regnum, value);
+}
+
+static int __devinit r6040_init_one(struct pci_dev *pdev,
+					 const struct pci_device_id *ent)
+{
+	struct net_device *dev;
+	struct r6040_private *lp;
+	int ioaddr, io_size, err;
+	static int card_idx = -1;
+	int chip_id = (int)ent->driver_data;
+
+	printk(KERN_INFO "%s\n", version);
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	/* this should always be supported */
+	if (pci_set_dma_mask(pdev, 0xffffffff)) {
+		printk(KERN_ERR DRV_NAME "32-bit PCI DMA addresses not supported by the card\n");
+		return  -ENODEV;
+	}
+
+	/* IO Size check */
+	io_size = r6040_chip_info[chip_id].io_size;
+	if (pci_resource_len(pdev, 0) < io_size)
+		return  -ENODEV;
+
+	ioaddr = pci_resource_start(pdev, 0);	/* IO map base address */
+	pci_set_master(pdev);
+
+	dev = alloc_etherdev(sizeof(struct r6040_private));
+	if (!dev)
+		return -ENOMEM;
+
+	if (pci_request_regions(pdev, DRV_NAME)) {
+		printk(KERN_ERR DRV_NAME ": Failed to request PCI regions\n");
+		err = -ENODEV;
+		goto err_out_disable;
+	}
+
+	/* Init system & device */
+	lp = dev->priv;
+	dev->base_addr = ioaddr;
+	dev->irq = pdev->irq;
+
+	spin_lock_init(&lp->lock);
+	pci_set_drvdata(pdev, dev);
+
+	/* Set MAC address */
+	card_idx++;
+
+	/* Check if the MAC address is 0xFFFFFFFFFFFF or 0 */
+	if (((inw(ioaddr + 0x68) == 0xFFFF) && (inw(ioaddr + 0x6A) == 0xFFFF) && (inw(ioaddr + 0x6C) == 0xFFFF)) ||
+		((inw(ioaddr + 0x68) == 0) && (inw(ioaddr + 0x6A) == 0) && (inw(ioaddr + 0x6C) == 0)))
+		memcpy(dev->dev_addr, (u8 *)&adr_table[card_idx][0], 6);
+	else {
+		u16 *adrp;
+		adrp = (u16 *) dev->dev_addr;
+		adrp[0] = inw(ioaddr + 0x68);
+		adrp[1] = inw(ioaddr + 0x6A);
+		adrp[2] = inw(ioaddr + 0x6C);
+	}
+
+	/* Link new device into r6040_root_dev */
+	lp->pdev = pdev;
+
+	/* Init RDC private data */
+	lp->mcr0 = 0x1002;
+	lp->phy_addr = phy_table[card_idx];
+	lp->switch_sig = 0;
+
+	/* The RDC-specific entries in the device structure. */
+	dev->open = &r6040_open;
+	dev->hard_start_xmit = &r6040_start_xmit;
+	dev->stop = &r6040_close;
+	dev->set_multicast_list = &set_multicast_list;
+	dev->do_ioctl = &r6040_ioctl;
+	dev->ethtool_ops = &netdev_ethtool_ops;
+	dev->tx_timeout = &r6040_tx_timeout;
+	dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_R6040_NAPI
+	netif_napi_add(dev, &lp->napi, r6040_poll, 64);
+#endif
+	lp->mii_if.dev = dev;
+	lp->mii_if.mdio_read = mdio_read;
+	lp->mii_if.mdio_write = mdio_write;
+	lp->mii_if.phy_id = lp->phy_addr;
+	lp->mii_if.phy_id_mask = 0x1f;
+	lp->mii_if.reg_num_mask = 0x1f;
+
+	/* Register net device. After this dev->name assign */
+	err = register_netdev(dev);
+	if (err) {
+		printk(KERN_ERR DRV_NAME ": Failed to register net device\n");
+		goto err_out_res;
+	}
+
+	netif_carrier_on(dev);
+	return 0;
+
+err_out_res:
+	pci_release_regions(pdev);
+err_out_disable:
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	kfree(dev);
+
+	return err;
+}
+
+static void __devexit r6040_remove_one(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	unregister_netdev(dev);
+	pci_release_regions(pdev);
+	kfree(dev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+}
+
+static int
+r6040_open(struct net_device *dev)
+{
+	struct r6040_private *lp = dev->priv;
+	int i;
+
+	/* Request IRQ and Register interrupt handler */
+	i = request_irq(dev->irq, &r6040_interrupt, IRQF_SHARED, dev->name, dev);
+	if (i)
+		return i;
+	/* Set MAC address */
+	r6040_mac_address(dev);
+	/* Allocate Descriptor memory */
+	lp->desc_pool = pci_alloc_consistent(lp->pdev, ALLOC_DESC_SIZE, &lp->desc_dma);
+	if (!lp->desc_pool)
+		return -ENOMEM;
+
+	r6040_up(dev);
+
+	napi_enable(&lp->napi);
+	netif_start_queue(dev);
+
+	if (lp->switch_sig != ICPLUS_PHY_ID) {
+		/* set and active a timer process */
+		init_timer(&lp->timer);
+		lp->timer.expires = TIMER_WUT;
+		lp->timer.data = (unsigned long)dev;
+		lp->timer.function = &r6040_timer;
+		add_timer(&lp->timer);
+	}
+	return 0;
+}
+
+static void
+r6040_tx_timeout(struct net_device *dev)
+{
+	struct r6040_private *priv = netdev_priv(dev);
+	spin_lock(&priv->lock);
+	dev->stats.tx_errors++;
+	spin_unlock(&priv->lock);
+
+	netif_stop_queue(dev);
+}
+
+
+static int
+r6040_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+	struct r6040_descriptor *descptr;
+	int ioaddr = dev->base_addr;
+	unsigned long flags;
+
+	if (!skb)	/* NULL skb directly return */
+		return 0;
+	if (skb->len >= MAX_BUF_SIZE) {	/* Packet too long, drop it */
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	/* Critical Section */
+	spin_lock_irqsave(&lp->lock, flags);
+
+	/* TX resource check */
+	if (!lp->tx_free_desc) {
+		spin_unlock_irqrestore(&lp->lock, flags);
+		printk(KERN_ERR DRV_NAME ": NO TX DESC ");
+		return 1;
+	}
+
+	/* Statistic Counter */
+	dev->stats.tx_packets++;
+	dev->stats.tx_bytes += skb->len;
+	/* Set TX descriptor & Transmit it */
+	lp->tx_free_desc--;
+	descptr = lp->tx_insert_ptr;
+	if (skb->len < 0x3c)
+		descptr->len = 0x3c;
+	else
+		descptr->len = skb->len;
+
+	descptr->skb_ptr = skb;
+	descptr->buf = cpu_to_le32(pci_map_single(lp->pdev, skb->data, skb->len, PCI_DMA_TODEVICE));
+	descptr->status = 0x8000;
+	outw(0x01, ioaddr + 0x14);
+	lp->tx_insert_ptr = descptr->vndescp;
+
+	/* If no tx resource, stop */
+	if (!lp->tx_free_desc)
+		netif_stop_queue(dev);
+
+	dev->trans_start = jiffies;
+	spin_unlock_irqrestore(&lp->lock, flags);
+	return 0;
+}
+
+/* The RDC interrupt handler. */
+static irqreturn_t
+r6040_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct r6040_private *lp = netdev_priv(dev);
+	int ioaddr, status;
+	unsigned long flags;
+#ifdef CONFIG_R6040_NAPI
+	int handled = 1;
+#else
+	int handled = 0;
+#endif
+
+	if (!dev) {
+		printk(KERN_ERR DRV_NAME ": INT() unknown device.\n");
+		return IRQ_RETVAL(handled);
+	}
+
+	spin_lock_irqsave(&lp->lock, flags);
+
+	/* Check MAC Interrupt status */
+	ioaddr = dev->base_addr;
+	outw(0x0, ioaddr + 0x40);	/* Mask Off RDC MAC interrupt */
+	status = inw(ioaddr + 0x3c);	/* Read INTR status and clear */
+
+#ifdef CONFIG_R6040_NAPI
+	if (netif_rx_schedule_prep(dev, &lp->napi)) {
+		NAPI_status = status;
+		__netif_rx_schedule(dev, &lp->napi);
+	}
+
+	spin_unlock_irqrestore(&lp->lock, flags);
+	return IRQ_RETVAL(handled);
+#else
+	/* TX interrupt request */
+	if (status & 0x10) {
+		handled = 1;
+		descptr = lp->tx_remove_ptr;
+		while (lp->tx_free_desc < TX_DCNT) {
+			if (descptr->status & 0x8000)
+				break; /* Not complte */
+			skb_ptr = descptr->skb_ptr;
+			pci_unmap_single(lp->pdev, descptr->buf, skb_ptr->len, PCI_DMA_TODEVICE);
+			dev_kfree_skb_irq(skb_ptr); /* Free buffer */
+			descptr->skb_ptr = 0;
+			descptr = descptr->vndescp; /* To next descriptor */
+			lp->tx_free_desc++;
+		}
+		lp->tx_remove_ptr = descptr;
+		if (lp->tx_free_desc)
+			netif_wake_queue(dev);
+	}
+
+	/* RX interrupt request */
+	if (status & 0x01) {
+		handled = 1;
+		descptr = lp->rx_remove_ptr;
+		while (lp->rx_free_desc) {
+			if (descptr->status & 0x8000)
+				break; /* No Rx packet */
+			skb_ptr = descptr->skb_ptr;
+			descptr->skb_ptr = 0;
+			skb_ptr->dev = dev;
+			skb_put(skb_ptr, descptr->len - 4);
+			pci_unmap_single(lp->pdev, descptr->buf, MAX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+			skb_ptr->protocol = eth_type_trans(skb_ptr, dev);
+			netif_rx(skb_ptr);  /* Send to upper layer */
+			lp->dev->stats.rx_packets++;
+			lp->dev->stats.rx_bytes += descptr->len;
+			descptr = descptr->vndescp; /* To next descriptor */
+			lp->rx_free_desc--;
+		}
+		lp->rx_remove_ptr = descptr;
+	}
+
+	/* Allocate new RX buffer */
+	if (lp->rx_free_desc < RX_DCNT)
+		rx_buf_alloc(lp, dev);
+
+	outw(R6040_INT_MASK, ioaddr + 0x40);	/* TX/RX interrupt enable */
+	spin_unlock_irqrestore(&lp->lock, flags);
+#endif
+	return IRQ_RETVAL(handled);
+}
+
+
+/*
+ *     Set or clear the multicast filter for this adaptor.
+ */
+static void
+set_multicast_list(struct net_device *dev)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+	struct dev_mc_list *mcptr;
+	int ioaddr = dev->base_addr;
+	u16 *adrp, i;
+	unsigned long flags;
+
+	/* MAC Address */
+	adrp = (u16 *) dev->dev_addr;
+	outw(adrp[0], ioaddr + 0x68);
+	outw(adrp[1], ioaddr + 0x6A);
+	outw(adrp[2], ioaddr + 0x6C);
+
+
+	/* Promiscous Mode */
+	spin_lock_irqsave(&lp->lock, flags);
+	i = inw(ioaddr) & ~0x0120;		/* Clear AMCP & PROM */
+	if (dev->flags & IFF_PROMISC) {
+		i |= 0x0020;
+		lp->mcr0 |= 0x0020;
+	}
+	if (dev->mc_count > 4)
+		i |= 0x0020;	/* Too many multicast address */
+	outw(i, ioaddr);
+	spin_unlock_irqrestore(&lp->lock, flags);
+	/* Multicast Address */
+	if (dev->mc_count > 4)	/* Wait to do: Hash Table for multicast */
+		return;
+	/* Multicast Address 1~4 case */
+	for (i = 0, mcptr = dev->mc_list; (i < dev->mc_count) && (i < 4); i++) {
+		adrp = (u16 *)mcptr->dmi_addr;
+		outw(adrp[0], ioaddr + 0x70 + 8*i);
+		outw(adrp[1], ioaddr + 0x72 + 8*i);
+		outw(adrp[2], ioaddr + 0x74 + 8*i);
+		mcptr = mcptr->next;
+	}
+	for (i = dev->mc_count; i < 4; i++) {
+		outw(0xffff, ioaddr + 0x68 + 8*i);
+		outw(0xffff, ioaddr + 0x6A + 8*i);
+		outw(0xffff, ioaddr + 0x6C + 8*i);
+	}
+}
+
+static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	struct r6040_private *rp = netdev_priv(dev);
+
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	strcpy(info->bus_info, pci_name(rp->pdev));
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+};
+
+static int
+r6040_close(struct net_device *dev)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+
+	/* deleted timer */
+	del_timer_sync(&lp->timer);
+
+	spin_lock_irq(&lp->lock);
+
+	netif_stop_queue(dev);
+	napi_disable(&lp->napi);
+
+	r6040_down(dev);
+
+	spin_unlock_irq(&lp->lock);
+
+	return 0;
+}
+
+static int r6040_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+	struct mii_ioctl_data *data = (struct mii_ioctl_data *) &rq->ifr_data;
+	int rc;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+	spin_lock_irq(&lp->lock);
+	rc = generic_mii_ioctl(&lp->mii_if, data, cmd, NULL);
+	spin_unlock_irq(&lp->lock);
+	return rc;
+}
+
+/* Stop RDC MAC and Free the allocated resource */
+static void r6040_down(struct net_device *dev)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+	int i;
+	int ioaddr = dev->base_addr;
+	u16 *adrp;
+
+	/* Stop MAC */
+	outw(0x0000, ioaddr + 0x40);	/* Mask Off Interrupt */
+	outw(0x0001, ioaddr + 0x04);	/* Reset RDC MAC */
+	i = 0;
+	do {} while ((i++ < 2048) && (inw(ioaddr + 0x04) & 0x1));
+
+	/* Restore MAC Address to MIDx */
+	adrp = (u16 *) dev->dev_addr;
+	outw(adrp[0], ioaddr + 0x68);
+	outw(adrp[1], ioaddr + 0x6A);
+	outw(adrp[2], ioaddr + 0x6C);
+	free_irq(dev->irq, dev);
+	/* Free RX buffer */
+	for (i = 0; i < RX_DCNT; i++) {
+		if (lp->rx_insert_ptr->skb_ptr) {
+			pci_unmap_single(lp->pdev, lp->rx_insert_ptr->buf, MAX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(lp->rx_insert_ptr->skb_ptr);
+			lp->rx_insert_ptr->skb_ptr = 0;
+		}
+		lp->rx_insert_ptr = lp->rx_insert_ptr->vndescp;
+	}
+
+	/* Free TX buffer */
+	for (i = 0; i < TX_DCNT; i++) {
+		if (lp->tx_insert_ptr->skb_ptr) {
+			pci_unmap_single(lp->pdev, lp->tx_insert_ptr->buf, MAX_BUF_SIZE, PCI_DMA_TODEVICE);
+			dev_kfree_skb(lp->tx_insert_ptr->skb_ptr);
+			lp->rx_insert_ptr->skb_ptr = 0;
+		}
+		lp->tx_insert_ptr = lp->tx_insert_ptr->vndescp;
+	}
+
+	/* Free Descriptor memory */
+	pci_free_consistent(lp->pdev, ALLOC_DESC_SIZE, lp->desc_pool, lp->desc_dma);
+}
+
+
+
+#ifdef CONFIG_R6040_NAPI
+static int r6040_poll(struct napi_struct *napi, int budget)
+{
+	struct r6040_private *priv = container_of(napi, struct r6040_private, napi);
+	struct r6040_descriptor *descptr;
+	struct sk_buff *skb_ptr;
+	int ioaddr = priv->dev->base_addr;
+	/* TX interrupt request */
+	if (NAPI_status & 0x10) {
+		descptr = priv->tx_remove_ptr;
+		while (priv->tx_free_desc < TX_DCNT) {
+			if (descptr->status & 0x8000)
+				break; /* Not complte */
+			skb_ptr = descptr->skb_ptr;
+			pci_unmap_single(priv->pdev, descptr->buf, skb_ptr->len, PCI_DMA_TODEVICE);
+			dev_kfree_skb_irq(skb_ptr); /* Free buffer */
+			descptr->skb_ptr = 0;
+			descptr = descptr->vndescp; /* To next descriptor */
+			priv->tx_free_desc++;
+		}
+		priv->tx_remove_ptr = descptr;
+		if (priv->tx_free_desc)
+			netif_wake_queue(priv->dev);
+	}
+	/* RX interrupt request */
+	if (NAPI_status & 0x01) {
+		descptr = priv->rx_remove_ptr;
+		while (priv->rx_free_desc) {
+			if (descptr->status & 0x8000)
+				break; /* No Rx packet */
+			skb_ptr = descptr->skb_ptr;
+			descptr->skb_ptr = 0;
+			skb_ptr->dev = priv->dev;
+			skb_put(skb_ptr, descptr->len - 4);
+			pci_unmap_single(priv->pdev, descptr->buf, MAX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+			skb_ptr->protocol = eth_type_trans(skb_ptr, priv->dev);
+			netif_receive_skb(skb_ptr); /* Send to upper layer */
+			priv->dev->stats.rx_packets++;
+			priv->dev->stats.rx_bytes += descptr->len;
+			descptr = descptr->vndescp; /* To next descriptor */
+			priv->rx_free_desc--;
+		}
+		priv->rx_remove_ptr = descptr;
+	}
+	/* Allocate new RX buffer */
+	if (priv->rx_free_desc < RX_DCNT)
+		rx_buf_alloc(priv, priv->dev);
+	local_irq_disable();
+	netif_rx_complete(priv->dev, napi);
+	outw(R6040_INT_MASK, ioaddr + 0x40);
+	local_irq_enable();
+	return 0;
+}
+#endif
+
+/* Init RDC MAC */
+static void r6040_up(struct net_device *dev)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+	struct r6040_descriptor *descptr;
+	int i;
+	int ioaddr = dev->base_addr;
+	u32 tmp_addr;
+	dma_addr_t desc_dma, start_dma;
+	/* Initialize */
+	lp->tx_free_desc = TX_DCNT;
+	lp->rx_free_desc = 0;
+	/* Init descriptor */
+	memset(lp->desc_pool, 0, ALLOC_DESC_SIZE); /* Let all descriptor = 0 */
+	lp->tx_insert_ptr = (struct r6040_descriptor *)lp->desc_pool;
+	lp->tx_remove_ptr = lp->tx_insert_ptr;
+	lp->rx_insert_ptr = (struct r6040_descriptor *)lp->tx_insert_ptr+TX_DCNT;
+	lp->rx_remove_ptr = lp->rx_insert_ptr;
+	/* Init TX descriptor */
+	descptr = lp->tx_insert_ptr;
+	desc_dma = lp->desc_dma;
+	start_dma = desc_dma;
+	for (i = 0; i < TX_DCNT; i++) {
+		descptr->ndesc = cpu_to_le32(desc_dma + sizeof(struct r6040_descriptor));
+		descptr->vndescp = (descptr + 1);
+		descptr = (descptr + 1);
+		desc_dma += sizeof(struct r6040_descriptor);
+	}
+	(descptr - 1)->ndesc = cpu_to_le32(start_dma);
+	(descptr - 1)->vndescp = lp->tx_insert_ptr;
+
+	/* Init RX descriptor */
+	start_dma = desc_dma;
+	descptr = lp->rx_insert_ptr;
+	for (i = 0; i < RX_DCNT; i++) {
+		descptr->ndesc = cpu_to_le32(desc_dma + sizeof(struct r6040_descriptor));
+		descptr->vndescp = (descptr + 1);
+		descptr = (descptr + 1);
+		desc_dma += sizeof(struct r6040_descriptor);
+	}
+	(descptr - 1)->ndesc = cpu_to_le32(start_dma);
+	(descptr - 1)->vndescp = lp->rx_insert_ptr;
+
+	/* Allocate buffer for RX descriptor */
+	rx_buf_alloc(lp, dev);
+
+	/* TX and RX descriptor start Register */
+	tmp_addr = cpu_to_le32((u32)lp->tx_insert_ptr);
+	tmp_addr = virt_to_bus((volatile void *)tmp_addr);
+	outw((u16) tmp_addr, ioaddr+0x2c);
+	outw(tmp_addr >> 16, ioaddr+0x30);
+	tmp_addr = cpu_to_le32((u32)lp->rx_insert_ptr);
+	tmp_addr = virt_to_bus((volatile void *)tmp_addr);
+	outw((u16) tmp_addr, ioaddr+0x34);
+	outw(tmp_addr >> 16, ioaddr+0x38);
+
+	/* Buffer Size Register */
+	outw(MAX_BUF_SIZE, ioaddr+0x18);
+	lp->switch_sig = phy_read(ioaddr, 0, 2);
+
+	if (lp->switch_sig  == ICPLUS_PHY_ID) {
+		phy_write(ioaddr, 29, 31, 0x175C); /* Enable registers */
+		lp->phy_mode = 0x8000;
+	} else {
+		/* PHY Mode Check */
+		phy_write(ioaddr, lp->phy_addr, 4, PHY_CAP);
+		phy_write(ioaddr, lp->phy_addr, 0, PHY_MODE);
+
+		if (PHY_MODE == 0x3100)
+			lp->phy_mode = phy_mode_chk(dev);
+		else
+			lp->phy_mode = (PHY_MODE & 0x0100) ? 0x8000:0x0;
+	}
+	/* MAC Bus Control Register */
+	outw(MBCR_DEFAULT, ioaddr+0x8);
+
+	/* MAC TX/RX Enable */
+	lp->mcr0 |= lp->phy_mode;
+	outw(lp->mcr0, ioaddr);
+
+	/* set interrupt waiting time and packet numbers */
+	outw(0x0802, ioaddr + 0x0C);
+	outw(0x0802, ioaddr + 0x10);
+
+	/* upgrade performance (by RDC guys) */
+	phy_write(ioaddr, 30, 17, (phy_read(ioaddr, 30, 17) | 0x4000));        /* bit 14=1 */
+	phy_write(ioaddr, 30, 17, ~((~phy_read(ioaddr, 30, 17)) | 0x2000));    /* bit 13=0 */
+	phy_write(ioaddr, 0, 19, 0x0000);
+	phy_write(ioaddr, 0, 30, 0x01F0);
+
+	/* Interrupt Mask Register */
+	outw(R6040_INT_MASK, ioaddr + 0x40);
+}
+
+/*
+  A periodic timer routine
+	Polling PHY Chip Link Status
+*/
+static void r6040_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct r6040_private *lp = netdev_priv(dev->priv);
+	u16 ioaddr = dev->base_addr, phy_mode;
+
+	/* Polling PHY Chip Status */
+	if (PHY_MODE == 0x3100)
+		phy_mode = phy_mode_chk(dev);
+	else
+		phy_mode = (PHY_MODE & 0x0100) ? 0x8000:0x0;
+
+	if (phy_mode != lp->phy_mode) {
+		lp->phy_mode = phy_mode;
+		lp->mcr0 = (lp->mcr0 & 0x7fff) | phy_mode;
+		outw(lp->mcr0, ioaddr);
+		printk(KERN_INFO "Link Change %x \n", inw(ioaddr));
+	}
+
+	/* Timer active again */
+	lp->timer.expires = TIMER_WUT;
+	add_timer(&lp->timer);
+}
+
+/* Allocate skb buffer for rx descriptor */
+static void rx_buf_alloc(struct r6040_private *lp, struct net_device *dev)
+{
+	struct r6040_descriptor *descptr;
+	int ioaddr = dev->base_addr ;
+
+	descptr = lp->rx_insert_ptr;
+	while (lp->rx_free_desc < RX_DCNT) {
+		descptr->skb_ptr = dev_alloc_skb(MAX_BUF_SIZE);
+
+		if (!descptr->skb_ptr)
+			break;
+		descptr->buf = cpu_to_le32(pci_map_single(lp->pdev, descptr->skb_ptr->tail, MAX_BUF_SIZE, PCI_DMA_FROMDEVICE));
+		descptr->status = 0x8000;
+		descptr = descptr->vndescp;
+		lp->rx_free_desc++;
+		outw(lp->mcr0 | 0x0002, ioaddr); /* Trigger Rx DMA */
+	}
+	lp->rx_insert_ptr = descptr;
+}
+
+/* Status of PHY CHIP */
+static int phy_mode_chk(struct net_device *dev)
+{
+	struct r6040_private *lp = netdev_priv(dev);
+	int ioaddr = dev->base_addr, phy_dat;
+
+	/* PHY Link Status Check */
+	phy_dat = phy_read(ioaddr, lp->phy_addr, 1);
+	if (!(phy_dat & 0x4))
+		return 0x8000;	/* Link Failed, full duplex */
+
+	/* PHY Chip Auto-Negotiation Status */
+	phy_dat = phy_read(ioaddr, lp->phy_addr, 1);
+	if (phy_dat & 0x0020) {
+		/* Auto Negotiation Mode */
+		phy_dat = phy_read(ioaddr, lp->phy_addr, 5);
+		phy_dat &= phy_read(ioaddr, lp->phy_addr, 4);
+		if (phy_dat & 0x140)
+			phy_dat = 0x8000;
+		else
+			phy_dat = 0;
+	} else {
+		/* Force Mode */
+		phy_dat = phy_read(ioaddr, lp->phy_addr, 0);
+		if (phy_dat & 0x100)
+			phy_dat = 0x8000;
+		else
+			phy_dat = 0x0000;
+	}
+
+	return phy_dat;
+};
+
+/* Read a word data from PHY Chip */
+static int phy_read(int ioaddr, int phy_addr, int reg_idx)
+{
+	int i = 0;
+
+	outw(0x2000 + reg_idx + (phy_addr << 8), ioaddr + 0x20);
+	do {} while ((i++ < 2048) && (inw(ioaddr + 0x20) & 0x2000));
+
+	return inw(ioaddr + 0x24);
+}
+
+/* Write a word data from PHY Chip */
+static void phy_write(int ioaddr, int phy_addr, int reg_idx, int dat)
+{
+	int i = 0;
+
+	outw(dat, ioaddr + 0x28);
+	outw(0x4000 + reg_idx + (phy_addr << 8), ioaddr + 0x20);
+	do {} while ((i++ < 2048) && (inw(ioaddr + 0x20) & 0x4000));
+}
+
+/* Read/set MAC address routines */
+static void r6040_mac_address(struct net_device *dev)
+{
+       int ioaddr = dev->base_addr ;
+       u16 *adrp ;
+
+       /* MAC operation register */
+       outw(0x01, ioaddr + 0x04); /* Reset MAC */
+       outw(2, ioaddr + 0xAC); /* Reset internal state machine */
+       outw(0, ioaddr + 0xAC);
+       udelay(5000);
+
+       /* Restore MAC Address */
+       adrp = (u16 *) dev->dev_addr;
+       outw(adrp[0], ioaddr + 0x68);
+       outw(adrp[1], ioaddr + 0x6A);
+       outw(adrp[2], ioaddr + 0x6C);
+}
+
+static struct pci_device_id r6040_pci_tbl[] = {
+	{PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6040, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x1106, 0x3065, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+MODULE_DEVICE_TABLE(pci, r6040_pci_tbl);
+
+static struct pci_driver r6040_driver = {
+	.name		= "r6040",
+	.id_table	= r6040_pci_tbl,
+	.probe		= r6040_init_one,
+	.remove		= __devexit_p(r6040_remove_one),
+};
+
+
+static int __init r6040_init (void)
+{
+	return pci_register_driver (&r6040_driver);
+}
+
+
+static void __exit r6040_cleanup (void)
+{
+	pci_unregister_driver (&r6040_driver);
+}
+
+module_init(r6040_init);
+module_exit(r6040_cleanup);
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ