On Mon, Jun 26, 2006 at 04:59:24AM +0200, Lennert Buytenhek wrote:

> The cirrus ep93xx is an ARM SoC that includes an ethernet MAC --
> this patch adds a driver for that ethernet MAC.

Attached is a new version that optimises interrupt handling somewhat.
Since we clear RX status as the first thing we do in the poll handler,
we might as well read the read-to-clear version of the interrupt status
register in the interrupt handler and avoid the explicit clear in the
poll handler.  This shaves close to a second off a 128M sendfile() test.

At ~40 seconds for a 128M sendfile (~3.2MB/sec), the network performance
of this CPU isn't impressive by any means, but given that the CPU only
runs at 200MHz and that the MAC doesn't do checksum offloading and insists
on 4-byte buffer alignment, we can't really do a whole lot better.  The
performance with this driver is still a good deal better than with the
vendor driver, though -- for this particular test (128M sendfile), the
vendor driver needs 1m21s.

Apart from that it still uses numeric chip register addresses, I'm
quite happy with the driver as it is, it survives heavy beating and
is pretty stable.

Index: linux-2.6.17-git10/drivers/net/arm/Kconfig
===================================================================
--- linux-2.6.17-git10.orig/drivers/net/arm/Kconfig
+++ linux-2.6.17-git10/drivers/net/arm/Kconfig
@@ -39,3 +39,10 @@ config ARM_AT91_ETHER
        help
          If you wish to compile a kernel for the AT91RM9200 and enable
          ethernet support, then you should always answer Y to this.
+
+config EP93XX_ETH
+       tristate "EP93xx Ethernet support"
+       depends on NET_ETHERNET && ARM && ARCH_EP93XX
+       help
+         This is a driver for the ethernet hardware included in EP93xx CPUs.
+         Say Y if you are building a kernel for EP93xx based devices.
Index: linux-2.6.17-git10/drivers/net/arm/Makefile
===================================================================
--- linux-2.6.17-git10.orig/drivers/net/arm/Makefile
+++ linux-2.6.17-git10/drivers/net/arm/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_ARM_ETHERH)        += etherh.o
 obj-$(CONFIG_ARM_ETHER3)       += ether3.o
 obj-$(CONFIG_ARM_ETHER1)       += ether1.o
 obj-$(CONFIG_ARM_AT91_ETHER)   += at91_ether.o
