diff options
| author | Ralf Bächle <ralf@linux-mips.org> | 2003-06-25 08:56:47 -0400 |
|---|---|---|
| committer | Ralf Bächle <ralf@linux-mips.org> | 2003-06-25 08:56:47 -0400 |
| commit | e3e33952885ee80865b86f1f63d43713b5bfb5ba (patch) | |
| tree | 6bd276107dc28698ec5faf8ae0fa8edec151c887 /drivers | |
| parent | 6acd6bab81ceb8f350bcf21bd5262381522583d5 (diff) | |
[netdrvr] add driver "meth", for SGI O2 MACE fast eth
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/net/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/net/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/meth.c | 862 | ||||
| -rw-r--r-- | drivers/net/meth.h | 273 |
4 files changed, 1140 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index b1c87711705f..6e9ed10ae2da 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -511,6 +511,10 @@ config SGI_IOC3_ETH the Ethernet-HOWTO, available from <http://www.tldp.org/docs.html#howto>. +config SGI_O2MACE_ETH + tristate "SGI O2 MACE Fast Ethernet support" + depends on NET_ETHERNET && SGI_IP32=y + config STNIC tristate "National DP83902AV support" depends on NET_ETHERNET && SUPERH diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 4a1b01882805..d841e5768c3e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -117,6 +117,7 @@ obj-$(CONFIG_SUN3_82586) += sun3_82586.o obj-$(CONFIG_SUN3LANCE) += sun3lance.o obj-$(CONFIG_DEFXX) += defxx.o obj-$(CONFIG_SGISEEQ) += sgiseeq.o +obj-$(CONFIG_SGI_O2MACE_ETH) += meth.o obj-$(CONFIG_AT1700) += at1700.o obj-$(CONFIG_FMV18X) += fmv18x.o obj-$(CONFIG_EL1) += 3c501.o diff --git a/drivers/net/meth.c b/drivers/net/meth.c new file mode 100644 index 000000000000..eca8974e91df --- /dev/null +++ b/drivers/net/meth.c @@ -0,0 +1,862 @@ +/* + * meth.c -- O2 Builtin 10/100 Ethernet driver + * + * Copyright (C) 2001 Ilya Volynets + * + * 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/module.h> +#include <linux/init.h> + +#include <linux/sched.h> +#include <linux/kernel.h> /* printk() */ +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/interrupt.h> /* mark_bh */ +#include <linux/pci.h> + +#include <linux/in.h> +#include <linux/netdevice.h> /* struct device, and other headers */ +#include <linux/etherdevice.h> /* eth_type_trans */ +#include <linux/ip.h> /* struct iphdr */ +#include <linux/tcp.h> /* struct tcphdr */ +#include <linux/skbuff.h> +#include <linux/mii.h> /*MII definitions */ + +#include <asm/ip32/crime.h> +#include <asm/ip32/mace.h> +#include <asm/ip32/ip32_ints.h> + +#include "meth.h" + +#include <linux/in6.h> +#include <asm/checksum.h> + +#ifndef MFE_DEBUG +#define MFE_DEBUG 0 +#endif + +#if MFE_DEBUG>=1 +#define DPRINTK(str,args...) printk (KERN_DEBUG "meth(%ld): %s: " str, jiffies, __FUNCTION__ , ## args) +#define MFE_RX_DEBUG 2 +#else +#define DPRINTK(str,args...) +#define MFE_RX_DEBUG 0 +#endif + + +static const char *version="meth.c: Ilya Volynets (ilya@theIlya.com)"; +static const char *meth_str="SGI O2 Fast Ethernet"; +MODULE_AUTHOR("Ilya Volynets"); +MODULE_DESCRIPTION("SGI O2 Builtin Fast Ethernet driver"); + +/* This is a load-time options */ +/*static int eth = 0; +MODULE_PARM(eth, "i");*/ + +#define HAVE_TX_TIMEOUT +/* The maximum time waited (in jiffies) before assuming a Tx failed. (400ms) */ +#define TX_TIMEOUT (400*HZ/1000) + +#ifdef HAVE_TX_TIMEOUT +static int timeout = TX_TIMEOUT; +MODULE_PARM(timeout, "i"); +#endif + +int meth_eth; + +/* + * This structure is private to each device. It is used to pass + * packets in and out, so there is place for a packet + */ + +typedef struct meth_private { + struct net_device_stats stats; + volatile struct meth_regs *regs; + u64 mode; /* in-memory copy of MAC control register */ + int phy_addr; /* address of phy, used by mdio_* functions, initialized in mdio_probe*/ + tx_packet *tx_ring; + dma_addr_t tx_ring_dma; + int free_space; + struct sk_buff *tx_skbs[TX_RING_ENTRIES]; + dma_addr_t tx_skb_dmas[TX_RING_ENTRIES]; + int tx_read,tx_write; + int tx_count; + + rx_packet *rx_ring[RX_RING_ENTRIES]; + dma_addr_t rx_ring_dmas[RX_RING_ENTRIES]; + int rx_write; + + spinlock_t meth_lock; +} meth_private; + +extern struct net_device meth_devs[]; +void meth_tx_timeout (struct net_device *dev); +void meth_interrupt(int irq, void *dev_id, struct pt_regs *pregs); + +/* global, initialized in ip32-setup.c */ +char o2meth_eaddr[8]={0,0,0,0,0,0,0,0}; + +static inline void load_eaddr(struct net_device *dev, + volatile struct meth_regs *regs) +{ + int i; + DPRINTK("Loading MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n", + (int)o2meth_eaddr[0]&0xFF,(int)o2meth_eaddr[1]&0xFF,(int)o2meth_eaddr[2]&0xFF, + (int)o2meth_eaddr[3]&0xFF,(int)o2meth_eaddr[4]&0xFF,(int)o2meth_eaddr[5]&0xFF); + //memcpy(dev->dev_addr,o2meth_eaddr+2,6); + for (i=0; i<6; i++) + dev->dev_addr[i]=o2meth_eaddr[i]; + regs->mac_addr= //dev->dev_addr[0]|(dev->dev_addr[1]<<8)| + //dev->dev_addr[2]<<16|(dev->dev_addr[3]<<24)| + //dev->dev_addr[4]<<32|(dev->dev_addr[5]<<40); + (*(u64*)o2meth_eaddr)>>16; + DPRINTK("MAC, finally is %0lx\n",regs->mac_addr); +} + +/* + *Waits for BUSY status of mdio bus to clear + */ +#define WAIT_FOR_PHY(___regs, ___rval) \ + while((___rval=___regs->phy_data)&MDIO_BUSY){ \ + udelay(25); \ + } +/*read phy register, return value read */ +static int mdio_read(meth_private *priv,int phyreg) +{ + volatile meth_regs* regs=priv->regs; + volatile u32 rval; + WAIT_FOR_PHY(regs,rval) + regs->phy_registers=(priv->phy_addr<<5)|(phyreg&0x1f); + udelay(25); + regs->phy_trans_go=1; + udelay(25); + WAIT_FOR_PHY(regs,rval) + return rval&MDIO_DATA_MASK; +} + +/*write phy register */ +static void mdio_write(meth_private* priv,int pfyreg,int val) +{ + volatile meth_regs* regs=priv->regs; + int rval; +/// DPRINTK("Trying to write value %i to reguster %i\n",val, pfyreg); + spin_lock_irq(&priv->meth_lock); + WAIT_FOR_PHY(regs,rval) + regs->phy_registers=(priv->phy_addr<<5)|(pfyreg&0x1f); + regs->phy_data=val; + udelay(25); + WAIT_FOR_PHY(regs,rval) + spin_unlock_irq(&priv->meth_lock); +} + +/* Modify phy register using given mask and value */ +static void mdio_update(meth_private* priv,int phyreg, int val, int mask) +{ + int rval; + DPRINTK("RMW value %i to PHY register %i with mask %i\n",val,phyreg,mask); + rval=mdio_read(priv,phyreg); + rval=(rval&~mask)|(val&mask); + mdio_write(priv,phyreg,rval); +} + +/* handle errata data on MDIO bus */ +//static void mdio_errata(meth_private *priv) +//{ + /* Hmmm... what the hell is phyerrata? does it come from sys init parameters in IRIX */ +//} +static int mdio_probe(meth_private *priv) +{ + int i, p2, p3; + DPRINTK("Detecting PHY kind\n"); + /* check if phy is detected already */ + if(priv->phy_addr>=0&&priv->phy_addr<32) + return 0; + spin_lock_irq(&priv->meth_lock); + for (i=0;i<32;++i){ + priv->phy_addr=(char)i; + p2=mdio_read(priv,2); +#ifdef MFE_DEBUG + p3=mdio_read(priv,3); + switch ((p2<<12)|(p3>>4)){ + case PHY_QS6612X: + DPRINTK("PHY is QS6612X\n"); + break; + case PHY_ICS1889: + DPRINTK("PHY is ICS1889\n"); + break; + case PHY_ICS1890: + DPRINTK("PHY is ICS1890\n"); + break; + case PHY_DP83840: + DPRINTK("PHY is DP83840\n"); + break; + } +#endif + if(p2!=0xffff&&p2!=0x0000){ + DPRINTK("PHY code: %x\n",(p2<<12)|(p3>>4)); + break; + } + } + spin_unlock_irq(&priv->meth_lock); + if(priv->phy_addr<32) { + return 0; + } + DPRINTK("Oopsie! PHY is not known!\n"); + priv->phy_addr=-1; + return -ENODEV; +} + +static void meth_check_link(struct net_device *dev) +{ + struct meth_private *priv = (struct meth_private *) dev->priv; + int mii_partner = mdio_read(priv, 5); + int mii_advertising = mdio_read(priv, 4); + int negotiated = mii_advertising & mii_partner; + int duplex, speed; + + if (mii_partner == 0xffff) + return; + + duplex = ((negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040)?METH_PHY_FDX:0; + speed = (negotiated & 0x0380)?METH_100MBIT:0; + + if ((priv->mode & METH_PHY_FDX) ^ duplex) + { + DPRINTK("Setting %s-duplex\n", duplex ? "full" : "half"); + if (duplex) + priv->mode |= METH_PHY_FDX; + else + priv->mode &= ~METH_PHY_FDX; + priv->regs->mac_ctrl = priv->mode; + } + + if ((priv->mode & METH_100MBIT) ^ speed) + { + DPRINTK("Setting %dMbs mode\n", speed ? 100 : 10); + if (duplex) + priv->mode |= METH_100MBIT; + else + priv->mode &= ~METH_100MBIT; + priv->regs->mac_ctrl = priv->mode; + } +} + + +static int meth_init_tx_ring(meth_private *priv) +{ + /* Init TX ring */ + DPRINTK("Initializing TX ring\n"); + priv->tx_ring = (tx_packet *) pci_alloc_consistent(NULL,TX_RING_BUFFER_SIZE,&(priv->tx_ring_dma)); + if(!priv->tx_ring) + return -ENOMEM; + memset(priv->tx_ring, 0, TX_RING_BUFFER_SIZE); + priv->tx_count = priv->tx_read = priv->tx_write = 0; + priv->regs->tx_ring_base = priv->tx_ring_dma; + priv->free_space = TX_RING_ENTRIES; + /* Now init skb save area */ + memset(priv->tx_skbs,0,sizeof(priv->tx_skbs)); + memset(priv->tx_skb_dmas,0,sizeof(priv->tx_skb_dmas)); + DPRINTK("Done with TX ring init\n"); + return 0; +} + +static int meth_init_rx_ring(meth_private *priv) +{ + int i; + DPRINTK("Initializing RX ring\n"); + for(i=0;i<RX_RING_ENTRIES;i++){ + DPRINTK("\t1:\t%i\t",i); + /*if(!(priv->rx_ring[i]=get_free_page(GFP_KERNEL))) + return -ENOMEM; + DPRINTK("\t2:\t%i\n",i);*/ + priv->rx_ring[i]=(rx_packet*)pci_alloc_consistent(NULL,METH_RX_BUFF_SIZE,&(priv->rx_ring_dmas[i])); + /* I'll need to re-sync it after each RX */ + DPRINTK("\t%p\n",priv->rx_ring[i]); + priv->regs->rx_fifo=priv->rx_ring_dmas[i]; + } + priv->rx_write = 0; + DPRINTK("Done with RX ring\n"); + return 0; +} +static void meth_free_tx_ring(meth_private *priv) +{ + int i; + + /* Remove any pending skb */ + for (i = 0; i < TX_RING_ENTRIES; i++) { + if (priv->tx_skbs[i]) + dev_kfree_skb(priv->tx_skbs[i]); + priv->tx_skbs[i] = NULL; + } + pci_free_consistent(NULL, + TX_RING_BUFFER_SIZE, + priv->tx_ring, + priv->tx_ring_dma); +} +static void meth_free_rx_ring(meth_private *priv) +{ + int i; + + for(i=0;i<RX_RING_ENTRIES;i++) + pci_free_consistent(NULL, + METH_RX_BUFF_SIZE, + priv->rx_ring[i], + priv->rx_ring_dmas[i]); +} + +int meth_reset(struct net_device *dev) +{ + struct meth_private *priv = (struct meth_private *) dev->priv; + + /* Reset card */ + priv->regs->mac_ctrl = SGI_MAC_RESET; + priv->regs->mac_ctrl = 0; + udelay(25); + DPRINTK("MAC control after reset: %016lx\n", priv->regs->mac_ctrl); + + /* Load ethernet address */ + load_eaddr(dev, priv->regs); + /* Should load some "errata", but later */ + + /* Check for device */ + if(mdio_probe(priv) < 0) { + DPRINTK("Unable to find PHY\n"); + return -ENODEV; + } + + /* Initial mode -- 10|Half-duplex|Accept normal packets */ + priv->mode=METH_ACCEPT_MCAST|METH_DEFAULT_IPG; + if(dev->flags | IFF_PROMISC) + priv->mode |= METH_PROMISC; + priv->regs->mac_ctrl = priv->mode; + + /* Autonegociate speed and duplex mode */ + meth_check_link(dev); + + /* Now set dma control, but don't enable DMA, yet */ + priv->regs->dma_ctrl= (4 << METH_RX_OFFSET_SHIFT) | + (RX_RING_ENTRIES << METH_RX_DEPTH_SHIFT); + + return(0); +} + +/*============End Helper Routines=====================*/ + +/* + * Open and close + */ + +int meth_open(struct net_device *dev) +{ + meth_private *priv=dev->priv; + volatile meth_regs *regs=priv->regs; + + MOD_INC_USE_COUNT; + + /* Start DMA */ + regs->dma_ctrl|= + METH_DMA_TX_EN|/*METH_DMA_TX_INT_EN|*/ + METH_DMA_RX_EN|METH_DMA_RX_INT_EN; + + if(request_irq(dev->irq,meth_interrupt,SA_SHIRQ,meth_str,dev)){ + printk(KERN_ERR "%s: Can't get irq %d\n", dev->name, dev->irq); + return -EAGAIN; + } + netif_start_queue(dev); + DPRINTK("Opened... DMA control=0x%08lx\n", regs->dma_ctrl); + return 0; +} + +int meth_release(struct net_device *dev) +{ + netif_stop_queue(dev); /* can't transmit any more */ + /* shut down dma */ + ((meth_private*)(dev->priv))->regs->dma_ctrl&= + ~(METH_DMA_TX_EN|METH_DMA_TX_INT_EN| + METH_DMA_RX_EN|METH_DMA_RX_INT_EN); + free_irq(dev->irq, dev); + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Configuration changes (passed on by ifconfig) + */ +int meth_config(struct net_device *dev, struct ifmap *map) +{ + if (dev->flags & IFF_UP) /* can't act on a running interface */ + return -EBUSY; + + /* Don't allow changing the I/O address */ + if (map->base_addr != dev->base_addr) { + printk(KERN_WARNING "meth: Can't change I/O address\n"); + return -EOPNOTSUPP; + } + + /* Allow changing the IRQ */ + if (map->irq != dev->irq) { + printk(KERN_WARNING "meth: Can't change IRQ\n"); + return -EOPNOTSUPP; + } + DPRINTK("Configured\n"); + + /* ignore other fields */ + return 0; +} + +/* + * Receive a packet: retrieve, encapsulate and pass over to upper levels + */ +void meth_rx(struct net_device* dev) +{ + struct sk_buff *skb; + struct meth_private *priv = (struct meth_private *) dev->priv; + rx_packet *rxb; + DPRINTK("RX...\n"); + // TEMP while((rxb=priv->rx_ring[priv->rx_write])->status.raw&0x8000000000000000){ + while((rxb=priv->rx_ring[priv->rx_write])->status.raw&0x8000000000000000){ + int len=rxb->status.parsed.rx_len - 4; /* omit CRC */ + DPRINTK("(%i)\n",priv->rx_write); + /* length sanity check */ + if(len < 60 || len > 1518) { + printk(KERN_DEBUG "%s: bogus packet size: %d, status=%#2x.\n", + dev->name, priv->rx_write, rxb->status.raw); + priv->stats.rx_errors++; + priv->stats.rx_length_errors++; + } + if(!(rxb->status.raw&METH_RX_STATUS_ERRORS)){ + skb=alloc_skb(len+2,GFP_ATOMIC);/* Should be atomic -- we are in interrupt */ + if(!skb){ + /* Ouch! No memory! Drop packet on the floor */ + DPRINTK("!!!>>>Ouch! Not enough Memory for RX buffer!\n"); + priv->stats.rx_dropped++; + } else { + skb_reserve(skb, 2); /* align IP on 16B boundary */ + memcpy(skb_put(skb, len), rxb->buf, len); + /* Write metadata, and then pass to the receive level */ + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + //skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + + DPRINTK("passing packet\n"); + DPRINTK("len = %d rxb->status = %x\n", + len, rxb->status.raw); + netif_rx(skb); + dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes+=len; + DPRINTK("There we go... Whew...\n"); + } + } + priv->regs->rx_fifo=priv->rx_ring_dmas[priv->rx_write]; + rxb->status.raw=0; + priv->rx_write=(priv->rx_write+1)&(RX_RING_ENTRIES-1); + } +} + +static int meth_tx_full(struct net_device *dev) +{ + struct meth_private *priv = (struct meth_private *) dev->priv; + + return(priv->tx_count >= TX_RING_ENTRIES-1); +} + +void meth_tx_cleanup(struct net_device* dev, int rptr) +{ + meth_private *priv=dev->priv; + tx_packet* status; + struct sk_buff *skb; + + spin_lock(&priv->meth_lock); + + /* Stop DMA */ + priv->regs->dma_ctrl &= ~(METH_DMA_TX_INT_EN); + + while(priv->tx_read != rptr){ + skb = priv->tx_skbs[priv->tx_read]; + status = &priv->tx_ring[priv->tx_read]; + if(!status->header.res.sent) + break; + if(status->header.raw & METH_TX_STATUS_DONE) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + } + dev_kfree_skb_irq(skb); + priv->tx_skbs[priv->tx_read] = NULL; + status->header.raw = 0; + priv->tx_read = (priv->tx_read+1)&(TX_RING_ENTRIES-1); + priv->tx_count --; + } + + /* wake up queue if it was stopped */ + if (netif_queue_stopped(dev) && ! meth_tx_full(dev)) { + netif_wake_queue(dev); + } + + spin_unlock(priv->meth_lock); +} + +/* + * The typical interrupt entry point + */ +void meth_interrupt(int irq, void *dev_id, struct pt_regs *pregs) +{ + struct meth_private *priv; + union { + u32 reg; /*Whole status register */ + struct { + u32 : 2, + rx_seq : 5, + tx_read : 9, + + rx_read : 8, + int_mask: 8; + } parsed; + } status; + /* + * As usual, check the "device" pointer for shared handlers. + * Then assign "struct device *dev" + */ + struct net_device *dev = (struct net_device *)dev_id; + /* ... and check with hw if it's really ours */ + + if (!dev /*paranoid*/ ) return; + + /* Lock the device */ + priv = (struct meth_private *) dev->priv; + + status.reg = priv->regs->int_flags; + + DPRINTK("Interrupt, status %08x...\n",status.reg); + if (status.parsed.int_mask & METH_INT_RX_THRESHOLD) { + /* send it to meth_rx for handling */ + meth_rx(dev); + } + + if (status.parsed.int_mask & (METH_INT_TX_EMPTY|METH_INT_TX_PKT)) { + /* a transmission is over: free the skb */ + meth_tx_cleanup(dev, status.parsed.tx_read); + } + /* check for errors too... */ + if (status.parsed.int_mask & (METH_INT_TX_LINK_FAIL)) + printk(KERN_WARNING "meth: link failure\n"); + if (status.parsed.int_mask & (METH_INT_MEM_ERROR)) + printk(KERN_WARNING "meth: memory error\n"); + if (status.parsed.int_mask & (METH_INT_TX_ABORT)) + printk(KERN_WARNING "meth: aborted\n"); + DPRINTK("Interrupt handling done...\n"); + + priv->regs->int_flags=status.reg&0xff; /* clear interrupts */ +} + +/* + * Transmits packets that fit into TX descriptor (are <=120B) + */ +static void meth_tx_short_prepare(meth_private* priv, struct sk_buff* skb) +{ + tx_packet *desc=&priv->tx_ring[priv->tx_write]; + int len = (skb->len<ETH_ZLEN)?ETH_ZLEN:skb->len; + + DPRINTK("preparing short packet\n"); + /* maybe I should set whole thing to 0 first... */ + memcpy(desc->data.dt+(120-len),skb->data,skb->len); + if(skb->len < len) + memset(desc->data.dt+120-len+skb->len,0,len-skb->len); + desc->header.raw=METH_TX_CMD_INT_EN|(len-1)|((128-len)<<16); + DPRINTK("desc=%016lx\n",desc->header.raw); +} +#define TX_CATBUF1 BIT(25) +static void meth_tx_1page_prepare(meth_private* priv, struct sk_buff* skb) +{ + tx_packet *desc=&priv->tx_ring[priv->tx_write]; + void *buffer_data = (void *)(((u64)skb->data + 7ULL) & (~7ULL)); + int unaligned_len = (int)((u64)buffer_data - (u64)skb->data); + int buffer_len = skb->len - unaligned_len; + dma_addr_t catbuf; + + DPRINTK("preparing 1 page...\n"); + DPRINTK("length=%d data=%p\n", skb->len, skb->data); + DPRINTK("unaligned_len=%d\n", unaligned_len); + DPRINTK("buffer_data=%p buffer_len=%d\n", + buffer_data, + buffer_len); + + desc->header.raw=METH_TX_CMD_INT_EN|TX_CATBUF1|(skb->len-1); + + /* unaligned part */ + if(unaligned_len){ + memcpy(desc->data.dt+(120-unaligned_len), + skb->data, unaligned_len); + desc->header.raw |= (128-unaligned_len) << 16; + } + + /* first page */ + catbuf = pci_map_single(NULL, + buffer_data, + buffer_len, + PCI_DMA_TODEVICE); + DPRINTK("catbuf=%x\n", catbuf); + desc->data.cat_buf[0].form.start_addr = catbuf >> 3; + desc->data.cat_buf[0].form.len = buffer_len-1; + DPRINTK("desc=%016lx\n",desc->header.raw); + DPRINTK("cat_buf[0].raw=%016lx\n",desc->data.cat_buf[0].raw); +} +#define TX_CATBUF2 BIT(26) +static void meth_tx_2page_prepare(meth_private* priv, struct sk_buff* skb) +{ + tx_packet *desc=&priv->tx_ring[priv->tx_write]; + void *buffer1_data = (void *)(((u64)skb->data + 7ULL) & (~7ULL)); + void *buffer2_data = (void *)PAGE_ALIGN((u64)skb->data); + int unaligned_len = (int)((u64)buffer1_data - (u64)skb->data); + int buffer1_len = (int)((u64)buffer2_data - (u64)buffer1_data); + int buffer2_len = skb->len - buffer1_len - unaligned_len; + dma_addr_t catbuf1, catbuf2; + + DPRINTK("preparing 2 pages... \n"); + DPRINTK("length=%d data=%p\n", skb->len, skb->data); + DPRINTK("unaligned_len=%d\n", unaligned_len); + DPRINTK("buffer1_data=%p buffer1_len=%d\n", + buffer1_data, + buffer1_len); + DPRINTK("buffer2_data=%p buffer2_len=%d\n", + buffer2_data, + buffer2_len); + + desc->header.raw=METH_TX_CMD_INT_EN|TX_CATBUF1|TX_CATBUF2|(skb->len-1); + /* unaligned part */ + if(unaligned_len){ + memcpy(desc->data.dt+(120-unaligned_len), + skb->data, unaligned_len); + desc->header.raw |= (128-unaligned_len) << 16; + } + + /* first page */ + catbuf1 = pci_map_single(NULL, + buffer1_data, + buffer1_len, + PCI_DMA_TODEVICE); + DPRINTK("catbuf1=%x\n", catbuf1); + desc->data.cat_buf[0].form.start_addr = catbuf1 >> 3; + desc->data.cat_buf[0].form.len = buffer1_len-1; + /* second page */ + catbuf2 = pci_map_single(NULL, + buffer2_data, + buffer2_len, + PCI_DMA_TODEVICE); + DPRINTK("catbuf2=%x\n", catbuf2); + desc->data.cat_buf[1].form.start_addr = catbuf2 >> 3; + desc->data.cat_buf[1].form.len = buffer2_len-1; + DPRINTK("desc=%016lx\n",desc->header.raw); + DPRINTK("cat_buf[0].raw=%016lx\n",desc->data.cat_buf[0].raw); + DPRINTK("cat_buf[1].raw=%016lx\n",desc->data.cat_buf[1].raw); +} + + +void meth_add_to_tx_ring(meth_private *priv, struct sk_buff* skb) +{ + DPRINTK("Transmitting data...\n"); + if(skb->len <= 120) { + /* Whole packet fits into descriptor */ + meth_tx_short_prepare(priv,skb); + } else if(PAGE_ALIGN((u64)skb->data) != + PAGE_ALIGN((u64)skb->data+skb->len-1)) { + /* Packet crosses page boundary */ + meth_tx_2page_prepare(priv,skb); + } else { + /* Packet is in one page */ + meth_tx_1page_prepare(priv,skb); + } + + /* Remember the skb, so we can free it at interrupt time */ + priv->tx_skbs[priv->tx_write] = skb; + priv->tx_write = (priv->tx_write+1) & (TX_RING_ENTRIES-1); + priv->regs->tx_info.wptr = priv->tx_write; + priv->tx_count ++; + /* Enable DMA transfer */ + priv->regs->dma_ctrl |= METH_DMA_TX_INT_EN; +} + +/* + * Transmit a packet (called by the kernel) + */ +int meth_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct meth_private *priv = (struct meth_private *) dev->priv; + + spin_lock_irq(&priv->meth_lock); + + meth_add_to_tx_ring(priv, skb); + dev->trans_start = jiffies; /* save the timestamp */ + + /* If TX ring is full, tell the upper layer to stop sending packets */ + if (meth_tx_full(dev)) { + DPRINTK("TX full: stopping\n"); + netif_stop_queue(dev); + } + + spin_unlock_irq(&priv->meth_lock); + + return 0; +} + +/* + * Deal with a transmit timeout. + */ + +void meth_tx_timeout (struct net_device *dev) +{ + struct meth_private *priv = (struct meth_private *) dev->priv; + + printk(KERN_WARNING "%s: transmit timed out\n", dev->name); + + /* Protect against concurrent rx interrupts */ + spin_lock_irq(&priv->meth_lock); + + /* Try to reset the adaptor. */ + meth_reset(dev); + + priv->stats.tx_errors++; + + /* Clear all rings */ + meth_free_tx_ring(priv); + meth_free_rx_ring(priv); + meth_init_tx_ring(priv); + meth_init_rx_ring(priv); + + /* Restart dma */ + priv->regs->dma_ctrl|=METH_DMA_TX_EN|METH_DMA_RX_EN|METH_DMA_RX_INT_EN; + + /* Enable interrupt */ + spin_unlock_irq(&priv->meth_lock); + + dev->trans_start = jiffies; + netif_wake_queue(dev); + + return; +} + +/* + * Ioctl commands + */ +int meth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + + DPRINTK("ioctl\n"); + return 0; +} + +/* + * Return statistics to the caller + */ +struct net_device_stats *meth_stats(struct net_device *dev) +{ + struct meth_private *priv = (struct meth_private *) dev->priv; + return &priv->stats; +} + +/* + * The init function (sometimes called probe). + * It is invoked by register_netdev() + */ +int meth_init(struct net_device *dev) +{ + meth_private *priv; + int ret; + /* + * Then, assign other fields in dev, using ether_setup() and some + * hand assignments + */ + ether_setup(dev); /* assign some of the fields */ + + dev->open = meth_open; + dev->stop = meth_release; + dev->set_config = meth_config; + dev->hard_start_xmit = meth_tx; + dev->do_ioctl = meth_ioctl; + dev->get_stats = meth_stats; +#ifdef HAVE_TX_TIMEOUT + dev->tx_timeout = meth_tx_timeout; + dev->watchdog_timeo = timeout; +#endif + dev->irq = MACE_ETHERNET_IRQ; + SET_MODULE_OWNER(dev); + + /* + * Then, allocate the priv field. This encloses the statistics + * and a few private fields. + */ + priv = kmalloc(sizeof(struct meth_private), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + dev->priv=priv; + memset(priv, 0, sizeof(struct meth_private)); + spin_lock_init(&((struct meth_private *) dev->priv)->meth_lock); + /* + * Make the usual checks: check_region(), probe irq, ... -ENODEV + * should be returned if no device found. No resource should be + * grabbed: this is done on open(). + */ + priv->regs=(meth_regs*)SGI_MFE; + dev->base_addr=SGI_MFE; + priv->phy_addr = -1; /* No phy is known yet... */ + + /* Initialize the hardware */ + if((ret=meth_reset(dev)) < 0) + return ret; + + /* Allocate the ring buffers */ + if((ret=meth_init_tx_ring(priv))<0||(ret=meth_init_rx_ring(priv))<0){ + meth_free_tx_ring(priv); + meth_free_rx_ring(priv); + return ret; + } + + printk("SGI O2 Fast Ethernet rev. %ld\n", priv->regs->mac_ctrl >> 29); + + return 0; +} + +/* + * The devices + */ + +struct net_device meth_devs[1] = { + { init: meth_init, } /* init, nothing more */ +}; + +/* + * Finally, the module stuff + */ + +int meth_init_module(void) +{ + int result, device_present = 0; + + strcpy(meth_devs[0].name, "eth%d"); + + if ( (result = register_netdev(meth_devs)) ) + printk("meth: error %i registering device \"%s\"\n", + result, meth_devs->name); + else device_present++; +#ifndef METH_DEBUG + EXPORT_NO_SYMBOLS; +#endif + + return device_present ? 0 : -ENODEV; +} + +void meth_cleanup(void) +{ + kfree(meth_devs->priv); + unregister_netdev(meth_devs); + return; +} + +module_init(meth_init_module); +module_exit(meth_cleanup); diff --git a/drivers/net/meth.h b/drivers/net/meth.h new file mode 100644 index 000000000000..afff85da63ea --- /dev/null +++ b/drivers/net/meth.h @@ -0,0 +1,273 @@ + +/* + * snull.h -- definitions for the network module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +/* version dependencies have been confined to a separate file */ + +#define SGI_MFE (MACE_BASE+MACE_ENET) +/* (0xBF280000)*/ + +/* Tunable parameters */ +#define TX_RING_ENTRIES 64 /* 64-512?*/ + +#define RX_RING_ENTRIES 16 /* Do not change */ +/* Internal constants */ +#define TX_RING_BUFFER_SIZE (TX_RING_ENTRIES*sizeof(tx_packet)) +#define RX_BUFFER_SIZE 1546 /* ethenet packet size */ +#define METH_RX_BUFF_SIZE 4096 +#define RX_BUFFER_OFFSET (sizeof(rx_status_vector)+2) /* staus vector + 2 bytes of padding */ +#define RX_BUCKET_SIZE 256 + + + +/* For more detailed explanations of what each field menas, + see Nick's great comments to #defines below (or docs, if + you are lucky enough toget hold of them :)*/ + +/* tx status vector is written over tx command header upon + dma completion. */ + +typedef struct tx_status_vector { + u64 sent:1; /* always set to 1...*/ + u64 pad0:34;/* always set to 0 */ + u64 flags:9; /*I'm too lazy to specify each one separately at the moment*/ + u64 col_retry_cnt:4; /*collision retry count*/ + u64 len:16; /*Transmit length in bytes*/ +} tx_status_vector; + +/* + * Each packet is 128 bytes long. + * It consists of header, 0-3 concatination + * buffer pointers and up to 120 data bytes. + */ +typedef struct tx_packet_hdr { + u64 pad1:36; /*should be filled with 0 */ + u64 cat_ptr3_valid:1, /*Concatination pointer valid flags*/ + cat_ptr2_valid:1, + cat_ptr1_valid:1; + u64 tx_int_flag:1; /*Generate TX intrrupt when packet has been sent*/ + u64 term_dma_flag:1; /*Terminate transmit DMA on transmit abort conditions*/ + u64 data_offset:7; /*Starting byte offset in ring data block*/ + u64 data_len:16; /*Length of valid data in bytes-1*/ +} tx_packet_hdr; +typedef union tx_cat_ptr { + struct { + u64 pad2:16; /* should be 0 */ + u64 len:16; /*length of buffer data - 1*/ + u64 start_addr:29; /*Physical starting address*/ + u64 pad1:3; /* should be zero */ + } form; + u64 raw; +} tx_cat_ptr; + +typedef struct tx_packet { + union { + tx_packet_hdr header; + tx_status_vector res; + u64 raw; + }header; + union { + tx_cat_ptr cat_buf[3]; + char dt[120]; + } data; +} tx_packet; + +typedef union rx_status_vector { + struct { + u64 pad1:1;/*fill it with ones*/ + u64 pad2:15;/*fill with 0*/ + u64 ip_chk_sum:16; + u64 seq_num:5; + u64 mac_addr_match:1; + u64 mcast_addr_match:1; + u64 carrier_event_seen:1; + u64 bad_packet:1; + u64 long_event_seen:1; + u64 invalid_preamble:1; + u64 broadcast:1; + u64 multicast:1; + u64 crc_error:1; + u64 huh:1;/*???*/ + u64 rx_code_violation:1; + u64 rx_len:16; + } parsed; + u64 raw; +} rx_status_vector; + +typedef struct rx_packet { + rx_status_vector status; + u64 pad[3]; /* For whatever reason, there needs to be 4 double-word offset */ + u16 pad2; + char buf[METH_RX_BUFF_SIZE-sizeof(rx_status_vector)-3*sizeof(u64)-sizeof(u16)];/* data */ +} rx_packet; + +typedef struct meth_regs { + u64 mac_ctrl; /*0x00,rw,31:0*/ + u64 int_flags; /*0x08,rw,30:0*/ + u64 dma_ctrl; /*0x10,rw,15:0*/ + u64 timer; /*0x18,rw,5:0*/ + u64 int_tx; /*0x20,wo,0:0*/ + u64 int_rx; /*0x28,wo,9:4*/ + struct { + u32 tx_info_pad; + u32 rptr:16,wptr:16; + } tx_info; /*0x30,rw,31:0*/ + u64 tx_info_al; /*0x38,rw,31:0*/ + struct { + u32 rx_buff_pad1; + u32 rx_buff_pad2:8, + wptr:8, + rptr:8, + depth:8; + } rx_buff; /*0x40,ro,23:0*/ + u64 rx_buff_al1; /*0x48,ro,23:0*/ + u64 rx_buff_al2; /*0x50,ro,23:0*/ + u64 int_update; /*0x58,wo,31:0*/ + u32 phy_data_pad; + u32 phy_data; /*0x60,rw,16:0*/ + u32 phy_reg_pad; + u32 phy_registers; /*0x68,rw,9:0*/ + u64 phy_trans_go; /*0x70,wo,0:0*/ + u64 backoff_seed; /*0x78,wo,10:0*/ + u64 imq_reserved[4];/*0x80,ro,64:0(x4)*/ + /*===================================*/ + u64 mac_addr; /*0xA0,rw,47:0, I think it's MAC address, but I'm not sure*/ + u64 mcast_addr; /*0xA8,rw,47:0, This seems like secondary MAC address*/ + u64 mcast_filter; /*0xB0,rw,63:0*/ + u64 tx_ring_base; /*0xB8,rw,31:13*/ + /* Following are read-only debugging info register */ + u64 tx_pkt1_hdr; /*0xC0,ro,63:0*/ + u64 tx_pkt1_ptr[3]; /*0xC8,ro,63:0(x3)*/ + u64 tx_pkt2_hdr; /*0xE0,ro,63:0*/ + u64 tx_pkt2_ptr[3]; /*0xE8,ro,63:0(x3)*/ + /*===================================*/ + u32 rx_pad; + u32 rx_fifo; + u64 reserved[31]; +}meth_regs; + + /* Bits in METH_MAC */ + +#define SGI_MAC_RESET BIT(0) /* 0: MAC110 active in run mode, 1: Global reset signal to MAC110 core is active */ +#define METH_PHY_FDX BIT(1) /* 0: Disable full duplex, 1: Enable full duplex */ +#define METH_PHY_LOOP BIT(2) /* 0: Normal operation, follows 10/100mbit and M10T/MII select, 1: loops internal MII bus */ + /* selects ignored */ +#define METH_100MBIT BIT(3) /* 0: 10meg mode, 1: 100meg mode */ +#define METH_PHY_MII BIT(4) /* 0: MII selected, 1: SIA selected */ + /* Note: when loopback is set this bit becomes collision control. Setting this bit will */ + /* cause a collision to be reported. */ + + /* Bits 5 and 6 are used to determine the the Destination address filter mode */ +#define METH_ACCEPT_MY 0 /* 00: Accept PHY address only */ +#define METH_ACCEPT_MCAST 0x20 /* 01: Accept physical, broadcast, and multicast filter matches only */ +#define METH_ACCEPT_AMCAST 0x40 /* 10: Accept physical, broadcast, and all multicast packets */ +#define METH_PROMISC 0x60 /* 11: Promiscious mode */ + +#define METH_PHY_LINK_FAIL BIT(7) /* 0: Link failure detection disabled, 1: Hardware scans for link failure in PHY */ + +#define METH_MAC_IPG 0x1ffff00 + +#define METH_DEFAULT_IPG ((17<<15) | (11<<22) | (21<<8)) + /* 0x172e5c00 */ /* 23, 23, 23 */ /*0x54A9500 *//*21,21,21*/ + /* Bits 8 through 14 are used to determine Inter-Packet Gap between "Back to Back" packets */ + /* The gap depends on the clock speed of the link, 80ns per increment for 100baseT, 800ns */ + /* per increment for 10BaseT */ + + /* Bits 15 through 21 are used to determine IPGR1 */ + + /* Bits 22 through 28 are used to determine IPGR2 */ + +#define METH_REV_SHIFT 29 /* Bits 29 through 31 are used to determine the revision */ + /* 000: Inital revision */ + /* 001: First revision, Improved TX concatenation */ + + +/* DMA control bits */ +#define METH_RX_OFFSET_SHIFT 12 /* Bits 12:14 of DMA control register indicate starting offset of packet data for RX operation */ +#define METH_RX_DEPTH_SHIFT 4 /* Bits 8:4 define RX fifo depth -- when # of RX fifo entries != depth, interrupt is generted */ + +#define METH_DMA_TX_EN BIT(1) /* enable TX DMA */ +#define METH_DMA_TX_INT_EN BIT(0) /* enable TX Buffer Empty interrupt */ +#define METH_DMA_RX_EN BIT(15) /* Enable RX */ +#define METH_DMA_RX_INT_EN BIT(9) /* Enable interrupt on RX packet */ + + +/* RX status bits */ + +#define METH_RX_ST_RCV_CODE_VIOLATION BIT(16) +#define METH_RX_ST_DRBL_NBL BIT(17) +#define METH_RX_ST_CRC_ERR BIT(18) +#define METH_RX_ST_MCAST_PKT BIT(19) +#define METH_RX_ST_BCAST_PKT BIT(20) +#define METH_RX_ST_INV_PREAMBLE_CTX BIT(21) +#define METH_RX_ST_LONG_EVT_SEEN BIT(22) +#define METH_RX_ST_BAD_PACKET BIT(23) +#define METH_RX_ST_CARRIER_EVT_SEEN BIT(24) +#define METH_RX_ST_MCAST_FILTER_MATCH BIT(25) +#define METH_RX_ST_PHYS_ADDR_MATCH BIT(26) + +#define METH_RX_STATUS_ERRORS \ + ( \ + METH_RX_ST_RCV_CODE_VIOLATION| \ + METH_RX_ST_CRC_ERR| \ + METH_RX_ST_INV_PREAMBLE_CTX| \ + METH_RX_ST_LONG_EVT_SEEN| \ + METH_RX_ST_BAD_PACKET| \ + METH_RX_ST_CARRIER_EVT_SEEN \ + ) + /* Bits in METH_INT */ + /* Write _1_ to corresponding bit to clear */ +#define METH_INT_TX_EMPTY BIT(0) /* 0: No interrupt pending, 1: The TX ring buffer is empty */ +#define METH_INT_TX_PKT BIT(1) /* 0: No interrupt pending */ + /* 1: A TX message had the INT request bit set, the packet has been sent. */ +#define METH_INT_TX_LINK_FAIL BIT(2) /* 0: No interrupt pending, 1: PHY has reported a link failure */ +#define METH_INT_MEM_ERROR BIT(3) /* 0: No interrupt pending */ + /* 1: A memory error occurred durring DMA, DMA stopped, Fatal */ +#define METH_INT_TX_ABORT BIT(4) /* 0: No interrupt pending, 1: The TX aborted operation, DMA stopped, FATAL */ +#define METH_INT_RX_THRESHOLD BIT(5) /* 0: No interrupt pending, 1: Selected receive threshold condition Valid */ +#define METH_INT_RX_UNDERFLOW BIT(6) /* 0: No interrupt pending, 1: FIFO was empty, packet could not be queued */ +#define METH_INT_RX_OVERFLOW BIT(7) /* 0: No interrupt pending, 1: DMA FIFO Overflow, DMA stopped, FATAL */ + +#define METH_INT_RX_RPTR_MASK 0x0001F00 /* Bits 8 through 12 alias of RX read-pointer */ + + /* Bits 13 through 15 are always 0. */ + +#define METH_INT_TX_RPTR_MASK 0x1FF0000 /* Bits 16 through 24 alias of TX read-pointer */ + +#define METH_INT_SEQ_MASK 0x2E000000 /* Bits 25 through 29 are the starting seq number for the message at the */ + /* top of the queue */ + +#define METH_ERRORS ( \ + METH_INT_RX_OVERFLOW| \ + METH_INT_RX_UNDERFLOW| \ + METH_INT_MEM_ERROR| \ + METH_INT_TX_ABORT) + +#define METH_INT_MCAST_HASH BIT(30) /* If RX DMA is enabled the hash select logic output is latched here */ + +/* TX status bits */ +#define METH_TX_STATUS_DONE BIT(23) /* Packet was transmitted successfully */ + +/* Tx command header bits */ +#define METH_TX_CMD_INT_EN BIT(24) /* Generate TX interrupt when packet is sent */ + +/* Phy MDIO interface busy flag */ +#define MDIO_BUSY BIT(16) +#define MDIO_DATA_MASK 0xFFFF +/* PHY defines */ +#define PHY_QS6612X 0x0181441 /* Quality TX */ +#define PHY_ICS1889 0x0015F41 /* ICS FX */ +#define PHY_ICS1890 0x0015F42 /* ICS TX */ +#define PHY_DP83840 0x20005C0 /* National TX */ |
