[PATCH] Driver for xilinx ethernet MAC core
Peter 'p2' De Schrijver
p2 at mind.be
Thu Sep 5 05:57:12 EST 2002
Hi,
Attached you will find a patch to add support for the xilinx ethernet
MAC core. It was tested on an implementation on a Xilinx Virtex2Pro.
Comments welcome,
Peter.
--
-------------- next part --------------
diff -urN -x CVS -x config -x modules -x mtd -x jffs2 -x jffs linuxppc_2_4_clean/drivers/net/Config.in linuxppc_2_4_xseg2.new.clean2/drivers/net/Config.in
--- linuxppc_2_4_clean/drivers/net/Config.in 2002-07-20 12:03:29.000000000 +0200
+++ linuxppc_2_4_xseg2.new.clean2/drivers/net/Config.in 2002-09-04 20:07:43.000000000 +0200
@@ -41,6 +41,9 @@
tristate ' National DP83902AV (Oak ethernet) support' CONFIG_OAKNET
fi
fi
+ if [ "$CONFIG_XSEG2" = "y" ]; then
+ tristate ' Xilinx ethernet MAC support' CONFIG_XEMAC
+ fi
if [ "$CONFIG_ZORRO" = "y" ]; then
tristate ' Ariadne support' CONFIG_ARIADNE
tristate ' Ariadne II and X-Surf support' CONFIG_NE2K_ZORRO
diff -urN -x CVS -x config -x modules -x mtd -x jffs2 -x jffs linuxppc_2_4_clean/drivers/net/Makefile linuxppc_2_4_xseg2.new.clean2/drivers/net/Makefile
--- linuxppc_2_4_clean/drivers/net/Makefile 2002-07-20 12:03:47.000000000 +0200
+++ linuxppc_2_4_xseg2.new.clean2/drivers/net/Makefile 2002-07-21 17:04:11.000000000 +0200
@@ -84,6 +84,7 @@
obj-$(CONFIG_FEALNX) += fealnx.o mii.o
obj-$(CONFIG_TC35815) += tc35815.o
obj-$(CONFIG_TIGON3) += tg3.o
+obj-$(CONFIG_XEMAC) += xemac.o
ifeq ($(CONFIG_SK98LIN),y)
obj-y += sk98lin/sk98lin.o
diff -urN -x CVS -x config -x modules -x mtd -x jffs2 -x jffs linuxppc_2_4_clean/drivers/net/xemac.c linuxppc_2_4_xseg2.new.clean2/drivers/net/xemac.c
--- linuxppc_2_4_clean/drivers/net/xemac.c 1970-01-01 01:00:00.000000000 +0100
+++ linuxppc_2_4_xseg2.new.clean2/drivers/net/xemac.c 2002-09-04 20:45:14.000000000 +0200
@@ -0,0 +1,838 @@
+/*
+ * xemac.c: A driver for Xilinx 10/100Mbit/s ethernet MAC core
+ *
+ * Copyright 2002 Mind NV
+ *
+ * http://www.mind.be/
+ *
+ * Author : Peter De Schrijver (p2 at mind.be)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License (GPL) version 2, incorporated herein by
+ * reference. Drivers based on or derived from this code fall under the GPL
+ * and must retain the authorship, copyright and this license notice. This
+ * file is not a complete program and may only be used when the entire
+ * operating system is licensed under the GPL.
+ *
+ */
+
+
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/rtnetlink.h>
+#include <asm/io.h>
+
+#include "xemac.h"
+
+/* RX and TX DMA seem to be broken */
+
+#undef USE_RX_DMA
+#undef USE_TX_DMA
+
+#define XEMAC_BASE 0x60000000
+#define XEMAC_LEN 0x3000
+#define XEMAC_IRQ 28
+
+#define TX_TIMEOUT (4*HZ)
+
+#define MAX_UNITS 1
+
+static int media[MAX_UNITS] = { -1 };
+
+static int max_interrupt_work = 100;
+
+static inline void xemac_writel(unsigned int v,unsigned int a) {
+
+ out_be32((volatile unsigned int *)(a),(v));
+
+}
+
+static inline unsigned int xemac_readl(unsigned int a) {
+
+ return in_be32((volatile unsigned int *)(a));
+
+}
+
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int value) {
+
+ unsigned int ioaddr=dev->base_addr;
+
+ xemac_writel(value, ioaddr+MGTDR);
+ xemac_writel(MGTCR_SB | (phy_id << 25) | (location << 20) |
+ MGTCR_IE, ioaddr+MGTCR);
+
+ while(xemac_readl(ioaddr+MGTCR) & MGTCR_BUSY);
+
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int location) {
+
+ unsigned int ioaddr=dev->base_addr;
+
+ xemac_writel(MGTCR_SB | MGTCR_RWN | (phy_id << 25) |
+ (location << 20) | MGTCR_IE, ioaddr+MGTCR);
+ while(xemac_readl(ioaddr+MGTCR) & MGTCR_BUSY);
+
+ return(xemac_readl(ioaddr+MGTDR) & 0xffff);
+
+}
+
+
+static void xemac_hw_start(struct net_device *dev) {
+
+ struct xemac_priv *xp=dev->priv;
+ unsigned int ioaddr=dev->base_addr;
+ int par,i;
+
+ printk("dev: %p, dev->base_addr: %08lx, xp: %p\n",dev,dev->base_addr,xp);
+ printk("ioaddr: %08x\n",ioaddr);
+
+#ifdef USE_TX_DMA
+ for(i=0;i<NUM_TX_DESCR;i++) {
+ xp->tx_buffer_descr[i].device_status=0;
+ xp->tx_buffer_descr[i].control=DMACR_SG_DISABLE;
+ xp->tx_buffer_descr[i].length=0;
+ xp->tx_buffer_descr[i].status=0;
+ xp->tx_buffer_descr[i].next=
+ virt_to_bus(&xp->tx_buffer_descr[(i+1)%NUM_TX_DESCR]);
+ xp->tx_buffer_descr[i].flags=0;
+ xp->tx_buffer_descr[i].req_length=0;
+ xp->tx_buffer_descr[i].destination=XEMAC_BASE+FIFO_TX_DATA;
+ xp->tx_buffer_descr[i].source=xp->tx_buffer_dma_addr+i*TX_BUF_LEN;
+ xp->tx_buffer_descr[i].buffer=xp->tx_buffer+i*TX_BUF_LEN;
+ }
+#endif
+
+ xemac_writel(ECR_RSTTX | ECR_RSTRX,ioaddr+ECR);
+ udelay(100);
+ xemac_writel(ECR_ENPHY | ECR_TXPAD | ECR_TXFCS | ECR_UA | ECR_BA,
+ ioaddr+ECR);
+
+ mdio_write(dev, xp->phys[0], 0, 0x8000);
+ while(mdio_read(dev, xp->phys[0],0) & 0x8000);
+
+ xemac_writel((dev->dev_addr[0] << 8) |
+ (dev->dev_addr[1]), ioaddr+SAH);
+
+ xemac_writel((dev->dev_addr[2] << 24) |
+ (dev->dev_addr[3] << 16) |
+ (dev->dev_addr[4] << 8) |
+ (dev->dev_addr[5]), ioaddr+SAL);
+
+ par=mdio_read(dev, xp->phys[0], 0x11);
+
+ xemac_writel(xemac_readl(ioaddr+ECR) | ((par & (1<<9)) ? (1<<31) : 0), ioaddr+ECR);
+ xemac_writel(xemac_readl(ioaddr+ECR) | ECR_ENTX | ECR_ENRX, ioaddr+ECR);
+
+#ifdef USE_TX_DMA
+ xemac_writel(DMA_IX_PKT_DONE | DMA_IX_DMA_ERROR,
+ ioaddr + TX_DMA_IEREG);
+#endif
+
+#ifdef USE_RX_DMA
+ xemac_writel(DMA_IX_PKT_DONE | DMA_IX_DMA_ERROR,
+ ioaddr + RX_DMA_IEREG);
+#endif
+
+ xp->cur_tx=xp->tx_inuse=xp->tx_dirty=0;
+#ifdef USE_TX_DMA
+ xemac_writel(xp->tx_dma_addr,ioaddr+TX_DMA_BDA);
+#endif
+ xp->cur_rx=0;
+
+#ifdef USE_RX_DMA
+ xemac_writel(xp->rx_dma_addr,ioaddr+RX_DMA_BDA);
+#endif
+
+
+ xemac_writel(EMAC_INT |
+#ifdef USE_RX_DMA
+ RECV_DMA_INT |
+#endif
+#ifdef USE_TX_DMA
+ XMIT_DMA_INT |
+#endif
+ 0,ioaddr+INT_MASK);
+
+ xemac_writel(0 |
+#ifndef USE_RX_DMA
+ RECV_DONE_INT |
+#endif
+#ifndef USE_TX_DMA
+ TX_DONE_INT |
+#endif
+ 0, ioaddr+IPIF_IE);
+
+
+#ifdef USE_RX_DMA
+ xemac_writel(xemac_readl(ioaddr+RX_DMAC_REG) & ~DMACR_SG_DISABLE,
+ ioaddr+RX_DMAC_REG);
+ xemac_writel(xemac_readl(ioaddr+RX_SWCR_REG) | SWCR_SG_ENABLE,
+ ioaddr+RX_SWCR_REG);
+#endif
+
+ xemac_writel(1<<31, ioaddr + INT_GLOBAL);
+
+ netif_start_queue(dev);
+
+}
+
+static void xemac_set_multicast(struct net_device *dev) {
+
+ unsigned int ioaddr=dev->base_addr;
+
+ if(dev->flags & IFF_PROMISC) {
+ printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+ xemac_writel(xemac_readl(ioaddr+ECR) | ECR_PROMISC,ioaddr + ECR);
+ }
+ if((dev->flags & IFF_ALLMULTI) || dev->mc_count)
+ xemac_writel(xemac_readl(ioaddr+ECR) | ECR_MULTI,ioaddr + ECR);
+
+}
+
+static struct net_device_stats *xemac_get_stats(struct net_device *dev) {
+
+ struct xemac_priv *xp=dev->priv;
+
+ return &xp->stats;
+
+}
+
+static void xemac_tx_timeout(struct net_device *dev) {
+
+ unsigned int ioaddr=dev->base_addr;
+
+ xemac_writel(xemac_readl(ioaddr+TX_SWCR_REG) & ~SWCR_SG_ENABLE,ioaddr+TX_SWCR_REG);
+ xemac_writel(0,ioaddr+TX_RESET_REG);
+ xemac_writel(0, ioaddr+INT_MASK);
+ xemac_writel((xemac_readl(ioaddr+ECR) | ECR_RSTTX | ECR_RSTRX) &
+ ~(ECR_ENTX | ECR_ENRX | ECR_ENPHY),
+ ioaddr+ECR);
+
+ xemac_hw_start(dev);
+
+}
+
+#ifndef USE_TX_DMA
+static void inline write_tx_fifo(struct sk_buff *skb,unsigned int ioaddr) {
+
+ int i;
+ unsigned int *data;
+
+ data=(unsigned int *)skb->data;
+
+ for(i=skb->len;i>0;i-=4){
+ *(unsigned int *)(ioaddr+FIFO_TX_DATA)=*(data++);
+ }
+ if(i>1) {
+ *(unsigned short *)(ioaddr+FIFO_TX_DATA)=*((unsigned short *)data++);
+ i-=2;
+ }
+ if(i>0) {
+ *(unsigned char *)(ioaddr+FIFO_TX_DATA)=*(unsigned char *)data;
+ }
+
+}
+#endif
+
+#ifdef USE_TX_DMA
+static int xemac_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+
+ struct xemac_priv *xp=dev->priv;
+ unsigned int ioaddr=dev->base_addr;
+ int entry,len=skb->len;
+
+ spin_lock_irq(&xp->lock);
+
+ entry=xp->cur_tx ;
+
+ if(xp->tx_inuse)
+ xp->tx_buffer_descr[(entry-1)% NUM_TX_DESCR].control&=~DMACR_SG_DISABLE;
+
+ if(likely(len < TX_BUF_LEN)) {
+ skb_copy_and_csum_dev(skb, xp->tx_buffer_descr[entry].buffer);
+ dev_kfree_skb(skb);
+ }
+ else {
+ dev_kfree_skb(skb);
+ xp->stats.tx_dropped++;
+ return 0;
+ }
+
+ xp->tx_buffer_descr[entry].length=len;
+ xp->tx_buffer_descr[entry].req_length=len;
+ xp->tx_buffer_descr[entry].control=DMACR_SOURCE_INCR | DMACR_DEST_LOCAL
+ | DMACR_LAST_BD | DMACR_SG_DISABLE;
+
+#if 0
+ if(!(xemac_readl(ioaddr+TX_DMAS_REG) & DMASR_SG_BUSY)) {
+#endif
+ xemac_writel(xemac_readl(ioaddr+TX_DMAC_REG) & ~DMACR_SG_DISABLE,ioaddr+TX_DMAC_REG);
+ xemac_writel(xemac_readl(ioaddr+TX_SWCR_REG) | SWCR_SG_ENABLE,ioaddr+TX_SWCR_REG);
+#if 0
+ }
+#endif
+
+ xp->cur_tx=(entry + 1) % NUM_TX_DESCR;
+ xp->tx_inuse++;
+ if(xp->tx_inuse >= (NUM_TX_DESCR-1))
+ netif_stop_queue (dev);
+
+ dev->trans_start = jiffies;
+
+ spin_unlock_irq(&xp->lock);
+
+ return 0;
+
+}
+#else
+static int xemac_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+
+ struct xemac_priv *xp=dev->priv;
+ unsigned int ioaddr=dev->base_addr;
+
+ spin_lock_irq(&xp->lock);
+
+ xp->tx_buffers[xp->tx_dirty]=skb;
+
+ if(!xp->tx_inuse) {
+ write_tx_fifo(xp->tx_buffers[xp->cur_tx],ioaddr);
+ xemac_writel(skb->len,ioaddr + TPLR);
+ }
+
+ xp->tx_inuse++;
+ xp->tx_dirty=(xp->tx_dirty+1) % NUM_TX_BUFFERS;
+
+ if(xp->tx_inuse==NUM_TX_BUFFERS)
+ netif_stop_queue (dev);
+
+ dev->trans_start = jiffies;
+
+ spin_unlock_irq(&xp->lock);
+
+ return 0;
+
+}
+#endif
+
+#ifdef USE_RX_DMA
+static inline void xemac_rx_interrupt(struct net_device *dev,
+ struct xemac_priv *xp,
+ unsigned int ioaddr) {
+
+ int bdcount;
+ int cur_rx=xp->cur_rx,len;
+
+ bdcount=xemac_readl(ioaddr + RX_DMA_PKTCNT);
+
+ for(;bdcount;bdcount--) {
+ struct sk_buff *skb=xp->rx_buffer_descr[cur_rx].skb;
+ struct rx_buffer_descr *rxb=&xp->rx_buffer_descr[cur_rx];
+
+ len=rxb->length;
+
+ skb_put(skb,len);
+ skb->protocol = eth_type_trans(skb, dev);
+
+ netif_rx(skb);
+
+ skb=dev_alloc_skb(MAX_ETH_FRAME_SIZE);
+ if(skb) {
+ rxb->skb=skb;
+ dma_cache_wback_inv((unsigned long)skb->data,MAX_ETH_FRAME_SIZE);
+ rxb->destination=virt_to_bus(skb->data);
+ rxb->control=DMACR_GEN_BD_INTR|DMACR_DEST_INCR|DMACR_SOURCE_LOCAL;
+ rxb->length=MAX_ETH_FRAME_SIZE;
+ rxb->req_length=MAX_ETH_FRAME_SIZE;
+ }
+ else {
+ panic("FIXME: out of memory\n");
+ }
+
+ cur_rx=(++cur_rx) % NUM_RX_DESCR;
+
+ }
+
+ xp->cur_rx=cur_rx;
+
+ return ;
+
+}
+#else
+static inline void xemac_rx_fifo_interrupt(struct net_device *dev,
+ struct xemac_priv *xp,
+ unsigned int ioaddr) {
+
+ struct sk_buff *skb;
+ unsigned int *data;
+ int len,i;
+
+ len=xemac_readl(ioaddr + RPLR);
+ skb=dev_alloc_skb(len+2);
+ if(skb) {
+ skb->dev = dev;
+ skb_reserve(skb,2);
+ data=(unsigned int *)skb->data;
+ for(i=len;i>0;i-=4) {
+ *(data++)=*(unsigned int *)(ioaddr+FIFO_RX_DATA);
+ }
+ if(i>1) {
+ *((unsigned short *)data++)=*(unsigned short *)(ioaddr+FIFO_RX_DATA);
+ i-=2;
+ }
+ if(i>0) {
+ *(unsigned char *)data=*(unsigned char *)(ioaddr+FIFO_RX_DATA);
+ }
+ skb_put (skb, len);
+ skb->protocol = eth_type_trans (skb, dev);
+
+ netif_rx (skb);
+ dev->last_rx = jiffies;
+ }
+ else {
+ printk (KERN_WARNING
+ "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ xp->stats.rx_dropped++;
+ writel(0,ioaddr+ FIFO_RX_RESET);
+ }
+
+}
+#endif
+
+#ifdef USE_TX_DMA
+static inline void xemac_tx_interrupt(struct net_device *dev,
+ struct xemac_priv *xp,
+ unsigned int ioaddr) {
+
+ int status;
+ int tx_dirty=xp->tx_dirty;
+
+ status=xemac_readl(ioaddr+TX_DMA_ISREG);
+
+ if(status & DMA_IX_PKT_DONE) {
+ while(tx_dirty!=xp->cur_tx) {
+ xp->tx_buffer_descr[tx_dirty].control=DMACR_SG_DISABLE;
+ tx_dirty=(tx_dirty+1)%NUM_TX_DESCR;
+ xp->tx_inuse--;
+ }
+ xemac_writel(DMA_IX_PKT_DONE, ioaddr+TX_DMA_ISREG);
+ }
+
+ if(status & DMA_IX_DMA_ERROR) {
+ printk(KERN_WARNING "%s: TX DMA error\n", dev->name);
+ xemac_hw_start(dev);
+ }
+
+ xp->tx_dirty=tx_dirty;
+
+ if (netif_queue_stopped (dev))
+ netif_wake_queue (dev);
+
+
+}
+#else
+static inline void xemac_tx_fifo_interrupt(struct net_device *dev,
+ struct xemac_priv *xp,
+ unsigned int ioaddr) {
+
+ int status;
+
+ status=xemac_readl(ioaddr+TSR);
+
+ dev_kfree_skb_irq(xp->tx_buffers[xp->cur_tx]);
+ xp->cur_tx=(xp->cur_tx+1) % NUM_TX_BUFFERS;
+ xp->tx_inuse--;
+ if(xp->tx_inuse) {
+ write_tx_fifo(xp->tx_buffers[xp->cur_tx],ioaddr);
+ xemac_writel(xp->tx_buffers[xp->cur_tx]->len,ioaddr + TPLR);
+ }
+
+ if (netif_queue_stopped (dev))
+ netif_wake_queue (dev);
+
+}
+#endif
+
+static inline void xemac_int(struct net_device *dev,
+ struct xemac_priv *xp,
+ unsigned int ioaddr) {
+
+ int status;
+
+ status=xemac_readl(ioaddr+IPIF_IS) & (RECV_DONE_INT | TX_DONE_INT);
+
+ if(status & RECV_DONE_INT) {
+ xemac_rx_fifo_interrupt(dev,xp,ioaddr);
+ xemac_writel(RECV_DONE_INT,ioaddr + IPIF_IS);
+ }
+
+ if(status & TX_DONE_INT) {
+ xemac_tx_fifo_interrupt(dev,xp,ioaddr);
+ xemac_writel(TX_DONE_INT,ioaddr + IPIF_IS);
+ }
+
+ return ;
+
+}
+
+static void xemac_interrupt(int irq, void *dev_instance, struct pt_regs *regs) {
+
+ struct net_device *dev = (struct net_device *) dev_instance;
+ struct xemac_priv *xp=dev->priv;
+ unsigned int ioaddr=dev->base_addr;
+ int status,workcount=max_interrupt_work;
+
+ spin_lock(&xp->lock);
+
+ do {
+ status=xemac_readl(ioaddr+INT_PENDING);
+
+#ifdef USE_RX_DMA
+ if(status & RECV_DMA_INT) {
+ xemac_rx_interrupt(dev,xp,ioaddr);
+ xemac_writel(RECV_DMA_INT, ioaddr+INT_STATUS);
+ }
+#endif
+#ifdef USE_TX_DMA
+ if(status & XMIT_DMA_INT) {
+ xemac_tx_interrupt(dev,xp,ioaddr);
+ xemac_writel(XMIT_DMA_INT, ioaddr+INT_STATUS);
+ }
+#endif
+ if(status & EMAC_INT) {
+ xemac_int(dev,xp,ioaddr);
+ xemac_writel(EMAC_INT, ioaddr+INT_STATUS);
+ }
+
+ if(status & ERROR_INT)
+ xemac_writel(ERROR_INT,ioaddr+INT_STATUS);
+
+ workcount--;
+
+ } while(status && (workcount>0));
+
+ if(workcount<= 0) {
+ printk(KERN_WARNING
+ "%s: Too much work at interrupt, "
+ "status: %08x.\n", dev->name,status);
+ xemac_writel(0xffffffff, ioaddr+INT_STATUS);
+ }
+
+ spin_unlock (&xp->lock);
+
+}
+static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) {
+
+ int rc=0;
+ u16 *data = (u16 *) & rq->ifr_data;
+ struct xemac_priv *xp=dev->priv;
+
+ switch (cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0]=xp->phys[0] & 0x3f;
+ /* Fall Through */
+
+ case SIOCDEVPRIVATE + 1: /* Read the specified MII register. */
+ data[3] = mdio_read (dev, data[0], data[1] & 0x1f);
+ break;
+
+ case SIOCDEVPRIVATE + 2: /* Write the specified MII register */
+ if (!capable (CAP_NET_ADMIN)) {
+ rc = -EPERM;
+ break;
+ }
+
+ mdio_write (dev, data[0], data[1] & 0x1f, data[2]);
+ break;
+
+ default:
+ rc = -EOPNOTSUPP;
+ break;
+
+ }
+
+ return rc;
+}
+
+static inline void xemac_thread_iter(struct net_device *dev, struct xemac_priv *xp) {
+
+ unsigned int ioaddr=dev->base_addr;
+ int mii_lpa;
+
+ mii_lpa=mdio_read (dev, xp->phys[0], MII_LPA);
+ if (!xp->mii.duplex_lock && mii_lpa != 0xffff) {
+
+ int duplex = (mii_lpa & LPA_100FULL) || (mii_lpa & 0x01C0) == 0x0040;
+
+ if(xp->mii.full_duplex != duplex) {
+
+ unsigned int ctrl;
+
+ xp->mii.full_duplex = duplex;
+
+ if(mii_lpa) {
+ printk (KERN_INFO
+ "%s: Setting %s-duplex based on MII #%d link"
+ " partner ability of %4.4x.\n",
+ dev->name, xp->mii.full_duplex ? "full" : "half",
+ xp->phys[0], mii_lpa);
+ } else {
+ printk(KERN_INFO
+ "%s: media is unconnected, link down, or "
+ "incompatible connection\n", dev->name);
+ }
+ ctrl=xemac_readl(ioaddr+ECR);
+ xemac_writel(ctrl & ~(ECR_ENTX | ECR_ENRX),ioaddr+ECR);
+ ctrl&=~ECR_FD;
+ ctrl|=xp->mii.full_duplex ? ECR_FD : 0;
+ xemac_writel(ctrl,ioaddr+ECR);
+ }
+
+ }
+}
+
+static int xemac_thread (void *data) {
+
+ struct net_device *dev = data;
+ struct xemac_priv *xp=dev->priv;
+ unsigned long timeout;
+
+ daemonize();
+ reparent_to_init();
+ spin_lock_irq(¤t->sigmask_lock);
+ sigemptyset(¤t->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+
+ strncpy (current->comm, dev->name, sizeof(current->comm) - 1);
+ current->comm[sizeof(current->comm) - 1] = '\0';
+
+ while(1) {
+ timeout = HZ;
+ do {
+ timeout = interruptible_sleep_on_timeout (&xp->thr_wait, timeout);
+ } while (!signal_pending (current) && (timeout > 0));
+
+ if (signal_pending (current)) {
+ spin_lock_irq(¤t->sigmask_lock);
+ flush_signals(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+ }
+
+ if(xp->time_to_die)
+ break;
+
+ rtnl_lock();
+ xemac_thread_iter(dev, xp);
+ rtnl_unlock();
+ }
+
+ complete_and_exit (&xp->thr_exited, 0);
+}
+
+static int xemac_close(struct net_device *dev) {
+
+ struct xemac_priv *xp=dev->priv;
+ unsigned int ioaddr=dev->base_addr;
+
+ netif_stop_queue (dev);
+
+ spin_lock_irq (&xp->lock);
+
+ xemac_writel(0, ioaddr+INT_MASK);
+ xemac_writel((xemac_readl(ioaddr+ECR) | ECR_RSTTX | ECR_RSTRX) &
+ ~(ECR_ENTX | ECR_ENRX | ECR_ENPHY),
+ ioaddr+ECR);
+
+ spin_unlock_irq (&xp->lock);
+
+ synchronize_irq ();
+
+ consistent_free(xp->rx_buffer_descr);
+ consistent_free(xp->tx_buffer);
+#ifdef USE_RX_DMA
+ consistent_free(xp->tx_buffer_descr);
+#endif
+
+ free_irq (dev->irq, dev);
+
+ return 0;
+}
+
+static int xemac_open(struct net_device *dev) {
+
+ struct xemac_priv *xp=dev->priv;
+ struct rx_buffer_descr *rxb;
+ int retval,i;
+
+#ifdef USE_TX_DMA
+ xp->tx_buffer_descr=consistent_alloc(GFP_KERNEL|GFP_DMA,
+ NUM_TX_DESCR * sizeof(struct tx_buffer_descr),
+ &xp->tx_dma_addr);
+
+ if(!xp->tx_buffer_descr)
+ return -ENOMEM;
+
+ xp->tx_buffer=consistent_alloc(GFP_KERNEL|GFP_DMA,
+ NUM_TX_DESCR * TX_BUF_LEN,
+ &xp->tx_buffer_dma_addr);
+
+ if(!xp->tx_buffer)
+ return -ENOMEM;
+#endif
+
+#ifdef USE_RX_DMA
+ rxb=consistent_alloc(GFP_KERNEL|GFP_DMA,
+ NUM_RX_DESCR * sizeof(struct rx_buffer_descr),
+ &xp->rx_dma_addr);
+
+ if(!rxb)
+ return -ENOMEM;
+
+ xp->rx_buffer_descr=rxb;
+
+ for(i=0;i<NUM_RX_DESCR;i++,rxb++) {
+ struct sk_buff *skb;
+
+ rxb->device_status=0;
+ rxb->control=DMACR_DEST_INCR|DMACR_SOURCE_LOCAL| DMACR_SG_DISABLE | DMACR_LAST_BD;
+ rxb->length=MAX_ETH_FRAME_SIZE;
+ rxb->status=0;
+ rxb->next=virt_to_bus(&xp->rx_buffer_descr[(i+1)%NUM_RX_DESCR]);
+ rxb->flags=0;
+ rxb->req_length=MAX_ETH_FRAME_SIZE;
+ rxb->source=XEMAC_BASE+FIFO_RX_DATA;
+
+ skb=dev_alloc_skb(MAX_ETH_FRAME_SIZE);
+ rxb->skb=skb;
+ dma_cache_inv((unsigned long)skb->data,MAX_ETH_FRAME_SIZE);
+ rxb->destination=0x30000000; /* virt_to_bus(skb->data); */
+
+printk("rxb->destination: %08x, skb->data: %08x\n",rxb->destination, skb->data);
+
+ }
+#endif
+
+ retval=request_irq(dev->irq, xemac_interrupt, SA_SHIRQ, dev->name, dev);
+ if (retval) {
+ return retval;
+ }
+
+ xemac_hw_start(dev);
+
+ xp->time_to_die = 0;
+ xp->thr_pid = kernel_thread (xemac_thread, dev, CLONE_FS | CLONE_FILES);
+ if (xp->thr_pid < 0)
+ printk (KERN_WARNING "%s: unable to start kernel thread\n", dev->name);
+
+ return 0;
+
+}
+
+int __devinit xemac_probe(void) {
+
+ unsigned int ioaddr,ver_major,ver_minor,rev_letter;
+ struct net_device *dev;
+ struct xemac_priv *xp;
+ int err;
+
+
+ if(!request_region(XEMAC_BASE, XEMAC_LEN,"Xilinx Ethernet MAC"))
+ return -EBUSY;
+
+ ioaddr=(unsigned int)ioremap(XEMAC_BASE, XEMAC_LEN);
+
+ if(ioaddr==0) {
+ printk(KERN_ERR "cannot remap xemac MMIO range, aborting\n");
+ release_region(XEMAC_BASE, XEMAC_LEN);
+ return -EIO;
+ }
+
+ dev=alloc_etherdev(sizeof(struct xemac_priv));
+ if(dev==NULL) {
+ release_region(XEMAC_BASE, XEMAC_LEN);
+ return -ENOMEM;
+ }
+
+ xp=dev->priv;
+ dev->open=xemac_open;
+ dev->hard_start_xmit=xemac_start_xmit;
+ dev->stop=xemac_close;
+ dev->get_stats=xemac_get_stats;
+ dev->set_multicast_list=xemac_set_multicast;
+ dev->do_ioctl=mii_ioctl;
+ dev->tx_timeout=xemac_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ dev->irq=XEMAC_IRQ;
+
+ dev->base_addr = (unsigned int) ioaddr;
+
+ xp->phys[0]=0;
+
+ spin_lock_init (&xp->lock);
+ init_waitqueue_head (&xp->thr_wait);
+ init_completion (&xp->thr_exited);
+
+ err=register_netdev(dev);
+ if(err) {
+ release_region(XEMAC_BASE, XEMAC_LEN);
+ kfree(dev);
+ return err;
+ }
+
+ dev->dev_addr[0]=0x00;
+ dev->dev_addr[1]=0xE0;
+ dev->dev_addr[2]=0x29;
+ dev->dev_addr[3]=0x30;
+ dev->dev_addr[4]=0xcc;
+ dev->dev_addr[5]=0xe8;
+
+ ver_major=xemac_readl(ioaddr + EMIR) >> 28;
+ ver_minor=(xemac_readl(ioaddr + EMIR) >> 21) & 0x7f;
+ rev_letter=(xemac_readl(ioaddr + EMIR) >> 16) & 0x1f;
+
+ printk(KERN_INFO "%s: Xilinx Ethernet MAC (Rev %d.%d%c) at 0x%x,"
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+ "IRQ %d\n",
+ dev->name,
+ ver_major,ver_minor,'a'+rev_letter,
+ XEMAC_BASE,
+ dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3],
+ dev->dev_addr[4], dev->dev_addr[5],
+ dev->irq);
+
+ if(media[0] > 0) {
+ xp->medialock=1;
+ xp->duplex=media[0] & 0x20 ? 100 : 10;
+ xp->speed=(media[0] & 0x10) ? 0x0100 : 0;
+
+ printk(KERN_INFO "Forcing %dMbps %s-duplex operation.\n",
+ (xp->speed? 100 : 10),
+ (xp->duplex ? "full" : "half"));
+
+ mdio_write(dev, xp->phys[0], 4, (xp->duplex ? 0x2000 : 0) |
+ (xp->speed ? 0x0100 : 0));
+
+ }
+
+ return 0;
+
+}
+
+module_init(xemac_probe);
+
diff -urN -x CVS -x config -x modules -x mtd -x jffs2 -x jffs linuxppc_2_4_clean/drivers/net/xemac.h linuxppc_2_4_xseg2.new.clean2/drivers/net/xemac.h
--- linuxppc_2_4_clean/drivers/net/xemac.h 1970-01-01 01:00:00.000000000 +0100
+++ linuxppc_2_4_xseg2.new.clean2/drivers/net/xemac.h 2002-09-04 20:57:33.000000000 +0200
@@ -0,0 +1,196 @@
+/*
+ * xemac.h: A driver for Xilinx 10/100Mbit/s ethernet MAC core
+ *
+ * Copyright 2002 Mind NV
+ *
+ * http://www.mind.be/
+ *
+ * Author : Peter De Schrijver (p2 at mind.be)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License (GPL) version 2, incorporated herein by
+ * reference. Drivers based on or derived from this code fall under the GPL
+ * and must retain the authorship, copyright and this license notice. This
+ * file is not a complete program and may only be used when the entire
+ * operating system is licensed under the GPL.
+ *
+ */
+
+#ifndef _XEMAC_H
+#define _XEMAC_H
+
+#include <asm/io.h>
+#include <linux/mii.h>
+
+#define NUM_TX_DESCR 32
+#define NUM_TX_BUFFERS 4
+#define NUM_RX_DESCR 1
+
+#define MAX_ETH_FRAME_SIZE 1536
+#define TX_BUF_LEN MAX_ETH_FRAME_SIZE
+
+/* DMA buffer descriptor */
+
+struct tx_buffer_descr {
+
+ unsigned int device_status;
+ unsigned int control;
+ unsigned int source;
+ unsigned int destination;
+ unsigned int length;
+ unsigned int status;
+ unsigned int next;
+ unsigned char *buffer;
+ unsigned int flags;
+ unsigned int req_length;
+
+};
+
+struct rx_buffer_descr {
+
+ unsigned int device_status;
+ unsigned int control;
+ unsigned int source;
+ unsigned int destination;
+ unsigned int length;
+ unsigned int status;
+ unsigned int next;
+ struct sk_buff *skb;
+ unsigned int flags;
+ unsigned int req_length;
+
+};
+
+struct xemac_priv {
+
+ struct net_device_stats stats;
+ spinlock_t lock;
+ struct tx_buffer_descr *tx_buffer_descr;
+ struct rx_buffer_descr *rx_buffer_descr;
+ struct sk_buff *tx_buffers[NUM_TX_BUFFERS];
+ unsigned char *tx_buffer;
+ unsigned int tx_dma_addr;
+ unsigned int tx_buffer_dma_addr;
+ unsigned int rx_dma_addr;
+ int cur_rx,cur_tx, tx_inuse,tx_dirty;
+ unsigned int medialock:1;
+ unsigned int duplex:1;
+ unsigned int speed:1;
+ struct mii_if_info mii;
+ char phys[1];
+ pid_t thr_pid;
+ wait_queue_head_t thr_wait;
+ struct completion thr_exited;
+ int time_to_die;
+};
+
+
+/* emac core registers */
+
+#define EMIR 0x1100
+
+#define ECR 0x1104
+#define ECR_PROMISC (1<<14)
+#define ECR_MULTI (1<<16)
+#define ECR_FD (1<<31)
+#define ECR_RSTTX (1<<30)
+#define ECR_ENTX (1<<29)
+#define ECR_RSTRX (1<<28)
+#define ECR_ENRX (1<<27)
+#define ECR_ENPHY (1<<26)
+#define ECR_UA (1<<17)
+#define ECR_BA (1<<15)
+#define ECR_TXPAD (1<<25)
+#define ECR_TXFCS (1<<24)
+
+#define SAH 0x110c
+#define SAL 0x1110
+
+#define MGTCR 0x1114
+#define MGTCR_SB (1<<31)
+#define MGTCR_BUSY (1<<31)
+#define MGTCR_RWN (1<<30)
+#define MGTCR_IE (1<<19)
+
+#define MGTDR 0x1118
+
+#define RPLR 0x111C
+
+#define TPLR 0x1120
+
+#define TSR 0x1124
+
+/* emac ipif registers */
+
+#define IPIF_IS 0x20
+#define IPIF_IE 0x28
+
+#define RECV_DONE_INT 0x2
+#define TX_DONE_INT 0x1
+
+/* interrupt registers */
+
+#define INT_STATUS 0
+
+#define INT_PENDING 4
+
+#define INT_MASK 8
+
+#define INT_GLOBAL 0x1c
+
+#define SEND_FIFO_INT 0x00000020
+#define RECV_FIFO_INT 0x00000020
+#define RECV_DMA_INT 0x00000010
+#define XMIT_DMA_INT 0x00000008
+#define EMAC_INT 0x00000004
+#define ERROR_INT 0x00000001
+
+/* TX and RX FIFO registers */
+
+#define FIFO_RX_RESET 0x2010
+
+#define FIFO_TX_DATA 0x2100
+#define FIFO_RX_DATA 0x2200
+
+/* TX and RX DMA registers */
+
+#define TX_RESET_REG 0x2300
+
+#define TX_SWCR_REG 0x231c
+#define RX_SWCR_REG 0x235c
+#define SWCR_SG_ENABLE 0x80000000
+
+#define TX_DMAC_REG 0x2304
+#define RX_DMAC_REG 0x2344
+#define DMACR_SOURCE_INCR 0x80000000
+#define DMACR_DEST_INCR 0x40000000
+#define DMACR_SOURCE_LOCAL 0x20000000
+#define DMACR_DEST_LOCAL 0x10000000
+#define DMACR_SG_DISABLE 0x08000000
+#define DMACR_GEN_BD_INTR 0x04000000
+#define DMACR_LAST_BD 0x02000000
+
+#define TX_DMAS_REG 0x2314
+#define RX_DMAS_REG 0x2354
+#define DMASR_SG_BUSY 0x08000000
+
+#define TX_DMA_BDA 0x2318
+#define RX_DMA_BDA 0x2358
+
+#define TX_DMA_ISREG 0x232C
+#define RX_DMA_ISREG 0x236C
+#define TX_DMA_IEREG 0x2330
+#define RX_DMA_IEREG 0x2370
+
+#define DMA_IX_DMA_DONE 1
+#define DMA_IX_DMA_ERROR 2
+#define DMA_IX_PKT_DONE 4
+#define DMA_IX_PKT_THRESHOLD 8
+#define DMA_IX_PKT_WAITBOUND 16
+#define DMA_IX_SG_DISABLE_ACK 32
+#define DMA_IX_SG_END 64
+#define DMA_IX_BD_DONE 128
+
+#define RX_DMA_PKTCNT 0x2360
+
+#endif
More information about the Linuxppc-embedded
mailing list