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-prev] [thread-next>] [day] [month] [year] [list]
Date:	Tue, 05 Dec 2006 13:36:39 -0800
From:	Dan Nicolaescu <dann@....uci.edu>
To:	Stephen Hemminger <shemminger@...l.org>
Cc:	netdev@...r.kernel.org
Subject: Re: [RFC patch] driver for the Opencores Ethernet Controller

Stephen Hemminger <shemminger@...l.org> writes:

  > > 
  > >   > Indentation. See Documentation style.
  > >   > What about IRQF_SHARED?
  > > 
  > > Not sure, maybe I should make this another driver parameter. On my
  > > platform is not shared...
  > 
  > The trouble with devices, is that some poor sop clones the hardware to
  > another board and your assumption is no longer valid.

Tt's a parameter now.

  > >
  > >   > > +static int oeth_start_xmit(struct sk_buff *skb, struct net_device *dev)
  > >   > > +{
  > >   > > +	struct oeth_private *cep = netdev_priv(dev);
  > >   > > +	volatile struct oeth_bd *bdp;
  > >   > > +	unsigned long flags;
  > >   > > +	u32 len_status;
  > >   > > +
  > >   > > +	spin_lock_irqsave(&cep->lock, flags);
  > >   > > +
  > >   > > +	if (cep->tx_full) {
  > >   > > +		/* All transmit buffers are full.  Bail out. */
  > >   > > +		printk("%s: tx queue full!.\n", dev->name);
  > >   > > +		print_queue(cep->tx_bd_base, cep->tx_next, OETH_TXBD_NUM);
  > >   > > +		spin_unlock_irqrestore(&cep->lock, flags);
  > >   > > +		return 1;
  > >   > return NETDEV_TX_BUSY.
  > >   >
  > >   > you forgot to call stop_queue
  > > 
  > > Fixed.
  > > 
  > > What should I return in the case below: 
  > > 
  > > if (skb->len > OETH_TX_BUFF_SIZE) {
  > >         printk("%s: tx frame too long!.\n", dev->name);
  > >         spin_unlock_irqrestore(&cep->lock, flags);
  > >         return 1;
  > > }
  > 
  > Drop the skb with dev_kfree_skb_irq() and return NETDEV_TX_OK.
  > You should net_ratelimit() the message also if you don't want the
  > machine to hang if you ever get a buggy application.
 
Done.

  > >   > > +
  > >   > > +	/* Copy data to TX buffer. */
  > >   > > +	memcpy_hton ((unsigned char *)bdp->addr, skb->data,  skb->len);
  > >   > 
  > >   > Use skb_copy_and_csum_dev and you get checksum offload for free.
  > > 
  > > Wouldn't that just add (a bit of) overhead? It performs the memcpy, but it also
  > > checks if the HW is capable of doing the checksum (which it is)... 
  > > Incidentally the memcpy_hton is just memcpy now.
  > 
  > The cost of copy and checksum is the same as copy on all most hardware.

Unfortunately on my platform is not.

  > And does your hardware do IPV6 etc?

No. 

  > >   > > +			cep->stats.rx_dropped++;
  > >   > > +		}
  > >   > > +		else {
  > >   > > +			skb->dev = dev;
  > >   > > +			OEDRX((printk("RX in ETH buf\n")));
  > >   > > +			OEDRX((oeth_print_packet((u32*)bdp->addr, pkt_len)));
  > >   > > +
  > >   > > +			memcpy_ntoh(skb_put(skb, pkt_len), (unsigned char *)bdp->addr, pkt_len);
  > >   > 
  > >   > 
  > >   > Copying packet in IRQ routine causes long latencies.
  > > 
  > > Any suggestions on how else to do this?
  > 
  > You can use NAPI (see 8139too.c) it has similar "issues"

I implemented NAPI. New version attached. 

Is there anything else that I should do?

  > >   > > +#if CONFIG_MII
  > >   > > +static int check_if_running(struct net_device *dev)
  > >   > > +{
  > >   > > +	if (!netif_running(dev))
  > >   > > +		return -EINVAL;
  > >   > > +	return 0;
  > >   > > +}
  > >   > 
  > >   > Bogus wrapper.
  > > 
  > > OK. BTW this is present in 3 more files: hamachi.c, starfire.c and
  > > sundance.c 
  > 
  > Send the Bunk after it.

Sorry, I don't know what that means...

 Kconfig    |    5 
 open_eth.c |  753 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 open_eth.h |  132 ++++++++++
 3 files changed, 890 insertions(+)

--- /dev/null	2006-09-20 11:38:04.545479250 -0700
+++ drivers/net/open_eth.c	2006-12-05 11:50:57.977895000 -0800
@@ -0,0 +1,753 @@
+/*
+ * Ethernet driver for Open Ethernet Controller (www.opencores.org).
+ *      Copyright (c) 2002 Simon Srot (simons@...ncores.org)
+ *      Copyright (c) 2006 Tensilica Inc.
+ * 
+ * Based on:
+ *
+ * Ethernet driver for Motorola MPC8xx.
+ *      Copyright (c) 1997 Dan Malek (dmalek@....net)
+ *
+ * mcen302.c: A Linux network driver for Mototrola 68EN302 MCU
+ *
+ *      Copyright (C) 1999 Aplio S.A. Written by Vadim Lebedev
+ *
+ * 
+ *  The Open Ethernet Controller is just a MAC, it needs to be
+ *  combined with a PHY and buffer memory in order to create an
+ *  ethernet device. Thus some of the hardware parameters are device
+ *  specific. They need to be defined in asm/hardware.h. Example:
+ * 
+ * The IRQ for the device:
+ * #define OETH_IRQ                1
+ *
+ * The flag to be passed to request_irq:
+ * #define OETH_REQUEST_IRQ_FLAG   0
+ * 
+ * The address where the MAC registers are mapped:
+ * #define OETH_BASE_ADDR          0xFD030000
+ *
+ * The address where the MAC RX/TX buffers are mapped:
+ * #define OETH_SRAM_BUFF_BASE     0xFD800000
+ * 
+ * Sizes for a RX or TX buffer:
+ * #define OETH_RX_BUFF_SIZE       2048
+ * #define OETH_TX_BUFF_SIZE       2048
+ * The number of RX and TX buffers:
+ * #define OETH_RXBD_NUM           16
+ * #define OETH_TXBD_NUM           16
+ * The PHY ID (needed if MII is enabled):
+ * #define OETH_PHY_ID             0
+ * 
+ * Code to perform the device specific initialization (REGS is a
+ *  struct oeth_regs*):
+ * #define OETH_PLATFORM_SPECIFIC_INIT(REGS)
+ * it should at least initialize the device MAC address in
+ *  REGS->mac_addr1 and REGS->mac_addr2.
+ * 
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/module.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+
+#include <asm/hardware.h>
+
+#include "open_eth.h"
+
+#define DRV_NAME "OpencoresEthernet"
+
+/* The Opencores Ethernet driver needs some parameters from the
+ * hardware implementation. They should be defined in the
+ asm/hardware.h file. */
+
+/* Debug helpers. */
+/* #define OETH_DEBUG_TRANSMIT  */
+#ifdef OETH_DEBUG_TRANSMIT
+#define OEDTX(x) x
+#else
+#define OEDTX(x)
+#endif
+
+/* #define OETH_DEBUG_RECEIVE */
+#ifdef OETH_DEBUG_RECEIVE
+#define OEDRX(x) x
+#else
+#define OEDRX(x)
+#endif
+
+#define OETH_REGS_SIZE  0x1000	/* MAC registers + RX and TX descriptors */
+#define OETH_BD_BASE    (OETH_BASE_ADDR + 0x400)
+#define OETH_TOTAL_BD   128
+
+/* The transmitter timeout FIXME: dann this needs to be handled */
+#define OETH_TX_TIMEOUT	       (2*HZ)
+
+/* The buffer descriptors track the ring buffers. */
+struct oeth_private {
+	struct oeth_regs *regs;	/* Address of controller registers. */
+	struct oeth_bd *rx_bd_base;	/* Address of Rx BDs. */
+	struct oeth_bd *tx_bd_base;	/* Address of Tx BDs. */
+	u8 tx_next;		/* Next buffer to be sent */
+	u8 tx_last;		/* Next buffer to be checked if packet sent */
+	u8 tx_full;		/* Buffer ring full indicator */
+	u8 rx_cur;		/* Next buffer to be checked if packet received */
+	spinlock_t lock;
+	spinlock_t rx_lock;
+	struct net_device_stats stats;
+#if CONFIG_MII
+	struct mii_if_info mii_if;	/* MII lib hooks/info */
+#endif
+};
+
+static void oeth_tx(struct net_device *dev);
+static irqreturn_t oeth_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+#if defined(OETH_DEBUG_RECEIVE) || defined(OETH_DEBUG_TRANSMIT)
+static void oeth_print_packet(u32 * add, int len)
+{
+	int i;
+
+	printk("ipacket: add = %p len = %d\n", add, len);
+	for (i = 0; i < len; i++) {
+		if (!(i % 16))
+			printk("\n");
+		else if (!(i % 8))
+			printk(" ");
+		printk(" %.2x", *(((unsigned char *)add) + i));
+	}
+	printk("\n");
+}
+#endif
+
+static int oeth_open(struct net_device *dev)
+{
+	int ret;
+	struct oeth_private *cep = netdev_priv(dev);
+	struct oeth_regs *regs = cep->regs;
+
+	/* Install our interrupt handler. */
+	ret = request_irq(OETH_IRQ, oeth_interrupt, OETH_REQUEST_IRQ_FLAG,
+			  dev->name, (void *)dev);
+	if (ret) {
+		printk("request_irq failed for the Opencore ethernet device\n");
+		return ret;
+	}
+	/* Enable the receiver and transmiter. */
+	regs->moder |= OETH_MODER_RXEN | OETH_MODER_TXEN;
+
+	/* Start the queue, we are ready to process packets now. */
+	netif_start_queue(dev);
+	return 0;
+}
+
+static int oeth_close(struct net_device *dev)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	struct oeth_regs *regs = cep->regs;
+	volatile struct oeth_bd *bdp;
+	int i;
+
+	spin_lock_irq(&cep->lock);
+	/* Disable the receiver and transmiter. */
+	regs->moder &= ~(OETH_MODER_RXEN | OETH_MODER_TXEN);
+
+	bdp = cep->rx_bd_base;
+	for (i = 0; i < OETH_RXBD_NUM; i++) {
+		bdp->len_status &= ~(OETH_TX_BD_STATS | OETH_TX_BD_READY);
+		bdp++;
+	}
+
+	bdp = cep->tx_bd_base;
+	for (i = 0; i < OETH_TXBD_NUM; i++) {
+		bdp->len_status &= ~(OETH_RX_BD_STATS | OETH_RX_BD_EMPTY);
+		bdp++;
+	}
+
+	spin_unlock_irq(&cep->lock);
+
+	return 0;
+}
+
+static int oeth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	volatile struct oeth_bd *bdp;
+	unsigned long flags;
+	u32 len_status;
+
+	spin_lock_irqsave(&cep->lock, flags);
+
+	if (cep->tx_full) {
+		/* All transmit buffers are full.  Bail out. */
+		printk("%s: tx queue full!.\n", dev->name);
+		netif_stop_queue(dev);
+		spin_unlock_irqrestore(&cep->lock, flags);
+		return NETDEV_TX_BUSY;
+	}
+
+	/* Fill in a Tx ring entry. */
+	bdp = cep->tx_bd_base + cep->tx_next;
+
+	len_status = bdp->len_status;
+
+	/* Clear all of the status flags. */
+	len_status &= ~OETH_TX_BD_STATS;
+
+	/* If the frame is short, tell MAC to pad it. */
+	if (skb->len <= ETH_ZLEN)
+		len_status |= OETH_TX_BD_PAD;
+	else
+		len_status &= ~OETH_TX_BD_PAD;
+
+	OEDTX((printk("TX skb\n")));
+	OEDTX((oeth_print_packet((u32 *) skb->data, skb->len)));
+	OEDTX((printk("end TX skb print\n")));
+
+	if (skb->len > OETH_TX_BUFF_SIZE) {
+		if (net_ratelimit())
+			printk("%s: tx frame too long!.\n", dev->name);
+		dev_kfree_skb_irq(skb);
+		spin_unlock_irqrestore(&cep->lock, flags);
+		return NETDEV_TX_OK;
+	}
+
+	/* Copy data to TX buffer. */
+	memcpy((unsigned char *)bdp->addr, skb->data, skb->len);
+
+	len_status = (len_status & 0x0000ffff) | (skb->len << 16);
+
+	if ((bdp->addr + (len_status >> 16))
+	    >= (OETH_SRAM_BUFF_BASE + OETH_TXBD_NUM * OETH_TX_BUFF_SIZE
+		+ OETH_RXBD_NUM * OETH_RX_BUFF_SIZE))
+		panic("MEMORY OVERWRITE at address: %x !!!\n",
+		      (bdp->addr + (len_status >> 16)));
+
+	OEDTX((printk("TX controller buff\n")));
+	OEDTX((oeth_print_packet((u32 *) bdp->addr, bdp->len_status >> 16)));
+	OEDTX(printk("end TX controller buff print\n"));
+
+	dev_kfree_skb_irq(skb);
+
+	cep->tx_next =
+	    (cep->tx_next + 1 == OETH_TXBD_NUM) ? 0 : (cep->tx_next + 1);
+
+	if (cep->tx_next == cep->tx_last) {
+		cep->tx_full = 1;
+		/* Do not transmit anymore if the TX queue is full. */
+		netif_stop_queue(dev);
+	}
+
+	/* Send it on its way.  Tell controller its ready, interrupt when done,
+	 * and to put the CRC on the end. */
+	len_status |= (OETH_TX_BD_READY | OETH_TX_BD_IRQ | OETH_TX_BD_CRC);
+	bdp->len_status = len_status;
+
+	spin_unlock_irqrestore(&cep->lock, flags);
+
+	dev->trans_start = jiffies;
+	return 0;
+}
+
+/* The interrupt handler. */
+static irqreturn_t oeth_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
+{
+	struct net_device *dev = dev_id;
+	struct oeth_private *cep = netdev_priv(dev);
+	volatile struct oeth_regs *regs = cep->regs;
+	u32 int_events;
+
+	spin_lock(&cep->lock);
+
+	/* Get the interrupt events that caused us to be here. */
+	int_events = regs->int_src;
+	/* Acknowledge interrupt. */
+	regs->int_src = int_events;
+
+	/* If RX or BUSY enable RX polling. */
+	if (int_events & (OETH_INT_RXF | OETH_INT_RXE | OETH_INT_BUSY))
+		if (netif_rx_schedule_prep(dev)) {
+			regs->int_mask &= ~(OETH_INT_MASK_RXF
+					    | OETH_INT_MASK_RXE);
+			__netif_rx_schedule(dev);
+		}
+
+	/* Handle transmit event in its own function. */
+	if (int_events & (OETH_INT_TXB | OETH_INT_TXE)) {
+		oeth_tx(dev);
+	}
+
+	spin_unlock(&cep->lock);
+
+	return IRQ_HANDLED;
+}
+
+static void oeth_tx(struct net_device *dev)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	volatile struct oeth_bd *bdp;
+
+	for (;;
+	     cep->tx_last =
+	     (cep->tx_last + 1 == OETH_TXBD_NUM) ? 0 : (cep->tx_last + 1)) {
+		u32 len_status;
+
+		bdp = cep->tx_bd_base + cep->tx_last;
+		len_status = bdp->len_status;
+
+		/* If the OETH_TX_BD_READY is set the transmission has
+		 * not been done yet! */
+		if ((len_status & OETH_TX_BD_READY)
+		    || ((cep->tx_last == cep->tx_next) && !cep->tx_full))
+			break;
+
+		/* Check status for errors. */
+		if (len_status & OETH_TX_BD_LATECOL)
+			cep->stats.tx_window_errors++;
+		if (len_status & OETH_TX_BD_RETLIM)
+			cep->stats.tx_aborted_errors++;
+		if (len_status & OETH_TX_BD_UNDERRUN)
+			cep->stats.tx_fifo_errors++;
+		if (len_status & OETH_TX_BD_CARRIER) {
+			cep->stats.tx_carrier_errors++;
+		}
+		if (len_status &
+		    (OETH_TX_BD_LATECOL | OETH_TX_BD_RETLIM |
+		     OETH_TX_BD_UNDERRUN))
+			cep->stats.tx_errors++;
+
+		cep->stats.tx_packets++;
+		cep->stats.tx_bytes += len_status >> 16;
+		cep->stats.collisions += (len_status & OETH_TX_BD_RETRY) >> 4;
+
+		if (cep->tx_full) {
+			cep->tx_full = 0;
+			/* We have freed an entry in the TX queue, we can
+			 * start transmitting again. */
+			if (netif_queue_stopped(dev))
+				netif_wake_queue(dev);
+		}
+	}
+}
+
+static unsigned int oeth_rx(struct net_device *dev, int budget)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	volatile struct oeth_bd *bdp;
+	struct sk_buff *skb;
+	int received;
+	int pkt_len;
+	int bad = 0;
+	int current_buff = cep->rx_cur;
+	static int last_current_buff = 0;
+
+	for (received = 0; received < budget;
+	     cep->rx_cur =
+	     (cep->rx_cur + 1 == OETH_RXBD_NUM) ? 0 : (cep->rx_cur + 1)) {
+		u32 len_status;
+		bdp = cep->rx_bd_base + cep->rx_cur;
+
+		/* First, grab all of the stats for the incoming
+		 * packet.  These get messed up if we get called due
+		 * to a busy condition. */
+		len_status = bdp->len_status;
+
+		if (len_status & OETH_RX_BD_EMPTY)
+			break;
+
+		received++;
+
+		/* Check status for errors. */
+		if (len_status & (OETH_RX_BD_TOOLONG | OETH_RX_BD_SHORT)) {
+			cep->stats.rx_length_errors++;
+			bad = 1;
+		}
+		if (len_status & OETH_RX_BD_DRIBBLE) {
+			cep->stats.rx_frame_errors++;
+			bad = 1;
+		}
+		if (len_status & OETH_RX_BD_CRCERR) {
+			cep->stats.rx_crc_errors++;
+			bad = 1;
+		}
+		if (len_status & OETH_RX_BD_OVERRUN) {
+			cep->stats.rx_crc_errors++;
+			bad = 1;
+		}
+		if (len_status & OETH_RX_BD_TOOLONG) {
+			cep->stats.rx_crc_errors++;
+			bad = 1;
+		}
+		if (len_status & OETH_RX_BD_MISS) {
+
+		}
+		if (len_status & OETH_RX_BD_LATECOL) {
+			cep->stats.rx_frame_errors++;
+			bad = 1;
+		}
+
+		if (bad) {
+			bdp->len_status = (len_status & ~OETH_RX_BD_STATS)
+			    | OETH_RX_BD_EMPTY;
+			continue;
+		}
+
+		/* Process the incoming frame. */
+		pkt_len = len_status >> 16;
+
+		skb = netdev_alloc_skb(pkt_len);
+
+		if (likely(skb)) {
+			skb->dev = dev;
+			OEDRX((printk("RX in ETH buf\n")));
+			OEDRX((oeth_print_packet((u32 *) bdp->addr, pkt_len)));
+
+			memcpy(skb_put(skb, pkt_len),
+			       (unsigned char *)bdp->addr, pkt_len);
+			OEDRX((printk("RX in memory\n")));
+			OEDRX((oeth_print_packet((u32 *) skb->data, pkt_len)));
+			skb->protocol = eth_type_trans(skb, dev);
+			dev->last_rx = jiffies;
+			cep->stats.rx_packets++;
+			cep->stats.rx_bytes += pkt_len;
+			netif_receive_skb(skb);
+		}
+
+		bdp->len_status = (len_status & ~OETH_RX_BD_STATS)
+		    | OETH_RX_BD_EMPTY;
+	}
+}
+
+static int oeth_poll(struct net_device *dev, int *budget)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	int orig_budget = min(*budget, dev->quota);
+	int done = 1;
+	unsigned int work_done;
+	static int no_work_done = 0;
+
+	spin_lock(&cep->rx_lock);
+
+	work_done = oeth_rx(dev, orig_budget);
+	if (likely(work_done > 0)) {
+		*budget -= work_done;
+		dev->quota -= work_done;
+		done = (work_done < orig_budget);
+	}
+	
+	if (done) {
+		/* Stop polling and reenable interrupts. */
+		__netif_rx_complete(dev);
+		cep->regs->int_mask |= (OETH_INT_MASK_RXF | OETH_INT_MASK_RXE);
+	}
+	spin_unlock(&cep->rx_lock);
+
+	return !done;
+}
+
+static struct net_device_stats *oeth_get_stats(struct net_device *dev)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	return &cep->stats;
+}
+
+static int oeth_set_mac_address(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = p;
+	struct oeth_private *cep = netdev_priv(dev);
+	volatile struct oeth_regs *regs = cep->regs;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	regs->mac_addr1 = addr->sa_data[0] << 8 | addr->sa_data[1];
+	regs->mac_addr0 = addr->sa_data[2] << 24
+	    | addr->sa_data[3] << 16 | addr->sa_data[4] << 8 | addr->sa_data[5];
+	return 0;
+}
+
+static int __init oeth_init(struct net_device *dev, unsigned int base_addr,
+			    unsigned int irq);
+
+/*
+ * Probe for an Opencores ethernet controller.
+ */
+static struct net_device *__devinit oeth_probe(int unit)
+{
+	struct net_device *dev = alloc_etherdev(sizeof(struct oeth_private));
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	if (!check_mem_region(OETH_BASE_ADDR, OETH_REGS_SIZE)) {
+		SET_MODULE_OWNER(dev);
+		if (oeth_init(dev, OETH_BASE_ADDR, OETH_IRQ) == 0) {
+			if (register_netdev(dev))
+				printk(KERN_WARNING
+				       "Openethernet: No card found\n");
+			else
+				return dev;
+		}
+
+	}
+	return NULL;
+}
+
+#if CONFIG_MII
+static void oeth_get_drvinfo(struct net_device *dev,
+			     struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, "0.0");
+	strcpy(info->bus_info, "none");
+}
+
+static int oeth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	spin_lock_irq(&cep->lock);
+	mii_ethtool_gset(&cep->mii_if, ecmd);
+	spin_unlock_irq(&cep->lock);
+	return 0;
+}
+
+static int oeth_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	int res;
+	spin_lock_irq(&cep->lock);
+	res = mii_ethtool_sset(&cep->mii_if, ecmd);
+	spin_unlock_irq(&cep->lock);
+	return res;
+}
+
+static int oeth_nway_reset(struct net_device *dev)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	return mii_nway_restart(&cep->mii_if);
+}
+
+static u32 oeth_get_link(struct net_device *dev)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	return mii_link_ok(&cep->mii_if);
+}
+
+static struct ethtool_ops ethtool_ops = {
+	.get_drvinfo = oeth_get_drvinfo,
+	.get_link = oeth_get_link,
+	.get_settings = oeth_get_settings,
+	.set_settings = oeth_set_settings,
+	.nway_reset = oeth_nway_reset,
+};
+#endif
+
+/* MII Data accesses. */
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	struct oeth_regs *regs = cep->regs;
+	int read_value, i;
+	volatile int v;
+
+	regs->miiaddress = (phy_id & OETH_MIIADDRESS_FIAD)
+	    | ((location << 8) & OETH_MIIADDRESS_RGAD);
+	regs->miicommand = OETH_MIICOMMAND_RSTAT;
+
+	/* Check if the MII is done. */
+	for (i = 10000; i >= 0; i--) {
+		v = regs->miistatus;
+		if (!(v & OETH_MIISTATUS_BUSY)) {
+			read_value = regs->miirx_data;
+			/* Don't leave miicommand in read status, it
+			 * seems to not be reset to 0 after completion. */
+			regs->miicommand = 0;
+			return read_value;
+		}
+	}
+	printk(KERN_ERR "mdio_read timeout %s\n", dev->name);
+	return -1;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int location,
+		       int value)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	struct oeth_regs *regs = cep->regs;
+	int i;
+	volatile int v;
+	regs->miiaddress = (phy_id & OETH_MIIADDRESS_FIAD)
+	    | ((location << 8) & OETH_MIIADDRESS_RGAD);
+	regs->miitx_data = value;
+	regs->miicommand = OETH_MIICOMMAND_WCTRLDATA;
+	/* Check if the MII is done. */
+	for (i = 10000; i >= 0; i--) {
+		v = regs->miistatus;
+		if (!(v & OETH_MIISTATUS_BUSY))
+			return;
+	}
+	printk(KERN_ERR "mdio_write timeout %s\n", dev->name);
+}
+
+/* Initialize the Open Ethernet MAC. */
+static int oeth_init(struct net_device *dev, unsigned int base_addr,
+		     unsigned int irq)
+{
+	struct oeth_private *cep = netdev_priv(dev);
+	volatile struct oeth_regs *regs;
+	volatile struct oeth_bd *tx_bd, *rx_bd;
+	int i;
+	unsigned long mem_addr = OETH_SRAM_BUFF_BASE;
+
+	/* Initialize the locks. */
+	spin_lock_init(&cep->lock);
+	spin_lock_init(&cep->rx_lock);
+
+	/* Memory regions for the controller registers and buffer space. */
+	request_region(base_addr, OETH_REGS_SIZE, DRV_NAME);
+	dev->base_addr = base_addr;
+	request_region(OETH_SRAM_BUFF_BASE,
+		       OETH_TXBD_NUM * OETH_TX_BUFF_SIZE
+		       + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE, DRV_NAME);
+	/* Get pointer ethernet controller configuration registers. */
+	regs = cep->regs = (struct oeth_regs *)(base_addr);
+
+	/* Reset the controller. */
+	regs->moder = OETH_MODER_RST;	/* Reset ON */
+	regs->moder &= ~OETH_MODER_RST;	/* Reset OFF */
+
+	/* Setting TXBD base to OETH_TXBD_NUM. */
+	regs->tx_bd_num = OETH_TXBD_NUM;
+
+	/* Initialize TXBD pointer. */
+	cep->tx_bd_base = (struct oeth_bd *)OETH_BD_BASE;
+	tx_bd = cep->tx_bd_base;
+
+	/* Initialize RXBD pointer. */
+	cep->rx_bd_base = cep->tx_bd_base + OETH_TXBD_NUM;
+	rx_bd = cep->rx_bd_base;
+
+	/* Initialize receive/transmit pointers. */
+	cep->rx_cur = 0;
+	cep->tx_next = 0;
+	cep->tx_last = 0;
+	cep->tx_full = 0;
+
+	/* Set min (64) and max (1536) packet length. */
+	regs->packet_len = (64 << 16) | 1536;
+
+	/* Set IPGT, IPGR1, IPGR2 and COLLCONF registers to the
+	 * recommended values. */
+	regs->ipgt = 0x00000015;
+	regs->ipgr1 = 0x0000000c;
+	regs->ipgr2 = 0x00000012;
+	regs->collconf = 0x000f003f;
+
+	/* Set control module mode. Do not deal with PAUSE frames for now. */
+	regs->ctrlmoder = 0;
+
+#if CONFIG_MII
+	/* Initialize MII. */
+	cep->mii_if.dev = dev;
+	cep->mii_if.mdio_read = mdio_read;
+	cep->mii_if.mdio_write = mdio_write;
+	cep->mii_if.phy_id = OETH_PHY_ID;
+	cep->mii_if.phy_id_mask = OETH_MIIADDRESS_FIAD;
+	cep->mii_if.reg_num_mask = 0x1f;
+	SET_ETHTOOL_OPS(dev, &ethtool_ops);
+#endif
+
+	/* Platform specific initialization. This function should set
+	   at least set regs->mac_addr1 and regs->mac_addr2. */
+	OETH_PLATFORM_SPECIFIC_INIT(regs);
+
+	/* Initialize TXBDs. */
+	for (i = 0; i < OETH_TXBD_NUM; i++) {
+		tx_bd[i].len_status =
+		    OETH_TX_BD_PAD | OETH_TX_BD_CRC | OETH_TX_BD_IRQ;
+		tx_bd[i].addr = mem_addr;
+		mem_addr += OETH_TX_BUFF_SIZE;
+	}
+	tx_bd[OETH_TXBD_NUM - 1].len_status |= OETH_TX_BD_WRAP;
+
+	/* Initialize RXBDs. */
+	for (i = 0; i < OETH_RXBD_NUM; i++) {
+		rx_bd[i].len_status = OETH_RX_BD_EMPTY | OETH_RX_BD_IRQ;
+		rx_bd[i].addr = mem_addr;
+		mem_addr += OETH_RX_BUFF_SIZE;
+	}
+	rx_bd[OETH_RXBD_NUM - 1].len_status |= OETH_RX_BD_WRAP;
+
+	/* Set default ethernet MAC address. */
+	dev->dev_addr[0] = (regs->mac_addr1 >> 8) & 0xff;
+	dev->dev_addr[1] = regs->mac_addr1 & 0xff;
+	dev->dev_addr[2] = (regs->mac_addr0 >> 24) & 0xff;
+	dev->dev_addr[3] = (regs->mac_addr0 >> 16) & 0xff;
+	dev->dev_addr[4] = (regs->mac_addr0 >> 8) & 0xff;
+	dev->dev_addr[5] = regs->mac_addr0 & 0xff;
+
+	/* Clear all pending interrupts. */
+	regs->int_src = 0xffffffff;
+
+	/* Promisc, IFG, CRCEn  */
+	regs->moder |= OETH_MODER_PAD | OETH_MODER_IFG | OETH_MODER_CRCEN;
+
+	/* Enable interrupt sources. */
+	regs->int_mask = OETH_INT_MASK_TXB | OETH_INT_MASK_TXE
+	    | OETH_INT_MASK_RXF | OETH_INT_MASK_RXE
+	    | OETH_INT_MASK_TXC | OETH_INT_MASK_RXC | OETH_INT_MASK_BUSY;
+
+	/* The Open Ethernet specific entries in the device structure. */
+	dev->open	     = oeth_open;
+	dev->hard_start_xmit = oeth_start_xmit;
+	dev->stop	     = oeth_close;
+	dev->get_stats	     = oeth_get_stats;
+	dev->set_mac_address = oeth_set_mac_address;
+	dev->poll	     = oeth_poll;
+	dev->weight	     = OETH_RXBD_NUM * 2;
+	dev->irq	     = irq;
+	/* FIXME: Something needs to be done with dev->tx_timeout and
+	   dev->watchdog timeout here. */
+
+	printk(KERN_INFO "Open Ethernet Core Version 1.0\n");
+
+	return 0;
+}
+
+static struct net_device *oeth_dev;
+
+static int __init oeth_init_module(void)
+{
+	oeth_dev = oeth_probe(0);
+	if (!oeth_dev)
+		return PTR_ERR(oeth_dev);
+	return 0;
+}
+
+static void __exit oeth_cleanup_module(void)
+{
+	unregister_netdev(oeth_dev);
+	release_region(oeth_dev->base_addr, OETH_REGS_SIZE);
+	release_region(OETH_SRAM_BUFF_BASE,
+		       OETH_TXBD_NUM * OETH_TX_BUFF_SIZE
+		       + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE);
+	free_netdev(oeth_dev);
+}
+
+module_init(oeth_init_module);
+module_exit(oeth_cleanup_module);
+
+MODULE_DESCRIPTION("Opencores ethernet driver.");
+MODULE_LICENSE("GPL");
--- /dev/null	2006-09-20 11:38:04.545479250 -0700
+++ drivers/net/open_eth.h	2006-12-04 15:00:55.000000000 -0800
@@ -0,0 +1,132 @@
+/* Ethernet configuration registers */
+struct oeth_regs {
+	u32 moder;		/* Mode Register */
+	u32 int_src;		/* Interrupt Source Register */
+	u32 int_mask;		/* Interrupt Mask Register */
+	u32 ipgt;		/* Back to Bak Inter Packet Gap Register */
+	u32 ipgr1;		/* Non Back to Back Inter Packet Gap Register 1 */
+	u32 ipgr2;		/* Non Back to Back Inter Packet Gap Register 2 */
+	u32 packet_len;		/* Packet Length Register (min. and max.) */
+	u32 collconf;		/* Collision and Retry Configuration Register */
+	u32 tx_bd_num;		/* Transmit Buffer Descriptor Number Register */
+	u32 ctrlmoder;		/* Control Module Mode Register */
+	u32 miimoder;		/* MII Mode Register */
+	u32 miicommand;		/* MII Command Register */
+	u32 miiaddress;		/* MII Address Register */
+	u32 miitx_data;		/* MII Transmit Data Register */
+	u32 miirx_data;		/* MII Receive Data Register */
+	u32 miistatus;		/* MII Status Register */
+	u32 mac_addr0;		/* MAC Individual Address Register 0 */
+	u32 mac_addr1;		/* MAC Individual Address Register 1 */
+	u32 hash_addr0;		/* Hash Register 0 */
+	u32 hash_addr1;		/* Hash Register 1 */
+};
+
+/* Ethernet buffer descriptor */
+struct oeth_bd {
+	u32 len_status;
+	u32 addr;		/* Buffer address */
+};
+
+/* Tx BD */
+#define OETH_TX_BD_READY        0x8000	/* Tx BD Ready */
+#define OETH_TX_BD_IRQ          0x4000	/* Tx BD IRQ Enable */
+#define OETH_TX_BD_WRAP         0x2000	/* Tx BD Wrap (last BD) */
+#define OETH_TX_BD_PAD          0x1000	/* Tx BD Pad Enable */
+#define OETH_TX_BD_CRC          0x0800	/* Tx BD CRC Enable */
+
+#define OETH_TX_BD_UNDERRUN     0x0100	/* Tx BD Underrun Status */
+#define OETH_TX_BD_RETRY        0x00F0	/* Tx BD Retry Status */
+#define OETH_TX_BD_RETLIM       0x0008	/* Tx BD Retransmission Limit Status */
+#define OETH_TX_BD_LATECOL      0x0004	/* Tx BD Late Collision Status */
+#define OETH_TX_BD_DEFER        0x0002	/* Tx BD Defer Status */
+#define OETH_TX_BD_CARRIER      0x0001	/* Tx BD Carrier Sense Lost Status */
+#define OETH_TX_BD_STATS        (OETH_TX_BD_UNDERRUN            | \
+                                OETH_TX_BD_RETRY                | \
+                                OETH_TX_BD_RETLIM               | \
+                                OETH_TX_BD_LATECOL              | \
+                                OETH_TX_BD_DEFER                | \
+                                OETH_TX_BD_CARRIER)
+
+/* Rx BD */
+#define OETH_RX_BD_EMPTY        0x8000	/* Rx BD Empty */
+#define OETH_RX_BD_IRQ          0x4000	/* Rx BD IRQ Enable */
+#define OETH_RX_BD_WRAP         0x2000	/* Rx BD Wrap (last BD) */
+
+#define OETH_RX_BD_MISS         0x0080	/* Rx BD Miss Status */
+#define OETH_RX_BD_OVERRUN      0x0040	/* Rx BD Overrun Status */
+#define OETH_RX_BD_INVSIMB      0x0020	/* Rx BD Invalid Symbol Status */
+#define OETH_RX_BD_DRIBBLE      0x0010	/* Rx BD Dribble Nibble Status */
+#define OETH_RX_BD_TOOLONG      0x0008	/* Rx BD Too Long Status */
+#define OETH_RX_BD_SHORT        0x0004	/* Rx BD Too Short Frame Status */
+#define OETH_RX_BD_CRCERR       0x0002	/* Rx BD CRC Error Status */
+#define OETH_RX_BD_LATECOL      0x0001	/* Rx BD Late Collision Status */
+#define OETH_RX_BD_STATS        (OETH_RX_BD_MISS                | \
+                                OETH_RX_BD_OVERRUN              | \
+                                OETH_RX_BD_INVSIMB              | \
+                                OETH_RX_BD_DRIBBLE              | \
+                                OETH_RX_BD_TOOLONG              | \
+                                OETH_RX_BD_SHORT                | \
+                                OETH_RX_BD_CRCERR               | \
+                                OETH_RX_BD_LATECOL)
+
+/* MODER Register */
+#define OETH_MODER_RXEN         0x00000001	/* Receive Enable  */
+#define OETH_MODER_TXEN         0x00000002	/* Transmit Enable */
+#define OETH_MODER_NOPRE        0x00000004	/* No Preamble  */
+#define OETH_MODER_BRO          0x00000008	/* Reject Broadcast */
+#define OETH_MODER_IAM          0x00000010	/* Use Individual Hash */
+#define OETH_MODER_PRO          0x00000020	/* Promiscuous (receive all) */
+#define OETH_MODER_IFG          0x00000040	/* Min. IFG not required */
+#define OETH_MODER_LOOPBCK      0x00000080	/* Loop Back */
+#define OETH_MODER_NOBCKOF      0x00000100	/* No Backoff */
+#define OETH_MODER_EXDFREN      0x00000200	/* Excess Defer */
+#define OETH_MODER_FULLD        0x00000400	/* Full Duplex */
+#define OETH_MODER_RST          0x00000800	/* Reset MAC */
+#define OETH_MODER_DLYCRCEN     0x00001000	/* Delayed CRC Enable */
+#define OETH_MODER_CRCEN        0x00002000	/* CRC Enable */
+#define OETH_MODER_HUGEN        0x00004000	/* Huge Enable */
+#define OETH_MODER_PAD          0x00008000	/* Pad Enable */
+#define OETH_MODER_RECSMALL     0x00010000	/* Receive Small */
+
+/* Interrupt Source Register */
+#define OETH_INT_TXB            0x00000001	/* Transmit Buffer IRQ */
+#define OETH_INT_TXE            0x00000002	/* Transmit Error IRQ */
+#define OETH_INT_RXF            0x00000004	/* Receive Frame IRQ */
+#define OETH_INT_RXE            0x00000008	/* Receive Error IRQ */
+#define OETH_INT_BUSY           0x00000010	/* Busy IRQ */
+#define OETH_INT_TXC            0x00000020	/* Transmit Control Frame IRQ */
+#define OETH_INT_RXC            0x00000040	/* Received Control Frame IRQ */
+
+/* Interrupt Mask Register */
+#define OETH_INT_MASK_TXB       0x00000001	/* Transmit Buffer IRQ Mask */
+#define OETH_INT_MASK_TXE       0x00000002	/* Transmit Error IRQ Mask */
+#define OETH_INT_MASK_RXF       0x00000004	/* Receive Frame IRQ Mask */
+#define OETH_INT_MASK_RXE       0x00000008	/* Receive Error IRQ Mask */
+#define OETH_INT_MASK_BUSY      0x00000010	/* Busy IRQ Mask */
+#define OETH_INT_MASK_TXC       0x00000020	/* Transmit Control Frame IRQ Mask */
+#define OETH_INT_MASK_RXC       0x00000040	/* Received Control Frame IRQ Mask */
+
+/* Control Module Mode Register */
+#define OETH_CTRLMODER_PASSALL  0x00000001	/* Pass Control Frames */
+#define OETH_CTRLMODER_RXFLOW   0x00000002	/* Receive Control Flow Enable */
+#define OETH_CTRLMODER_TXFLOW   0x00000004	/* Transmit Control Flow Enable */
+
+/* MII Mode Register */
+#define OETH_MIIMODER_CLKDIV    0x000000FF	/* Clock Divider */
+#define OETH_MIIMODER_NOPRE     0x00000100	/* No Preamble */
+#define OETH_MIIMODER_RST       0x00000200	/* MIIM Reset */
+
+/* MII Command Register */
+#define OETH_MIICOMMAND_SCANSTAT  0x00000001	/* Scan Status */
+#define OETH_MIICOMMAND_RSTAT     0x00000002	/* Read Status */
+#define OETH_MIICOMMAND_WCTRLDATA 0x00000004	/* Write Control Data */
+
+/* MII Address Register */
+#define OETH_MIIADDRESS_FIAD    0x0000001F	/* PHY Address */
+#define OETH_MIIADDRESS_RGAD    0x00001F00	/* RGAD Address */
+
+/* MII Status Register */
+#define OETH_MIISTATUS_LINKFAIL 0x00000001	/* Link Fail */
+#define OETH_MIISTATUS_BUSY     0x00000002	/* MII Busy */
+#define OETH_MIISTATUS_NVALID   0x00000004	/* Data in MII Status Register is invalid */
--- drivers/net/Kconfig~	2006-09-05 12:30:28.000000000 -0700
+++ drivers/net/Kconfig	2006-12-01 11:29:25.000000000 -0800
@@ -550,6 +550,11 @@
         help
           If you have an XT2000 board, you probably want to enable this driver.
 
+config OPENCORES_ETHERNET
+       tristate "Opencores.org Ethernet MAC driver"
+       depends on NET_ETHERNET
+       help
+	  
 config SUNBMAC
 	tristate "Sun BigMAC 10/100baseT support (EXPERIMENTAL)"
 	depends on NET_ETHERNET && SBUS && EXPERIMENTAL
-
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