Stephen Hemminger <[EMAIL PROTECTED]> writes: > > > > > Indentation. See Documentation style. > > > What about IRQF_SHARED? > > > > Not sure, maybe I should make this another driver parameter. On my > > platform is not shared... > > The trouble with devices, is that some poor sop clones the hardware to > another board and your assumption is no longer valid.
Tt's a parameter now. > > > > > > +static int oeth_start_xmit(struct sk_buff *skb, struct net_device *dev) > > > > +{ > > > > + struct oeth_private *cep = netdev_priv(dev); > > > > + volatile struct oeth_bd *bdp; > > > > + unsigned long flags; > > > > + u32 len_status; > > > > + > > > > + spin_lock_irqsave(&cep->lock, flags); > > > > + > > > > + if (cep->tx_full) { > > > > + /* All transmit buffers are full. Bail out. */ > > > > + printk("%s: tx queue full!.\n", dev->name); > > > > + print_queue(cep->tx_bd_base, cep->tx_next, OETH_TXBD_NUM); > > > > + spin_unlock_irqrestore(&cep->lock, flags); > > > > + return 1; > > > return NETDEV_TX_BUSY. > > > > > > you forgot to call stop_queue > > > > Fixed. > > > > What should I return in the case below: > > > > if (skb->len > OETH_TX_BUFF_SIZE) { > > printk("%s: tx frame too long!.\n", dev->name); > > spin_unlock_irqrestore(&cep->lock, flags); > > return 1; > > } > > Drop the skb with dev_kfree_skb_irq() and return NETDEV_TX_OK. > You should net_ratelimit() the message also if you don't want the > machine to hang if you ever get a buggy application. Done. > > > > + > > > > + /* Copy data to TX buffer. */ > > > > + memcpy_hton ((unsigned char *)bdp->addr, skb->data, skb->len); > > > > > > Use skb_copy_and_csum_dev and you get checksum offload for free. > > > > Wouldn't that just add (a bit of) overhead? It performs the memcpy, but it also > > checks if the HW is capable of doing the checksum (which it is)... > > Incidentally the memcpy_hton is just memcpy now. > > The cost of copy and checksum is the same as copy on all most hardware. Unfortunately on my platform is not. > And does your hardware do IPV6 etc? No. > > > > + cep->stats.rx_dropped++; > > > > + } > > > > + else { > > > > + skb->dev = dev; > > > > + OEDRX((printk("RX in ETH buf\n"))); > > > > + OEDRX((oeth_print_packet((u32*)bdp->addr, pkt_len))); > > > > + > > > > + memcpy_ntoh(skb_put(skb, pkt_len), (unsigned char *)bdp->addr, pkt_len); > > > > > > > > > Copying packet in IRQ routine causes long latencies. > > > > Any suggestions on how else to do this? > > You can use NAPI (see 8139too.c) it has similar "issues" I implemented NAPI. New version attached. Is there anything else that I should do? > > > > +#if CONFIG_MII > > > > +static int check_if_running(struct net_device *dev) > > > > +{ > > > > + if (!netif_running(dev)) > > > > + return -EINVAL; > > > > + return 0; > > > > +} > > > > > > Bogus wrapper. > > > > OK. BTW this is present in 3 more files: hamachi.c, starfire.c and > > sundance.c > > Send the Bunk after it. Sorry, I don't know what that means... Kconfig | 5 open_eth.c | 753 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ open_eth.h | 132 ++++++++++ 3 files changed, 890 insertions(+) --- /dev/null 2006-09-20 11:38:04.545479250 -0700 +++ drivers/net/open_eth.c 2006-12-05 11:50:57.977895000 -0800 @@ -0,0 +1,753 @@ +/* + * Ethernet driver for Open Ethernet Controller (www.opencores.org). + * Copyright (c) 2002 Simon Srot ([EMAIL PROTECTED]) + * Copyright (c) 2006 Tensilica Inc. + * + * Based on: + * + * Ethernet driver for Motorola MPC8xx. + * Copyright (c) 1997 Dan Malek ([EMAIL PROTECTED]) + * + * mcen302.c: A Linux network driver for Mototrola 68EN302 MCU + * + * Copyright (C) 1999 Aplio S.A. Written by Vadim Lebedev + * + * + * The Open Ethernet Controller is just a MAC, it needs to be + * combined with a PHY and buffer memory in order to create an + * ethernet device. Thus some of the hardware parameters are device + * specific. They need to be defined in asm/hardware.h. Example: + * + * The IRQ for the device: + * #define OETH_IRQ 1 + * + * The flag to be passed to request_irq: + * #define OETH_REQUEST_IRQ_FLAG 0 + * + * The address where the MAC registers are mapped: + * #define OETH_BASE_ADDR 0xFD030000 + * + * The address where the MAC RX/TX buffers are mapped: + * #define OETH_SRAM_BUFF_BASE 0xFD800000 + * + * Sizes for a RX or TX buffer: + * #define OETH_RX_BUFF_SIZE 2048 + * #define OETH_TX_BUFF_SIZE 2048 + * The number of RX and TX buffers: + * #define OETH_RXBD_NUM 16 + * #define OETH_TXBD_NUM 16 + * The PHY ID (needed if MII is enabled): + * #define OETH_PHY_ID 0 + * + * Code to perform the device specific initialization (REGS is a + * struct oeth_regs*): + * #define OETH_PLATFORM_SPECIFIC_INIT(REGS) + * it should at least initialize the device MAC address in + * REGS->mac_addr1 and REGS->mac_addr2. + * + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/module.h> +#include <linux/ethtool.h> +#include <linux/mii.h> + +#include <asm/hardware.h> + +#include "open_eth.h" + +#define DRV_NAME "OpencoresEthernet" + +/* The Opencores Ethernet driver needs some parameters from the + * hardware implementation. They should be defined in the + asm/hardware.h file. */ + +/* Debug helpers. */ +/* #define OETH_DEBUG_TRANSMIT */ +#ifdef OETH_DEBUG_TRANSMIT +#define OEDTX(x) x +#else +#define OEDTX(x) +#endif + +/* #define OETH_DEBUG_RECEIVE */ +#ifdef OETH_DEBUG_RECEIVE +#define OEDRX(x) x +#else +#define OEDRX(x) +#endif + +#define OETH_REGS_SIZE 0x1000 /* MAC registers + RX and TX descriptors */ +#define OETH_BD_BASE (OETH_BASE_ADDR + 0x400) +#define OETH_TOTAL_BD 128 + +/* The transmitter timeout FIXME: dann this needs to be handled */ +#define OETH_TX_TIMEOUT (2*HZ) + +/* The buffer descriptors track the ring buffers. */ +struct oeth_private { + struct oeth_regs *regs; /* Address of controller registers. */ + struct oeth_bd *rx_bd_base; /* Address of Rx BDs. */ + struct oeth_bd *tx_bd_base; /* Address of Tx BDs. */ + u8 tx_next; /* Next buffer to be sent */ + u8 tx_last; /* Next buffer to be checked if packet sent */ + u8 tx_full; /* Buffer ring full indicator */ + u8 rx_cur; /* Next buffer to be checked if packet received */ + spinlock_t lock; + spinlock_t rx_lock; + struct net_device_stats stats; +#if CONFIG_MII + struct mii_if_info mii_if; /* MII lib hooks/info */ +#endif +}; + +static void oeth_tx(struct net_device *dev); +static irqreturn_t oeth_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +#if defined(OETH_DEBUG_RECEIVE) || defined(OETH_DEBUG_TRANSMIT) +static void oeth_print_packet(u32 * add, int len) +{ + int i; + + printk("ipacket: add = %p len = %d\n", add, len); + for (i = 0; i < len; i++) { + if (!(i % 16)) + printk("\n"); + else if (!(i % 8)) + printk(" "); + printk(" %.2x", *(((unsigned char *)add) + i)); + } + printk("\n"); +} +#endif + +static int oeth_open(struct net_device *dev) +{ + int ret; + struct oeth_private *cep = netdev_priv(dev); + struct oeth_regs *regs = cep->regs; + + /* Install our interrupt handler. */ + ret = request_irq(OETH_IRQ, oeth_interrupt, OETH_REQUEST_IRQ_FLAG, + dev->name, (void *)dev); + if (ret) { + printk("request_irq failed for the Opencore ethernet device\n"); + return ret; + } + /* Enable the receiver and transmiter. */ + regs->moder |= OETH_MODER_RXEN | OETH_MODER_TXEN; + + /* Start the queue, we are ready to process packets now. */ + netif_start_queue(dev); + return 0; +} + +static int oeth_close(struct net_device *dev) +{ + struct oeth_private *cep = netdev_priv(dev); + struct oeth_regs *regs = cep->regs; + volatile struct oeth_bd *bdp; + int i; + + spin_lock_irq(&cep->lock); + /* Disable the receiver and transmiter. */ + regs->moder &= ~(OETH_MODER_RXEN | OETH_MODER_TXEN); + + bdp = cep->rx_bd_base; + for (i = 0; i < OETH_RXBD_NUM; i++) { + bdp->len_status &= ~(OETH_TX_BD_STATS | OETH_TX_BD_READY); + bdp++; + } + + bdp = cep->tx_bd_base; + for (i = 0; i < OETH_TXBD_NUM; i++) { + bdp->len_status &= ~(OETH_RX_BD_STATS | OETH_RX_BD_EMPTY); + bdp++; + } + + spin_unlock_irq(&cep->lock); + + return 0; +} + +static int oeth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct oeth_private *cep = netdev_priv(dev); + volatile struct oeth_bd *bdp; + unsigned long flags; + u32 len_status; + + spin_lock_irqsave(&cep->lock, flags); + + if (cep->tx_full) { + /* All transmit buffers are full. Bail out. */ + printk("%s: tx queue full!.\n", dev->name); + netif_stop_queue(dev); + spin_unlock_irqrestore(&cep->lock, flags); + return NETDEV_TX_BUSY; + } + + /* Fill in a Tx ring entry. */ + bdp = cep->tx_bd_base + cep->tx_next; + + len_status = bdp->len_status; + + /* Clear all of the status flags. */ + len_status &= ~OETH_TX_BD_STATS; + + /* If the frame is short, tell MAC to pad it. */ + if (skb->len <= ETH_ZLEN) + len_status |= OETH_TX_BD_PAD; + else + len_status &= ~OETH_TX_BD_PAD; + + OEDTX((printk("TX skb\n"))); + OEDTX((oeth_print_packet((u32 *) skb->data, skb->len))); + OEDTX((printk("end TX skb print\n"))); + + if (skb->len > OETH_TX_BUFF_SIZE) { + if (net_ratelimit()) + printk("%s: tx frame too long!.\n", dev->name); + dev_kfree_skb_irq(skb); + spin_unlock_irqrestore(&cep->lock, flags); + return NETDEV_TX_OK; + } + + /* Copy data to TX buffer. */ + memcpy((unsigned char *)bdp->addr, skb->data, skb->len); + + len_status = (len_status & 0x0000ffff) | (skb->len << 16); + + if ((bdp->addr + (len_status >> 16)) + >= (OETH_SRAM_BUFF_BASE + OETH_TXBD_NUM * OETH_TX_BUFF_SIZE + + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE)) + panic("MEMORY OVERWRITE at address: %x !!!\n", + (bdp->addr + (len_status >> 16))); + + OEDTX((printk("TX controller buff\n"))); + OEDTX((oeth_print_packet((u32 *) bdp->addr, bdp->len_status >> 16))); + OEDTX(printk("end TX controller buff print\n")); + + dev_kfree_skb_irq(skb); + + cep->tx_next = + (cep->tx_next + 1 == OETH_TXBD_NUM) ? 0 : (cep->tx_next + 1); + + if (cep->tx_next == cep->tx_last) { + cep->tx_full = 1; + /* Do not transmit anymore if the TX queue is full. */ + netif_stop_queue(dev); + } + + /* Send it on its way. Tell controller its ready, interrupt when done, + * and to put the CRC on the end. */ + len_status |= (OETH_TX_BD_READY | OETH_TX_BD_IRQ | OETH_TX_BD_CRC); + bdp->len_status = len_status; + + spin_unlock_irqrestore(&cep->lock, flags); + + dev->trans_start = jiffies; + return 0; +} + +/* The interrupt handler. */ +static irqreturn_t oeth_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) +{ + struct net_device *dev = dev_id; + struct oeth_private *cep = netdev_priv(dev); + volatile struct oeth_regs *regs = cep->regs; + u32 int_events; + + spin_lock(&cep->lock); + + /* Get the interrupt events that caused us to be here. */ + int_events = regs->int_src; + /* Acknowledge interrupt. */ + regs->int_src = int_events; + + /* If RX or BUSY enable RX polling. */ + if (int_events & (OETH_INT_RXF | OETH_INT_RXE | OETH_INT_BUSY)) + if (netif_rx_schedule_prep(dev)) { + regs->int_mask &= ~(OETH_INT_MASK_RXF + | OETH_INT_MASK_RXE); + __netif_rx_schedule(dev); + } + + /* Handle transmit event in its own function. */ + if (int_events & (OETH_INT_TXB | OETH_INT_TXE)) { + oeth_tx(dev); + } + + spin_unlock(&cep->lock); + + return IRQ_HANDLED; +} + +static void oeth_tx(struct net_device *dev) +{ + struct oeth_private *cep = netdev_priv(dev); + volatile struct oeth_bd *bdp; + + for (;; + cep->tx_last = + (cep->tx_last + 1 == OETH_TXBD_NUM) ? 0 : (cep->tx_last + 1)) { + u32 len_status; + + bdp = cep->tx_bd_base + cep->tx_last; + len_status = bdp->len_status; + + /* If the OETH_TX_BD_READY is set the transmission has + * not been done yet! */ + if ((len_status & OETH_TX_BD_READY) + || ((cep->tx_last == cep->tx_next) && !cep->tx_full)) + break; + + /* Check status for errors. */ + if (len_status & OETH_TX_BD_LATECOL) + cep->stats.tx_window_errors++; + if (len_status & OETH_TX_BD_RETLIM) + cep->stats.tx_aborted_errors++; + if (len_status & OETH_TX_BD_UNDERRUN) + cep->stats.tx_fifo_errors++; + if (len_status & OETH_TX_BD_CARRIER) { + cep->stats.tx_carrier_errors++; + } + if (len_status & + (OETH_TX_BD_LATECOL | OETH_TX_BD_RETLIM | + OETH_TX_BD_UNDERRUN)) + cep->stats.tx_errors++; + + cep->stats.tx_packets++; + cep->stats.tx_bytes += len_status >> 16; + cep->stats.collisions += (len_status & OETH_TX_BD_RETRY) >> 4; + + if (cep->tx_full) { + cep->tx_full = 0; + /* We have freed an entry in the TX queue, we can + * start transmitting again. */ + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + } +} + +static unsigned int oeth_rx(struct net_device *dev, int budget) +{ + struct oeth_private *cep = netdev_priv(dev); + volatile struct oeth_bd *bdp; + struct sk_buff *skb; + int received; + int pkt_len; + int bad = 0; + int current_buff = cep->rx_cur; + static int last_current_buff = 0; + + for (received = 0; received < budget; + cep->rx_cur = + (cep->rx_cur + 1 == OETH_RXBD_NUM) ? 0 : (cep->rx_cur + 1)) { + u32 len_status; + bdp = cep->rx_bd_base + cep->rx_cur; + + /* First, grab all of the stats for the incoming + * packet. These get messed up if we get called due + * to a busy condition. */ + len_status = bdp->len_status; + + if (len_status & OETH_RX_BD_EMPTY) + break; + + received++; + + /* Check status for errors. */ + if (len_status & (OETH_RX_BD_TOOLONG | OETH_RX_BD_SHORT)) { + cep->stats.rx_length_errors++; + bad = 1; + } + if (len_status & OETH_RX_BD_DRIBBLE) { + cep->stats.rx_frame_errors++; + bad = 1; + } + if (len_status & OETH_RX_BD_CRCERR) { + cep->stats.rx_crc_errors++; + bad = 1; + } + if (len_status & OETH_RX_BD_OVERRUN) { + cep->stats.rx_crc_errors++; + bad = 1; + } + if (len_status & OETH_RX_BD_TOOLONG) { + cep->stats.rx_crc_errors++; + bad = 1; + } + if (len_status & OETH_RX_BD_MISS) { + + } + if (len_status & OETH_RX_BD_LATECOL) { + cep->stats.rx_frame_errors++; + bad = 1; + } + + if (bad) { + bdp->len_status = (len_status & ~OETH_RX_BD_STATS) + | OETH_RX_BD_EMPTY; + continue; + } + + /* Process the incoming frame. */ + pkt_len = len_status >> 16; + + skb = netdev_alloc_skb(pkt_len); + + if (likely(skb)) { + skb->dev = dev; + OEDRX((printk("RX in ETH buf\n"))); + OEDRX((oeth_print_packet((u32 *) bdp->addr, pkt_len))); + + memcpy(skb_put(skb, pkt_len), + (unsigned char *)bdp->addr, pkt_len); + OEDRX((printk("RX in memory\n"))); + OEDRX((oeth_print_packet((u32 *) skb->data, pkt_len))); + skb->protocol = eth_type_trans(skb, dev); + dev->last_rx = jiffies; + cep->stats.rx_packets++; + cep->stats.rx_bytes += pkt_len; + netif_receive_skb(skb); + } + + bdp->len_status = (len_status & ~OETH_RX_BD_STATS) + | OETH_RX_BD_EMPTY; + } +} + +static int oeth_poll(struct net_device *dev, int *budget) +{ + struct oeth_private *cep = netdev_priv(dev); + int orig_budget = min(*budget, dev->quota); + int done = 1; + unsigned int work_done; + static int no_work_done = 0; + + spin_lock(&cep->rx_lock); + + work_done = oeth_rx(dev, orig_budget); + if (likely(work_done > 0)) { + *budget -= work_done; + dev->quota -= work_done; + done = (work_done < orig_budget); + } + + if (done) { + /* Stop polling and reenable interrupts. */ + __netif_rx_complete(dev); + cep->regs->int_mask |= (OETH_INT_MASK_RXF | OETH_INT_MASK_RXE); + } + spin_unlock(&cep->rx_lock); + + return !done; +} + +static struct net_device_stats *oeth_get_stats(struct net_device *dev) +{ + struct oeth_private *cep = netdev_priv(dev); + return &cep->stats; +} + +static int oeth_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + struct oeth_private *cep = netdev_priv(dev); + volatile struct oeth_regs *regs = cep->regs; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + regs->mac_addr1 = addr->sa_data[0] << 8 | addr->sa_data[1]; + regs->mac_addr0 = addr->sa_data[2] << 24 + | addr->sa_data[3] << 16 | addr->sa_data[4] << 8 | addr->sa_data[5]; + return 0; +} + +static int __init oeth_init(struct net_device *dev, unsigned int base_addr, + unsigned int irq); + +/* + * Probe for an Opencores ethernet controller. + */ +static struct net_device *__devinit oeth_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct oeth_private)); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (!check_mem_region(OETH_BASE_ADDR, OETH_REGS_SIZE)) { + SET_MODULE_OWNER(dev); + if (oeth_init(dev, OETH_BASE_ADDR, OETH_IRQ) == 0) { + if (register_netdev(dev)) + printk(KERN_WARNING + "Openethernet: No card found\n"); + else + return dev; + } + + } + return NULL; +} + +#if CONFIG_MII +static void oeth_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, "0.0"); + strcpy(info->bus_info, "none"); +} + +static int oeth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct oeth_private *cep = netdev_priv(dev); + spin_lock_irq(&cep->lock); + mii_ethtool_gset(&cep->mii_if, ecmd); + spin_unlock_irq(&cep->lock); + return 0; +} + +static int oeth_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct oeth_private *cep = netdev_priv(dev); + int res; + spin_lock_irq(&cep->lock); + res = mii_ethtool_sset(&cep->mii_if, ecmd); + spin_unlock_irq(&cep->lock); + return res; +} + +static int oeth_nway_reset(struct net_device *dev) +{ + struct oeth_private *cep = netdev_priv(dev); + return mii_nway_restart(&cep->mii_if); +} + +static u32 oeth_get_link(struct net_device *dev) +{ + struct oeth_private *cep = netdev_priv(dev); + return mii_link_ok(&cep->mii_if); +} + +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = oeth_get_drvinfo, + .get_link = oeth_get_link, + .get_settings = oeth_get_settings, + .set_settings = oeth_set_settings, + .nway_reset = oeth_nway_reset, +}; +#endif + +/* MII Data accesses. */ +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct oeth_private *cep = netdev_priv(dev); + struct oeth_regs *regs = cep->regs; + int read_value, i; + volatile int v; + + regs->miiaddress = (phy_id & OETH_MIIADDRESS_FIAD) + | ((location << 8) & OETH_MIIADDRESS_RGAD); + regs->miicommand = OETH_MIICOMMAND_RSTAT; + + /* Check if the MII is done. */ + for (i = 10000; i >= 0; i--) { + v = regs->miistatus; + if (!(v & OETH_MIISTATUS_BUSY)) { + read_value = regs->miirx_data; + /* Don't leave miicommand in read status, it + * seems to not be reset to 0 after completion. */ + regs->miicommand = 0; + return read_value; + } + } + printk(KERN_ERR "mdio_read timeout %s\n", dev->name); + return -1; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, + int value) +{ + struct oeth_private *cep = netdev_priv(dev); + struct oeth_regs *regs = cep->regs; + int i; + volatile int v; + regs->miiaddress = (phy_id & OETH_MIIADDRESS_FIAD) + | ((location << 8) & OETH_MIIADDRESS_RGAD); + regs->miitx_data = value; + regs->miicommand = OETH_MIICOMMAND_WCTRLDATA; + /* Check if the MII is done. */ + for (i = 10000; i >= 0; i--) { + v = regs->miistatus; + if (!(v & OETH_MIISTATUS_BUSY)) + return; + } + printk(KERN_ERR "mdio_write timeout %s\n", dev->name); +} + +/* Initialize the Open Ethernet MAC. */ +static int oeth_init(struct net_device *dev, unsigned int base_addr, + unsigned int irq) +{ + struct oeth_private *cep = netdev_priv(dev); + volatile struct oeth_regs *regs; + volatile struct oeth_bd *tx_bd, *rx_bd; + int i; + unsigned long mem_addr = OETH_SRAM_BUFF_BASE; + + /* Initialize the locks. */ + spin_lock_init(&cep->lock); + spin_lock_init(&cep->rx_lock); + + /* Memory regions for the controller registers and buffer space. */ + request_region(base_addr, OETH_REGS_SIZE, DRV_NAME); + dev->base_addr = base_addr; + request_region(OETH_SRAM_BUFF_BASE, + OETH_TXBD_NUM * OETH_TX_BUFF_SIZE + + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE, DRV_NAME); + /* Get pointer ethernet controller configuration registers. */ + regs = cep->regs = (struct oeth_regs *)(base_addr); + + /* Reset the controller. */ + regs->moder = OETH_MODER_RST; /* Reset ON */ + regs->moder &= ~OETH_MODER_RST; /* Reset OFF */ + + /* Setting TXBD base to OETH_TXBD_NUM. */ + regs->tx_bd_num = OETH_TXBD_NUM; + + /* Initialize TXBD pointer. */ + cep->tx_bd_base = (struct oeth_bd *)OETH_BD_BASE; + tx_bd = cep->tx_bd_base; + + /* Initialize RXBD pointer. */ + cep->rx_bd_base = cep->tx_bd_base + OETH_TXBD_NUM; + rx_bd = cep->rx_bd_base; + + /* Initialize receive/transmit pointers. */ + cep->rx_cur = 0; + cep->tx_next = 0; + cep->tx_last = 0; + cep->tx_full = 0; + + /* Set min (64) and max (1536) packet length. */ + regs->packet_len = (64 << 16) | 1536; + + /* Set IPGT, IPGR1, IPGR2 and COLLCONF registers to the + * recommended values. */ + regs->ipgt = 0x00000015; + regs->ipgr1 = 0x0000000c; + regs->ipgr2 = 0x00000012; + regs->collconf = 0x000f003f; + + /* Set control module mode. Do not deal with PAUSE frames for now. */ + regs->ctrlmoder = 0; + +#if CONFIG_MII + /* Initialize MII. */ + cep->mii_if.dev = dev; + cep->mii_if.mdio_read = mdio_read; + cep->mii_if.mdio_write = mdio_write; + cep->mii_if.phy_id = OETH_PHY_ID; + cep->mii_if.phy_id_mask = OETH_MIIADDRESS_FIAD; + cep->mii_if.reg_num_mask = 0x1f; + SET_ETHTOOL_OPS(dev, ðtool_ops); +#endif + + /* Platform specific initialization. This function should set + at least set regs->mac_addr1 and regs->mac_addr2. */ + OETH_PLATFORM_SPECIFIC_INIT(regs); + + /* Initialize TXBDs. */ + for (i = 0; i < OETH_TXBD_NUM; i++) { + tx_bd[i].len_status = + OETH_TX_BD_PAD | OETH_TX_BD_CRC | OETH_TX_BD_IRQ; + tx_bd[i].addr = mem_addr; + mem_addr += OETH_TX_BUFF_SIZE; + } + tx_bd[OETH_TXBD_NUM - 1].len_status |= OETH_TX_BD_WRAP; + + /* Initialize RXBDs. */ + for (i = 0; i < OETH_RXBD_NUM; i++) { + rx_bd[i].len_status = OETH_RX_BD_EMPTY | OETH_RX_BD_IRQ; + rx_bd[i].addr = mem_addr; + mem_addr += OETH_RX_BUFF_SIZE; + } + rx_bd[OETH_RXBD_NUM - 1].len_status |= OETH_RX_BD_WRAP; + + /* Set default ethernet MAC address. */ + dev->dev_addr[0] = (regs->mac_addr1 >> 8) & 0xff; + dev->dev_addr[1] = regs->mac_addr1 & 0xff; + dev->dev_addr[2] = (regs->mac_addr0 >> 24) & 0xff; + dev->dev_addr[3] = (regs->mac_addr0 >> 16) & 0xff; + dev->dev_addr[4] = (regs->mac_addr0 >> 8) & 0xff; + dev->dev_addr[5] = regs->mac_addr0 & 0xff; + + /* Clear all pending interrupts. */ + regs->int_src = 0xffffffff; + + /* Promisc, IFG, CRCEn */ + regs->moder |= OETH_MODER_PAD | OETH_MODER_IFG | OETH_MODER_CRCEN; + + /* Enable interrupt sources. */ + regs->int_mask = OETH_INT_MASK_TXB | OETH_INT_MASK_TXE + | OETH_INT_MASK_RXF | OETH_INT_MASK_RXE + | OETH_INT_MASK_TXC | OETH_INT_MASK_RXC | OETH_INT_MASK_BUSY; + + /* The Open Ethernet specific entries in the device structure. */ + dev->open = oeth_open; + dev->hard_start_xmit = oeth_start_xmit; + dev->stop = oeth_close; + dev->get_stats = oeth_get_stats; + dev->set_mac_address = oeth_set_mac_address; + dev->poll = oeth_poll; + dev->weight = OETH_RXBD_NUM * 2; + dev->irq = irq; + /* FIXME: Something needs to be done with dev->tx_timeout and + dev->watchdog timeout here. */ + + printk(KERN_INFO "Open Ethernet Core Version 1.0\n"); + + return 0; +} + +static struct net_device *oeth_dev; + +static int __init oeth_init_module(void) +{ + oeth_dev = oeth_probe(0); + if (!oeth_dev) + return PTR_ERR(oeth_dev); + return 0; +} + +static void __exit oeth_cleanup_module(void) +{ + unregister_netdev(oeth_dev); + release_region(oeth_dev->base_addr, OETH_REGS_SIZE); + release_region(OETH_SRAM_BUFF_BASE, + OETH_TXBD_NUM * OETH_TX_BUFF_SIZE + + OETH_RXBD_NUM * OETH_RX_BUFF_SIZE); + free_netdev(oeth_dev); +} + +module_init(oeth_init_module); +module_exit(oeth_cleanup_module); + +MODULE_DESCRIPTION("Opencores ethernet driver."); +MODULE_LICENSE("GPL"); --- /dev/null 2006-09-20 11:38:04.545479250 -0700 +++ drivers/net/open_eth.h 2006-12-04 15:00:55.000000000 -0800 @@ -0,0 +1,132 @@ +/* Ethernet configuration registers */ +struct oeth_regs { + u32 moder; /* Mode Register */ + u32 int_src; /* Interrupt Source Register */ + u32 int_mask; /* Interrupt Mask Register */ + u32 ipgt; /* Back to Bak Inter Packet Gap Register */ + u32 ipgr1; /* Non Back to Back Inter Packet Gap Register 1 */ + u32 ipgr2; /* Non Back to Back Inter Packet Gap Register 2 */ + u32 packet_len; /* Packet Length Register (min. and max.) */ + u32 collconf; /* Collision and Retry Configuration Register */ + u32 tx_bd_num; /* Transmit Buffer Descriptor Number Register */ + u32 ctrlmoder; /* Control Module Mode Register */ + u32 miimoder; /* MII Mode Register */ + u32 miicommand; /* MII Command Register */ + u32 miiaddress; /* MII Address Register */ + u32 miitx_data; /* MII Transmit Data Register */ + u32 miirx_data; /* MII Receive Data Register */ + u32 miistatus; /* MII Status Register */ + u32 mac_addr0; /* MAC Individual Address Register 0 */ + u32 mac_addr1; /* MAC Individual Address Register 1 */ + u32 hash_addr0; /* Hash Register 0 */ + u32 hash_addr1; /* Hash Register 1 */ +}; + +/* Ethernet buffer descriptor */ +struct oeth_bd { + u32 len_status; + u32 addr; /* Buffer address */ +}; + +/* Tx BD */ +#define OETH_TX_BD_READY 0x8000 /* Tx BD Ready */ +#define OETH_TX_BD_IRQ 0x4000 /* Tx BD IRQ Enable */ +#define OETH_TX_BD_WRAP 0x2000 /* Tx BD Wrap (last BD) */ +#define OETH_TX_BD_PAD 0x1000 /* Tx BD Pad Enable */ +#define OETH_TX_BD_CRC 0x0800 /* Tx BD CRC Enable */ + +#define OETH_TX_BD_UNDERRUN 0x0100 /* Tx BD Underrun Status */ +#define OETH_TX_BD_RETRY 0x00F0 /* Tx BD Retry Status */ +#define OETH_TX_BD_RETLIM 0x0008 /* Tx BD Retransmission Limit Status */ +#define OETH_TX_BD_LATECOL 0x0004 /* Tx BD Late Collision Status */ +#define OETH_TX_BD_DEFER 0x0002 /* Tx BD Defer Status */ +#define OETH_TX_BD_CARRIER 0x0001 /* Tx BD Carrier Sense Lost Status */ +#define OETH_TX_BD_STATS (OETH_TX_BD_UNDERRUN | \ + OETH_TX_BD_RETRY | \ + OETH_TX_BD_RETLIM | \ + OETH_TX_BD_LATECOL | \ + OETH_TX_BD_DEFER | \ + OETH_TX_BD_CARRIER) + +/* Rx BD */ +#define OETH_RX_BD_EMPTY 0x8000 /* Rx BD Empty */ +#define OETH_RX_BD_IRQ 0x4000 /* Rx BD IRQ Enable */ +#define OETH_RX_BD_WRAP 0x2000 /* Rx BD Wrap (last BD) */ + +#define OETH_RX_BD_MISS 0x0080 /* Rx BD Miss Status */ +#define OETH_RX_BD_OVERRUN 0x0040 /* Rx BD Overrun Status */ +#define OETH_RX_BD_INVSIMB 0x0020 /* Rx BD Invalid Symbol Status */ +#define OETH_RX_BD_DRIBBLE 0x0010 /* Rx BD Dribble Nibble Status */ +#define OETH_RX_BD_TOOLONG 0x0008 /* Rx BD Too Long Status */ +#define OETH_RX_BD_SHORT 0x0004 /* Rx BD Too Short Frame Status */ +#define OETH_RX_BD_CRCERR 0x0002 /* Rx BD CRC Error Status */ +#define OETH_RX_BD_LATECOL 0x0001 /* Rx BD Late Collision Status */ +#define OETH_RX_BD_STATS (OETH_RX_BD_MISS | \ + OETH_RX_BD_OVERRUN | \ + OETH_RX_BD_INVSIMB | \ + OETH_RX_BD_DRIBBLE | \ + OETH_RX_BD_TOOLONG | \ + OETH_RX_BD_SHORT | \ + OETH_RX_BD_CRCERR | \ + OETH_RX_BD_LATECOL) + +/* MODER Register */ +#define OETH_MODER_RXEN 0x00000001 /* Receive Enable */ +#define OETH_MODER_TXEN 0x00000002 /* Transmit Enable */ +#define OETH_MODER_NOPRE 0x00000004 /* No Preamble */ +#define OETH_MODER_BRO 0x00000008 /* Reject Broadcast */ +#define OETH_MODER_IAM 0x00000010 /* Use Individual Hash */ +#define OETH_MODER_PRO 0x00000020 /* Promiscuous (receive all) */ +#define OETH_MODER_IFG 0x00000040 /* Min. IFG not required */ +#define OETH_MODER_LOOPBCK 0x00000080 /* Loop Back */ +#define OETH_MODER_NOBCKOF 0x00000100 /* No Backoff */ +#define OETH_MODER_EXDFREN 0x00000200 /* Excess Defer */ +#define OETH_MODER_FULLD 0x00000400 /* Full Duplex */ +#define OETH_MODER_RST 0x00000800 /* Reset MAC */ +#define OETH_MODER_DLYCRCEN 0x00001000 /* Delayed CRC Enable */ +#define OETH_MODER_CRCEN 0x00002000 /* CRC Enable */ +#define OETH_MODER_HUGEN 0x00004000 /* Huge Enable */ +#define OETH_MODER_PAD 0x00008000 /* Pad Enable */ +#define OETH_MODER_RECSMALL 0x00010000 /* Receive Small */ + +/* Interrupt Source Register */ +#define OETH_INT_TXB 0x00000001 /* Transmit Buffer IRQ */ +#define OETH_INT_TXE 0x00000002 /* Transmit Error IRQ */ +#define OETH_INT_RXF 0x00000004 /* Receive Frame IRQ */ +#define OETH_INT_RXE 0x00000008 /* Receive Error IRQ */ +#define OETH_INT_BUSY 0x00000010 /* Busy IRQ */ +#define OETH_INT_TXC 0x00000020 /* Transmit Control Frame IRQ */ +#define OETH_INT_RXC 0x00000040 /* Received Control Frame IRQ */ + +/* Interrupt Mask Register */ +#define OETH_INT_MASK_TXB 0x00000001 /* Transmit Buffer IRQ Mask */ +#define OETH_INT_MASK_TXE 0x00000002 /* Transmit Error IRQ Mask */ +#define OETH_INT_MASK_RXF 0x00000004 /* Receive Frame IRQ Mask */ +#define OETH_INT_MASK_RXE 0x00000008 /* Receive Error IRQ Mask */ +#define OETH_INT_MASK_BUSY 0x00000010 /* Busy IRQ Mask */ +#define OETH_INT_MASK_TXC 0x00000020 /* Transmit Control Frame IRQ Mask */ +#define OETH_INT_MASK_RXC 0x00000040 /* Received Control Frame IRQ Mask */ + +/* Control Module Mode Register */ +#define OETH_CTRLMODER_PASSALL 0x00000001 /* Pass Control Frames */ +#define OETH_CTRLMODER_RXFLOW 0x00000002 /* Receive Control Flow Enable */ +#define OETH_CTRLMODER_TXFLOW 0x00000004 /* Transmit Control Flow Enable */ + +/* MII Mode Register */ +#define OETH_MIIMODER_CLKDIV 0x000000FF /* Clock Divider */ +#define OETH_MIIMODER_NOPRE 0x00000100 /* No Preamble */ +#define OETH_MIIMODER_RST 0x00000200 /* MIIM Reset */ + +/* MII Command Register */ +#define OETH_MIICOMMAND_SCANSTAT 0x00000001 /* Scan Status */ +#define OETH_MIICOMMAND_RSTAT 0x00000002 /* Read Status */ +#define OETH_MIICOMMAND_WCTRLDATA 0x00000004 /* Write Control Data */ + +/* MII Address Register */ +#define OETH_MIIADDRESS_FIAD 0x0000001F /* PHY Address */ +#define OETH_MIIADDRESS_RGAD 0x00001F00 /* RGAD Address */ + +/* MII Status Register */ +#define OETH_MIISTATUS_LINKFAIL 0x00000001 /* Link Fail */ +#define OETH_MIISTATUS_BUSY 0x00000002 /* MII Busy */ +#define OETH_MIISTATUS_NVALID 0x00000004 /* Data in MII Status Register is invalid */ --- drivers/net/Kconfig~ 2006-09-05 12:30:28.000000000 -0700 +++ drivers/net/Kconfig 2006-12-01 11:29:25.000000000 -0800 @@ -550,6 +550,11 @@ help If you have an XT2000 board, you probably want to enable this driver. +config OPENCORES_ETHERNET + tristate "Opencores.org Ethernet MAC driver" + depends on NET_ETHERNET + help + config SUNBMAC tristate "Sun BigMAC 10/100baseT support (EXPERIMENTAL)" depends on NET_ETHERNET && SBUS && EXPERIMENTAL - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html