[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <200612052136.kB5LadEQ016623@oogie-boogie.ics.uci.edu>
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, ðtool_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