+obj-$(CONFIG_EP93XX_ETH)       += ep93xx_eth.o
Index: linux-2.6.17-git10/drivers/net/arm/ep93xx_eth.c
===================================================================
--- /dev/null
+++ linux-2.6.17-git10/drivers/net/arm/ep93xx_eth.c
@@ -0,0 +1,668 @@
+/*
+ * EP93xx ethernet network device driver
+ * Copyright (C) 2006 Lennert Buytenhek <[EMAIL PROTECTED]>
+ * Dedicated to Marija Kulikova.
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <asm/arch/ep93xx-regs.h>
+#include <asm/arch/platform.h>
+#include <asm/io.h>
+#include "ep93xx_eth.h"
+
+#define DRV_MODULE_VERSION     "0.1"
+
+#define RX_QUEUE_ENTRIES       64
+#define TX_QUEUE_ENTRIES       8
+
+struct ep93xx_descs
+{
+       struct ep93xx_rdesc     rdesc[RX_QUEUE_ENTRIES];
+       struct ep93xx_tdesc     tdesc[TX_QUEUE_ENTRIES];
+       struct ep93xx_rstat     rstat[RX_QUEUE_ENTRIES];
+       struct ep93xx_tstat     tstat[TX_QUEUE_ENTRIES];
+};
+
+struct ep93xx_priv
+{
+       struct resource         *res;
+       void                    *base_addr;
+       int                     irq;
+
+       struct ep93xx_descs     *descs;
+       dma_addr_t              descs_dma_addr;
+
+       void                    *rx_buf[RX_QUEUE_ENTRIES];
+       void                    *tx_buf[TX_QUEUE_ENTRIES];
+
+       int                     rx_pointer;
+       int                     tx_clean_pointer;
+       int                     tx_pointer;
+       int                     tx_pending;
+
+       struct net_device_stats stats;
+};
+
+#define rdb(ep, off)           __raw_readb((ep)->base_addr + (off))
+#define rdw(ep, off)           __raw_readw((ep)->base_addr + (off))
+#define rdl(ep, off)           __raw_readl((ep)->base_addr + (off))
+#define wrb(ep, off, val)      __raw_writeb((val), (ep)->base_addr + (off))
+#define wrw(ep, off, val)      __raw_writew((val), (ep)->base_addr + (off))
+#define wrl(ep, off, val)      __raw_writel((val), (ep)->base_addr + (off))
+
+static int ep93xx_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+       int entry;
+
+       if (unlikely(skb->len) > PAGE_SIZE) {
+               ep->stats.tx_dropped++;
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       entry = ep->tx_pointer;
+       ep->tx_pointer = (ep->tx_pointer + 1) % TX_QUEUE_ENTRIES;
+
+       ep->descs->tdesc[entry].tdesc1 =
+               TDESC1_EOF | (entry << 16) | (skb->len & 0xfff);
+       skb_copy_and_csum_dev(skb, ep->tx_buf[entry]);
+       dma_sync_single(NULL, ep->descs->tdesc[entry].buf_addr,
+                               skb->len, DMA_TO_DEVICE);
+       dev_kfree_skb(skb);
+
+       dev->trans_start = jiffies;
+
+       local_irq_disable();
+       ep->tx_pending++;
+       if (ep->tx_pending == TX_QUEUE_ENTRIES)
+               netif_stop_queue(dev);
+       local_irq_enable();
+
+       wrl(ep, 0x00bc, 1);
+
+       return 0;
+}
+
+static int ep93xx_rx(struct net_device *dev, int *budget)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+       int tail_offset;
+       int rx_done;
+       int processed;
+
+       tail_offset = rdl(ep, 0x00a8) - ep->descs_dma_addr;
+
+       rx_done = 0;
+       processed = 0;
+       while (*budget > 0) {
+               int entry;
+               struct ep93xx_rstat *rstat;
+               u32 rstat0;
+               u32 rstat1;
+               int length;
+               struct sk_buff *skb;
+
+               entry = ep->rx_pointer;
+               rstat = ep->descs->rstat + entry;
+               if ((void *)rstat - (void *)ep->descs == tail_offset) {
+                       rx_done = 1;
+                       break;
+               }
+
+               rstat0 = rstat->rstat0;
+               rstat1 = rstat->rstat1;
+               rstat->rstat0 = 0;
+               rstat->rstat1 = 0;
+
+               if (!(rstat0 & RSTAT0_RFP)) {
+                       printk(KERN_CRIT "ep93xx_rx: buffer not done "
+                                        " %.8x %.8x\n", rstat0, rstat1);
+                       BUG();
+               }
+               if (!(rstat0 & RSTAT0_EOF)) {
+                       printk(KERN_CRIT "ep93xx_rx: not end-of-frame "
+                                        " %.8x %.8x\n", rstat0, rstat1);
+                       BUG();
+               }
+               if (!(rstat0 & RSTAT0_EOB)) {
+                       printk(KERN_CRIT "ep93xx_rx: not end-of-buffer "
+                                        " %.8x %.8x\n", rstat0, rstat1);
+                       BUG();
+               }
+               if (!(rstat1 & RSTAT1_RFP)) {
+                       printk(KERN_CRIT "ep93xx_rx: buffer1 not done "
+                                        " %.8x %.8x\n", rstat0, rstat1);
+                       BUG();
+               }
+               if ((rstat1 & RSTAT1_BUFFER_INDEX) >> 16 != entry) {
+                       printk(KERN_INFO "ep93xx_rx: entry mismatch "
+                                        " %.8x %.8x\n", rstat0, rstat1);
+                       BUG();
+               }
+
+               if (!(rstat0 & RSTAT0_RWE)) {
+                       printk(KERN_NOTICE "ep93xx_rx: receive error "
+                                        " %.8x %.8x\n", rstat0, rstat1);
+
+                       ep->stats.rx_errors++;
+                       if (rstat0 & RSTAT0_OE)
+                               ep->stats.rx_fifo_errors++;
+                       if (rstat0 & RSTAT0_FE)
+                               ep->stats.rx_frame_errors++;
+                       if (rstat0 & (RSTAT0_RUNT | RSTAT0_EDATA))
+                               ep->stats.rx_length_errors++;
+                       if (rstat0 & RSTAT0_CRCE)
+                               ep->stats.rx_crc_errors++;
+                       goto err;
+               }
+
+               length = rstat1 & RSTAT1_FRAME_LENGTH;
+               if (length < 4 || length > PAGE_SIZE) {
+                       printk(KERN_NOTICE "ep93xx_rx: invalid length "
+                                        " %.8x %.8x\n", rstat0, rstat1);
+                       goto err;
+               }
+
+               /* Strip FCS.  */
+               if (rstat0 & RSTAT0_CRCI)
+                       length -= 4;
+
+               skb = dev_alloc_skb(length + 2);
+               if (likely(skb != NULL)) {
+                       skb->dev = dev;
+                       skb_reserve(skb, 2);
+                       dma_sync_single(NULL, ep->descs->rdesc[entry].buf_addr,
+                                               length, DMA_FROM_DEVICE);
+                       eth_copy_and_sum(skb, ep->rx_buf[entry], length, 0);
+                       skb_put(skb, length);
+                       skb->protocol = eth_type_trans(skb, dev);
+
+                       dev->last_rx = jiffies;
+
+                       netif_receive_skb(skb);
+
+                       ep->stats.rx_packets++;
+                       ep->stats.rx_bytes += length;
+               } else {
+                       ep->stats.rx_dropped++;
+               }
+
+err:
+               ep->rx_pointer = (entry + 1) % RX_QUEUE_ENTRIES;
+               processed++;
+               dev->quota--;
+               (*budget)--;
+       }
+
+       if (processed) {
+               wrw(ep, 0x009c, processed);
+               wrw(ep, 0x00ac, processed);
+       }
+
+       return !rx_done;
+}
+
+static int ep93xx_have_more_rx(struct ep93xx_priv *ep)
+{
+       struct ep93xx_rstat *rstat;
+       int tail_offset;
+
+       rstat = ep->descs->rstat + ep->rx_pointer;
+       tail_offset = rdl(ep, 0x00a8) - ep->descs_dma_addr;
+
+       return !((void *)rstat - (void *)ep->descs == tail_offset);
+}
+
+static int ep93xx_poll(struct net_device *dev, int *budget)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+
+       /* @@@ Have to stop polling when device is downed while we
+        * are polling.  */
+
+poll_some_more:
+       if (ep93xx_rx(dev, budget))
+               return 1;
+
+       netif_rx_complete(dev);
+
+       local_irq_disable();
+       wrl(ep, 0x0024, 0x0000000f);
+       if (ep93xx_have_more_rx(ep)) {
+               wrl(ep, 0x0024, 0x00000008);
+               wrl(ep, 0x0028, 0x00000004);
+               local_irq_enable();
+
+               if (netif_rx_reschedule(dev, 0))
+                       goto poll_some_more;
+
+               return 0;
+       }
+       local_irq_enable();
+
+       return 0;
+}
+
+static void ep93xx_tx_complete(struct net_device *dev)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+       int tail_offset;
+       int wake;
+
+       tail_offset = rdl(ep, 0x00c8) - ep->descs_dma_addr;
+
+       wake = 0;
+       while (1) {
+               int entry;
+               struct ep93xx_tstat *tstat;
+               u32 tstat0;
+
+               entry = ep->tx_clean_pointer;
+               tstat = ep->descs->tstat + entry;
+               if ((void *)tstat - (void *)ep->descs == tail_offset)
+                       break;
+
+               tstat0 = tstat->tstat0;
+               tstat->tstat0 = 0;
+
+               if (!(tstat0 & TSTAT0_TXFP)) {
+                       printk(KERN_CRIT "ep93xx_tx_complete: buffer not done "
+                                        " %.8x\n", tstat0);
+                       BUG();
+               }
+               if (tstat0 & TSTAT0_FA) {
+                       printk(KERN_CRIT "ep93xx_tx_complete: frame aborted "
+                                        " %.8x\n", tstat0);
+                       BUG();
+               }
+               if ((tstat0 & TSTAT0_BUFFER_INDEX) != entry) {
+                       printk(KERN_INFO "ep93xx_tx_complete: entry mismatch "
+                                        " %.8x\n", tstat0);
+                       BUG();
+               }
+
+               if (tstat0 & TSTAT0_TXWE) {
+                       int length = ep->descs->tdesc[entry].tdesc1 & 0xfff;
+
+                       ep->stats.tx_packets++;
+                       ep->stats.tx_bytes += length;
+               } else {
+                       ep->stats.tx_errors++;
+               }
+               if (tstat0 & TSTAT0_LCRS)
+                       ep->stats.tx_carrier_errors++;
+               if (tstat0 & TSTAT0_OW)
+                       ep->stats.tx_window_errors++;
+               if (tstat0 & TSTAT0_TXU)
+                       ep->stats.tx_fifo_errors++;
+               ep->stats.collisions += (tstat0 >> 16) & 0x1f;
+
+               ep->tx_clean_pointer = (entry + 1) % TX_QUEUE_ENTRIES;
+               if (ep->tx_pending == TX_QUEUE_ENTRIES)
+                       wake = 1;
+               ep->tx_pending--;
+       }
+
+       if (wake)
+               netif_wake_queue(dev);
+}
+
+static irqreturn_t ep93xx_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = dev_id;
+       struct ep93xx_priv *ep = netdev_priv(dev);
+       u32 status;
+
+       status = rdl(ep, 0x002c);
+       if (status == 0)
+               return IRQ_NONE;
+
+       if (status & 0x00000004) {
+               if (likely(__netif_rx_schedule_prep(dev))) {
+                       wrl(ep, 0x0024, 0x00000008);
+                       __netif_rx_schedule(dev);
+               }
+       }
+
+       if (status & 0x00000008)
+               ep93xx_tx_complete(dev);
+
+       return IRQ_HANDLED;
+}
+
+static void ep93xx_free_buffers(struct ep93xx_priv *ep)
+{
+       int i;
+
+       for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+               dma_addr_t d;
+
+               d = ep->descs->rdesc[i].buf_addr;
+               if (d)
+                       dma_unmap_single(NULL, d, PAGE_SIZE, DMA_FROM_DEVICE);
+
+               if (ep->rx_buf[i] != NULL)
+                       free_page((unsigned long)ep->rx_buf[i]);
+       }
+
+       for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
+               dma_addr_t d;
+
+               d = ep->descs->tdesc[i].buf_addr;
+               if (d)
+                       dma_unmap_single(NULL, d, PAGE_SIZE, DMA_TO_DEVICE);
+
+               if (ep->tx_buf[i] != NULL)
+                       free_page((unsigned long)ep->tx_buf[i]);
+       }
+
+       dma_free_coherent(NULL, sizeof(struct ep93xx_descs), ep->descs,
+                                                       ep->descs_dma_addr);
+}
+
+static int ep93xx_alloc_buffers(struct ep93xx_priv *ep)
+{
+       int i;
+
+       ep->descs = dma_alloc_coherent(NULL, sizeof(struct ep93xx_descs),
+                               &ep->descs_dma_addr, GFP_KERNEL | GFP_DMA);
+       if (ep->descs == NULL)
+               return 1;
+
+       for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
+               void *page;
+               dma_addr_t d;
+
+               page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+               if (page == NULL)
+                       goto err;
+
+               d = dma_map_single(NULL, page, PAGE_SIZE, DMA_FROM_DEVICE);
+               if (dma_mapping_error(d)) {
+                       free_page((unsigned long)page);
+                       goto err;
+               }
+
+               ep->rx_buf[i] = page;
+               ep->descs->rdesc[i].buf_addr = d;
+               ep->descs->rdesc[i].rdesc1 = (i << 16) | PAGE_SIZE;
+       }
+
+       for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
+               void *page;
+               dma_addr_t d;
+
+               page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+               if (page == NULL)
+                       goto err;
+
+               d = dma_map_single(NULL, page, PAGE_SIZE, DMA_TO_DEVICE);
+               if (dma_mapping_error(d)) {
+                       free_page((unsigned long)page);
+                       goto err;
+               }
+
+               ep->tx_buf[i] = page;
+               ep->descs->tdesc[i].buf_addr = d;
+       }
+
+       return 0;
+
+err:
+       ep93xx_free_buffers(ep);
+       return 1;
+}
+
+static void ep93xx_start_hw(struct net_device *dev)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+       unsigned long addr;
+
+       wrl(ep, 0x0020, 0x00000001);
+       while (rdl(ep, 0x0020) & 0x00000001)
+               ;
+
+       /* Receive descriptor ring.  */
+       addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rdesc);
+       wrl(ep, 0x0090, addr);
+       wrl(ep, 0x0098, addr);
+       wrw(ep, 0x0094, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rdesc));
+
+       /* Receive status ring.  */
+       addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rstat);
+       wrl(ep, 0x00a0, addr);
+       wrl(ep, 0x00a8, addr);
+       wrw(ep, 0x00a4, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rstat));
+
+       /* Transmit descriptor ring.  */
+       addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tdesc);
+       wrl(ep, 0x00b0, addr);
+       wrl(ep, 0x00b8, addr);
+       wrw(ep, 0x00b4, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tdesc));
+
+       /* Transmit status ring.  */
+       addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tstat);
+       wrl(ep, 0x00c0, addr);
+       wrl(ep, 0x00c8, addr);
+       wrw(ep, 0x00c4, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tstat));
+
+       wrl(ep, 0x0080, 0x00000101);                    // bmctl
+       wrl(ep, 0x0024, 0x0000000f);                    // inten
+       wrl(ep, 0x0064, 0x00000000);                    // gintmask
+       while (!(rdl(ep, 0x0084) & 0x00000008))
+               cpu_relax();
+       wrl(ep, 0x009c, RX_QUEUE_ENTRIES);
+       wrl(ep, 0x00ac, RX_QUEUE_ENTRIES);
+
+       wrb(ep, 0x0050, dev->dev_addr[0]);
+       wrb(ep, 0x0051, dev->dev_addr[1]);
+       wrb(ep, 0x0052, dev->dev_addr[2]);
+       wrb(ep, 0x0053, dev->dev_addr[3]);
+       wrb(ep, 0x0054, dev->dev_addr[4]);
+       wrb(ep, 0x0055, dev->dev_addr[5]);
+       wrl(ep, 0x004c, 0x00000000);
+
+       wrl(ep, 0x0000, 0x00073800);
+       wrl(ep, 0x0004, 0x00000001);
+}
+
+static void ep93xx_stop_hw(struct net_device *dev)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+
+       wrl(ep, 0x0020, 0x00000001);
+       while (rdl(ep, 0x0020) & 0x00000001)
+               ;
+}
+
+static int ep93xx_open(struct net_device *dev)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+       int err;
+
+       if (ep93xx_alloc_buffers(ep))
+               return -ENOMEM;
+
+       ep93xx_start_hw(dev);
+       ep->rx_pointer = 0;
+       ep->tx_clean_pointer = 0;
+       ep->tx_pointer = 0;
+       ep->tx_pending = 0;
+
+       err = request_irq(ep->irq, ep93xx_irq, SA_SHIRQ, "ep93xx_eth", dev);
+       if (err) {
+               ep93xx_stop_hw(dev);
+               ep93xx_free_buffers(ep);
+               return err;
+       }
+
+       wrl(ep, 0x0064, 0x00008000);                    // gintmask
+
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+static int ep93xx_close(struct net_device *dev)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+
+       netif_stop_queue(dev);
+
+       wrl(ep, 0x0064, 0x00000000);                    // gintmask
+       free_irq(ep->irq, dev);
+       ep93xx_stop_hw(dev);
+       ep93xx_free_buffers(ep);
+
+       return 0;
+}
+
+static struct net_device_stats *ep93xx_get_stats(struct net_device *dev)
+{
+       struct ep93xx_priv *ep = netdev_priv(dev);
+       return &(ep->stats);
+}
+
+struct net_device *ep93xx_dev_alloc(struct ep93xx_eth_data *data)
+{
+       struct net_device *dev;
+       struct ep93xx_priv *ep;
+
+       dev = alloc_etherdev(sizeof(struct ep93xx_priv));
+       if (dev == NULL)
+               return NULL;
+       ep = netdev_priv(dev);
+
+       memcpy(dev->dev_addr, data->dev_addr, ETH_ALEN);
+
+       dev->hard_start_xmit = ep93xx_xmit;
+       dev->poll = ep93xx_poll;
+       dev->open = ep93xx_open;
+       dev->stop = ep93xx_close;
+       dev->get_stats = ep93xx_get_stats;
+
+       dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
+       dev->weight = 64;
+
+       return dev;
+}
+
+
+static int ep93xx_eth_remove(struct platform_device *pdev)
+{
+       struct net_device *dev;
+       struct ep93xx_priv *ep;
+
+       dev = platform_get_drvdata(pdev);
+       if (dev == NULL)
+               return 0;
+       platform_set_drvdata(pdev, NULL);
+
+       ep = netdev_priv(dev);
+
+       // @@@ force down
+       unregister_netdev(dev);
+       ep93xx_free_buffers(ep);
+
+       if (ep->base_addr != NULL)
+               iounmap(ep->base_addr);
+
+       if (ep->res != NULL) {
+               release_resource(ep->res);
+               kfree(ep->res);
+       }
+
+       free_netdev(dev);
+
+       return 0;
+}
+
+static int ep93xx_eth_probe(struct platform_device *pdev)
+{
+       struct ep93xx_eth_data *data;
+       struct net_device *dev;
+       struct ep93xx_priv *ep;
+       int err;
+
+       data = pdev->dev.platform_data;
+       if (pdev == NULL)
+               return -ENODEV;
+
+       printk(KERN_INFO "ep93xx-eth " DRV_MODULE_VERSION " loading\n");
+
+       dev = ep93xx_dev_alloc(data);
+       if (dev == NULL) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       ep = netdev_priv(dev);
+
+       platform_set_drvdata(pdev, dev);
+
+       ep->res = request_mem_region(pdev->resource[0].start,
+                       pdev->resource[0].end - pdev->resource[0].start + 1,
+                       pdev->dev.bus_id);
+       if (ep->res == NULL) {
+               dev_err(&pdev->dev, "Could not reserve memory region\n");
+               err = -ENOMEM;
+               goto err_out;
+       }
+
+       ep->base_addr = ioremap(pdev->resource[0].start,
+                       pdev->resource[0].end - pdev->resource[0].start);
+       if (ep->base_addr == NULL) {
+               dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
+               err = -EIO;
+               goto err_out;
+       }
+       ep->irq = pdev->resource[1].start;
+
+       err = register_netdev(dev);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to register netdev\n");
+               goto err_out;
+       }
+
+       return 0;
+
+err_out:
+       ep93xx_eth_remove(pdev);
+       return err;
+}
+
+
+static struct platform_driver ep93xx_eth_driver = {
+       .probe          = ep93xx_eth_probe,
+       .remove         = ep93xx_eth_remove,
+       .driver         = {
+               .name   = "ep93xx-eth",
+       },
+};
+
+static int __init ep93xx_eth_init_module(void)
+{
+       return platform_driver_register(&ep93xx_eth_driver);
+}
+
+static void __exit ep93xx_eth_cleanup_module(void)
+{
+       platform_driver_unregister(&ep93xx_eth_driver);
+}
+
+module_init(ep93xx_eth_init_module);
+module_exit(ep93xx_eth_cleanup_module);
+MODULE_LICENSE("GPL");
Index: linux-2.6.17-git10/drivers/net/arm/ep93xx_eth.h
===================================================================
--- /dev/null
+++ linux-2.6.17-git10/drivers/net/arm/ep93xx_eth.h
@@ -0,0 +1,75 @@
+/*
+ * EP93xx ethernet network device driver
+ * Copyright (C) 2006 Lennert Buytenhek <[EMAIL PROTECTED]>
+ * Dedicated to Marija Kulikova.
+ *
+ * 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.
+ */
+
+#ifndef __EP93XX_ETH_H
+#define __EP93XX_ETH_H
+
+struct ep93xx_rdesc
+{
+       u32     buf_addr;
+       u32     rdesc1;
+};
+
+#define RDESC1_NSOF            0x80000000
+#define RDESC1_BUFFER_INDEX    0x7fff0000
+#define RDESC1_BUFFER_LENGTH   0x0000ffff
+
+struct ep93xx_rstat
+{
+       u32     rstat0;
+       u32     rstat1;
+};
+
+#define RSTAT0_RFP             0x80000000
+#define RSTAT0_RWE             0x40000000
+#define RSTAT0_EOF             0x20000000
+#define RSTAT0_EOB             0x10000000
+#define RSTAT0_AM              0x00c00000
+#define RSTAT0_RX_ERR          0x00200000
+#define RSTAT0_OE              0x00100000
+#define RSTAT0_FE              0x00080000
+#define RSTAT0_RUNT            0x00040000
+#define RSTAT0_EDATA           0x00020000
+#define RSTAT0_CRCE            0x00010000
+#define RSTAT0_CRCI            0x00008000
+#define RSTAT0_HTI             0x00003f00
+#define RSTAT1_RFP             0x80000000
+#define RSTAT1_BUFFER_INDEX    0x7fff0000
+#define RSTAT1_FRAME_LENGTH    0x0000ffff
+
+struct ep93xx_tdesc
+{
+       u32     buf_addr;
+       u32     tdesc1;
+};
+
+#define TDESC1_EOF             0x80000000
+#define TDESC1_BUFFER_INDEX    0x7fff0000
+#define TDESC1_BUFFER_ABORT    0x00008000
+#define TDESC1_BUFFER_LENGTH   0x00000fff
+
+struct ep93xx_tstat
+{
+       u32     tstat0;
+};
+
+#define TSTAT0_TXFP            0x80000000
+#define TSTAT0_TXWE            0x40000000
+#define TSTAT0_FA              0x20000000
+#define TSTAT0_LCRS            0x10000000
+#define TSTAT0_OW              0x04000000
+#define TSTAT0_TXU             0x02000000
+#define TSTAT0_ECOLL           0x01000000
+#define TSTAT0_NCOLL           0x001f0000
+#define TSTAT0_BUFFER_INDEX    0x00007fff
+
+
+#endif
Index: linux-2.6.17-git10/include/asm-arm/arch-ep93xx/ep93xx-regs.h
===================================================================
--- linux-2.6.17-git10.orig/include/asm-arm/arch-ep93xx/ep93xx-regs.h
+++ linux-2.6.17-git10/include/asm-arm/arch-ep93xx/ep93xx-regs.h
@@ -27,6 +27,7 @@
 #define EP93XX_DMA_BASE                        (EP93XX_AHB_VIRT_BASE + 
0x00000000)
 
 #define EP93XX_ETHERNET_BASE           (EP93XX_AHB_VIRT_BASE + 0x00010000)
+#define EP93XX_ETHERNET_PHYS_BASE      (EP93XX_AHB_PHYS_BASE + 0x00010000)
 
 #define EP93XX_USB_BASE                        (EP93XX_AHB_VIRT_BASE + 
0x00020000)
 #define EP93XX_USB_PHYS_BASE           (EP93XX_AHB_PHYS_BASE + 0x00020000)
Index: linux-2.6.17-git10/include/asm-arm/arch-ep93xx/platform.h
===================================================================
--- linux-2.6.17-git10.orig/include/asm-arm/arch-ep93xx/platform.h
+++ linux-2.6.17-git10/include/asm-arm/arch-ep93xx/platform.h
@@ -11,5 +11,10 @@ void ep93xx_init_devices(void);
 void ep93xx_clock_init(void);
 extern struct sys_timer ep93xx_timer;
 
+struct ep93xx_eth_data
+{
+       unsigned char   dev_addr[6];
+};
+
 
 #endif
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to