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